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

# Render a song to MIDI.

use List::Util 'shuffle';
use MIDI::Simple;
use Music::BachChoralHarmony;
use Music::Chord::Note;
use lib '/Users/gene/sandbox/Music-Duration/lib';
use Music::Duration;
use Music::Tempo;

my $id  = shift || '000106b_';
my $bpm = shift || 90;

Music::Duration::tuple( 'wn', 'z', 5 ); # 5 notes in place of one whole note

my $bach = Music::BachChoralHarmony->new;
my $song = $bach->parse();
#use Data::Dumper;warn(Dumper[sort keys%$song]);exit;

# Redefine the progression for the given id:
$song = $song->{$id};

my $bassline;
my $melodyline;
my $chordline;

my @scale = qw( C Cs D Ds E F Fs G Gs A As B );

my $cn = Music::Chord::Note->new();

my $score = MIDI::Simple->new_score();
$score->set_tempo( bpm_to_ms($bpm) * 1000 );

for my $event ( @{ $song->{events} } ) {
    my $note = $event->{bass};
    $note =~ s/b/f/;
    $note =~ s/#/s/;
    push @$bassline, $note;

    my $size = count_ones( $event->{notes} );
    my $duration =
        $size == 1 ? 'wn'  :
        $size == 2 ? 'hn'  :
        $size == 3 ? 'thn' :
        $size == 4 ? 'qn'  : 'zwn';
    my @notes;
    my $i = 0;
    for my $bit ( split //, $event->{notes} ) {
        push @notes, [ $duration, $scale[$i] ]
            if $bit;
        $i++;
    }
    push @$melodyline, shuffle @notes;

    my $chord = $event->{chord};
    $chord =~ s/M//;
    $chord =~ s/_//;
    $chord =~ s/d/dim/;
    $chord =~ s/dim6/dim/; # What is a dim6 chord?
    $chord =~ s/4/add4/;
    my @tone = $cn->chord($chord);
    for ( @tone ) {
        s/#/s/;
        s/b/f/;
    }
    push @$chordline, \@tone;
}

$score->synch(
    \&bass,
    \&melody,
    \&chords,
);
$score->write_score("$0.mid");

sub count_ones {
    my ($bits) = @_;
    $bits =~ tr/0//d;
    return length $bits;
}

sub bass {
    $score->Volume(127);
    $score->Channel(0);
    $score->patch_change( 0, 34 );
    $score->Octave(3);
    $score->n( 'wn', $_ ) for @$bassline;
    $score->n( 'wn', $bassline->[-1] );
}

sub melody {
    $score->Volume(90);
    $score->Channel(1);
    $score->patch_change( 1, 73 );
    $score->Octave(5);
    $score->n( @$_ ) for @$melodyline;
}

sub chords {
    $score->Volume(90);
    $score->Channel(2);
    $score->patch_change( 2, 4 );
    $score->Octave(4);
    $score->n( 'wn', @$_ ) for @$chordline;
}
