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::RhythmSet - sets of rhythms and various generation functions

SYNOPSIS

  use 5.24.0;
  use Music::RhythmSet;
  use Music::RhythmSet::Util qw(rand_onsets);
  
  my $rest = [ (0) x 16 ];
  
  # randomly select a rhythm with five onsets in 16 beats
  # that will live for eight measures
  sub newpat { rand_onsets(5, 16), 8 }
  
  # three voices, with a delayed entrance on two of them
  my $set = Music::RhythmSet->new->add(
      { pattern => rand_onsets(5, 16), ttl => 16 },
      { next => \&newpat, pattern => $rest, ttl => 2 },
      { next => \&newpat, pattern => $rest, ttl => 4 },
  );
  
  # generate 8 measures of (probably) noise
  $set->advance(8);
  
  # export with different notes for each track
  $set->to_midi(
      track => [ {}, { note => 67 }, { note => 72 } ]
  )->write_to_file("noise.midi");

DESCRIPTION

This module supports sets of rhythms, each being a Music::RhythmSet::Voice object, which is where most of the action happens. Music::RhythmSet::Util offers various rhythm generation and classification functions. Rhythms have a lifetime (ttl), and can have a callback function that can set a new rhythm and time-to-live when the ttl expires. Rhythms can be exported in various formats.

See eg/beatinator and eg/texty in the distribution for this module for various ways to generate MIDI, import from string form, etc.

Various calls will throw exceptions if something goes awry.

CONSTRUCTOR

The new method accepts any of the "ATTRIBUTES"; the add method or special voicel argument would be the most typical means of adding voices, though.

  # new object, add two empty voices
  $set = Music::RhythmSet->new->add({},{});

  # same as the above
  $set = Music::RhythmSet->new(voicel => [{},{}]);

  # same as the above
  $set = Music::RhythmSet->new;
  $set->voices([
    Music::RhythmSet::Voice->new(id => 0),
    Music::RhythmSet::Voice->new(id => 1)
  ]);

However, voices probably need at least a pattern and ttl set, and even then probably also a next callback function. Or a replay log could be manually supplied...

BUILD

Constructor helper subroutine. See Moo.

ATTRIBUTES

stash

A place for the caller to store whatever. The advance method passes the current set object down to next callback code as the set parameter; individual voices could use the set object stash as a shared variable store.

This attribute is not used by code in this distribution.

voices

Array reference of voices. These are Music::RhythmSet::Voice objects. Probably should not be manually edited, unless you know what you are doing. Use the add method to add more voices to a set.

METHODS

add voice [ voice ... ]

Each voice must be a hash reference that is fed to the constructor of Music::RhythmSet::Voice. Any caller-supplied id attribute will however be ignored as this module manages those values itself.

advance count [ param ]

This call steps each of the voices forward by count measures, which may result in new entries into the replay log for each voice, as well as next callbacks being run to set new rhythms. Voices are advanced in turn from first to last in the voices list.

param is used to pass settings down to the advance method of Music::RhythmSet::Voice and from there into the next callback. In particular the set attribute will contain a reference to the $set object involved should the voices need to query other voices during a next callback, or access the set stash.

changes param

Utility method that shows when voices change their patterns in their replay logs, and what other patterns are active at those points. Voices must have something in their replay log before this method is called.

The eg/beatinator script in this module's distribution shows one way to use this call.

There are two mandatory parameters:

Callback; it is passed the current "measure" number of the change. This happens before the voice callback works through each voice.

voice

Callback; called for each voice in turn. It is passed the "measure" number, voice ID, the current pattern as an array reference, the current pattern as a beatstring, a boolean for whether the pattern might have changed, and another boolean that indicates whether the pattern was a repeat of the previous.

Two booleans are used because a next callback could return the same pattern as before; this will create a new entry in the replay log (what the first boolean indicates) that may be the same as before (the second boolean).

And two optional parameters:

divisor

A positive integer that indicates how many beats there are in a measure. 1 by default, which means a "measure" is the number of beats since the beginning of the replay log (counting from 0, not 1). A divisor of 16 (and assuming the pattern used are all of length 16) would mean that the term "measure" no longer needs scare quotes, as it would represent a measure number as they are more typically known (except for the counting from zero thing, which musicians usually do not do).

max

