Algorithmic Music with Perl

Gene Boggs


Brian Eno - excerpt from "Music For Airports"

TPRC 2023 • Toronto, ON

Let's talk about music! The first part will cover concepts, which I will try to whip-though, so we can get to the algorithms. If you want to follow along or reread what just happened, these slides are live at the link on the bottom of the pages.

What this *is* about

...

What this is *not* about

...

Sorry!

This used to have a bunch of descriptions, types, and techniques. But it took way too long! See the OLD/ directory, in the github repo.

Why Make Algorithmic Music?

...

Fundamental Questions

These are questions to answer if we want to follow traditional theory. But if we don't care about traditions, then anything goes. And MIDI is probably not your thing. For instance, the idea of phrase development itself is controversial. E.g. Brian Eno, Primus guitar solos.

Task::MusicBundle Modules

App::MusicTools Csound Guitar::Scale MIDI Music::Bassline::Generate MIDI::Chord::Guitar MIDI::Drummer::Tiny MIDI::Morph MIDI::Pitch MIDI::Praxis::Variation MIDI::Simple::Drummer MIDI::SoundFont MIDI::Tools MIDI::Trans MIDI::Tweaks MIDI::Util Music::AtonalUtil Music::Cadence Music::Canon Music::ChordBot Music::Chord::Namer Music::Chord::Note Music::Chord::Positions Music::Chord::Progression Music::Chord::Progression::Transform Music::Duration Music::Duration::Partition Music::Gestalt Music::Guidonian Music::Harmonics Music::MelodicDevice::Inversion Music::MelodicDevice::Transposition Music::MelodicDevice::Ornamentation Music::NeoRiemannianTonnetz Music::Note Music::Note::Frequency Music::Note::Role::Operators Music::PitchNum Music::RecRhythm Music::RhythmSet Music::ScaleNote Music::Scales Music::Tempo Music::Tension Music::ToRoman Music::VoiceGen Music::Voss Text::Chord::Piano

Linked modules are those mentioned in this presentation.

Basic Algorithms 1 of 2

Setup, Sync, & Write

use MIDI::Util qw(setup_score);

my $score = setup_score();  # Default piano, volume, bpm, etc

$score->synch(
  sub { bass($score) },
  sub { treble($score) },
);

$score->write_score("$0.mid"); 
...

Basic Algorithms 2 of 2

sub bass {
  my ($score) = @_;

  for my $note (qw(C3 E3 C3 G3)) {  # <- Static notes
    $score->n('wn', $note);
  }
}

sub treble {
  my ($score) = @_;

  for my $note (qw(E4 E4 F4 G4 G4 F4 E4 D4 C4 C4 D4 E4 E4 D4 D4)) {
    $score->n('qn', $note);
  }
} 
...

Audio Example 1

> timidity -c timidity.cfg -A10a code/2-random.pl.mid -Ow -o - \
  | ffmpeg -i - -acodec libmp3lame -ab 64k audio/2-random.mp3 
...

Basic Algorithms 1 of 3

Use random pitches instead of static

use MIDI::Util qw(setup_score set_chan_patch); # New import
use Music::Scales qw(get_scale_MIDI);          # New module

my $score = setup_score(bpm => 120);           # New arg

$score->synch(
  sub { bass($score) },
  sub { treble($score) },
) for 1 .. 4;                                  # Loop

$score->write_score("$0.mid"); 
...

Basic Algorithms 2 of 3

sub bass {
  my ($score) = @_;

  set_chan_patch($score, 0, 35);       # Fretless on channel 0

  my @pitches = get_scale_MIDI('C', 2, 'pentatonic');

  for my $n (1 .. 4) {
    my $pitch = $pitches[int rand @pitches];  # Random pitch
    $score->n('hn', $pitch);                  # Add note to score
  }
} 
...

Basic Algorithms 3 of 3

sub treble {
  my ($score) = @_;

  set_chan_patch($score, 1, 0);        # Piano on ch 1

  my @pitches = (
    get_scale_MIDI('C', 4, 'major'),
    get_scale_MIDI('C', 5, 'major'),   # <- 2 octaves
  );

  for my $n (1 .. 4) {
    my $pitch = $pitches[int rand @pitches];
    $score->n('qn', $pitch);
  }
} 
...

Audio Example 2

...

Basic Algorithms 1 of 2

Play a list of named chords held for a whole note each

use MIDI::Util qw(setup_score midi_format);  # New import
use Music::Scales qw(get_scale_MIDI);
use Music::Chord::Note ();                   # New module

my $score = setup_score();

$score->synch(
  sub { chords($score) },
  #sub { treble($score) },                   # YMMV!
);

$score->write_score("$0.mid"); 
...

Basic Algorithms 2 of 2

sub chords {
  my ($score) = @_;

  my $mcn = Music::Chord::Note->new;

  for my $named (qw(Cm7 F7 BbM7 EbM7 Adim7 D7 Gm)) {
    my @chord = $mcn->chord_with_octave($named, 4);

    @chord = midi_format(@chord);
    # [ C4 Ds4 G4 As4 ], ... [ G4 As4 D5 ], etc.

    $score->n('wn', @chord);
  }
} 
...

Audio Example 3

...

Groove-Tool

...

Figured Bass Part 1 of 2

Phrase generator for rhythmic motifs

my $phrase = Music::Duration::Partition->new(
  size    => 3,                   # Number of beats to render
  pool    => [qw(hn dqn qn en)],
  weights => [   1, 1,  2, 2  ],  # Optional
  groups  => [   1, 1,  1, 2  ],  # Optional
);

my @motifs = $phrase->motifs(2); 
...

Figured Bass Part 2 of 2

my @pitches = get_scale_MIDI($note, $octave, $scale);

my $voice = Music::VoiceGen->new(
  pitches   => \@pitches,
  intervals => [qw/-4 -3 -2 2 3 4/],  # Allowed interval jumps
);

# main phrase voices
my @voices1 = map { $voice->rand } $motifs[0]->@*;

# ... more code ...

my @voices2 = map { $voice->rand } $motifs[1]->@*; 
Music::VoiceGen module by Jeremy Mates (AKA thrig) - I know you're out there!

Links

This Slideshow is on GitHub (and made with S5)

Perl Algorithmic Composition Tutorial

The #perl-music channel on irc.perl.org

The Groove-Tool, Rock-Tool and Jazz-Tool on GitHub

Shameless plug: My latest album "XI" - youtube, spotify, apple music, etc. and includes the partially algorithmic tune "Permutational" - Check it out!