App-MusicTools

 view release on metacpan or  search on metacpan

bin/scalemogrifier  view on Meta::CPAN

#!perl
#
# scalemogrifier - generates notes of arbitrary scales (subsets of the
# Western 12-tone chromatic system) from a specified starting note in a
# specified direction and so forth from other options. This file uses
# "scale" and "subset" and "interval series" and "interval vector"
# interchangeably.
#
# Run perldoc(1) on this file for additional documentation.
#
# A ZSH completion script is available in the zsh-compdef/ directory of
# the App::MusicTools distribution.

use 5.14.0;
use warnings;
use Getopt::Long        qw/GetOptions/;
use List::Util          qw/sum/;
use Music::LilyPondUtil ();
use Text::Wrap          qw/wrap/;

# XXX but these have no concept of direction, which for some scales
# changes the notes, in particular:
#
# * mminor - melodic minor, should only be used ascending here.
#
# * amdorian - anti-melodic dorian, mixin from locrian, like melodic
#   minor, except for downwards trending notes? Conventional voice
#   leading tricky due to the diminished chords.
#
# XXX probably should standardize on Music::Scale (unmaintained?) or use
# Masaya Yamaguchi's complete scale system, plus offer ability for user
# to define new scales.
my %modes = (
    ionian     => [qw(2 2 1 2 2 2 1)],
    lydian     => [qw(2 2 2 1 2 2 1)],
    mixolydian => [qw(2 2 1 2 2 1 2)],
    dorian     => [qw(2 1 2 2 2 1 2)],
    amdorian   => [qw(2 1 2 1 2 2 2)],
    aeolian    => [qw(2 1 2 2 1 2 2)],
    mminor     => [qw(2 1 2 2 2 2 1)],
    hminor     => [qw(2 1 2 2 1 3 1)],
    hunminor   => [qw(2 1 3 2 1 3 1)],
    phrygian   => [qw(1 2 2 2 1 2 2)],
    locrian    => [qw(1 2 2 1 2 2 2)],
    'p-major'  => [qw(2 2 3 2 3)],
    'p-minor'  => [qw(3 2 2 3 2)],
    'p-sakura' => [qw(1 4 2 1 4)],
);
$modes{'major'} = $modes{'ionian'};
$modes{'minor'} = $modes{'aeolian'};

# Note names for program output
my %num2note = (
    'ly-sharp' => {qw/0 c 1 cis 2 d 3 dis 4 e 5 f 6 fis 7 g 8 gis 9 a 10 ais 11 b/},
    'ly-flat'  => {qw/0 c 1 des 2 d 3 ees 4 e 5 f 6 ges 7 g 8 aes 9 a 10 bes 11 b/},
);
my $num2note_flavor = 'ly-';

# Not The Scale, but to map back to That Other Scale.
my $DEG_IN_SCALE = 12;

my @interval_vector = @{ $modes{'major'} };
my $length          = 8;
my $direction       = 1;
my $in_reverse      = 0;
my $relative        = 0;
my $transpose;

my $output_delimiter = " ";
my ( @output_intervals, $output_sum );

my $lyu = Music::LilyPondUtil->new( ignore_register => 1 );

########################################################################
#
# MAIN

GetOptions(
    'direction=i'            => \$direction,
    'flats!'                 => \my $use_flats,
    'help|h|?'               => \&print_help,
    'intervals=s'            => \my $intervals,
    'listmodes'              => \my $list_modes,
    'minor'                  => \my $use_minor,
    'mode=s'                 => \my $mode,
    'ors=s'                  => \$output_delimiter,
    'output_intervals|ois=s' => \my $output_intervals,
    'relative'               => \$relative,
    'length=i'               => \$length,
    'raw'                    => \my $raw_output,
    'reverse'                => \$in_reverse,
    'transpose|t=s'          => \$transpose,
) or die "error: could not parse options\n";

print_help() if @ARGV;

if ($list_modes) {
    print "$_\n" for sort keys %modes;
    exit 0;
}

