Twelve-bar Jazz Practice

Tags:

I stumbled upon this chart of 17 blues-jazz (jazz-blues?) chord progressions by Dan Haerle. Naturally, being a programmer-musician, I made a program with which to practice improv skills. (tl;dr: blues-progressions & audio below)

UPDATE: I made a phone-friendly, Mojolicious web UI of this engine. Woo!

UPDATE #2: I made a powerful cousin to this practice aid, Rock::Tool - Dig it!

This is an account of the program code itself. For examples of a couple runs, see the MP3s below.

Here is the traditional Perl preamble:

#!/usr/bin/env perl
use strict;
use warnings;

Followed by the libraries and their functionality, that we will use:

use Data::Dumper::Compact qw(ddc);              # verbose output
use Getopt::Long qw(GetOptions);                # commandline options
use MIDI::Drummer::Tiny ();                     # MIDI score operations
use MIDI::Util qw(set_chan_patch midi_format);  # "
use Music::Cadence ();                          # ending musical cadence
use Music::Chord::Note ();                      # notes of a chord
use Music::MelodicDevice::Transposition ();     # transpose a note
use Music::Note ();                             # enharmonic notes

With those things declared, we define defaults, and then grab their argument values from the command-line:

my %opts = (
    tonic   => 'C',     # note to transpose things to
    octave  => 4,       # octave of chord notes
    patch   => 5,       # 0=piano, etc general midi
    bpm     => 90,      # beats per minute
    bars    => 12,      # number of 4/4 bars
    repeat  => 1,       # number of times to repeat
    percent => 25,      # maximum half-note percentage
    hihat   => 'pedal', # pedal, closed, open
    drums   => 0,       # to drum, or not to drum?
    bass    => 0,       # to have a parallel bass or not
    simple  => 0,       # don't randomly choose a transition
    verbose => 0,
);
GetOptions( \%opts, 
    'tonic=s',
    'octave=i',
    'patch=i',
    'bpm=i',
    'bars=i',
    'repeat=i',
    'percent=i',
    'hihat=s',
    'drums',
    'bass',
    'simple',
    'verbose',
);

Next we find our drummer (joke-omitted):

my $d = MIDI::Drummer::Tiny->new(
    file   => "$0.mid",
    bars   => $opts{bars},
    bpm    => $opts{bpm},
    reverb => 15,
);

my @bass_notes; # global accumulator for the optional bass

This includes everything we will need to compose a MIDI score.

But we need to synchronize the patterns so that they are played simultaneously:

$d->sync(
    \&drums,
    \&chords,
    \&bass,
);

You can alter the drums in the following ways: Have a cheesy drumline or have a steady, quarter-note hi-hat pulse. More on that later.

Finally, write the MIDI out to a file called "blues-progressions.mid":

$d->write;

Here are the drums:

sub drums {
    if ($opts{drums}) {
        $d->metronome44swing($d->bars * $opts{repeat});
        $d->note($d->whole, $d->kick, $d->ride1);
    }
    else {
        my $metronome = $opts{hihat} . '_hh';
        $d->count_in({
            bars  => $d->bars * $opts{repeat},
            patch => $d->$metronome(),
        });
    }
}

This says either play the metronome44swing() method, followed by a kick + ride whole-note, OR play a steady hi-hat (pedal, closed, or open).

And here is the bass subroutine:

