line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package main; |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $options; |
6
|
|
|
|
|
|
|
our $config; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package ChordPro::Song; |
9
|
|
|
|
|
|
|
|
10
|
81
|
|
|
81
|
|
565
|
use strict; |
|
81
|
|
|
|
|
170
|
|
|
81
|
|
|
|
|
2512
|
|
11
|
81
|
|
|
81
|
|
411
|
use warnings; |
|
81
|
|
|
|
|
209
|
|
|
81
|
|
|
|
|
1960
|
|
12
|
|
|
|
|
|
|
|
13
|
81
|
|
|
81
|
|
448
|
use ChordPro; |
|
81
|
|
|
|
|
169
|
|
|
81
|
|
|
|
|
1603
|
|
14
|
81
|
|
|
81
|
|
474
|
use ChordPro::Chords; |
|
81
|
|
|
|
|
202
|
|
|
81
|
|
|
|
|
1942
|
|
15
|
81
|
|
|
81
|
|
34137
|
use ChordPro::Chords::Appearance; |
|
81
|
|
|
|
|
227
|
|
|
81
|
|
|
|
|
2896
|
|
16
|
81
|
|
|
81
|
|
633
|
use ChordPro::Chords::Parser; |
|
81
|
|
|
|
|
221
|
|
|
81
|
|
|
|
|
1920
|
|
17
|
81
|
|
|
81
|
|
408
|
use ChordPro::Output::Common; |
|
81
|
|
|
|
|
192
|
|
|
81
|
|
|
|
|
3992
|
|
18
|
81
|
|
|
81
|
|
527
|
use ChordPro::Utils; |
|
81
|
|
|
|
|
168
|
|
|
81
|
|
|
|
|
7580
|
|
19
|
|
|
|
|
|
|
|
20
|
81
|
|
|
81
|
|
543
|
use Carp; |
|
81
|
|
|
|
|
227
|
|
|
81
|
|
|
|
|
3999
|
|
21
|
81
|
|
|
81
|
|
549
|
use List::Util qw(any); |
|
81
|
|
|
|
|
168
|
|
|
81
|
|
|
|
|
5001
|
|
22
|
81
|
|
|
81
|
|
40796
|
use File::LoadLines; |
|
81
|
|
|
|
|
1073689
|
|
|
81
|
|
|
|
|
5367
|
|
23
|
81
|
|
|
81
|
|
696
|
use Storable qw(dclone); |
|
81
|
|
|
|
|
201
|
|
|
81
|
|
|
|
|
3633
|
|
24
|
81
|
|
|
81
|
|
559
|
use feature 'state'; |
|
81
|
|
|
|
|
225
|
|
|
81
|
|
|
|
|
6086
|
|
25
|
81
|
|
|
81
|
|
581
|
use Text::ParseWords qw(quotewords); |
|
81
|
|
|
|
|
207
|
|
|
81
|
|
|
|
|
4741
|
|
26
|
81
|
|
|
81
|
|
567
|
use File::Basename qw(basename); |
|
81
|
|
|
|
|
210
|
|
|
81
|
|
|
|
|
916705
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# Parser context. |
29
|
|
|
|
|
|
|
my $def_context = ""; |
30
|
|
|
|
|
|
|
my $in_context = $def_context; |
31
|
|
|
|
|
|
|
my $skip_context = 0; |
32
|
|
|
|
|
|
|
my $grid_arg; |
33
|
|
|
|
|
|
|
my $grid_cells; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# Local transposition. |
36
|
|
|
|
|
|
|
my $xpose = 0; |
37
|
|
|
|
|
|
|
my $xpose_dir; |
38
|
|
|
|
|
|
|
my $capo; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# Used chords, in order of appearance. |
41
|
|
|
|
|
|
|
my @used_chords; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# Chorus lines, if any. |
44
|
|
|
|
|
|
|
my @chorus; |
45
|
|
|
|
|
|
|
my $chorus_xpose = 0; |
46
|
|
|
|
|
|
|
my $chorus_xpose_dir = 0; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# Memorized chords. |
49
|
|
|
|
|
|
|
my %memchords; # all sections |
50
|
|
|
|
|
|
|
my $memchords; # current section |
51
|
|
|
|
|
|
|
my $memcrdinx; # chords tally |
52
|
|
|
|
|
|
|
my $memorizing; # if memorizing (a.o.t. recalling) |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# Keep track of unknown chords, to avoid dup warnings. |
55
|
|
|
|
|
|
|
my %warned_chords; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
my $re_chords; # for chords |
58
|
|
|
|
|
|
|
my $intervals; # number of note intervals |
59
|
|
|
|
|
|
|
my @labels; # labels used |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# Normally, transposition and subtitutions are handled by the parser. |
62
|
|
|
|
|
|
|
my $decapo; |
63
|
|
|
|
|
|
|
my $no_transpose; # NYI |
64
|
|
|
|
|
|
|
my $xcmov; # transcode to movable system |
65
|
|
|
|
|
|
|
my $no_substitute; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# Stack for properties like textsize. |
68
|
|
|
|
|
|
|
my %propstack; |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
my $diag; # for diagnostics |
71
|
|
|
|
|
|
|
my $lineinfo; # keep lineinfo |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# Constructor. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub new { |
76
|
172
|
|
|
172
|
0
|
2922
|
my ( $pkg, $filesource ) = @_; |
77
|
|
|
|
|
|
|
|
78
|
172
|
|
|
|
|
406
|
$xpose = 0; |
79
|
172
|
|
|
|
|
631
|
$grid_arg = [ 4, 4, 1, 1 ]; # 1+4x4+1 |
80
|
172
|
|
|
|
|
452
|
$in_context = $def_context; |
81
|
172
|
|
|
|
|
451
|
@used_chords = (); |
82
|
172
|
|
|
|
|
400
|
%warned_chords = (); |
83
|
172
|
|
|
|
|
411
|
%memchords = (); |
84
|
172
|
|
|
|
|
397
|
%propstack = (); |
85
|
172
|
|
|
|
|
888
|
ChordPro::Chords::reset_song_chords(); |
86
|
172
|
|
|
|
|
393
|
@labels = (); |
87
|
172
|
|
|
|
|
1580
|
@chorus = (); |
88
|
172
|
|
|
|
|
346
|
$capo = undef; |
89
|
172
|
|
|
|
|
335
|
$xcmov = undef; |
90
|
172
|
|
|
|
|
696
|
upd_config(); |
91
|
|
|
|
|
|
|
|
92
|
172
|
|
|
|
|
627
|
$diag->{format} = $config->{diagnostics}->{format}; |
93
|
172
|
|
|
|
|
469
|
$diag->{file} = $filesource; |
94
|
172
|
|
|
|
|
455
|
$diag->{line} = 0; |
95
|
172
|
|
|
|
|
465
|
$diag->{orig} = "(at start of song)"; |
96
|
|
|
|
|
|
|
|
97
|
172
|
|
|
|
|
1877
|
bless { chordsinfo => {}, |
98
|
|
|
|
|
|
|
meta => {}, |
99
|
|
|
|
|
|
|
structure => "linear", |
100
|
|
|
|
|
|
|
} => $pkg; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub upd_config { |
104
|
353
|
|
|
353
|
0
|
1207
|
$decapo = $config->{settings}->{decapo}; |
105
|
353
|
|
|
|
|
764
|
$lineinfo = $config->{settings}->{lineinfo}; |
106
|
353
|
|
|
|
|
575
|
$intervals = @{ $config->{notes}->{sharp} }; |
|
353
|
|
|
|
|
919
|
|
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
170
|
|
|
sub ::break() {} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub parse_song { |
112
|
170
|
|
|
170
|
0
|
642
|
my ( $self, $lines, $linecnt, $meta, $defs ) = @_; |
113
|
170
|
50
|
|
|
|
716
|
die("OOPS! Wrong meta") unless ref($meta) eq 'HASH'; |
114
|
170
|
|
|
|
|
185003
|
local $config = dclone($config); |
115
|
|
|
|
|
|
|
|
116
|
170
|
50
|
|
|
|
1323
|
warn("Processing song ", $diag->{file}, "...\n") if $options->{verbose}; |
117
|
170
|
|
|
|
|
779
|
::break(); |
118
|
170
|
|
|
|
|
361
|
my @configs; |
119
|
|
|
|
|
|
|
# |
120
|
170
|
50
|
|
|
|
1030
|
if ( $lines->[0] =~ /^##config:\s*json/ ) { |
121
|
0
|
|
|
|
|
0
|
my $cf = ""; |
122
|
0
|
|
|
|
|
0
|
shift(@$lines); |
123
|
0
|
|
|
|
|
0
|
$$linecnt++; |
124
|
0
|
|
|
|
|
0
|
while ( @$lines ) { |
125
|
0
|
0
|
|
|
|
0
|
if ( $lines->[0] =~ /^# (.*)/ ) { |
126
|
0
|
|
|
|
|
0
|
$cf .= $1 . "\n"; |
127
|
0
|
|
|
|
|
0
|
shift(@$lines); |
128
|
0
|
|
|
|
|
0
|
$$linecnt++; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
else { |
131
|
0
|
|
|
|
|
0
|
last; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
} |
134
|
0
|
0
|
|
|
|
0
|
if ( $cf ) { |
135
|
0
|
|
|
|
|
0
|
my $pp = JSON::PP->new->relaxed; |
136
|
0
|
|
|
|
|
0
|
my $precfg = $pp->decode($cf); |
137
|
0
|
|
|
|
|
0
|
my $prename = "__PRECFG__"; |
138
|
0
|
|
|
|
|
0
|
ChordPro::Config::precheck( $precfg, $prename ); |
139
|
0
|
|
|
|
|
0
|
push( @configs, ChordPro::Config::prep_configs( $precfg, $prename) ); |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
# Load song-specific config, if any. |
143
|
170
|
50
|
66
|
|
|
998
|
if ( !$options->{nosongconfig} && $diag->{file} ) { |
144
|
79
|
50
|
|
|
|
286
|
if ( $options->{verbose} ) { |
145
|
0
|
|
|
|
|
0
|
my $this = ChordPro::Chords::get_parser(); |
146
|
0
|
0
|
|
|
|
0
|
$this = defined($this) ? $this->{system} : ""; |
147
|
0
|
|
|
|
|
0
|
print STDERR ("Parsers at start of ", $diag->{file}, ":"); |
148
|
|
|
|
|
|
|
print STDERR ( $this eq $_ ? " *" : " ", "$_") |
149
|
0
|
0
|
|
|
|
0
|
for keys %{ ChordPro::Chords::Parser->parsers }; |
|
0
|
|
|
|
|
0
|
|
150
|
0
|
|
|
|
|
0
|
print STDERR ("\n"); |
151
|
|
|
|
|
|
|
} |
152
|
79
|
50
|
33
|
|
|
482
|
if ( $meta && $meta->{__config} ) { |
153
|
0
|
|
|
|
|
0
|
my $cf = delete($meta->{__config})->[0]; |
154
|
0
|
0
|
|
|
|
0
|
die("Missing config: $cf\n") unless -s $cf; |
155
|
0
|
0
|
|
|
|
0
|
warn("Config[song]: $cf\n") if $options->{verbose}; |
156
|
0
|
|
|
|
|
0
|
my $have = ChordPro::Config::get_config($cf); |
157
|
0
|
|
|
|
|
0
|
push( @configs, ChordPro::Config::prep_configs( $have, $cf) ); |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
else { |
160
|
79
|
|
|
|
|
277
|
for ( "prp", "json" ) { |
161
|
158
|
|
|
|
|
712
|
( my $cf = $diag->{file} ) =~ s/\.\w+$/.$_/; |
162
|
158
|
100
|
|
|
|
664
|
$cf .= ".$_" if $cf eq $diag->{file}; |
163
|
158
|
50
|
|
|
|
2479
|
next unless -s $cf; |
164
|
0
|
0
|
|
|
|
0
|
warn("Config[song]: $cf\n") if $options->{verbose}; |
165
|
0
|
|
|
|
|
0
|
my $have = ChordPro::Config::get_config($cf); |
166
|
0
|
|
|
|
|
0
|
push( @configs, ChordPro::Config::prep_configs( $have, $cf) ); |
167
|
0
|
|
|
|
|
0
|
last; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
} |
171
|
170
|
|
|
|
|
578
|
my $tuncheck = join("|",@{$config->{tuning}}); |
|
170
|
|
|
|
|
841
|
|
172
|
170
|
|
|
|
|
676
|
foreach my $have ( @configs ) { |
173
|
0
|
0
|
|
|
|
0
|
warn("Config[song*]: ", $have->{_src}, "\n") if $options->{verbose}; |
174
|
0
|
|
|
|
|
0
|
my $chords = $have->{chords}; |
175
|
0
|
|
|
|
|
0
|
$config->augment($have); |
176
|
0
|
0
|
|
|
|
0
|
if ( $tuncheck ne join("|",@{$config->{tuning}}) ) { |
|
0
|
|
|
|
|
0
|
|
177
|
0
|
|
|
|
|
0
|
my $res = |
178
|
|
|
|
|
|
|
ChordPro::Chords::set_tuning($config); |
179
|
0
|
0
|
|
|
|
0
|
warn( "Invalid tuning in config: ", $res, "\n" ) if $res; |
180
|
|
|
|
|
|
|
} |
181
|
0
|
|
|
|
|
0
|
ChordPro::Chords::reset_parser(); |
182
|
0
|
|
|
|
|
0
|
ChordPro::Chords::Parser->reset_parsers; |
183
|
0
|
0
|
|
|
|
0
|
if ( $chords ) { |
184
|
0
|
|
|
|
|
0
|
my $c = $chords; |
185
|
0
|
0
|
0
|
|
|
0
|
if ( @$c && $c->[0] eq "append" ) { |
186
|
0
|
|
|
|
|
0
|
shift(@$c); |
187
|
|
|
|
|
|
|
} |
188
|
0
|
|
|
|
|
0
|
foreach ( @$c ) { |
189
|
0
|
|
|
|
|
0
|
my $res = |
190
|
|
|
|
|
|
|
ChordPro::Chords::add_config_chord($_); |
191
|
|
|
|
|
|
|
warn( "Invalid chord in config: ", |
192
|
0
|
0
|
|
|
|
0
|
$_->{name}, ": ", $res, "\n" ) if $res; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
} |
195
|
0
|
0
|
|
|
|
0
|
if ( $options->{verbose} > 1 ) { |
196
|
0
|
0
|
|
|
|
0
|
warn( "Processed ", scalar(@$chords), " chord entries\n") |
197
|
|
|
|
|
|
|
if $chords; |
198
|
0
|
|
|
|
|
0
|
warn( "Totals: ", |
199
|
|
|
|
|
|
|
ChordPro::Chords::chord_stats(), "\n" ); |
200
|
|
|
|
|
|
|
} |
201
|
0
|
|
|
|
|
0
|
if ( 0 && $options->{verbose} ) { |
202
|
|
|
|
|
|
|
my $this = ChordPro::Chords::get_parser()->{system}; |
203
|
|
|
|
|
|
|
print STDERR ("Parsers after local config:"); |
204
|
|
|
|
|
|
|
print STDERR ( $this eq $_ ? " *" : " ", "$_") |
205
|
|
|
|
|
|
|
for keys %{ ChordPro::Chords::Parser->parsers }; |
206
|
|
|
|
|
|
|
print STDERR ("\n"); |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
170
|
|
|
|
|
1218
|
$config->unlock; |
211
|
|
|
|
|
|
|
|
212
|
170
|
50
|
|
|
|
292990
|
if ( %$defs ) { |
213
|
0
|
|
|
|
|
0
|
my $c = $config->hmerge( prp2cfg( $defs, $config ) ); |
214
|
0
|
|
|
|
|
0
|
bless $c => ref($config); |
215
|
0
|
|
|
|
|
0
|
$config = $c; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
170
|
|
|
|
|
658
|
for ( qw( transpose transcode decapo lyrics-only ) ) { |
219
|
680
|
100
|
|
|
|
2030
|
next unless defined $options->{$_}; |
220
|
18
|
|
|
|
|
79
|
$config->{settings}->{$_} = $options->{$_}; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
# Catch common error. |
223
|
170
|
50
|
|
|
|
1180
|
unless ( UNIVERSAL::isa( $config->{instrument}, 'HASH' ) ) { |
224
|
0
|
|
0
|
|
|
0
|
$config->{instrument} //= "guitar"; |
225
|
|
|
|
|
|
|
$config->{instrument} = |
226
|
|
|
|
|
|
|
{ type => $config->{instrument}, |
227
|
0
|
|
|
|
|
0
|
description => ucfirst $config->{instrument} }; |
228
|
|
|
|
|
|
|
do_warn( "Missing or invalid instrument - set to ", |
229
|
0
|
|
|
|
|
0
|
$config->{instrument}->{type}, "\n" ); |
230
|
|
|
|
|
|
|
} |
231
|
170
|
|
|
|
|
915
|
$config->lock; |
232
|
170
|
|
|
|
|
314899
|
for ( keys %{ $config->{meta} } ) { |
|
170
|
|
|
|
|
1005
|
|
233
|
0
|
|
0
|
|
|
0
|
$meta->{$_} //= []; |
234
|
0
|
0
|
|
|
|
0
|
if ( UNIVERSAL::isa($config->{meta}->{$_}, 'ARRAY') ) { |
235
|
0
|
|
|
|
|
0
|
push( @{ $meta->{$_} }, @{ $config->{meta}->{$_} } ); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
else { |
238
|
0
|
|
|
|
|
0
|
push( @{ $meta->{$_} }, $config->{meta}->{$_} ); |
|
0
|
|
|
|
|
0
|
|
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
170
|
|
|
|
|
561
|
$no_transpose = $options->{'no-transpose'}; |
243
|
170
|
|
|
|
|
419
|
$no_substitute = $options->{'no-substitute'}; |
244
|
170
|
|
|
|
|
421
|
my $fragment = $options->{fragment}; |
245
|
170
|
|
|
|
|
497
|
my $target = $config->{settings}->{transcode}; |
246
|
170
|
100
|
|
|
|
585
|
if ( $target ) { |
247
|
2
|
50
|
|
|
|
13
|
unless ( ChordPro::Chords::Parser->have_parser($target) ) { |
248
|
2
|
50
|
|
|
|
19
|
if ( my $file = ::rsc_or_file("config/notes/$target.json") ) { |
249
|
2
|
|
|
|
|
11
|
for ( ChordPro::Config::get_config($file) ) { |
250
|
2
|
|
|
|
|
15
|
my $new = $config->hmerge($_); |
251
|
2
|
|
|
|
|
12
|
local $config = $new; |
252
|
2
|
|
|
|
|
25
|
ChordPro::Chords::Parser->new($new); |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
} |
256
|
2
|
50
|
|
|
|
22
|
unless ( ChordPro::Chords::Parser->have_parser($target) ) { |
257
|
0
|
|
|
|
|
0
|
die("No transcoder for ", $target, "\n"); |
258
|
|
|
|
|
|
|
} |
259
|
2
|
50
|
|
|
|
13
|
warn("Got transcoder for $target\n") if $::options->{verbose}; |
260
|
2
|
|
|
|
|
14
|
ChordPro::Chords::set_parser($target); |
261
|
2
|
|
|
|
|
9
|
my $p = ChordPro::Chords::get_parser; |
262
|
2
|
|
|
|
|
11
|
$xcmov = $p->movable; |
263
|
2
|
50
|
|
|
|
10
|
if ( $target ne $p->{system} ) { |
264
|
0
|
|
|
|
|
0
|
::dump(ChordPro::Chords::Parser->parsers); |
265
|
|
|
|
|
|
|
warn("OOPS parser mixup, $target <> ", |
266
|
|
|
|
|
|
|
ChordPro::Chords::get_parser->{system}) |
267
|
0
|
|
|
|
|
0
|
} |
268
|
2
|
|
|
|
|
19
|
ChordPro::Chords::set_parser($self->{system}); |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
else { |
271
|
168
|
|
|
|
|
704
|
$target = $self->{system}; |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
170
|
|
|
|
|
588
|
upd_config(); |
275
|
170
|
|
|
|
|
1293
|
$self->{source} = { file => $diag->{file}, line => 1 + $$linecnt }; |
276
|
170
|
|
|
|
|
609
|
$self->{system} = $config->{notes}->{system}; |
277
|
170
|
|
|
|
|
481
|
$self->{config} = $config; |
278
|
170
|
50
|
|
|
|
781
|
$self->{meta} = $meta if $meta; |
279
|
170
|
|
|
|
|
471
|
$self->{chordsinfo} = {}; |
280
|
170
|
|
66
|
|
|
1067
|
$target //= $self->{system}; |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
# Preprocessor. |
283
|
170
|
|
|
|
|
1020
|
my $prep = make_preprocessor( $config->{parser}->{preprocess} ); |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
# Pre-fill meta data, if any. TODO? ALREADY DONE? |
286
|
170
|
50
|
|
|
|
786
|
if ( $options->{meta} ) { |
287
|
0
|
|
|
|
|
0
|
while ( my ($k, $v ) = each( %{ $options->{meta} } ) ) { |
|
0
|
|
|
|
|
0
|
|
288
|
0
|
|
|
|
|
0
|
$self->{meta}->{$k} = [ $v ]; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# Build regexp to split out chords. |
293
|
170
|
100
|
|
|
|
992
|
if ( $config->{settings}->{memorize} ) { |
294
|
1
|
|
|
|
|
7
|
$re_chords = qr/(\[.*?\]|\^)/; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
else { |
297
|
169
|
|
|
|
|
2621
|
$re_chords = qr/(\[.*?\])/; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
170
|
|
|
|
|
486
|
my $skipcnt = 0; |
301
|
170
|
|
|
|
|
600
|
while ( @$lines ) { |
302
|
1936
|
50
|
|
|
|
3738
|
if ( $skipcnt ) { |
303
|
0
|
|
|
|
|
0
|
$skipcnt--; |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
else { |
306
|
1936
|
|
|
|
|
3838
|
$diag->{line} = ++$$linecnt; |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
1936
|
|
|
|
|
3724
|
$_ = shift(@$lines); |
310
|
1936
|
|
33
|
|
|
6059
|
while ( /\\\Z/ && @$lines ) { |
311
|
0
|
|
|
|
|
0
|
chop; |
312
|
0
|
|
|
|
|
0
|
my $cont = shift(@$lines); |
313
|
0
|
|
|
|
|
0
|
$$linecnt++; |
314
|
0
|
|
|
|
|
0
|
$cont =~ s/^\s+//; |
315
|
0
|
|
|
|
|
0
|
$_ .= $cont; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# Uncomment this to allow \uXXXX escapes. |
319
|
1936
|
|
|
|
|
6529
|
s/\\u([0-9a-f]{4})/chr(hex("0x$1"))/ige; |
|
0
|
|
|
|
|
0
|
|
320
|
|
|
|
|
|
|
# Uncomment this to allow \u{XX...} escapes. |
321
|
|
|
|
|
|
|
# s/\\u\{([0-9a-f]+)\}/chr(hex("0x$1"))/ige; |
322
|
|
|
|
|
|
|
|
323
|
1936
|
|
|
|
|
172087
|
$diag->{orig} = $_; |
324
|
|
|
|
|
|
|
# Get rid of TABs. |
325
|
1936
|
|
|
|
|
3555
|
s/\t/ /g; |
326
|
|
|
|
|
|
|
|
327
|
1936
|
50
|
|
|
|
4547
|
if ( $config->{debug}->{echo} ) { |
328
|
0
|
|
|
|
|
0
|
warn(sprintf("==[%3d]=> %s\n", $diag->{line}, $diag->{orig} ) ); |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
1936
|
50
|
|
|
|
4029
|
if ( $prep->{all} ) { |
332
|
|
|
|
|
|
|
# warn("PRE: ", $_, "\n"); |
333
|
0
|
|
|
|
|
0
|
$prep->{all}->($_); |
334
|
|
|
|
|
|
|
# warn("POST: ", $_, "\n"); |
335
|
0
|
0
|
|
|
|
0
|
if ( /\n/ ) { |
336
|
0
|
|
|
|
|
0
|
my @a = split( /\n/, $_ ); |
337
|
0
|
|
|
|
|
0
|
$_ = shift(@a); |
338
|
0
|
|
|
|
|
0
|
unshift( @$lines, @a ); |
339
|
0
|
|
|
|
|
0
|
$skipcnt += @a; |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
1936
|
100
|
|
|
|
3683
|
if ( $skip_context ) { |
344
|
4
|
100
|
|
|
|
23
|
if ( /^\s*\{(\w+)\}\s*$/ ) { |
345
|
2
|
|
|
|
|
8
|
my $dir = $self->parse_directive($1); |
346
|
2
|
50
|
|
|
|
9
|
if ( $dir->{name} eq "end_of_$in_context" ) { |
347
|
2
|
|
|
|
|
4
|
$in_context = $def_context; |
348
|
2
|
|
|
|
|
167
|
$skip_context = 0; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
} |
351
|
4
|
|
|
|
|
10
|
next; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
1932
|
100
|
|
|
|
4861
|
if ( /^\s*\{(new_song|ns)\}\s*$/ ) { |
355
|
32
|
100
|
|
|
|
149
|
last if $self->{body}; |
356
|
3
|
|
|
|
|
14
|
next; |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
|
359
|
1900
|
100
|
|
|
|
3976
|
if ( /^#/ ) { |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# Handle assets. |
362
|
55
|
|
|
|
|
140
|
my $kw = ""; |
363
|
55
|
|
|
|
|
128
|
my $kv = {}; |
364
|
55
|
100
|
|
|
|
166
|
if ( /^##(image|asset):\s+(.*)/i ) { |
365
|
1
|
|
|
|
|
3
|
$kw = lc($1); |
366
|
1
|
|
|
|
|
4
|
$kv = parse_kv($2); |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
55
|
100
|
|
|
|
154
|
if ( $kw eq "image" ) { |
370
|
1
|
|
|
|
|
5
|
my $id = $kv->{id}; |
371
|
1
|
50
|
|
|
|
3
|
unless ( $id ) { |
372
|
0
|
|
|
|
|
0
|
do_warn("Missing id for image asset\n"); |
373
|
0
|
|
|
|
|
0
|
next; |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# In-line image asset. |
377
|
1
|
|
|
|
|
502
|
require MIME::Base64; |
378
|
1
|
|
|
|
|
694
|
require Image::Info; |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# Read the image. |
381
|
1
|
|
|
|
|
3
|
my $data = ''; |
382
|
1
|
|
66
|
|
|
12
|
while ( @$lines && $lines->[0] =~ /^# (.+)/ ) { |
383
|
3
|
|
|
|
|
15
|
$data .= MIME::Base64::decode($1); |
384
|
3
|
|
|
|
|
13
|
shift(@$lines); |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# Get info. |
388
|
1
|
|
|
|
|
11
|
my $info = Image::Info::image_info(\$data); |
389
|
1
|
50
|
|
|
|
4282
|
if ( $info->{error} ) { |
390
|
0
|
|
|
|
|
0
|
do_warn($info->{error}); |
391
|
0
|
|
|
|
|
0
|
next; |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
# Store in assets. |
395
|
1
|
|
50
|
|
|
10
|
$self->{assets} //= {}; |
396
|
|
|
|
|
|
|
$self->{assets}->{$id} = |
397
|
|
|
|
|
|
|
{ data => $data, type => $info->{file_ext}, |
398
|
|
|
|
|
|
|
width => $info->{width}, height => $info->{height}, |
399
|
1
|
50
|
|
|
|
10
|
$kv->{persist} ? ( persist => 1 ) : (), |
400
|
|
|
|
|
|
|
}; |
401
|
|
|
|
|
|
|
|
402
|
1
|
50
|
|
|
|
5
|
if ( $config->{debug}->{images} ) { |
403
|
|
|
|
|
|
|
warn("asset[$id] ", length($data), " bytes, ", |
404
|
|
|
|
|
|
|
"width=$info->{width}, height=$info->{height}", |
405
|
0
|
0
|
|
|
|
0
|
$kv->{persist} ? ", persist" : "", |
406
|
|
|
|
|
|
|
"\n"); |
407
|
|
|
|
|
|
|
} |
408
|
1
|
|
|
|
|
16
|
next; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
|
411
|
54
|
50
|
|
|
|
126
|
if ( $kw eq "asset" ) { |
412
|
0
|
|
|
|
|
0
|
my $id = $kv->{id}; |
413
|
0
|
|
|
|
|
0
|
my $type = $kv->{type}; |
414
|
0
|
0
|
|
|
|
0
|
unless ( $id ) { |
415
|
0
|
|
|
|
|
0
|
do_warn("Missing id for asset\n"); |
416
|
0
|
|
|
|
|
0
|
next; |
417
|
|
|
|
|
|
|
} |
418
|
0
|
0
|
|
|
|
0
|
unless ( $type ) { |
419
|
0
|
|
|
|
|
0
|
do_warn("Missing type for asset\n"); |
420
|
0
|
|
|
|
|
0
|
next; |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
# Read the data. |
424
|
0
|
|
|
|
|
0
|
my @data; |
425
|
0
|
|
0
|
|
|
0
|
while ( @$lines && $lines->[0] =~ /^# (.+)/ ) { |
426
|
0
|
|
|
|
|
0
|
push( @data, $1 ); |
427
|
0
|
|
|
|
|
0
|
shift(@$lines); |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# Store in assets. |
431
|
0
|
|
0
|
|
|
0
|
$self->{assets} //= {}; |
432
|
|
|
|
|
|
|
$self->{assets}->{$id} = |
433
|
|
|
|
|
|
|
{ data => \@data, type => $type, |
434
|
|
|
|
|
|
|
subtype => $config->{delegates}->{$type}->{type}, |
435
|
|
|
|
|
|
|
handler => $config->{delegates}->{$type}->{handler}, |
436
|
0
|
|
|
|
|
0
|
}; |
437
|
0
|
0
|
|
|
|
0
|
if ( $config->{debug}->{images} ) { |
438
|
0
|
|
|
|
|
0
|
warn("asset[$id] ", ::dump($self->{assets}->{$id})); |
439
|
|
|
|
|
|
|
} |
440
|
0
|
|
|
|
|
0
|
next; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# Collect pre-title stuff separately. |
444
|
54
|
50
|
33
|
|
|
153
|
if ( exists $self->{title} || $fragment ) { |
445
|
54
|
|
|
|
|
150
|
$self->add( type => "ignore", text => $_ ); |
446
|
|
|
|
|
|
|
} |
447
|
|
|
|
|
|
|
else { |
448
|
0
|
|
|
|
|
0
|
push( @{ $self->{preamble} }, $_ ); |
|
0
|
|
|
|
|
0
|
|
449
|
|
|
|
|
|
|
} |
450
|
54
|
|
|
|
|
134
|
next; |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
|
453
|
1845
|
100
|
|
|
|
3832
|
if ( $in_context eq "tab" ) { |
454
|
62
|
100
|
|
|
|
234
|
unless ( /^\s*\{(?:end_of_tab|eot)\}\s*$/ ) { |
455
|
49
|
|
|
|
|
166
|
$self->add( type => "tabline", text => $_ ); |
456
|
49
|
|
|
|
|
103
|
next; |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
|
460
|
1796
|
50
|
|
|
|
4120
|
if ( exists $config->{delegates}->{$in_context} ) { |
461
|
|
|
|
|
|
|
# 'open' indicates open. |
462
|
0
|
0
|
|
|
|
0
|
if ( /^\s*\{(?:end_of_\Q$in_context\E)\}\s*$/ ) { |
|
|
0
|
|
|
|
|
|
463
|
0
|
0
|
|
|
|
0
|
if ( $config->{delegates}->{$in_context}->{omit} ) { |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
else { |
466
|
0
|
|
|
|
|
0
|
delete $self->{body}->[-1]->{open}; |
467
|
|
|
|
|
|
|
# A subsequent {start_of_XXX} will reopen a new item |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
elsif ( $config->{delegates}->{$in_context}->{omit} ) { |
471
|
0
|
|
|
|
|
0
|
next; |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
else { |
474
|
|
|
|
|
|
|
# Add to an open item. |
475
|
0
|
0
|
0
|
|
|
0
|
if ( $self->{body} && @{ $self->{body} } |
|
0
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
|
|
476
|
|
|
|
|
|
|
&& $self->{body}->[-1]->{context} eq $in_context |
477
|
|
|
|
|
|
|
&& $self->{body}->[-1]->{open} ) { |
478
|
0
|
|
|
|
|
0
|
push( @{$self->{body}->[-1]->{data}}, $_ ); |
|
0
|
|
|
|
|
0
|
|
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
# Else start new item. |
482
|
|
|
|
|
|
|
else { |
483
|
0
|
|
|
|
|
0
|
my %opts; |
484
|
|
|
|
|
|
|
####TODO |
485
|
0
|
0
|
0
|
|
|
0
|
if ( $xpose || $config->{settings}->{transpose} ) { |
486
|
|
|
|
|
|
|
$opts{transpose} = |
487
|
0
|
|
0
|
|
|
0
|
$xpose + ($config->{settings}->{transpose}//0 ); |
488
|
|
|
|
|
|
|
} |
489
|
0
|
|
|
|
|
0
|
my $d = $config->{delegates}->{$in_context}; |
490
|
|
|
|
|
|
|
$self->add( type => "delegate", |
491
|
|
|
|
|
|
|
delegate => $d->{module}, |
492
|
|
|
|
|
|
|
subtype => $d->{type}, |
493
|
|
|
|
|
|
|
handler => $d->{handler}, |
494
|
0
|
|
|
|
|
0
|
data => [ $_ ], |
495
|
|
|
|
|
|
|
opts => \%opts, |
496
|
|
|
|
|
|
|
open => 1 ); |
497
|
|
|
|
|
|
|
} |
498
|
0
|
|
|
|
|
0
|
next; |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
# For now, directives should go on their own lines. |
503
|
1796
|
100
|
|
|
|
7273
|
if ( /^\s*\{(.*)\}\s*$/ ) { |
504
|
1004
|
|
|
|
|
3140
|
my $dir = $1; |
505
|
1004
|
50
|
|
|
|
2413
|
if ( $prep->{directive} ) { |
506
|
|
|
|
|
|
|
# warn("PRE: ", $_, "\n"); |
507
|
0
|
|
|
|
|
0
|
$prep->{directive}->($dir); |
508
|
|
|
|
|
|
|
# warn("POST: ", $_, "\n"); |
509
|
|
|
|
|
|
|
} |
510
|
1004
|
100
|
|
|
|
2834
|
$self->add( type => "ignore", |
511
|
|
|
|
|
|
|
text => $_ ) |
512
|
|
|
|
|
|
|
unless $self->directive($dir); |
513
|
1004
|
|
|
|
|
3218
|
next; |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
792
|
50
|
66
|
|
|
4957
|
if ( /\S/ && !$fragment && !exists $self->{title} ) { |
|
|
|
66
|
|
|
|
|
517
|
0
|
|
|
|
|
0
|
do_warn("Missing {title} -- prepare for surprising results"); |
518
|
0
|
|
|
|
|
0
|
unshift( @$lines, "{title:$_}"); |
519
|
0
|
|
|
|
|
0
|
$skipcnt++; |
520
|
0
|
|
|
|
|
0
|
next; |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
|
523
|
792
|
50
|
|
|
|
1793
|
if ( $in_context eq "tab" ) { |
524
|
0
|
|
|
|
|
0
|
$self->add( type => "tabline", text => $_ ); |
525
|
0
|
|
|
|
|
0
|
warn("OOPS"); |
526
|
0
|
|
|
|
|
0
|
next; |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
792
|
100
|
|
|
|
1594
|
if ( $in_context eq "grid" ) { |
530
|
39
|
|
|
|
|
165
|
$self->add( type => "gridline", $self->decompose_grid($_) ); |
531
|
39
|
|
|
|
|
136
|
next; |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
|
534
|
753
|
100
|
33
|
|
|
2504
|
if ( /\S/ ) { |
|
|
50
|
|
|
|
|
|
535
|
456
|
50
|
|
|
|
1106
|
if ( $prep->{songline} ) { |
536
|
|
|
|
|
|
|
# warn("PRE: ", $_, "\n"); |
537
|
0
|
|
|
|
|
0
|
$prep->{songline}->($_); |
538
|
|
|
|
|
|
|
# warn("POST: ", $_, "\n"); |
539
|
|
|
|
|
|
|
} |
540
|
456
|
|
|
|
|
1386
|
$self->add( type => "songline", $self->decompose($_) ); |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
elsif ( exists $self->{title} || $fragment ) { |
543
|
297
|
|
|
|
|
789
|
$self->add( type => "empty" ); |
544
|
|
|
|
|
|
|
} |
545
|
|
|
|
|
|
|
else { |
546
|
|
|
|
|
|
|
# Collect pre-title stuff separately. |
547
|
0
|
|
|
|
|
0
|
push( @{ $self->{preamble} }, $_ ); |
|
0
|
|
|
|
|
0
|
|
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
} |
550
|
170
|
50
|
|
|
|
711
|
do_warn("Unterminated context in song: $in_context") |
551
|
|
|
|
|
|
|
if $in_context; |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
# These don't make sense after processing. Or do they? |
554
|
|
|
|
|
|
|
# delete $self->{meta}->{$_} for qw( key_actual key_from ); |
555
|
|
|
|
|
|
|
|
556
|
170
|
50
|
|
|
|
647
|
warn("Processed song...\n") if $options->{verbose}; |
557
|
170
|
|
|
|
|
474
|
$diag->{format} = "\"%f\": %m"; |
558
|
|
|
|
|
|
|
|
559
|
170
|
50
|
|
|
|
635
|
$self->dump(0) if $config->{debug}->{song} > 1; |
560
|
|
|
|
|
|
|
|
561
|
170
|
100
|
|
|
|
534
|
if ( @labels ) { |
562
|
1
|
|
|
|
|
4
|
$self->{labels} = [ @labels ]; |
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
# Suppress chords that the user considers 'easy'. |
566
|
170
|
|
|
|
|
734
|
my %suppress; |
567
|
170
|
|
|
|
|
502
|
my $xc = $config->{settings}->{transcode}; |
568
|
170
|
|
|
|
|
335
|
for ( @{ $config->{diagrams}->{suppress} } ) { |
|
170
|
|
|
|
|
743
|
|
569
|
0
|
|
|
|
|
0
|
my $info = ChordPro::Chords::known_chord($_); |
570
|
0
|
0
|
|
|
|
0
|
warn("Unknown chord \"$_\" in suppress list\n"), next |
571
|
|
|
|
|
|
|
unless $info; |
572
|
|
|
|
|
|
|
# Note we do transcode, but we do not transpose. |
573
|
0
|
0
|
|
|
|
0
|
if ( $xc ) { |
574
|
0
|
|
|
|
|
0
|
$info = $info->transcode($xc); |
575
|
|
|
|
|
|
|
} |
576
|
0
|
|
|
|
|
0
|
$suppress{$info->name} = 1; |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
# Suppress chords that the user don't want. |
579
|
170
|
|
|
|
|
367
|
while ( my ($k,$v) = each %{ $self->{chordsinfo} } ) { |
|
600
|
|
|
|
|
2226
|
|
580
|
430
|
100
|
100
|
|
|
2177
|
$suppress{$k} = 1 if !is_true($v->{diagram}//1); |
581
|
|
|
|
|
|
|
} |
582
|
170
|
100
|
|
|
|
760
|
@used_chords = map { $suppress{$_} ? () : $_ } @used_chords; |
|
902
|
|
|
|
|
2224
|
|
583
|
|
|
|
|
|
|
|
584
|
170
|
|
|
|
|
382
|
my $diagrams; |
585
|
170
|
100
|
|
|
|
699
|
if ( exists($self->{settings}->{diagrams} ) ) { |
586
|
5
|
|
|
|
|
21
|
$diagrams = $self->{settings}->{diagrams}; |
587
|
5
|
|
100
|
|
|
29
|
$diagrams &&= $config->{diagrams}->{show} || "all"; |
|
|
|
66
|
|
|
|
|
588
|
|
|
|
|
|
|
} |
589
|
|
|
|
|
|
|
else { |
590
|
165
|
|
|
|
|
482
|
$diagrams = $config->{diagrams}->{show}; |
591
|
|
|
|
|
|
|
} |
592
|
|
|
|
|
|
|
|
593
|
170
|
50
|
66
|
|
|
1776
|
if ( $diagrams =~ /^(user|all)$/ |
594
|
|
|
|
|
|
|
&& !ChordPro::Chords::Parser->get_parser($target,1)->has_diagrams ) { |
595
|
|
|
|
|
|
|
do_warn( "Chord diagrams suppressed for " . |
596
|
0
|
0
|
|
|
|
0
|
ucfirst($target) . " chords" ) unless $options->{silent}; |
597
|
0
|
|
|
|
|
0
|
$diagrams = "none"; |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
|
600
|
170
|
50
|
|
|
|
1527
|
if ( $diagrams eq "user" ) { |
601
|
|
|
|
|
|
|
|
602
|
0
|
0
|
0
|
|
|
0
|
if ( $self->{define} && @{$self->{define}} ) { |
|
0
|
|
|
|
|
0
|
|
603
|
0
|
|
|
|
|
0
|
my %h = map { demarkup($_) => 1 } @used_chords; |
|
0
|
|
|
|
|
0
|
|
604
|
|
|
|
|
|
|
@used_chords = |
605
|
0
|
0
|
|
|
|
0
|
map { $h{$_->{name}} ? $_->{name} : () } @{$self->{define}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
606
|
|
|
|
|
|
|
} |
607
|
|
|
|
|
|
|
else { |
608
|
0
|
|
|
|
|
0
|
@used_chords = (); |
609
|
|
|
|
|
|
|
} |
610
|
|
|
|
|
|
|
} |
611
|
|
|
|
|
|
|
else { |
612
|
170
|
|
|
|
|
349
|
my %h; |
613
|
900
|
100
|
|
|
|
2615
|
@used_chords = map { $h{$_}++ ? () : $_ } |
614
|
170
|
|
|
|
|
466
|
map { demarkup($_) } @used_chords; |
|
900
|
|
|
|
|
1855
|
|
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
|
617
|
170
|
50
|
|
|
|
892
|
if ( $config->{diagrams}->{sorted} ) { |
618
|
0
|
|
|
|
|
0
|
@used_chords = |
619
|
|
|
|
|
|
|
sort ChordPro::Chords::chordcompare @used_chords; |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
# For headings, footers, table of contents, ... |
623
|
170
|
|
50
|
|
|
2714
|
$self->{meta}->{chords} //= [ @used_chords ]; |
624
|
170
|
|
|
|
|
366
|
$self->{meta}->{numchords} = [ scalar(@{$self->{meta}->{chords}}) ]; |
|
170
|
|
|
|
|
713
|
|
625
|
|
|
|
|
|
|
|
626
|
170
|
100
|
|
|
|
944
|
if ( $diagrams =~ /^(user|all)$/ ) { |
627
|
|
|
|
|
|
|
$self->{chords} = |
628
|
114
|
|
|
|
|
757
|
{ type => "diagrams", |
629
|
|
|
|
|
|
|
origin => "song", |
630
|
|
|
|
|
|
|
show => $diagrams, |
631
|
|
|
|
|
|
|
chords => [ @used_chords ], |
632
|
|
|
|
|
|
|
}; |
633
|
|
|
|
|
|
|
|
634
|
114
|
50
|
|
|
|
425
|
if ( %warned_chords ) { |
635
|
0
|
|
|
|
|
0
|
my @a = sort ChordPro::Chords::chordcompare |
636
|
|
|
|
|
|
|
keys(%warned_chords); |
637
|
0
|
|
|
|
|
0
|
my $l; |
638
|
0
|
0
|
|
|
|
0
|
if ( @a > 1 ) { |
639
|
0
|
|
|
|
|
0
|
my $a = pop(@a); |
640
|
0
|
|
|
|
|
0
|
$l = '"' . join('", "', @a) . '" and "' . $a . '"'; |
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
else { |
643
|
0
|
|
|
|
|
0
|
$l = '"' . $a[0] . '"'; |
644
|
|
|
|
|
|
|
} |
645
|
0
|
|
|
|
|
0
|
do_warn( "No chord diagram defined for $l (skipped)\n" ); |
646
|
|
|
|
|
|
|
} |
647
|
|
|
|
|
|
|
} |
648
|
|
|
|
|
|
|
|
649
|
170
|
50
|
|
|
|
620
|
$self->dump(0) if $config->{debug}->{song}; |
650
|
170
|
50
|
|
|
|
578
|
$self->dump(1) if $config->{debug}->{songfull}; |
651
|
|
|
|
|
|
|
|
652
|
170
|
|
|
|
|
907
|
return $self; |
653
|
|
|
|
|
|
|
} |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
sub add { |
656
|
1238
|
|
|
1238
|
0
|
2086
|
my $self = shift; |
657
|
1238
|
50
|
|
|
|
2542
|
return if $skip_context; |
658
|
1238
|
|
|
|
|
5366
|
push( @{$self->{body}}, |
659
|
|
|
|
|
|
|
{ context => $in_context, |
660
|
1238
|
100
|
|
|
|
1736
|
$lineinfo ? ( line => $diag->{line} ) : (), |
661
|
|
|
|
|
|
|
@_ } ); |
662
|
1238
|
100
|
|
|
|
13708
|
if ( $in_context eq "chorus" ) { |
663
|
108
|
|
|
|
|
383
|
push( @chorus, { context => $in_context, @_ } ); |
664
|
108
|
|
|
|
|
264
|
$chorus_xpose = $xpose; |
665
|
108
|
|
|
|
|
327
|
$chorus_xpose_dir = $xpose_dir; |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
} |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
# Parses a chord and adds it to the song. |
670
|
|
|
|
|
|
|
# It understands markup, parenthesized chords and annotations. |
671
|
|
|
|
|
|
|
# Returns the chord Appearance. |
672
|
|
|
|
|
|
|
sub chord { |
673
|
937
|
|
|
937
|
0
|
2360
|
my ( $self, $orig ) = @_; |
674
|
937
|
50
|
|
|
|
2508
|
Carp::confess unless length($orig); |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
# Intercept annotations. |
677
|
937
|
100
|
66
|
|
|
5157
|
if ( $orig =~ /^\*(.+)/ || $orig =~ /^(\||\s+)$/ ) { |
678
|
3
|
|
|
|
|
52
|
my $i = ChordPro::Chord::Annotation->new |
679
|
|
|
|
|
|
|
( { name => $orig, text => $1 } ); |
680
|
|
|
|
|
|
|
return |
681
|
3
|
|
|
|
|
29
|
ChordPro::Chords::Appearance->new |
682
|
|
|
|
|
|
|
( key => $self->add_chord($i), info => $i, orig => $orig ); |
683
|
|
|
|
|
|
|
} |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
# Check for markup. |
686
|
934
|
|
|
|
|
1954
|
my $markup = $orig; |
687
|
934
|
|
|
|
|
3020
|
my $c = demarkup($orig); |
688
|
934
|
100
|
|
|
|
2487
|
if ( $markup eq $c ) { # no markup |
689
|
927
|
|
|
|
|
1581
|
undef $markup; |
690
|
|
|
|
|
|
|
} |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
# Special treatment for parenthesized chords. |
693
|
934
|
|
|
|
|
1795
|
$c =~ s/^\((.*)\)$/$1/; |
694
|
934
|
50
|
|
|
|
2058
|
do_warn("Double parens in chord: \"$orig\"") |
695
|
|
|
|
|
|
|
if $c =~ s/^\((.*)\)$/$1/; |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
# We have a 'bare' chord now. Parse it. |
698
|
934
|
|
|
|
|
2498
|
my $info = $self->parse_chord($c); |
699
|
934
|
50
|
|
|
|
2553
|
unless ( defined $info ) { |
700
|
|
|
|
|
|
|
# Warning was given. |
701
|
|
|
|
|
|
|
# Make annotation. |
702
|
0
|
|
|
|
|
0
|
my $i = ChordPro::Chord::Annotation->new |
703
|
|
|
|
|
|
|
( { name => $orig, text => $orig } ); |
704
|
|
|
|
|
|
|
return |
705
|
0
|
|
|
|
|
0
|
ChordPro::Chords::Appearance->new |
706
|
|
|
|
|
|
|
( key => $self->add_chord($i), info => $i, orig => $orig ); |
707
|
|
|
|
|
|
|
} |
708
|
|
|
|
|
|
|
|
709
|
934
|
|
|
|
|
6864
|
my $ap = ChordPro::Chords::Appearance->new( orig => $orig ); |
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
# Handle markup, if any. |
712
|
934
|
100
|
|
|
|
15325
|
if ( $markup ) { |
|
|
50
|
|
|
|
|
|
713
|
7
|
100
|
100
|
|
|
173
|
if ( $markup =~ s/\>\Q$c\E\>%{formatted} |
714
|
|
|
|
|
|
|
|| |
715
|
|
|
|
|
|
|
$markup =~ s/\>\(\Q$c\E\)\>(%{formatted}) ) { |
716
|
|
|
|
|
|
|
} |
717
|
|
|
|
|
|
|
else { |
718
|
1
|
|
|
|
|
10
|
do_warn("Invalid markup in chord: \"$markup\"\n"); |
719
|
|
|
|
|
|
|
} |
720
|
7
|
|
|
|
|
41
|
$ap->format = $markup; |
721
|
|
|
|
|
|
|
} |
722
|
|
|
|
|
|
|
elsif ( (my $m = $orig) =~ s/\Q$c\E/%{formatted}/ ) { |
723
|
927
|
100
|
|
|
|
2855
|
$ap->format = $m unless $m eq "%{formatted}"; |
724
|
|
|
|
|
|
|
} |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
# After parsing, the chord can be changed by transpose/code. |
727
|
|
|
|
|
|
|
# info->name is the new key. |
728
|
934
|
|
|
|
|
2912
|
$ap->key = $self->add_chord( $info, $c = $info->name ); |
729
|
934
|
|
|
|
|
2609
|
$ap->info = $info; |
730
|
|
|
|
|
|
|
|
731
|
934
|
100
|
100
|
|
|
2454
|
unless ( $info->is_nc || $info->is_note ) { |
732
|
|
|
|
|
|
|
# if ( $info->is_keyboard ) { |
733
|
922
|
50
|
0
|
|
|
3354
|
if ( $::config->{instrument}->{type} eq "keyboard" ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
734
|
0
|
|
|
|
|
0
|
push( @used_chords, $c ); |
735
|
|
|
|
|
|
|
} |
736
|
|
|
|
|
|
|
elsif ( $info->{origin} ) { |
737
|
|
|
|
|
|
|
# Include if we have diagram info. |
738
|
766
|
50
|
|
|
|
1894
|
push( @used_chords, $c ) if $info->has_diagram; |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
elsif ( $::running_under_test ) { |
741
|
|
|
|
|
|
|
# Tests run without config and chords, so pretend. |
742
|
156
|
|
|
|
|
367
|
push( @used_chords, $c ); |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
elsif ( ! ( $info->is_rootless |
745
|
|
|
|
|
|
|
|| $info->has_diagram |
746
|
|
|
|
|
|
|
|| !$info->parser->has_diagrams |
747
|
|
|
|
|
|
|
) ) { |
748
|
|
|
|
|
|
|
do_warn("Unknown chord: $c") |
749
|
0
|
0
|
|
|
|
0
|
unless $warned_chords{$c}++; |
750
|
|
|
|
|
|
|
} |
751
|
|
|
|
|
|
|
} |
752
|
|
|
|
|
|
|
|
753
|
934
|
|
|
|
|
2817
|
return $ap; |
754
|
|
|
|
|
|
|
} |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
sub decompose { |
757
|
580
|
|
|
580
|
0
|
1421
|
my ($self, $orig) = @_; |
758
|
580
|
|
|
|
|
1779
|
my $line = fmt_subst( $self, $orig ); |
759
|
580
|
100
|
|
|
|
49215
|
undef $orig if $orig eq $line; |
760
|
580
|
|
|
|
|
3224
|
$line =~ s/\s+$//; |
761
|
580
|
|
|
|
|
5998
|
my @a = split( $re_chords, $line, -1); |
762
|
|
|
|
|
|
|
|
763
|
580
|
100
|
|
|
|
1961
|
if ( @a <= 1 ) { |
764
|
237
|
50
|
|
|
|
1541
|
return ( phrases => [ $line ], |
765
|
|
|
|
|
|
|
$orig ? ( orig => $orig ) : (), |
766
|
|
|
|
|
|
|
); |
767
|
|
|
|
|
|
|
} |
768
|
|
|
|
|
|
|
|
769
|
343
|
|
|
|
|
657
|
my $dummy; |
770
|
343
|
100
|
|
|
|
896
|
shift(@a) if $a[0] eq ""; |
771
|
343
|
100
|
|
|
|
2489
|
unshift(@a, '[]'), $dummy++ if $a[0] !~ $re_chords; |
772
|
|
|
|
|
|
|
|
773
|
343
|
|
|
|
|
784
|
my @phrases; |
774
|
|
|
|
|
|
|
my @chords; |
775
|
343
|
|
|
|
|
886
|
while ( @a ) { |
776
|
1035
|
|
|
|
|
1911
|
my $chord = shift(@a); |
777
|
1035
|
|
|
|
|
1997
|
push(@phrases, shift(@a)); |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
# Normal chords. |
780
|
1035
|
100
|
100
|
|
|
8612
|
if ( $chord =~ s/^\[(.*)\]$/$1/ && $chord ne "^" ) { |
|
|
100
|
66
|
|
|
|
|
781
|
1008
|
100
|
|
|
|
3803
|
push(@chords, $chord eq "" ? "" : $self->chord($chord)); |
782
|
1008
|
100
|
100
|
|
|
2597
|
if ( $memchords && !$dummy ) { |
783
|
21
|
100
|
|
|
|
48
|
if ( $memcrdinx == 0 ) { |
784
|
3
|
|
|
|
|
5
|
$memorizing++; |
785
|
|
|
|
|
|
|
} |
786
|
21
|
100
|
|
|
|
44
|
if ( $memorizing ) { |
787
|
20
|
|
|
|
|
31
|
push( @$memchords, $chords[-1] ); |
788
|
|
|
|
|
|
|
warn("Chord memorized for $in_context\[$memcrdinx]: ", |
789
|
|
|
|
|
|
|
$chords[-1], "\n") |
790
|
20
|
50
|
|
|
|
47
|
if $config->{debug}->{chords}; |
791
|
|
|
|
|
|
|
} |
792
|
21
|
|
|
|
|
38
|
$memcrdinx++; |
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
} |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
# Recall memorized chords. |
797
|
|
|
|
|
|
|
elsif ( $memchords && $in_context ) { |
798
|
20
|
100
|
100
|
|
|
77
|
if ( $memcrdinx == 0 && @$memchords == 0 ) { |
|
|
50
|
|
|
|
|
|
799
|
1
|
|
|
|
|
7
|
do_warn("No chords memorized for $in_context"); |
800
|
1
|
|
|
|
|
9
|
push( @chords, $chord ); |
801
|
1
|
|
|
|
|
3
|
undef $memchords; |
802
|
|
|
|
|
|
|
} |
803
|
|
|
|
|
|
|
elsif ( $memcrdinx >= @$memchords ) { |
804
|
0
|
|
|
|
|
0
|
do_warn("Not enough chords memorized for $in_context"); |
805
|
0
|
|
|
|
|
0
|
push( @chords, $chord ); |
806
|
|
|
|
|
|
|
} |
807
|
|
|
|
|
|
|
else { |
808
|
19
|
|
|
|
|
68
|
push( @chords, $self->chord($memchords->[$memcrdinx]->chord_display)); |
809
|
|
|
|
|
|
|
warn("Chord recall $in_context\[$memcrdinx]: ", $chords[-1], "\n") |
810
|
19
|
50
|
|
|
|
61
|
if $config->{debug}->{chords}; |
811
|
|
|
|
|
|
|
} |
812
|
20
|
|
|
|
|
35
|
$memcrdinx++; |
813
|
|
|
|
|
|
|
} |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
# Not memorizing. |
816
|
|
|
|
|
|
|
else { |
817
|
|
|
|
|
|
|
# do_warn("No chords memorized for $in_context"); |
818
|
7
|
|
|
|
|
12
|
push( @chords, $chord ); |
819
|
|
|
|
|
|
|
} |
820
|
1035
|
|
|
|
|
2920
|
$dummy = 0; |
821
|
|
|
|
|
|
|
} |
822
|
|
|
|
|
|
|
|
823
|
343
|
100
|
|
|
|
1919
|
return ( phrases => \@phrases, |
824
|
|
|
|
|
|
|
chords => \@chords, |
825
|
|
|
|
|
|
|
$orig ? ( orig => $orig ) : (), |
826
|
|
|
|
|
|
|
); |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
sub cdecompose { |
830
|
124
|
|
|
124
|
0
|
371
|
my ( $self, $line ) = @_; |
831
|
124
|
50
|
|
|
|
558
|
$line = fmt_subst( $self, $line ) unless $no_substitute; |
832
|
124
|
|
|
|
|
10282
|
my %res = $self->decompose($line); |
833
|
124
|
100
|
|
|
|
756
|
return ( text => $line ) unless $res{chords}; |
834
|
14
|
|
|
|
|
81
|
return %res; |
835
|
|
|
|
|
|
|
} |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
sub decompose_grid { |
838
|
39
|
|
|
39
|
0
|
130
|
my ($self, $line) = @_; |
839
|
39
|
|
|
|
|
116
|
$line =~ s/^\s+//; |
840
|
39
|
|
|
|
|
203
|
$line =~ s/\s+$//; |
841
|
39
|
50
|
|
|
|
122
|
return ( tokens => [] ) if $line eq ""; |
842
|
|
|
|
|
|
|
|
843
|
39
|
|
|
|
|
69
|
my $orig; |
844
|
|
|
|
|
|
|
my %res; |
845
|
39
|
50
|
|
|
|
311
|
if ( $line !~ /\|/ ) { |
846
|
0
|
|
|
|
|
0
|
$res{margin} = { $self->cdecompose($line), orig => $line }; |
847
|
0
|
|
|
|
|
0
|
$line = ""; |
848
|
|
|
|
|
|
|
} |
849
|
|
|
|
|
|
|
else { |
850
|
39
|
50
|
|
|
|
315
|
if ( $line =~ /(.*\|\S*)\s([^\|]*)$/ ) { |
851
|
0
|
|
|
|
|
0
|
$line = $1; |
852
|
0
|
|
|
|
|
0
|
$res{comment} = { $self->cdecompose($2), orig => $2 }; |
853
|
0
|
0
|
|
|
|
0
|
do_warn( "No margin cell for trailing comment" ) |
854
|
|
|
|
|
|
|
unless $grid_cells->[2]; |
855
|
|
|
|
|
|
|
} |
856
|
39
|
50
|
|
|
|
149
|
if ( $line =~ /^([^|]+?)\s*(\|.*)/ ) { |
857
|
0
|
|
|
|
|
0
|
$line = $2; |
858
|
0
|
|
|
|
|
0
|
$res{margin} = { $self->cdecompose($1), orig => $1 }; |
859
|
0
|
0
|
|
|
|
0
|
do_warn( "No cell for margin text" ) |
860
|
|
|
|
|
|
|
unless $grid_cells->[1]; |
861
|
|
|
|
|
|
|
} |
862
|
|
|
|
|
|
|
} |
863
|
|
|
|
|
|
|
|
864
|
39
|
|
|
|
|
68
|
my @tokens; |
865
|
39
|
|
|
|
|
252
|
my @t = split( ' ', $line ); |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
# Unfortunately, gets split too. |
868
|
39
|
|
|
|
|
138
|
while ( @t ) { |
869
|
663
|
|
|
|
|
939
|
$_ = shift(@t); |
870
|
663
|
|
|
|
|
1195
|
push( @tokens, $_ ); |
871
|
663
|
50
|
|
|
|
1354
|
if ( /\
|
872
|
0
|
|
|
|
|
0
|
while ( @t ) { |
873
|
0
|
|
|
|
|
0
|
$_ = shift(@t); |
874
|
0
|
|
|
|
|
0
|
$tokens[-1] .= " " . $_; |
875
|
0
|
0
|
|
|
|
0
|
last if /\<\/span>/; |
876
|
|
|
|
|
|
|
} |
877
|
|
|
|
|
|
|
} |
878
|
|
|
|
|
|
|
} |
879
|
|
|
|
|
|
|
|
880
|
39
|
|
|
|
|
121
|
my $nbt = 0; # non-bar tokens |
881
|
39
|
|
|
|
|
155
|
foreach ( @tokens ) { |
882
|
663
|
50
|
33
|
|
|
4905
|
if ( $_ eq "|:" || $_ eq "{" ) { |
|
|
50
|
33
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
883
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "bar" }; |
884
|
|
|
|
|
|
|
} |
885
|
|
|
|
|
|
|
elsif ( /^\|(\d+)(>?)$/ ) { |
886
|
0
|
|
|
|
|
0
|
$_ = { symbol => '|', volta => $1, class => "bar" }; |
887
|
0
|
0
|
|
|
|
0
|
$_->{align} = 1 if $2; |
888
|
|
|
|
|
|
|
} |
889
|
|
|
|
|
|
|
elsif ( $_ eq ":|" || $_ eq "}" ) { |
890
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "bar" }; |
891
|
|
|
|
|
|
|
} |
892
|
|
|
|
|
|
|
elsif ( $_ eq ":|:" || $_ eq "}{" ) { |
893
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "bar" }; |
894
|
|
|
|
|
|
|
} |
895
|
|
|
|
|
|
|
elsif ( $_ eq "|" ) { |
896
|
149
|
|
|
|
|
515
|
$_ = { symbol => $_, class => "bar" }; |
897
|
|
|
|
|
|
|
} |
898
|
|
|
|
|
|
|
elsif ( $_ eq "||" ) { |
899
|
2
|
|
|
|
|
15
|
$_ = { symbol => $_, class => "bar" }; |
900
|
|
|
|
|
|
|
} |
901
|
|
|
|
|
|
|
elsif ( $_ eq "|." ) { |
902
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "bar" }; |
903
|
|
|
|
|
|
|
} |
904
|
|
|
|
|
|
|
elsif ( $_ eq "%" ) { |
905
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "repeat1" }; |
906
|
|
|
|
|
|
|
} |
907
|
|
|
|
|
|
|
elsif ( $_ eq '%%' ) { |
908
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "repeat2" }; |
909
|
|
|
|
|
|
|
} |
910
|
|
|
|
|
|
|
elsif ( $_ eq "/" ) { |
911
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "slash" }; |
912
|
|
|
|
|
|
|
} |
913
|
|
|
|
|
|
|
elsif ( $_ eq "." ) { |
914
|
395
|
|
|
|
|
1051
|
$_ = { symbol => $_, class => "space" }; |
915
|
395
|
|
|
|
|
615
|
$nbt++; |
916
|
|
|
|
|
|
|
} |
917
|
|
|
|
|
|
|
else { |
918
|
|
|
|
|
|
|
# Multiple chords in a cell? |
919
|
117
|
|
|
|
|
329
|
my @a = split( /~/, $_, -1 ); |
920
|
117
|
100
|
|
|
|
242
|
if ( @a == 1) { |
921
|
|
|
|
|
|
|
# Normal case, single chord. |
922
|
116
|
|
|
|
|
297
|
$_ = { chord => $self->chord($_), class => "chord" }; |
923
|
|
|
|
|
|
|
} |
924
|
|
|
|
|
|
|
else { |
925
|
|
|
|
|
|
|
# Multiple chords. |
926
|
|
|
|
|
|
|
$_ = { chords => |
927
|
1
|
50
|
33
|
|
|
3
|
[ map { ( $_ eq '.' || $_ eq '' ) |
|
2
|
50
|
|
|
|
14
|
|
928
|
|
|
|
|
|
|
? '' |
929
|
|
|
|
|
|
|
: $_ eq "/" |
930
|
|
|
|
|
|
|
? "/" |
931
|
|
|
|
|
|
|
: $self->chord($_) } @a ], |
932
|
|
|
|
|
|
|
class => "chords" }; |
933
|
|
|
|
|
|
|
} |
934
|
117
|
|
|
|
|
320
|
$nbt++; |
935
|
|
|
|
|
|
|
} |
936
|
|
|
|
|
|
|
} |
937
|
39
|
50
|
|
|
|
177
|
if ( $nbt > $grid_cells->[0] ) { |
938
|
0
|
|
|
|
|
0
|
do_warn( "Too few cells for grid content" ); |
939
|
|
|
|
|
|
|
} |
940
|
39
|
|
|
|
|
268
|
return ( tokens => \@tokens, %res ); |
941
|
|
|
|
|
|
|
} |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
################ Parsing directives ################ |
944
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
my @directives = qw( |
946
|
|
|
|
|
|
|
chord |
947
|
|
|
|
|
|
|
chordcolour |
948
|
|
|
|
|
|
|
chordfont |
949
|
|
|
|
|
|
|
chordsize |
950
|
|
|
|
|
|
|
chorus |
951
|
|
|
|
|
|
|
column_break |
952
|
|
|
|
|
|
|
columns |
953
|
|
|
|
|
|
|
comment |
954
|
|
|
|
|
|
|
comment_box |
955
|
|
|
|
|
|
|
comment_italic |
956
|
|
|
|
|
|
|
define |
957
|
|
|
|
|
|
|
end_of_bridge |
958
|
|
|
|
|
|
|
end_of_chorus |
959
|
|
|
|
|
|
|
end_of_grid |
960
|
|
|
|
|
|
|
end_of_tab |
961
|
|
|
|
|
|
|
end_of_verse |
962
|
|
|
|
|
|
|
footersize |
963
|
|
|
|
|
|
|
footercolour |
964
|
|
|
|
|
|
|
footerfont |
965
|
|
|
|
|
|
|
grid |
966
|
|
|
|
|
|
|
highlight |
967
|
|
|
|
|
|
|
image |
968
|
|
|
|
|
|
|
meta |
969
|
|
|
|
|
|
|
new_page |
970
|
|
|
|
|
|
|
new_physical_page |
971
|
|
|
|
|
|
|
new_song |
972
|
|
|
|
|
|
|
no_grid |
973
|
|
|
|
|
|
|
pagetype |
974
|
|
|
|
|
|
|
start_of_bridge |
975
|
|
|
|
|
|
|
start_of_chorus |
976
|
|
|
|
|
|
|
start_of_grid |
977
|
|
|
|
|
|
|
start_of_tab |
978
|
|
|
|
|
|
|
start_of_verse |
979
|
|
|
|
|
|
|
subtitle |
980
|
|
|
|
|
|
|
tabcolour |
981
|
|
|
|
|
|
|
tabfont |
982
|
|
|
|
|
|
|
tabsize |
983
|
|
|
|
|
|
|
textcolour |
984
|
|
|
|
|
|
|
textfont |
985
|
|
|
|
|
|
|
textsize |
986
|
|
|
|
|
|
|
title |
987
|
|
|
|
|
|
|
titlesize |
988
|
|
|
|
|
|
|
titlecolour |
989
|
|
|
|
|
|
|
titlefont |
990
|
|
|
|
|
|
|
titles |
991
|
|
|
|
|
|
|
tocsize |
992
|
|
|
|
|
|
|
toccolour |
993
|
|
|
|
|
|
|
tocfont |
994
|
|
|
|
|
|
|
transpose |
995
|
|
|
|
|
|
|
); |
996
|
|
|
|
|
|
|
# NOTE: Flex: start_of_... end_of_... x_... |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
my %abbrevs = ( |
999
|
|
|
|
|
|
|
c => "comment", |
1000
|
|
|
|
|
|
|
cb => "comment_box", |
1001
|
|
|
|
|
|
|
cf => "chordfont", |
1002
|
|
|
|
|
|
|
ci => "comment_italic", |
1003
|
|
|
|
|
|
|
colb => "column_break", |
1004
|
|
|
|
|
|
|
cs => "chordsize", |
1005
|
|
|
|
|
|
|
grid => "diagrams", # not really an abbrev |
1006
|
|
|
|
|
|
|
eob => "end_of_bridge", |
1007
|
|
|
|
|
|
|
eoc => "end_of_chorus", |
1008
|
|
|
|
|
|
|
eot => "end_of_tab", |
1009
|
|
|
|
|
|
|
eov => "end_of_verse", |
1010
|
|
|
|
|
|
|
g => "diagrams", |
1011
|
|
|
|
|
|
|
highlight => "comment", # not really an abbrev |
1012
|
|
|
|
|
|
|
ng => "no_grid", |
1013
|
|
|
|
|
|
|
np => "new_page", |
1014
|
|
|
|
|
|
|
npp => "new_physical_page", |
1015
|
|
|
|
|
|
|
ns => "new_song", |
1016
|
|
|
|
|
|
|
sob => "start_of_bridge", |
1017
|
|
|
|
|
|
|
soc => "start_of_chorus", |
1018
|
|
|
|
|
|
|
sot => "start_of_tab", |
1019
|
|
|
|
|
|
|
sov => "start_of_verse", |
1020
|
|
|
|
|
|
|
st => "subtitle", |
1021
|
|
|
|
|
|
|
t => "title", |
1022
|
|
|
|
|
|
|
tf => "textfont", |
1023
|
|
|
|
|
|
|
ts => "textsize", |
1024
|
|
|
|
|
|
|
); |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
my $dirpat; |
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
sub parse_directive { |
1029
|
1006
|
|
|
1006
|
0
|
1868
|
my ( $self, $d ) = @_; |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
# Pattern for all recognized directives. |
1032
|
1006
|
100
|
|
|
|
2404
|
unless ( $dirpat ) { |
1033
|
|
|
|
|
|
|
$dirpat = |
1034
|
|
|
|
|
|
|
'(?:' . |
1035
|
|
|
|
|
|
|
join( '|', @directives, |
1036
|
58
|
|
|
|
|
205
|
@{$config->{metadata}->{keys}}, |
|
58
|
|
|
|
|
1513
|
|
1037
|
|
|
|
|
|
|
keys(%abbrevs), |
1038
|
|
|
|
|
|
|
'(?:start|end)_of_\w+' ) . |
1039
|
|
|
|
|
|
|
')'; |
1040
|
58
|
|
|
|
|
19901
|
$dirpat = qr/$dirpat/; |
1041
|
|
|
|
|
|
|
} |
1042
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
# $d is the complete directive line, without leading/trailing { }. |
1044
|
1006
|
|
|
|
|
3123
|
$d =~ s/^[: ]+//; |
1045
|
1006
|
|
|
|
|
3072
|
$d =~ s/\s+$//; |
1046
|
1006
|
|
|
|
|
2767
|
my $dir = lc($d); |
1047
|
1006
|
|
|
|
|
7054
|
my $arg = ""; |
1048
|
1006
|
100
|
|
|
|
4078
|
if ( $d =~ /^(.*?)[: ]\s*(.*)/ ) { |
1049
|
791
|
|
|
|
|
2981
|
( $dir, $arg ) = ( lc($1), $2 ); |
1050
|
|
|
|
|
|
|
} |
1051
|
1006
|
|
|
|
|
2214
|
$dir =~ s/[: ]+$//; |
1052
|
|
|
|
|
|
|
# $dir is the lowcase directive name. |
1053
|
|
|
|
|
|
|
# $arg is the rest, if any. |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
# Check for xxx-yyy selectors. |
1056
|
1006
|
100
|
|
|
|
24786
|
if ( $dir =~ /^($dirpat)-(.+)$/ ) { |
1057
|
11
|
|
66
|
|
|
66
|
$dir = $abbrevs{$1} // $1; |
1058
|
11
|
|
|
|
|
29
|
my $sel = $2; |
1059
|
11
|
|
|
|
|
46
|
my $negate = $sel =~ s/\!$//; |
1060
|
|
|
|
|
|
|
$sel = ( $sel eq lc($config->{instrument}->{type}) ) |
1061
|
|
|
|
|
|
|
|| |
1062
|
|
|
|
|
|
|
( $sel eq lc($config->{user}->{name}) |
1063
|
|
|
|
|
|
|
|| |
1064
|
11
|
|
66
|
|
|
81
|
( $self->{meta}->{lc $sel} && is_true($self->{meta}->{lc $sel}->[0]) ) |
1065
|
|
|
|
|
|
|
); |
1066
|
11
|
100
|
|
|
|
32
|
$sel = !$sel if $negate; |
1067
|
11
|
100
|
|
|
|
26
|
unless ( $sel ) { |
1068
|
4
|
100
|
|
|
|
19
|
if ( $dir =~ /^start_of_/ ) { |
1069
|
2
|
|
|
|
|
10
|
return { name => $dir, arg => $arg, omit => 2 }; |
1070
|
|
|
|
|
|
|
} |
1071
|
|
|
|
|
|
|
else { |
1072
|
2
|
|
|
|
|
12
|
return { name => $dir, arg => $arg, omit => 1 }; |
1073
|
|
|
|
|
|
|
} |
1074
|
|
|
|
|
|
|
} |
1075
|
|
|
|
|
|
|
} |
1076
|
|
|
|
|
|
|
else { |
1077
|
995
|
|
66
|
|
|
4766
|
$dir = $abbrevs{$dir} // $dir; |
1078
|
|
|
|
|
|
|
} |
1079
|
|
|
|
|
|
|
|
1080
|
1002
|
|
|
|
|
5330
|
return { name => $dir, arg => $arg, omit => 0 } |
1081
|
|
|
|
|
|
|
} |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
sub directive { |
1084
|
1004
|
|
|
1004
|
0
|
2312
|
my ( $self, $d ) = @_; |
1085
|
|
|
|
|
|
|
|
1086
|
1004
|
|
|
|
|
2558
|
my $dd = $self->parse_directive($d); |
1087
|
1004
|
100
|
|
|
|
3486
|
return 1 if $dd->{omit} == 1; |
1088
|
|
|
|
|
|
|
|
1089
|
1002
|
|
|
|
|
1786
|
my $arg = $dd->{arg}; |
1090
|
1002
|
100
|
|
|
|
2284
|
if ( $arg ne "" ) { |
1091
|
788
|
|
|
|
|
2918
|
$arg = fmt_subst( $self, $arg ); |
1092
|
788
|
50
|
|
|
|
93928
|
return 1 if $arg !~ /\S/; |
1093
|
|
|
|
|
|
|
} |
1094
|
1002
|
|
|
|
|
2474
|
my $dir = $dd->{name}; |
1095
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
# Context flags. |
1097
|
|
|
|
|
|
|
|
1098
|
1002
|
100
|
|
|
|
2831
|
if ( $dir =~ /^start_of_(\w+)$/ ) { |
1099
|
77
|
50
|
|
|
|
292
|
do_warn("Already in " . ucfirst($in_context) . " context\n") |
1100
|
|
|
|
|
|
|
if $in_context; |
1101
|
77
|
|
|
|
|
213
|
$in_context = $1; |
1102
|
77
|
100
|
|
|
|
281
|
if ( $dd->{omit} ) { |
1103
|
2
|
|
|
|
|
4
|
$skip_context = 1; |
1104
|
|
|
|
|
|
|
# warn("Skipping context: $in_context\n"); |
1105
|
2
|
|
|
|
|
9
|
return 1; |
1106
|
|
|
|
|
|
|
} |
1107
|
75
|
100
|
|
|
|
278
|
@chorus = (), $chorus_xpose = $chorus_xpose_dir = 0 |
1108
|
|
|
|
|
|
|
if $in_context eq "chorus"; |
1109
|
75
|
100
|
66
|
|
|
329
|
if ( $in_context eq "grid" ) { |
|
|
100
|
|
|
|
|
|
1110
|
25
|
100
|
|
|
|
217
|
if ( $arg eq "" ) { |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1111
|
3
|
|
|
|
|
9
|
$self->add( type => "set", |
1112
|
|
|
|
|
|
|
name => "gridparams", |
1113
|
|
|
|
|
|
|
value => $grid_arg ); |
1114
|
|
|
|
|
|
|
} |
1115
|
|
|
|
|
|
|
elsif ( $arg =~ m/^ |
1116
|
|
|
|
|
|
|
(?: (\d+) \+)? |
1117
|
|
|
|
|
|
|
(\d+) (?: x (\d+) )? |
1118
|
|
|
|
|
|
|
(?:\+ (\d+) )? |
1119
|
|
|
|
|
|
|
(?:[:\s+] (.*)? )? $/x ) { |
1120
|
22
|
50
|
|
|
|
90
|
do_warn("Invalid grid params: $arg (must be non-zero)"), return |
1121
|
|
|
|
|
|
|
unless $2; |
1122
|
22
|
|
50
|
|
|
262
|
$grid_arg = [ $2, $3//1, $1//0, $4//0 ]; |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
1123
|
22
|
|
50
|
|
|
212
|
$self->add( type => "set", |
1124
|
|
|
|
|
|
|
name => "gridparams", |
1125
|
|
|
|
|
|
|
value => [ @$grid_arg, $5||"" ] ); |
1126
|
22
|
50
|
50
|
|
|
164
|
push( @labels, $5 ) if length($5||""); |
1127
|
|
|
|
|
|
|
} |
1128
|
|
|
|
|
|
|
elsif ( $arg ne "" ) { |
1129
|
0
|
|
|
|
|
0
|
$self->add( type => "set", |
1130
|
|
|
|
|
|
|
name => "gridparams", |
1131
|
|
|
|
|
|
|
value => [ @$grid_arg, $arg ] ); |
1132
|
0
|
|
|
|
|
0
|
push( @labels, $arg ); |
1133
|
|
|
|
|
|
|
} |
1134
|
25
|
|
|
|
|
119
|
$grid_cells = [ $grid_arg->[0] * $grid_arg->[1], |
1135
|
|
|
|
|
|
|
$grid_arg->[2], $grid_arg->[3] ]; |
1136
|
|
|
|
|
|
|
} |
1137
|
|
|
|
|
|
|
elsif ( $arg && $arg ne "" ) { |
1138
|
2
|
|
|
|
|
9
|
$self->add( type => "set", |
1139
|
|
|
|
|
|
|
name => "label", |
1140
|
|
|
|
|
|
|
value => $arg ); |
1141
|
|
|
|
|
|
|
push( @labels, $arg ) |
1142
|
2
|
50
|
33
|
|
|
10
|
unless $in_context eq "chorus" && !$config->{settings}->{choruslabels}; |
1143
|
|
|
|
|
|
|
} |
1144
|
|
|
|
|
|
|
else { |
1145
|
48
|
50
|
|
|
|
113
|
do_warn("Garbage in start_of_$1: $arg (ignored)\n") |
1146
|
|
|
|
|
|
|
if $arg; |
1147
|
|
|
|
|
|
|
} |
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
# Enabling this always would allow [^] to recall anyway. |
1150
|
|
|
|
|
|
|
# Feature? |
1151
|
75
|
100
|
|
|
|
285
|
if ( $config->{settings}->{memorize} ) { |
1152
|
7
|
|
100
|
|
|
38
|
$memchords = $memchords{$in_context} //= []; |
1153
|
7
|
|
|
|
|
11
|
$memcrdinx = 0; |
1154
|
7
|
|
|
|
|
15
|
$memorizing = 0; |
1155
|
|
|
|
|
|
|
} |
1156
|
75
|
|
|
|
|
797
|
return 1; |
1157
|
|
|
|
|
|
|
} |
1158
|
925
|
100
|
|
|
|
2570
|
if ( $dir =~ /^end_of_(\w+)$/ ) { |
1159
|
75
|
50
|
|
|
|
365
|
do_warn("Not in " . ucfirst($1) . " context\n") |
1160
|
|
|
|
|
|
|
unless $in_context eq $1; |
1161
|
75
|
|
|
|
|
332
|
$self->add( type => "set", |
1162
|
|
|
|
|
|
|
name => "context", |
1163
|
|
|
|
|
|
|
value => $def_context ); |
1164
|
75
|
|
|
|
|
154
|
$in_context = $def_context; |
1165
|
75
|
|
|
|
|
189
|
undef $memchords; |
1166
|
75
|
|
|
|
|
327
|
return 1; |
1167
|
|
|
|
|
|
|
} |
1168
|
850
|
100
|
|
|
|
2431
|
if ( $dir =~ /^chorus$/i ) { |
1169
|
30
|
50
|
|
|
|
90
|
if ( $in_context ) { |
1170
|
0
|
|
|
|
|
0
|
do_warn("{chorus} encountered while in $in_context context -- ignored\n"); |
1171
|
0
|
|
|
|
|
0
|
return 1; |
1172
|
|
|
|
|
|
|
} |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
# Clone the chorus so we can modify the label, if required. |
1175
|
30
|
100
|
|
|
|
5311
|
my $chorus = @chorus ? dclone(\@chorus) : []; |
1176
|
|
|
|
|
|
|
|
1177
|
30
|
50
|
66
|
|
|
36762
|
if ( @$chorus && $arg && $arg ne "" ) { |
|
|
|
33
|
|
|
|
|
1178
|
0
|
0
|
0
|
|
|
0
|
if ( $chorus->[0]->{type} eq "set" && $chorus->[0]->{name} eq "label" ) { |
1179
|
0
|
|
|
|
|
0
|
$chorus->[0]->{value} = $arg; |
1180
|
|
|
|
|
|
|
} |
1181
|
|
|
|
|
|
|
else { |
1182
|
0
|
|
|
|
|
0
|
unshift( @$chorus, |
1183
|
|
|
|
|
|
|
{ type => "set", |
1184
|
|
|
|
|
|
|
name => "label", |
1185
|
|
|
|
|
|
|
value => $arg, |
1186
|
|
|
|
|
|
|
context => "chorus", |
1187
|
|
|
|
|
|
|
} ); |
1188
|
|
|
|
|
|
|
} |
1189
|
|
|
|
|
|
|
push( @labels, $arg ) |
1190
|
0
|
0
|
|
|
|
0
|
if $config->{settings}->{choruslabels}; |
1191
|
|
|
|
|
|
|
} |
1192
|
|
|
|
|
|
|
|
1193
|
30
|
100
|
|
|
|
103
|
if ( $chorus_xpose != ( my $xp = $xpose ) ) { |
1194
|
17
|
|
|
|
|
46
|
$xp -= $chorus_xpose; |
1195
|
17
|
|
|
|
|
62
|
for ( @$chorus ) { |
1196
|
32
|
100
|
|
|
|
124
|
if ( $_->{type} eq "songline" ) { |
1197
|
16
|
|
|
|
|
31
|
for ( @{ $_->{chords} } ) { |
|
16
|
|
|
|
|
51
|
|
1198
|
61
|
100
|
|
|
|
186
|
next if $_ eq ''; |
1199
|
46
|
|
|
|
|
162
|
my $info = $self->{chordsinfo}->{$_->key}; |
1200
|
46
|
50
|
|
|
|
162
|
next if $info->is_annotation; |
1201
|
46
|
50
|
|
|
|
188
|
$info = $info->transpose($xp, $xpose <=> 0) if $xp; |
1202
|
46
|
|
|
|
|
138
|
$info = $info->new($info); |
1203
|
46
|
|
|
|
|
186
|
$_ = ChordPro::Chords::Appearance->new |
1204
|
|
|
|
|
|
|
( key => $self->add_chord($info), |
1205
|
|
|
|
|
|
|
info => $info, |
1206
|
|
|
|
|
|
|
maybe format => $_->format |
1207
|
|
|
|
|
|
|
); |
1208
|
|
|
|
|
|
|
} |
1209
|
|
|
|
|
|
|
} |
1210
|
|
|
|
|
|
|
} |
1211
|
|
|
|
|
|
|
} |
1212
|
|
|
|
|
|
|
|
1213
|
30
|
100
|
|
|
|
165
|
$self->add( type => "rechorus", |
1214
|
|
|
|
|
|
|
@$chorus |
1215
|
|
|
|
|
|
|
? ( "chorus" => $chorus ) |
1216
|
|
|
|
|
|
|
: (), |
1217
|
|
|
|
|
|
|
); |
1218
|
30
|
|
|
|
|
143
|
return 1; |
1219
|
|
|
|
|
|
|
} |
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
# Song settings. |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
# Breaks. |
1224
|
|
|
|
|
|
|
|
1225
|
820
|
100
|
|
|
|
2039
|
if ( $dir eq "column_break" ) { |
1226
|
13
|
|
|
|
|
89
|
$self->add( type => "colb" ); |
1227
|
13
|
|
|
|
|
59
|
return 1; |
1228
|
|
|
|
|
|
|
} |
1229
|
|
|
|
|
|
|
|
1230
|
807
|
100
|
100
|
|
|
3450
|
if ( $dir eq "new_page" || $dir eq "new_physical_page" ) { |
1231
|
16
|
|
|
|
|
88
|
$self->add( type => "newpage" ); |
1232
|
16
|
|
|
|
|
61
|
return 1; |
1233
|
|
|
|
|
|
|
} |
1234
|
|
|
|
|
|
|
|
1235
|
791
|
50
|
|
|
|
1782
|
if ( $dir eq "new_song" ) { |
1236
|
0
|
|
|
|
|
0
|
die("FATAL - cannot start a new song now\n"); |
1237
|
|
|
|
|
|
|
} |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
# Comments. Strictly speaking they do not belong here. |
1240
|
|
|
|
|
|
|
|
1241
|
791
|
100
|
|
|
|
2526
|
if ( $dir =~ /^comment(_italic|_box)?$/ ) { |
1242
|
124
|
|
|
|
|
613
|
my %res = $self->cdecompose($arg); |
1243
|
124
|
|
|
|
|
407
|
$res{orig} = $dd->{arg}; |
1244
|
|
|
|
|
|
|
$self->add( type => $dir, %res ) |
1245
|
124
|
50
|
66
|
|
|
1298
|
unless exists($res{text}) && $res{text} =~ /^[ \t]*$/; |
1246
|
124
|
|
|
|
|
591
|
return 1; |
1247
|
|
|
|
|
|
|
} |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
# Images. |
1250
|
667
|
100
|
|
|
|
1531
|
if ( $dir eq "image" ) { |
1251
|
3
|
|
|
|
|
19
|
my $res = parse_kv($arg); |
1252
|
3
|
|
|
|
|
10
|
my $uri; |
1253
|
|
|
|
|
|
|
my $id; |
1254
|
3
|
|
|
|
|
0
|
my %opts; |
1255
|
3
|
|
|
|
|
17
|
while ( my($k,$v) = each(%$res) ) { |
1256
|
9
|
100
|
66
|
|
|
134
|
if ( $k =~ /^(title)$/i && $v ne "" ) { |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
|
50
|
66
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1257
|
1
|
|
|
|
|
7
|
$opts{lc($k)} = $v; |
1258
|
|
|
|
|
|
|
} |
1259
|
|
|
|
|
|
|
elsif ( $k =~ /^(border|spread|center)$/i && $v =~ /^(\d+)$/ ) { |
1260
|
2
|
|
|
|
|
11
|
$opts{lc($k)} = $v; |
1261
|
|
|
|
|
|
|
} |
1262
|
|
|
|
|
|
|
elsif ( $k =~ /^(width|height)$/i && $v =~ /^(\d+(?:\.\d+)?\%?)$/ ) { |
1263
|
2
|
|
|
|
|
14
|
$opts{lc($k)} = $v; |
1264
|
|
|
|
|
|
|
} |
1265
|
|
|
|
|
|
|
elsif ( $k =~ /^(x|y)$/i && $v =~ /^([-+]?\d+(?:\.\d+)?\%?)$/ ) { |
1266
|
0
|
|
|
|
|
0
|
$opts{lc($k)} = $v; |
1267
|
|
|
|
|
|
|
} |
1268
|
|
|
|
|
|
|
elsif ( $k =~ /^(scale)$/ && $v =~ /^(\d+(?:\.\d+)?)(%)?$/ ) { |
1269
|
1
|
50
|
|
|
|
11
|
$opts{lc($k)} = $2 ? $1/100 : $1; |
1270
|
|
|
|
|
|
|
} |
1271
|
|
|
|
|
|
|
elsif ( $k =~ /^(center|border|spread)$/i ) { |
1272
|
0
|
|
|
|
|
0
|
$opts{lc($k)} = $v; |
1273
|
|
|
|
|
|
|
} |
1274
|
|
|
|
|
|
|
elsif ( $k =~ /^(src|uri)$/i && $v ne "" ) { |
1275
|
0
|
|
|
|
|
0
|
$uri = $v; |
1276
|
|
|
|
|
|
|
} |
1277
|
|
|
|
|
|
|
elsif ( $k =~ /^(id)$/i && $v ne "" ) { |
1278
|
1
|
|
|
|
|
5
|
$id = $v; |
1279
|
|
|
|
|
|
|
} |
1280
|
|
|
|
|
|
|
elsif ( $k =~ /^(anchor)$/i |
1281
|
|
|
|
|
|
|
&& $v =~ /^(paper|page|column|float|line)$/ ) { |
1282
|
0
|
|
|
|
|
0
|
$opts{lc($k)} = lc($v); |
1283
|
|
|
|
|
|
|
} |
1284
|
|
|
|
|
|
|
elsif ( $uri ) { |
1285
|
0
|
|
|
|
|
0
|
do_warn( "Unknown image attribute: $k\n" ); |
1286
|
0
|
|
|
|
|
0
|
next; |
1287
|
|
|
|
|
|
|
} |
1288
|
|
|
|
|
|
|
# Assume just an image file uri. |
1289
|
|
|
|
|
|
|
else { |
1290
|
2
|
|
|
|
|
8
|
$uri = $k; |
1291
|
|
|
|
|
|
|
} |
1292
|
|
|
|
|
|
|
} |
1293
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
# If the image name does not have a directory, look it up |
1295
|
|
|
|
|
|
|
# next to the song, and then in the images folder of the |
1296
|
|
|
|
|
|
|
# CHORDPRO_LIB. |
1297
|
3
|
100
|
66
|
|
|
16
|
if ( $uri && $uri !~ m;^([a-z]:)?[/\\];i ) { # not abs |
1298
|
81
|
|
|
81
|
|
900
|
use File::Basename qw(dirname); |
|
81
|
|
|
|
|
222
|
|
|
81
|
|
|
|
|
21315
|
|
1299
|
2
|
|
|
|
|
93
|
L: for ( dirname($diag->{file}) ) { |
1300
|
2
|
50
|
|
|
|
47
|
$uri = "$_/$uri", last if -s "$_/$uri"; |
1301
|
0
|
|
|
|
|
0
|
for ( ::rsc_or_file("images/$uri") ) { |
1302
|
0
|
0
|
|
|
|
0
|
last unless $_; |
1303
|
0
|
0
|
|
|
|
0
|
$uri = $_, last L if -s $_; |
1304
|
|
|
|
|
|
|
} |
1305
|
0
|
|
|
|
|
0
|
do_warn("Missing image for \"$uri\""); |
1306
|
|
|
|
|
|
|
} |
1307
|
|
|
|
|
|
|
} |
1308
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
# uri + id -> define asset |
1310
|
3
|
50
|
66
|
|
|
16
|
if ( $uri && $id ) { |
1311
|
|
|
|
|
|
|
# Define a new asset. |
1312
|
0
|
0
|
|
|
|
0
|
if ( %opts ) { |
1313
|
0
|
|
|
|
|
0
|
do_warn("Asset definition \"$id\" does not take attributes"); |
1314
|
0
|
|
|
|
|
0
|
return; |
1315
|
|
|
|
|
|
|
} |
1316
|
81
|
|
|
81
|
|
46966
|
use Image::Info; |
|
81
|
|
|
|
|
156583
|
|
|
81
|
|
|
|
|
709669
|
|
1317
|
0
|
|
|
|
|
0
|
open( my $fd, '<:raw', $uri ); |
1318
|
0
|
0
|
|
|
|
0
|
unless ( $fd ) { |
1319
|
0
|
|
|
|
|
0
|
do_warn("$uri: $!"); |
1320
|
0
|
|
|
|
|
0
|
return; |
1321
|
|
|
|
|
|
|
} |
1322
|
0
|
|
|
|
|
0
|
my $data = do { local $/; <$fd> }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1323
|
|
|
|
|
|
|
# Get info. |
1324
|
0
|
|
|
|
|
0
|
my $info = Image::Info::image_info(\$data); |
1325
|
0
|
0
|
|
|
|
0
|
if ( $info->{error} ) { |
1326
|
0
|
|
|
|
|
0
|
do_warn($info->{error}); |
1327
|
0
|
|
|
|
|
0
|
return; |
1328
|
|
|
|
|
|
|
} |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
# Store in assets. |
1331
|
0
|
|
0
|
|
|
0
|
$self->{assets} //= {}; |
1332
|
|
|
|
|
|
|
$self->{assets}->{$id} = |
1333
|
|
|
|
|
|
|
{ data => $data, type => $info->{file_ext}, |
1334
|
|
|
|
|
|
|
width => $info->{width}, height => $info->{height}, |
1335
|
0
|
|
|
|
|
0
|
}; |
1336
|
|
|
|
|
|
|
|
1337
|
0
|
0
|
|
|
|
0
|
if ( $config->{debug}->{images} ) { |
1338
|
0
|
|
|
|
|
0
|
warn("asset[$id] ", length($data), " bytes, ", |
1339
|
|
|
|
|
|
|
"width=$info->{width}, height=$info->{height}", |
1340
|
|
|
|
|
|
|
"\n"); |
1341
|
|
|
|
|
|
|
} |
1342
|
0
|
|
|
|
|
0
|
return 1; |
1343
|
|
|
|
|
|
|
} |
1344
|
|
|
|
|
|
|
|
1345
|
3
|
100
|
|
|
|
9
|
$uri = "id=$id" if $id; |
1346
|
3
|
50
|
|
|
|
11
|
unless ( $uri ) { |
1347
|
0
|
|
|
|
|
0
|
do_warn( "Missing image source\n" ); |
1348
|
0
|
|
|
|
|
0
|
return; |
1349
|
|
|
|
|
|
|
} |
1350
|
3
|
50
|
|
|
|
23
|
$self->add( type => $uri =~ /\.svg$/ ? "svg" : "image", |
1351
|
|
|
|
|
|
|
uri => $uri, |
1352
|
|
|
|
|
|
|
opts => \%opts ); |
1353
|
3
|
|
|
|
|
19
|
return 1; |
1354
|
|
|
|
|
|
|
} |
1355
|
|
|
|
|
|
|
|
1356
|
664
|
100
|
|
|
|
1566
|
if ( $dir eq "title" ) { |
1357
|
174
|
|
|
|
|
602
|
$self->{title} = $arg; |
1358
|
174
|
|
|
|
|
367
|
push( @{ $self->{meta}->{title} }, $arg ); |
|
174
|
|
|
|
|
739
|
|
1359
|
174
|
|
|
|
|
899
|
return 1; |
1360
|
|
|
|
|
|
|
} |
1361
|
|
|
|
|
|
|
|
1362
|
490
|
100
|
|
|
|
1449
|
if ( $dir eq "subtitle" ) { |
1363
|
28
|
|
|
|
|
83
|
push( @{ $self->{subtitle} }, $arg ); |
|
28
|
|
|
|
|
139
|
|
1364
|
28
|
|
|
|
|
66
|
push( @{ $self->{meta}->{subtitle} }, $arg ); |
|
28
|
|
|
|
|
85
|
|
1365
|
28
|
|
|
|
|
118
|
return 1; |
1366
|
|
|
|
|
|
|
} |
1367
|
|
|
|
|
|
|
|
1368
|
|
|
|
|
|
|
# Metadata extensions (legacy). Should use meta instead. |
1369
|
|
|
|
|
|
|
# Only accept the list from config. |
1370
|
462
|
100
|
|
5513
|
|
2502
|
if ( any { $_ eq $dir } @{ $config->{metadata}->{keys} } ) { |
|
5513
|
|
|
|
|
7532
|
|
|
462
|
|
|
|
|
2220
|
|
1371
|
225
|
|
|
|
|
708
|
$arg = "$dir $arg"; |
1372
|
225
|
|
|
|
|
855
|
$dir = "meta"; |
1373
|
|
|
|
|
|
|
} |
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
# Metadata. |
1376
|
462
|
100
|
|
|
|
2133
|
if ( $dir eq "meta" ) { |
1377
|
263
|
50
|
|
|
|
1452
|
if ( $arg =~ /([^ :]+)[ :]+(.*)/ ) { |
1378
|
263
|
|
|
|
|
898
|
my $key = lc $1; |
1379
|
263
|
|
|
|
|
806
|
my @vals = ( $2 ); |
1380
|
263
|
100
|
|
|
|
1098
|
if ( $config->{metadata}->{autosplit} ) { |
1381
|
256
|
|
|
|
|
707
|
@vals = map { s/s\+$//; $_ } |
|
256
|
|
|
|
|
950
|
|
1382
|
256
|
|
|
|
|
5970
|
split( quotemeta($config->{metadata}->{separator}), $vals[0] ); |
1383
|
|
|
|
|
|
|
} |
1384
|
263
|
|
|
|
|
646
|
my $m = $self->{meta}; |
1385
|
|
|
|
|
|
|
|
1386
|
|
|
|
|
|
|
# User and instrument cannot be set here. |
1387
|
263
|
50
|
33
|
|
|
1243
|
if ( $key eq "user" || $key eq "instrument" ) { |
1388
|
0
|
|
|
|
|
0
|
do_warn("\"$key\" can be set from config only.\n"); |
1389
|
0
|
|
|
|
|
0
|
return 1; |
1390
|
|
|
|
|
|
|
} |
1391
|
|
|
|
|
|
|
|
1392
|
263
|
|
|
|
|
646
|
for my $val ( @vals ) { |
1393
|
|
|
|
|
|
|
|
1394
|
263
|
100
|
|
|
|
645
|
if ( $key eq "key" ) { |
1395
|
92
|
|
|
|
|
307
|
$val =~ s/[\[\]]//g; |
1396
|
92
|
|
|
|
|
424
|
my $info = $self->parse_chord($val); |
1397
|
92
|
|
|
|
|
342
|
my $name = $info->name; |
1398
|
92
|
|
|
|
|
221
|
my $act = $name; |
1399
|
|
|
|
|
|
|
|
1400
|
92
|
50
|
|
|
|
257
|
if ( $capo ) { |
1401
|
0
|
|
|
|
|
0
|
$act = $self->add_chord( $info->transpose($capo) ); |
1402
|
0
|
0
|
|
|
|
0
|
$name = $act if $decapo; |
1403
|
|
|
|
|
|
|
} |
1404
|
|
|
|
|
|
|
|
1405
|
92
|
|
|
|
|
179
|
push( @{ $m->{key} }, $name ); |
|
92
|
|
|
|
|
372
|
|
1406
|
92
|
|
|
|
|
340
|
$m->{key_actual} = [ $act ]; |
1407
|
|
|
|
|
|
|
# warn("XX key=$name act=$act capo=", |
1408
|
|
|
|
|
|
|
# $capo//""," decapo=$decapo\n"); |
1409
|
92
|
|
|
|
|
477
|
return 1; |
1410
|
|
|
|
|
|
|
} |
1411
|
|
|
|
|
|
|
|
1412
|
|
|
|
|
|
|
|
1413
|
171
|
100
|
66
|
|
|
790
|
if ( $key eq "capo" ) { |
|
|
100
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
do_warn("Multiple capo settings may yield surprising results.") |
1415
|
16
|
100
|
|
|
|
69
|
if exists $m->{capo}; |
1416
|
|
|
|
|
|
|
|
1417
|
16
|
|
50
|
|
|
64
|
$capo = $val || undef; |
1418
|
16
|
50
|
33
|
|
|
97
|
if ( $capo && $m->{key} ) { |
1419
|
16
|
100
|
|
|
|
53
|
if ( $decapo ) { |
1420
|
|
|
|
|
|
|
my $key = $self->store_chord |
1421
|
4
|
|
|
|
|
39
|
($self->{chordsinfo}->{$m->{key}->[-1]} |
1422
|
|
|
|
|
|
|
->transpose($val)); |
1423
|
4
|
|
|
|
|
37
|
$m->{key}->[-1] = $key; |
1424
|
|
|
|
|
|
|
$key = $self->store_chord |
1425
|
4
|
|
|
|
|
19
|
($self->{chordsinfo}->{$m->{key}->[-1]} |
1426
|
|
|
|
|
|
|
->transpose($xpose)); |
1427
|
4
|
|
|
|
|
45
|
$m->{key_actual} = [ $key ]; |
1428
|
|
|
|
|
|
|
} |
1429
|
|
|
|
|
|
|
else { |
1430
|
12
|
|
|
|
|
142
|
my $act = $m->{key_actual}->[-1]; |
1431
|
12
|
|
|
|
|
48
|
$m->{key_from} = [ $act ]; |
1432
|
|
|
|
|
|
|
my $key = $self->store_chord |
1433
|
12
|
|
|
|
|
77
|
($self->{chordsinfo}->{$act}->transpose($val)); |
1434
|
12
|
|
|
|
|
82
|
$m->{key_actual} = [ $key ]; |
1435
|
|
|
|
|
|
|
} |
1436
|
|
|
|
|
|
|
} |
1437
|
|
|
|
|
|
|
} |
1438
|
|
|
|
|
|
|
|
1439
|
|
|
|
|
|
|
elsif ( $key eq "duration" && $val ) { |
1440
|
9
|
|
|
|
|
77
|
$val = duration($val); |
1441
|
|
|
|
|
|
|
} |
1442
|
|
|
|
|
|
|
|
1443
|
171
|
50
|
33
|
|
|
544
|
if ( $config->{metadata}->{strict} |
1444
|
1296
|
|
|
1296
|
|
1995
|
&& ! any { $_ eq $key } @{ $config->{metadata}->{keys} } ) { |
|
171
|
|
|
|
|
2320
|
|
1445
|
|
|
|
|
|
|
# Unknown, and strict. |
1446
|
|
|
|
|
|
|
do_warn("Unknown metadata item: $key") |
1447
|
0
|
0
|
|
|
|
0
|
if $config->{settings}->{strict}; |
1448
|
0
|
|
|
|
|
0
|
return; |
1449
|
|
|
|
|
|
|
} |
1450
|
|
|
|
|
|
|
|
1451
|
171
|
50
|
|
|
|
819
|
push( @{ $self->{meta}->{$key} }, $val ) if defined $val; |
|
171
|
|
|
|
|
870
|
|
1452
|
|
|
|
|
|
|
} |
1453
|
|
|
|
|
|
|
} |
1454
|
|
|
|
|
|
|
else { |
1455
|
|
|
|
|
|
|
do_warn("Incomplete meta directive: $d\n") |
1456
|
0
|
0
|
|
|
|
0
|
if $config->{settings}->{strict}; |
1457
|
0
|
|
|
|
|
0
|
return; |
1458
|
|
|
|
|
|
|
} |
1459
|
171
|
|
|
|
|
733
|
return 1; |
1460
|
|
|
|
|
|
|
} |
1461
|
|
|
|
|
|
|
|
1462
|
|
|
|
|
|
|
# Song / Global settings. |
1463
|
|
|
|
|
|
|
|
1464
|
199
|
100
|
66
|
|
|
918
|
if ( $dir eq "titles" |
1465
|
|
|
|
|
|
|
&& $arg =~ /^(left|right|center|centre)$/i ) { |
1466
|
|
|
|
|
|
|
$self->{settings}->{titles} = |
1467
|
22
|
100
|
|
|
|
185
|
lc($1) eq "centre" ? "center" : lc($1); |
1468
|
22
|
|
|
|
|
140
|
return 1; |
1469
|
|
|
|
|
|
|
} |
1470
|
|
|
|
|
|
|
|
1471
|
177
|
100
|
66
|
|
|
736
|
if ( $dir eq "columns" |
1472
|
|
|
|
|
|
|
&& $arg =~ /^(\d+)$/ ) { |
1473
|
|
|
|
|
|
|
# If there a column specifications in the config, retain them |
1474
|
|
|
|
|
|
|
# if the number of columns match. |
1475
|
19
|
50
|
33
|
|
|
162
|
unless( ref($config->{settings}->{columns}) eq 'ARRAY' |
1476
|
0
|
|
|
|
|
0
|
&& $arg == @{$config->{settings}->{columns}} |
1477
|
|
|
|
|
|
|
) { |
1478
|
19
|
|
|
|
|
69
|
$self->{settings}->{columns} = $arg; |
1479
|
|
|
|
|
|
|
} |
1480
|
19
|
|
|
|
|
92
|
return 1; |
1481
|
|
|
|
|
|
|
} |
1482
|
|
|
|
|
|
|
|
1483
|
158
|
100
|
100
|
|
|
693
|
if ( $dir eq "pagetype" || $dir eq "pagesize" ) { |
1484
|
2
|
|
|
|
|
9
|
$self->{settings}->{papersize} = $arg; |
1485
|
2
|
|
|
|
|
9
|
return 1; |
1486
|
|
|
|
|
|
|
} |
1487
|
|
|
|
|
|
|
|
1488
|
156
|
100
|
|
|
|
395
|
if ( $dir eq "diagrams" ) { # AKA grid |
1489
|
2
|
100
|
|
|
|
12
|
if ( $arg ne "" ) { |
1490
|
1
|
|
|
|
|
7
|
$self->{settings}->{diagrams} = !!is_true($arg); |
1491
|
1
|
50
|
|
|
|
10
|
$self->{settings}->{diagrampos} = lc($arg) |
1492
|
|
|
|
|
|
|
if $arg =~ /^(right|bottom|top|below)$/i; |
1493
|
|
|
|
|
|
|
} |
1494
|
|
|
|
|
|
|
else { |
1495
|
1
|
|
|
|
|
6
|
$self->{settings}->{diagrams} = 1; |
1496
|
|
|
|
|
|
|
} |
1497
|
2
|
|
|
|
|
9
|
return 1; |
1498
|
|
|
|
|
|
|
} |
1499
|
154
|
100
|
|
|
|
381
|
if ( $dir eq "no_grid" ) { |
1500
|
3
|
|
|
|
|
18
|
$self->{settings}->{diagrams} = 0; |
1501
|
3
|
|
|
|
|
13
|
return 1; |
1502
|
|
|
|
|
|
|
} |
1503
|
|
|
|
|
|
|
|
1504
|
151
|
100
|
|
|
|
389
|
if ( $dir eq "transpose" ) { |
1505
|
53
|
|
100
|
|
|
284
|
$propstack{transpose} //= []; |
1506
|
|
|
|
|
|
|
|
1507
|
53
|
100
|
|
|
|
273
|
if ( $arg =~ /^([-+]?\d+)\s*$/ ) { |
1508
|
32
|
|
|
|
|
111
|
my $new = $1; |
1509
|
32
|
|
|
|
|
64
|
push( @{ $propstack{transpose} }, [ $xpose, $xpose_dir ] ); |
|
32
|
|
|
|
|
120
|
|
1510
|
32
|
|
|
|
|
213
|
my %a = ( type => "control", |
1511
|
|
|
|
|
|
|
name => "transpose", |
1512
|
|
|
|
|
|
|
previous => [ $xpose, $xpose_dir ] |
1513
|
|
|
|
|
|
|
); |
1514
|
32
|
|
|
|
|
304
|
$xpose += $new; |
1515
|
32
|
|
|
|
|
79
|
$xpose_dir = $new <=> 0; |
1516
|
32
|
|
|
|
|
82
|
my $m = $self->{meta}; |
1517
|
32
|
100
|
|
|
|
112
|
if ( $m->{key} ) { |
1518
|
22
|
|
|
|
|
61
|
my $key = $m->{key}->[-1]; |
1519
|
22
|
|
|
|
|
47
|
my $xp = $xpose; |
1520
|
22
|
100
|
|
|
|
99
|
$xp += $capo if $capo; |
1521
|
22
|
|
|
|
|
159
|
my $xpk = $self->{chordsinfo}->{$key}->transpose($xp, $xp <=> 0); |
1522
|
22
|
|
|
|
|
117
|
$self->{chordsinfo}->{$xpk->name} = $xpk; |
1523
|
22
|
|
|
|
|
128
|
$m->{key_from} = [ $m->{key_actual}->[0] ]; |
1524
|
22
|
|
|
|
|
75
|
$m->{key_actual} = [ $xpk->name ]; |
1525
|
|
|
|
|
|
|
} |
1526
|
32
|
50
|
|
|
|
163
|
$self->add( %a, value => $xpose, dir => $xpose_dir ) |
1527
|
|
|
|
|
|
|
if $no_transpose; |
1528
|
|
|
|
|
|
|
} |
1529
|
|
|
|
|
|
|
else { |
1530
|
21
|
|
|
|
|
147
|
my %a = ( type => "control", |
1531
|
|
|
|
|
|
|
name => "transpose", |
1532
|
|
|
|
|
|
|
previous => [ $xpose, $xpose_dir ] |
1533
|
|
|
|
|
|
|
); |
1534
|
21
|
|
|
|
|
72
|
my $m = $self->{meta}; |
1535
|
21
|
|
|
|
|
51
|
my ( $new, $dir ); |
1536
|
21
|
50
|
|
|
|
38
|
if ( @{ $propstack{transpose} } ) { |
|
21
|
|
|
|
|
72
|
|
1537
|
21
|
|
|
|
|
38
|
( $new, $dir ) = @{ pop( @{ $propstack{transpose} } ) }; |
|
21
|
|
|
|
|
35
|
|
|
21
|
|
|
|
|
64
|
|
1538
|
|
|
|
|
|
|
} |
1539
|
|
|
|
|
|
|
else { |
1540
|
0
|
|
|
|
|
0
|
$new = 0; |
1541
|
0
|
|
|
|
|
0
|
$dir = $config->{settings}->{transpose} <=> 0; |
1542
|
|
|
|
|
|
|
} |
1543
|
21
|
|
|
|
|
61
|
$xpose = $new; |
1544
|
21
|
|
|
|
|
52
|
$xpose_dir = $dir; |
1545
|
21
|
100
|
|
|
|
95
|
if ( $m->{key} ) { |
1546
|
15
|
|
|
|
|
56
|
$m->{key_from} = [ $m->{key_actual}->[0] ]; |
1547
|
15
|
|
|
|
|
35
|
my $xp = $xpose; |
1548
|
15
|
50
|
66
|
|
|
61
|
$xp += $capo if $capo && $decapo; |
1549
|
|
|
|
|
|
|
$m->{key_actual} = |
1550
|
15
|
|
|
|
|
108
|
[ $self->{chordsinfo}->{$m->{key}->[-1]}->transpose($xp)->name ]; |
1551
|
|
|
|
|
|
|
} |
1552
|
21
|
100
|
|
|
|
173
|
if ( !@{ $propstack{transpose} } ) { |
|
21
|
|
|
|
|
96
|
|
1553
|
12
|
|
|
|
|
113
|
delete $m->{$_} for qw( key_from ); |
1554
|
|
|
|
|
|
|
} |
1555
|
21
|
50
|
|
|
|
262
|
$self->add( %a, value => $xpose, dir => $dir ) |
1556
|
|
|
|
|
|
|
if $no_transpose; |
1557
|
|
|
|
|
|
|
} |
1558
|
53
|
|
|
|
|
251
|
return 1; |
1559
|
|
|
|
|
|
|
} |
1560
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
# More private hacks. |
1562
|
98
|
50
|
33
|
|
|
611
|
if ( !$options->{reference} && $d =~ /^([-+])([-\w.]+)$/i ) { |
1563
|
0
|
0
|
|
|
|
0
|
if ( $2 eq "dumpmeta" ) { |
1564
|
0
|
|
|
|
|
0
|
warn(::dump($self->{meta})); |
1565
|
|
|
|
|
|
|
} |
1566
|
0
|
0
|
|
|
|
0
|
$self->add( type => "set", |
1567
|
|
|
|
|
|
|
name => $2, |
1568
|
|
|
|
|
|
|
value => $1 eq "+" ? 1 : 0, |
1569
|
|
|
|
|
|
|
); |
1570
|
0
|
|
|
|
|
0
|
return 1; |
1571
|
|
|
|
|
|
|
} |
1572
|
|
|
|
|
|
|
|
1573
|
98
|
100
|
66
|
|
|
501
|
if ( !$options->{reference} && $dir =~ /^\+([-\w.]+(?:\.[<>])?)$/ ) { |
1574
|
11
|
|
|
|
|
68
|
$self->add( type => "set", |
1575
|
|
|
|
|
|
|
name => $1, |
1576
|
|
|
|
|
|
|
value => $arg, |
1577
|
|
|
|
|
|
|
); |
1578
|
|
|
|
|
|
|
|
1579
|
|
|
|
|
|
|
# THIS IS BASICALLY A COPY OF THE CODE IN Config.pm. |
1580
|
|
|
|
|
|
|
# TODO: GENERALIZE. |
1581
|
11
|
|
|
|
|
26
|
my $ccfg = {}; |
1582
|
11
|
|
|
|
|
87
|
my @k = split( /[:.]/, $1 ); |
1583
|
11
|
|
|
|
|
27
|
my $c = \$ccfg; # new |
1584
|
11
|
|
|
|
|
23
|
my $o = $config; # current |
1585
|
11
|
|
|
|
|
33
|
my $lk = pop(@k); # last key |
1586
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
# Step through the keys. |
1588
|
11
|
|
|
|
|
30
|
foreach ( @k ) { |
1589
|
17
|
|
|
|
|
56
|
$c = \($$c->{$_}); |
1590
|
17
|
|
|
|
|
55
|
$o = $o->{$_}; |
1591
|
|
|
|
|
|
|
} |
1592
|
|
|
|
|
|
|
|
1593
|
|
|
|
|
|
|
# Turn hash.array into hash.array.> (append). |
1594
|
11
|
50
|
33
|
|
|
91
|
if ( ref($o) eq 'HASH' && ref($o->{$lk}) eq 'ARRAY' ) { |
1595
|
0
|
|
|
|
|
0
|
$c = \($$c->{$lk}); |
1596
|
0
|
|
|
|
|
0
|
$o = $o->{$lk}; |
1597
|
0
|
|
|
|
|
0
|
$lk = '>'; |
1598
|
|
|
|
|
|
|
} |
1599
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
# Final key. Merge array if so. |
1601
|
11
|
50
|
33
|
|
|
152
|
if ( ( $lk =~ /^\d+$/ || $lk eq '>' || $lk eq '<' ) |
|
|
|
33
|
|
|
|
|
1602
|
|
|
|
|
|
|
&& ref($o) eq 'ARRAY' ) { |
1603
|
0
|
0
|
|
|
|
0
|
unless ( ref($$c) eq 'ARRAY' ) { |
1604
|
|
|
|
|
|
|
# Only copy orig values the first time. |
1605
|
0
|
|
|
|
|
0
|
$$c->[$_] = $o->[$_] for 0..scalar(@{$o})-1; |
|
0
|
|
|
|
|
0
|
|
1606
|
|
|
|
|
|
|
} |
1607
|
0
|
0
|
|
|
|
0
|
if ( $lk eq '>' ) { |
|
|
0
|
|
|
|
|
|
1608
|
0
|
|
|
|
|
0
|
push( @{$$c}, $arg ); |
|
0
|
|
|
|
|
0
|
|
1609
|
|
|
|
|
|
|
} |
1610
|
|
|
|
|
|
|
elsif ( $lk eq '<' ) { |
1611
|
0
|
|
|
|
|
0
|
unshift( @{$$c}, $arg ); |
|
0
|
|
|
|
|
0
|
|
1612
|
|
|
|
|
|
|
} |
1613
|
|
|
|
|
|
|
else { |
1614
|
0
|
|
|
|
|
0
|
$$c->[$lk] = $arg; |
1615
|
|
|
|
|
|
|
} |
1616
|
|
|
|
|
|
|
} |
1617
|
|
|
|
|
|
|
else { |
1618
|
11
|
|
|
|
|
44
|
$$c->{$lk} = $arg; |
1619
|
|
|
|
|
|
|
} |
1620
|
|
|
|
|
|
|
|
1621
|
11
|
|
|
|
|
70
|
$config->augment($ccfg); |
1622
|
11
|
|
|
|
|
46
|
upd_config(); |
1623
|
|
|
|
|
|
|
|
1624
|
11
|
|
|
|
|
99
|
return 1; |
1625
|
|
|
|
|
|
|
} |
1626
|
|
|
|
|
|
|
|
1627
|
|
|
|
|
|
|
# Formatting. {chordsize XX} and such. |
1628
|
87
|
100
|
|
|
|
339
|
if ( $dir =~ m/ ^( text | chord | chorus | tab | grid | diagrams |
1629
|
|
|
|
|
|
|
| title | footer | toc ) |
1630
|
|
|
|
|
|
|
( font | size | colou?r ) |
1631
|
|
|
|
|
|
|
$/x ) { |
1632
|
28
|
|
|
|
|
99
|
my $item = $1; |
1633
|
28
|
|
|
|
|
53
|
my $prop = $2; |
1634
|
|
|
|
|
|
|
|
1635
|
28
|
|
|
|
|
85
|
$self->propset( $item, $prop, $arg ); |
1636
|
|
|
|
|
|
|
|
1637
|
|
|
|
|
|
|
# Derived props. |
1638
|
28
|
100
|
|
|
|
74
|
$self->propset( "chorus", $prop, $arg ) if $item eq "text"; |
1639
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
#::dump( { %propstack, line => $diag->{line} } ); |
1641
|
28
|
|
|
|
|
106
|
return 1; |
1642
|
|
|
|
|
|
|
} |
1643
|
|
|
|
|
|
|
|
1644
|
|
|
|
|
|
|
# define A: base-fret N frets N N N N N N fingers N N N N N N |
1645
|
|
|
|
|
|
|
# define: A base-fret N frets N N N N N N fingers N N N N N N |
1646
|
|
|
|
|
|
|
# optional: base-fret N (defaults to 1) |
1647
|
|
|
|
|
|
|
# optional: N N N N N N (for unknown chords) |
1648
|
|
|
|
|
|
|
# optional: fingers N N N N N N |
1649
|
|
|
|
|
|
|
|
1650
|
59
|
100
|
100
|
|
|
218
|
if ( $dir eq "define" or $dir eq "chord" ) { |
1651
|
|
|
|
|
|
|
|
1652
|
58
|
|
|
|
|
206
|
return $self->define_chord( $dir, $arg ); |
1653
|
|
|
|
|
|
|
} |
1654
|
|
|
|
|
|
|
|
1655
|
|
|
|
|
|
|
# Warn about unknowns, unless they are x_... form. |
1656
|
|
|
|
|
|
|
do_warn("Unknown directive: $d\n") |
1657
|
1
|
50
|
33
|
|
|
4
|
if $config->{settings}->{strict} && $d !~ /^x_/; |
1658
|
1
|
|
|
|
|
21
|
return; |
1659
|
|
|
|
|
|
|
} |
1660
|
|
|
|
|
|
|
|
1661
|
|
|
|
|
|
|
sub propset { |
1662
|
37
|
|
|
37
|
0
|
85
|
my ( $self, $item, $prop, $value ) = @_; |
1663
|
37
|
100
|
|
|
|
78
|
$prop = "color" if $prop eq "colour"; |
1664
|
37
|
|
|
|
|
78
|
my $name = "$item-$prop"; |
1665
|
37
|
|
100
|
|
|
236
|
$propstack{$name} //= []; |
1666
|
|
|
|
|
|
|
|
1667
|
37
|
100
|
|
|
|
82
|
if ( $value eq "" ) { |
1668
|
|
|
|
|
|
|
# Pop current value from stack. |
1669
|
2
|
50
|
|
|
|
4
|
if ( @{ $propstack{$name} } ) { |
|
2
|
|
|
|
|
6
|
|
1670
|
2
|
|
|
|
|
4
|
my $old = pop( @{ $propstack{$name} } ); |
|
2
|
|
|
|
|
4
|
|
1671
|
|
|
|
|
|
|
# A trailing number after a font directive means there |
1672
|
|
|
|
|
|
|
# was also a size saved. Pop it. |
1673
|
2
|
50
|
33
|
|
|
7
|
if ( $prop eq "font" && $old =~ /\s(\d+(?:\.\d+)?)$/ ) { |
1674
|
0
|
|
|
|
|
0
|
pop( @{ $propstack{"$item-size"} } ); |
|
0
|
|
|
|
|
0
|
|
1675
|
|
|
|
|
|
|
} |
1676
|
|
|
|
|
|
|
} |
1677
|
|
|
|
|
|
|
else { |
1678
|
0
|
|
|
|
|
0
|
do_warn("No saved value for property $item$prop\n" ) |
1679
|
|
|
|
|
|
|
} |
1680
|
|
|
|
|
|
|
# Use new current value, if any. |
1681
|
2
|
50
|
|
|
|
3
|
if ( @{ $propstack{$name} } ) { |
|
2
|
|
|
|
|
6
|
|
1682
|
2
|
|
|
|
|
5
|
$value = $propstack{$name}->[-1] |
1683
|
|
|
|
|
|
|
} |
1684
|
|
|
|
|
|
|
else { |
1685
|
0
|
|
|
|
|
0
|
$value = undef; |
1686
|
|
|
|
|
|
|
} |
1687
|
2
|
|
|
|
|
6
|
$self->add( type => "control", |
1688
|
|
|
|
|
|
|
name => $name, |
1689
|
|
|
|
|
|
|
value => $value ); |
1690
|
2
|
|
|
|
|
3
|
return 1; |
1691
|
|
|
|
|
|
|
} |
1692
|
|
|
|
|
|
|
|
1693
|
35
|
100
|
|
|
|
66
|
if ( $prop eq "size" ) { |
1694
|
10
|
50
|
|
|
|
81
|
unless ( $value =~ /^\d+(?:\.\d+)?\%?$/ ) { |
1695
|
0
|
|
|
|
|
0
|
do_warn("Illegal value \"$value\" for $item$prop\n"); |
1696
|
0
|
|
|
|
|
0
|
return 1; |
1697
|
|
|
|
|
|
|
} |
1698
|
|
|
|
|
|
|
} |
1699
|
35
|
100
|
|
|
|
73
|
if ( $prop eq "color" ) { |
1700
|
15
|
|
|
|
|
28
|
my $v; |
1701
|
15
|
50
|
|
|
|
37
|
unless ( $v = get_color($value) ) { |
1702
|
0
|
|
|
|
|
0
|
do_warn("Illegal value \"$value\" for $item$prop\n"); |
1703
|
0
|
|
|
|
|
0
|
return 1; |
1704
|
|
|
|
|
|
|
} |
1705
|
15
|
|
|
|
|
30
|
$value = $v; |
1706
|
|
|
|
|
|
|
} |
1707
|
35
|
100
|
|
|
|
109
|
$value = $prop eq "font" ? $value : lc($value); |
1708
|
35
|
|
|
|
|
105
|
$self->add( type => "control", |
1709
|
|
|
|
|
|
|
name => $name, |
1710
|
|
|
|
|
|
|
value => $value ); |
1711
|
35
|
|
|
|
|
50
|
push( @{ $propstack{$name} }, $value ); |
|
35
|
|
|
|
|
100
|
|
1712
|
|
|
|
|
|
|
|
1713
|
|
|
|
|
|
|
# A trailing number after a font directive is an implicit size |
1714
|
|
|
|
|
|
|
# directive. |
1715
|
35
|
50
|
66
|
|
|
127
|
if ( $prop eq 'font' && $value =~ /\s(\d+(?:\.\d+)?)$/ ) { |
1716
|
0
|
|
|
|
|
0
|
$self->add( type => "control", |
1717
|
|
|
|
|
|
|
name => "$item-size", |
1718
|
|
|
|
|
|
|
value => $1 ); |
1719
|
0
|
|
|
|
|
0
|
push( @{ $propstack{"$item-size"} }, $1 ); |
|
0
|
|
|
|
|
0
|
|
1720
|
|
|
|
|
|
|
} |
1721
|
|
|
|
|
|
|
} |
1722
|
|
|
|
|
|
|
|
1723
|
|
|
|
|
|
|
sub add_chord { |
1724
|
991
|
|
|
991
|
0
|
2346
|
my ( $self, $info, $new_id ) = @_; |
1725
|
|
|
|
|
|
|
|
1726
|
991
|
100
|
|
|
|
1993
|
if ( $new_id ) { |
1727
|
942
|
100
|
|
|
|
2201
|
if ( $new_id eq "1" ) { |
1728
|
10
|
|
|
|
|
36
|
state $id = "ch0000"; |
1729
|
10
|
|
|
|
|
32
|
$new_id = " $id"; |
1730
|
10
|
|
|
|
|
24
|
$id++; |
1731
|
|
|
|
|
|
|
} |
1732
|
|
|
|
|
|
|
} |
1733
|
|
|
|
|
|
|
else { |
1734
|
49
|
|
|
|
|
157
|
$new_id = $info->name; |
1735
|
|
|
|
|
|
|
} |
1736
|
991
|
|
|
|
|
2496
|
$self->{chordsinfo}->{$new_id} = $info->new($info); |
1737
|
|
|
|
|
|
|
|
1738
|
991
|
|
|
|
|
4215
|
return $new_id; |
1739
|
|
|
|
|
|
|
} |
1740
|
|
|
|
|
|
|
|
1741
|
|
|
|
|
|
|
sub define_chord { |
1742
|
70
|
|
|
70
|
0
|
184
|
my ( $self, $dir, $args ) = @_; |
1743
|
|
|
|
|
|
|
|
1744
|
|
|
|
|
|
|
# Split the arguments and keep a copy for error messages. |
1745
|
|
|
|
|
|
|
# Note that quotewords returns an empty result if it gets confused, |
1746
|
|
|
|
|
|
|
# so fall back to the ancient split method if so. |
1747
|
70
|
|
|
|
|
225
|
$args =~ s/^\s+//; |
1748
|
70
|
|
|
|
|
328
|
$args =~ s/\s+$//; |
1749
|
70
|
|
|
|
|
283
|
my @a = quotewords( '[: ]+', 0, $args ); |
1750
|
70
|
100
|
|
|
|
20598
|
@a = split( /[: ]+/, $args ) unless @a; |
1751
|
|
|
|
|
|
|
|
1752
|
70
|
|
|
|
|
248
|
my @orig = @a; |
1753
|
70
|
|
|
|
|
161
|
my $show = $dir eq "chord"; |
1754
|
70
|
|
|
|
|
123
|
my $fail = 0; |
1755
|
70
|
|
|
|
|
138
|
my $name = shift(@a); |
1756
|
70
|
|
|
|
|
281
|
my $strings = $config->diagram_strings; |
1757
|
|
|
|
|
|
|
|
1758
|
|
|
|
|
|
|
# Process the options. |
1759
|
70
|
|
|
|
|
225
|
my %kv = ( name => $name ); |
1760
|
70
|
|
|
|
|
217
|
while ( @a ) { |
1761
|
162
|
|
|
|
|
276
|
my $a = shift(@a); |
1762
|
|
|
|
|
|
|
|
1763
|
|
|
|
|
|
|
# Copy existing definition. |
1764
|
162
|
100
|
66
|
|
|
1222
|
if ( $a eq "copy" || $a eq "copyall" ) { |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1765
|
11
|
50
|
|
|
|
42
|
if ( my $i = ChordPro::Chords::known_chord($a[0]) ) { |
1766
|
11
|
|
|
|
|
37
|
$kv{$a} = $a[0]; |
1767
|
11
|
|
|
|
|
30
|
$kv{orig} = $i; |
1768
|
11
|
|
|
|
|
38
|
shift(@a); |
1769
|
|
|
|
|
|
|
} |
1770
|
|
|
|
|
|
|
else { |
1771
|
0
|
|
|
|
|
0
|
do_warn("Unknown chord to copy: $a[0]\n"); |
1772
|
0
|
|
|
|
|
0
|
$fail++; |
1773
|
0
|
|
|
|
|
0
|
last; |
1774
|
|
|
|
|
|
|
} |
1775
|
|
|
|
|
|
|
} |
1776
|
|
|
|
|
|
|
|
1777
|
|
|
|
|
|
|
# display |
1778
|
|
|
|
|
|
|
elsif ( $a eq "display" && @a ) { |
1779
|
2
|
|
|
|
|
14
|
$kv{display} = demarkup($a[0]); |
1780
|
|
|
|
|
|
|
do_warn( "\"display\" should not contain markup, use \"format\"" ) |
1781
|
2
|
50
|
|
|
|
10
|
unless $kv{display} eq shift(@a); |
1782
|
2
|
|
|
|
|
18
|
$kv{display} = $self->parse_chord($kv{display},1); |
1783
|
2
|
50
|
|
|
|
12
|
delete $kv{display} unless defined $kv{display}; |
1784
|
|
|
|
|
|
|
} |
1785
|
|
|
|
|
|
|
|
1786
|
|
|
|
|
|
|
# format |
1787
|
|
|
|
|
|
|
elsif ( $a eq "format" && @a ) { |
1788
|
9
|
|
|
|
|
33
|
$kv{format} = shift(@a); |
1789
|
|
|
|
|
|
|
} |
1790
|
|
|
|
|
|
|
|
1791
|
|
|
|
|
|
|
# base-fret N |
1792
|
|
|
|
|
|
|
elsif ( $a eq "base-fret" ) { |
1793
|
46
|
50
|
|
|
|
235
|
if ( $a[0] =~ /^\d+$/ ) { |
1794
|
46
|
|
|
|
|
181
|
$kv{base} = shift(@a); |
1795
|
|
|
|
|
|
|
} |
1796
|
|
|
|
|
|
|
else { |
1797
|
0
|
|
|
|
|
0
|
do_warn("Invalid base-fret value: $a[0]\n"); |
1798
|
0
|
|
|
|
|
0
|
$fail++; |
1799
|
0
|
|
|
|
|
0
|
last; |
1800
|
|
|
|
|
|
|
} |
1801
|
|
|
|
|
|
|
} |
1802
|
|
|
|
|
|
|
# frets N N ... N |
1803
|
|
|
|
|
|
|
elsif ( $a eq "frets" ) { |
1804
|
57
|
|
|
|
|
91
|
my @f; |
1805
|
57
|
|
100
|
|
|
422
|
while ( @a && $a[0] =~ /^(?:[0-9]+|[-xXN])$/ && @f < $strings ) { |
|
|
|
66
|
|
|
|
|
1806
|
342
|
|
|
|
|
1612
|
push( @f, shift(@a) ); |
1807
|
|
|
|
|
|
|
} |
1808
|
57
|
50
|
|
|
|
179
|
if ( @f == $strings ) { |
1809
|
57
|
100
|
|
|
|
140
|
$kv{frets} = [ map { $_ =~ /^\d+/ ? $_ : -1 } @f ]; |
|
342
|
|
|
|
|
1052
|
|
1810
|
|
|
|
|
|
|
} |
1811
|
|
|
|
|
|
|
else { |
1812
|
0
|
|
|
|
|
0
|
do_warn("Incorrect number of fret positions (" . |
1813
|
|
|
|
|
|
|
scalar(@f) . ", should be $strings)\n"); |
1814
|
0
|
|
|
|
|
0
|
$fail++; |
1815
|
0
|
|
|
|
|
0
|
last; |
1816
|
|
|
|
|
|
|
} |
1817
|
|
|
|
|
|
|
} |
1818
|
|
|
|
|
|
|
|
1819
|
|
|
|
|
|
|
# fingers N N ... N |
1820
|
|
|
|
|
|
|
elsif ( $a eq "fingers" ) { |
1821
|
16
|
|
|
|
|
32
|
my @f; |
1822
|
|
|
|
|
|
|
# It is tempting to limit the fingers to 1..5 ... |
1823
|
16
|
|
100
|
|
|
64
|
while ( @a && @f < $strings ) { |
1824
|
96
|
|
|
|
|
173
|
local $_ = shift(@a); |
1825
|
96
|
100
|
|
|
|
263
|
if ( /^[0-9]+$/ ) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1826
|
90
|
|
|
|
|
315
|
push( @f, 0 + $_ ); |
1827
|
|
|
|
|
|
|
} |
1828
|
|
|
|
|
|
|
elsif ( /^[A-MO-WYZ]$/ ) { |
1829
|
0
|
|
|
|
|
0
|
push( @f, $_ ); |
1830
|
|
|
|
|
|
|
} |
1831
|
|
|
|
|
|
|
elsif ( /^[-xNX]$/ ) { |
1832
|
6
|
|
|
|
|
20
|
push( @f, -1 ); |
1833
|
|
|
|
|
|
|
} |
1834
|
|
|
|
|
|
|
else { |
1835
|
0
|
|
|
|
|
0
|
unshift( @a, $_ ); |
1836
|
0
|
|
|
|
|
0
|
last; |
1837
|
|
|
|
|
|
|
} |
1838
|
|
|
|
|
|
|
} |
1839
|
16
|
50
|
|
|
|
62
|
if ( @f == $strings ) { |
1840
|
16
|
|
|
|
|
59
|
$kv{fingers} = \@f; |
1841
|
|
|
|
|
|
|
} |
1842
|
|
|
|
|
|
|
else { |
1843
|
0
|
|
|
|
|
0
|
do_warn("Incorrect number of finger settings (" . |
1844
|
|
|
|
|
|
|
scalar(@f) . ", should be $strings)\n"); |
1845
|
0
|
|
|
|
|
0
|
$fail++; |
1846
|
0
|
|
|
|
|
0
|
last; |
1847
|
|
|
|
|
|
|
} |
1848
|
|
|
|
|
|
|
} |
1849
|
|
|
|
|
|
|
|
1850
|
|
|
|
|
|
|
# keys N N ... N |
1851
|
|
|
|
|
|
|
elsif ( $a eq "keys" ) { |
1852
|
8
|
|
|
|
|
16
|
my @f; |
1853
|
8
|
|
100
|
|
|
45
|
while ( @a && $a[0] =~ /^[0-9]+$/ ) { |
1854
|
24
|
|
|
|
|
100
|
push( @f, shift(@a) ); |
1855
|
|
|
|
|
|
|
} |
1856
|
8
|
50
|
|
|
|
17
|
if ( @f ) { |
1857
|
8
|
|
|
|
|
23
|
$kv{keys} = \@f; |
1858
|
|
|
|
|
|
|
} |
1859
|
|
|
|
|
|
|
else { |
1860
|
0
|
|
|
|
|
0
|
do_warn("Invalid or missing keys\n"); |
1861
|
0
|
|
|
|
|
0
|
$fail++; |
1862
|
0
|
|
|
|
|
0
|
last; |
1863
|
|
|
|
|
|
|
} |
1864
|
|
|
|
|
|
|
} |
1865
|
|
|
|
|
|
|
|
1866
|
|
|
|
|
|
|
elsif ( $a eq "diagram" && @a > 0 ) { |
1867
|
13
|
50
|
33
|
|
|
42
|
if ( $show && !is_true($a[0]) ) { |
1868
|
0
|
|
|
|
|
0
|
do_warn("Useless diagram suppression"); |
1869
|
0
|
|
|
|
|
0
|
next; |
1870
|
|
|
|
|
|
|
} |
1871
|
13
|
|
|
|
|
39
|
$kv{diagram} = shift(@a); |
1872
|
|
|
|
|
|
|
} |
1873
|
|
|
|
|
|
|
|
1874
|
|
|
|
|
|
|
# Wrong... |
1875
|
|
|
|
|
|
|
else { |
1876
|
|
|
|
|
|
|
# Insert a marker to show how far we got. |
1877
|
0
|
|
|
|
|
0
|
splice( @orig, @orig-@a, 0, "<<<" ); |
1878
|
0
|
|
|
|
|
0
|
splice( @orig, @orig-@a-2, 0, ">>>" ); |
1879
|
0
|
|
|
|
|
0
|
do_warn("Invalid chord definition: @orig\n"); |
1880
|
0
|
|
|
|
|
0
|
$fail++; |
1881
|
0
|
|
|
|
|
0
|
last; |
1882
|
|
|
|
|
|
|
} |
1883
|
|
|
|
|
|
|
} |
1884
|
|
|
|
|
|
|
|
1885
|
70
|
50
|
|
|
|
173
|
return 1 if $fail; |
1886
|
|
|
|
|
|
|
# All options are verified and stored in %kv; |
1887
|
|
|
|
|
|
|
|
1888
|
|
|
|
|
|
|
# Result structure. |
1889
|
70
|
|
|
|
|
210
|
my $res = { name => $name }; |
1890
|
|
|
|
|
|
|
|
1891
|
|
|
|
|
|
|
# Try to find info. |
1892
|
70
|
|
|
|
|
238
|
my $info = $self->parse_chord( $name, "def" ); |
1893
|
70
|
50
|
|
|
|
183
|
if ( $info ) { |
1894
|
|
|
|
|
|
|
# Copy the chord info. |
1895
|
|
|
|
|
|
|
$res->{$_} //= $info->{$_} // '' |
1896
|
70
|
|
100
|
|
|
2110
|
for qw( root qual ext bass |
|
|
|
66
|
|
|
|
|
1897
|
|
|
|
|
|
|
root_canon qual_canon ext_canon bass_canon |
1898
|
|
|
|
|
|
|
root_ord root_mod bass_ord bass_mod |
1899
|
|
|
|
|
|
|
); |
1900
|
70
|
100
|
|
|
|
202
|
if ( $show ) { |
1901
|
|
|
|
|
|
|
$res->{$_} //= $info->{$_} |
1902
|
8
|
|
66
|
|
|
90
|
for qw( base frets fingers keys ); |
1903
|
|
|
|
|
|
|
} |
1904
|
|
|
|
|
|
|
} |
1905
|
|
|
|
|
|
|
else { |
1906
|
0
|
|
|
|
|
0
|
$res->{parser} = ChordPro::Chords::get_parser(); |
1907
|
|
|
|
|
|
|
} |
1908
|
|
|
|
|
|
|
|
1909
|
|
|
|
|
|
|
# Copy existing definition. |
1910
|
70
|
|
66
|
|
|
347
|
for ( $kv{copyall} // $kv{copy} ) { |
1911
|
70
|
100
|
|
|
|
199
|
next unless defined; |
1912
|
11
|
|
|
|
|
34
|
$res->{copy} = $_; |
1913
|
11
|
|
|
|
|
29
|
my $orig = $res->{orig} = $kv{orig}; |
1914
|
|
|
|
|
|
|
$res->{$_} //= $orig->{$_} |
1915
|
11
|
|
33
|
|
|
119
|
for qw( base frets fingers keys ); |
1916
|
11
|
50
|
|
|
|
48
|
if ( $kv{copyall} ) { |
1917
|
|
|
|
|
|
|
$res->{$_} //= $orig->{$_} |
1918
|
0
|
|
0
|
|
|
0
|
for qw( display format ); |
1919
|
|
|
|
|
|
|
} |
1920
|
|
|
|
|
|
|
} |
1921
|
70
|
|
|
|
|
117
|
for ( qw( display format ) ) { |
1922
|
140
|
100
|
|
|
|
336
|
$res->{$_} = $kv{$_} if defined $kv{$_}; |
1923
|
|
|
|
|
|
|
} |
1924
|
|
|
|
|
|
|
|
1925
|
|
|
|
|
|
|
# If we've got diagram visibility, remove it if true. |
1926
|
70
|
100
|
|
|
|
186
|
if ( defined $kv{diagram} ) { |
1927
|
13
|
|
|
|
|
30
|
for ( my $v = $kv{diagram} ) { |
1928
|
13
|
100
|
|
|
|
53
|
if ( is_true($v) ) { |
1929
|
7
|
100
|
|
|
|
30
|
if ( is_ttrue($v) ) { |
1930
|
6
|
|
|
|
|
18
|
next; |
1931
|
|
|
|
|
|
|
} |
1932
|
|
|
|
|
|
|
} |
1933
|
|
|
|
|
|
|
else { |
1934
|
6
|
|
|
|
|
15
|
$v = 0; |
1935
|
|
|
|
|
|
|
} |
1936
|
7
|
|
|
|
|
21
|
$res->{diagram} = $v; |
1937
|
|
|
|
|
|
|
} |
1938
|
|
|
|
|
|
|
} |
1939
|
|
|
|
|
|
|
|
1940
|
|
|
|
|
|
|
# Copy rest of options. |
1941
|
70
|
|
|
|
|
163
|
for ( qw( base frets fingers keys display format ) ) { |
1942
|
420
|
100
|
|
|
|
833
|
next unless defined $kv{$_}; |
1943
|
138
|
|
|
|
|
288
|
$res->{$_} = $kv{$_}; |
1944
|
|
|
|
|
|
|
} |
1945
|
|
|
|
|
|
|
|
1946
|
|
|
|
|
|
|
# At this time, $res is still just a hash. Time to make a chord. |
1947
|
70
|
|
100
|
|
|
210
|
$res->{base} ||= 1; |
1948
|
70
|
100
|
|
|
|
959
|
$res = ChordPro::Chord::Common->new |
1949
|
|
|
|
|
|
|
( { %$res, origin => $show ? "inline" : "song" } ); |
1950
|
70
|
|
33
|
|
|
651
|
$res->{parser} //= ChordPro::Chords::get_parser(); |
1951
|
|
|
|
|
|
|
|
1952
|
70
|
100
|
|
|
|
164
|
if ( $show) { |
1953
|
8
|
|
|
|
|
49
|
my $ci = $res->clone; |
1954
|
8
|
|
|
|
|
6300
|
my $chidx = $self->add_chord( $ci, 1 ); |
1955
|
|
|
|
|
|
|
# Combine consecutive entries. |
1956
|
8
|
100
|
66
|
|
|
81
|
if ( defined($self->{body}) |
1957
|
|
|
|
|
|
|
&& $self->{body}->[-1]->{type} eq "diagrams" ) { |
1958
|
2
|
|
|
|
|
10
|
push( @{ $self->{body}->[-1]->{chords} }, $chidx ); |
|
2
|
|
|
|
|
10
|
|
1959
|
|
|
|
|
|
|
} |
1960
|
|
|
|
|
|
|
else { |
1961
|
6
|
|
|
|
|
33
|
$self->add( type => "diagrams", |
1962
|
|
|
|
|
|
|
show => "user", |
1963
|
|
|
|
|
|
|
origin => "chord", |
1964
|
|
|
|
|
|
|
chords => [ $chidx ] ); |
1965
|
|
|
|
|
|
|
} |
1966
|
8
|
|
|
|
|
100
|
return 1; |
1967
|
|
|
|
|
|
|
} |
1968
|
|
|
|
|
|
|
|
1969
|
62
|
|
|
|
|
125
|
my $def = {}; |
1970
|
62
|
|
|
|
|
148
|
for ( qw( name base frets fingers keys display format diagram ) ) { |
1971
|
496
|
100
|
|
|
|
988
|
next unless defined $res->{$_}; |
1972
|
246
|
|
|
|
|
470
|
$def->{$_} = $res->{$_}; |
1973
|
|
|
|
|
|
|
} |
1974
|
62
|
|
|
|
|
110
|
push( @{$self->{define}}, $def ); |
|
62
|
|
|
|
|
192
|
|
1975
|
62
|
|
|
|
|
197
|
my $ret = ChordPro::Chords::add_song_chord($res); |
1976
|
62
|
50
|
|
|
|
171
|
if ( $ret ) { |
1977
|
0
|
|
|
|
|
0
|
do_warn("Invalid chord: ", $res->{name}, ": ", $ret, "\n"); |
1978
|
0
|
|
|
|
|
0
|
return 1; |
1979
|
|
|
|
|
|
|
} |
1980
|
62
|
|
|
|
|
179
|
$info = ChordPro::Chords::known_chord($res->{name}); |
1981
|
62
|
50
|
|
|
|
167
|
croak("We just entered it?? ", $res->{name}) unless $info; |
1982
|
|
|
|
|
|
|
|
1983
|
62
|
50
|
|
|
|
204
|
$info->dump if $config->{debug}->{x1}; |
1984
|
|
|
|
|
|
|
|
1985
|
62
|
|
|
|
|
555
|
return 1; |
1986
|
|
|
|
|
|
|
} |
1987
|
|
|
|
|
|
|
|
1988
|
|
|
|
|
|
|
sub duration { |
1989
|
9
|
|
|
9
|
0
|
46
|
my ( $dur ) = @_; |
1990
|
|
|
|
|
|
|
|
1991
|
9
|
50
|
|
|
|
96
|
if ( $dur =~ /(?:(?:(\d+):)?(\d+):)?(\d+)/ ) { |
1992
|
9
|
50
|
|
|
|
81
|
$dur = $3 + ( $2 ? 60 * $2 :0 ) + ( $1 ? 3600 * $1 : 0 ); |
|
|
50
|
|
|
|
|
|
1993
|
|
|
|
|
|
|
} |
1994
|
9
|
|
|
|
|
134
|
my $res = sprintf( "%d:%02d:%02d", |
1995
|
|
|
|
|
|
|
int( $dur / 3600 ), |
1996
|
|
|
|
|
|
|
int( ( $dur % 3600 ) / 60 ), |
1997
|
|
|
|
|
|
|
$dur % 60 ); |
1998
|
9
|
|
|
|
|
58
|
$res =~ s/^[0:]+//; |
1999
|
9
|
|
|
|
|
27
|
return $res; |
2000
|
|
|
|
|
|
|
} |
2001
|
|
|
|
|
|
|
|
2002
|
|
|
|
|
|
|
sub get_color { |
2003
|
15
|
|
|
15
|
0
|
42
|
$_[0]; |
2004
|
|
|
|
|
|
|
} |
2005
|
|
|
|
|
|
|
|
2006
|
|
|
|
|
|
|
sub _diag { |
2007
|
23
|
|
|
23
|
|
19412
|
my ( $self, %d ) = @_; |
2008
|
23
|
|
|
|
|
157
|
$diag->{$_} = $d{$_} for keys(%d); |
2009
|
|
|
|
|
|
|
} |
2010
|
|
|
|
|
|
|
|
2011
|
|
|
|
|
|
|
sub msg { |
2012
|
3
|
|
|
3
|
0
|
14
|
my $m = join("", @_); |
2013
|
3
|
|
|
|
|
13
|
$m =~ s/\n+$//; |
2014
|
3
|
|
|
|
|
10
|
my $t = $diag->{format}; |
2015
|
3
|
|
|
|
|
20
|
$t =~ s/\\n/\n/g; |
2016
|
3
|
|
|
|
|
9
|
$t =~ s/\\t/\t/g; |
2017
|
3
|
|
|
|
|
21
|
$t =~ s/\%f/$diag->{file}/g; |
2018
|
3
|
|
|
|
|
17
|
$t =~ s/\%n/$diag->{line}/g; |
2019
|
3
|
|
|
|
|
14
|
$t =~ s/\%l/$diag->{orig}/g; |
2020
|
3
|
|
|
|
|
12
|
$t =~ s/\%m/$m/g; |
2021
|
3
|
|
|
|
|
46
|
$t; |
2022
|
|
|
|
|
|
|
} |
2023
|
|
|
|
|
|
|
|
2024
|
|
|
|
|
|
|
sub do_warn { |
2025
|
3
|
|
|
3
|
0
|
29
|
warn(msg(@_)."\n"); |
2026
|
|
|
|
|
|
|
} |
2027
|
|
|
|
|
|
|
|
2028
|
|
|
|
|
|
|
# Parse a chord. |
2029
|
|
|
|
|
|
|
# Handles transpose/transcode. |
2030
|
|
|
|
|
|
|
# Returns the chord object. |
2031
|
|
|
|
|
|
|
# No parens or annotations, please. |
2032
|
|
|
|
|
|
|
sub parse_chord { |
2033
|
1112
|
|
|
1112
|
0
|
3462
|
my ( $self, $chord, $def ) = @_; |
2034
|
|
|
|
|
|
|
|
2035
|
1112
|
|
|
|
|
2619
|
my $debug = $config->{debug}->{chords}; |
2036
|
|
|
|
|
|
|
|
2037
|
1112
|
50
|
|
|
|
2378
|
warn("Parsing chord: \"$chord\"\n") if $debug; |
2038
|
1112
|
|
|
|
|
1654
|
my $info; |
2039
|
1112
|
|
|
|
|
2279
|
my $xp = $xpose + $config->{settings}->{transpose}; |
2040
|
1112
|
100
|
100
|
|
|
2623
|
$xp += $capo if $capo && $decapo; |
2041
|
1112
|
|
|
|
|
2586
|
my $xc = $config->{settings}->{transcode}; |
2042
|
1112
|
|
|
|
|
2057
|
my $global_dir = $config->{settings}->{transpose} <=> 0; |
2043
|
1112
|
|
|
|
|
1587
|
my $unk; |
2044
|
|
|
|
|
|
|
|
2045
|
|
|
|
|
|
|
# When called from {define} ignore xc/xp. |
2046
|
1112
|
100
|
|
|
|
2152
|
$xc = $xp = '' if $def; |
2047
|
|
|
|
|
|
|
|
2048
|
1112
|
|
|
|
|
3211
|
$info = ChordPro::Chords::known_chord($chord); |
2049
|
1112
|
100
|
|
|
|
2603
|
if ( $info ) { |
2050
|
|
|
|
|
|
|
warn( "Parsing chord: \"$chord\" found \"", |
2051
|
880
|
50
|
|
|
|
1937
|
$info->name, "\" in ", $info->{_via}, "\n" ) if $debug > 1; |
2052
|
880
|
50
|
|
|
|
1783
|
$info->dump if $debug > 1; |
2053
|
|
|
|
|
|
|
} |
2054
|
|
|
|
|
|
|
else { |
2055
|
232
|
|
|
|
|
591
|
$info = ChordPro::Chords::parse_chord($chord); |
2056
|
|
|
|
|
|
|
warn( "Parsing chord: \"$chord\" parsed ok [", |
2057
|
|
|
|
|
|
|
$info->{system}, |
2058
|
232
|
50
|
66
|
|
|
1168
|
"]\n" ) if $info && $debug > 1; |
2059
|
|
|
|
|
|
|
} |
2060
|
1112
|
|
|
|
|
2030
|
$unk = !defined $info; |
2061
|
|
|
|
|
|
|
|
2062
|
1112
|
100
|
100
|
|
|
6222
|
if ( ( $def || $xp || $xc ) |
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
2063
|
|
|
|
|
|
|
&& |
2064
|
|
|
|
|
|
|
! ($info && $info->is_xpxc ) ) { |
2065
|
21
|
|
|
|
|
63
|
local $::config->{settings}->{chordnames} = "relaxed"; |
2066
|
21
|
|
|
|
|
53
|
$info = ChordPro::Chords::parse_chord($chord); |
2067
|
|
|
|
|
|
|
} |
2068
|
|
|
|
|
|
|
|
2069
|
1112
|
0
|
33
|
|
|
4328
|
unless ( ( $info && $info->is_xpxc ) |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
33
|
|
|
|
|
2070
|
|
|
|
|
|
|
|| |
2071
|
|
|
|
|
|
|
( $def && !( $xc || $xp ) ) ) { |
2072
|
|
|
|
|
|
|
do_warn( "Cannot parse", |
2073
|
|
|
|
|
|
|
$xp ? "/transpose" : "", |
2074
|
|
|
|
|
|
|
$xc ? "/transcode" : "", |
2075
|
|
|
|
|
|
|
" chord \"$chord\"\n" ) |
2076
|
0
|
0
|
0
|
|
|
0
|
if $xp || $xc || $config->{debug}->{chords}; |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2077
|
|
|
|
|
|
|
} |
2078
|
|
|
|
|
|
|
|
2079
|
1112
|
100
|
66
|
|
|
3149
|
if ( $xp && $info ) { |
2080
|
|
|
|
|
|
|
# For transpose/transcode, chord must be wellformed. |
2081
|
156
|
|
100
|
|
|
733
|
my $i = $info->transpose( $xp, |
2082
|
|
|
|
|
|
|
$xpose_dir // $global_dir); |
2083
|
|
|
|
|
|
|
# Prevent self-references. |
2084
|
156
|
100
|
|
|
|
728
|
$i->{xp} = $info unless $i eq $info; |
2085
|
156
|
|
|
|
|
289
|
$info = $i; |
2086
|
156
|
50
|
|
|
|
446
|
warn( "Parsing chord: \"$chord\" transposed ", |
2087
|
|
|
|
|
|
|
sprintf("%+d", $xp), " to \"", |
2088
|
|
|
|
|
|
|
$info->name, "\"\n" ) if $debug > 1; |
2089
|
|
|
|
|
|
|
} |
2090
|
|
|
|
|
|
|
# else: warning has been given. |
2091
|
|
|
|
|
|
|
|
2092
|
1112
|
50
|
|
|
|
2330
|
if ( $info ) { # TODO roman? |
2093
|
|
|
|
|
|
|
# Look it up now, the name may change by transcode. |
2094
|
1112
|
100
|
33
|
|
|
2637
|
if ( my $i = ChordPro::Chords::known_chord($info) ) { |
|
|
50
|
|
|
|
|
|
2095
|
|
|
|
|
|
|
warn( "Parsing chord: \"$chord\" found ", |
2096
|
|
|
|
|
|
|
$i->name, " for ", $info->name, |
2097
|
885
|
50
|
|
|
|
2052
|
" in ", $i->{_via}, "\n" ) if $debug > 1; |
2098
|
|
|
|
|
|
|
$info = $i->new({ %$i, name => $info->name, |
2099
|
|
|
|
|
|
|
$info->{xp} ? ( xp => $info->{xp} ) : (), |
2100
|
885
|
100
|
|
|
|
5268
|
$info->{xc} ? ( xc => $info->{xc} ) : (), |
|
|
50
|
|
|
|
|
|
2101
|
|
|
|
|
|
|
}) ; |
2102
|
885
|
|
|
|
|
4549
|
$unk = 0; |
2103
|
|
|
|
|
|
|
} |
2104
|
|
|
|
|
|
|
elsif ( $config->{instrument}->{type} eq 'keyboard' |
2105
|
|
|
|
|
|
|
&& ( my $k = ChordPro::Chords::get_keys($info) ) ) { |
2106
|
0
|
0
|
|
|
|
0
|
warn( "Parsing chord: \"$chord\" \"", $info->name, "\" not found ", |
2107
|
|
|
|
|
|
|
"but we know what to do\n" ) if $debug > 1; |
2108
|
0
|
|
|
|
|
0
|
$info = $info->new({ %$info, keys => $k }) ; |
2109
|
0
|
|
|
|
|
0
|
$unk = 0; |
2110
|
|
|
|
|
|
|
} |
2111
|
|
|
|
|
|
|
else { |
2112
|
227
|
50
|
|
|
|
497
|
warn( "Parsing chord: \"$chord\" \"", $info->name, |
2113
|
|
|
|
|
|
|
"\" not found in song/config chords\n" ) if $debug; |
2114
|
|
|
|
|
|
|
# warn("XX \'", $info->agnostic, "\'\n"); |
2115
|
227
|
|
|
|
|
402
|
$unk = 1; |
2116
|
|
|
|
|
|
|
} |
2117
|
|
|
|
|
|
|
} |
2118
|
|
|
|
|
|
|
|
2119
|
1112
|
100
|
66
|
|
|
3036
|
if ( $xc && $info ) { |
2120
|
20
|
|
|
|
|
32
|
my $key_ord; |
2121
|
|
|
|
|
|
|
$key_ord = $self->{chordsinfo}->{$self->{meta}->{key}->[-1]}->{root_ord} |
2122
|
20
|
100
|
|
|
|
118
|
if $self->{meta}->{key}; |
2123
|
20
|
50
|
33
|
|
|
63
|
if ( $xcmov && !defined $key_ord ) { |
2124
|
0
|
|
|
|
|
0
|
do_warn("Warning: Transcoding to $xc without key may yield unexpected results\n"); |
2125
|
0
|
|
|
|
|
0
|
undef $xcmov; |
2126
|
|
|
|
|
|
|
} |
2127
|
20
|
|
|
|
|
158
|
my $i = $info->transcode( $xc, $key_ord ); |
2128
|
|
|
|
|
|
|
# Prevent self-references. |
2129
|
20
|
50
|
|
|
|
107
|
$i->{xc} = $info unless $i eq $info; |
2130
|
20
|
|
|
|
|
32
|
$info = $i; |
2131
|
|
|
|
|
|
|
warn( "Parsing chord: \"$chord\" transcoded to ", |
2132
|
|
|
|
|
|
|
$info->name, |
2133
|
20
|
50
|
|
|
|
54
|
" (", $info->{system}, ")", |
2134
|
|
|
|
|
|
|
"\n" ) if $debug > 1; |
2135
|
20
|
100
|
|
|
|
64
|
if ( my $i = ChordPro::Chords::known_chord($info) ) { |
2136
|
8
|
50
|
|
|
|
23
|
warn( "Parsing chord: \"$chord\" found \"", |
2137
|
|
|
|
|
|
|
$info->name, "\" in song/config chords\n" ) if $debug > 1; |
2138
|
8
|
|
|
|
|
22
|
$unk = 0; |
2139
|
|
|
|
|
|
|
} |
2140
|
|
|
|
|
|
|
} |
2141
|
|
|
|
|
|
|
# else: warning has been given. |
2142
|
|
|
|
|
|
|
|
2143
|
1112
|
50
|
|
|
|
2444
|
if ( ! $info ) { |
2144
|
0
|
0
|
|
|
|
0
|
if ( my $i = ChordPro::Chords::known_chord($chord) ) { |
2145
|
0
|
|
|
|
|
0
|
$info = $i; |
2146
|
|
|
|
|
|
|
warn( "Parsing chord: \"$chord\" found \"", |
2147
|
|
|
|
|
|
|
$chord, "\" in ", |
2148
|
0
|
0
|
|
|
|
0
|
$i->{_via}, "\n" ) if $debug > 1; |
2149
|
0
|
|
|
|
|
0
|
$unk = 0; |
2150
|
|
|
|
|
|
|
} |
2151
|
|
|
|
|
|
|
} |
2152
|
|
|
|
|
|
|
|
2153
|
1112
|
50
|
33
|
|
|
2627
|
unless ( $info || $def ) { |
2154
|
0
|
0
|
0
|
|
|
0
|
if ( $config->{debug}->{chords} || ! $warned_chords{$chord}++ ) { |
2155
|
0
|
0
|
|
|
|
0
|
warn("Parsing chord: \"$chord\" unknown\n") if $debug; |
2156
|
0
|
0
|
|
|
|
0
|
do_warn( "Unknown chord: \"$chord\"\n" ) |
2157
|
|
|
|
|
|
|
unless $chord =~ /^n\.?c\.?$/i; |
2158
|
|
|
|
|
|
|
} |
2159
|
|
|
|
|
|
|
} |
2160
|
|
|
|
|
|
|
|
2161
|
1112
|
50
|
|
|
|
2241
|
if ( $info ) { |
2162
|
1112
|
0
|
|
|
|
2257
|
warn( "Parsing chord: \"$chord\" okay: \"", |
|
|
50
|
|
|
|
|
|
2163
|
|
|
|
|
|
|
$info->name, "\" \"", |
2164
|
|
|
|
|
|
|
$info->chord_display, "\"", |
2165
|
|
|
|
|
|
|
$unk ? " but unknown" : "", |
2166
|
|
|
|
|
|
|
"\n" ) if $debug > 1; |
2167
|
1112
|
|
|
|
|
3290
|
$self->store_chord($info); |
2168
|
1112
|
|
|
|
|
2894
|
return $info; |
2169
|
|
|
|
|
|
|
} |
2170
|
|
|
|
|
|
|
|
2171
|
0
|
0
|
|
|
|
0
|
warn( "Parsing chord: \"$chord\" not found\n" ) if $debug; |
2172
|
0
|
|
|
|
|
0
|
return; |
2173
|
|
|
|
|
|
|
} |
2174
|
|
|
|
|
|
|
|
2175
|
|
|
|
|
|
|
sub store_chord { |
2176
|
1132
|
|
|
1132
|
0
|
2222
|
my ( $self, $info ) = @_; |
2177
|
1132
|
|
|
|
|
3113
|
$self->{chordsinfo}->{$info->name} = $info; |
2178
|
1132
|
|
|
|
|
3051
|
$info->name; |
2179
|
|
|
|
|
|
|
} |
2180
|
|
|
|
|
|
|
|
2181
|
|
|
|
|
|
|
sub structurize { |
2182
|
13
|
|
|
13
|
0
|
48
|
my ( $self ) = @_; |
2183
|
|
|
|
|
|
|
|
2184
|
13
|
50
|
|
|
|
59
|
return if $self->{structure} eq "structured"; |
2185
|
|
|
|
|
|
|
|
2186
|
13
|
|
|
|
|
26
|
my @body; |
2187
|
13
|
|
|
|
|
31
|
my $context = $def_context; |
2188
|
|
|
|
|
|
|
|
2189
|
13
|
|
|
|
|
27
|
foreach my $item ( @{ $self->{body} } ) { |
|
13
|
|
|
|
|
40
|
|
2190
|
251
|
100
|
66
|
|
|
590
|
if ( $item->{type} eq "empty" && $item->{context} eq $def_context ) { |
2191
|
56
|
|
|
|
|
83
|
$context = $def_context; |
2192
|
56
|
|
|
|
|
86
|
next; |
2193
|
|
|
|
|
|
|
} |
2194
|
195
|
100
|
100
|
|
|
489
|
if ( $item->{type} eq "songline" && $item->{context} eq '' ){ # A songline should have a context - non means verse |
2195
|
36
|
|
|
|
|
60
|
$item->{context} = 'verse'; |
2196
|
|
|
|
|
|
|
} |
2197
|
195
|
100
|
|
|
|
359
|
if ( $context ne $item->{context} ) { |
2198
|
49
|
|
|
|
|
166
|
push( @body, { type => $context = $item->{context}, body => [] } ); |
2199
|
|
|
|
|
|
|
} |
2200
|
195
|
100
|
|
|
|
296
|
if ( $context ) { |
2201
|
135
|
|
|
|
|
190
|
push( @{ $body[-1]->{body} }, $item ); |
|
135
|
|
|
|
|
260
|
|
2202
|
|
|
|
|
|
|
} |
2203
|
|
|
|
|
|
|
else { |
2204
|
60
|
|
|
|
|
135
|
push( @body, $item ); |
2205
|
|
|
|
|
|
|
} |
2206
|
|
|
|
|
|
|
} |
2207
|
13
|
|
|
|
|
136
|
$self->{body} = [ @body ]; |
2208
|
13
|
|
|
|
|
66
|
$self->{structure} = "structured"; |
2209
|
|
|
|
|
|
|
} |
2210
|
|
|
|
|
|
|
|
2211
|
|
|
|
|
|
|
sub dump { |
2212
|
0
|
|
|
0
|
0
|
|
my ( $self, $full ) = @_; |
2213
|
0
|
|
|
|
|
|
my $a = dclone($self); |
2214
|
0
|
|
|
|
|
|
$a->{config} = ref(delete($a->{config})); |
2215
|
0
|
0
|
|
|
|
|
unless ( $full ) { |
2216
|
0
|
|
|
|
|
|
for my $ci ( keys %{$a->{chordsinfo}} ) { |
|
0
|
|
|
|
|
|
|
2217
|
0
|
|
|
|
|
|
$a->{chordsinfo}{$ci} = $a->{chordsinfo}{$ci}->simplify; |
2218
|
|
|
|
|
|
|
} |
2219
|
|
|
|
|
|
|
} |
2220
|
|
|
|
|
|
|
# require Data::Dump::Filtered; |
2221
|
|
|
|
|
|
|
# warn Data::Dump::Filtered::dump_filtered($a, sub { |
2222
|
|
|
|
|
|
|
# my ( $ctx, $o ) = @_; |
2223
|
|
|
|
|
|
|
# my $h = { hide_keys => [ 'parser' ] }; |
2224
|
|
|
|
|
|
|
# $h->{bless} = "" |
2225
|
|
|
|
|
|
|
# if $ctx->class; |
2226
|
|
|
|
|
|
|
# $h; |
2227
|
|
|
|
|
|
|
# }); |
2228
|
0
|
|
|
|
|
|
::dump($a); |
2229
|
|
|
|
|
|
|
} |
2230
|
|
|
|
|
|
|
|
2231
|
|
|
|
|
|
|
unless ( caller ) { |
2232
|
|
|
|
|
|
|
require DDumper; |
2233
|
|
|
|
|
|
|
binmode STDERR => ':utf8'; |
2234
|
|
|
|
|
|
|
ChordPro::Config::configurator(); |
2235
|
|
|
|
|
|
|
my $s = ChordPro::Song->new; |
2236
|
|
|
|
|
|
|
$options->{settings}->{transpose} = 0; |
2237
|
|
|
|
|
|
|
for ( @ARGV ) { |
2238
|
|
|
|
|
|
|
if ( /^[a-z]/ ) { |
2239
|
|
|
|
|
|
|
$options->{settings}->{transcode} = $_; |
2240
|
|
|
|
|
|
|
next; |
2241
|
|
|
|
|
|
|
} |
2242
|
|
|
|
|
|
|
# DDumper::DDumper( $s->parse_chord($_) ); |
2243
|
|
|
|
|
|
|
my ( undef, $i ) = $s->parse_chord($_); |
2244
|
|
|
|
|
|
|
warn("$_ => ", $i->name, " => ", $s->add_chord($i, $i->name eq 'D'), "\n" ); |
2245
|
|
|
|
|
|
|
$xpose++; |
2246
|
|
|
|
|
|
|
} |
2247
|
|
|
|
|
|
|
DDumper::DDumper($s->{chordsinfo}); |
2248
|
|
|
|
|
|
|
} |
2249
|
|
|
|
|
|
|
|
2250
|
|
|
|
|
|
|
1; |