Help language development. Donate to The Perl Foundation

## EuclideanRhythm cpan:JSTOWE last updated on 2021-03-12

lib/EuclideanRhythm.pm
```use v6;

=begin pod

EuclideanRhythm - create rhythmic patterns based on the Euclidean algorithm

=begin code

use EuclideanRhythm;

my \$r = EuclideanRhythm.new(slots => 16, fills => 7);

for \$r.list {
# do something if the value is True
}

=end code

This provides an implementation of the algorithm described in
L<http://cgm.cs.mcgill.ca/~godfried/publications/banff.pdf> which is
in turn derived from a Euclidean algorithm.  Simply put it provides a
simple method to distribute a certain number of "fills" (hits, beats,
notes or whatever you want to call them,) as evenly as possibly among a
number of "slots".  The linked paper describes how, using this method,
you can approximate any number of common rhythmic patterns (as well as
not so common ones.)

You could of course use it for something other than generating
musical rhythms: any requirement where a number of events need to be
distributed over a fixed number of slots may find this useful.

method new(Int :\$slots!, Int :\$fills!)

The constructor has two required named arguments. The total number
of C<slots> to be filled, and the number of C<fills> which are to be
distributed into them.  The number of fills should not be larger than
the number of slots.  The C<slots> could be thought of as being the total
number of "beats" in a measure (or bar,) but does not necessarily have to
be an even number at all. Obviously if you are using more than one object
to create a rhythmic structure you may want to either arrange things such
that either the C<slots> of each have some common multiple or adjust the
timing of the slots such that it can resolve over time to the same length.

method once()

This returns a C<slots> sized array, filled appropriately.

method list()

This is an infinite lazy list of the calculated pattern and is probably
the more useful interface, obviously this is produced as fast as
possible so if you are using it to generate events which you care about
the timing of then you will need to take care of the timing yourself.

=end pod

class EuclideanRhythm {
has Int \$.slots is required;
has Int \$.fills is required;

has @!count;
has @!remainder;
has \$!level;

method !bitmap(Int \$level, @count, @remainder ) {

if \$level == -1 {
take False;
}
elsif  \$level == -2 {
take True;
}
else {
if @remainder[\$level] != 0 {
self!bitmap(\$level - 2, @count, @remainder);
}
for 0 .. @count[\$level] - 1 {
self!bitmap(\$level - 1, @count, @remainder);
}
}
}

submethod BUILD(Int :\$!slots, Int :\$!fills where * <= \$!slots, Int :\$rotate = 0) {
my \$divisor = \$!slots - \$!fills;
@!remainder = \$!fills;
\$!level = 0;
while @!remainder[\$!level] > 1 {
@!count[\$!level] = \$divisor / @!remainder[\$!level];
@!remainder[\$!level+1] = \$divisor % @!remainder[\$!level];
\$divisor = @!remainder[\$!level];
\$!level++;
}
@!count[\$!level] = \$divisor;

}

method list() {
gather {
loop {
self!bitmap(\$!level, @!count, @!remainder);
}
}
}

method once() {
do gather { self!bitmap(\$!level, @!count, @!remainder) };
}
}

# vim: expandtab shiftwidth=4 ft=raku
```