#!/usr/bin/env perl

# Real Book #387
# Duke Ellington, Solitude (in Db)

use strict;
use warnings;

# local author directories:
use lib map { "$ENV{HOME}/sandbox/$_/lib" } qw(MIDI-Bassline-Walk MIDI-Chord-Guitar Music-Duration MIDI-Drummer-Tiny MIDI-Util);

use MIDI::Bassline::Walk;
use MIDI::Drummer::Tiny;
use MIDI::Chord::Guitar;
use MIDI::Util qw(set_chan_patch);

use constant VOICINGS   => undef; #"$ENV{HOME}/sandbox/MIDI-Chord-Guitar/share/midi-guitar-chord-voicings.csv"; # author voicings
use constant NEXT_CHORD => 1; # consider the next chord in note choice

my $bpm          = 70;
my $chord_patch  = 4;
my $bass_patch   = 35;
my $melody_patch = 66;

my $d = MIDI::Drummer::Tiny->new(
    file => $0 . '.mid',
    bpm  => $bpm,
    bars => 31,
);

my $channel = 0; # Internal increment

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

$d->write;

sub drums {
    $d->count_in(1);
    for my $n (1 .. $d->bars) {
        $d->note($d->quarter,        $d->ride1, $d->kick);
        $d->note($d->triplet_eighth, $d->ride1);
        $d->rest($d->triplet_eighth);
        $d->note($d->triplet_eighth, $d->ride1);
        $d->note($d->quarter,        $d->ride1, $d->snare);
        $d->note($d->triplet_eighth, $d->ride1, $d->kick);
        $d->rest($d->triplet_eighth);
        $d->note($d->triplet_eighth, $d->ride1);
    }
}

sub chords {
    set_chan_patch($d->score, $channel++, $chord_patch);

    my $mcg = MIDI::Chord::Guitar->new(VOICINGS ? (voicing_file => VOICINGS) : ());

    my $Dfmaj7  = $mcg->transform('Db3', 'maj7', 1);
    my $Bfm7    = $mcg->transform('Bb2', 'm7',   2);
    my $Ef7_1   = $mcg->transform('Eb3', '7',    0);
    my $Ef7_2   = $mcg->transform('Eb3', '7',    1);
    my $Efm7    = $mcg->transform('Eb3', 'm7',   1);
    my $Af7     = $mcg->transform('Ab2', '7',    3);
    my $Aaug7   = $mcg->transform('A2',  '7#5',  3);
    my $Df7     = $mcg->transform('Db3', '7',    1);
    my $Gfmaj7  = $mcg->transform('Gb2', 'maj7', 3);
    my $Gdim    = $mcg->transform('G2',  'dim',  1);
    my $Df6     = $mcg->transform('Db3', '6',    0);
    my $Df6Af   = ['44', @$Df6]; # * Impossible on the guitar?
    my $Afm7    = $mcg->transform('Ab2', 'm7',   2);
    my $Bf7f9   = $mcg->transform('Bb2', '7b9',  0);
    my $Afaug7  = $mcg->transform('Ab2', '7#5',  3);
    my $Edim    = $mcg->transform('E3',  'dim',  0);
    my $Dfmaj7F = ['41', @$Dfmaj7]; # * Impossible on the guitar?
    my $G7f5    = $mcg->transform('G2',  '7b5',  2);
    my $Bf7     = $mcg->transform('Bb2', '7',    3);

    $d->rest($d->whole); # while counting-in

    for my $n (1 .. 2) {
        $d->note($d->whole,   @$Dfmaj7);
        $d->note($d->quarter, @$Dfmaj7);
        $d->note($d->quarter, @$Dfmaj7);
        $d->note($d->half,    @$Bfm7);
        $d->note($d->whole,   @$Ef7_1);
        $d->note($d->whole,   @$Ef7_2);
        $d->note($d->whole,   @$Efm7);
        $d->note($d->whole,   @$Af7);
        $d->note($d->whole,   @$Dfmaj7);
        if ($n % 2) { # odd
            $d->note($d->half, @$Aaug7);
            $d->note($d->half, @$Af7);
        }
        else { # even
            $d->note($d->half, @$Dfmaj7);
            $d->note($d->half, @$Df7);
        }
    }
    $d->note($d->whole,   @$Gfmaj7);
    $d->note($d->whole,   @$Gdim);
    $d->note($d->whole,   @$Df6Af); # *
    $d->note($d->half,    @$Afm7);
    $d->note($d->half,    @$Df7);
    $d->note($d->whole,   @$Gfmaj7);
    $d->note($d->whole,   @$Gdim);
    $d->note($d->half,    @$Df6Af); # *
    $d->note($d->half,    @$Bf7f9);
    $d->note($d->half,    @$Efm7);
    $d->note($d->half,    @$Afaug7);
    $d->note($d->quarter, @$Dfmaj7);
    $d->note($d->quarter, @$Dfmaj7);
    $d->note($d->quarter, @$Efm7);
    $d->note($d->quarter, @$Edim);
    $d->note($d->half,    @$Dfmaj7F); # *
    $d->note($d->half,    @$G7f5);
    $d->note($d->whole,   @$Gfmaj7);
    $d->note($d->quarter, @$Ef7_1);
    $d->note($d->quarter, @$Ef7_1);
    $d->note($d->quarter, @$Ef7_1);
    $d->note($d->quarter, @$Bf7);
    $d->note($d->whole,   @$Efm7);
    $d->note($d->whole,   @$Af7);
    $d->note($d->whole,   @$Dfmaj7);
}

