The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Music::Canon - routines for musical canon construction

SYNOPSIS

  use Music::Canon ();
  my $mc = Music::Canon->new;

  # options affecting all the *_map routines
  $mc->set_contrary(1);
  $mc->set_retrograde(1);
  $mc->set_transpose(12);     # by semitones
  $mc->set_transpose(q{c'});  # or "to" a lilypond note

  # 1:1 semitone mapping
  my @new_phrase = $mc->exact_map(qw/0 7 4 0 -1 0/);
  $mc->exact_map_reset;

  # trickier is the so-called modal mapping;
  # default is Major to Major
  @new_phrase = $mc->modal_map(qw/0 7 4 0 -1 0/);
  $mc->modal_map_reset;

  # or instead modal mapping by scale name (via Music::Scales)
  $mc->set_scale_intervals( 'input',  'minor'  );
  $mc->set_scale_intervals( 'output', 'dorian' );
  @new_phrase = $mc->modal_map(qw/0 7 4 0 -1 0/);

See also canonical of the App::MusicTools module for a command line tool interface to this code, and the eg/ and t/ directories of this distribution for more example code.

DESCRIPTION

Musical canons involve horizontal lines of music (often called voices) that are combined with other canon or free counterpoint voices to produce harmony. This module assists with the creation of new voices via *_map methods that transform pitches according to various rules. Chords could also be transformed via the *_map functions by passing the pitches of the chord to the *_map method, then forming a new chord from the results.

Whether the output is usable is left to the composer. Harmony can be created by careful selection of the input material and the mapping settings, or perhaps by adding a free counterpoint voice to support the canon voices. Analyzing the results with Music::Tension may help search for suitable material.

The methods of this module at present suit crab canon, as those lines are relatively easy to calculate. Other forms of canon would ideally require a counterpoint module, which has not yet been written. The modal_map method also suits the calculation of new voices of a fugue, for example converting the subject to the dominant.

Knowledge of canon will doubtless help any user of this module; the "SEE ALSO" section lists resources for learning these.

METHODS

Methods may die or croak under various conditions. new would be a good one to start with, then one of the *_map functions to transform the list of pitches into new material.

Most methods, notably the *_map methods, operate only on pitch numbers. Some methods also accept lilypond note names (via Music::LilyPondUtil). Use notes2pitches of Music::LilyPondUtil to convert notes to pitches suitable for passing to a *_map function. Otherwise, MIDI pitch numbers could easily be fed to the mapping methods, or objects that have a pitch method.

exact_map phrase

One-to-one semitone mapping from the input phrase to the returned list. phrase may be a list or an array reference, and may contain raw pitch numbers, objects that support a pitch method, or other data that will be passed through unchanged.

Affected by various settings, notably set_contrary, set_retrograde, and set_transpose.

Be sure to call exact_map_reset when done converting a phrase, or disable the keep_state option of new and then pass the phrase in a single call to exact_map.

exact_map_reset

Resets current state of the exact_map method. Not necessary if keep_state option of new disabled, and entire phrases passed in one go to exact_map.

Returns the Music::Canon object, so can be chained with other method calls.

get_contrary

Returns the current contrary setting (boolean).

get_modal_chrome

Returns current modal_map chrome weighting (troolean).

get_modal_pitches

Returns the current modal input and output layer starting pitches used by modal_map. These will be undefined if unset, or might have values leftover from some previous conversion, unless reset via reset_modal_pitches.

get_retrograde

Returns the current retrograde setting (boolean). Retrograde is a fancy way to indicate that the output list be reversed.

get_scale_intervals layer

Returns the scale intervals for the indicated layer (input or output), or throws an exception if these are unset. The intervals are returned as a list of two array references, the first for the scale ascending, the second for the scale descending.

Note that descending scale intervals are noted from the highest note down.

get_transpose

Returns the current transpose setting (integer of semitones or lilypond note name, depending on what was previously set).

Modal mapping of the pitches in phrase from an arbitrary input mode to an arbitrary output mode, as set by set_scale_intervals, or the Major to Major scales by default. Returns a list, or throws an exception if a pitch cannot be converted. phrase may be a list or an array reference, and may contain raw pitch numbers, objects that support a pitch method, or other data that will be passed through unchanged.

Configuring the starting pitches via set_modal_pitches is a necessity if the phrase starts on a scale degree that is not the root or tonic of the mode involved. That is, a phrase that begins on the note E will create a mapping around E-major by default; if a mapping around C- Major (at MIDI pitch 60) is intended, this must be set in advance:

  # by pitch number
  $mc->set_modal_pitches( 60, 60 );
  $mc->modal_map(qw/64 .../);

  # or the equivalent via lilypond note names
  $mc->set_modal_pitches(qw/c' c'/);
  $mc->modal_map(qw/e' .../);

NOTE modal_map is somewhat experimental, so likely has edge cases or bugs unknown to me, or may change without notice as I puzzle through the mapping logic. Consult the tests under the module distribution t/ directory for what cases are covered. It is also relatively unexplored, for example mapping between exotic scales or Forte Numbers.

The algorithm operates by converting the intervals between the notes into diatonic steps via the input mode, then replicates that many steps in the output mode, followed by any necessary chromatic adjustments. The initial starting pitches (derived from the input phrase and transpose setting, or via pitches set via the set_modal_pitches method) form the point of linkage between the two scales or really any arbitrary interval sequences.

Transposition (via set_transpose) will influence the output linking pitch, unless that value was already set via a prior conversion or set_modal_pitches call.

An example may help illustrate this function. Assuming Major to Major conversion, contrary motion, and a transposition by an octave (12 semitones), the software will convert pitches as shown in this chart (the "linking point" is from 0 in the input scale to 12 in the output scale):

        0    1    2   3    4   5   6    7   8    9   10  11  12
  In  | C  | c# | D | d# | E | F | f# | G | g# | A | a# | B | C' |
  Out | C' | x  | B | a# | A | G | f# | F | x  | E | d# | D | C  |
       12        11   10   9   7   6    5        4   3    2   0

Assuming an input phrase of C G c#, the output phrase would be C' F and then an exception would be thrown, as there is no way to convert c# using this modal mapping and transposition. Other mappings and transpositions will have between zero to several notes that cannot be converted. The eg/conversion-charts file of this module's distribution contains more such charts, as also can be generated by the eg/brutecanon utility.

How to map non-diatonic notes is another concern; the above chart shows two x for notes that cannot be converted. Depending on the mapping, there might be zero, one, or several possible choices for a given chromatic. Consider c# of C Major to various entry points of the sakura scale G# A# B D# E:

    C Major    | C  | c# | D  | 
  ------------------------------------------------------------
  Sakura @ G#  | G# | a  | A# |  - one choice
  Sakura @ A#  | A# | x  | B  |  - throw exception
  Sakura @ B   | B  | ?  | D# |  - (c, c#, d)

The chrome_weight parameter to new controls the multiple choice situation. The default setting of 0 results in c#, as that value is halfway between B and D, just as the input scale chromatic is halfway between C and D. Otherwise, with a negative chrome_weight, c is favored, or for a positive chrome_weight, d. Test cases are advised to confirm that the resulting chromatics are appropriate, though this should only be necessary if the output scale has intervals greater than two (e.g. hungarian minor, or any of the pentatonic scales, etc).

modal_map is affected by various settings, notably set_contrary, set_modal_pitches, set_retrograde, set_scale_intervals, and set_transpose.

Be sure to call modal_map_reset when done converting a phrase. reset_modal_pitches may also be necessary to clear prior scale linking points.

Resets the state variables associated with modal_map, with the exception of the pitches set by the set_modal_pitches call (or automatically from the input phrase), which are not reset by this method (use the reset_modal_pitches method to accomplish that).

Returns the Music::Canon object, so can be chained with other method calls.

new

Constructor. Accepts a number of options, the useful or safe of which are listed here.

  • chrome_weight - sets the chrome_weight troolean. Zero by default.

    chrome_weight controls how chromatics between output intervals greater than two semitones are handled in modal_map; if this parameter is negative, the literal chromatic step from the input scale will be used from the previous pitch (bottom up), if positive the literal chromatic step will be applied in the other direction (top down), and if zero (the default) a value proportionally closest to where the chromatic is in the input interval will be calculated. See documentation for modal_map for a longer example.

  • contrary - sets the contrary boolean. On by default.

  • input - scale or Forte Number or interval set for the modal_map input mode. Defaults to the Major scale if unset. See set_scale_intervals and modal_map for details.

  • keep_state - configures whether state is maintained through different calls to the various *_map methods. On by default, which requires the use of the corresponding *_map_reset method when a phrase is complete. There are two possible workflows; with state enabled, multiple calls can be made to the mapping function, which suits modal_map:

      my $mc_state = Music::Canon->new;
      for my $e (@input) {
        my $result;
        eval { $result = $mc_state->modal_map($e) };
        if ($@ and $@ =~ m/undefined chromatic conversion/) {
          $result = 'r';   # make it a lilypond rest
        }
        push @output, $result;
      }
      @output = reverse @output if $mc->get_retrograde;
      $mc_state->modal_map_reset;

    The other workflow is to disable state, and pass entire phrases for conversion in one call. This better suits exact_map, which unlike the modal transformation will not have pitches it cannot convert, and thus will not need to handle individual note exceptions:

      my $mc_no_state = Music::Canon->new(keep_state => 0);
      my @output = $mc_no_state->exact_map(\@input);
  • non_octave_scales - configures whether scales should be bounded at an octave (12 semitones) or not. The default is to pad interval sets that sum up to less than 12 to include an additional element such that the sum of the intervals is 12. Interval sets greater than 12 will cause an exception to be thrown.

    Enable this option only if dealing with a maqam or similar scale that is not bounded by the Western notion of octave. For example, the whole tone scale (which is really just an expensive way to do an exact_map):

      my $mc = Music::Canon->new( non_octave_scales => 1 );
      # or Forte Number '6-35'
      $mc->set_scale_intervals('input',  [2,2,2,2,2] );
      $mc->set_scale_intervals('output', [2,2,2,2,2] );
  • output - scale or Forte Number or interval set for the modal_map output mode. Defaults to the Major scale if unset. See set_scale_intervals and modal_map for details.

  • retrograde - sets whether phrases are reversed. On by default.

  • transpose - value to transpose by, in semitones, or "to" a lilypond note name.

reset_modal_pitches

Routine to nullify the modal_map pitches that are either set by the first note of the input phrase, or via the set_modal_pitches method. These values otherwise persist across calls to modal_map.

set_contrary boolean

Sets the contrary boolean (on by default). With this set, phrases from the *_map routines will be set in contrary motion to the input phrase.

Returns the Music::Canon object, so can be chained with other method calls.

set_modal_chrome weight

Sets the modal_map chrome weighting. Troolean: 0 (proportional), negative (literal from previous note up), or positive (literal from current note down).

set_modal_pitches input_tonic, [ output_tonic ]

Sets the tonic note or pitch of the input and output interval sets used by modal_map. If the input_tonic is set, but not the optional output_tonic, the cached output_tonic value, if any, will be wiped out.

  $mc->set_modal_pitches(60, 62);
  $mc->set_modal_pitches(undef, 64);  # just output start pitch
  $mc->set_modal_pitches(q{c'});      # by lilypond note

Using this method is a necessity if the phrase passed to modal_map begins on a non-tonic scale degree, as otherwise that non- tonic scale degree will become the tonic for whatever interval set is involved. That is, if the notes e e f g g are passed to modal_map, by default modal_map will assume e Major as the input scale, and e Major as the output scale (though that may vary depending on the transpose setting).

Returns the Music::Canon object, so can be chained with other method calls.

set_retrograde boolean

Sets the retrograde boolean (on by default). If set, phrases from the *_map routines will be reversed. Meaningless if *_map calls are being made note-by-note (see the keep_state documentation).

Returns the Music::Canon object, so can be chained with other method calls.

set_scale_intervals layer, asc, [dsc]

Sets the scale intervals for the indicated layer (input or output). The asc (and optional dsc) can be one of several different things:

  $mc->set_scale_intervals('input', 'minor');  # Music::Scales
  $mc->set_scale_intervals('input', '7-23');   # Forte Number
  # arbitrary interval sequence
  $mc->set_scale_intervals('input', [qw/2 1 3 2 1 3 1/]);

If the dsc is undefined, the corresponding asc intervals will be used, except for Music::Scales, for which the descending intervals associated with the ascending scale will be used. If asc is undefined, dsc must then be set to something. This allows the descending intervals alone to be adjusted.

  $mc->set_scale_intervals('output', undef, 'aeolian');

Note that the descending intervals must be ordered from the highest pitch down. That is, melodic minor can be stated manually via:

  $mc->set_scale_intervals( 'output',
    [2,1,2,2,2,2],  # ascending  - c d ees f g a b
    [2,2,1,2,2,1]   # descending - c bes aes g f ees d
  );

Though this particular case would be much more easily stated via Music::Scales via:

  $mc->set_scale_intervals('output', 'mm');

set_scale_intervals returns the Music::Canon object, so can be chained with other method calls.

set_transpose integer or lilypond note

Sets the value to transpose to or by in *_map methods, either in semitones, or to a particular lilypond note:

  $mc->set_transpose(-12)    # down by an octave
  $mc->set_transpose(q{c'})  # to the lilypond note

Returns the Music::Canon object, so can be chained with other method calls.

SEE ALSO

"Counterpoint in Composition" by Felix Salzer and Carl Schachter.

"The Technique of Canon" by Hugo Norden

"Counterpointer" by Ars Nova (counterpoint instruction software).

http://en.wikipedia.org/wiki/Forte_number

Music::AtonalUtil, Music::LilyPondUtil, Music::Scales, Music::Tension

The canonical and scalemogrifier utilities of App::MusicTools may also be of interest.

AUTHOR

Jeremy Mates, <jmates@cpan.org>

COPYRIGHT AND LICENSE

Copyright (C) 2013 by Jeremy Mates

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.16 or, at your option, any later version of Perl 5 you may have available.