forked from ology/Music
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bach-choral
123 lines (103 loc) · 2.61 KB
/
bach-choral
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/usr/bin/env perl
use strict;
use warnings;
# Run bach-choral.R and divide-up the data file, before running this.
# Then: perl % ~/Documents/bach-choral-000206b_.csv
use lib '/Users/gene/sandbox/MIDI-Util/lib';
use MIDI::Util qw(setup_score);
use Text::CSV;
use Music::Chord::Note;
use Music::Tempo;
use List::Util qw( shuffle );
my $file = shift || die "Usage: perl $0 file.csv\n";
my $csv = Text::CSV->new({ binary => 1 })
or die 'Cannot use CSV: ' . Text::CSV->error_diag ();
open my $fh, "<:encoding(utf8)", $file
or die "Can't read $file: $!";
my $cn = Music::Chord::Note->new;
my $score = setup_score( patch => 5 );
my ( $name, $melody, $chords, $bassline );
my %note_index = (
3 => 'C',
4 => 'Cs',
5 => 'D',
6 => 'Ds',
7 => 'E',
8 => 'F',
9 => 'Fs',
10 => 'G',
11 => 'Gs',
12 => 'A',
13 => 'As',
14 => 'B',
);
my $i = 0;
while ( my $row = $csv->getline($fh) ) {
$i++;
next if $i == 1;
$name = $row->[1];
# Capture the "melody"
my @notes;
for my $note ( 3 .. 14 ) {
push @notes, $note_index{$note}
if $row->[$note] eq 'YES';
}
my $duration =
@notes == 2 ? 'hn' :
@notes == 3 ? 'thn' :
@notes == 4 ? 'qn' : 'tqn';
for my $note (shuffle @notes) {
push @$melody, [ $duration, $note ];
}
# Capture the chordal accompaniment
my $chord = $row->[17];
$chord =~ s/\s+//;
$chord =~ s/M$//;
$chord =~ s/_//;
$chord =~ s/^([\w\#]+)d/$1dim/;
$chord =~ s/^([\w\#]+)M4$/$1add4/;
$chord =~ s/^([\w\#]+)m4$/$1m/;
$chord =~ s/^([\w\#]+)M6$/$1X6/;
$chord =~ s/X//;
$chord =~ s/^([\w\#]+)dim6$/$1dim/;
my @tone = $cn->chord($chord);
s/#/s/ for @tone;
# print "$i. $name - $chord => @tone\n";
push @$chords, \@tone;
# Capture the bass-line
my $bassnote = $row->[15];
$bassnote =~ s/b/f/;
$bassnote =~ s/#/s/;
# print "\t$bassnote\n";
push @$bassline, $bassnote;
}
$csv->eof or $csv->error_diag();
close $fh;
my @parts = (
\&chordline,
\&bassline,
\&melody
);
$score->synch(@parts);
#$score->write_score("$0-$name.mid");
$score->write_score("$0.mid");
sub chordline {
$score->Channel(1);
$score->patch_change( 1, 4 );
$score->Octave(5);
$score->n( 'wn', @$_ ) for @$chords;
}
sub bassline {
$score->Volume(127);
$score->Channel(2);
$score->patch_change( 2, 34 );
$score->Octave(3);
$score->n( 'wn', $_ ) for @$bassline;
}
sub melody {
$score->Volume(90);
$score->Channel(3);
$score->patch_change( 3, 73 );
$score->Octave(6);
$score->n( @$_ ) for @$melody;
}