sub melody {
    set_chan_patch($d->score, $channel++, $melody_patch);

    $d->rest($d->half); # while counting-in

    $d->note($d->quarter, 'Af4');
    $d->note($d->quarter, 'Bf4');

    for my $n (1 .. 2) {
        $d->note($d->quarter,      'C5');
        $d->note($d->quarter,      'C5');
        $d->note($d->whole,        'C5');
        $d->note($d->half,         'Df5');
        $d->note($d->whole,        'Df5');
        $d->note($d->dotted_half,  'F4');
        $d->note($d->quarter,      'Bf4');
        $d->note($d->quarter,      'Bf4');
        $d->note($d->quarter,      'Af4');
        $d->note($d->dotted_half,  'Af4');
        $d->note($d->quarter,      'Gf4');
        $d->note($d->quarter,      'Gf4');
        $d->note($d->quarter,      'F4');
        $d->note($d->dotted_whole, 'F4');
        if ($n % 2) { # odd
            $d->note($d->quarter, 'Af4');
            $d->note($d->quarter, 'Bf4');
        }
        else { # even
            $d->rest($d->dotted_quarter);
            $d->note($d->eighth, 'Bf4');
        }
    }

    for my $n (1 .. 2) {
        for my $m (1 .. 2) {
            $d->note($d->eighth,         'Df5');
            $d->note($d->eighth,         'Bf4');
            $d->note($d->quarter,        'Df5');
            $d->note($d->dotted_quarter, 'Df4');
            $d->note($d->eighth,         'Bf4');
        }
        $d->note($d->triplet_quarter, 'Df5');
        $d->note($d->triplet_quarter, 'Bf4');
        $d->note($d->triplet_quarter, 'Df5');
        $d->note($d->quarter,         'Bf4');
        if ($n % 2) { # odd
            $d->note($d->quarter,     'F4');
            $d->note($d->dotted_half, 'Ef4');
            $d->rest($d->eighth);
            $d->note($d->eighth,      'Bf4');
        }
        else { # even
            $d->note($d->quarter, 'Bf4');
            $d->note($d->half,    'Bf4');
            $d->note($d->quarter, 'Af4');
            $d->note($d->quarter, 'Bf4');
        }
    }

    $d->note($d->quarter,     'C5');
    $d->note($d->quarter,     'C5');
    $d->note($d->whole,       'C5');
    $d->note($d->half,        'Df5');
    $d->note($d->whole,       'Df5');
    $d->note($d->dotted_half, 'F4');
    $d->note($d->quarter,     'Bf4');
    $d->note($d->quarter,     'Bf4');
    $d->note($d->quarter,     'Af4');
    $d->note($d->dotted_half, 'Af4');
    $d->note($d->quarter,     'Gf4');
    $d->note($d->quarter,     'Gf4');
    $d->note($d->quarter,     'Gf4');
    $d->note($d->whole,       'F4');
}

sub bass {
    set_chan_patch($d->score, $channel++, $bass_patch);

    my $n = 2;
    my $duration = $d->half;

    # for in-key chords
    my $modal = MIDI::Bassline::Walk->new(
        verbose     => 1,
        guitar      => 1,
        chord_notes => 0,
        modal       => 1,
        keycenter   => 'Db',
    );

    # for "flavored" chords
    my $tandem = MIDI::Bassline::Walk->new(
        verbose => 1,
        guitar  => 1,
        scale   => sub { '' }, # chord notes only
    );

    $d->rest($d->whole); # while counting-in

    for my $n (1 .. 2) {
        _walk($modal, 'DbM7', 6, 'Bbm7');
        _walk($modal, 'Bbm7', 2, 'Eb7');
        _walk($modal, 'Eb7',  8, 'Ebm7');
        _walk($modal, 'Ebm7', 4, 'Ab7');
        _walk($modal, 'Ab7',  4, 'DbM7');
        _walk($modal, 'DbM7', 4);
        if ($n % 2) { # odd
            _walk($tandem, 'Aaug7', 2);
            _walk($modal, 'Ab7',   2);
        }
        else { # even
            _walk($modal, 'DbM7', 2, 'Db7');
            _walk($modal, 'Db7',  2, 'GbM7');
        }
    }
    _walk($modal,  'GbM7',   4, 'Gdim');
    _walk($tandem, 'Gdim',   4, 'Db6');
    _walk($modal,  'Db6',    4, 'Abm7');
    _walk($modal,  'Abm7',   2, 'Db7');
    _walk($modal,  'Db7',    2, 'GbM7');
    _walk($modal,  'GbM7',   4, 'Gdim');
    _walk($tandem, 'Gdim',   4, 'Db6');
    _walk($modal,  'Db6',    2, 'Bb7b9');
    _walk($tandem, 'Bb7b9',  2, 'Ebm7');
    _walk($modal,  'Ebm7',   2, 'Abaug7');
    _walk($tandem, 'Abaug7', 2, 'DbM7');
    _walk($modal,  'DbM7',   2, 'Ebm7');
    _walk($modal,  'Ebm7',   1, 'Edim');
    _walk($tandem, 'Edim',   1, 'DbM7');
    _walk($modal,  'DbM7',   2, 'G7b5');
    _walk($tandem, 'G7b5',   2, 'GbM7');
    _walk($modal,  'GbM7',   4, 'Eb7');
    _walk($modal,  'Eb7',    3, 'Bb7');
    _walk($modal,  'Bb7',    1, 'Ebm7');
    _walk($modal,  'Ebm7',   2, 'Ab7');
    _walk($modal,  'Ab7',    4, 'DbM7');
    _walk($modal,  'DbM7',   4);

    $d->note('wn', 'Df3');
    $d->note('wn', 'Df3');
}

sub _walk {
    my ($bass, $chord, $n, $next) = @_;
    my $notes = $bass->generate($chord, $n, NEXT_CHORD ? $next : undef);
    $d->note('qn', $_) for @$notes;
}