line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#! perl |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# Data::iRealPro::Output::MMA -- produce MMA song |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# Author : Johan Vromans |
6
|
|
|
|
|
|
|
# Created On : Mon Jan 7 08:20:16 2019 |
7
|
|
|
|
|
|
|
# Last Modified By: Johan Vromans |
8
|
|
|
|
|
|
|
# Last Modified On: Sun Jan 20 22:22:42 2019 |
9
|
|
|
|
|
|
|
# Update Count : 481 |
10
|
|
|
|
|
|
|
# Status : Unknown, Use with caution! |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
################ Common stuff ################ |
13
|
|
|
|
|
|
|
|
14
|
2
|
|
|
2
|
|
2842
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
60
|
|
15
|
2
|
|
|
2
|
|
11
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
50
|
|
16
|
2
|
|
|
2
|
|
9
|
use Carp; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
88
|
|
17
|
2
|
|
|
2
|
|
588
|
use utf8; |
|
2
|
|
|
|
|
15
|
|
|
2
|
|
|
|
|
10
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
package Data::iRealPro::Output::MMA; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my $VERSION = "0.01"; |
22
|
|
|
|
|
|
|
|
23
|
2
|
|
|
2
|
|
133
|
use parent qw( Data::iRealPro::Output::Base ); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
16
|
|
24
|
|
|
|
|
|
|
|
25
|
2
|
|
|
2
|
|
88
|
use Data::iRealPro::URI; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
40
|
|
26
|
2
|
|
|
2
|
|
1188
|
use Data::Dumper; |
|
2
|
|
|
|
|
13030
|
|
|
2
|
|
|
|
|
123
|
|
27
|
2
|
|
|
2
|
|
15
|
use Encode qw(encode_utf8); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
8980
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my $regtest = $ENV{IRP_REGTEST}; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my %stype = ( i => "Intro", |
32
|
|
|
|
|
|
|
v => "Verse", |
33
|
|
|
|
|
|
|
coda => "Coda", |
34
|
|
|
|
|
|
|
); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub options { |
37
|
2
|
|
|
2
|
0
|
5
|
my $self = shift; |
38
|
2
|
|
|
|
|
5
|
[ @{ $self->SUPER::options }, qw( split dir ) ]; |
|
2
|
|
|
|
|
11
|
|
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub process { |
42
|
2
|
|
|
2
|
0
|
2097
|
my ( $self, $u, $options ) = @_; |
43
|
|
|
|
|
|
|
|
44
|
2
|
|
|
|
|
8
|
for ( qw( trace debug verbose ) ) { |
45
|
6
|
50
|
|
|
|
19
|
$self->{$_} = $options->{$_} if exists $options->{$_}; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
2
|
50
|
|
|
|
8
|
unless ( $self->{split} ) { |
49
|
|
|
|
|
|
|
|
50
|
2
|
|
50
|
|
|
19
|
$self->{output} ||= $options->{output} || "__new__.mma"; |
|
|
|
33
|
|
|
|
|
51
|
|
|
|
|
|
|
|
52
|
2
|
50
|
|
|
|
8
|
if ( $u->{transpose} ) { |
53
|
0
|
|
|
|
|
0
|
foreach my $song ( @{ $u->{playlist}->{songs} } ) { |
|
0
|
|
|
|
|
0
|
|
54
|
|
|
|
|
|
|
# Do not change key to actual. |
55
|
0
|
|
|
|
|
0
|
local $song->{_transpose} = 0; |
56
|
|
|
|
|
|
|
|
57
|
0
|
|
0
|
|
|
0
|
$song->{key} = $song->xpose($song->{key} // "C"); |
58
|
0
|
0
|
|
|
|
0
|
if ( $song->{actual_key} ne '' ) { |
59
|
|
|
|
|
|
|
$song->{actual_key} = |
60
|
0
|
|
|
|
|
0
|
( $song->{actual_key} + $song->{transpose} ) % 12; |
61
|
|
|
|
|
|
|
} |
62
|
0
|
|
|
|
|
0
|
$song->tokenize; |
63
|
0
|
0
|
|
|
|
0
|
$song->{data} = $song->{dataxp} if $song->{dataxp}; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
2
|
50
|
|
|
|
10
|
if ( ref( $self->{output} ) ) { |
68
|
2
|
|
|
|
|
10
|
${ $self->{output} } = $self->to_mma($u); |
|
2
|
|
|
|
|
7
|
|
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
else { |
71
|
|
|
|
|
|
|
open( my $fd, ">:utf8", $self->{output} ) |
72
|
0
|
0
|
|
|
|
0
|
or croak( "Cannot create ", $self->{output}, " [$!]\n" ); |
73
|
0
|
|
|
|
|
0
|
print $fd $self->to_mma($u); |
74
|
0
|
|
|
|
|
0
|
close($fd); |
75
|
|
|
|
|
|
|
} |
76
|
2
|
|
|
|
|
7
|
return; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
0
|
|
0
|
|
|
0
|
my $outdir = $self->{dir} || ""; |
80
|
0
|
0
|
0
|
|
|
0
|
$outdir .= "/" if $outdir && $outdir !~ m;/$;; |
81
|
0
|
0
|
|
|
|
0
|
make_path( $outdir, {} ) unless -d $outdir; |
82
|
|
|
|
|
|
|
|
83
|
0
|
|
|
|
|
0
|
foreach my $song ( @{ $u->{playlist}->{songs} } ) { |
|
0
|
|
|
|
|
0
|
|
84
|
|
|
|
|
|
|
# Do not change key to actual. |
85
|
0
|
|
|
|
|
0
|
local $song->{_transpose} = 0; |
86
|
|
|
|
|
|
|
|
87
|
0
|
0
|
|
|
|
0
|
if ( $song->{transpose} ) { |
88
|
0
|
|
0
|
|
|
0
|
$song->{key} = $song->xpose($song->{key} // "C"); |
89
|
0
|
0
|
|
|
|
0
|
if ( $song->{actual_key} ne '' ) { |
90
|
|
|
|
|
|
|
$song->{actual_key} = |
91
|
0
|
|
|
|
|
0
|
( $song->{actual_key} + $song->{transpose} ) % 12; |
92
|
|
|
|
|
|
|
} |
93
|
0
|
|
|
|
|
0
|
$song->tokenize; |
94
|
0
|
0
|
|
|
|
0
|
$song->{data} = $song->{dataxp} if $song->{dataxp}; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# Make a playlist with just this song. |
98
|
0
|
|
|
|
|
0
|
my $pls = Data::iRealPro::Playlist->new( song => $song ); |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# Make an URI for this playlist. |
101
|
0
|
|
|
|
|
0
|
my $uri = Data::iRealPro::URI->new( playlist => $pls ); |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# Write it out. |
104
|
0
|
|
|
|
|
0
|
my $title = $song->{title}; |
105
|
|
|
|
|
|
|
# Mask dangerous characters. |
106
|
0
|
|
|
|
|
0
|
$title =~ s/[:?\\\/*"<>|]/@/g; |
107
|
0
|
|
|
|
|
0
|
my $file = $outdir.$title.".mma"; |
108
|
0
|
|
|
|
|
0
|
my $out = encode_utf8($file); |
109
|
0
|
0
|
|
|
|
0
|
open( my $fd, '>:utf8', $out ) |
110
|
|
|
|
|
|
|
or die( "$out: $!\n" ); |
111
|
0
|
|
|
|
|
0
|
print $fd $self->to_mma($uri); |
112
|
0
|
|
|
|
|
0
|
close($fd); |
113
|
|
|
|
|
|
|
warn( "Wrote $out\n" ) |
114
|
0
|
0
|
|
|
|
0
|
if $self->{verbose}; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub to_mma { |
119
|
2
|
|
|
2
|
0
|
5
|
my ( $self, $u ) = @_; |
120
|
|
|
|
|
|
|
|
121
|
2
|
|
|
|
|
5
|
my $pl = $u->{playlist}; |
122
|
2
|
|
|
|
|
5
|
my $title; |
123
|
2
|
50
|
|
|
|
6
|
if ( $pl->{name} ) { |
124
|
0
|
|
|
|
|
0
|
$title = $pl->{name}; |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
else { |
127
|
2
|
|
|
|
|
6
|
$title = $pl->{songs}->[0]->{title}; |
128
|
|
|
|
|
|
|
} |
129
|
2
|
50
|
|
|
|
19
|
my $vv = $regtest ? "" : " version $Data::iRealPro::VERSION"; |
130
|
|
|
|
|
|
|
|
131
|
2
|
|
|
|
|
7
|
my $song = $u->{playlist}->{songs}->[0]; |
132
|
2
|
|
50
|
|
|
8
|
my $tempo = $song->{actual_tempo} || 120; |
133
|
|
|
|
|
|
|
|
134
|
2
|
|
|
|
|
6
|
my $time_d; |
135
|
|
|
|
|
|
|
my $time_n; |
136
|
2
|
|
|
|
|
5
|
my $time = "4/4"; |
137
|
2
|
|
|
|
|
4
|
foreach ( @{ $song->tokens } ) { |
|
2
|
|
|
|
|
9
|
|
138
|
27
|
100
|
|
|
|
57
|
next unless /time (\d\/\d)/; |
139
|
1
|
|
|
|
|
3
|
$time = $1; |
140
|
1
|
|
|
|
|
3
|
last; |
141
|
|
|
|
|
|
|
} |
142
|
2
|
|
|
|
|
15
|
( $time_d, $time_n ) = $time =~ m;^(\d+)/(\d+)$;; |
143
|
|
|
|
|
|
|
|
144
|
2
|
|
|
|
|
11
|
my @t = split( ' ', $song->{composer} ); |
145
|
2
|
50
|
|
|
|
14
|
@t[0,1] = @t[1,0] if @t == 2; |
146
|
2
|
|
|
|
|
8
|
my $me = "Data::iRealPro $Data::iRealPro::VERSION"; |
147
|
2
|
|
|
|
|
5
|
my @mma = ( <
|
148
|
2
|
|
|
|
|
10
|
// Title: @{[ $song->{title} ]} |
149
|
2
|
|
|
|
|
14
|
// Style: @{[ $song->{style} ]} |
150
|
|
|
|
|
|
|
// Composer: @t |
151
|
|
|
|
|
|
|
// Converted from iReal by $me |
152
|
|
|
|
|
|
|
|
153
|
2
|
|
|
|
|
10
|
MIDIText @{[ $song->{title} ]} |
154
|
|
|
|
|
|
|
MIDIText MMA input generated by $me |
155
|
|
|
|
|
|
|
|
156
|
2
|
|
|
|
|
24
|
KeySig @{[ key2mma($song->{key}) ]} |
157
|
|
|
|
|
|
|
Time $time_d |
158
|
|
|
|
|
|
|
TimeSig $time |
159
|
|
|
|
|
|
|
Tempo $tempo |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
EOD |
162
|
|
|
|
|
|
|
|
163
|
2
|
|
|
|
|
7
|
my $mma = ""; |
164
|
2
|
|
|
|
|
9
|
my $s = $self->interpret($song); |
165
|
2
|
|
|
|
|
5
|
my $mcnt = 0; |
166
|
2
|
|
|
|
|
3
|
my $in_intro = 1; |
167
|
2
|
|
100
|
|
|
9
|
my $grepeat = $song->{actual_repeats} || 3; |
168
|
|
|
|
|
|
|
|
169
|
2
|
|
|
|
|
14
|
my $has_jumps; |
170
|
2
|
|
|
|
|
5
|
foreach ( @{ $s->{content} } ) { |
|
2
|
|
|
|
|
6
|
|
171
|
4
|
100
|
|
|
|
19
|
next unless $_->{hasjumps}; |
172
|
1
|
|
|
|
|
3
|
$has_jumps++; |
173
|
1
|
|
|
|
|
2
|
last; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
2
|
|
|
|
|
11
|
push( @mma, "Set SongForms $grepeat\n" ); |
177
|
2
|
100
|
|
|
|
7
|
push( @mma, "\nLabel Capo\n" ) if $has_jumps; |
178
|
|
|
|
|
|
|
|
179
|
2
|
|
|
|
|
5
|
foreach my $section ( @{ $s->{content} } ) { |
|
2
|
|
|
|
|
5
|
|
180
|
|
|
|
|
|
|
#print "section ", $section->{type}, "\n"; |
181
|
5
|
50
|
|
|
|
8
|
next unless @{ $section->{content} }; |
|
5
|
|
|
|
|
15
|
|
182
|
5
|
|
|
|
|
11
|
my $type = $section->{type}; |
183
|
5
|
50
|
|
|
|
22
|
next if $type eq "hspace"; |
184
|
|
|
|
|
|
|
|
185
|
5
|
100
|
|
|
|
14
|
if ( $mma ) { |
186
|
3
|
|
|
|
|
6
|
push( @mma, $mma ); |
187
|
|
|
|
|
|
|
} |
188
|
5
|
|
|
|
|
11
|
$mma = ""; |
189
|
|
|
|
|
|
|
|
190
|
5
|
100
|
100
|
|
|
29
|
if ( $in_intro && $type ne 'i' ) { |
|
|
50
|
66
|
|
|
|
|
191
|
2
|
|
|
|
|
8
|
$mma .= "\nRepeat // song form\n"; |
192
|
2
|
|
|
|
|
4
|
$in_intro = 0; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# iRealPro has two styles of song form repeats. Normally, |
196
|
|
|
|
|
|
|
# the coda part comes after the song form. However, when |
197
|
|
|
|
|
|
|
# jumps are used, the coda part becomes part of the song form. |
198
|
|
|
|
|
|
|
elsif ( $type eq "coda" && !$has_jumps ) { |
199
|
0
|
|
|
|
|
0
|
$mma .= "\n" . endrepeat('$SongForms'); |
200
|
0
|
|
|
|
|
0
|
$grepeat = 0; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
5
|
|
66
|
|
|
35
|
$mma .= "\n// Section: " . ($stype{$type}//$type) . "\n"; |
204
|
5
|
100
|
100
|
|
|
20
|
$mma .= "Label Coda\n" if $has_jumps && $type eq "coda"; |
205
|
5
|
|
|
|
|
22
|
$mma .= "Groove " . style2mma( $song->{style} ) . "\n"; |
206
|
5
|
|
|
|
|
10
|
foreach my $item ( @{ $section->{content} } ) { |
|
5
|
|
|
|
|
14
|
|
207
|
|
|
|
|
|
|
#print "item ", $item->{type}, "\n"; |
208
|
|
|
|
|
|
|
|
209
|
52
|
100
|
|
|
|
138
|
if ( $item->{type} eq "measure" ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
210
|
39
|
|
|
|
|
45
|
$mcnt++; |
211
|
39
|
|
|
|
|
96
|
my $res = sprintf("%3d", $mcnt); |
212
|
39
|
|
|
|
|
56
|
my @chords; |
213
|
39
|
|
|
|
|
52
|
foreach my $i ( @{ $item->{content} } ) { |
|
39
|
|
|
|
|
68
|
|
214
|
|
|
|
|
|
|
# printf( "%3d %s\n", $mcnt, "@$i" ); |
215
|
81
|
|
|
|
|
124
|
foreach my $j ( @$i ) { |
216
|
84
|
100
|
|
|
|
295
|
if ( $j =~ /^chord\s+(\S+)/ ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
217
|
40
|
|
|
|
|
79
|
push( @chords, chord2mma($1) ); |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
elsif ( $j =~ /^advance / ) { |
220
|
41
|
|
|
|
|
81
|
push( @chords, "/" ); |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
elsif ( $j =~ /^time\s+(\S+)/ ) { |
223
|
1
|
50
|
|
|
|
8
|
warn("Time signature change: $time -> $1\n") |
224
|
|
|
|
|
|
|
unless $1 eq $time; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
elsif ( $j eq "segno" ) { |
227
|
1
|
|
|
|
|
11
|
$mma .= "Label Segno\n"; |
228
|
1
|
|
|
|
|
5
|
next; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
else { |
231
|
1
|
|
|
|
|
4
|
$res .= " /* $j? */"; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
} |
235
|
39
|
100
|
|
|
|
83
|
if ( @chords < $time_d ) { |
236
|
38
|
50
|
|
|
|
85
|
if ( @chords == 1 ) { |
|
|
100
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# Okay, MMA will fill. |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
elsif ( @chords == 2 ) { |
240
|
37
|
|
|
|
|
101
|
splice( @chords, 1, 0, ("/") x int($time_d/2) ); |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
} |
243
|
39
|
|
|
|
|
77
|
while ( $chords[-1] eq "/" ) { |
244
|
114
|
|
|
|
|
207
|
pop(@chords); |
245
|
|
|
|
|
|
|
} |
246
|
39
|
|
|
|
|
77
|
$res .= " @chords"; |
247
|
|
|
|
|
|
|
$res .= " /* " . $item->{jump}->{text} ." */" |
248
|
39
|
100
|
|
|
|
93
|
if $item->{jump}; |
249
|
39
|
|
|
|
|
102
|
$mma .= $res . "\n"; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
elsif ( $item->{type} eq "repeat start" ) { |
252
|
5
|
|
|
|
|
9
|
push( @mma, $mma ); |
253
|
5
|
|
|
|
|
12
|
$mma = "Repeat\n"; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
elsif ( $item->{type} =~ /repeat end(.*)/ ) { |
256
|
8
|
|
|
|
|
26
|
$mma .= "RepeatEnd$1\n"; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
else { |
259
|
0
|
|
|
|
|
0
|
warn("OOPS: ", $item->{type}, "\n"); |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
2
|
50
|
|
|
|
38
|
push( @mma, $mma ) if $mma; |
265
|
2
|
|
|
|
|
16
|
push( @mma, "\n" . endrepeat('$SongForms') ); |
266
|
|
|
|
|
|
|
|
267
|
2
|
|
|
|
|
46
|
join( "", @mma ); |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub endrepeat { |
271
|
2
|
|
|
2
|
0
|
4
|
my ( $n ) = @_; |
272
|
2
|
|
|
|
|
5
|
my $res = "RepeatEnd"; |
273
|
2
|
50
|
0
|
|
|
12
|
if ( $n =~ /^\$/ ) { |
|
|
0
|
|
|
|
|
|
274
|
2
|
|
|
|
|
9
|
$res .= " NoWarn $n"; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
elsif ( $n && $n ne "2" ) { |
277
|
0
|
|
|
|
|
0
|
$res .= " $n"; |
278
|
|
|
|
|
|
|
} |
279
|
2
|
|
|
|
|
8
|
$res . "\n"; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub interpret { |
283
|
2
|
|
|
2
|
0
|
5
|
my ( $self, $song ) = @_; |
284
|
|
|
|
|
|
|
|
285
|
2
|
|
|
|
|
8
|
my $res = $self->_interpret1($song); |
286
|
2
|
|
|
|
|
6
|
foreach ( @{ $res->{content} } ) { |
|
2
|
|
|
|
|
8
|
|
287
|
5
|
|
|
|
|
33
|
$self->_interpret2($_); |
288
|
5
|
|
|
|
|
17
|
$self->_interpret3($_); |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
2
|
50
|
|
|
|
11
|
if ( $self->{debug} ) { |
292
|
0
|
|
|
|
|
0
|
$Data::Dumper::Deepcopy = 1; |
293
|
0
|
|
|
|
|
0
|
$Data::Dumper::Sortkeys = 1; |
294
|
0
|
|
|
|
|
0
|
warn Dumper($res); |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
2
|
|
|
|
|
6
|
return $res; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# Filter, and break into sections. |
301
|
|
|
|
|
|
|
sub _interpret1 { |
302
|
2
|
|
|
2
|
|
7
|
my ( $self, $song ) = @_; |
303
|
2
|
|
|
|
|
4
|
my $tokens = [ @{ $song->tokens } ]; |
|
2
|
|
|
|
|
9
|
|
304
|
|
|
|
|
|
|
|
305
|
2
|
|
|
|
|
15
|
while ( $tokens->[0] =~ /^advance|hspace/ ) { |
306
|
0
|
|
|
|
|
0
|
shift(@$tokens); |
307
|
|
|
|
|
|
|
} |
308
|
2
|
|
|
|
|
12
|
while ( $tokens->[-1] =~ /^advance|hspace/ ) { |
309
|
1
|
|
|
|
|
4
|
pop(@$tokens); |
310
|
|
|
|
|
|
|
} |
311
|
2
|
50
|
|
|
|
13
|
unless ( $tokens->[0] =~ /^start (?:repeat|section)/ ) { |
312
|
0
|
|
|
|
|
0
|
warn("Invalid section start: ", $tokens->[0], "\n"); |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
2
|
|
|
|
|
19
|
my $res = { tokens => [ @$tokens ], |
316
|
|
|
|
|
|
|
content => [] }; |
317
|
2
|
|
|
|
|
5
|
my $section; # current section |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
my $add_section = sub { |
320
|
5
|
|
|
|
|
21
|
push( @{$res->{content}}, { %$section } ) |
321
|
11
|
100
|
100
|
11
|
|
27
|
if $section && @{ $section->{tokens} }; |
|
9
|
|
|
|
|
38
|
|
322
|
2
|
|
|
|
|
11
|
}; |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
my $new_section = sub { |
325
|
9
|
|
|
9
|
|
20
|
$add_section->(); |
326
|
9
|
|
|
|
|
34
|
$section = { type => "section", |
327
|
|
|
|
|
|
|
tokens => [], |
328
|
|
|
|
|
|
|
}; |
329
|
2
|
|
|
|
|
8
|
}; |
330
|
|
|
|
|
|
|
|
331
|
2
|
|
|
|
|
9
|
$new_section->(); |
332
|
|
|
|
|
|
|
|
333
|
2
|
|
|
|
|
9
|
for ( my $tp = 0; $tp < @$tokens; $tp++ ) { |
334
|
142
|
|
|
|
|
188
|
my $t = $tokens->[$tp]; |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# Treat marked repeat as a section start. |
337
|
142
|
100
|
66
|
|
|
268
|
if ( $t eq "start repeat" && $tokens->[$tp+1] =~ /^mark/ |
|
|
|
100
|
|
|
|
|
338
|
4
|
|
|
|
|
22
|
&& @{ $section->{tokens} } ) { |
339
|
1
|
|
|
|
|
12
|
unshift( @$tokens, $t ); |
340
|
1
|
|
|
|
|
9
|
$t = "start section"; |
341
|
|
|
|
|
|
|
# Fall through... |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
|
344
|
142
|
100
|
|
|
|
236
|
if ( $t eq "start section" ) { |
345
|
3
|
|
|
|
|
9
|
$new_section->(); |
346
|
3
|
|
|
|
|
9
|
next; |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
139
|
100
|
|
|
|
249
|
if ( $t =~ /^hspace\s+(\d+)$/ ) { |
350
|
1
|
|
|
|
|
3
|
next; |
351
|
|
|
|
|
|
|
} |
352
|
138
|
50
|
33
|
|
|
371
|
if ( $t eq "small" || $t eq "large" ) { |
353
|
0
|
|
|
|
|
0
|
next; |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
138
|
100
|
|
|
|
237
|
if ( $t =~ /^mark\s+(.+)$/ ) { |
357
|
3
|
|
|
|
|
9
|
$section->{type} = $1; |
358
|
3
|
|
|
|
|
7
|
next; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
135
|
100
|
|
|
|
216
|
if ( $t eq "end section" ) { |
362
|
3
|
|
|
|
|
10
|
$new_section->(); |
363
|
3
|
|
|
|
|
9
|
next; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
132
|
50
|
|
|
|
269
|
if ( $t =~ /^chord (\(.+\))/ ) { |
367
|
0
|
|
|
|
|
0
|
$t = "text $1"; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
132
|
|
|
|
|
165
|
push( @{ $section->{tokens} }, $t ); |
|
132
|
|
|
|
|
215
|
|
371
|
|
|
|
|
|
|
|
372
|
132
|
100
|
|
|
|
303
|
if ( $t eq "end" ) { |
373
|
1
|
|
|
|
|
3
|
$new_section->(); |
374
|
1
|
|
|
|
|
3
|
next; |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
2
|
|
|
|
|
6
|
$add_section->(); |
380
|
|
|
|
|
|
|
|
381
|
2
|
|
|
|
|
27
|
return $res; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
# Process repeat sections. |
385
|
|
|
|
|
|
|
sub _interpret2 { |
386
|
5
|
|
|
5
|
|
14
|
my ( $self, $section ) = @_; |
387
|
5
|
|
|
|
|
11
|
my @tokens = @{ $section->{tokens} }; |
|
5
|
|
|
|
|
24
|
|
388
|
|
|
|
|
|
|
|
389
|
5
|
|
|
|
|
10
|
my $in_repeat; |
390
|
|
|
|
|
|
|
my $repeatpending; |
391
|
5
|
|
|
|
|
8
|
my $alternatives = 0; |
392
|
5
|
|
|
|
|
10
|
my $lastalternative; |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
my $tp; |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
my $peek = sub { |
397
|
6
|
|
|
6
|
|
10
|
my ( $pat ) = @_; |
398
|
6
|
|
|
|
|
22
|
foreach ( @tokens[ $tp+1 .. $#tokens ] ) { |
399
|
7
|
100
|
|
|
|
35
|
return 1 if /$pat/; |
400
|
3
|
100
|
|
|
|
13
|
return if /^chord|advance|end|start/; |
401
|
|
|
|
|
|
|
} |
402
|
1
|
|
|
|
|
3
|
return; |
403
|
5
|
|
|
|
|
21
|
}; |
404
|
|
|
|
|
|
|
|
405
|
5
|
|
|
|
|
27
|
for ( $tp = 0; $tp < @tokens; $tp++ ) { |
406
|
132
|
|
|
|
|
185
|
my $t = $tokens[$tp]; |
407
|
|
|
|
|
|
|
# printf STDERR ("%2d: %s\n", $tp, $t ); |
408
|
|
|
|
|
|
|
|
409
|
132
|
100
|
|
|
|
210
|
if ( $t eq "start repeat" ) { |
410
|
3
|
|
|
|
|
5
|
$tokens[$tp] = "repeat start"; |
411
|
3
|
|
|
|
|
4
|
$in_repeat = 1; |
412
|
3
|
|
|
|
|
6
|
$alternatives = 0; |
413
|
3
|
50
|
|
|
|
7
|
$tokens[$lastalternative] = "repeat end" |
414
|
|
|
|
|
|
|
if defined $lastalternative; |
415
|
3
|
|
|
|
|
11
|
$lastalternative = undef; |
416
|
3
|
|
|
|
|
3
|
$repeatpending = 2; |
417
|
3
|
|
|
|
|
10
|
next; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
# Implied repeat at start of section. |
421
|
129
|
100
|
100
|
|
|
342
|
if ( $t eq "end repeat" || $t =~ /^alternative / ) { |
422
|
11
|
100
|
|
|
|
18
|
if ( !$in_repeat ) { |
423
|
1
|
|
|
|
|
4
|
unshift( @tokens, "repeat start" ); |
424
|
1
|
|
|
|
|
2
|
$in_repeat = 1; |
425
|
1
|
|
|
|
|
2
|
$tp++; |
426
|
1
|
|
50
|
|
|
5
|
$repeatpending //= 2; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# [ A | B | N1 C } N2 D | will play A B C A B D, but |
431
|
|
|
|
|
|
|
# [ A | B } N1 C } N2 D | will play A B A B C A B A B D |
432
|
129
|
100
|
|
|
|
216
|
if ( $t eq "end repeat" ) { |
433
|
6
|
100
|
|
|
|
27
|
if ( $peek->(qr/^alternative/) ) { |
434
|
4
|
100
|
|
|
|
14
|
if ( $alternatives ) { |
435
|
3
|
|
|
|
|
7
|
splice( @tokens, $tp, 1 ); |
436
|
3
|
|
|
|
|
18
|
$tp--; |
437
|
3
|
|
|
|
|
10
|
next; |
438
|
|
|
|
|
|
|
} |
439
|
1
|
|
|
|
|
3
|
unshift( @tokens, "repeat start" ); |
440
|
1
|
|
50
|
|
|
3
|
$repeatpending //= 2; |
441
|
1
|
|
|
|
|
2
|
$tp++; |
442
|
|
|
|
|
|
|
} |
443
|
3
|
|
|
|
|
11
|
$tokens[$tp] = "repeat end"; |
444
|
3
|
50
|
|
|
|
11
|
$tokens[$tp] .= " " . $repeatpending |
445
|
|
|
|
|
|
|
if $repeatpending != 2; |
446
|
3
|
|
|
|
|
5
|
$lastalternative = undef; |
447
|
3
|
|
|
|
|
7
|
next; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
123
|
50
|
|
|
|
214
|
if ( $t =~ /^text\s+\d+\s+(\d+)x$/ ) { |
451
|
0
|
|
|
|
|
0
|
$repeatpending = $1; |
452
|
0
|
|
|
|
|
0
|
next; |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
|
455
|
123
|
100
|
|
|
|
268
|
if ( $t =~ /^alternative\s+(\d+)$/ ) { |
456
|
5
|
100
|
66
|
|
|
25
|
if ( $1 > $alternatives+1 && defined $lastalternative ) { |
457
|
1
|
|
|
|
|
4
|
$tokens[$lastalternative] = "repeat ending " . ($1-$alternatives); |
458
|
|
|
|
|
|
|
} |
459
|
5
|
|
|
|
|
10
|
$tokens[$tp] = "repeat ending"; |
460
|
5
|
|
|
|
|
20
|
$alternatives = $1; |
461
|
5
|
|
|
|
|
8
|
$lastalternative = $tp; |
462
|
5
|
|
|
|
|
13
|
next; |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
|
466
|
5
|
100
|
|
|
|
17
|
$tokens[$lastalternative] = "repeat end" |
467
|
|
|
|
|
|
|
if defined $lastalternative; |
468
|
|
|
|
|
|
|
|
469
|
5
|
|
|
|
|
30
|
$section->{tokens} = \@tokens; |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
sub _interpret3 { |
473
|
5
|
|
|
5
|
|
11
|
my ( $self, $section ) = @_; |
474
|
5
|
|
|
|
|
17
|
my $tokens = [ @{ $section->{tokens} } ]; |
|
5
|
|
|
|
|
25
|
|
475
|
5
|
|
|
|
|
15
|
my @tokens; |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
my $measure; # current measure |
478
|
5
|
|
|
|
|
0
|
my $cell; # current cell |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
my $new_cell = sub { |
481
|
135
|
|
|
135
|
|
262
|
$cell = []; |
482
|
5
|
|
|
|
|
18
|
}; |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
my $new_measure = sub { |
485
|
54
|
|
|
54
|
|
104
|
$new_cell->(); |
486
|
39
|
|
|
|
|
63
|
push( @{ $section->{content} }, { %{ $measure } } ) |
|
39
|
|
|
|
|
182
|
|
487
|
54
|
100
|
100
|
|
|
138
|
if $measure && @{ $measure->{content} }; |
|
49
|
|
|
|
|
135
|
|
488
|
54
|
|
|
|
|
194
|
$measure = { type => "measure", |
489
|
|
|
|
|
|
|
content => [], |
490
|
|
|
|
|
|
|
}; |
491
|
5
|
|
|
|
|
18
|
}; |
492
|
|
|
|
|
|
|
|
493
|
5
|
|
|
|
|
14
|
$new_measure->(); |
494
|
5
|
|
|
|
|
18
|
$section->{content} = []; |
495
|
|
|
|
|
|
|
|
496
|
5
|
|
|
|
|
12
|
my $i = 0; |
497
|
5
|
|
|
|
|
7
|
my $barskip = 0; |
498
|
5
|
|
|
|
|
20
|
my $in_repeat; |
499
|
|
|
|
|
|
|
my $repeatpending; |
500
|
|
|
|
|
|
|
|
501
|
5
|
|
|
|
|
14
|
while ( @$tokens ) { |
502
|
131
|
|
|
|
|
199
|
my $t = shift(@$tokens); |
503
|
131
|
|
|
|
|
170
|
$i++; |
504
|
|
|
|
|
|
|
|
505
|
131
|
|
|
|
|
167
|
my $done = 0; |
506
|
|
|
|
|
|
|
# warn( $t, $barskip ? "*\n" : "\n" ); |
507
|
|
|
|
|
|
|
|
508
|
131
|
50
|
|
|
|
233
|
if ( $t =~ /^chord (\(.+\))/ ) { |
509
|
0
|
|
|
|
|
0
|
$t = "text $1"; |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
131
|
|
|
|
|
212
|
push( @tokens, $t ); |
513
|
|
|
|
|
|
|
|
514
|
131
|
100
|
|
|
|
256
|
if ( $t =~ /^coda/ ) { |
515
|
1
|
50
|
33
|
|
|
4
|
if ( @{ $section->{content} } == 0 && @{ $measure->{content} } == 0 ) { |
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
4
|
|
516
|
1
|
|
|
|
|
5
|
$section->{type} = $t; |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
else { |
519
|
0
|
|
|
|
|
0
|
$measure->{jump} = { text => "Coda", al => "coda" }; |
520
|
0
|
|
|
|
|
0
|
$section->{hasjumps} = 1; |
521
|
|
|
|
|
|
|
} |
522
|
1
|
|
|
|
|
2
|
next; |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
|
525
|
130
|
50
|
|
|
|
209
|
if ( $barskip ) { |
526
|
0
|
0
|
|
|
|
0
|
if ( $t =~ /^(?:bar|end.*|start.*|repeat*)$/ ) { |
527
|
0
|
|
|
|
|
0
|
$barskip = 0; |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
else { |
530
|
0
|
|
|
|
|
0
|
next; |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
|
534
|
130
|
100
|
100
|
|
|
363
|
if ( $t eq "bar" || $t =~ /^repeat/ ) { |
535
|
44
|
|
|
|
|
100
|
$new_measure->(); |
536
|
44
|
100
|
|
|
|
118
|
next if $t eq "bar"; |
537
|
13
|
|
|
|
|
17
|
push( @{ $section->{content} }, { type => $t } ); |
|
13
|
|
|
|
|
34
|
|
538
|
13
|
|
|
|
|
30
|
next; |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
|
541
|
86
|
100
|
|
|
|
284
|
if ( $t =~ /^(chord\s+(.*)|advance\s+\d+)$/ ) { |
542
|
81
|
|
|
|
|
146
|
push( @$cell, $t ); |
543
|
81
|
|
|
|
|
100
|
push( @{ $measure->{content} }, [ @$cell ] ); |
|
81
|
|
|
|
|
188
|
|
544
|
81
|
|
|
|
|
186
|
$new_cell->(); |
545
|
81
|
|
|
|
|
201
|
next; |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
5
|
50
|
|
|
|
11
|
if ( $t eq "measure repeat single" ) { |
549
|
0
|
|
|
|
|
0
|
$measure = $section->{content}->[-1]; |
550
|
0
|
|
|
|
|
0
|
$barskip = 1; |
551
|
0
|
|
|
|
|
0
|
next; |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
5
|
50
|
|
|
|
12
|
if ( $t =~ /^measure repeat double$/ ) { |
555
|
0
|
|
|
|
|
0
|
push( @{ $section->{content} }, $section->{content}->[-2] ); |
|
0
|
|
|
|
|
0
|
|
556
|
0
|
|
|
|
|
0
|
$measure = $section->{content}->[-2]; |
557
|
0
|
|
|
|
|
0
|
$barskip = 1; |
558
|
0
|
|
|
|
|
0
|
next; |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
5
|
100
|
|
|
|
16
|
if ( $t =~ /^text \d+ (.*)/ ) { |
562
|
1
|
50
|
|
|
|
14
|
if ( my $jump = isjump($1) ) { |
563
|
1
|
|
|
|
|
3
|
$section->{hasjumps} = 1; |
564
|
1
|
|
|
|
|
2
|
$measure->{jump} = $jump; |
565
|
|
|
|
|
|
|
} |
566
|
1
|
|
|
|
|
4
|
next; |
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
|
569
|
4
|
50
|
33
|
|
|
16
|
if ( $t eq "small" || $t eq "large" ) { |
570
|
0
|
|
|
|
|
0
|
next; |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
|
573
|
4
|
|
|
|
|
10
|
push( @$cell, $t ); |
574
|
|
|
|
|
|
|
} |
575
|
5
|
|
|
|
|
13
|
$new_measure->(); |
576
|
5
|
|
|
|
|
42
|
$section->{tokens} = \@tokens; |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
my %chordqual = |
580
|
|
|
|
|
|
|
('' => '', |
581
|
|
|
|
|
|
|
'+' => '+', |
582
|
|
|
|
|
|
|
'-#5' => 'm#5', |
583
|
|
|
|
|
|
|
'-' => 'm', |
584
|
|
|
|
|
|
|
'-11' => 'm11', |
585
|
|
|
|
|
|
|
'-6' => 'm6', |
586
|
|
|
|
|
|
|
'-69' => 'm69', |
587
|
|
|
|
|
|
|
'-7' => 'm7', |
588
|
|
|
|
|
|
|
'-7b5' => 'm7b5', |
589
|
|
|
|
|
|
|
'-9' => 'm9', |
590
|
|
|
|
|
|
|
'-^7' => 'mM7', |
591
|
|
|
|
|
|
|
'-^9' => 'mM9', |
592
|
|
|
|
|
|
|
'-b6' => 'mb6', # ??? |
593
|
|
|
|
|
|
|
'11' => '11', |
594
|
|
|
|
|
|
|
'13#11' => '13#11', |
595
|
|
|
|
|
|
|
'13#9' => '13#9', |
596
|
|
|
|
|
|
|
'13' => '13', |
597
|
|
|
|
|
|
|
'13b9' => '13b9', |
598
|
|
|
|
|
|
|
'13sus' => '13sus', |
599
|
|
|
|
|
|
|
'13x11' => '13#11', |
600
|
|
|
|
|
|
|
'13x9' => '13#9', |
601
|
|
|
|
|
|
|
'2' => '2', |
602
|
|
|
|
|
|
|
'5' => '5', |
603
|
|
|
|
|
|
|
'6' => '6', |
604
|
|
|
|
|
|
|
'69' => '69', |
605
|
|
|
|
|
|
|
'7#11' => '7#11', |
606
|
|
|
|
|
|
|
'7#5' => '+7', |
607
|
|
|
|
|
|
|
'7#9#11' => '7#9#11', |
608
|
|
|
|
|
|
|
'7#9#5' => '+7#9', |
609
|
|
|
|
|
|
|
'7#9' => '7#9', |
610
|
|
|
|
|
|
|
'7#9b5' => '7b5#9', |
611
|
|
|
|
|
|
|
'7' => '7', |
612
|
|
|
|
|
|
|
'7alt' => '7alt', |
613
|
|
|
|
|
|
|
'7b13' => '7b13', |
614
|
|
|
|
|
|
|
'7b13sus' => '7b13sus', # ??? |
615
|
|
|
|
|
|
|
'7b5' => '7b5', |
616
|
|
|
|
|
|
|
'7b9#11' => '7b9#11', |
617
|
|
|
|
|
|
|
'7b9#5' => '7#5b9', |
618
|
|
|
|
|
|
|
'7b9#9' => '7b9#9', # ??? |
619
|
|
|
|
|
|
|
'7b9' => '7b9', |
620
|
|
|
|
|
|
|
'7b9b13' => '7b9b13', # ??? |
621
|
|
|
|
|
|
|
'7b9b5' => '7b5b9', |
622
|
|
|
|
|
|
|
'7b9sus' => '7b9sus', |
623
|
|
|
|
|
|
|
'7b9x11' => '7b9#11', |
624
|
|
|
|
|
|
|
'7b9x5' => '7#5b9', |
625
|
|
|
|
|
|
|
'7b9x9' => '7b9#9', # ??? |
626
|
|
|
|
|
|
|
'7sus' => '7sus4', |
627
|
|
|
|
|
|
|
'7susadd3' => '7sus(add3)', # ??? |
628
|
|
|
|
|
|
|
'7x11' => '7#11', |
629
|
|
|
|
|
|
|
'7x5' => '7#5', |
630
|
|
|
|
|
|
|
'7x9' => '7#9', |
631
|
|
|
|
|
|
|
'7x9b5' => '7b5#9', |
632
|
|
|
|
|
|
|
'7x9x11' => '7#9#11', |
633
|
|
|
|
|
|
|
'7x9x5' => '+7#9', |
634
|
|
|
|
|
|
|
'9#11' => '9#11', |
635
|
|
|
|
|
|
|
'9#5' => '+9', |
636
|
|
|
|
|
|
|
'9' => '9', |
637
|
|
|
|
|
|
|
'9b5' => '9b5', |
638
|
|
|
|
|
|
|
'9sus' => '9sus4', |
639
|
|
|
|
|
|
|
'9x11' => '9#11', |
640
|
|
|
|
|
|
|
'9x5' => '+9', |
641
|
|
|
|
|
|
|
'^' => 'M7', |
642
|
|
|
|
|
|
|
'^13' => 'M13', |
643
|
|
|
|
|
|
|
'^7#11' => 'M7#11', |
644
|
|
|
|
|
|
|
'^7#5' => '+M7', |
645
|
|
|
|
|
|
|
'^7' => 'M7', |
646
|
|
|
|
|
|
|
'^9#11' => 'M9#11', |
647
|
|
|
|
|
|
|
'^9' => 'M9', |
648
|
|
|
|
|
|
|
'add9' => 'add9', |
649
|
|
|
|
|
|
|
'alt' => 'alt', |
650
|
|
|
|
|
|
|
'h' => 'ΓΈ', # dim or dim7 ? |
651
|
|
|
|
|
|
|
'h7' => 'dim', |
652
|
|
|
|
|
|
|
'h9' => 'h9', # ??? |
653
|
|
|
|
|
|
|
'm#5' => 'm#5', # ??? |
654
|
|
|
|
|
|
|
'm' => 'm', |
655
|
|
|
|
|
|
|
'm11' => 'm11', |
656
|
|
|
|
|
|
|
'm6' => 'm6', |
657
|
|
|
|
|
|
|
'm69' => 'm69', |
658
|
|
|
|
|
|
|
'm7' => 'm7', |
659
|
|
|
|
|
|
|
'm7b5' => 'm7b5', |
660
|
|
|
|
|
|
|
'm9' => 'm9', |
661
|
|
|
|
|
|
|
'mb6' => 'mb6', # ??? |
662
|
|
|
|
|
|
|
'mv7' => 'mM7', |
663
|
|
|
|
|
|
|
'mv9' => 'mM9', |
664
|
|
|
|
|
|
|
'mx5' => 'm#5', # ??? |
665
|
|
|
|
|
|
|
'o' => 'dim', |
666
|
|
|
|
|
|
|
'o7' => 'dim7', |
667
|
|
|
|
|
|
|
'p' => 'aug', |
668
|
|
|
|
|
|
|
'sus' => 'sus4', |
669
|
|
|
|
|
|
|
'v' => '^', |
670
|
|
|
|
|
|
|
'v13' => '^13', |
671
|
|
|
|
|
|
|
'v7' => '^7', |
672
|
|
|
|
|
|
|
'v7x11' => 'M7#11', |
673
|
|
|
|
|
|
|
'v7x5' => '+M7', |
674
|
|
|
|
|
|
|
'v9' => 'M9', |
675
|
|
|
|
|
|
|
'v9x11' => 'M9#11', |
676
|
|
|
|
|
|
|
); |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
my $pc; |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
sub chord2mma { |
681
|
40
|
|
|
40
|
0
|
89
|
my ( $chord ) = @_; |
682
|
|
|
|
|
|
|
|
683
|
40
|
|
|
|
|
54
|
my $bass; |
684
|
40
|
100
|
|
|
|
86
|
if ( $chord =~ m;^(.+)/(.+)$; ) { |
685
|
4
|
|
|
|
|
8
|
$bass = $2; |
686
|
4
|
|
|
|
|
6
|
$chord = $1; |
687
|
|
|
|
|
|
|
} |
688
|
40
|
50
|
|
|
|
67
|
if ( $chord eq "W" ) { |
689
|
0
|
|
|
|
|
0
|
$chord = $pc; |
690
|
|
|
|
|
|
|
} |
691
|
|
|
|
|
|
|
else { |
692
|
40
|
|
|
|
|
59
|
$pc = $chord; |
693
|
|
|
|
|
|
|
} |
694
|
|
|
|
|
|
|
|
695
|
40
|
50
|
|
|
|
86
|
if ( $chord =~ /^N\.?C\.?/ ) { |
696
|
0
|
|
|
|
|
0
|
$chord = "z"; |
697
|
|
|
|
|
|
|
} |
698
|
|
|
|
|
|
|
|
699
|
40
|
50
|
|
|
|
120
|
unless ( $chord =~ /^([ABCDEFGWz][b\#]?)(.*)/ ) { |
700
|
0
|
|
|
|
|
0
|
Carp::croak("Invalid chord key: $chord"); |
701
|
|
|
|
|
|
|
} |
702
|
|
|
|
|
|
|
|
703
|
40
|
|
|
|
|
91
|
my ( $root, $mod ) = ( $1, $2 ); |
704
|
|
|
|
|
|
|
|
705
|
40
|
50
|
|
|
|
86
|
if ( defined($chordqual{$mod}) ) { |
706
|
40
|
|
|
|
|
76
|
$root .= $chordqual{$mod}; |
707
|
|
|
|
|
|
|
} |
708
|
|
|
|
|
|
|
else { |
709
|
0
|
|
|
|
|
0
|
Carp::croak("Invalid chord modifier: $chord"); |
710
|
|
|
|
|
|
|
} |
711
|
|
|
|
|
|
|
|
712
|
40
|
100
|
|
|
|
78
|
$root .= "/$bass" if $bass; |
713
|
40
|
|
|
|
|
114
|
return $root; |
714
|
|
|
|
|
|
|
} |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
sub key2mma { |
717
|
2
|
|
|
2
|
0
|
7
|
my ( $key ) = @_; |
718
|
|
|
|
|
|
|
|
719
|
2
|
50
|
|
|
|
15
|
unless ( $key =~ /^([ABCDEFGW][b#]?)([-m])?$/ ) { |
720
|
0
|
|
|
|
|
0
|
Carp::croak("Invalid key: $key"); |
721
|
|
|
|
|
|
|
} |
722
|
|
|
|
|
|
|
|
723
|
2
|
|
|
|
|
11
|
my ( $root, $min ) = ( $1, $2 ); |
724
|
2
|
50
|
|
|
|
8
|
$root .= $min ? " minor" : " major"; |
725
|
|
|
|
|
|
|
|
726
|
2
|
|
|
|
|
16
|
return $root; |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
} |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
sub style2mma { |
731
|
5
|
|
|
5
|
0
|
13
|
my ( $style, $section ) = @_; |
732
|
5
|
|
|
|
|
25
|
return "CountrySwing"; |
733
|
|
|
|
|
|
|
} |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
sub isjump { |
736
|
1
|
|
|
1
|
0
|
5
|
my ( $code ) = @_; |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
return unless |
739
|
1
|
50
|
|
|
|
14
|
$code =~ m{ ^ |
740
|
|
|
|
|
|
|
D\. ([CS]) \. \s+ al \s+ |
741
|
|
|
|
|
|
|
( Coda | Fine | (?:1st|2nd|3rd) \s+ End ) |
742
|
|
|
|
|
|
|
$ |
743
|
|
|
|
|
|
|
}xoi; |
744
|
|
|
|
|
|
|
|
745
|
1
|
|
|
|
|
5
|
my $al = lc($2); |
746
|
1
|
50
|
|
|
|
15
|
$al = $1 if $al =~ /^(\d)/; |
747
|
1
|
50
|
|
|
|
13
|
{ text => $code, da => $1 eq "C" ? "capo" : "segno", al => $al } |
748
|
|
|
|
|
|
|
} |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
1; |