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

use Data::Dumper;
# Use local libraries
use lib map { "$ENV{HOME}/sandbox/$_/lib" } qw( MIDI-Praxis-Variation MIDI-Util Music-Duration-Partition );
use MIDI::Praxis::Variation qw(augmentation transposition);
use MIDI::Util;
use Music::Duration::Partition;
use Music::Scales;

my $t_max   = shift || 64; # Number of treble repetitions
my $bpm     = shift || 120; # Beats per minute
my $t_patch = shift || 4; # Treble patch
my @pool    = @ARGV ? @ARGV : qw/ hn qn en /;

my $size     = 4; # Motif duration size in quarter notes
my $t_octave = 4; # Treble octave
my $b_patch  = 35; #42;

my $sizes = { %MIDI::Simple::Length }; # Named durations used by the counter

my $score = MIDI::Util::setup_score(bpm => $bpm);

# Rhythmic motif used by treble and bass
my $mdp = Music::Duration::Partition->new(size => $size, pool => \@pool);
my $motif = $mdp->motif;
print Dumper $motif; # Show the generated motif

my @notes; # List of accumulated treble notes

my $count = 0; # Duration counter used by the drums

$score->synch(
    \&treble,
    \&bass,
    \&drums,
);

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

sub treble {
    MIDI::Util::set_chan_patch($score, 0, $t_patch);

    my @scale = get_scale_MIDI('C', $t_octave, 'pentatonic');

    for my $n (0 .. $t_max - 1) {
        my $note = $scale[int rand @scale];
        push @notes, $note; # Accumulate the notes

        my $dura = $motif->[$n % @$motif];

        $score->n($dura, $note);

        $count += $sizes->{$dura};
    }
}

sub bass {
    MIDI::Util::set_chan_patch($score, 1, $b_patch);

    # Lower the notes and stretch the rhythm
    my @transposed = transposition(-24, @notes);
    my @stretched  = augmentation(@$motif);

    for my $n (0 .. $t_max / 2 - 1) {
        $score->n($stretched[$n % @stretched], $transposed[$n % @transposed]);
    }
}

sub drums {
    MIDI::Util::set_chan_patch($score, 9, 44);

    for (1 .. $count) {
        $score->n('qn', 44); # hi-hat
    }
}