sub bass {
    if ($opts{bass}) {
        set_chan_patch($d->score, 1, 35);

        for (1 .. $opts{repeat}) {
            for my $n (@bass_notes) {
                $n =~ s/^([A-G][#b]?)\d$/$1 . 3/e;
                $d->note($d->whole, midi_format($n));
            } 
        }
    }
}

For this, we play notes in the 3rd octave with the fretless bass patch (35).

Ok. So much for the easy bits... Next up is the meat of what we actually want to practice: Soloing over (or playing a bass-line to) the generated chords.

sub chords {
    set_chan_patch($d->score, 0, $opts{patch});

This sets the MIDI channel and the patch we want to use.

Next are instantiations of various music programming objects. One for transposition, one for finding the notes of chords, and one for ending the progression on a cadence chord:

    my $md = Music::MelodicDevice::Transposition->new;
    my $cn = Music::Chord::Note->new;
    my $mc = Music::Cadence->new(
        key    => $opts{tonic},
        octave => $opts{octave},
        format => 'midi',
    );

Now we may wish to transpose things from the default key of C. This is done by indicating the tonic on the command-line invocation.

Here, C is equal to zero for the scale() method, meaning that we do not want to transpose. Everything else goes to 11. That is, C=0 .. B=11.

    my $transpose = $cn->scale($opts{tonic});

Next, we need the actual lists of chords to use:

    my @bars = bars();
    my %net  = net();

The bar lists and network transitions are static. I took them from:

This is freely available in the Jazz Handbook.

We will be collecting MIDI note specifications (duration + pitch + octave) with this variable:

    my @specs;

So for each bar defined...

    for my $n (0 .. $d->bars - 1) {

We collect the chords of a bar (and there are 12 bars, given by the bars() function).

        my @pool = $bars[ $n % @bars ]->@*;

If we are running with the simple command-line option, use the first defined chord in each bar pool. This results in the standard progression:

C7 C7 C7 C7 / F7 F7 C7 C7 / G7 G7 C7 C7

If we are not being simple, then get a random chord from that list, possibly transpose it, and finally get the notes that define the chord.

        my $chord = $opts{simple} ? $pool[0] : $pool[ int rand @pool ];
        my $new_chord = transposition($transpose, $chord, $md);
        my @notes = $cn->chord_with_octave($new_chord, $opts{octave});

Keep track of the chord name(s) for verbose mode:

        my $names = $new_chord;

We use an individual MIDI note specification temporarily inside the loop:

        my @spec;

Next, we want to add either a whole or two half notes. Whether to add a half is determined by the simple and percent command-line options:

        if (!$opts{simple} && $opts{percent} >= int(rand 100) + 1) {

The "percent" is really a "possible maximum." The default is 25%. This means that a half-note will be chosen a maximum of 25% of the time. Could be less!

Anyway, if we are not simple and the percent is right, accumulate the computed chord notes, and re-collect for the next half-note:

            push @spec, [ $d->half, @notes ];

            @pool = $net{$chord}->@*;
            $chord = $pool[ int rand @pool ];
            my $new_chord = transposition($transpose, $chord, $md);
            @notes = $cn->chord_with_octave($new_chord, $opts{octave});

            $names .= "-$new_chord";

            push @spec, [ $d->half, @notes ];
        }

Notice that the @pool is defined by the net of the chord, rather than the nth bar. This expands the possiblities enormously.

Ok, if we have not decided to divide into half-notes, just add a whole-note to the score:

        else {
            push @spec, [ $d->whole, @notes ];
        }

Tell us what we've won, Bob!

        printf '%*d. %13s: %s', length($opts{bars}), $n + 1, $names, ddc(\@spec)
            if $opts{verbose};

An example of the verbose output is shown below.

Now we accumulate the note specification (either 2 halves or 1 whole):

        push @specs, @spec;
    }

Ok we made it! All chords accounted for! Next up is to actually add them to the score. This is done in a for loop for the number of repeats we (may or may not) have requested:

    for (1 .. $opts{repeat}) {
        $d->note(midi_format(@$_)) for @specs;
    }

(Note that the midi_format() function is used to make sure all the sharps and flats are reconizable to MIDI-Perl.)

Finally, the first chord of an imperfect cadence is added to the end:

    my $cadence = $mc->cadence(type => 'imperfect');  # 2 chords, but...
    $d->note($d->whole, $cadence->[0]->@*);           # only use the 1st
}

For reference, here is the chord transposition subroutine:

sub transposition {
    my ($transpose, $chord, $md) = @_;
    if ($transpose && $chord =~ /^([A-G][#b]?)(.*)$/) { 
        my $note = $1;
        my $flav = $2;
        my $transposed = $md->transpose($transpose, [$note]);
        (my $new_note = $transposed->[0]) =~ s/^([A-G][#b]?).*$/$1/;
        $new_note = accidental($new_note); # convert sharp to flat
        $chord = $new_note;
        $chord .= $flav if $flav;
    }
    return $chord;
}

And here is the note/chord "flattener" subroutine:

sub accidental {
    my ($string) = @_; # note or chord name
    if ($string =~ /^([A-G]#)(.*)?$/) { # is the note sharp?
        my $note = $1;
        my $flav = $2;
        my $mn = Music::Note->new($note, 'isobase');
        $mn->en_eq('b'); # convert to flat
        $string = $mn->format('isobase');
        $string .= $flav if $flav;
    } 
    return $string;
}

And here are the possible chords (taken from the chart above) for the seventh-chord, 12-bar blues:

sub bars {
    no warnings qw(qw);
    return (                                  # bar
        [qw( C7 CM7 C#m7                  )], #  1
        [qw( C7 F7  Bm7  FM7    C#m7      )], #  2
        [qw( C7 Am7 Em7  BM7              )], #  3
        [qw( C7 Gm7 Dbm7 AbM7             )], #  4
        [qw( F7 FM7                       )], #  5
        [qw( F7 Bb7 Gbm7 Gbdim7 Fm7       )], #  6
        [qw( C7 Em7 EbM7 EM7              )], #  7
        [qw( C7 A7  Bb7  Ebm7   Em7       )], #  8
        [qw( G7 D7  Dm7  Ab7    DbM7 DM7  )], #  9
        [qw( G7 F7  Abm7 Db7    Dm7  DbM7 )], # 10
        [qw( C7 Em7 FM7                   )], # 11
        [qw( C7 G7  Dm7  Ab7    Abm7 DM7  )], # 12
    );
}

And here is the transition network for each chord to a list of connected chords:

sub net {
    no warnings qw(qw);
    return (
        'A7'     => [qw( Ebm7 D7 Dm7 Ab7 DM7 Abm7 )],
        'Ab7'    => [qw( DbM7 Dm7 G7 )],
        ...
        'Gbdim7' => [qw( Em7 )],
        'Gm7'    => [qw( C7 Gb7 )],
    );
}

Yes, every one is not used (yet)...

Ok now for the audio!

Sometimes I use a raw midi file, played by timidity, to practice with. But sometimes after curating a few generated runs, I import those files into my DAW, possibly reorganize things, and render with superior instrumentation (and possibly drums, too).

Below are various timidity renderings with their command-line options.

Btw, in vim I do this type of thing:

:!rm -f %.mid ; perl % --verbose --simple --bass ; timidity %.mid

But anyway:

$ perl blues-progressions --verbose --percent=20 --hihat=closed --repeat=2
$ timidity blues-progressions.mid

This can be abbreviated, by the way:

$ perl blues-progressions --ver --per=20 --hih=closed --rep=2

And --verbose tells you what chords (and even notes) are generated. So that's really the way to practice - be verbose.

For me, I'll practice scales and arpeggios over --simple for a few --repeated phrases. Then expand harmonically:

$ perl blues-progressions --verbose --percent=0 --tonic=Bb

Which looks and sounds like this:

1.  BbM7: [ [ 'wn', 'Bb4', 'D5', 'F5', 'A5' ] ]
2.   Am7: [ [ 'wn', 'A4', 'C5', 'E5', 'G5' ] ]
3.   Bb7: [ [ 'wn', 'Bb4', 'D5', 'F5', 'Ab5' ] ]
4.   Fm7: [ [ 'wn', 'F4', 'Ab4', 'C5', 'Eb5' ] ]
5.   Eb7: [ [ 'wn', 'Eb4', 'G4', 'Bb4', 'Db5' ] ]
6.   Eb7: [ [ 'wn', 'Eb4', 'G4', 'Bb4', 'Db5' ] ]
7.   Bb7: [ [ 'wn', 'Bb4', 'D5', 'F5', 'Ab5' ] ]
8.   Bb7: [ [ 'wn', 'Bb4', 'D5', 'F5', 'Ab5' ] ]
9.   Cm7: [ [ 'wn', 'C4', 'Eb4', 'G4', 'Bb4' ] ]
10.   F7: [ [ 'wn', 'F4', 'A4', 'C5', 'Eb5' ] ]
11.  Dm7: [ [ 'wn', 'D4', 'F4', 'A4', 'C5' ] ]
12.  Cm7: [ [ 'wn', 'C4', 'Eb4', 'G4', 'Bb4' ] ]

For the ambitious practitioner, we can speed up the tempo and make the progression "vertical" - i.e. chords seemingly stacked on top of each other.

$ perl blues-progressions --percent=100 --patch=0 --bpm=120

If you really want, there can be a cheesy drum pattern, too:

$ perl blues-progressions --drums

Some Steely Dan changes, man... But some really lame drums! :D

Anyway, like I say, I repeat 8 or even 12 times or more, import into my DAW for better patches, then I practice! :)