A positive integer for when to stop working through the "measures" of the replay log. Influenced by the divisor.

Note that changes uses the total beat count possibly divided by a divisor to determine when to stop; to_ly and to_midi only use the measure count (and are ignorant of how many total beats have been generated). So max here may produce different amounts of output than the maxm parameter used by those other calls. to_string supports a divisor but only uses that for display purposes.

clone

Clones each of the voices and returns a new Music::RhythmSet object with those cloned voices.

from_string string [ param ]

Attempts to parse the string (presumably from to_string or of compatible form) and adds any pattern,ttl parsed to the replay log of each voice. The events are assumed to be in sequential order for each voice; the beat-count field is ignored. The ID values must be in ascending order (at least when first encountered). Same parameters as to_string. A default split on whitespace delimits the fields.

eg/texty in the distribution for this module uses this method.

Lines that only contain whitespace, are empty, or start with a # that may have whitespace before it will be skipped. Trailing whitespace and # comments on lines are ignored.

measure count

Sets the measure attribute of each voice to the given count. Possibly useful when reloading from a replay file or under similar manual edits to the voices so that any subsequent advance calls use the correct measure number in any relevant next callback calculations.

This assumes the measures (patterns) of the voices are all the same size, which may not be true. To make your life easier, you probably do want the patterns to be all of the same length, which for 16-beat against 12-beat would require first converting everything into

  $ perl -MMath::BigInt -E 'say Math::BigInt->new(16)->blcm(12)'
  48

beat length patterns. See also the upsize function in Music::RhythmSet::Util.

to_ly [ param ]

Returns an array reference of strings that contain the replay log of each voice formatted for LilyPond.

  use File::Slurper 'write_text';
  my $i = 0;
  for my $str ($set->to_ly->@*) {
      write_text("noise.$i.ly", $str);
      $i++;
  }

These files can then be included from another LilyPond file:

  \version "2.18.2"
  lino = \relative c' { \include "noise.0.ly" }
  lipa = \relative c' { \include "noise.1.ly" }
  lire = \relative c' { \include "noise.2.ly" }
  zgike = {
    \new StaffGroup <<
      \new Staff \lino
      \new Staff \lipa
      \new Staff \lire
    >>
  }
  \score { \zgike \layout { } \midi { } }

The LilyPond "Notation Reference" documentation may be helpful.

The param can include a voice element; this allows the dur, note, and rest parameters of the individual voices to be customized. dur, note, and rest can also be set at the top level to change the defaults for all the voices, unless there is a more specific setting for a voice. maxm limits the output to a particular measure number.

  my $ret = $set->to_ly(
      # quarter notes for all voices
      dur => 4,
      # voice specifics
      voice => [
          { note => 'b' },
          { note => 'a', rest => 's' },
          { note => 'c' }
      ]
  );
to_midi [ param ]

Returns a MIDI::Opus object containing tracks for each of the voices. Use the write_to_file call of that object to produce a MIDI file.

Parameters accepted include format (probably should be 1), ticks, and track. track allows parameters for the Music::RhythmSet::Voice to_midi call to be passed to a specific voice. These can also be specified in this to_midi call to apply to all the tracks:

  my $opus = $set->to_midi(
      chan  => 9,
      tempo => 640_000,
      track => [ {}, { note => 67 }, { note => 72 } ]
  );
  $opus->write_to_file("noise.midi");

Note that the MIDI events are by default duplicated to save memory. If the opus track events are adjusted (e.g. via the events_r method of MIDI::Track) the individual events must be made unique prior to modification so that a change is not replicated into the repeats of that event. With embig enabled (since version 0.04) the MIDI events will be de-duplicated.

  ...->to_midi( embig => 1, ...

MIDI::Event documents most of the values the track parameters can take. maxm will limit the output to the given number of measures.

to_string [ param ]

Converts the replay logs of the voices (if any) into a custom text format. See the to_string method of Music::RhythmSet::Voice for details.

BUGS

None known.

SEE ALSO

MIDI, Music::AtonalUtil, Music::RecRhythm

"The Geometry of Musical Rhythm" by Godfried T. Toussaint.

COPYRIGHT AND LICENSE

Copyright 2021 Jeremy Mates

This program is distributed under the (Revised) BSD License: https://opensource.org/licenses/BSD-3-Clause