| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#! perl |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package main; |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $options; |
|
6
|
|
|
|
|
|
|
our $config; |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package ChordPro::Output::MMA; |
|
9
|
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
8
|
use ChordPro::Output::Common; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
74
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
1
|
|
|
1
|
|
7
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
37
|
|
|
13
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
3575
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub generate_songbook { |
|
16
|
7
|
|
|
7
|
0
|
25
|
my ( $self, $sb ) = @_; |
|
17
|
7
|
|
|
|
|
15
|
my @book; |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
die("MMA generation requires a single song\n") |
|
20
|
7
|
50
|
|
|
|
14
|
if @{$sb->{songs}} > 1; |
|
|
7
|
|
|
|
|
31
|
|
|
21
|
|
|
|
|
|
|
|
|
22
|
7
|
|
|
|
|
14
|
foreach my $song ( @{$sb->{songs}} ) { |
|
|
7
|
|
|
|
|
23
|
|
|
23
|
7
|
50
|
|
|
|
20
|
if ( @book ) { |
|
24
|
0
|
0
|
|
|
|
0
|
push(@book, "") if $options->{'backend-option'}->{tidy}; |
|
25
|
0
|
|
|
|
|
0
|
push(@book, "-- New song"); |
|
26
|
|
|
|
|
|
|
} |
|
27
|
7
|
|
|
|
|
14
|
push(@book, @{generate_song($song)}); |
|
|
7
|
|
|
|
|
26
|
|
|
28
|
|
|
|
|
|
|
} |
|
29
|
|
|
|
|
|
|
|
|
30
|
7
|
|
|
|
|
22
|
push( @book, ""); |
|
31
|
7
|
|
|
|
|
25
|
\@book; |
|
32
|
|
|
|
|
|
|
} |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
my $groove; # groove to use |
|
35
|
|
|
|
|
|
|
my $single_space = 0; # suppress chords line when empty |
|
36
|
|
|
|
|
|
|
my $chords_under = 0; # chords under lyrics |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub safemeta { |
|
39
|
14
|
|
|
14
|
0
|
46
|
my ( $s, $meta, $default ) = @_; |
|
40
|
14
|
50
|
33
|
|
|
58
|
return $default undef unless defined $meta && defined $s->{meta}->{$meta}; |
|
41
|
14
|
|
|
|
|
161
|
return $s->{meta}->{$meta}->[0]; |
|
42
|
|
|
|
|
|
|
} |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub generate_song { |
|
45
|
7
|
|
|
7
|
0
|
20
|
my ( $s ) = @_; |
|
46
|
|
|
|
|
|
|
|
|
47
|
7
|
|
|
|
|
14
|
my $st = 0; # current MMA statement number |
|
48
|
7
|
|
|
|
|
14
|
my $cur = ''; # MMA statement under construction |
|
49
|
7
|
|
|
|
|
18
|
my $prev = ''; # previous MMA statement |
|
50
|
7
|
|
|
|
|
21
|
my $did = 0; # preamble was emitted |
|
51
|
7
|
|
|
|
|
16
|
my $pchord = '.'; # last real chord |
|
52
|
|
|
|
|
|
|
|
|
53
|
7
|
|
|
|
|
22
|
$groove = $options->{'backend-option'}->{groove}; |
|
54
|
7
|
|
|
|
|
18
|
my $tidy = $options->{'backend-option'}->{tidy}; |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# Normally a counting beat is 1 quarter. deCoda uses 1/8th. |
|
57
|
7
|
|
66
|
|
|
37
|
my $decoda = $options->{'backend-option'}->{decoda} || $options->{'backend-option'}->{deCoda}; |
|
58
|
|
|
|
|
|
|
|
|
59
|
7
|
|
|
|
|
18
|
$single_space = $options->{'single-space'}; |
|
60
|
7
|
|
|
|
|
24
|
$chords_under = $config->{settings}->{'chords-under'}; |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
$s->structurize |
|
63
|
7
|
50
|
50
|
|
|
42
|
if ( $options->{'backend-option'}->{structure} // '' ) eq 'structured'; |
|
64
|
|
|
|
|
|
|
|
|
65
|
7
|
|
|
|
|
15
|
my @s; |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# Preamble. |
|
68
|
7
|
50
|
|
|
|
44
|
push( @s, "// title: " . $s->{title}, "" ) if defined $s->{title}; |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# Select a groove. |
|
71
|
7
|
|
|
|
|
13
|
my $bpm = 4; |
|
72
|
7
|
|
|
|
|
15
|
my $q = 4; |
|
73
|
7
|
50
|
|
|
|
31
|
( $bpm, $q ) = ( $1, $2 ) if safemeta( $s, "time", "4/4" ) =~ /^(\d+)\/(\d+)/; |
|
74
|
7
|
50
|
|
|
|
35
|
unless ( $groove ) { |
|
75
|
0
|
0
|
|
|
|
0
|
if ( $bpm == 3 ) { |
|
|
|
0
|
|
|
|
|
|
|
76
|
0
|
0
|
|
|
|
0
|
$q = 4 unless $q == 8; |
|
77
|
0
|
|
|
|
|
0
|
$groove = "Neutral$bpm$q"; |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
elsif ( $bpm == 6 ) { |
|
80
|
0
|
0
|
|
|
|
0
|
warn("Time 6/$q set to 6/8\n") unless $q == 8; |
|
81
|
0
|
|
|
|
|
0
|
$q = 8; |
|
82
|
0
|
|
|
|
|
0
|
$groove = "Neutral$bpm$q"; |
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
else { |
|
85
|
0
|
|
|
|
|
0
|
warn("Time $bpm/$q set to 4/4\n"); |
|
86
|
0
|
|
|
|
|
0
|
$q = $bpm = 4; |
|
87
|
0
|
|
|
|
|
0
|
$groove = "Neutral44"; |
|
88
|
|
|
|
|
|
|
} |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
|
|
91
|
7
|
|
|
|
|
58
|
push( @s, sprintf( "Time %d/%d", $bpm, $q ) ); |
|
92
|
7
|
|
|
|
|
39
|
push( @s, makegroove( $bpm, $q ) ); |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# When deCoda decodes a song in 6/8 at 100bpm, it gets interpreted as 2/4. |
|
95
|
|
|
|
|
|
|
# When the time signature is manually fixed to 6/8, the song becomes |
|
96
|
|
|
|
|
|
|
# twice as long. So we must double the tempo. |
|
97
|
7
|
100
|
100
|
|
|
26
|
push( @s, sprintf( "Tempo %d", |
|
98
|
|
|
|
|
|
|
safemeta( $s, "tempo", 60 ) * (( $q == 8 && $decoda ) ? 2 : 1 ) |
|
99
|
|
|
|
|
|
|
) ); |
|
100
|
|
|
|
|
|
|
|
|
101
|
7
|
|
|
|
|
31
|
push( @s, "", "/**** End of Preamble ****/", "" ); |
|
102
|
|
|
|
|
|
|
|
|
103
|
7
|
|
|
|
|
26
|
my $ctx = ""; |
|
104
|
7
|
|
|
|
|
13
|
my $line; |
|
105
|
|
|
|
|
|
|
|
|
106
|
7
|
|
|
|
|
14
|
foreach my $elt ( @{$s->{body}} ) { |
|
|
7
|
|
|
|
|
26
|
|
|
107
|
70
|
|
|
|
|
183
|
my $line = sprintf( "%3d", $elt->{line} ); |
|
108
|
|
|
|
|
|
|
|
|
109
|
70
|
100
|
|
|
|
161
|
if ( $elt->{context} ne $ctx ) { |
|
110
|
14
|
100
|
|
|
|
59
|
push(@s, "// $line End of $ctx") if $ctx; |
|
111
|
14
|
100
|
|
|
|
60
|
push(@s, "// $line Start of $ctx") if $ctx = $elt->{context}; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
|
|
114
|
70
|
100
|
|
|
|
159
|
if ( $elt->{type} eq "empty" ) { |
|
115
|
|
|
|
|
|
|
push(@s, "***SHOULD NOT HAPPEN***") |
|
116
|
14
|
50
|
|
|
|
49
|
if $s->{structure} eq 'structured'; |
|
117
|
14
|
|
|
|
|
34
|
push(@s, ""); |
|
118
|
14
|
|
|
|
|
30
|
next; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
|
|
121
|
56
|
50
|
|
|
|
104
|
if ( $elt->{type} eq "colb" ) { |
|
122
|
0
|
|
|
|
|
0
|
push(@s, "// $line Column break"); |
|
123
|
0
|
|
|
|
|
0
|
next; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
|
|
126
|
56
|
50
|
|
|
|
115
|
if ( $elt->{type} eq "newpage" ) { |
|
127
|
0
|
|
|
|
|
0
|
push(@s, "// $line New page"); |
|
128
|
0
|
|
|
|
|
0
|
next; |
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
|
|
131
|
56
|
100
|
|
|
|
126
|
if ( $elt->{type} eq "gridline" ) { |
|
132
|
21
|
|
|
|
|
33
|
my @a = @{ $elt->{tokens} }; |
|
|
21
|
|
|
|
|
94
|
|
|
133
|
|
|
|
|
|
|
# Reduce the elements (objects) to simple chords or symbols. |
|
134
|
21
|
|
|
|
|
55
|
@a = map { $_->{class} eq 'chord' |
|
135
|
|
|
|
|
|
|
? $_->{chord}->key |
|
136
|
405
|
100
|
|
|
|
974
|
: $_->{symbol} } @a; |
|
137
|
|
|
|
|
|
|
|
|
138
|
21
|
|
|
|
|
110
|
push( @s, "// $line @a" ); |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# Remove label and initial bar symbol. |
|
141
|
21
|
|
|
|
|
39
|
my $firstbar; |
|
142
|
21
|
|
|
|
|
64
|
do { } until is_bar( $firstbar = shift(@a) ); |
|
143
|
|
|
|
|
|
|
|
|
144
|
21
|
100
|
100
|
|
|
89
|
if ( $decoda && $q == 4 ) { |
|
145
|
|
|
|
|
|
|
# deCoda always uses a beat step of 8. For x/4 times we must reduce. |
|
146
|
9
|
|
|
|
|
27
|
@a = reduce( \@a, $bpm, $line, \@s); |
|
147
|
9
|
|
|
|
|
49
|
push( @s, "// $line $firstbar " . |
|
148
|
|
|
|
|
|
|
join(" ", @a) ); |
|
149
|
|
|
|
|
|
|
} |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# Bars must be full. |
|
152
|
21
|
50
|
|
|
|
65
|
if ( @a % ( $bpm + 1 ) ) { |
|
153
|
0
|
|
|
|
|
0
|
push( @s, "// $line $bpm $q ".scalar(@a)." OOPS?" ); |
|
154
|
0
|
|
|
|
|
0
|
next; |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
|
|
157
|
21
|
|
|
|
|
41
|
my $rept = 0; |
|
158
|
21
|
|
|
|
|
34
|
my $bar = 0; |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# Process the elements. |
|
161
|
21
|
|
|
|
|
47
|
while ( @a ) { |
|
162
|
|
|
|
|
|
|
# Increment bar number and mma statement number. |
|
163
|
56
|
|
|
|
|
85
|
$bar++; |
|
164
|
56
|
|
|
|
|
73
|
$st++; |
|
165
|
56
|
|
|
|
|
84
|
my $c = ''; # mma statement being constructed |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
# Reuse last chord if we have none. |
|
168
|
56
|
100
|
66
|
|
|
136
|
if ( $a[0] eq '.' && $a[1] eq '.' ) { |
|
169
|
7
|
|
|
|
|
11
|
$a[0] = $pchord; |
|
170
|
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# Process the beats. |
|
173
|
56
|
|
|
|
|
124
|
for ( my $b = 1; $b <= $bpm; $b++ ) { |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# Get a chord. |
|
176
|
240
|
|
|
|
|
328
|
$cur = shift(@a); |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# Append to statement. |
|
179
|
240
|
100
|
|
|
|
435
|
$c .= $cur eq '.' ? "/ " : "$cur "; |
|
180
|
240
|
100
|
|
|
|
568
|
$pchord = $cur unless $cur eq '.'; |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# Remove trailing slashes. |
|
184
|
56
|
|
|
|
|
257
|
$c =~ s;[\s/]+$;;; |
|
185
|
56
|
50
|
|
|
|
166
|
$c = $prev unless $c =~ /\S/; |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
# Print MMA statement. |
|
188
|
56
|
100
|
100
|
|
|
195
|
if ( $prev eq $c || $st == 1 ) { |
|
189
|
14
|
|
|
|
|
22
|
$rept++; |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
else { |
|
192
|
42
|
50
|
|
|
|
201
|
push( @s, |
|
|
|
100
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sprintf( "%3d %s%s", $st-$rept, $prev, |
|
194
|
|
|
|
|
|
|
$rept > 1 ? " * $rept" : "" ) |
|
195
|
|
|
|
|
|
|
) if $rept; |
|
196
|
42
|
|
|
|
|
70
|
$rept = 1; |
|
197
|
|
|
|
|
|
|
} |
|
198
|
56
|
|
|
|
|
81
|
$prev = $c; |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# Check for trailing barline. |
|
201
|
56
|
50
|
|
|
|
109
|
unless ( is_bar(shift(@a)) ) { |
|
202
|
0
|
|
|
|
|
0
|
push( @s, "// bar $bar: Missing final barline?" ); |
|
203
|
0
|
|
|
|
|
0
|
warn("line $., bar $bar: Missing final barline?\n"); |
|
204
|
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
} |
|
206
|
21
|
50
|
|
|
|
89
|
push( @s, |
|
207
|
|
|
|
|
|
|
sprintf( "%3d %s%s", $st-$rept+1, $prev, |
|
208
|
|
|
|
|
|
|
$rept > 1 ? " * $rept" : "" ) ); |
|
209
|
21
|
|
|
|
|
35
|
$rept = 0; |
|
210
|
|
|
|
|
|
|
|
|
211
|
21
|
|
|
|
|
41
|
next; |
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
|
|
214
|
35
|
100
|
|
|
|
106
|
if ( $elt->{type} =~ /^comment(?:_italic|_box)?$/ ) { |
|
215
|
7
|
50
|
|
|
|
18
|
push(@s, "") if $tidy; |
|
216
|
7
|
|
|
|
|
25
|
my $text = $elt->{text}; |
|
217
|
7
|
50
|
|
|
|
23
|
if ( $elt->{chords} ) { |
|
218
|
0
|
|
|
|
|
0
|
$text = ""; |
|
219
|
0
|
|
|
|
|
0
|
for ( 0..$#{ $elt->{chords} } ) { |
|
|
0
|
|
|
|
|
0
|
|
|
220
|
|
|
|
|
|
|
$text .= "[" . $elt->{chords}->[$_] . "]" |
|
221
|
0
|
0
|
|
|
|
0
|
if $elt->{chords}->[$_] ne ""; |
|
222
|
0
|
|
|
|
|
0
|
$text .= $elt->{phrases}->[$_]; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
} |
|
225
|
7
|
|
|
|
|
29
|
$text = fmt_subst( $s, $text ); |
|
226
|
7
|
|
|
|
|
568
|
push(@s, "// $line comment: $text"); |
|
227
|
7
|
50
|
|
|
|
30
|
push(@s, "") if $tidy; |
|
228
|
7
|
|
|
|
|
20
|
next; |
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
|
|
231
|
28
|
|
|
|
|
51
|
next; |
|
232
|
|
|
|
|
|
|
|
|
233
|
0
|
0
|
|
|
|
0
|
if ( $elt->{type} eq "songline" ) { |
|
234
|
0
|
|
|
|
|
0
|
push(@s, songline($elt)); |
|
235
|
0
|
|
|
|
|
0
|
next; |
|
236
|
|
|
|
|
|
|
} |
|
237
|
|
|
|
|
|
|
|
|
238
|
0
|
0
|
|
|
|
0
|
if ( $elt->{type} eq "tabline" ) { |
|
239
|
0
|
|
|
|
|
0
|
push(@s, $elt->{text}); |
|
240
|
0
|
|
|
|
|
0
|
next; |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
|
|
243
|
0
|
0
|
|
|
|
0
|
if ( $elt->{type} eq "chorus" ) { |
|
244
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
|
245
|
0
|
|
|
|
|
0
|
push(@s, "// $line Start of chorus*"); |
|
246
|
0
|
|
|
|
|
0
|
foreach my $e ( @{$elt->{body}} ) { |
|
|
0
|
|
|
|
|
0
|
|
|
247
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "empty" ) { |
|
248
|
0
|
|
|
|
|
0
|
push(@s, ""); |
|
249
|
0
|
|
|
|
|
0
|
next; |
|
250
|
|
|
|
|
|
|
} |
|
251
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "songline" ) { |
|
252
|
0
|
|
|
|
|
0
|
push(@s, songline($e)); |
|
253
|
0
|
|
|
|
|
0
|
next; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
} |
|
256
|
0
|
|
|
|
|
0
|
push(@s, "// $line End of chorus*"); |
|
257
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
|
258
|
0
|
|
|
|
|
0
|
next; |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
|
|
261
|
0
|
0
|
|
|
|
0
|
if ( $elt->{type} eq "tab" ) { |
|
262
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
|
263
|
0
|
|
|
|
|
0
|
push(@s, "// $line Start of tab"); |
|
264
|
0
|
|
|
|
|
0
|
push(@s, map { "// " . $_->{text} } @{$elt->{body}} ); |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
265
|
0
|
|
|
|
|
0
|
push(@s, "// $line End of tab"); |
|
266
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
|
267
|
0
|
|
|
|
|
0
|
next; |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
|
|
270
|
0
|
0
|
|
|
|
0
|
if ( $elt->{type} eq "verse" ) { |
|
271
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
|
272
|
0
|
|
|
|
|
0
|
push(@s, "// $line Start of verse"); |
|
273
|
0
|
|
|
|
|
0
|
foreach my $e ( @{$elt->{body}} ) { |
|
|
0
|
|
|
|
|
0
|
|
|
274
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "empty" ) { |
|
275
|
|
|
|
|
|
|
push(@s, "***SHOULD NOT HAPPEN***") |
|
276
|
0
|
0
|
|
|
|
0
|
if $s->{structure} eq 'structured'; |
|
277
|
0
|
|
|
|
|
0
|
next; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "songline" ) { |
|
280
|
0
|
|
|
|
|
0
|
push(@s, songline($e)); |
|
281
|
0
|
|
|
|
|
0
|
next; |
|
282
|
|
|
|
|
|
|
} |
|
283
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "comment" ) { |
|
284
|
0
|
|
|
|
|
0
|
push(@s, "-c- " . $e->{text}); |
|
285
|
0
|
|
|
|
|
0
|
next; |
|
286
|
|
|
|
|
|
|
} |
|
287
|
0
|
0
|
|
|
|
0
|
if ( $e->{type} eq "comment_italic" ) { |
|
288
|
0
|
|
|
|
|
0
|
push(@s, "-i- " . $e->{text}); |
|
289
|
0
|
|
|
|
|
0
|
next; |
|
290
|
|
|
|
|
|
|
} |
|
291
|
|
|
|
|
|
|
} |
|
292
|
0
|
|
|
|
|
0
|
push(@s, "// $line End of verse"); |
|
293
|
0
|
0
|
|
|
|
0
|
push(@s, "") if $tidy; |
|
294
|
0
|
|
|
|
|
0
|
next; |
|
295
|
|
|
|
|
|
|
} |
|
296
|
|
|
|
|
|
|
|
|
297
|
0
|
0
|
|
|
|
0
|
if ( $elt->{type} eq "image" ) { |
|
298
|
0
|
|
|
|
|
0
|
my @args = ( "image:", $elt->{uri} ); |
|
299
|
0
|
|
|
|
|
0
|
while ( my($k,$v) = each( %{ $elt->{opts} } ) ) { |
|
|
0
|
|
|
|
|
0
|
|
|
300
|
0
|
|
|
|
|
0
|
push( @args, "$k=$v" ); |
|
301
|
|
|
|
|
|
|
} |
|
302
|
0
|
|
|
|
|
0
|
foreach ( @args ) { |
|
303
|
0
|
0
|
|
|
|
0
|
next unless /\s/; |
|
304
|
0
|
|
|
|
|
0
|
$_ = '"' . $_ . '"'; |
|
305
|
|
|
|
|
|
|
} |
|
306
|
0
|
|
|
|
|
0
|
push( @s, "// $line @args" ); |
|
307
|
0
|
|
|
|
|
0
|
next; |
|
308
|
|
|
|
|
|
|
} |
|
309
|
|
|
|
|
|
|
|
|
310
|
0
|
0
|
|
|
|
0
|
if ( $elt->{type} eq "set" ) { |
|
311
|
0
|
|
|
|
|
0
|
next; |
|
312
|
|
|
|
|
|
|
} |
|
313
|
|
|
|
|
|
|
|
|
314
|
0
|
0
|
|
|
|
0
|
if ( $elt->{type} eq "control" ) { |
|
315
|
0
|
|
|
|
|
0
|
next; |
|
316
|
|
|
|
|
|
|
} |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# Ignore everyting else. |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
} |
|
321
|
7
|
50
|
|
|
|
25
|
push(@s, "// $line End of $ctx") if $ctx; |
|
322
|
|
|
|
|
|
|
|
|
323
|
7
|
|
|
|
|
70
|
\@s; |
|
324
|
|
|
|
|
|
|
} |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
sub songline { |
|
327
|
0
|
|
|
0
|
0
|
0
|
my ($elt) = @_; |
|
328
|
|
|
|
|
|
|
|
|
329
|
0
|
|
|
|
|
0
|
my $t_line = ""; |
|
330
|
|
|
|
|
|
|
|
|
331
|
0
|
0
|
0
|
|
|
0
|
if ( $single_space && ! ( $elt->{chords} && join( "", @{ $elt->{chords} } ) =~ /\S/ ) |
|
|
|
|
0
|
|
|
|
|
|
332
|
|
|
|
|
|
|
) { |
|
333
|
0
|
|
|
|
|
0
|
$t_line = join( "", @{ $elt->{phrases} } ); |
|
|
0
|
|
|
|
|
0
|
|
|
334
|
0
|
|
|
|
|
0
|
$t_line =~ s/\s+$//; |
|
335
|
0
|
|
|
|
|
0
|
return $t_line; |
|
336
|
|
|
|
|
|
|
} |
|
337
|
|
|
|
|
|
|
|
|
338
|
0
|
0
|
|
|
|
0
|
unless ( $elt->{chords} ) { |
|
339
|
0
|
|
|
|
|
0
|
return ( "", join( " ", @{ $elt->{phrases} } ) ); |
|
|
0
|
|
|
|
|
0
|
|
|
340
|
|
|
|
|
|
|
} |
|
341
|
|
|
|
|
|
|
|
|
342
|
0
|
0
|
|
|
|
0
|
if ( my $f = $::config->{settings}->{'inline-chords'} ) { |
|
343
|
0
|
0
|
|
|
|
0
|
$f = '[%s]' unless $f =~ /^[^%]*\%s[^%]*$/; |
|
344
|
0
|
|
|
|
|
0
|
$f .= '%s'; |
|
345
|
0
|
|
|
|
|
0
|
foreach ( 0..$#{$elt->{chords}} ) { |
|
|
0
|
|
|
|
|
0
|
|
|
346
|
|
|
|
|
|
|
$t_line .= sprintf( $f, |
|
347
|
|
|
|
|
|
|
$elt->{chords}->[$_]->key, |
|
348
|
0
|
|
|
|
|
0
|
$elt->{phrases}->[$_] ); |
|
349
|
|
|
|
|
|
|
} |
|
350
|
0
|
|
|
|
|
0
|
return ( $t_line ); |
|
351
|
|
|
|
|
|
|
} |
|
352
|
|
|
|
|
|
|
|
|
353
|
0
|
|
|
|
|
0
|
my $c_line = ""; |
|
354
|
0
|
|
|
|
|
0
|
foreach ( 0..$#{$elt->{chords}} ) { |
|
|
0
|
|
|
|
|
0
|
|
|
355
|
0
|
|
|
|
|
0
|
$c_line .= $elt->{chords}->[$_]->key . " "; |
|
356
|
0
|
|
|
|
|
0
|
$t_line .= $elt->{phrases}->[$_]; |
|
357
|
0
|
|
|
|
|
0
|
my $d = length($c_line) - length($t_line); |
|
358
|
0
|
0
|
|
|
|
0
|
$t_line .= "-" x $d if $d > 0; |
|
359
|
0
|
0
|
|
|
|
0
|
$c_line .= " " x -$d if $d < 0; |
|
360
|
|
|
|
|
|
|
} |
|
361
|
0
|
|
|
|
|
0
|
s/\s+$// for ( $t_line, $c_line ); |
|
362
|
0
|
0
|
|
|
|
0
|
return $chords_under |
|
363
|
|
|
|
|
|
|
? ( $t_line, $c_line ) |
|
364
|
|
|
|
|
|
|
: ( $c_line, $t_line ) |
|
365
|
|
|
|
|
|
|
} |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub is_bar { |
|
368
|
102
|
|
|
102
|
0
|
181
|
for ( $_[0] ) { |
|
369
|
102
|
50
|
33
|
|
|
1009
|
return 1 |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
370
|
|
|
|
|
|
|
if $_ eq "|:" || $_ eq "{" |
|
371
|
|
|
|
|
|
|
|| $_ eq ":|" || $_ eq "}" |
|
372
|
|
|
|
|
|
|
|| $_ eq ":|:" || $_ eq "}{" |
|
373
|
|
|
|
|
|
|
|| $_ eq "|" || $_ eq "||" || $_ eq "|."; |
|
374
|
|
|
|
|
|
|
} |
|
375
|
1
|
|
|
|
|
13
|
return; |
|
376
|
|
|
|
|
|
|
} |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub reduce { |
|
379
|
9
|
|
|
9
|
0
|
23
|
my ( $a, $bpm, $line, $s ) = @_; |
|
380
|
9
|
|
|
|
|
59
|
my @a = @$a; |
|
381
|
9
|
50
|
|
|
|
30
|
warn("R: ", join(' ',@a), "\n") if $config->{debug}->{mma}; |
|
382
|
9
|
|
|
|
|
12
|
my @reduced; |
|
383
|
9
|
|
|
|
|
17
|
my $bar = 0; |
|
384
|
9
|
|
|
|
|
15
|
my $carry; |
|
385
|
|
|
|
|
|
|
|
|
386
|
9
|
|
|
|
|
24
|
while ( @a ) { |
|
387
|
24
|
|
|
|
|
31
|
$bar++; |
|
388
|
24
|
50
|
|
|
|
49
|
if ( $carry ) { |
|
389
|
0
|
0
|
|
|
|
0
|
if ( $a[0] eq '.' ) { |
|
390
|
0
|
|
|
|
|
0
|
$a[0] = $carry; |
|
391
|
|
|
|
|
|
|
} |
|
392
|
|
|
|
|
|
|
else { |
|
393
|
0
|
|
|
|
|
0
|
push( @$s, |
|
394
|
|
|
|
|
|
|
sprintf( "// line %d, bar %d, cannot resolve %s (from previous line)", |
|
395
|
|
|
|
|
|
|
$line, $bar, $carry ) ); |
|
396
|
|
|
|
|
|
|
} |
|
397
|
0
|
|
|
|
|
0
|
$carry = ''; |
|
398
|
|
|
|
|
|
|
} |
|
399
|
24
|
|
|
|
|
52
|
for ( my $b = 1; $b <= $bpm; $b++ ) { |
|
400
|
88
|
|
|
|
|
124
|
my $a0 = shift(@a); |
|
401
|
88
|
|
|
|
|
127
|
my $a1 = shift(@a); |
|
402
|
|
|
|
|
|
|
# Check for clash. |
|
403
|
88
|
100
|
100
|
|
|
192
|
if ( $a0 ne '.' && $a1 ne '.' ) { |
|
404
|
1
|
50
|
33
|
|
|
13
|
if ( @a > 1 && $a[0] eq '.' && $a[1] eq '.' ) { |
|
|
|
|
33
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# X Y . . => X . Y . |
|
406
|
1
|
|
|
|
|
4
|
$a[0] = $a1; |
|
407
|
1
|
|
|
|
|
7
|
push( @$s, |
|
408
|
|
|
|
|
|
|
sprintf("// line %d, bar %d, beat %d: shifting %s to beat %d", |
|
409
|
|
|
|
|
|
|
$line, $bar, $b, $a[0], $b+1) ); |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
else { |
|
412
|
|
|
|
|
|
|
# Cannot resolve. |
|
413
|
0
|
|
|
|
|
0
|
push( @$s, |
|
414
|
|
|
|
|
|
|
sprintf( "// line %d, bar %d, beat %d: too many chords", |
|
415
|
|
|
|
|
|
|
$line, $bar, $b ) ); |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# Check for clash and try to resolve. |
|
420
|
|
|
|
|
|
|
# . X => X . |
|
421
|
88
|
100
|
100
|
|
|
269
|
if ( $a0 eq '.' && $a1 ne '.' ) { |
|
|
|
100
|
|
|
|
|
|
|
422
|
1
|
|
|
|
|
3
|
$a0 = $a1; |
|
423
|
1
|
|
|
|
|
9
|
push( @$s, |
|
424
|
|
|
|
|
|
|
sprintf( "// line %d, bar %d, beat %d: move back %s", |
|
425
|
|
|
|
|
|
|
$line, $bar, $b, $a1) ); |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
# x X . => x . X |
|
428
|
|
|
|
|
|
|
elsif ( $a1 ne '.' ) { |
|
429
|
1
|
50
|
33
|
|
|
12
|
if ( @a > 1 && is_bar($a[0]) && $a[1] eq '.' ) { |
|
|
|
50
|
33
|
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
430
|
0
|
|
|
|
|
0
|
$a[1] = $a1; |
|
431
|
0
|
|
|
|
|
0
|
push( @$s, |
|
432
|
|
|
|
|
|
|
sprintf( "// line %d, bar %d, beat %d: advancing %s", |
|
433
|
|
|
|
|
|
|
$line, $bar, $b, $a1) ); |
|
434
|
|
|
|
|
|
|
} |
|
435
|
|
|
|
|
|
|
elsif ( @a > 0 && $a[0] eq '.' ) { |
|
436
|
0
|
|
|
|
|
0
|
$a[0] = $a1; |
|
437
|
0
|
|
|
|
|
0
|
push( @$s, |
|
438
|
|
|
|
|
|
|
sprintf( "// line %d, bar %d, beat %d: advancing %s", |
|
439
|
|
|
|
|
|
|
$line, $bar, $b, $a1) ); |
|
440
|
|
|
|
|
|
|
} |
|
441
|
|
|
|
|
|
|
elsif ( !@a ) { |
|
442
|
0
|
|
|
|
|
0
|
$carry = $a1; |
|
443
|
0
|
|
|
|
|
0
|
push( @$s, |
|
444
|
|
|
|
|
|
|
sprintf( "// line %d, bar %d, beat %d: carry %s to next line", |
|
445
|
|
|
|
|
|
|
$line, $bar, $b, $a1) ); |
|
446
|
|
|
|
|
|
|
} |
|
447
|
|
|
|
|
|
|
} |
|
448
|
88
|
|
|
|
|
191
|
push( @reduced, $a0 ); |
|
449
|
|
|
|
|
|
|
} |
|
450
|
24
|
50
|
|
|
|
42
|
if ( is_bar($a[0]) ) { |
|
451
|
24
|
|
|
|
|
66
|
push( @reduced, shift(@a) ); |
|
452
|
|
|
|
|
|
|
} |
|
453
|
|
|
|
|
|
|
else { |
|
454
|
0
|
|
|
|
|
0
|
push( @$s, |
|
455
|
|
|
|
|
|
|
sprintf( "// line %d, bar %d, missing bar line?", $line, $bar ) ) |
|
456
|
|
|
|
|
|
|
} |
|
457
|
|
|
|
|
|
|
} |
|
458
|
9
|
|
|
|
|
56
|
return @reduced; |
|
459
|
|
|
|
|
|
|
} |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
sub makegroove { |
|
462
|
7
|
|
|
7
|
0
|
21
|
my ( $bpm, $q ) = @_; |
|
463
|
|
|
|
|
|
|
|
|
464
|
7
|
50
|
|
|
|
35
|
return ( "Groove $groove" ) if $groove; |
|
465
|
|
|
|
|
|
|
|
|
466
|
0
|
|
|
|
|
|
my @s; |
|
467
|
0
|
0
|
|
|
|
|
if ( $bpm == 3 ) { |
|
|
|
0
|
|
|
|
|
|
|
468
|
0
|
0
|
|
|
|
|
$q = 4 unless $q == 8; |
|
469
|
0
|
|
|
|
|
|
$groove = "Neutral$bpm$q"; |
|
470
|
|
|
|
|
|
|
} |
|
471
|
|
|
|
|
|
|
elsif ( $bpm == 6 ) { |
|
472
|
0
|
0
|
|
|
|
|
$q = 4 unless $q == 8; |
|
473
|
0
|
|
|
|
|
|
$groove = "Neutral$bpm$q"; |
|
474
|
|
|
|
|
|
|
} |
|
475
|
|
|
|
|
|
|
else { |
|
476
|
0
|
|
|
|
|
|
$groove = "Neutral44"; |
|
477
|
|
|
|
|
|
|
} |
|
478
|
|
|
|
|
|
|
|
|
479
|
0
|
|
|
|
|
|
my $seq; |
|
480
|
|
|
|
|
|
|
my $whole; |
|
481
|
|
|
|
|
|
|
|
|
482
|
0
|
0
|
0
|
|
|
|
if ( $bpm == 3 && $q == 4 ) { |
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
483
|
0
|
|
|
|
|
|
$whole = "2."; |
|
484
|
0
|
|
|
|
|
|
$seq = "{ 1 0 90; 2 0 30; 3 0 30 }"; |
|
485
|
|
|
|
|
|
|
} |
|
486
|
|
|
|
|
|
|
elsif ( $bpm == 3 && $q == 8 ) { |
|
487
|
0
|
|
|
|
|
|
$whole = "4."; |
|
488
|
0
|
|
|
|
|
|
$seq = "{ 1 0 90; 1.67 0 30; 2.33 0 30 }"; |
|
489
|
|
|
|
|
|
|
} |
|
490
|
|
|
|
|
|
|
elsif ( $bpm == 6 && $q == 8 ) { |
|
491
|
0
|
|
|
|
|
|
$whole = "1."; |
|
492
|
0
|
|
|
|
|
|
$seq = "{ 1 0 90; 2 0 30; 3 0 30; 4 0 80; 5 0 30; 6 0 30 }"; |
|
493
|
|
|
|
|
|
|
} |
|
494
|
|
|
|
|
|
|
else { # assume 4/4 |
|
495
|
0
|
|
|
|
|
|
$whole = "1"; |
|
496
|
0
|
|
|
|
|
|
$seq = "{ 1 0 90; 2 0 30; 3 0 50; 4 0 30 }"; |
|
497
|
|
|
|
|
|
|
} |
|
498
|
|
|
|
|
|
|
|
|
499
|
0
|
|
|
|
|
|
return split( /\n/, <
|
|
500
|
|
|
|
|
|
|
SeqClear |
|
501
|
|
|
|
|
|
|
SeqSize 1 |
|
502
|
|
|
|
|
|
|
Time $bpm/$q |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
Begin Drum-Side |
|
505
|
|
|
|
|
|
|
Tone KickDrum1 |
|
506
|
|
|
|
|
|
|
Sequence { 1.0 0 60 } |
|
507
|
|
|
|
|
|
|
Volume 30 |
|
508
|
|
|
|
|
|
|
End |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
Begin Drum-CHH |
|
511
|
|
|
|
|
|
|
Tone ClosedHiHat |
|
512
|
|
|
|
|
|
|
Sequence $seq |
|
513
|
|
|
|
|
|
|
Volume 30 |
|
514
|
|
|
|
|
|
|
End |
|
515
|
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
Begin Chord |
|
517
|
|
|
|
|
|
|
Channel 2 |
|
518
|
|
|
|
|
|
|
Voice ReedOrgan |
|
519
|
|
|
|
|
|
|
Sequence { 1 $whole 50 } |
|
520
|
|
|
|
|
|
|
Volume 30 |
|
521
|
|
|
|
|
|
|
Articulate 100 |
|
522
|
|
|
|
|
|
|
End |
|
523
|
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
DefGroove $groove |
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
Groove $groove |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
EOD |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
} |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
1; |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
unless ( caller) { |
|
535
|
|
|
|
|
|
|
my $bpm = 4; |
|
536
|
|
|
|
|
|
|
my @s = (); |
|
537
|
|
|
|
|
|
|
unless ( join( ' ', |
|
538
|
|
|
|
|
|
|
reduce( [split(' ','C . . . . . . . | C . . . . . . . | C . . . . . . . | C . . . . . . . |')], $bpm, 1, \@s) ) |
|
539
|
|
|
|
|
|
|
eq 'C . . . | C . . . | C . . . | C . . . |' |
|
540
|
|
|
|
|
|
|
) { |
|
541
|
|
|
|
|
|
|
warn("reduce error\n"); |
|
542
|
|
|
|
|
|
|
print "$_\n" for @s; |
|
543
|
|
|
|
|
|
|
} |
|
544
|
|
|
|
|
|
|
while ( <> ) { |
|
545
|
|
|
|
|
|
|
@s = (); |
|
546
|
|
|
|
|
|
|
chomp; |
|
547
|
|
|
|
|
|
|
print("=> ", join(' ',reduce([split(' ',$_)], $bpm, 1, \@s)), "\n"); |
|
548
|
|
|
|
|
|
|
print "$_\n" for @s; |
|
549
|
|
|
|
|
|
|
} |
|
550
|
|
|
|
|
|
|
} |