NAME

Music::RecRhythm - rhythms within rhythms within rhythms

SYNOPSIS

use Music::RecRhythm;

my $one = Music::RecRhythm->new( set => [qw(2 2 1 2 2 2 1)] );

my $two = $one->rebuild;  # clone the (original) object

$one->is_silent(1);       # silent (but present)

$one->next($two);         # link for recursion
$two->prev;               # now returns $one

$one->recurse( sub { ... } );

DESCRIPTION

A utility module for recursive rhythm construction, where a set is defined as an array reference of positive integers (beats). Multiple such objects may be linked through the next attribute, which the recurse method follows. Each next rhythm is played out in full for each beat of the parent rhythm, though whether these events are simultaneous or strung out in time depends on the callback code provided to recurse.

CONSTRUCTOR

The new method accepts any of the "ATTRIBUTES". The set attribute must be supplied.

ATTRIBUTES

count

A read-only count of the beats in the set. Updated when set is changed.

extra

Use this to stash anything extra you need to associate with an instance, e.g. a MIDI track, some other object, etc.

is_silent

Boolean as to whether or not the callback function of recurse will be invoked for beats of the set. False by default. Recursion will continue through silent objects as per usual; is_silent merely disables calling the callback, so "silent, but present" may be a more accurate term for this attribute.

next

Optional next object to recurse into. While often a Music::RecRhythm object, any object that supports the necessary method calls could be used. Recursion will stop should this attribute be undef (the default). Probably should not be changed in the middle of a recurse call.

Calls prev in the given object to setup a bi-directional chain.

prev

Previous object, if any. Will be automatically set by a next call.

set

An array reference of one or more positive integers (a.k.a. beats). This attribute must be supplied at construction time.

sum

A read-only sum of the beats in the set. Updated when set is changed.

METHODS

audible_levels

Returns the number of audible levels (those that do not set is_silent) recursion will take place over. See also levels.

beatfactor

Least common multiple of the beats and sum of the beats such that the durations at each level of recursion work out to the same overall duration. Hopefully. Uses Math::BigInt though downgrades that via numify, so integers larger than what perl can handle internally may be problematic. See duration under "CALLBACK" for how this information is exposed during recursion.

levels

Returns the number of levels recursion will take place over. May be useful prior to a recurse call if an array of MIDI tracks (one for each level) need be created, or similar. Note that the actual level numbers may be discontiguous if any of the objects enable is_silent, hence also the audible_levels method.

recurse coderef extra

Iterates the beats of the set and recurses through every next for each beat, calling the coderef unless is_silent is true for the object. extra gets passed along to the callback coderef.

See "CALLBACK" for the arguments the callback sub will be passed.

validate_set set

Class method. Checks whether a set really is a list of positive numbers (the int truncation is done elsewhere). The empty set is not allowed. Used internally by the set attribute.

Music::RecRhythm->validate_set("cat")      # 0
Music::RecRhythm->validate_set([qw/1 2/])  # 1

CALLBACK

The coderef called during recurse (only for not-silenced objects) is passed four or more arguments. First, the Music::RecRhythm object, second, a hash reference containing various parameters (listed below), third, extra, a scalar that can be whatever you want it to be (reference, object, whatever) and fourth a list of the durations of the recursion stack where the last element of that list is the current $param{beat}.

$one->recurse(
    sub {
        my ( $rset, $param, $extra, @beats ) = @_;
        ...
    },
    $this_is_passed_as_dollarextra
);

The param passed as the second argument (which are read-write (though probably should not be changed on the fly)) include:

audible_level

Level of recursion, counting from 0 but only incremented when is_silent is not set. See level for the exact level of recursion.

beat

The current beat, a member of the set at the given index.

duration

A calculated duration based on the beat and beatfactor such that each beat in combination with the others of the set lasts the duration of the ancestor set. A fudge factor will likely be necessary to change the least common multiples to something suitable for MIDI durations:

$one->recurse(
    sub {
        my ( $rset, $param ) = @_;
        my $duration = $param->{duration} * 128;
        ...
    },
);

Another option is to multiply the current and ancestor @beats together, again with a fudge factor:

use List::Util qw(reduce);
$one->recurse(
    sub {
        my ( $rset, $param, undef, @beats ) = @_;
        my $duration = reduce { $a * $b } @beats, 128;
        ...
    },
);

though this may produce different results and adds work.

index

Index of the current beat in the set, numbered from 0 on up.

level

Level of recursion, 0 for the first level, 1 for the second, and so forth. The level numbers will have gaps if is_silent is set. See audible_level if that is a problem.

next

The next object, or undef should this be the lowest level of recursion. Probably should not be changed on the fly.

set

Array reference containing the beats of the current set, of which beat is the current at index index.

There are, again, example callbacks in the eg/ and t/ directory code.

BUGS

Reporting Bugs

Please report any bugs or feature requests to bug-music-recrhythm at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Music-RecRhythm.

Patches might best be applied towards:

https://github.com/thrig/Music-RecRhythm

Known Issues

  • beatfactor may still be buggy, and does not produce minimum factors in various cases (see t/200-recursion-see-recursion.t). A fudge factor to get the appropriate MIDI duration for a specific set of rhythm sets will likely be necessary (see eg/rhythm2midi).

  • Loops created with next calls will cause various methods to then run forever. If this is a risk for generated code, wrap these calls with alarm to abort them should they run for too long (or add loop detection somehow (or don't create loops via next calls, sheesh!)).

  • next should be checked via isa or somesuch to audit that passed objects are suitable to be used in beatfactor and recurse.

  • prev is primitive and may have problems on object destruction; this is an unknown as my scripts that use this module exit the entire perl process when they are done.

SEE ALSO

MIDI or MIDI::Simple may assist in the callback code to produce MIDI during the recursion. Consult the eg/ and t/ directories under this module's distribution for example code.

Music::Voss is a similar if different means of changing a rhythm over time.

Various scripts under https://github.com/thrig/compositions use this module, in particular "Three Levels of the Standard Pattern".

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

AUTHOR

thrig - Jeremy Mates (cpan:JMATES) <jmates at cpan.org>

COPYRIGHT AND LICENSE

Copyright (C) 2016-2017 by Jeremy Mates

This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:

http://www.perlfoundation.org/artistic_license_2_0