$direction = int($direction);
die "error: a direction of zero is a bit too aimless\n" if $direction == 0;
$length = int($length);
die "error: length must be at least one\n" if $length < 1;

$transpose = defined $transpose ? $lyu->notes2pitches($transpose) : 0;

$mode = 'minor' if $use_minor;
if ( defined $mode ) {

bin/scalemogrifier  view on Meta::CPAN

}

sub print_help {
    $Text::Wrap::columns = 78;
    my $modes = wrap( '  ', '  ', sort keys %modes );

    warn <<"END_HELP";
Usage: $0 [options]
  --listmodes       Shows list of available modes.

Options affecting input:
  --direction       Positive, scale goes up, negative, down. Magnitudes
                    greater than one multiply the interval.
  --intervals       List of intervals or notes of the scale.
  --length          How many notes to generate in output.
  --mode            Specify named interval series instead of --intervals.
  --minor           Use minor mode instead of --intervals (default: major).
  --reverse         Reverses order of intervals.

Options affecting output:
  --flats           Use flats in output, instead of default sharps.
  --ois             Custom scale or interval set output will be mapped into.
  --ors             What will be printed between output elements.
                    Defaults to the space character.
  --raw             Emit pitch numbers instead of note names.
  --relative        Generate relative lilypond output (default: absolute).
  --transpose       Number to transpose the output by.

Available modes for --modes option:

$modes
END_HELP
    exit 64;
}

END {
    # Report problems when writing to stdout (perldoc perlopentut)
    unless ( close(STDOUT) ) {
        warn "error: problem closing STDOUT: $!\n";
        exit 74;
    }
}

__END__

=head1 NAME

scalemogrifier - generate notes of named or arbitrary scales

=head1 SYNOPSIS

  $ scalemogrifier
  c d e f g a b c'

The list of accepted lilypond note names and other elements include:

  c cis des d dis ees e f fis ges g gis aes a ais bes b   nl

Available named modes include:

  aeolian amdorian dorian hminor hunminor ionian locrian lydian
  major minor mixolydian mminor phrygian

=head1 DESCRIPTION

Generates notes of arbitrary scales (subsets of the Western 12-tone
chromatic system) from a specified starting note in a specified
direction and so forth from other options.

The output is in lilypond absolute format. The default scale is The
Major Scale, but that's easy to adjust to say C Minor:

  $ scalemogrifier --minor --flats

By default, scales loop back to the starting note. If this is not the
case, suffix C<nl> to a custom interval series:

  $ scalemogrifier --intervals=c,cis,dis,e,g,nl --length=15
  c cis dis e g gis ais b d' dis' f' fis' a' ais' c''

The output is based on the assumption that C<c> equals 0. The output
can be transposed via:

  $ scalemogrifier --mode=mixolydian --transpose=-5

Integer interval numbers can be used instead of note names or the
--mode option, for example the Major Scale:

  $ scalemogrifier --intervals='2 2 1 2 2 2 1' --raw

Or to form a -P4,+P5 sequence:

  $ scalemogrifier --intervals=-5,7 --len=24

Or just plain made up:

  $ scalemogrifier --intervals=1,2,3,5,7 --dir=53 --relative --len=24

Scales can also be rendered backwards:

  $ scalemogrifier --dir=-1

Or with the interval order reversed:

  $ scalemogrifier --rev

The scale generated can be fed back into a different output scale (the
direction and reverse options only apply to the --intervals option
scale, not the --ois scale):

  $ scalemogrifier --intervals=c,cis,dis,e,g,nl --ois=aes,bes,ces,ees,fes \
     --len=48 --flats --rel

Practical application of the results to music theory or composition left
as an exercise to the user. The output can be fed to C<ly-fu> for
playback and display, for example:

  $ ly-fu --abs -l --open $(scalemogrifier ...)
  $ ly-fu       -l --open $(scalemogrifier --relative ...)

=head1 OPTIONS



( run in 0.717 second using v1.01-cache-2.11-cpan-98e64b0badf )