Musical Rhythms with Math in Perl
Furnished content.
Let’s talk about music programming! There are a million aspects to this subject, but today, we’ll touch on generating rhythmic patterns with mathematical and combinatorial techniques. These include the generation of partitions, necklaces, and Euclidean patterns.Stefan and J. Richard Hollos wrote an excellent little book called “Creating Rhythms” that has been turned into C, Perl, and Python. It features a number of algorithms that produce or modify lists of numbers or bit-vectors (of ones and zeroes). These can be beat onsets (the ones) and rests (the zeroes) of a rhythm. We’ll check out these concepts with Perl.For each example, we’ll save the MIDI with the MIDI::Util module. Also, in order to actually hear the rhythms, we will need a MIDI synthesizer. For these illustrations, fluidsynth will work. Of course, any MIDI capable synth will do! I often control my eurorack analog synthesizer with code (and a MIDI interface module).Here’s how I start fluidsynth on my mac in the terminal, in a separate session. It uses a generic soundfont file (sf2) that can be downloaded here (124MB zip).
fluidsynth -a coreaudio -m coremidi -g 2.0 ~/Music/soundfont/FluidR3_GM.sf2use MIDI::RtMidi::FFI::Device ();my = RtMidiIn->new;my = RtMidiOut->new;print "Input devices:\n";->print_ports;print "\n";print "Output devices:\n";->print_ports;print "\n";fluidsynth is alive and ready for interaction.Okay, on with the show!First-up, let’s look at partition algorithms. With the part() function, we can generate all partitions of n, where n is 5, and the “parts” all add up to 5. Then taking one of these (say, the third element), we convert it to a binary sequence that can be interpreted as a rhythmic phrase, and play it 4 times.#!/usr/bin/env perluse strict;use warnings;use Music::CreatingRhythms ();my = Music::CreatingRhythms->new;my = ->part(5);# [ [ 1, 1, 1, 1, 1 ], [ 1, 1, 1, 2 ], [ 1, 2, 2 ], [ 1, 1, 3 ], [ 2, 3 ], [ 1, 4 ], [ 5 ] ]my $p = ->[2]; # [ 1, 2, 2 ]my = ->int2b([$p]); # [ [ 1, 1, 0, 1, 0 ] ]use MIDI::Util qw(setup_score);my = setup_score(bpm => 120, channel => 9);for (1 .. 4) { for my (->[0]->@*) { if () { ->n('en', 40); } else { ->r('en'); } }}->write_score('perldotcom-1.mid');fluidsynth like this:fluidsynth -i ~/Music/soundfont/FluidR3_GM.sf2 perldotcom-1.midn with the duplicates removed.”Okay. Well, the 7 partitions of 5 are:[[1, 1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 3], [1, 2, 2], [1, 4], [2, 3], [5]]And the 16 compositions of 5 are:[[1, 1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 2, 1], [1, 1, 3], [1, 2, 1, 1], [1, 2, 2], [1, 3, 1], [1, 4], [2, 1, 1, 1], [2, 1, 2], [2, 2, 1], [2, 3], [3, 1, 1], [3, 2], [4, 1], [5]]That is, the list of compositions has, not only the partition [1, 2, 2], but also its variations: [2, 1, 2] and [2, 2, 1]. Same with the other partitions. Selections from this list will produce possibly cool rhythms.Here are the compositions of 5 turned into sequences, played by a snare drum, and written to the disk:use Music::CreatingRhythms ();use MIDI::Util qw(setup_score);my = Music::CreatingRhythms->new;my = ->compm(5, 3); # compositions of 5 with 3 elementsmy = ->int2b();my = setup_score(bpm => 120, channel => 9);for my (->@*) { for my (@) { if () { ->n('en', 40); # snare patch } else { ->r('en'); } }}->write_score('perldotcom-2.mid');timidity.cfg configuration file, this would be:timidity -c ~/timidity.cfg -Od perldotcom-2.midtimidity -c ~/timidity.cfg perldotcom-2.mid -Ow -o - | ffmpeg -i - -acodec libmp3lame -ab 64k perldotcom-2.mp3use MIDI::Util qw(setup_score);use Music::CreatingRhythms ();my = Music::CreatingRhythms->new;my = ->compm(4, 2); # snaremy = ->int2b();my = ->compm(4, 3); # kickmy = ->int2b();my = setup_score(bpm => 120, channel => 9);for (1 .. 8) { # repeats my = ->[ int rand @ ]; my = ->[ int rand @ ]; for my $i (0 .. $#) { # pattern position my @notes = (42); # hi-hat every time if (->[$i]) { push @notes, 40; } if (->[$i]) { push @notes, 36; } ->n('en', @notes); }}->write_score('perldotcom-3.mid');
Image from The Geometry of Musical RhythmRhythm necklaces are circular diagrams of equally spaced, connected nodes. A necklace is a lexicographical ordering with no rotational duplicates. For instance, the necklaces of 3 beats are [[1, 1, 1], [1, 1, 0], [1, 0, 0], [0, 0, 0]]. Notice that there is no [1, 0, 1] or [0, 1, 1]. Also, there are no rotated versions of [1, 0, 0], either.So, how many 16 beat rhythm necklaces are there?my = ->neck(16);print scalar @, "\n"; # 4116 of 'em!8 instead, pull a random choice, and play the pattern with a percussion instrument.use MIDI::Util qw(setup_score);use Music::CreatingRhythms ();my = shift || 75; # clavesmy = Music::CreatingRhythms->new;my = ->neck(8);my = ->[ int rand @ ];my = setup_score(bpm => 120, channel => 9);for (1 .. 4) { # repeats for my (@) { # pattern position if () { ->n('en', ); } else { ->r('en'); } }}->write_score('perldotcom-4.mid');use MIDI::Util qw(setup_score);use Music::CreatingRhythms ();my = Music::CreatingRhythms->new;my = ->neck(8);my = ->[ int rand @ ];my = ->[ int rand @ ];my = ->[ int rand @ ];my = setup_score(bpm => 120, channel => 9);for (1 .. 4) { # repeats for my $i (0 .. $#) { # pattern position my @notes; if (->[$i]) { push @notes, 75; # claves } if (->[$i]) { push @notes, 63; # hi_conga } if (->[$i]) { push @notes, 64; # low_conga } ->n('en', @notes); }}->write_score('perldotcom-5.mid');P that are filled with a number of beats Q that is less than or equal to P. They are named for Euclid because they are generated by applying the “Euclidean algorithm,” which was originally designed to find the greatest common divisor (GCD) of two numbers, to distribute musical beats as evenly as possible.use MIDI::Util qw(setup_score);use Music::CreatingRhythms ();my = Music::CreatingRhythms->new;my = 16;my = ->rotate_n(4, ->euclid(2, )); # snaremy = ->euclid(2, ); # kickmy = ->euclid(11, ); # hi-hatsmy = setup_score(bpm => 120, channel => 9);for (1 .. 4) { # repeats for my $i (0 .. - 1) { # pattern position my @notes; if (->[$i]) { push @notes, 40; # snare } if (->[$i]) { push @notes, 36; # kick } if (->[$i]) { push @notes, 42; # hi-hats } if (@notes) { ->n('en', @notes); } else { ->r('en'); } }}->write_score('perldotcom-6.mid');euclid() method distributes a number of beats, like 2 or 11, over the number of beats, 16. The kick and snare use the same arguments, but the snare pattern is rotated by 4 beats, so that they alternate.edit: Programming/Perl/auto___musical_rhythms_with_math_in_perl.wikieditish...