| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
|
2
|
|
|
|
|
|
|
|
|
3
|
90
|
|
|
90
|
|
696
|
use utf8; |
|
|
90
|
|
|
|
|
232
|
|
|
|
90
|
|
|
|
|
853
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package main; |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $options; |
|
8
|
|
|
|
|
|
|
our $config; |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
package ChordPro::Song; |
|
11
|
|
|
|
|
|
|
|
|
12
|
90
|
|
|
90
|
|
8804
|
use strict; |
|
|
90
|
|
|
|
|
225
|
|
|
|
90
|
|
|
|
|
2851
|
|
|
13
|
90
|
|
|
90
|
|
549
|
use warnings; |
|
|
90
|
|
|
|
|
187
|
|
|
|
90
|
|
|
|
|
4789
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
90
|
|
|
90
|
|
543
|
use ChordPro; |
|
|
90
|
|
|
|
|
207
|
|
|
|
90
|
|
|
|
|
2411
|
|
|
16
|
90
|
|
|
90
|
|
465
|
use ChordPro::Files; |
|
|
90
|
|
|
|
|
183
|
|
|
|
90
|
|
|
|
|
15346
|
|
|
17
|
90
|
|
|
90
|
|
649
|
use ChordPro::Paths; |
|
|
90
|
|
|
|
|
195
|
|
|
|
90
|
|
|
|
|
4902
|
|
|
18
|
90
|
|
|
90
|
|
582
|
use ChordPro::Chords; |
|
|
90
|
|
|
|
|
200
|
|
|
|
90
|
|
|
|
|
2539
|
|
|
19
|
90
|
|
|
90
|
|
44297
|
use ChordPro::Chords::Appearance; |
|
|
90
|
|
|
|
|
293
|
|
|
|
90
|
|
|
|
|
5212
|
|
|
20
|
90
|
|
|
90
|
|
735
|
use ChordPro::Chords::Parser; |
|
|
90
|
|
|
|
|
204
|
|
|
|
90
|
|
|
|
|
3065
|
|
|
21
|
90
|
|
|
90
|
|
528
|
use ChordPro::Output::Common; |
|
|
90
|
|
|
|
|
182
|
|
|
|
90
|
|
|
|
|
5968
|
|
|
22
|
90
|
|
|
90
|
|
588
|
use ChordPro::Utils; |
|
|
90
|
|
|
|
|
178
|
|
|
|
90
|
|
|
|
|
14780
|
|
|
23
|
90
|
|
|
90
|
|
647
|
use ChordPro::Symbols qw( is_strum ); |
|
|
90
|
|
|
|
|
175
|
|
|
|
90
|
|
|
|
|
942
|
|
|
24
|
|
|
|
|
|
|
|
|
25
|
90
|
|
|
90
|
|
536
|
use Carp; |
|
|
90
|
|
|
|
|
176
|
|
|
|
90
|
|
|
|
|
5635
|
|
|
26
|
90
|
|
|
90
|
|
594
|
use List::Util qw(any); |
|
|
90
|
|
|
|
|
191
|
|
|
|
90
|
|
|
|
|
6207
|
|
|
27
|
90
|
|
|
90
|
|
553
|
use Storable qw(dclone); |
|
|
90
|
|
|
|
|
195
|
|
|
|
90
|
|
|
|
|
4603
|
|
|
28
|
90
|
|
|
90
|
|
533
|
use feature 'state'; |
|
|
90
|
|
|
|
|
181
|
|
|
|
90
|
|
|
|
|
10284
|
|
|
29
|
90
|
|
|
90
|
|
580
|
use Text::ParseWords qw(quotewords); |
|
|
90
|
|
|
|
|
188
|
|
|
|
90
|
|
|
|
|
6246
|
|
|
30
|
90
|
|
|
90
|
|
599
|
use Ref::Util qw( is_arrayref ); |
|
|
90
|
|
|
|
|
192
|
|
|
|
90
|
|
|
|
|
2910553
|
|
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# Parser context. |
|
33
|
|
|
|
|
|
|
my $def_context = ""; |
|
34
|
|
|
|
|
|
|
my $in_context = $def_context; |
|
35
|
|
|
|
|
|
|
my $skip_context = 0; |
|
36
|
|
|
|
|
|
|
my $grid_arg; # also used for grilles? |
|
37
|
|
|
|
|
|
|
my $grid_cells; # also used for grilles? |
|
38
|
|
|
|
|
|
|
my $grid_type = 0; # 0 = chords, 1,2 = strums |
|
39
|
|
|
|
|
|
|
my @grille; |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# Local transposition. |
|
42
|
|
|
|
|
|
|
my $xpose = 0; |
|
43
|
|
|
|
|
|
|
my $xpose_dir; |
|
44
|
|
|
|
|
|
|
my $capo; |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# Used chords, in order of appearance. |
|
47
|
|
|
|
|
|
|
my @used_chords; |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# Chorus lines, if any. |
|
50
|
|
|
|
|
|
|
my @chorus; |
|
51
|
|
|
|
|
|
|
my $chorus_xpose = 0; |
|
52
|
|
|
|
|
|
|
my $chorus_xpose_dir = 0; |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# Memorized chords. |
|
55
|
|
|
|
|
|
|
my $cctag; # current cc name |
|
56
|
|
|
|
|
|
|
my %memchords; # all sections |
|
57
|
|
|
|
|
|
|
my $memchords; # current section |
|
58
|
|
|
|
|
|
|
my $memcrdinx; # chords tally |
|
59
|
|
|
|
|
|
|
my $memorizing; # if memorizing (a.o.t. recalling) |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# Keep track of unknown chords, to avoid dup warnings. |
|
62
|
|
|
|
|
|
|
my %warned_chords; |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
our $re_chords; # for chords |
|
65
|
|
|
|
|
|
|
my $propitems_re = propitems_re(); |
|
66
|
|
|
|
|
|
|
my $intervals; # number of note intervals |
|
67
|
|
|
|
|
|
|
my @labels; # labels used |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# Normally, transposition and subtitutions are handled by the parser. |
|
70
|
|
|
|
|
|
|
my $decapo; |
|
71
|
|
|
|
|
|
|
my $no_transpose; # NYI |
|
72
|
|
|
|
|
|
|
my $xcmov; # transcode to movable system |
|
73
|
|
|
|
|
|
|
my $no_substitute; |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# Stack for properties like textsize. |
|
76
|
|
|
|
|
|
|
my %propstack; |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
my $diag; # for diagnostics |
|
79
|
|
|
|
|
|
|
my @diag; # keep track of includes |
|
80
|
|
|
|
|
|
|
my $lineinfo; # keep lineinfo |
|
81
|
|
|
|
|
|
|
my $assetid = "001"; # for assets |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# Constructor. |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub new { |
|
86
|
216
|
|
|
216
|
0
|
225076
|
my ( $pkg, $opts ) = @_; |
|
87
|
|
|
|
|
|
|
|
|
88
|
216
|
|
66
|
|
|
1572
|
my $filesource = $opts->{filesource} || $opts->{_filesource}; |
|
89
|
|
|
|
|
|
|
|
|
90
|
216
|
|
|
|
|
547
|
$xpose = 0; |
|
91
|
216
|
|
|
|
|
999
|
$grid_arg = [ 4, 4, 1, 1, "" ]; # 1+4x4+1 |
|
92
|
216
|
|
|
|
|
627
|
$in_context = $def_context; |
|
93
|
216
|
|
|
|
|
689
|
@used_chords = (); |
|
94
|
216
|
|
|
|
|
617
|
%warned_chords = (); |
|
95
|
216
|
|
|
|
|
655
|
%memchords = (); |
|
96
|
216
|
|
|
|
|
549
|
%propstack = (); |
|
97
|
216
|
|
|
|
|
1483
|
ChordPro::Chords::reset_song_chords(); |
|
98
|
216
|
|
|
|
|
488
|
@labels = (); |
|
99
|
216
|
|
|
|
|
2145
|
@chorus = (); |
|
100
|
216
|
|
|
|
|
634
|
$capo = undef; |
|
101
|
216
|
|
|
|
|
461
|
$xcmov = undef; |
|
102
|
216
|
|
|
|
|
1001
|
upd_config(); |
|
103
|
|
|
|
|
|
|
|
|
104
|
216
|
|
33
|
|
|
2052
|
$diag->{format} = $opts->{diagformat} // $config->{diagnostics}->{format}; |
|
105
|
216
|
|
|
|
|
733
|
$diag->{file} = $filesource; |
|
106
|
216
|
|
|
|
|
622
|
$diag->{line} = 0; |
|
107
|
216
|
|
|
|
|
759
|
$diag->{orig} = "(at start of song)"; |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
bless { chordsinfo => {}, |
|
110
|
|
|
|
|
|
|
meta => {}, |
|
111
|
|
|
|
|
|
|
generate => $opts->{generate}, |
|
112
|
216
|
|
|
|
|
2387
|
structure => "linear", |
|
113
|
|
|
|
|
|
|
} => $pkg; |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub upd_config { |
|
117
|
441
|
|
|
441
|
0
|
1809
|
$decapo = $config->{settings}->{decapo}; |
|
118
|
441
|
|
|
|
|
1265
|
$lineinfo = $config->{settings}->{lineinfo}; |
|
119
|
441
|
|
|
|
|
906
|
$intervals = @{ $config->{notes}->{sharp} }; |
|
|
441
|
|
|
|
|
1725
|
|
|
120
|
|
|
|
|
|
|
} |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
214
|
|
|
sub ::break() {} |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
sub is_gridstrum($) { |
|
125
|
235
|
50
|
|
235
|
0
|
1388
|
$_[0] == 1 || $_[0] == 2; |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub parse_song { |
|
129
|
214
|
|
|
214
|
0
|
934
|
my ( $self, $lines, $linecnt, $meta, $defs ) = @_; |
|
130
|
214
|
50
|
|
|
|
1090
|
die("OOPS! Wrong meta") unless ref($meta) eq 'HASH'; |
|
131
|
214
|
|
|
|
|
373839
|
local $config = dclone($config); |
|
132
|
|
|
|
|
|
|
|
|
133
|
214
|
50
|
|
|
|
2031
|
warn("Processing song ", $diag->{file}, "...\n") if $options->{verbose}; |
|
134
|
214
|
|
|
|
|
1059
|
::break(); |
|
135
|
214
|
|
|
|
|
531
|
my @configs; |
|
136
|
|
|
|
|
|
|
# |
|
137
|
214
|
50
|
|
|
|
1392
|
if ( $lines->[0] =~ /^##config:\s*json/ ) { |
|
138
|
0
|
|
|
|
|
0
|
my $cf = ""; |
|
139
|
0
|
|
|
|
|
0
|
shift(@$lines); |
|
140
|
0
|
|
|
|
|
0
|
$$linecnt++; |
|
141
|
0
|
|
|
|
|
0
|
while ( @$lines ) { |
|
142
|
0
|
0
|
|
|
|
0
|
if ( $lines->[0] =~ /^# (.*)/ ) { |
|
143
|
0
|
|
|
|
|
0
|
$cf .= $1 . "\n"; |
|
144
|
0
|
|
|
|
|
0
|
shift(@$lines); |
|
145
|
0
|
|
|
|
|
0
|
$$linecnt++; |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
else { |
|
148
|
0
|
|
|
|
|
0
|
last; |
|
149
|
|
|
|
|
|
|
} |
|
150
|
|
|
|
|
|
|
} |
|
151
|
0
|
0
|
|
|
|
0
|
if ( $cf ) { |
|
152
|
0
|
|
|
|
|
0
|
my $prename = "__PRECFG__"; |
|
153
|
0
|
|
|
|
|
0
|
my $precfg = ChordPro::Config->new( json_load( $cf, $prename ) ); |
|
154
|
0
|
|
|
|
|
0
|
$precfg->precheck($prename); |
|
155
|
0
|
|
|
|
|
0
|
push( @configs, $precfg->prep_configs($prename) ); |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
# Load song-specific config, if any. |
|
159
|
214
|
50
|
66
|
|
|
1548
|
if ( !$options->{nosongconfig} && $diag->{file} ) { |
|
160
|
107
|
50
|
|
|
|
488
|
if ( $options->{verbose} ) { |
|
161
|
0
|
|
|
|
|
0
|
my $this = ChordPro::Chords::get_parser(); |
|
162
|
0
|
0
|
|
|
|
0
|
$this = defined($this) ? $this->{system} : ""; |
|
163
|
0
|
|
|
|
|
0
|
print STDERR ("Parsers at start of ", $diag->{file}, ":"); |
|
164
|
|
|
|
|
|
|
print STDERR ( $this eq $_ ? " *" : " ", "$_") |
|
165
|
0
|
0
|
|
|
|
0
|
for keys %{ ChordPro::Chords::Parser->parsers }; |
|
|
0
|
|
|
|
|
0
|
|
|
166
|
0
|
|
|
|
|
0
|
print STDERR ("\n"); |
|
167
|
|
|
|
|
|
|
} |
|
168
|
107
|
50
|
33
|
|
|
843
|
if ( $meta && $meta->{__config} ) { |
|
169
|
0
|
|
|
|
|
0
|
my $cf = delete($meta->{__config})->[0]; |
|
170
|
0
|
0
|
|
|
|
0
|
warn("Config[song]: $cf\n") if $options->{verbose}; |
|
171
|
0
|
|
|
|
|
0
|
my $have = ChordPro::Config::get_config( CP->findcfg($cf) ); |
|
172
|
0
|
0
|
|
|
|
0
|
die("Missing config: $cf\n") unless $have; |
|
173
|
0
|
|
|
|
|
0
|
push( @configs, $have->prep_configs($cf) ); |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
else { |
|
176
|
107
|
|
|
|
|
407
|
for ( "prp", "json" ) { |
|
177
|
214
|
|
|
|
|
1280
|
( my $cf = $diag->{file} ) =~ s/\.\w+$/.$_/; |
|
178
|
214
|
100
|
|
|
|
860
|
$cf .= ".$_" if $cf eq $diag->{file}; |
|
179
|
214
|
50
|
|
|
|
1679
|
next unless fs_test( s => $cf ); |
|
180
|
0
|
0
|
|
|
|
0
|
warn("Config[song]: $cf\n") if $options->{verbose}; |
|
181
|
0
|
|
|
|
|
0
|
my $have = ChordPro::Config::get_config($cf); |
|
182
|
0
|
|
|
|
|
0
|
push( @configs, $have->prep_configs($cf) ); |
|
183
|
0
|
|
|
|
|
0
|
last; |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
} |
|
187
|
214
|
|
|
|
|
1183
|
my $tuncheck = join("|",@{$config->{tuning}}); |
|
|
214
|
|
|
|
|
1204
|
|
|
188
|
214
|
|
|
|
|
812
|
foreach my $have ( @configs ) { |
|
189
|
0
|
0
|
|
|
|
0
|
warn("Config[song*]: ", $have->{_src}, "\n") if $options->{verbose}; |
|
190
|
0
|
|
|
|
|
0
|
my $chords = $have->{chords}; |
|
191
|
0
|
|
|
|
|
0
|
$config->augment($have); |
|
192
|
0
|
0
|
|
|
|
0
|
if ( $tuncheck ne join("|",@{$config->{tuning}}) ) { |
|
|
0
|
|
|
|
|
0
|
|
|
193
|
0
|
|
|
|
|
0
|
my $res = |
|
194
|
|
|
|
|
|
|
ChordPro::Chords::set_tuning($config); |
|
195
|
0
|
0
|
|
|
|
0
|
warn( "Invalid tuning in config: ", $res, "\n" ) if $res; |
|
196
|
|
|
|
|
|
|
} |
|
197
|
0
|
|
|
|
|
0
|
ChordPro::Chords::reset_parser(); |
|
198
|
0
|
|
|
|
|
0
|
ChordPro::Chords::Parser->reset_parsers; |
|
199
|
0
|
0
|
|
|
|
0
|
if ( $chords ) { |
|
200
|
0
|
|
|
|
|
0
|
my $c = $chords; |
|
201
|
0
|
0
|
0
|
|
|
0
|
if ( @$c && $c->[0] eq "append" ) { |
|
202
|
0
|
|
|
|
|
0
|
shift(@$c); |
|
203
|
|
|
|
|
|
|
} |
|
204
|
0
|
|
|
|
|
0
|
foreach ( @$c ) { |
|
205
|
0
|
|
|
|
|
0
|
my $res = |
|
206
|
|
|
|
|
|
|
ChordPro::Chords::add_config_chord($_); |
|
207
|
|
|
|
|
|
|
warn( "Invalid chord in config: ", |
|
208
|
0
|
0
|
|
|
|
0
|
$_->{name}, ": ", $res, "\n" ) if $res; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
} |
|
211
|
0
|
0
|
|
|
|
0
|
if ( $options->{verbose} > 1 ) { |
|
212
|
0
|
0
|
|
|
|
0
|
warn( "Processed ", scalar(@$chords), " chord entries\n") |
|
213
|
|
|
|
|
|
|
if $chords; |
|
214
|
0
|
|
|
|
|
0
|
warn( "Totals: ", |
|
215
|
|
|
|
|
|
|
ChordPro::Chords::chord_stats(), "\n" ); |
|
216
|
|
|
|
|
|
|
} |
|
217
|
0
|
|
|
|
|
0
|
if ( 0 && $options->{verbose} ) { |
|
218
|
|
|
|
|
|
|
my $this = ChordPro::Chords::get_parser()->{system}; |
|
219
|
|
|
|
|
|
|
print STDERR ("Parsers after local config:"); |
|
220
|
|
|
|
|
|
|
print STDERR ( $this eq $_ ? " *" : " ", "$_") |
|
221
|
|
|
|
|
|
|
for keys %{ ChordPro::Chords::Parser->parsers }; |
|
222
|
|
|
|
|
|
|
print STDERR ("\n"); |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
|
|
226
|
214
|
|
|
|
|
1740
|
$config->unlock; |
|
227
|
214
|
50
|
|
|
|
529431
|
if ( my $a = $config->{parser}->{altbrackets} ) { |
|
228
|
0
|
0
|
|
|
|
0
|
die("Config error: parser.altbrackets must be a 2-character string\n") |
|
229
|
|
|
|
|
|
|
unless length($a) == 2; |
|
230
|
|
|
|
|
|
|
} |
|
231
|
|
|
|
|
|
|
|
|
232
|
214
|
50
|
|
|
|
959
|
if ( %$defs ) { |
|
233
|
0
|
|
|
|
|
0
|
prpadd2cfg( $config, %$defs ); |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
|
|
236
|
214
|
|
|
|
|
790
|
for ( qw( transpose transcode decapo lyrics-only ) ) { |
|
237
|
856
|
100
|
|
|
|
2884
|
next unless defined $options->{$_}; |
|
238
|
18
|
|
|
|
|
148
|
$config->{settings}->{$_} = $options->{$_}; |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
# Catch common error. |
|
241
|
214
|
50
|
|
|
|
1433
|
unless ( UNIVERSAL::isa( $config->{instrument}, 'HASH' ) ) { |
|
242
|
0
|
|
0
|
|
|
0
|
$config->{instrument} //= "guitar"; |
|
243
|
|
|
|
|
|
|
$config->{instrument} = |
|
244
|
|
|
|
|
|
|
{ type => $config->{instrument}, |
|
245
|
0
|
|
|
|
|
0
|
description => ucfirst $config->{instrument} }; |
|
246
|
|
|
|
|
|
|
do_warn( "Missing or invalid instrument - set to ", |
|
247
|
0
|
|
|
|
|
0
|
$config->{instrument}->{type}, "\n" ); |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# Remove inactive delegates. |
|
251
|
214
|
|
|
|
|
550
|
while ( my ($k,$v) = each %{ $config->{delegates} } ) { |
|
|
1070
|
|
|
|
|
4039
|
|
|
252
|
|
|
|
|
|
|
delete( $config->{delegates}->{$k} ) |
|
253
|
856
|
50
|
33
|
|
|
4261
|
if !$v || $v->{type} eq 'none'; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# And lock the config. |
|
257
|
214
|
|
|
|
|
1689
|
$config->lock; |
|
258
|
|
|
|
|
|
|
|
|
259
|
214
|
|
|
|
|
579148
|
for ( keys %{ $config->{meta} } ) { |
|
|
214
|
|
|
|
|
1360
|
|
|
260
|
214
|
|
50
|
|
|
1110
|
$meta->{$_} //= []; |
|
261
|
214
|
|
|
|
|
972
|
my $v = $config->{meta}->{$_}; |
|
262
|
214
|
50
|
|
|
|
1360
|
$v = [ $v ] unless is_arrayref($v); |
|
263
|
214
|
50
|
|
|
|
1038
|
if ( is_arrayref($meta->{$_}) ) { |
|
264
|
0
|
|
|
|
|
0
|
push( @{ $meta->{$_} }, @$v ); |
|
|
0
|
|
|
|
|
0
|
|
|
265
|
|
|
|
|
|
|
} |
|
266
|
|
|
|
|
|
|
else { |
|
267
|
214
|
|
|
|
|
811
|
$meta->{$_} = $v; |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
214
|
|
|
|
|
845
|
$no_transpose = $options->{'no-transpose'}; |
|
272
|
214
|
|
|
|
|
661
|
$no_substitute = $options->{'no-substitute'}; |
|
273
|
214
|
|
|
|
|
613
|
my $fragment = $options->{fragment}; |
|
274
|
214
|
|
|
|
|
932
|
my $target = $config->{settings}->{transcode}; |
|
275
|
214
|
100
|
|
|
|
840
|
if ( $target ) { |
|
276
|
2
|
50
|
|
|
|
23
|
unless ( ChordPro::Chords::Parser->have_parser($target) ) { |
|
277
|
2
|
50
|
|
|
|
15
|
if ( my $file = CP->findres("config/notes/$target.json") ) { |
|
278
|
2
|
|
|
|
|
15
|
for ( ChordPro::Config::get_config($file) ) { |
|
279
|
2
|
|
|
|
|
20
|
my $new = $config->hmerge($_); |
|
280
|
2
|
|
|
|
|
8
|
local $config = $new; |
|
281
|
2
|
|
|
|
|
35
|
ChordPro::Chords::Parser->new($new); |
|
282
|
|
|
|
|
|
|
} |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
} |
|
285
|
2
|
50
|
|
|
|
46
|
unless ( ChordPro::Chords::Parser->have_parser($target) ) { |
|
286
|
0
|
|
|
|
|
0
|
die("No transcoder for ", $target, "\n"); |
|
287
|
|
|
|
|
|
|
} |
|
288
|
2
|
50
|
|
|
|
10
|
warn("Got transcoder for $target\n") if $::options->{verbose}; |
|
289
|
2
|
|
|
|
|
17
|
ChordPro::Chords::set_parser($target); |
|
290
|
2
|
|
|
|
|
9
|
my $p = ChordPro::Chords::get_parser; |
|
291
|
2
|
|
|
|
|
11
|
$xcmov = $p->movable; |
|
292
|
2
|
50
|
|
|
|
10
|
if ( $target ne $p->{system} ) { |
|
293
|
0
|
|
|
|
|
0
|
::dump(ChordPro::Chords::Parser->parsers); |
|
294
|
|
|
|
|
|
|
warn("OOPS parser mixup, $target <> ", |
|
295
|
|
|
|
|
|
|
ChordPro::Chords::get_parser->{system}) |
|
296
|
0
|
|
|
|
|
0
|
} |
|
297
|
2
|
|
|
|
|
26
|
ChordPro::Chords::set_parser($self->{system}); |
|
298
|
|
|
|
|
|
|
} |
|
299
|
|
|
|
|
|
|
else { |
|
300
|
212
|
|
|
|
|
1091
|
$target = $self->{system}; |
|
301
|
|
|
|
|
|
|
} |
|
302
|
|
|
|
|
|
|
|
|
303
|
214
|
|
|
|
|
967
|
upd_config(); |
|
304
|
214
|
|
|
|
|
1796
|
$self->{source} = { file => $diag->{file}, line => 1 + $$linecnt }; |
|
305
|
214
|
|
|
|
|
1061
|
$self->{system} = $config->{notes}->{system}; |
|
306
|
214
|
|
|
|
|
799
|
$self->{config} = $config; |
|
307
|
214
|
50
|
|
|
|
1304
|
$self->{meta} = $meta if $meta; |
|
308
|
214
|
|
|
|
|
711
|
$self->{chordsinfo} = {}; |
|
309
|
214
|
|
66
|
|
|
1534
|
$target //= $self->{system}; |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# Preprocessor. |
|
312
|
214
|
|
|
|
|
1679
|
my $prep = make_preprocessor( $config->{parser}->{preprocess} ); |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
# Pre-fill meta data, if any. TODO? ALREADY DONE? |
|
315
|
214
|
50
|
|
|
|
1245
|
if ( $options->{meta} ) { |
|
316
|
0
|
|
|
|
|
0
|
while ( my ($k, $v ) = each( %{ $options->{meta} } ) ) { |
|
|
0
|
|
|
|
|
0
|
|
|
317
|
0
|
|
|
|
|
0
|
$self->{meta}->{$k} = [ $v ]; |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
$self->{meta}->{"chordpro.songsource"} = $diag->{file} |
|
321
|
214
|
100
|
|
|
|
824
|
unless $::running_under_test; |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
# Build regexp to split out chords. |
|
324
|
214
|
100
|
|
|
|
1987
|
if ( $config->{settings}->{memorize} ) { |
|
325
|
3
|
|
|
|
|
19
|
$re_chords = qr/(\[.*?\]|\^)/; |
|
326
|
|
|
|
|
|
|
} |
|
327
|
|
|
|
|
|
|
else { |
|
328
|
211
|
|
|
|
|
1836
|
$re_chords = qr/(\[.*?\])/; |
|
329
|
|
|
|
|
|
|
} |
|
330
|
|
|
|
|
|
|
|
|
331
|
214
|
|
|
|
|
654
|
my $skipcnt = 0; |
|
332
|
214
|
|
|
|
|
946
|
while ( @$lines ) { |
|
333
|
2201
|
50
|
|
|
|
5450
|
if ( $skipcnt ) { |
|
334
|
0
|
|
|
|
|
0
|
$skipcnt--; |
|
335
|
|
|
|
|
|
|
} |
|
336
|
|
|
|
|
|
|
else { |
|
337
|
2201
|
|
|
|
|
5788
|
$diag->{line} = ++$$linecnt; |
|
338
|
|
|
|
|
|
|
} |
|
339
|
|
|
|
|
|
|
|
|
340
|
2201
|
|
|
|
|
5597
|
$_ = shift(@$lines); |
|
341
|
2201
|
|
33
|
|
|
9089
|
while ( /\\\Z/ && @$lines ) { |
|
342
|
0
|
|
|
|
|
0
|
chop; |
|
343
|
0
|
|
|
|
|
0
|
my $cont = shift(@$lines); |
|
344
|
0
|
|
|
|
|
0
|
$$linecnt++; |
|
345
|
0
|
|
|
|
|
0
|
$cont =~ s/^\s+//; |
|
346
|
0
|
|
|
|
|
0
|
$_ .= $cont; |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
# Uncomment this to allow \uDXXX\uDYYY (surrogate) escapes. |
|
350
|
|
|
|
|
|
|
s/ \\u(d[89ab][[:xdigit:]]{2})\\u(d[cdef][[:xdigit:]]{2}) |
|
351
|
0
|
|
|
|
|
0
|
/ pack('U*', 0x10000 + (hex($1) - 0xD800) * 0x400 + (hex($2) - 0xDC00) ) |
|
352
|
2201
|
|
|
|
|
4800
|
/igex; |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# Uncomment this to allow \uXXXX escapes. |
|
355
|
2201
|
|
|
|
|
4356
|
s/\\u([0-9a-f]{4})/chr(hex("0x$1"))/ige; |
|
|
0
|
|
|
|
|
0
|
|
|
356
|
|
|
|
|
|
|
# Uncomment this to allow \u{XX...} escapes. |
|
357
|
2201
|
|
|
|
|
5059
|
s/\\u\{([0-9a-f]+)\}/chr(hex("0x$1"))/ige; |
|
|
0
|
|
|
|
|
0
|
|
|
358
|
|
|
|
|
|
|
|
|
359
|
2201
|
|
|
|
|
5012
|
$diag->{orig} = $_; |
|
360
|
|
|
|
|
|
|
# Get rid of TABs. |
|
361
|
2201
|
|
|
|
|
4193
|
s/\t/ /g; |
|
362
|
|
|
|
|
|
|
|
|
363
|
2201
|
50
|
|
|
|
7784
|
if ( $config->{debug}->{echo} ) { |
|
364
|
0
|
|
|
|
|
0
|
warn(sprintf("==[%3d]=> %s\n", $diag->{line}, $diag->{orig} ) ); |
|
365
|
|
|
|
|
|
|
} |
|
366
|
|
|
|
|
|
|
|
|
367
|
2201
|
|
|
|
|
5976
|
for my $pp ( "all", "env-$in_context" ) { |
|
368
|
|
|
|
|
|
|
next if $pp eq "env-$in_context" |
|
369
|
|
|
|
|
|
|
&& /^\s*\{(\w+)\}\s*$/ |
|
370
|
4402
|
100
|
100
|
|
|
24634
|
&& $self->parse_directive($1)->{name} eq "end_of_$in_context"; |
|
|
|
|
100
|
|
|
|
|
|
371
|
4305
|
50
|
|
|
|
13090
|
if ( $prep->{$pp} ) { |
|
372
|
0
|
0
|
|
|
|
0
|
$config->{debug}->{pp} && warn("PRE: ", $_, "\n"); |
|
373
|
0
|
|
|
|
|
0
|
$prep->{$pp}->($_); |
|
374
|
0
|
0
|
|
|
|
0
|
$config->{debug}->{pp} && warn("POST: ", $_, "\n"); |
|
375
|
0
|
0
|
|
|
|
0
|
if ( /\n/ ) { |
|
376
|
0
|
|
|
|
|
0
|
my @a = split( /\n/, $_ ); |
|
377
|
0
|
|
|
|
|
0
|
$_ = shift(@a); |
|
378
|
0
|
|
|
|
|
0
|
unshift( @$lines, @a ); |
|
379
|
0
|
|
|
|
|
0
|
$skipcnt += @a; |
|
380
|
|
|
|
|
|
|
} |
|
381
|
|
|
|
|
|
|
} |
|
382
|
|
|
|
|
|
|
} |
|
383
|
|
|
|
|
|
|
|
|
384
|
2201
|
100
|
|
|
|
5336
|
if ( $skip_context ) { |
|
385
|
4
|
100
|
|
|
|
45
|
if ( /^\s*\{(\w+)\}\s*$/ ) { |
|
386
|
2
|
|
|
|
|
9
|
my $dir = $self->parse_directive($1); |
|
387
|
2
|
50
|
|
|
|
12
|
if ( $dir->{name} eq "end_of_$in_context" ) { |
|
388
|
2
|
|
|
|
|
5
|
$in_context = $def_context; |
|
389
|
2
|
|
|
|
|
8
|
$skip_context = 0; |
|
390
|
|
|
|
|
|
|
} |
|
391
|
|
|
|
|
|
|
} |
|
392
|
4
|
|
|
|
|
12
|
next; |
|
393
|
|
|
|
|
|
|
} |
|
394
|
|
|
|
|
|
|
|
|
395
|
2197
|
100
|
|
|
|
6759
|
if ( /^\s*\{((?:new_song|ns)\b.*)\}\s*$/ ) { |
|
396
|
63
|
100
|
|
|
|
262
|
if ( $self->{body} ) { |
|
397
|
29
|
|
|
|
|
97
|
unshift( @$lines, $_ ); |
|
398
|
29
|
|
|
|
|
65
|
$$linecnt--; |
|
399
|
29
|
|
|
|
|
85
|
last; |
|
400
|
|
|
|
|
|
|
} |
|
401
|
34
|
|
|
|
|
152
|
my $dir = $self->parse_directive($1); |
|
402
|
34
|
50
|
50
|
|
|
289
|
next unless my $kv = parse_kv($dir->{arg}//""); |
|
403
|
34
|
50
|
|
|
|
145
|
if ( defined $kv->{toc} ) { |
|
404
|
0
|
|
|
|
|
0
|
$self->{meta}->{_TOC} = [ $kv->{toc} ]; |
|
405
|
|
|
|
|
|
|
} |
|
406
|
34
|
50
|
|
|
|
143
|
if ( $kv->{forceifempty} ) { |
|
407
|
0
|
|
|
|
|
0
|
push( @{ $self->{body} }, |
|
408
|
|
|
|
|
|
|
{ type => "set", |
|
409
|
|
|
|
|
|
|
name => "forceifempty", |
|
410
|
0
|
|
|
|
|
0
|
value => $kv->{forceifempty} } ); |
|
411
|
|
|
|
|
|
|
} |
|
412
|
34
|
|
|
|
|
196
|
next; |
|
413
|
|
|
|
|
|
|
} |
|
414
|
|
|
|
|
|
|
|
|
415
|
2134
|
100
|
|
|
|
5587
|
if ( /^#/ ) { |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
# Handle assets. |
|
418
|
57
|
|
|
|
|
160
|
my $kw = ""; |
|
419
|
57
|
|
|
|
|
124
|
my $kv = {}; |
|
420
|
57
|
100
|
66
|
|
|
287
|
if ( /^##(image|asset|include)(?:-(.+))?:\s+(.*)/i |
|
421
|
|
|
|
|
|
|
&& $self->selected($2) ) { |
|
422
|
3
|
|
|
|
|
13
|
$kw = lc($1); |
|
423
|
3
|
|
|
|
|
20
|
$kv = parse_kv($3); |
|
424
|
|
|
|
|
|
|
} |
|
425
|
|
|
|
|
|
|
|
|
426
|
57
|
100
|
|
|
|
184
|
if ( $kw eq "image" ) { |
|
427
|
2
|
|
|
|
|
8
|
my $id = $kv->{id}; |
|
428
|
2
|
50
|
|
|
|
8
|
unless ( $id ) { |
|
429
|
0
|
|
|
|
|
0
|
do_warn("Missing id for image asset\n"); |
|
430
|
0
|
|
|
|
|
0
|
next; |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# In-line image asset. |
|
434
|
2
|
|
|
|
|
20
|
require MIME::Base64; |
|
435
|
2
|
|
|
|
|
1646
|
require Image::Info; |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
# Read the image. |
|
438
|
2
|
|
|
|
|
7080
|
my $data = ''; |
|
439
|
2
|
|
100
|
|
|
28
|
while ( @$lines && $lines->[0] =~ /^# (.+)/ ) { |
|
440
|
9
|
|
|
|
|
45
|
$data .= MIME::Base64::decode($1); |
|
441
|
9
|
|
|
|
|
69
|
shift(@$lines); |
|
442
|
|
|
|
|
|
|
} |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# Get info. |
|
445
|
2
|
|
|
|
|
10
|
my $info = Image::Info::image_info(\$data); |
|
446
|
2
|
50
|
|
|
|
14179
|
if ( $info->{error} ) { |
|
447
|
0
|
|
|
|
|
0
|
do_warn($info->{error}); |
|
448
|
0
|
|
|
|
|
0
|
next; |
|
449
|
|
|
|
|
|
|
} |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
# Store in assets. |
|
452
|
2
|
|
100
|
|
|
15
|
$self->{assets} //= {}; |
|
453
|
|
|
|
|
|
|
$self->{assets}->{$id} = |
|
454
|
|
|
|
|
|
|
{ type => "image", |
|
455
|
|
|
|
|
|
|
data => $data, |
|
456
|
|
|
|
|
|
|
subtype => $info->{file_ext}, |
|
457
|
|
|
|
|
|
|
width => $info->{width}, |
|
458
|
|
|
|
|
|
|
height => $info->{height}, |
|
459
|
2
|
|
|
|
|
87
|
opts => $kv, |
|
460
|
|
|
|
|
|
|
}; |
|
461
|
|
|
|
|
|
|
|
|
462
|
2
|
50
|
|
|
|
17
|
if ( $config->{debug}->{images} ) { |
|
463
|
|
|
|
|
|
|
warn( "asset[$id] type=image/$info->{file_ext} ", |
|
464
|
|
|
|
|
|
|
length($data), " bytes, ", |
|
465
|
|
|
|
|
|
|
"width=$info->{width}, height=$info->{height}", |
|
466
|
0
|
0
|
|
|
|
0
|
$kv->{persist} ? ", persist" : "", |
|
467
|
|
|
|
|
|
|
"\n"); |
|
468
|
|
|
|
|
|
|
} |
|
469
|
2
|
|
|
|
|
31
|
next; |
|
470
|
|
|
|
|
|
|
} |
|
471
|
|
|
|
|
|
|
|
|
472
|
55
|
100
|
|
|
|
150
|
if ( $kw eq "asset" ) { |
|
473
|
1
|
|
|
|
|
3
|
my $id = $kv->{id}; |
|
474
|
1
|
|
|
|
|
5
|
my $type = $kv->{type}; |
|
475
|
1
|
50
|
|
|
|
7
|
unless ( $id ) { |
|
476
|
0
|
|
|
|
|
0
|
do_warn("Missing id for asset\n"); |
|
477
|
0
|
|
|
|
|
0
|
next; |
|
478
|
|
|
|
|
|
|
} |
|
479
|
1
|
50
|
|
|
|
3
|
unless ( $type ) { |
|
480
|
0
|
|
|
|
|
0
|
do_warn("Missing type for asset\n"); |
|
481
|
0
|
|
|
|
|
0
|
next; |
|
482
|
|
|
|
|
|
|
} |
|
483
|
1
|
50
|
|
|
|
8
|
unless ( exists $config->{delegates}->{$type} ) { |
|
484
|
0
|
|
|
|
|
0
|
do_warn("Unhandled type for asset: $type\n"); |
|
485
|
0
|
|
|
|
|
0
|
next; |
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# Read the data. |
|
489
|
1
|
|
|
|
|
3
|
my @data; |
|
490
|
1
|
|
66
|
|
|
12
|
while ( @$lines && $lines->[0] =~ /^# (.+)/ ) { |
|
491
|
3
|
|
|
|
|
54
|
push( @data, $1 ); |
|
492
|
3
|
|
|
|
|
18
|
shift(@$lines); |
|
493
|
|
|
|
|
|
|
} |
|
494
|
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
# Store in assets. |
|
496
|
1
|
|
50
|
|
|
4
|
$self->{assets} //= {}; |
|
497
|
|
|
|
|
|
|
$self->{assets}->{$id} = |
|
498
|
|
|
|
|
|
|
{ data => \@data, |
|
499
|
|
|
|
|
|
|
type => "image", |
|
500
|
|
|
|
|
|
|
subtype => $type, |
|
501
|
|
|
|
|
|
|
module => $config->{delegates}->{$type}->{module}, |
|
502
|
|
|
|
|
|
|
handler => $config->{delegates}->{$type}->{handler}, |
|
503
|
1
|
|
|
|
|
15
|
opts => $kv, |
|
504
|
|
|
|
|
|
|
}; |
|
505
|
1
|
50
|
|
|
|
5
|
if ( $config->{debug}->{images} ) { |
|
506
|
|
|
|
|
|
|
warn("asset[$id] type=image/$type ", |
|
507
|
|
|
|
|
|
|
scalar(@data), " lines", |
|
508
|
0
|
0
|
|
|
|
0
|
$kv->{persist} ? ", persist" : "", |
|
509
|
|
|
|
|
|
|
"\n"); |
|
510
|
|
|
|
|
|
|
} |
|
511
|
1
|
|
|
|
|
5
|
next; |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
|
|
514
|
54
|
50
|
|
|
|
131
|
if ( $kw eq "include" ) { |
|
515
|
0
|
0
|
|
|
|
0
|
if ( $kv->{end} ) { |
|
516
|
0
|
|
|
|
|
0
|
$diag = pop( @diag ); |
|
517
|
0
|
|
|
|
|
0
|
$$linecnt = $diag->{line}; |
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
else { |
|
520
|
0
|
|
|
|
|
0
|
my $uri = $kv->{src}; |
|
521
|
0
|
0
|
0
|
|
|
0
|
if ( $uri && CP->is_here($uri) ) { |
|
522
|
0
|
|
|
|
|
0
|
my $found = CP->siblingres( $diag->{file}, $uri, class => "include" ); |
|
523
|
0
|
0
|
|
|
|
0
|
if ( $found ) { |
|
524
|
0
|
|
|
|
|
0
|
$uri = $found; |
|
525
|
|
|
|
|
|
|
} |
|
526
|
|
|
|
|
|
|
else { |
|
527
|
0
|
|
|
|
|
0
|
do_warn("Missing include for \"$uri\""); |
|
528
|
0
|
|
|
|
|
0
|
$uri = undef; |
|
529
|
|
|
|
|
|
|
} |
|
530
|
|
|
|
|
|
|
} |
|
531
|
0
|
0
|
|
|
|
0
|
if ( $uri ) { |
|
532
|
0
|
|
|
|
|
0
|
unshift( @$lines, @{fs_load($uri)}, "##include: end=1" ); |
|
|
0
|
|
|
|
|
0
|
|
|
533
|
0
|
|
|
|
|
0
|
push( @diag, { %$diag } ); |
|
534
|
0
|
|
|
|
|
0
|
$diag->{file} = $uri; |
|
535
|
0
|
|
|
|
|
0
|
$diag->{line} = $$linecnt = 0; |
|
536
|
0
|
|
|
|
|
0
|
$diag->{orig} = "(including $uri)"; |
|
537
|
|
|
|
|
|
|
} |
|
538
|
|
|
|
|
|
|
} |
|
539
|
0
|
|
|
|
|
0
|
next; |
|
540
|
|
|
|
|
|
|
} |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
# Currently the ChordPro backend is the only one that |
|
543
|
|
|
|
|
|
|
# cares about comment lines. |
|
544
|
|
|
|
|
|
|
# Collect pre-title stuff separately. |
|
545
|
|
|
|
|
|
|
next unless exists $config->{lc $self->{generate}} |
|
546
|
|
|
|
|
|
|
&& exists $config->{lc $self->{generate}}->{comments} |
|
547
|
54
|
100
|
100
|
|
|
582
|
&& $config->{lc $self->{generate}}->{comments} eq "retain"; |
|
|
|
|
66
|
|
|
|
|
|
548
|
|
|
|
|
|
|
|
|
549
|
39
|
50
|
33
|
|
|
130
|
if ( exists $self->{title} || $fragment ) { |
|
550
|
39
|
|
|
|
|
147
|
$self->add( type => "ignore", text => $_ ); |
|
551
|
|
|
|
|
|
|
} |
|
552
|
|
|
|
|
|
|
else { |
|
553
|
0
|
|
|
|
|
0
|
push( @{ $self->{preamble} }, $_ ); |
|
|
0
|
|
|
|
|
0
|
|
|
554
|
|
|
|
|
|
|
} |
|
555
|
39
|
|
|
|
|
158
|
next; |
|
556
|
|
|
|
|
|
|
} |
|
557
|
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
# Tab content goes literally. |
|
559
|
2077
|
100
|
|
|
|
5131
|
if ( $in_context eq "tab" ) { |
|
560
|
64
|
100
|
|
|
|
203
|
unless ( /^\s*\{(?:end_of_tab|eot)\}\s*$/ ) { |
|
561
|
49
|
|
|
|
|
135
|
$self->add( type => "tabline", text => $_ ); |
|
562
|
49
|
|
|
|
|
99
|
next; |
|
563
|
|
|
|
|
|
|
} |
|
564
|
|
|
|
|
|
|
} |
|
565
|
|
|
|
|
|
|
|
|
566
|
2028
|
100
|
|
|
|
6564
|
if ( exists $config->{delegates}->{$in_context} ) { |
|
567
|
|
|
|
|
|
|
# 'open' indicates open. |
|
568
|
11
|
100
|
|
|
|
131
|
if ( /^\s*\{(?:end_of_\Q$in_context\E)\}\s*$/ ) { |
|
569
|
2
|
|
|
|
|
8
|
delete $self->{body}->[-1]->{open}; |
|
570
|
2
|
|
|
|
|
5
|
$grid_type = 0; |
|
571
|
|
|
|
|
|
|
# A subsequent {start_of_XXX} will open a new item |
|
572
|
|
|
|
|
|
|
|
|
573
|
2
|
|
|
|
|
7
|
my $d = $config->{delegates}->{$in_context}; |
|
574
|
2
|
50
|
|
|
|
9
|
if ( $d->{type} eq "image" ) { |
|
575
|
2
|
|
|
|
|
3
|
local $_; |
|
576
|
2
|
|
|
|
|
4
|
my $a = pop( @{ $self->{body} } ); |
|
|
2
|
|
|
|
|
7
|
|
|
577
|
2
|
|
|
|
|
5
|
my $id = $a->{id}; |
|
578
|
2
|
|
|
|
|
4
|
my $opts = {}; |
|
579
|
2
|
50
|
|
|
|
10
|
unless ( $id ) { |
|
580
|
2
|
|
|
|
|
6
|
my $pkg = 'ChordPro::Delegate::' . $a->{delegate}; |
|
581
|
2
|
50
|
|
|
|
265
|
eval "require $pkg" || warn($@); |
|
582
|
2
|
50
|
|
|
|
60
|
if ( my $c = $pkg->can("options") ) { |
|
583
|
2
|
|
|
|
|
10
|
$opts = $c->($a->{data}); |
|
584
|
2
|
|
|
|
|
7
|
$id = $opts->{id}; |
|
585
|
|
|
|
|
|
|
} |
|
586
|
|
|
|
|
|
|
} |
|
587
|
2
|
|
|
|
|
7
|
$opts = $a->{opts} = { %$opts, %{$a->{opts}} }; |
|
|
2
|
|
|
|
|
14
|
|
|
588
|
2
|
50
|
|
|
|
14
|
unless ( is_true($opts->{omit}) ) { |
|
589
|
2
|
0
|
33
|
|
|
8
|
if ( $opts->{align} && $opts->{x} && $opts->{x} =~ /\%$/ ) { |
|
|
|
|
0
|
|
|
|
|
|
590
|
0
|
|
|
|
|
0
|
do_warn( "Useless combination of x percentage with align (align ignored)" ); |
|
591
|
0
|
|
|
|
|
0
|
delete $opts->{align}; |
|
592
|
|
|
|
|
|
|
} |
|
593
|
|
|
|
|
|
|
|
|
594
|
2
|
|
|
|
|
6
|
my $def = !!$id; |
|
595
|
2
|
|
66
|
|
|
13
|
$id //= "_Image".$assetid++; |
|
596
|
|
|
|
|
|
|
|
|
597
|
2
|
50
|
|
|
|
6
|
if ( defined $opts->{spread} ) { |
|
598
|
0
|
|
|
|
|
0
|
$def++; |
|
599
|
0
|
0
|
|
|
|
0
|
if ( exists $self->{spreadimage} ) { |
|
600
|
0
|
|
|
|
|
0
|
do_warn("Skipping superfluous spread image"); |
|
601
|
|
|
|
|
|
|
} |
|
602
|
|
|
|
|
|
|
else { |
|
603
|
|
|
|
|
|
|
$self->{spreadimage} = |
|
604
|
0
|
|
|
|
|
0
|
{ id => $id, space => $opts->{spread} }; |
|
605
|
|
|
|
|
|
|
warn("Got spread image $id with space=$opts->{spread}\n") |
|
606
|
0
|
0
|
|
|
|
0
|
if $config->{debug}->{images}; |
|
607
|
|
|
|
|
|
|
} |
|
608
|
|
|
|
|
|
|
} |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
# Move to assets. |
|
611
|
2
|
|
|
|
|
13
|
$self->{assets}->{$id} = $a; |
|
612
|
2
|
100
|
|
|
|
8
|
if ( $def ) { |
|
613
|
1
|
|
|
|
|
3
|
my $label = delete $a->{label}; |
|
614
|
1
|
50
|
|
|
|
5
|
do_warn("Label \"$label\" ignored on non-displaying $in_context section\n") |
|
615
|
|
|
|
|
|
|
if $label; |
|
616
|
|
|
|
|
|
|
} |
|
617
|
|
|
|
|
|
|
else { |
|
618
|
1
|
|
|
|
|
3
|
my $label = delete $opts->{label}; |
|
619
|
1
|
50
|
33
|
|
|
6
|
$self->add( type => "set", |
|
620
|
|
|
|
|
|
|
name => "label", |
|
621
|
|
|
|
|
|
|
value => $label ) |
|
622
|
|
|
|
|
|
|
if $label && $label ne ""; |
|
623
|
1
|
|
|
|
|
6
|
$self->add( type => "image", |
|
624
|
|
|
|
|
|
|
opts => $opts, |
|
625
|
|
|
|
|
|
|
id => $id ); |
|
626
|
1
|
50
|
|
|
|
6
|
if ( $opts->{label} ) { |
|
627
|
|
|
|
|
|
|
push( @labels, $opts->{label} ) |
|
628
|
|
|
|
|
|
|
unless $in_context eq "chorus" |
|
629
|
0
|
0
|
0
|
|
|
0
|
&& !$config->{settings}->{choruslabels}; |
|
630
|
|
|
|
|
|
|
} |
|
631
|
|
|
|
|
|
|
} |
|
632
|
|
|
|
|
|
|
} |
|
633
|
|
|
|
|
|
|
} |
|
634
|
|
|
|
|
|
|
} |
|
635
|
|
|
|
|
|
|
else { |
|
636
|
|
|
|
|
|
|
# Add to an open item. |
|
637
|
9
|
50
|
33
|
|
|
32
|
if ( $self->{body} && @{ $self->{body} } |
|
|
9
|
|
33
|
|
|
83
|
|
|
|
|
|
33
|
|
|
|
|
|
638
|
|
|
|
|
|
|
&& $self->{body}->[-1]->{context} eq $in_context |
|
639
|
|
|
|
|
|
|
&& $self->{body}->[-1]->{open} ) { |
|
640
|
9
|
|
|
|
|
15
|
push( @{$self->{body}->[-1]->{data}}, |
|
|
9
|
|
|
|
|
48
|
|
|
641
|
|
|
|
|
|
|
fmt_subst( $self, $_ ) ); |
|
642
|
|
|
|
|
|
|
} |
|
643
|
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
# Else start new item. |
|
645
|
|
|
|
|
|
|
else { |
|
646
|
0
|
|
|
|
|
0
|
croak("Reopening delegate"); |
|
647
|
|
|
|
|
|
|
} |
|
648
|
9
|
|
|
|
|
1048
|
next; |
|
649
|
|
|
|
|
|
|
} |
|
650
|
|
|
|
|
|
|
} |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
# For now, directives should go on their own lines. |
|
653
|
2019
|
100
|
|
|
|
9456
|
if ( /^\s*\{(.*)\}\s*$/ ) { |
|
654
|
1204
|
|
|
|
|
4011
|
my $dir = $1; |
|
655
|
1204
|
50
|
|
|
|
3401
|
if ( $prep->{directive} ) { |
|
656
|
0
|
0
|
|
|
|
0
|
$config->{debug}->{pp} && warn("PRE: ", $_, "\n"); |
|
657
|
0
|
|
|
|
|
0
|
$prep->{directive}->($dir); |
|
658
|
0
|
0
|
|
|
|
0
|
$config->{debug}->{pp} && warn("POST: {", $dir, "}\n"); |
|
659
|
|
|
|
|
|
|
} |
|
660
|
1204
|
100
|
|
|
|
4763
|
$self->add( type => "ignore", |
|
661
|
|
|
|
|
|
|
text => $_ ) |
|
662
|
|
|
|
|
|
|
unless $self->directive($dir); |
|
663
|
1204
|
|
|
|
|
9730
|
next; |
|
664
|
|
|
|
|
|
|
} |
|
665
|
|
|
|
|
|
|
|
|
666
|
815
|
50
|
66
|
|
|
6135
|
if ( /\S/ && !$fragment && !exists $self->{title} ) { |
|
|
|
|
66
|
|
|
|
|
|
667
|
0
|
|
|
|
|
0
|
do_warn("Missing {title} -- prepare for surprising results"); |
|
668
|
0
|
|
|
|
|
0
|
unshift( @$lines, "{title:$_}"); |
|
669
|
0
|
|
|
|
|
0
|
$skipcnt++; |
|
670
|
0
|
|
|
|
|
0
|
next; |
|
671
|
|
|
|
|
|
|
} |
|
672
|
|
|
|
|
|
|
|
|
673
|
815
|
50
|
|
|
|
2126
|
if ( $in_context eq "tab" ) { |
|
674
|
0
|
|
|
|
|
0
|
$self->add( type => "tabline", text => $_ ); |
|
675
|
0
|
|
|
|
|
0
|
warn("OOPS"); |
|
676
|
0
|
|
|
|
|
0
|
next; |
|
677
|
|
|
|
|
|
|
} |
|
678
|
|
|
|
|
|
|
|
|
679
|
815
|
100
|
|
|
|
1889
|
if ( $in_context eq "grid" ) { |
|
680
|
39
|
|
|
|
|
186
|
$self->add( type => "gridline", $self->decompose_grid($_) ); |
|
681
|
39
|
|
|
|
|
159
|
next; |
|
682
|
|
|
|
|
|
|
} |
|
683
|
776
|
50
|
33
|
|
|
2277
|
if ( $in_context eq "grille" && @grille ) { |
|
684
|
|
|
|
|
|
|
push( @grille, { line => $diag->{line}, |
|
685
|
0
|
|
|
|
|
0
|
$self->decompose_grid($_) } ); |
|
686
|
0
|
|
|
|
|
0
|
next; |
|
687
|
|
|
|
|
|
|
} |
|
688
|
|
|
|
|
|
|
|
|
689
|
776
|
100
|
33
|
|
|
3143
|
if ( /\S/ ) { |
|
|
|
50
|
|
|
|
|
|
|
690
|
474
|
50
|
|
|
|
1291
|
if ( $prep->{songline} ) { |
|
691
|
0
|
0
|
|
|
|
0
|
$config->{debug}->{pp} && warn("PRE: ", $_, "\n"); |
|
692
|
0
|
|
|
|
|
0
|
$prep->{songline}->($_); |
|
693
|
0
|
0
|
|
|
|
0
|
$config->{debug}->{pp} && warn("POST: ", $_, "\n"); |
|
694
|
|
|
|
|
|
|
} |
|
695
|
474
|
50
|
33
|
|
|
2256
|
if ( $config->{settings}->{flowtext} |
|
696
|
0
|
|
0
|
|
|
0
|
&& @{ $self->{body}//[] } ) { |
|
697
|
0
|
|
|
|
|
0
|
my $prev = $self->{body}->[-1]; |
|
698
|
0
|
|
|
|
|
0
|
my $this = { $self->decompose($_) }; |
|
699
|
0
|
0
|
0
|
|
|
0
|
if ( $prev->{type} eq "songline" |
|
|
|
|
0
|
|
|
|
|
|
700
|
|
|
|
|
|
|
&& !$prev->{chords} |
|
701
|
|
|
|
|
|
|
&& !$this->{chords} ) { |
|
702
|
0
|
|
|
|
|
0
|
$prev->{phrases}->[0] .= " " . $this->{phrases}->[0]; |
|
703
|
|
|
|
|
|
|
} |
|
704
|
|
|
|
|
|
|
else { |
|
705
|
0
|
|
|
|
|
0
|
$self->add( type => "songline", %$this ); |
|
706
|
|
|
|
|
|
|
} |
|
707
|
|
|
|
|
|
|
} |
|
708
|
|
|
|
|
|
|
else { |
|
709
|
474
|
|
|
|
|
1903
|
$self->add( type => "songline", $self->decompose($_) ); |
|
710
|
|
|
|
|
|
|
} |
|
711
|
|
|
|
|
|
|
} |
|
712
|
|
|
|
|
|
|
elsif ( exists $self->{title} || $fragment ) { |
|
713
|
302
|
|
|
|
|
1243
|
$self->add( type => "empty" ); |
|
714
|
|
|
|
|
|
|
} |
|
715
|
|
|
|
|
|
|
else { |
|
716
|
|
|
|
|
|
|
# Collect pre-title stuff separately. |
|
717
|
0
|
|
|
|
|
0
|
push( @{ $self->{preamble} }, $_ ); |
|
|
0
|
|
|
|
|
0
|
|
|
718
|
|
|
|
|
|
|
} |
|
719
|
|
|
|
|
|
|
} |
|
720
|
214
|
50
|
|
|
|
777
|
do_warn("Unterminated context in song: $in_context") |
|
721
|
|
|
|
|
|
|
if $in_context; |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
# These don't make sense after processing. Or do they? |
|
724
|
|
|
|
|
|
|
# delete $self->{meta}->{$_} for qw( key_actual key_from ); |
|
725
|
|
|
|
|
|
|
|
|
726
|
214
|
50
|
|
|
|
1061
|
warn("Processed song...\n") if $options->{verbose}; |
|
727
|
214
|
|
|
|
|
842
|
$diag->{format} = "\"%f\": %m"; |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
::dump($self->{assets}, as => "Assets, Pass 1") |
|
730
|
214
|
50
|
|
|
|
1169
|
if $config->{debug}->{assets} & 1; |
|
731
|
214
|
50
|
|
|
|
974
|
$self->dump(0) if $config->{debug}->{song} > 1; |
|
732
|
|
|
|
|
|
|
|
|
733
|
214
|
100
|
|
|
|
755
|
if ( @labels ) { |
|
734
|
1
|
|
|
|
|
7
|
$self->{labels} = [ @labels ]; |
|
735
|
|
|
|
|
|
|
} |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
# Suppress chords that the user considers 'easy'. |
|
738
|
214
|
|
|
|
|
432
|
my %suppress; |
|
739
|
214
|
|
|
|
|
770
|
my $xc = $config->{settings}->{transcode}; |
|
740
|
214
|
|
|
|
|
581
|
for ( @{ $config->{diagrams}->{suppress} } ) { |
|
|
214
|
|
|
|
|
1219
|
|
|
741
|
0
|
|
|
|
|
0
|
my $info = ChordPro::Chords::known_chord($_); |
|
742
|
0
|
0
|
|
|
|
0
|
warn("Unknown chord \"$_\" in suppress list\n"), next |
|
743
|
|
|
|
|
|
|
unless $info; |
|
744
|
|
|
|
|
|
|
# Note we do transcode, but we do not transpose. |
|
745
|
0
|
0
|
|
|
|
0
|
if ( $xc ) { |
|
746
|
0
|
|
|
|
|
0
|
$info = $info->transcode($xc); |
|
747
|
|
|
|
|
|
|
} |
|
748
|
0
|
|
|
|
|
0
|
$suppress{$info->name} = $info->{origin} ne "song"; |
|
749
|
|
|
|
|
|
|
} |
|
750
|
|
|
|
|
|
|
# Suppress chords that the user don't want. |
|
751
|
214
|
|
|
|
|
499
|
while ( my ($k,$v) = each %{ $self->{chordsinfo} } ) { |
|
|
668
|
|
|
|
|
2903
|
|
|
752
|
454
|
100
|
100
|
|
|
2705
|
$suppress{$k} = 1 if !is_true($v->{diagram}//1); |
|
753
|
|
|
|
|
|
|
} |
|
754
|
214
|
100
|
|
|
|
718
|
@used_chords = map { $suppress{$_} ? () : $_ } @used_chords; |
|
|
947
|
|
|
|
|
2858
|
|
|
755
|
|
|
|
|
|
|
|
|
756
|
214
|
|
|
|
|
482
|
my $diagrams; |
|
757
|
214
|
100
|
|
|
|
1046
|
if ( exists($self->{settings}->{diagrams} ) ) { |
|
758
|
6
|
|
|
|
|
21
|
$diagrams = $self->{settings}->{diagrams}; |
|
759
|
6
|
|
100
|
|
|
40
|
$diagrams &&= $config->{diagrams}->{show} || "all"; |
|
|
|
|
66
|
|
|
|
|
|
760
|
|
|
|
|
|
|
} |
|
761
|
|
|
|
|
|
|
else { |
|
762
|
208
|
|
|
|
|
755
|
$diagrams = $config->{diagrams}->{show}; |
|
763
|
|
|
|
|
|
|
} |
|
764
|
|
|
|
|
|
|
|
|
765
|
214
|
50
|
66
|
|
|
2639
|
if ( $diagrams =~ /^(user|all)$/ |
|
766
|
|
|
|
|
|
|
&& !ChordPro::Chords::Parser->get_parser($target,1)->has_diagrams ) { |
|
767
|
|
|
|
|
|
|
do_warn( "Chord diagrams suppressed for " . |
|
768
|
0
|
0
|
|
|
|
0
|
ucfirst($target) . " chords" ) unless $options->{silent}; |
|
769
|
0
|
|
|
|
|
0
|
$diagrams = "none"; |
|
770
|
|
|
|
|
|
|
} |
|
771
|
|
|
|
|
|
|
|
|
772
|
214
|
50
|
|
|
|
831
|
if ( $diagrams eq "user" ) { |
|
773
|
|
|
|
|
|
|
|
|
774
|
0
|
0
|
0
|
|
|
0
|
if ( $self->{define} && @{$self->{define}} ) { |
|
|
0
|
|
|
|
|
0
|
|
|
775
|
0
|
|
|
|
|
0
|
my %h = map { demarkup($_) => 1 } @used_chords; |
|
|
0
|
|
|
|
|
0
|
|
|
776
|
|
|
|
|
|
|
@used_chords = |
|
777
|
0
|
0
|
|
|
|
0
|
map { $h{$_->{name}} ? $_->{name} : () } @{$self->{define}}; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
778
|
|
|
|
|
|
|
} |
|
779
|
|
|
|
|
|
|
else { |
|
780
|
0
|
|
|
|
|
0
|
@used_chords = (); |
|
781
|
|
|
|
|
|
|
} |
|
782
|
|
|
|
|
|
|
} |
|
783
|
|
|
|
|
|
|
else { |
|
784
|
214
|
|
|
|
|
493
|
my %h; |
|
785
|
945
|
100
|
|
|
|
2922
|
@used_chords = map { $h{$_}++ ? () : $_ } |
|
786
|
214
|
|
|
|
|
701
|
map { demarkup($_) } @used_chords; |
|
|
945
|
|
|
|
|
2224
|
|
|
787
|
|
|
|
|
|
|
} |
|
788
|
|
|
|
|
|
|
|
|
789
|
214
|
100
|
|
|
|
1979
|
if ( $config->{diagrams}->{sorted} ) { |
|
790
|
7
|
|
|
7
|
0
|
20
|
sub byname { ChordPro::Chords::chordcompare($a,$b) } |
|
791
|
1
|
|
|
|
|
12
|
@used_chords = sort byname @used_chords; |
|
792
|
|
|
|
|
|
|
} |
|
793
|
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
# For headings, footers, table of contents, ... |
|
795
|
214
|
|
50
|
|
|
1987
|
$self->{meta}->{chords} //= [ @used_chords ]; |
|
796
|
214
|
|
|
|
|
469
|
$self->{meta}->{numchords} = [ scalar(@{$self->{meta}->{chords}}) ]; |
|
|
214
|
|
|
|
|
1060
|
|
|
797
|
|
|
|
|
|
|
|
|
798
|
214
|
100
|
|
|
|
793
|
if ( %memchords ) { |
|
799
|
49
|
50
|
|
|
|
367
|
::dump(\%memchords, as => "cc (atend)") if $config->{debug}->{chords}; |
|
800
|
|
|
|
|
|
|
} |
|
801
|
|
|
|
|
|
|
else { |
|
802
|
|
|
|
|
|
|
# Avoid clutter. |
|
803
|
165
|
|
|
|
|
517
|
delete $self->{meta}->{cc}; |
|
804
|
|
|
|
|
|
|
} |
|
805
|
|
|
|
|
|
|
|
|
806
|
214
|
100
|
|
|
|
711
|
if ( %memchords ) { |
|
807
|
49
|
50
|
|
|
|
203
|
::dump(\%memchords, as => "cc (atend)") if $config->{debug}->{chords}; |
|
808
|
|
|
|
|
|
|
} |
|
809
|
|
|
|
|
|
|
else { |
|
810
|
|
|
|
|
|
|
# Avoid clutter. |
|
811
|
165
|
|
|
|
|
443
|
delete $self->{meta}->{cc}; |
|
812
|
|
|
|
|
|
|
} |
|
813
|
|
|
|
|
|
|
|
|
814
|
214
|
100
|
|
|
|
1223
|
if ( $diagrams =~ /^(user|all)$/ ) { |
|
815
|
|
|
|
|
|
|
$self->{chords} = |
|
816
|
130
|
|
|
|
|
1332
|
{ type => "diagrams", |
|
817
|
|
|
|
|
|
|
origin => "song", |
|
818
|
|
|
|
|
|
|
show => $diagrams, |
|
819
|
|
|
|
|
|
|
chords => [ @used_chords ], |
|
820
|
|
|
|
|
|
|
}; |
|
821
|
|
|
|
|
|
|
|
|
822
|
130
|
50
|
|
|
|
528
|
if ( %warned_chords ) { |
|
823
|
0
|
|
|
|
|
0
|
my @a = sort ChordPro::Chords::chordcompare |
|
824
|
|
|
|
|
|
|
keys(%warned_chords); |
|
825
|
0
|
|
|
|
|
0
|
my $l; |
|
826
|
0
|
0
|
|
|
|
0
|
if ( @a > 1 ) { |
|
827
|
0
|
|
|
|
|
0
|
my $a = pop(@a); |
|
828
|
0
|
|
|
|
|
0
|
$l = '"' . join('", "', @a) . '" and "' . $a . '"'; |
|
829
|
|
|
|
|
|
|
} |
|
830
|
|
|
|
|
|
|
else { |
|
831
|
0
|
|
|
|
|
0
|
$l = '"' . $a[0] . '"'; |
|
832
|
|
|
|
|
|
|
} |
|
833
|
0
|
|
|
|
|
0
|
do_warn( "No chord diagram defined for $l (skipped)\n" ); |
|
834
|
|
|
|
|
|
|
} |
|
835
|
|
|
|
|
|
|
} |
|
836
|
|
|
|
|
|
|
|
|
837
|
214
|
50
|
|
|
|
1483
|
$self->dump(0) if $config->{debug}->{song} > 0; |
|
838
|
214
|
50
|
|
|
|
1002
|
$self->dump(2) if $config->{debug}->{song} < 0; |
|
839
|
214
|
50
|
|
|
|
904
|
$self->dump(1) if $config->{debug}->{songfull}; |
|
840
|
|
|
|
|
|
|
|
|
841
|
214
|
|
|
|
|
1808
|
return $self; |
|
842
|
|
|
|
|
|
|
} |
|
843
|
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
sub add { |
|
845
|
1341
|
|
|
1341
|
0
|
2548
|
my $self = shift; |
|
846
|
1341
|
50
|
|
|
|
3268
|
return if $skip_context; |
|
847
|
1341
|
|
|
|
|
7800
|
push( @{$self->{body}}, |
|
848
|
|
|
|
|
|
|
{ context => $in_context, |
|
849
|
1341
|
100
|
|
|
|
2115
|
$lineinfo ? ( line => $diag->{line} ) : (), |
|
850
|
|
|
|
|
|
|
@_ } ); |
|
851
|
1341
|
100
|
|
|
|
5905
|
if ( $in_context eq "chorus" ) { |
|
852
|
110
|
|
|
|
|
462
|
push( @chorus, { context => $in_context, @_ } ); |
|
853
|
110
|
|
|
|
|
201
|
$chorus_xpose = $xpose; |
|
854
|
110
|
|
|
|
|
469
|
$chorus_xpose_dir = $xpose_dir; |
|
855
|
|
|
|
|
|
|
} |
|
856
|
|
|
|
|
|
|
} |
|
857
|
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
# Parses a chord and adds it to the song. |
|
859
|
|
|
|
|
|
|
# It understands markup, parenthesized chords and annotations. |
|
860
|
|
|
|
|
|
|
# Returns the chord Appearance. |
|
861
|
|
|
|
|
|
|
sub chord { |
|
862
|
991
|
|
|
991
|
0
|
2559
|
my ( $self, $orig ) = @_; |
|
863
|
991
|
50
|
|
|
|
2505
|
Carp::confess unless length($orig); |
|
864
|
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
# Intercept annotations. |
|
866
|
991
|
100
|
66
|
|
|
6542
|
if ( $orig =~ /^\*(.+)/ || $orig =~ /^(\||\s+)$/ ) { |
|
867
|
3
|
|
|
|
|
52
|
my $i = ChordPro::Chord::Annotation->new |
|
868
|
|
|
|
|
|
|
( { name => $orig, text => $1 } ); |
|
869
|
|
|
|
|
|
|
return |
|
870
|
3
|
|
|
|
|
18
|
ChordPro::Chords::Appearance->new |
|
871
|
|
|
|
|
|
|
( key => $self->add_chord($i), info => $i, orig => $orig ); |
|
872
|
|
|
|
|
|
|
} |
|
873
|
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
# Check for markup. |
|
875
|
988
|
|
|
|
|
2049
|
my $markup = $orig; |
|
876
|
988
|
|
|
|
|
4032
|
my $c = demarkup($orig); |
|
877
|
988
|
100
|
|
|
|
2871
|
if ( $markup eq $c ) { # no markup |
|
878
|
981
|
|
|
|
|
1784
|
undef $markup; |
|
879
|
|
|
|
|
|
|
} |
|
880
|
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
# Special treatment for parenthesized chords. |
|
882
|
988
|
|
|
|
|
2070
|
$c =~ s/^\((.*)\)$/$1/; |
|
883
|
988
|
50
|
|
|
|
2376
|
do_warn("Double parens in chord: \"$orig\"") |
|
884
|
|
|
|
|
|
|
if $c =~ s/^\((.*)\)$/$1/; |
|
885
|
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
# We have a 'bare' chord now. Parse it. |
|
887
|
988
|
|
|
|
|
3570
|
my $info = $self->parse_chord($c); |
|
888
|
988
|
100
|
|
|
|
2475
|
unless ( defined $info ) { |
|
889
|
|
|
|
|
|
|
# Warning was given. |
|
890
|
|
|
|
|
|
|
# Make annotation. |
|
891
|
8
|
|
|
|
|
66
|
my $i = ChordPro::Chord::Annotation->new |
|
892
|
|
|
|
|
|
|
( { name => $orig, text => $orig } ); |
|
893
|
|
|
|
|
|
|
return |
|
894
|
8
|
|
|
|
|
42
|
ChordPro::Chords::Appearance->new |
|
895
|
|
|
|
|
|
|
( key => $self->add_chord($i), info => $i, orig => $orig ); |
|
896
|
|
|
|
|
|
|
} |
|
897
|
|
|
|
|
|
|
|
|
898
|
980
|
|
|
|
|
14904
|
my $ap = ChordPro::Chords::Appearance->new( orig => $orig ); |
|
899
|
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
# Handle markup, if any. |
|
901
|
980
|
100
|
|
|
|
22790
|
if ( $markup ) { |
|
|
|
50
|
|
|
|
|
|
|
902
|
7
|
100
|
100
|
|
|
281
|
if ( $markup =~ s/\>\Q$c\E\>%{formatted} |
|
903
|
|
|
|
|
|
|
|| |
|
904
|
|
|
|
|
|
|
$markup =~ s/\>\(\Q$c\E\)\>(%{formatted}) ) { |
|
905
|
|
|
|
|
|
|
} |
|
906
|
|
|
|
|
|
|
else { |
|
907
|
1
|
|
|
|
|
8
|
do_warn("Invalid markup in chord: \"$markup\"\n"); |
|
908
|
|
|
|
|
|
|
} |
|
909
|
7
|
|
|
|
|
52
|
$ap->format = $markup; |
|
910
|
|
|
|
|
|
|
} |
|
911
|
|
|
|
|
|
|
elsif ( (my $m = $orig) =~ s/\Q$c\E/%{formatted}/ ) { |
|
912
|
973
|
100
|
|
|
|
3503
|
$ap->format = $m unless $m eq "%{formatted}"; |
|
913
|
|
|
|
|
|
|
} |
|
914
|
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
# After parsing, the chord can be changed by transpose/code. |
|
916
|
|
|
|
|
|
|
# info->name is the new key. |
|
917
|
980
|
|
|
|
|
3945
|
$ap->key = $self->add_chord( $info, $c = $info->name ); |
|
918
|
980
|
|
|
|
|
3219
|
$ap->info = $info; |
|
919
|
|
|
|
|
|
|
|
|
920
|
980
|
100
|
100
|
|
|
3104
|
unless ( $info->is_nc || $info->is_note ) { |
|
921
|
|
|
|
|
|
|
# if ( $info->is_keyboard ) { |
|
922
|
966
|
50
|
0
|
|
|
4957
|
if ( $::config->{instrument}->{type} eq "keyboard" ) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
923
|
0
|
|
|
|
|
0
|
push( @used_chords, $c ); |
|
924
|
|
|
|
|
|
|
} |
|
925
|
|
|
|
|
|
|
elsif ( $info->{origin} ) { |
|
926
|
|
|
|
|
|
|
# Include if we have diagram info. |
|
927
|
766
|
50
|
|
|
|
2125
|
push( @used_chords, $c ) if $info->has_diagram; |
|
928
|
|
|
|
|
|
|
} |
|
929
|
|
|
|
|
|
|
elsif ( $::running_under_test ) { |
|
930
|
|
|
|
|
|
|
# Tests run without config and chords, so pretend. |
|
931
|
200
|
|
|
|
|
652
|
push( @used_chords, $c ); |
|
932
|
|
|
|
|
|
|
} |
|
933
|
|
|
|
|
|
|
elsif ( ! ( $info->is_rootless |
|
934
|
|
|
|
|
|
|
|| $info->has_diagram |
|
935
|
|
|
|
|
|
|
|| !$info->parser->has_diagrams |
|
936
|
|
|
|
|
|
|
) ) { |
|
937
|
|
|
|
|
|
|
do_warn("Unknown chord: $c") |
|
938
|
0
|
0
|
|
|
|
0
|
unless $warned_chords{$c}++; |
|
939
|
|
|
|
|
|
|
} |
|
940
|
|
|
|
|
|
|
} |
|
941
|
|
|
|
|
|
|
|
|
942
|
980
|
|
|
|
|
3460
|
return $ap; |
|
943
|
|
|
|
|
|
|
} |
|
944
|
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
sub decompose { |
|
946
|
608
|
|
|
608
|
0
|
1771
|
my ($self, $orig) = @_; |
|
947
|
608
|
|
|
|
|
2422
|
my $line = fmt_subst( $self, $orig ); |
|
948
|
608
|
100
|
|
|
|
63936
|
undef $orig if $orig eq $line; |
|
949
|
608
|
|
|
|
|
4393
|
$line =~ s/\s+$//; |
|
950
|
608
|
|
|
|
|
8131
|
my @a = split( $re_chords, $line, -1); |
|
951
|
|
|
|
|
|
|
|
|
952
|
608
|
100
|
|
|
|
2278
|
if ( @a <= 1 ) { |
|
953
|
249
|
50
|
|
|
|
1932
|
return ( phrases => [ $line ], |
|
954
|
|
|
|
|
|
|
$orig ? ( orig => $orig ) : (), |
|
955
|
|
|
|
|
|
|
); |
|
956
|
|
|
|
|
|
|
} |
|
957
|
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
# For the exceptional case you need brackets [] in your lyrics |
|
959
|
|
|
|
|
|
|
# or annotations. |
|
960
|
359
|
50
|
|
|
|
1825
|
if ( my $a = $config->{parser}->{altbrackets} ) { |
|
961
|
0
|
|
|
|
|
0
|
@a = map { eval "tr/$a/[]/r" } @a; |
|
|
0
|
|
|
|
|
0
|
|
|
962
|
|
|
|
|
|
|
} |
|
963
|
|
|
|
|
|
|
|
|
964
|
359
|
|
|
|
|
726
|
my $dummy; |
|
965
|
359
|
100
|
|
|
|
1144
|
shift(@a) if $a[0] eq ""; |
|
966
|
359
|
100
|
|
|
|
3280
|
unshift(@a, '[]'), $dummy++ if $a[0] !~ $re_chords; |
|
967
|
|
|
|
|
|
|
|
|
968
|
359
|
|
|
|
|
968
|
my @phrases; |
|
969
|
|
|
|
|
|
|
my @chords; |
|
970
|
359
|
|
|
|
|
949
|
while ( @a ) { |
|
971
|
1084
|
|
|
|
|
3619
|
my $chord = shift(@a); |
|
972
|
1084
|
|
|
|
|
2333
|
push(@phrases, shift(@a)); |
|
973
|
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
# Normal chords. |
|
975
|
1084
|
100
|
100
|
|
|
11435
|
if ( $chord =~ s/^\[(.*)\]$/$1/ && $chord ne "^" ) { |
|
|
|
100
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
976
|
1040
|
100
|
|
|
|
4480
|
push(@chords, $chord eq "" ? "" : $self->chord($chord)); |
|
977
|
1040
|
100
|
100
|
|
|
4023
|
if ( $memchords && !$dummy && $chord !~ /^\*/ ) { |
|
|
|
|
66
|
|
|
|
|
|
978
|
222
|
100
|
|
|
|
555
|
if ( $memcrdinx == 0 ) { |
|
979
|
35
|
|
|
|
|
87
|
$memorizing++; |
|
980
|
|
|
|
|
|
|
} |
|
981
|
222
|
100
|
|
|
|
486
|
if ( $memorizing ) { |
|
982
|
220
|
50
|
|
|
|
742
|
push( @$memchords, $chord eq "" ? "" : $chord ); |
|
983
|
|
|
|
|
|
|
warn("Chord memorized for $in_context\[$memcrdinx]: ", |
|
984
|
|
|
|
|
|
|
$memchords->[-1], "\n") |
|
985
|
220
|
50
|
|
|
|
768
|
if $config->{debug}->{chords}; |
|
986
|
|
|
|
|
|
|
} |
|
987
|
222
|
|
|
|
|
447
|
$memcrdinx++; |
|
988
|
|
|
|
|
|
|
} |
|
989
|
|
|
|
|
|
|
} |
|
990
|
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
# Recall memorized chords. |
|
992
|
|
|
|
|
|
|
elsif ( $memchords && $in_context && $chord !~ /^\*/ ) { |
|
993
|
37
|
100
|
100
|
|
|
173
|
if ( $memcrdinx == 0 && @$memchords == 0 ) { |
|
|
|
50
|
|
|
|
|
|
|
994
|
1
|
|
|
|
|
8
|
do_warn("No chords memorized for $in_context"); |
|
995
|
1
|
|
|
|
|
15
|
push( @chords, $self->chord($chord) ); |
|
996
|
1
|
|
|
|
|
3
|
undef $memchords; |
|
997
|
|
|
|
|
|
|
} |
|
998
|
|
|
|
|
|
|
elsif ( $memcrdinx >= @$memchords ) { |
|
999
|
0
|
|
|
|
|
0
|
do_warn("Not enough chords memorized for $in_context"); |
|
1000
|
0
|
|
|
|
|
0
|
push( @chords, $self->chord($chord) ); |
|
1001
|
|
|
|
|
|
|
} |
|
1002
|
|
|
|
|
|
|
else { |
|
1003
|
|
|
|
|
|
|
warn("Chord recall $in_context\[$memcrdinx]: ", $memchords->[$memcrdinx], "\n") |
|
1004
|
36
|
50
|
|
|
|
126
|
if $config->{debug}->{chords}; |
|
1005
|
36
|
|
|
|
|
137
|
push( @chords, $self->chord($memchords->[$memcrdinx]) ); |
|
1006
|
|
|
|
|
|
|
} |
|
1007
|
37
|
|
|
|
|
90
|
$memcrdinx++; |
|
1008
|
|
|
|
|
|
|
} |
|
1009
|
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
# Not memorizing. |
|
1011
|
|
|
|
|
|
|
else { |
|
1012
|
|
|
|
|
|
|
# do_warn("No chords memorized for $in_context"); |
|
1013
|
7
|
|
|
|
|
25
|
push( @chords, $self->chord($chord) ); |
|
1014
|
|
|
|
|
|
|
} |
|
1015
|
1084
|
|
|
|
|
3482
|
$dummy = 0; |
|
1016
|
|
|
|
|
|
|
} |
|
1017
|
|
|
|
|
|
|
|
|
1018
|
359
|
100
|
|
|
|
2603
|
return ( phrases => \@phrases, |
|
1019
|
|
|
|
|
|
|
chords => \@chords, |
|
1020
|
|
|
|
|
|
|
$orig ? ( orig => $orig ) : (), |
|
1021
|
|
|
|
|
|
|
); |
|
1022
|
|
|
|
|
|
|
} |
|
1023
|
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
sub cdecompose { |
|
1025
|
134
|
|
|
134
|
0
|
395
|
my ( $self, $line ) = @_; |
|
1026
|
134
|
50
|
|
|
|
676
|
$line = fmt_subst( $self, $line ) unless $no_substitute; |
|
1027
|
134
|
|
|
|
|
13846
|
my %res = $self->decompose($line); |
|
1028
|
134
|
100
|
|
|
|
940
|
return ( text => $line ) unless $res{chords}; |
|
1029
|
14
|
|
|
|
|
105
|
return %res; |
|
1030
|
|
|
|
|
|
|
} |
|
1031
|
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
sub decompose_grid { |
|
1033
|
39
|
|
|
39
|
0
|
110
|
my ($self, $orig) = @_; |
|
1034
|
39
|
|
|
|
|
202
|
my $line = fmt_subst( $self, $orig ); |
|
1035
|
39
|
50
|
|
|
|
4206
|
undef $orig if $orig eq $line; |
|
1036
|
39
|
|
|
|
|
163
|
$line =~ s/^\s+//; |
|
1037
|
39
|
|
|
|
|
272
|
$line =~ s/\s+$//; |
|
1038
|
39
|
50
|
|
|
|
121
|
return ( tokens => [] ) if $line eq ""; |
|
1039
|
39
|
|
|
|
|
189
|
local $re_chords = qr/(\[.*?\])/; |
|
1040
|
39
|
|
|
|
|
91
|
my $memchords = $memchords; |
|
1041
|
|
|
|
|
|
|
|
|
1042
|
39
|
|
|
|
|
83
|
my %res; |
|
1043
|
39
|
50
|
|
|
|
165
|
if ( $line !~ /\|/ ) { |
|
1044
|
0
|
|
|
|
|
0
|
$res{margin} = { $self->cdecompose($line), orig => $line }; |
|
1045
|
0
|
|
|
|
|
0
|
$line = ""; |
|
1046
|
|
|
|
|
|
|
} |
|
1047
|
|
|
|
|
|
|
else { |
|
1048
|
39
|
50
|
|
|
|
368
|
if ( $line =~ /(.*\|\S*)\s([^\|]*)$/ ) { |
|
1049
|
0
|
|
|
|
|
0
|
$line = $1; |
|
1050
|
0
|
|
|
|
|
0
|
$res{comment} = { $self->cdecompose($2), orig => $2 }; |
|
1051
|
0
|
0
|
0
|
|
|
0
|
do_warn( "No margin cell for trailing comment" ) |
|
1052
|
|
|
|
|
|
|
unless $in_context eq "grille" || $grid_cells->[2]; |
|
1053
|
|
|
|
|
|
|
} |
|
1054
|
39
|
50
|
|
|
|
156
|
if ( $line =~ /^([^|]+?)\s*(\|.*)/ ) { |
|
1055
|
0
|
|
|
|
|
0
|
$line = $2; |
|
1056
|
0
|
|
|
|
|
0
|
$res{margin} = { $self->cdecompose($1), orig => $1 }; |
|
1057
|
0
|
0
|
0
|
|
|
0
|
do_warn( "No cell for margin text" ) |
|
1058
|
|
|
|
|
|
|
unless $in_context eq "grille" || $grid_cells->[1]; |
|
1059
|
|
|
|
|
|
|
} |
|
1060
|
|
|
|
|
|
|
} |
|
1061
|
|
|
|
|
|
|
|
|
1062
|
39
|
|
|
|
|
67
|
my @tokens; |
|
1063
|
39
|
|
|
|
|
274
|
my @t = split( ' ', $line ); |
|
1064
|
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
# Unfortunately, gets split too. |
|
1066
|
39
|
|
|
|
|
141
|
while ( @t ) { |
|
1067
|
663
|
|
|
|
|
1003
|
$_ = shift(@t); |
|
1068
|
663
|
|
|
|
|
1279
|
push( @tokens, $_ ); |
|
1069
|
663
|
50
|
|
|
|
1517
|
if ( /\
|
|
1070
|
0
|
|
|
|
|
0
|
while ( @t ) { |
|
1071
|
0
|
|
|
|
|
0
|
$_ = shift(@t); |
|
1072
|
0
|
|
|
|
|
0
|
$tokens[-1] .= " " . $_; |
|
1073
|
0
|
0
|
0
|
|
|
0
|
last if /\<\/span>/ |
|
1074
|
|
|
|
|
|
|
&& ! /\<\/span>.*?\
|
|
1075
|
|
|
|
|
|
|
} |
|
1076
|
|
|
|
|
|
|
} |
|
1077
|
|
|
|
|
|
|
} |
|
1078
|
39
|
|
|
|
|
92
|
my $nbt = 0; # non-bar tokens |
|
1079
|
39
|
|
|
|
|
124
|
my $p0; # this bar chords |
|
1080
|
|
|
|
|
|
|
my $p1; # prev chords (for % and %% repeat) |
|
1081
|
39
|
|
|
|
|
0
|
my $p2; # pprev chords (for %% repeat) |
|
1082
|
39
|
|
|
|
|
67
|
my $si = 0; # start index |
|
1083
|
|
|
|
|
|
|
|
|
1084
|
39
|
|
|
|
|
71
|
$grid_type = 0; |
|
1085
|
39
|
50
|
33
|
|
|
248
|
if ( @tokens && uc($tokens[0]) =~ /^\|.*S/i ) { |
|
1086
|
0
|
|
|
|
|
0
|
$grid_type = 1 + (chop($tokens[0]) eq "S"); # strum line |
|
1087
|
0
|
|
|
|
|
0
|
$memchords = 0; |
|
1088
|
|
|
|
|
|
|
} |
|
1089
|
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
my $chord = sub { |
|
1091
|
118
|
|
|
118
|
|
246
|
my $c = shift; |
|
1092
|
118
|
50
|
33
|
|
|
263
|
if ( is_gridstrum($grid_type) && is_strum($c) ) { |
|
1093
|
0
|
|
|
|
|
0
|
my $i = ChordPro::Chord::Strum->new( { name => $c } ); |
|
1094
|
0
|
|
|
|
|
0
|
ChordPro::Chords::Appearance->new |
|
1095
|
|
|
|
|
|
|
( key => $self->add_chord($i), info => $i ); |
|
1096
|
|
|
|
|
|
|
} |
|
1097
|
|
|
|
|
|
|
else { |
|
1098
|
118
|
|
|
|
|
480
|
$self->chord($c); |
|
1099
|
|
|
|
|
|
|
} |
|
1100
|
39
|
|
|
|
|
251
|
}; |
|
1101
|
|
|
|
|
|
|
|
|
1102
|
39
|
|
|
|
|
110
|
foreach ( @tokens ) { |
|
1103
|
663
|
50
|
33
|
|
|
6186
|
if ( $_ eq "|:" || $_ eq "{" ) { |
|
|
|
50
|
33
|
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1104
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "bar" }; |
|
1105
|
0
|
0
|
|
|
|
0
|
$si = @$memchords if $memchords; |
|
1106
|
|
|
|
|
|
|
} |
|
1107
|
|
|
|
|
|
|
elsif ( /^\|(\d+)(>?)$/ ) { |
|
1108
|
0
|
|
|
|
|
0
|
$_ = { symbol => '|', volta => $1, class => "bar" }; |
|
1109
|
0
|
0
|
|
|
|
0
|
$_->{align} = 1 if $2; |
|
1110
|
|
|
|
|
|
|
} |
|
1111
|
|
|
|
|
|
|
elsif ( $_ eq ":|" || $_ eq "}" ) { |
|
1112
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "bar" }; |
|
1113
|
0
|
0
|
|
|
|
0
|
if ( $memchords ) { |
|
1114
|
0
|
|
|
|
|
0
|
push( @$memchords, @$memchords[ $si .. $#{$memchords} ] ); |
|
|
0
|
|
|
|
|
0
|
|
|
1115
|
|
|
|
|
|
|
} |
|
1116
|
|
|
|
|
|
|
} |
|
1117
|
|
|
|
|
|
|
elsif ( $_ eq ":|:" || $_ eq "}{" ) { |
|
1118
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "bar" }; |
|
1119
|
0
|
0
|
|
|
|
0
|
if ( $memchords ) { |
|
1120
|
0
|
|
|
|
|
0
|
push( @$memchords, @$memchords[ $si .. $#{$memchords} ] ); |
|
|
0
|
|
|
|
|
0
|
|
|
1121
|
0
|
|
|
|
|
0
|
$si = @$memchords; |
|
1122
|
|
|
|
|
|
|
} |
|
1123
|
|
|
|
|
|
|
} |
|
1124
|
|
|
|
|
|
|
elsif ( $_ eq "|" ) { |
|
1125
|
149
|
|
|
|
|
513
|
$_ = { symbol => $_, class => "bar" }; |
|
1126
|
|
|
|
|
|
|
} |
|
1127
|
|
|
|
|
|
|
elsif ( $_ eq "||" ) { |
|
1128
|
2
|
|
|
|
|
13
|
$_ = { symbol => $_, class => "bar" }; |
|
1129
|
|
|
|
|
|
|
} |
|
1130
|
|
|
|
|
|
|
elsif ( $_ eq "|." ) { |
|
1131
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "bar" }; |
|
1132
|
|
|
|
|
|
|
} |
|
1133
|
|
|
|
|
|
|
elsif ( $_ eq "%" ) { |
|
1134
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "repeat1" }; |
|
1135
|
0
|
0
|
0
|
|
|
0
|
if ( $memchords && $p1 ) { |
|
1136
|
0
|
|
|
|
|
0
|
push( @$memchords, @$p1 ); |
|
1137
|
0
|
0
|
|
|
|
0
|
if ( $config->{debug}->{chords} ) { |
|
1138
|
|
|
|
|
|
|
warn("Chord memorized for $cctag\[$memcrdinx]: ", |
|
1139
|
|
|
|
|
|
|
$_, "\n"), $memcrdinx++ |
|
1140
|
0
|
|
|
|
|
0
|
for @$p1; |
|
1141
|
|
|
|
|
|
|
} |
|
1142
|
|
|
|
|
|
|
} |
|
1143
|
|
|
|
|
|
|
} |
|
1144
|
|
|
|
|
|
|
elsif ( $_ eq '%%' ) { |
|
1145
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "repeat2" }; |
|
1146
|
0
|
0
|
0
|
|
|
0
|
if ( $memchords && $p1 ) { |
|
1147
|
0
|
0
|
|
|
|
0
|
push( @$memchords, @$p2 ) if $p2; |
|
1148
|
0
|
|
|
|
|
0
|
push( @$memchords, @$p1 ); |
|
1149
|
0
|
0
|
|
|
|
0
|
if ( $config->{debug}->{chords} ) { |
|
1150
|
|
|
|
|
|
|
warn("Chord memorized for $cctag\[$memcrdinx]: ", |
|
1151
|
|
|
|
|
|
|
$_, "\n"), $memcrdinx++ |
|
1152
|
0
|
|
|
|
|
0
|
for @$p2, @$p1; |
|
1153
|
|
|
|
|
|
|
} |
|
1154
|
|
|
|
|
|
|
} |
|
1155
|
|
|
|
|
|
|
} |
|
1156
|
|
|
|
|
|
|
elsif ( $_ eq "/" ) { |
|
1157
|
0
|
|
|
|
|
0
|
$_ = { symbol => $_, class => "slash" }; |
|
1158
|
|
|
|
|
|
|
} |
|
1159
|
|
|
|
|
|
|
elsif ( $_ eq "." ) { |
|
1160
|
395
|
|
|
|
|
1306
|
$_ = { symbol => $_, class => "space" }; |
|
1161
|
395
|
|
|
|
|
619
|
$nbt++; |
|
1162
|
|
|
|
|
|
|
} |
|
1163
|
|
|
|
|
|
|
else { |
|
1164
|
|
|
|
|
|
|
# Multiple chords in a cell? |
|
1165
|
117
|
|
|
|
|
412
|
my @a = split( /~/, $_, -1 ); |
|
1166
|
117
|
100
|
|
|
|
298
|
if ( @a == 1) { |
|
1167
|
|
|
|
|
|
|
# Normal case, single chord. |
|
1168
|
116
|
|
|
|
|
307
|
$_ = { chord => $chord->($_), class => "chord" }; |
|
1169
|
|
|
|
|
|
|
} |
|
1170
|
|
|
|
|
|
|
else { |
|
1171
|
|
|
|
|
|
|
# Multiple chords. |
|
1172
|
|
|
|
|
|
|
$_ = { chords => |
|
1173
|
1
|
50
|
33
|
|
|
4
|
[ map { ( $_ eq '.' || $_ eq '' ) |
|
|
2
|
50
|
|
|
|
15
|
|
|
1174
|
|
|
|
|
|
|
? '' |
|
1175
|
|
|
|
|
|
|
: $_ eq "/" |
|
1176
|
|
|
|
|
|
|
? "/" |
|
1177
|
|
|
|
|
|
|
: $chord->($_) } @a ], |
|
1178
|
|
|
|
|
|
|
class => "chords" }; |
|
1179
|
|
|
|
|
|
|
} |
|
1180
|
117
|
50
|
33
|
|
|
533
|
if ( $memchords && !is_gridstrum($grid_type) ) { |
|
1181
|
117
|
|
|
|
|
266
|
@a = grep { !m;^[/.]?$; } @a; |
|
|
118
|
|
|
|
|
851
|
|
|
1182
|
117
|
|
|
|
|
296
|
push( @$memchords, @a ); |
|
1183
|
117
|
|
|
|
|
268
|
push( @$p0, @a ); |
|
1184
|
117
|
50
|
|
|
|
476
|
if ( $config->{debug}->{chords} ) { |
|
1185
|
|
|
|
|
|
|
warn("Chord memorized for $cctag\[$memcrdinx]: ", |
|
1186
|
|
|
|
|
|
|
$_, "\n"), $memcrdinx++ |
|
1187
|
0
|
|
|
|
|
0
|
for @a; |
|
1188
|
|
|
|
|
|
|
} |
|
1189
|
|
|
|
|
|
|
} |
|
1190
|
117
|
|
|
|
|
238
|
$nbt++; |
|
1191
|
|
|
|
|
|
|
} |
|
1192
|
663
|
100
|
|
|
|
1693
|
if ( $_->{class} eq "bar" ) { |
|
1193
|
151
|
|
|
|
|
255
|
$p2 = $p1; $p1 = $p0; undef $p0; |
|
|
151
|
|
|
|
|
262
|
|
|
|
151
|
|
|
|
|
362
|
|
|
1194
|
|
|
|
|
|
|
} |
|
1195
|
|
|
|
|
|
|
} |
|
1196
|
39
|
50
|
|
|
|
141
|
if ( $nbt > $grid_cells->[0] ) { |
|
1197
|
0
|
|
|
|
|
0
|
do_warn( "Too few cells for grid content" ); |
|
1198
|
|
|
|
|
|
|
} |
|
1199
|
39
|
50
|
|
|
|
656
|
return ( tokens => \@tokens, |
|
|
|
50
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
$grid_type == 1 ? ( type => "strumline" ) : (), |
|
1201
|
|
|
|
|
|
|
$grid_type == 2 ? ( type => "strumline", subtype => "cellbars" ) : (), |
|
1202
|
|
|
|
|
|
|
%res ); |
|
1203
|
|
|
|
|
|
|
} |
|
1204
|
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
################ Parsing directives ################ |
|
1206
|
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
my %directives = ( |
|
1208
|
|
|
|
|
|
|
chord => \&define_chord, |
|
1209
|
|
|
|
|
|
|
chorus => \&dir_chorus, |
|
1210
|
|
|
|
|
|
|
column_break => \&dir_column_break, |
|
1211
|
|
|
|
|
|
|
columns => \&dir_columns, |
|
1212
|
|
|
|
|
|
|
comment => \&dir_comment, |
|
1213
|
|
|
|
|
|
|
comment_box => \&dir_comment, |
|
1214
|
|
|
|
|
|
|
comment_italic => \&dir_comment, |
|
1215
|
|
|
|
|
|
|
define => \&define_chord, |
|
1216
|
|
|
|
|
|
|
diagrams => \&dir_diagrams, |
|
1217
|
|
|
|
|
|
|
end_of_bridge => undef, |
|
1218
|
|
|
|
|
|
|
end_of_chorus => undef, |
|
1219
|
|
|
|
|
|
|
end_of_grid => undef, |
|
1220
|
|
|
|
|
|
|
end_of_grille => undef, |
|
1221
|
|
|
|
|
|
|
end_of_tab => undef, |
|
1222
|
|
|
|
|
|
|
end_of_verse => undef, |
|
1223
|
|
|
|
|
|
|
grid => \&dir_grid, |
|
1224
|
|
|
|
|
|
|
highlight => \&dir_comment, |
|
1225
|
|
|
|
|
|
|
image => \&dir_image, |
|
1226
|
|
|
|
|
|
|
meta => \&dir_meta, |
|
1227
|
|
|
|
|
|
|
new_page => \&dir_new_page, |
|
1228
|
|
|
|
|
|
|
new_physical_page => \&dir_new_page, |
|
1229
|
|
|
|
|
|
|
new_song => \&dir_new_song, |
|
1230
|
|
|
|
|
|
|
no_grid => \&dir_no_grid, |
|
1231
|
|
|
|
|
|
|
pagesize => \&dir_papersize, |
|
1232
|
|
|
|
|
|
|
pagetype => \&dir_papersize, |
|
1233
|
|
|
|
|
|
|
start_of_bridge => undef, |
|
1234
|
|
|
|
|
|
|
start_of_chorus => undef, |
|
1235
|
|
|
|
|
|
|
start_of_grid => undef, |
|
1236
|
|
|
|
|
|
|
start_of_grille => undef, |
|
1237
|
|
|
|
|
|
|
start_of_tab => undef, |
|
1238
|
|
|
|
|
|
|
start_of_verse => undef, |
|
1239
|
|
|
|
|
|
|
subtitle => \&dir_subtitle, |
|
1240
|
|
|
|
|
|
|
title => \&dir_title, |
|
1241
|
|
|
|
|
|
|
titles => \&dir_titles, |
|
1242
|
|
|
|
|
|
|
transpose => \&dir_transpose, |
|
1243
|
|
|
|
|
|
|
); |
|
1244
|
|
|
|
|
|
|
# NOTE: Flex: start_of_... end_of_... x_... |
|
1245
|
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
my %abbrevs = ( |
|
1247
|
|
|
|
|
|
|
c => "comment", |
|
1248
|
|
|
|
|
|
|
cb => "comment_box", |
|
1249
|
|
|
|
|
|
|
cf => "chordfont", |
|
1250
|
|
|
|
|
|
|
ci => "comment_italic", |
|
1251
|
|
|
|
|
|
|
col => "colums", |
|
1252
|
|
|
|
|
|
|
colb => "column_break", |
|
1253
|
|
|
|
|
|
|
cs => "chordsize", |
|
1254
|
|
|
|
|
|
|
eob => "end_of_bridge", |
|
1255
|
|
|
|
|
|
|
eoc => "end_of_chorus", |
|
1256
|
|
|
|
|
|
|
eog => "end_of_grid", |
|
1257
|
|
|
|
|
|
|
eot => "end_of_tab", |
|
1258
|
|
|
|
|
|
|
eov => "end_of_verse", |
|
1259
|
|
|
|
|
|
|
g => "diagrams", |
|
1260
|
|
|
|
|
|
|
ng => "no_grid", |
|
1261
|
|
|
|
|
|
|
np => "new_page", |
|
1262
|
|
|
|
|
|
|
npp => "new_physical_page", |
|
1263
|
|
|
|
|
|
|
ns => "new_song", |
|
1264
|
|
|
|
|
|
|
sob => "start_of_bridge", |
|
1265
|
|
|
|
|
|
|
soc => "start_of_chorus", |
|
1266
|
|
|
|
|
|
|
sog => "start_of_grid", |
|
1267
|
|
|
|
|
|
|
sot => "start_of_tab", |
|
1268
|
|
|
|
|
|
|
sov => "start_of_verse", |
|
1269
|
|
|
|
|
|
|
st => "subtitle", |
|
1270
|
|
|
|
|
|
|
t => "title", |
|
1271
|
|
|
|
|
|
|
tf => "textfont", |
|
1272
|
|
|
|
|
|
|
ts => "textsize", |
|
1273
|
|
|
|
|
|
|
); |
|
1274
|
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
# Use by: runtimeinfo. |
|
1276
|
9
|
|
|
9
|
|
173
|
sub _directives { \%directives } |
|
1277
|
9
|
|
|
9
|
|
44
|
sub _directive_abbrevs { \%abbrevs } |
|
1278
|
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
my $dirpat; |
|
1280
|
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
sub parse_directive { |
|
1282
|
1772
|
|
|
1772
|
0
|
82837
|
my ( $self, $d ) = @_; |
|
1283
|
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
# Pattern for all recognized directives. |
|
1285
|
1772
|
100
|
|
|
|
5084
|
unless ( $dirpat ) { |
|
1286
|
|
|
|
|
|
|
$dirpat = |
|
1287
|
|
|
|
|
|
|
'(?:' . |
|
1288
|
|
|
|
|
|
|
join( '|', keys(%directives), |
|
1289
|
64
|
|
|
|
|
1007
|
@{$config->{metadata}->{keys}}, |
|
|
64
|
|
|
|
|
1945
|
|
|
1290
|
|
|
|
|
|
|
keys(%abbrevs), |
|
1291
|
|
|
|
|
|
|
'(?:start|end)_of_\w+', |
|
1292
|
|
|
|
|
|
|
"(?:$propitems_re". |
|
1293
|
|
|
|
|
|
|
'(?:font|size|colou?r))', |
|
1294
|
|
|
|
|
|
|
) . ')'; |
|
1295
|
64
|
|
|
|
|
30694
|
$dirpat = qr/$dirpat/; |
|
1296
|
|
|
|
|
|
|
} |
|
1297
|
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
# $d is the complete directive line, without leading/trailing { }. |
|
1299
|
1772
|
50
|
33
|
|
|
7443
|
if ( $options->{reference} and $d =~ s/^\s*:[: ]*//) { |
|
1300
|
0
|
|
|
|
|
0
|
do_warn("Incorrect start of directive (':' not allowed at start)"); |
|
1301
|
|
|
|
|
|
|
} |
|
1302
|
1772
|
|
|
|
|
5775
|
$d =~ s/^[: ]+//; |
|
1303
|
1772
|
|
|
|
|
5768
|
$d =~ s/\s+$//; |
|
1304
|
1772
|
|
|
|
|
4928
|
my $dir = lc($d); |
|
1305
|
1772
|
|
|
|
|
3167
|
my $arg = ""; |
|
1306
|
1772
|
100
|
|
|
|
8700
|
if ( $d =~ /^(.*?)([: ])\s*(.*)/ ) { |
|
1307
|
1129
|
|
|
|
|
5200
|
( $dir, $arg ) = ( lc($1), $3 ); |
|
1308
|
1129
|
50
|
|
|
|
3085
|
if ( $options->{reference} ) { |
|
1309
|
0
|
0
|
|
|
|
0
|
do_warn("Directive name must be followed by a ':'") |
|
1310
|
|
|
|
|
|
|
unless $2 eq ":"; |
|
1311
|
|
|
|
|
|
|
} |
|
1312
|
|
|
|
|
|
|
} |
|
1313
|
1772
|
|
|
|
|
4504
|
$dir =~ s/[: ]+$//; |
|
1314
|
|
|
|
|
|
|
# $dir is the lowcase directive name. |
|
1315
|
|
|
|
|
|
|
# $arg is the rest, if any. |
|
1316
|
|
|
|
|
|
|
|
|
1317
|
|
|
|
|
|
|
# Check for xxx-yyy selectors. |
|
1318
|
1772
|
100
|
|
|
|
38698
|
if ( $dir =~ /^($dirpat)-(.+)$/ ) { |
|
1319
|
229
|
|
66
|
|
|
1447
|
$dir = $abbrevs{$1} // $1; |
|
1320
|
229
|
100
|
|
|
|
830
|
unless ( $self->selected($2) ) { |
|
1321
|
113
|
100
|
|
|
|
227
|
if ( $dir =~ /^start_of_/ ) { |
|
1322
|
11
|
|
|
|
|
144
|
return { name => $dir, arg => $arg, omit => 2 }; |
|
1323
|
|
|
|
|
|
|
} |
|
1324
|
|
|
|
|
|
|
else { |
|
1325
|
102
|
|
|
|
|
521
|
return { name => $dir, arg => $arg, omit => 1 }; |
|
1326
|
|
|
|
|
|
|
} |
|
1327
|
|
|
|
|
|
|
} |
|
1328
|
|
|
|
|
|
|
} |
|
1329
|
|
|
|
|
|
|
else { |
|
1330
|
1543
|
|
66
|
|
|
7543
|
$dir = $abbrevs{$dir} // $dir; |
|
1331
|
|
|
|
|
|
|
} |
|
1332
|
|
|
|
|
|
|
|
|
1333
|
1659
|
50
|
100
|
|
|
7365
|
if ( $dir =~ /^start_of_(.*)/ |
|
|
|
|
66
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
&& exists $config->{delegates}->{$1} |
|
1335
|
|
|
|
|
|
|
&& $config->{delegates}->{$1}->{type} eq 'omit' ) { |
|
1336
|
0
|
|
|
|
|
0
|
return { name => $dir, arg => $arg, omit => 2 }; |
|
1337
|
|
|
|
|
|
|
} |
|
1338
|
|
|
|
|
|
|
|
|
1339
|
1659
|
|
|
|
|
10040
|
return { name => $dir, arg => $arg, omit => 0 } |
|
1340
|
|
|
|
|
|
|
} |
|
1341
|
|
|
|
|
|
|
|
|
1342
|
|
|
|
|
|
|
# Process a selector. |
|
1343
|
|
|
|
|
|
|
sub selected { |
|
1344
|
232
|
|
|
232
|
0
|
667
|
my ( $self, $sel ) = @_; |
|
1345
|
232
|
100
|
|
|
|
509
|
return 1 unless defined $sel; |
|
1346
|
229
|
|
|
|
|
521
|
my $negate = $sel =~ s/\!$//; |
|
1347
|
|
|
|
|
|
|
$sel = ( $sel eq lc($config->{instrument}->{type}) ) |
|
1348
|
|
|
|
|
|
|
|| |
|
1349
|
|
|
|
|
|
|
( $sel eq lc($config->{user}->{name}) |
|
1350
|
|
|
|
|
|
|
|| |
|
1351
|
229
|
|
100
|
|
|
2243
|
( $self->{meta}->{lc $sel} && is_true($self->{meta}->{lc $sel}->[0]) ) |
|
1352
|
|
|
|
|
|
|
); |
|
1353
|
229
|
100
|
|
|
|
528
|
$sel = !$sel if $negate; |
|
1354
|
229
|
|
|
|
|
727
|
return $sel; |
|
1355
|
|
|
|
|
|
|
} |
|
1356
|
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
sub directive { |
|
1358
|
1204
|
|
|
1204
|
0
|
2987
|
my ( $self, $d ) = @_; |
|
1359
|
|
|
|
|
|
|
|
|
1360
|
1204
|
|
|
|
|
3767
|
my $dd = $self->parse_directive($d); |
|
1361
|
1204
|
100
|
|
|
|
4141
|
return 1 if $dd->{omit} == 1; |
|
1362
|
|
|
|
|
|
|
|
|
1363
|
1202
|
|
|
|
|
2679
|
my $dir = $dd->{name}; |
|
1364
|
1202
|
|
|
|
|
2464
|
my $arg = $dd->{arg}; |
|
1365
|
1202
|
100
|
|
|
|
3263
|
if ( $arg ne "" ) { |
|
1366
|
948
|
|
|
|
|
4622
|
$arg = fmt_subst( $self, $arg ); |
|
1367
|
948
|
50
|
|
|
|
146476
|
if ( $arg !~ /\S/ ) { # expansion yields empty |
|
1368
|
0
|
0
|
|
|
|
0
|
if ( $dir =~ /^start_of_/ ) { |
|
1369
|
0
|
|
|
|
|
0
|
$dd->{omit} = 2; |
|
1370
|
|
|
|
|
|
|
} |
|
1371
|
|
|
|
|
|
|
else { |
|
1372
|
0
|
|
|
|
|
0
|
return 1; |
|
1373
|
|
|
|
|
|
|
} |
|
1374
|
|
|
|
|
|
|
} |
|
1375
|
|
|
|
|
|
|
} |
|
1376
|
|
|
|
|
|
|
|
|
1377
|
1202
|
100
|
|
|
|
5285
|
if ( $directives{$dir} ) { |
|
1378
|
688
|
|
|
|
|
3654
|
return $directives{$dir}->( $self, $dir, $arg, $dd->{arg} ); |
|
1379
|
|
|
|
|
|
|
} |
|
1380
|
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
# Context flags. |
|
1382
|
|
|
|
|
|
|
|
|
1383
|
514
|
100
|
|
|
|
1870
|
if ( $dir =~ /^start_of_(\w+)$/ ) { |
|
1384
|
97
|
50
|
|
|
|
316
|
do_warn("Already in " . ucfirst($in_context) . " context\n") |
|
1385
|
|
|
|
|
|
|
if $in_context; |
|
1386
|
97
|
|
|
|
|
275
|
$in_context = $1; |
|
1387
|
97
|
100
|
|
|
|
382
|
if ( $dd->{omit} ) { |
|
1388
|
2
|
|
|
|
|
6
|
$skip_context = 1; |
|
1389
|
|
|
|
|
|
|
# warn("Skipping context: $in_context\n"); |
|
1390
|
2
|
|
|
|
|
11
|
return 1; |
|
1391
|
|
|
|
|
|
|
} |
|
1392
|
95
|
100
|
|
|
|
412
|
@chorus = (), $chorus_xpose = $chorus_xpose_dir = 0 |
|
1393
|
|
|
|
|
|
|
if $in_context eq "chorus"; |
|
1394
|
95
|
|
|
|
|
287
|
undef $cctag; |
|
1395
|
|
|
|
|
|
|
|
|
1396
|
95
|
100
|
33
|
|
|
828
|
if ( $in_context eq "grid" |
|
|
|
100
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
|| ( $in_context eq "grille" && !exists $config->{delegates}->{$in_context} ) ) { |
|
1398
|
26
|
|
|
|
|
61
|
$cctag = $in_context; |
|
1399
|
26
|
|
|
|
|
152
|
my $kv = parse_kv( $arg, "shape" ); |
|
1400
|
26
|
|
50
|
|
|
103
|
my $shape = $kv->{shape} // ""; |
|
1401
|
26
|
50
|
|
|
|
297
|
if ( $in_context eq "grille" ) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1402
|
|
|
|
|
|
|
} |
|
1403
|
|
|
|
|
|
|
elsif ( $shape eq "" ) { |
|
1404
|
4
|
|
|
|
|
34
|
$self->add( type => "set", |
|
1405
|
|
|
|
|
|
|
name => "gridparams", |
|
1406
|
|
|
|
|
|
|
value => [ @$grid_arg[0..3] ] ); |
|
1407
|
|
|
|
|
|
|
} |
|
1408
|
|
|
|
|
|
|
elsif ( $shape =~ m/^ |
|
1409
|
|
|
|
|
|
|
(?: (\d+) \+)? |
|
1410
|
|
|
|
|
|
|
(\d+) (?: x (\d+) )? |
|
1411
|
|
|
|
|
|
|
(?:\+ (\d+) )? |
|
1412
|
|
|
|
|
|
|
(?:[:\s+] (.*)? )? $/x ) { |
|
1413
|
22
|
50
|
|
|
|
98
|
do_warn("Invalid grid params: $shape (must be non-zero)"), return |
|
1414
|
|
|
|
|
|
|
unless $2; |
|
1415
|
22
|
|
50
|
|
|
293
|
$grid_arg = [ $2, $3//1, $1//0, $4//0 ]; |
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1416
|
22
|
50
|
|
|
|
94
|
push( @$grid_arg, $5 ) if defined $5; |
|
1417
|
22
|
|
|
|
|
148
|
$self->add( type => "set", |
|
1418
|
|
|
|
|
|
|
name => "gridparams", |
|
1419
|
|
|
|
|
|
|
value => [ @$grid_arg ] ); |
|
1420
|
22
|
50
|
|
|
|
70
|
push( @labels, $5 ) if defined($5); |
|
1421
|
|
|
|
|
|
|
} |
|
1422
|
|
|
|
|
|
|
elsif ( $shape ne "" ) { |
|
1423
|
0
|
|
|
|
|
0
|
$self->add( type => "set", |
|
1424
|
|
|
|
|
|
|
name => "gridparams", |
|
1425
|
|
|
|
|
|
|
value => [ @$grid_arg[0..3], $shape ] ); |
|
1426
|
0
|
|
|
|
|
0
|
push( @labels, $shape ); |
|
1427
|
|
|
|
|
|
|
} |
|
1428
|
26
|
50
|
50
|
|
|
263
|
if ( ($kv->{label}//"") ne "" ) { |
|
1429
|
|
|
|
|
|
|
$self->add( type => "set", |
|
1430
|
|
|
|
|
|
|
name => "label", |
|
1431
|
0
|
|
|
|
|
0
|
value => $kv->{label} ); |
|
1432
|
0
|
|
|
|
|
0
|
push( @labels, $kv->{label} ); |
|
1433
|
|
|
|
|
|
|
} |
|
1434
|
|
|
|
|
|
|
|
|
1435
|
|
|
|
|
|
|
# Grid sections always memorize unless "cc=". |
|
1436
|
26
|
50
|
50
|
|
|
199
|
if ( ($kv->{cc}//="grid") ne "" ) { |
|
1437
|
26
|
|
|
|
|
67
|
$cctag = $kv->{cc}; |
|
1438
|
26
|
|
100
|
|
|
147
|
$memchords = $memchords{$cctag} //= []; |
|
1439
|
26
|
|
|
|
|
66
|
$memcrdinx = 0; |
|
1440
|
26
|
|
|
|
|
51
|
$memorizing = 1; |
|
1441
|
|
|
|
|
|
|
} |
|
1442
|
26
|
|
|
|
|
163
|
$grid_cells = [ $grid_arg->[0] * $grid_arg->[1], |
|
1443
|
|
|
|
|
|
|
$grid_arg->[2], $grid_arg->[3] ]; |
|
1444
|
|
|
|
|
|
|
|
|
1445
|
26
|
50
|
|
|
|
83
|
@grille = ( $kv ) if $in_context eq "grille"; |
|
1446
|
26
|
|
|
|
|
197
|
return 1; |
|
1447
|
|
|
|
|
|
|
} |
|
1448
|
|
|
|
|
|
|
elsif ( exists $config->{delegates}->{$in_context} ) { |
|
1449
|
2
|
|
|
|
|
6
|
my $d = $config->{delegates}->{$in_context}; |
|
1450
|
2
|
|
|
|
|
5
|
my %opts; |
|
1451
|
2
|
50
|
33
|
|
|
15
|
if ( $xpose || $config->{settings}->{transpose} ) { |
|
1452
|
|
|
|
|
|
|
$opts{transpose} = |
|
1453
|
0
|
|
0
|
|
|
0
|
$xpose + ($config->{settings}->{transpose}//0 ); |
|
1454
|
|
|
|
|
|
|
} |
|
1455
|
2
|
|
|
|
|
10
|
my $kv = parse_kv( $arg, "label" ); |
|
1456
|
2
|
50
|
50
|
|
|
12
|
delete $kv->{label} if ($kv->{label}//"") eq ""; |
|
1457
|
|
|
|
|
|
|
$self->add( type => "image", |
|
1458
|
|
|
|
|
|
|
subtype => "delegate", |
|
1459
|
|
|
|
|
|
|
delegate => $d->{module}, |
|
1460
|
|
|
|
|
|
|
handler => $d->{handler}, |
|
1461
|
|
|
|
|
|
|
data => [ ], |
|
1462
|
|
|
|
|
|
|
opts => { %opts, %$kv }, |
|
1463
|
2
|
50
|
|
|
|
19
|
exists($kv->{id}) ? ( id => $kv->{id} ) : (), |
|
1464
|
|
|
|
|
|
|
open => 1 ); |
|
1465
|
2
|
50
|
|
|
|
11
|
push( @labels, $kv->{label} ) if exists $kv->{label}; |
|
1466
|
|
|
|
|
|
|
} |
|
1467
|
|
|
|
|
|
|
elsif ( $arg ne "" ) { |
|
1468
|
2
|
|
|
|
|
9
|
my $kv = parse_kv( $arg, "label" ); |
|
1469
|
2
|
|
|
|
|
4
|
my $label = delete $kv->{label}; |
|
1470
|
2
|
|
|
|
|
5
|
my $chords = delete $kv->{cc}; |
|
1471
|
2
|
50
|
|
|
|
6
|
if ( %$kv ) { |
|
|
|
50
|
|
|
|
|
|
|
1472
|
|
|
|
|
|
|
# Assume a mistake. |
|
1473
|
0
|
|
|
|
|
0
|
do_warn("Garbage in start_of_$in_context: $arg (ignored)\n"); |
|
1474
|
|
|
|
|
|
|
} |
|
1475
|
|
|
|
|
|
|
elsif ( $label ) { |
|
1476
|
2
|
|
|
|
|
10
|
$self->add( type => "set", |
|
1477
|
|
|
|
|
|
|
name => "label", |
|
1478
|
|
|
|
|
|
|
value => $label ); |
|
1479
|
|
|
|
|
|
|
push( @labels, $label) |
|
1480
|
|
|
|
|
|
|
unless $in_context eq "chorus" |
|
1481
|
2
|
50
|
33
|
|
|
8
|
&& !$config->{settings}->{choruslabels}; |
|
1482
|
|
|
|
|
|
|
} |
|
1483
|
2
|
50
|
|
|
|
6
|
if ( $chords ) { |
|
1484
|
0
|
|
|
|
|
0
|
$chords =~ s/^\s*(.*)\s*/$1/; |
|
1485
|
0
|
|
|
|
|
0
|
$cctag = $in_context; |
|
1486
|
|
|
|
|
|
|
# Do we have a name? Chords? Both? |
|
1487
|
|
|
|
|
|
|
# name:C D E |
|
1488
|
|
|
|
|
|
|
# :C D E |
|
1489
|
|
|
|
|
|
|
# : |
|
1490
|
0
|
0
|
|
|
|
0
|
if ( $chords =~ /^(\w*):(.*)/ ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1491
|
|
|
|
|
|
|
# Name, possibly empty. |
|
1492
|
0
|
0
|
|
|
|
0
|
$cctag = $1 if length($1); |
|
1493
|
|
|
|
|
|
|
# Chords, possibly empty. |
|
1494
|
0
|
|
|
|
|
0
|
$chords = $2; |
|
1495
|
|
|
|
|
|
|
} |
|
1496
|
|
|
|
|
|
|
# C D E |
|
1497
|
|
|
|
|
|
|
elsif ( $chords =~ /\s/ ) { |
|
1498
|
|
|
|
|
|
|
# Whitespace separated chords. |
|
1499
|
|
|
|
|
|
|
} |
|
1500
|
|
|
|
|
|
|
# name |
|
1501
|
|
|
|
|
|
|
elsif ( $chords =~ /^\w+$/ ) { |
|
1502
|
0
|
|
|
|
|
0
|
$cctag = $chords; |
|
1503
|
0
|
|
|
|
|
0
|
$chords = ""; |
|
1504
|
|
|
|
|
|
|
} |
|
1505
|
|
|
|
|
|
|
# ??? |
|
1506
|
|
|
|
|
|
|
else { |
|
1507
|
0
|
0
|
|
|
|
0
|
warn("Unrecognized cc value: \"$chords\"\n") |
|
1508
|
|
|
|
|
|
|
if $chords; |
|
1509
|
0
|
|
|
|
|
0
|
$chords = ""; |
|
1510
|
|
|
|
|
|
|
} |
|
1511
|
0
|
0
|
|
|
|
0
|
if ( $chords ne "" ) { |
|
1512
|
0
|
|
|
|
|
0
|
$memchords = [ split( ' ', $chords ) ]; |
|
1513
|
0
|
|
|
|
|
0
|
$memchords{$cctag} = $memchords; |
|
1514
|
0
|
|
|
|
|
0
|
$memcrdinx = 0; |
|
1515
|
0
|
|
|
|
|
0
|
$memorizing = 0; |
|
1516
|
0
|
0
|
|
|
|
0
|
if ( $config->{debug}->{chords} ) { |
|
1517
|
0
|
|
|
|
|
0
|
my $i = 0; |
|
1518
|
|
|
|
|
|
|
warn("Chord memorized for $cctag\[$i]: ", |
|
1519
|
|
|
|
|
|
|
$_, "\n"), $i++ |
|
1520
|
0
|
|
|
|
|
0
|
for @$memchords; |
|
1521
|
|
|
|
|
|
|
} |
|
1522
|
0
|
|
|
|
|
0
|
return 1; |
|
1523
|
|
|
|
|
|
|
} |
|
1524
|
|
|
|
|
|
|
} |
|
1525
|
|
|
|
|
|
|
} |
|
1526
|
|
|
|
|
|
|
|
|
1527
|
|
|
|
|
|
|
# Enabling this always would allow [^] to recall anyway. |
|
1528
|
|
|
|
|
|
|
# Feature? |
|
1529
|
69
|
|
|
|
|
130
|
if ( 1 || $config->{settings}->{memorize} ) { |
|
1530
|
69
|
|
33
|
|
|
569
|
$memchords = ($memchords{$cctag//$in_context} //= []); |
|
|
|
|
100
|
|
|
|
|
|
1531
|
69
|
|
|
|
|
202
|
$memcrdinx = 0; |
|
1532
|
69
|
|
|
|
|
146
|
$memorizing = 0; |
|
1533
|
|
|
|
|
|
|
} |
|
1534
|
69
|
|
|
|
|
352
|
return 1; |
|
1535
|
|
|
|
|
|
|
} |
|
1536
|
|
|
|
|
|
|
|
|
1537
|
417
|
100
|
|
|
|
1649
|
if ( $dir =~ /^end_of_(\w+)$/ ) { |
|
1538
|
95
|
50
|
|
|
|
391
|
do_warn("Not in " . ucfirst($1) . " context\n") |
|
1539
|
|
|
|
|
|
|
unless $in_context eq $1; |
|
1540
|
95
|
|
|
|
|
206
|
$grid_type = 0; |
|
1541
|
95
|
50
|
33
|
|
|
424
|
if ( $in_context eq "grille" && @grille > 1 ) { |
|
1542
|
0
|
|
|
|
|
0
|
my $opts = shift(@grille); |
|
1543
|
0
|
|
|
|
|
0
|
my $id = $opts->{id}; |
|
1544
|
0
|
0
|
|
|
|
0
|
unless ( is_true($opts->{omit}) ) { |
|
1545
|
0
|
0
|
0
|
|
|
0
|
if ( $opts->{align} && $opts->{x} && $opts->{x} =~ /\%$/ ) { |
|
|
|
|
0
|
|
|
|
|
|
1546
|
0
|
|
|
|
|
0
|
do_warn( "Useless combination of x percentage with align (align ignored)" ); |
|
1547
|
0
|
|
|
|
|
0
|
delete $opts->{align}; |
|
1548
|
|
|
|
|
|
|
} |
|
1549
|
|
|
|
|
|
|
|
|
1550
|
0
|
|
|
|
|
0
|
my $def = !!$id; |
|
1551
|
0
|
|
0
|
|
|
0
|
$id //= "_Image".$assetid++; |
|
1552
|
|
|
|
|
|
|
|
|
1553
|
0
|
0
|
|
|
|
0
|
if ( defined $opts->{spread} ) { |
|
1554
|
0
|
|
|
|
|
0
|
$def++; |
|
1555
|
0
|
0
|
|
|
|
0
|
if ( exists $self->{spreadimage} ) { |
|
1556
|
0
|
|
|
|
|
0
|
do_warn("Skipping superfluous spread image"); |
|
1557
|
|
|
|
|
|
|
} |
|
1558
|
|
|
|
|
|
|
else { |
|
1559
|
|
|
|
|
|
|
$self->{spreadimage} = |
|
1560
|
0
|
|
|
|
|
0
|
{ id => $id, space => $opts->{spread} }; |
|
1561
|
|
|
|
|
|
|
warn("Got spread image $id with space=$opts->{spread}\n") |
|
1562
|
0
|
0
|
|
|
|
0
|
if $config->{debug}->{images}; |
|
1563
|
|
|
|
|
|
|
} |
|
1564
|
|
|
|
|
|
|
} |
|
1565
|
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
# Move to assets. |
|
1567
|
|
|
|
|
|
|
$self->{assets}->{$id} = |
|
1568
|
|
|
|
|
|
|
{ type => "image", |
|
1569
|
|
|
|
|
|
|
subtype => "delegate", |
|
1570
|
|
|
|
|
|
|
delegate => "Grille", |
|
1571
|
|
|
|
|
|
|
handler => "grille2xo", |
|
1572
|
|
|
|
|
|
|
opts => $opts, |
|
1573
|
|
|
|
|
|
|
line => $grille[0]{line}, |
|
1574
|
0
|
|
|
|
|
0
|
data => \@grille, |
|
1575
|
|
|
|
|
|
|
context => $in_context, |
|
1576
|
|
|
|
|
|
|
}; |
|
1577
|
0
|
0
|
|
|
|
0
|
if ( $def ) { |
|
1578
|
0
|
|
|
|
|
0
|
my $label = delete $a->{label}; |
|
1579
|
0
|
0
|
|
|
|
0
|
do_warn("Label \"$label\" ignored on non-displaying $in_context section\n") |
|
1580
|
|
|
|
|
|
|
if $label; |
|
1581
|
|
|
|
|
|
|
} |
|
1582
|
|
|
|
|
|
|
else { |
|
1583
|
0
|
|
|
|
|
0
|
my $label = delete $opts->{label}; |
|
1584
|
0
|
0
|
0
|
|
|
0
|
$self->add( type => "set", |
|
1585
|
|
|
|
|
|
|
name => "label", |
|
1586
|
|
|
|
|
|
|
value => $label ) |
|
1587
|
|
|
|
|
|
|
if $label && $label ne ""; |
|
1588
|
0
|
|
|
|
|
0
|
$self->add( type => "image", |
|
1589
|
|
|
|
|
|
|
opts => $opts, |
|
1590
|
|
|
|
|
|
|
id => $id ); |
|
1591
|
0
|
0
|
|
|
|
0
|
if ( $opts->{label} ) { |
|
1592
|
|
|
|
|
|
|
push( @labels, $opts->{label} ) |
|
1593
|
|
|
|
|
|
|
unless $in_context eq "chorus" |
|
1594
|
0
|
0
|
0
|
|
|
0
|
&& !$config->{settings}->{choruslabels}; |
|
1595
|
|
|
|
|
|
|
} |
|
1596
|
|
|
|
|
|
|
} |
|
1597
|
|
|
|
|
|
|
} |
|
1598
|
|
|
|
|
|
|
} |
|
1599
|
|
|
|
|
|
|
else { |
|
1600
|
95
|
|
|
|
|
358
|
$self->add( type => "set", |
|
1601
|
|
|
|
|
|
|
name => "context", |
|
1602
|
|
|
|
|
|
|
value => $def_context ); |
|
1603
|
|
|
|
|
|
|
} |
|
1604
|
95
|
|
|
|
|
204
|
$in_context = $def_context; |
|
1605
|
95
|
|
|
|
|
219
|
undef $memchords; |
|
1606
|
95
|
|
|
|
|
444
|
return 1; |
|
1607
|
|
|
|
|
|
|
} |
|
1608
|
|
|
|
|
|
|
|
|
1609
|
|
|
|
|
|
|
# Metadata extensions (legacy). Should use meta instead. |
|
1610
|
|
|
|
|
|
|
# Only accept the list from config. |
|
1611
|
322
|
100
|
|
3508
|
|
1877
|
if ( any { $_ eq $dir } @{ $config->{metadata}->{keys} } ) { |
|
|
3508
|
|
|
|
|
5427
|
|
|
|
322
|
|
|
|
|
2254
|
|
|
1612
|
238
|
|
|
|
|
1460
|
return $self->dir_meta( "meta", "$dir $arg" ); |
|
1613
|
|
|
|
|
|
|
} |
|
1614
|
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
# Formatting. {chordsize XX} and such. |
|
1616
|
84
|
100
|
|
|
|
1433
|
if ( $dir =~ m/ ^( $propitems_re ) |
|
1617
|
|
|
|
|
|
|
( font | size | colou?r ) |
|
1618
|
|
|
|
|
|
|
$/x ) { |
|
1619
|
72
|
|
|
|
|
211
|
my $item = $1; |
|
1620
|
72
|
|
|
|
|
129
|
my $prop = $2; |
|
1621
|
|
|
|
|
|
|
|
|
1622
|
72
|
|
|
|
|
301
|
$self->propset( $item, $prop, $arg ); |
|
1623
|
|
|
|
|
|
|
|
|
1624
|
|
|
|
|
|
|
# Derived props. |
|
1625
|
72
|
100
|
|
|
|
171
|
$self->propset( "chorus", $prop, $arg ) if $item eq "text"; |
|
1626
|
|
|
|
|
|
|
|
|
1627
|
|
|
|
|
|
|
# ::dump( { %propstack, line => $diag->{line} } ); |
|
1628
|
72
|
|
|
|
|
341
|
return 1; |
|
1629
|
|
|
|
|
|
|
} |
|
1630
|
|
|
|
|
|
|
# More private hacks. |
|
1631
|
12
|
50
|
33
|
|
|
200
|
if ( !$options->{reference} && $d =~ /^([-+])([-\w.]+)$/i ) { |
|
1632
|
0
|
0
|
|
|
|
0
|
if ( $2 eq "dumpmeta" ) { |
|
1633
|
0
|
|
|
|
|
0
|
warn(::dump($self->{meta})); |
|
1634
|
|
|
|
|
|
|
} |
|
1635
|
0
|
0
|
|
|
|
0
|
$self->add( type => "set", |
|
1636
|
|
|
|
|
|
|
name => $2, |
|
1637
|
|
|
|
|
|
|
value => $1 eq "+" ? 1 : 0, |
|
1638
|
|
|
|
|
|
|
); |
|
1639
|
0
|
|
|
|
|
0
|
return 1; |
|
1640
|
|
|
|
|
|
|
} |
|
1641
|
|
|
|
|
|
|
|
|
1642
|
12
|
100
|
66
|
|
|
149
|
if ( !$options->{reference} && $dir =~ /^\+([-\w.]+(?:\.[<>])?)$/ ) { |
|
1643
|
11
|
|
|
|
|
79
|
$self->add( type => "set", |
|
1644
|
|
|
|
|
|
|
name => $1, |
|
1645
|
|
|
|
|
|
|
value => $arg, |
|
1646
|
|
|
|
|
|
|
); |
|
1647
|
|
|
|
|
|
|
|
|
1648
|
11
|
|
|
|
|
78
|
$config->unlock; |
|
1649
|
11
|
|
|
|
|
25761
|
prpadd2cfg( $config, $1 => $arg ); |
|
1650
|
11
|
|
|
|
|
70
|
$config->lock; |
|
1651
|
|
|
|
|
|
|
|
|
1652
|
11
|
|
|
|
|
28360
|
upd_config(); |
|
1653
|
|
|
|
|
|
|
|
|
1654
|
11
|
|
|
|
|
102
|
return 1; |
|
1655
|
|
|
|
|
|
|
} |
|
1656
|
|
|
|
|
|
|
|
|
1657
|
|
|
|
|
|
|
# Warn about unknowns, unless they are x_... form. |
|
1658
|
|
|
|
|
|
|
do_warn("Unknown directive: $d\n") |
|
1659
|
1
|
50
|
33
|
|
|
4
|
if $config->{settings}->{strict} && $d !~ /^x_/; |
|
1660
|
1
|
|
|
|
|
22
|
return; |
|
1661
|
|
|
|
|
|
|
} |
|
1662
|
|
|
|
|
|
|
|
|
1663
|
|
|
|
|
|
|
sub dir_chorus { |
|
1664
|
31
|
|
|
31
|
0
|
128
|
my ( $self, $dir, $arg ) = @_; |
|
1665
|
|
|
|
|
|
|
|
|
1666
|
31
|
50
|
|
|
|
105
|
if ( $in_context ) { |
|
1667
|
0
|
|
|
|
|
0
|
do_warn("{chorus} encountered while in $in_context context -- ignored\n"); |
|
1668
|
0
|
|
|
|
|
0
|
return 1; |
|
1669
|
|
|
|
|
|
|
} |
|
1670
|
|
|
|
|
|
|
|
|
1671
|
|
|
|
|
|
|
# Clone the chorus so we can modify the label, if required. |
|
1672
|
31
|
100
|
|
|
|
7209
|
my $chorus = @chorus ? dclone(\@chorus) : []; |
|
1673
|
|
|
|
|
|
|
|
|
1674
|
31
|
50
|
66
|
|
|
58506
|
if ( @$chorus && $arg && $arg ne "" ) { |
|
|
|
|
33
|
|
|
|
|
|
1675
|
0
|
|
|
|
|
0
|
my $kv = parse_kv( $arg, "label" ); |
|
1676
|
0
|
|
|
|
|
0
|
my $label = $kv->{label}; |
|
1677
|
0
|
0
|
0
|
|
|
0
|
if ( $chorus->[0]->{type} eq "set" && $chorus->[0]->{name} eq "label" ) { |
|
|
|
0
|
|
|
|
|
|
|
1678
|
0
|
|
|
|
|
0
|
$chorus->[0]->{value} = $label; |
|
1679
|
|
|
|
|
|
|
} |
|
1680
|
|
|
|
|
|
|
elsif ( defined $label ) { |
|
1681
|
0
|
|
|
|
|
0
|
unshift( @$chorus, |
|
1682
|
|
|
|
|
|
|
{ type => "set", |
|
1683
|
|
|
|
|
|
|
name => "label", |
|
1684
|
|
|
|
|
|
|
value => $label, |
|
1685
|
|
|
|
|
|
|
context => "chorus", |
|
1686
|
|
|
|
|
|
|
} ); |
|
1687
|
|
|
|
|
|
|
} |
|
1688
|
|
|
|
|
|
|
push( @labels, $label ) |
|
1689
|
0
|
0
|
|
|
|
0
|
if $config->{settings}->{choruslabels}; |
|
1690
|
|
|
|
|
|
|
} |
|
1691
|
|
|
|
|
|
|
|
|
1692
|
31
|
100
|
|
|
|
160
|
if ( $chorus_xpose != ( my $xp = $xpose ) ) { |
|
1693
|
17
|
|
|
|
|
54
|
$xp -= $chorus_xpose; |
|
1694
|
17
|
|
|
|
|
64
|
for ( @$chorus ) { |
|
1695
|
32
|
100
|
|
|
|
165
|
if ( $_->{type} eq "songline" ) { |
|
1696
|
16
|
|
|
|
|
36
|
for ( @{ $_->{chords} } ) { |
|
|
16
|
|
|
|
|
68
|
|
|
1697
|
61
|
100
|
|
|
|
218
|
next if $_ eq ''; |
|
1698
|
46
|
|
|
|
|
214
|
my $info = $self->{chordsinfo}->{$_->key}; |
|
1699
|
46
|
50
|
|
|
|
195
|
next if $info->is_annotation; |
|
1700
|
46
|
50
|
|
|
|
228
|
$info = $info->transpose($xp, $xpose <=> 0) if $xp; |
|
1701
|
46
|
|
|
|
|
196
|
$info = $info->new($info); |
|
1702
|
46
|
|
|
|
|
226
|
$_ = ChordPro::Chords::Appearance->new |
|
1703
|
|
|
|
|
|
|
( key => $self->add_chord($info), |
|
1704
|
|
|
|
|
|
|
info => $info, |
|
1705
|
|
|
|
|
|
|
maybe format => $_->format |
|
1706
|
|
|
|
|
|
|
); |
|
1707
|
|
|
|
|
|
|
} |
|
1708
|
|
|
|
|
|
|
} |
|
1709
|
|
|
|
|
|
|
} |
|
1710
|
|
|
|
|
|
|
} |
|
1711
|
|
|
|
|
|
|
|
|
1712
|
31
|
100
|
|
|
|
239
|
$self->add( type => "rechorus", |
|
1713
|
|
|
|
|
|
|
@$chorus |
|
1714
|
|
|
|
|
|
|
? ( "chorus" => $chorus ) |
|
1715
|
|
|
|
|
|
|
: (), |
|
1716
|
|
|
|
|
|
|
); |
|
1717
|
31
|
|
|
|
|
338
|
return 1; |
|
1718
|
|
|
|
|
|
|
} |
|
1719
|
|
|
|
|
|
|
|
|
1720
|
|
|
|
|
|
|
#### Directive handlers #### |
|
1721
|
|
|
|
|
|
|
|
|
1722
|
|
|
|
|
|
|
# Song settings. |
|
1723
|
|
|
|
|
|
|
|
|
1724
|
|
|
|
|
|
|
# Breaks. |
|
1725
|
|
|
|
|
|
|
|
|
1726
|
|
|
|
|
|
|
sub dir_column_break { |
|
1727
|
15
|
|
|
15
|
0
|
60
|
my ( $self, $dir, $arg ) = @_; |
|
1728
|
15
|
|
|
|
|
81
|
$self->add( type => "colb" ); |
|
1729
|
15
|
|
|
|
|
79
|
return 1; |
|
1730
|
|
|
|
|
|
|
} |
|
1731
|
|
|
|
|
|
|
|
|
1732
|
|
|
|
|
|
|
sub dir_new_page { |
|
1733
|
20
|
|
|
20
|
0
|
76
|
my ( $self, $dir, $arg ) = @_; |
|
1734
|
20
|
|
|
|
|
88
|
$self->add( type => "newpage" ); |
|
1735
|
20
|
|
|
|
|
121
|
return 1; |
|
1736
|
|
|
|
|
|
|
} |
|
1737
|
|
|
|
|
|
|
|
|
1738
|
|
|
|
|
|
|
sub dir_new_song { |
|
1739
|
0
|
|
|
0
|
0
|
0
|
my ( $self, $dir, $arg ) = @_; |
|
1740
|
0
|
|
|
|
|
0
|
die("FATAL - cannot start a new song now\n"); |
|
1741
|
|
|
|
|
|
|
} |
|
1742
|
|
|
|
|
|
|
|
|
1743
|
|
|
|
|
|
|
# Comments. Strictly speaking they do not belong here. |
|
1744
|
|
|
|
|
|
|
|
|
1745
|
|
|
|
|
|
|
sub dir_comment { |
|
1746
|
134
|
|
|
134
|
0
|
524
|
my ( $self, $dir, $arg, $orig ) = @_; |
|
1747
|
134
|
100
|
|
|
|
494
|
$dir = "comment" if $dir eq "highlight"; |
|
1748
|
134
|
|
|
|
|
1033
|
my %res = $self->cdecompose($arg); |
|
1749
|
134
|
|
|
|
|
477
|
$res{orig} = $orig; |
|
1750
|
|
|
|
|
|
|
$self->add( type => $dir, %res ) |
|
1751
|
134
|
50
|
66
|
|
|
1701
|
unless exists($res{text}) && $res{text} =~ /^[ \t]*$/; |
|
1752
|
134
|
|
|
|
|
1056
|
return 1; |
|
1753
|
|
|
|
|
|
|
} |
|
1754
|
|
|
|
|
|
|
|
|
1755
|
|
|
|
|
|
|
sub dir_image { |
|
1756
|
7
|
|
|
7
|
0
|
29
|
my ( $self, $dir, $arg ) = @_; |
|
1757
|
7
|
100
|
66
|
|
|
54
|
return 1 if $::running_under_test && !$arg; |
|
1758
|
90
|
|
|
90
|
|
1550
|
use Text::ParseWords qw(quotewords); |
|
|
90
|
|
|
|
|
244
|
|
|
|
90
|
|
|
|
|
1761781
|
|
|
1759
|
6
|
|
|
|
|
35
|
my @words = quotewords( '\s+', 1, $arg ); |
|
1760
|
6
|
|
|
|
|
1532
|
my $res; |
|
1761
|
|
|
|
|
|
|
# Imply src= if word 0 is not kv. |
|
1762
|
6
|
100
|
66
|
|
|
90
|
if ( @words && $words[0] !~ /\w+=/ ) { |
|
1763
|
2
|
|
|
|
|
6
|
$words[0] = "src=" . $words[0]; |
|
1764
|
2
|
|
|
|
|
10
|
$res = parse_kv( \@words ); |
|
1765
|
|
|
|
|
|
|
} |
|
1766
|
|
|
|
|
|
|
else { |
|
1767
|
4
|
|
|
|
|
24
|
$res = parse_kv( \@words, "src" ); |
|
1768
|
|
|
|
|
|
|
} |
|
1769
|
|
|
|
|
|
|
|
|
1770
|
6
|
|
|
|
|
33
|
my $uri; |
|
1771
|
|
|
|
|
|
|
my $id; |
|
1772
|
6
|
|
|
|
|
0
|
my $chord; |
|
1773
|
6
|
|
|
|
|
0
|
my $type; |
|
1774
|
6
|
|
|
|
|
0
|
my %opts; |
|
1775
|
6
|
|
|
|
|
35
|
while ( my($k,$v) = each(%$res) ) { |
|
1776
|
20
|
100
|
66
|
|
|
332
|
if ( $k =~ /^(title)$/i && $v ne "" ) { |
|
|
|
100
|
66
|
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
|
50
|
66
|
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
|
|
50
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1777
|
1
|
|
|
|
|
4
|
$opts{lc($k)} = $v; |
|
1778
|
|
|
|
|
|
|
} |
|
1779
|
|
|
|
|
|
|
elsif ( $k =~ /^(border|spread|center|persist|omit)$/i |
|
1780
|
|
|
|
|
|
|
&& $v =~ /^(\d+)$/ ) { |
|
1781
|
4
|
100
|
66
|
|
|
15
|
if ( $k eq "center" && $v ) { |
|
1782
|
1
|
|
|
|
|
4
|
$opts{align} = $k; |
|
1783
|
|
|
|
|
|
|
} |
|
1784
|
|
|
|
|
|
|
else { |
|
1785
|
3
|
|
|
|
|
13
|
$opts{lc($k)} = $v; |
|
1786
|
|
|
|
|
|
|
} |
|
1787
|
|
|
|
|
|
|
} |
|
1788
|
|
|
|
|
|
|
elsif ( $k =~ /^(width|height)$/i |
|
1789
|
|
|
|
|
|
|
&& $v =~ /^(\d+(?:\.\d+)?\%?)$/ ) { |
|
1790
|
2
|
|
|
|
|
7
|
$opts{lc($k)} = $v; |
|
1791
|
|
|
|
|
|
|
} |
|
1792
|
|
|
|
|
|
|
elsif ( $k =~ /^(x|y)$/i |
|
1793
|
|
|
|
|
|
|
&& $v =~ /^(?:base[+-])?([-+]?\d+(?:\.\d+)?\%?)$/ ) { |
|
1794
|
2
|
|
|
|
|
12
|
$opts{lc($k)} = $v; |
|
1795
|
|
|
|
|
|
|
} |
|
1796
|
|
|
|
|
|
|
elsif ( $k =~ /^(scale)$/ |
|
1797
|
|
|
|
|
|
|
&& $v =~ /^(\d+(?:\.\d+)?)(%)?(?:,(\d+(?:\.\d+)?)(%)?)?$/ ) { |
|
1798
|
3
|
50
|
|
|
|
15
|
$opts{lc($k)} = [ $2 ? $1/100 : $1 ]; |
|
1799
|
3
|
0
|
|
|
|
23
|
$opts{lc($k)}->[1] = $3 ? $4 ? $3/100 : $3 : $opts{lc($k)}->[0]; |
|
|
|
50
|
|
|
|
|
|
|
1800
|
|
|
|
|
|
|
} |
|
1801
|
|
|
|
|
|
|
elsif ( $k =~ /^(center|border|spread|persist|omit)$/i ) { |
|
1802
|
0
|
0
|
|
|
|
0
|
if ( $k eq "center" ) { |
|
1803
|
0
|
|
|
|
|
0
|
$opts{align} = $k; |
|
1804
|
|
|
|
|
|
|
} |
|
1805
|
|
|
|
|
|
|
else { |
|
1806
|
0
|
|
|
|
|
0
|
$opts{lc($k)} = $v; |
|
1807
|
|
|
|
|
|
|
} |
|
1808
|
|
|
|
|
|
|
} |
|
1809
|
|
|
|
|
|
|
elsif ( $k =~ /^(src|uri)$/i && $v ne "" ) { |
|
1810
|
2
|
|
|
|
|
9
|
$uri = $v; |
|
1811
|
|
|
|
|
|
|
} |
|
1812
|
|
|
|
|
|
|
elsif ( $k =~ /^(id)$/i && $v ne "" ) { |
|
1813
|
4
|
|
|
|
|
18
|
$id = $v; |
|
1814
|
|
|
|
|
|
|
} |
|
1815
|
|
|
|
|
|
|
elsif ( $k =~ /^(chord)$/i && $v ne "" ) { |
|
1816
|
0
|
|
|
|
|
0
|
$chord = $v; |
|
1817
|
|
|
|
|
|
|
} |
|
1818
|
|
|
|
|
|
|
elsif ( $k =~ /^(type)$/i && $v ne "" ) { |
|
1819
|
0
|
|
|
|
|
0
|
$opts{type} = $v; |
|
1820
|
|
|
|
|
|
|
} |
|
1821
|
|
|
|
|
|
|
elsif ( $k =~ /^(label|href)$/i && $v ne "" ) { |
|
1822
|
0
|
|
|
|
|
0
|
$opts{lc($k)} = $v; |
|
1823
|
|
|
|
|
|
|
} |
|
1824
|
|
|
|
|
|
|
elsif ( $k =~ /^(anchor)$/i |
|
1825
|
|
|
|
|
|
|
&& $v =~ /^(paper|page|allpages|column|float|line)$/ ) { |
|
1826
|
2
|
|
|
|
|
13
|
$opts{lc($k)} = lc($v); |
|
1827
|
|
|
|
|
|
|
} |
|
1828
|
|
|
|
|
|
|
elsif ( $k =~ /^(align)$/i |
|
1829
|
|
|
|
|
|
|
&& $v =~ /^(center|left|right)$/ ) { |
|
1830
|
0
|
|
|
|
|
0
|
$opts{lc($k)} = lc($v); |
|
1831
|
|
|
|
|
|
|
} |
|
1832
|
|
|
|
|
|
|
elsif ( $k =~ /^(bordertrbl)$/i |
|
1833
|
|
|
|
|
|
|
&& $v =~ /^[trbl]*$/ ) { |
|
1834
|
0
|
|
|
|
|
0
|
$opts{lc($k)} = lc($v); |
|
1835
|
|
|
|
|
|
|
} |
|
1836
|
|
|
|
|
|
|
elsif ( $uri ) { |
|
1837
|
0
|
|
|
|
|
0
|
do_warn( "Unknown image attribute: $k\n" ); |
|
1838
|
0
|
|
|
|
|
0
|
next; |
|
1839
|
|
|
|
|
|
|
} |
|
1840
|
|
|
|
|
|
|
# Assume just an image file uri. |
|
1841
|
|
|
|
|
|
|
else { |
|
1842
|
0
|
|
|
|
|
0
|
$uri = $k; |
|
1843
|
|
|
|
|
|
|
} |
|
1844
|
|
|
|
|
|
|
} |
|
1845
|
|
|
|
|
|
|
|
|
1846
|
6
|
50
|
|
|
|
41
|
return if is_true($opts{omit}); |
|
1847
|
|
|
|
|
|
|
|
|
1848
|
6
|
50
|
66
|
|
|
47
|
unless ( $uri || $id || $chord ) { |
|
|
|
|
33
|
|
|
|
|
|
1849
|
0
|
|
|
|
|
0
|
do_warn( "Missing image source\n" ); |
|
1850
|
0
|
|
|
|
|
0
|
return; |
|
1851
|
|
|
|
|
|
|
} |
|
1852
|
6
|
50
|
66
|
|
|
24
|
if ( $opts{align} && $opts{x} && $opts{x} =~ /\%$/ ) { |
|
|
|
|
33
|
|
|
|
|
|
1853
|
0
|
|
|
|
|
0
|
do_warn( "Useless combination of x percentage with align (align ignored)" ); |
|
1854
|
0
|
|
|
|
|
0
|
delete $opts{align}; |
|
1855
|
|
|
|
|
|
|
} |
|
1856
|
|
|
|
|
|
|
|
|
1857
|
|
|
|
|
|
|
# If the image uri does not have a directory, look it up |
|
1858
|
|
|
|
|
|
|
# next to the song, and then in the images folder of the |
|
1859
|
|
|
|
|
|
|
# resources. |
|
1860
|
6
|
100
|
66
|
|
|
28
|
if ( $uri && CP->is_here($uri) ) { |
|
1861
|
|
|
|
|
|
|
my $found = CP->siblingres( $diag->{file}, $uri, class => "images" ) |
|
1862
|
2
|
|
33
|
|
|
6
|
|| CP->siblingres( $diag->{file}, $uri, class => "icons" ); |
|
1863
|
2
|
50
|
|
|
|
6
|
if ( $found ) { |
|
1864
|
2
|
|
|
|
|
3
|
$uri = $found; |
|
1865
|
|
|
|
|
|
|
} |
|
1866
|
|
|
|
|
|
|
else { |
|
1867
|
0
|
|
|
|
|
0
|
do_warn("Missing image for \"$uri\""); |
|
1868
|
0
|
|
|
|
|
0
|
return; |
|
1869
|
|
|
|
|
|
|
} |
|
1870
|
|
|
|
|
|
|
} |
|
1871
|
6
|
50
|
|
|
|
20
|
$uri = "chord:$chord" if $chord; |
|
1872
|
|
|
|
|
|
|
|
|
1873
|
6
|
|
66
|
|
|
28
|
my $aid = $id || "_Image".$assetid++; |
|
1874
|
|
|
|
|
|
|
|
|
1875
|
6
|
50
|
|
|
|
24
|
if ( defined $opts{spread} ) { |
|
1876
|
0
|
0
|
|
|
|
0
|
if ( exists $self->{spreadimage} ) { |
|
1877
|
0
|
|
|
|
|
0
|
do_warn("Skipping superfluous spread image"); |
|
1878
|
|
|
|
|
|
|
} |
|
1879
|
|
|
|
|
|
|
else { |
|
1880
|
|
|
|
|
|
|
$self->{spreadimage} = |
|
1881
|
0
|
|
|
|
|
0
|
{ id => $aid, space => $opts{spread} }; |
|
1882
|
|
|
|
|
|
|
warn("Got spread image $aid with $opts{spread} space\n") |
|
1883
|
0
|
0
|
|
|
|
0
|
if $config->{debug}->{images}; |
|
1884
|
|
|
|
|
|
|
} |
|
1885
|
|
|
|
|
|
|
} |
|
1886
|
|
|
|
|
|
|
|
|
1887
|
|
|
|
|
|
|
# Store as asset. |
|
1888
|
6
|
100
|
|
|
|
16
|
if ( $uri ) { |
|
1889
|
2
|
|
|
|
|
3
|
my $opts; |
|
1890
|
2
|
|
|
|
|
5
|
for ( qw( type persist href ) ) { |
|
1891
|
6
|
50
|
|
|
|
11
|
$opts->{$_} = $opts{$_} if defined $opts{$_}; |
|
1892
|
6
|
|
|
|
|
12
|
delete $opts{$_}; |
|
1893
|
|
|
|
|
|
|
} |
|
1894
|
2
|
|
|
|
|
3
|
for ( qw( spread ) ) { |
|
1895
|
2
|
50
|
|
|
|
6
|
$opts->{$_} = $opts{$_} if defined $opts{$_}; |
|
1896
|
|
|
|
|
|
|
} |
|
1897
|
|
|
|
|
|
|
|
|
1898
|
2
|
50
|
33
|
|
|
21
|
if ( $id && %opts ) { |
|
1899
|
0
|
|
|
|
|
0
|
do_warn("Asset definition \"$id\" does not take attributes", |
|
1900
|
|
|
|
|
|
|
" (" . join(" ",sort keys %opts) . ")"); |
|
1901
|
0
|
|
|
|
|
0
|
return; |
|
1902
|
|
|
|
|
|
|
} |
|
1903
|
|
|
|
|
|
|
|
|
1904
|
2
|
|
50
|
|
|
13
|
$self->{assets} //= {}; |
|
1905
|
2
|
|
|
|
|
3
|
my $a; |
|
1906
|
2
|
50
|
33
|
|
|
22
|
if ( $uri =~ /\.(\w+)$/ && exists $config->{delegates}->{$1} ) { |
|
1907
|
0
|
|
|
|
|
0
|
my $d = $config->{delegates}->{$1}; |
|
1908
|
|
|
|
|
|
|
$a = { type => "image", |
|
1909
|
|
|
|
|
|
|
subtype => "delegate", |
|
1910
|
|
|
|
|
|
|
delegate => $d->{module}, |
|
1911
|
|
|
|
|
|
|
handler => $d->{handler}, |
|
1912
|
0
|
|
|
|
|
0
|
uri => $uri, |
|
1913
|
|
|
|
|
|
|
}; |
|
1914
|
|
|
|
|
|
|
} |
|
1915
|
|
|
|
|
|
|
else { |
|
1916
|
2
|
|
|
|
|
10
|
$a = { type => "image", |
|
1917
|
|
|
|
|
|
|
uri => $uri, |
|
1918
|
|
|
|
|
|
|
}; |
|
1919
|
|
|
|
|
|
|
} |
|
1920
|
2
|
50
|
|
|
|
6
|
$a->{opts} = $opts if $opts; |
|
1921
|
2
|
|
|
|
|
6
|
$self->{assets}->{$aid} = $a; |
|
1922
|
|
|
|
|
|
|
|
|
1923
|
2
|
50
|
|
|
|
7
|
if ( $config->{debug}->{images} ) { |
|
1924
|
|
|
|
|
|
|
warn("asset[$aid] type=image uri=$uri", |
|
1925
|
|
|
|
|
|
|
$a->{subtype} ? " subtype=$a->{subtype}" : (), |
|
1926
|
|
|
|
|
|
|
$a->{delegate} ? " delegate=$a->{delegate}" : (), |
|
1927
|
0
|
0
|
|
|
|
0
|
$opts->{persist} ? " persist" : (), |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1928
|
|
|
|
|
|
|
"\n"); |
|
1929
|
|
|
|
|
|
|
} |
|
1930
|
2
|
50
|
33
|
|
|
11
|
return if $id || defined $opts{spread}; # defining only |
|
1931
|
|
|
|
|
|
|
} |
|
1932
|
|
|
|
|
|
|
|
|
1933
|
6
|
50
|
|
|
|
19
|
if ( $opts{label} ) { |
|
1934
|
|
|
|
|
|
|
$self->add( type => "set", |
|
1935
|
|
|
|
|
|
|
name => "label", |
|
1936
|
|
|
|
|
|
|
value => $opts{label}, |
|
1937
|
0
|
|
|
|
|
0
|
context => "image" ); |
|
1938
|
0
|
|
|
|
|
0
|
push( @labels, $opts{label} ); |
|
1939
|
|
|
|
|
|
|
} |
|
1940
|
|
|
|
|
|
|
|
|
1941
|
6
|
|
|
|
|
35
|
$self->add( type => "image", |
|
1942
|
|
|
|
|
|
|
id => $aid, |
|
1943
|
|
|
|
|
|
|
opts => \%opts ); |
|
1944
|
6
|
|
|
|
|
52
|
return 1; |
|
1945
|
|
|
|
|
|
|
} |
|
1946
|
|
|
|
|
|
|
|
|
1947
|
|
|
|
|
|
|
sub dir_title { |
|
1948
|
219
|
|
|
219
|
0
|
932
|
my ( $self, $dir, $arg ) = @_; |
|
1949
|
219
|
|
|
|
|
776
|
$self->{title} = $arg; |
|
1950
|
219
|
|
|
|
|
496
|
push( @{ $self->{meta}->{title} }, $arg ); |
|
|
219
|
|
|
|
|
1125
|
|
|
1951
|
219
|
|
|
|
|
1451
|
return 1; |
|
1952
|
|
|
|
|
|
|
} |
|
1953
|
|
|
|
|
|
|
|
|
1954
|
|
|
|
|
|
|
sub dir_subtitle { |
|
1955
|
51
|
|
|
51
|
0
|
194
|
my ( $self, $dir, $arg ) = @_; |
|
1956
|
51
|
|
|
|
|
122
|
push( @{ $self->{subtitle} }, $arg ); |
|
|
51
|
|
|
|
|
222
|
|
|
1957
|
51
|
|
|
|
|
122
|
push( @{ $self->{meta}->{subtitle} }, $arg ); |
|
|
51
|
|
|
|
|
187
|
|
|
1958
|
51
|
|
|
|
|
510
|
return 1; |
|
1959
|
|
|
|
|
|
|
} |
|
1960
|
|
|
|
|
|
|
|
|
1961
|
|
|
|
|
|
|
# Metadata. |
|
1962
|
|
|
|
|
|
|
|
|
1963
|
|
|
|
|
|
|
sub dir_meta { |
|
1964
|
277
|
|
|
277
|
0
|
878
|
my ( $self, $dir, $arg ) = @_; |
|
1965
|
|
|
|
|
|
|
|
|
1966
|
277
|
50
|
|
|
|
1869
|
if ( $arg =~ /([^ :]+)[ :]+(.*)/ ) { |
|
1967
|
277
|
|
|
|
|
1170
|
my $key = lc $1; |
|
1968
|
277
|
|
|
|
|
1085
|
my @vals = ( $2 ); |
|
1969
|
277
|
100
|
|
|
|
1481
|
if ( $config->{metadata}->{autosplit} ) { |
|
1970
|
270
|
|
|
|
|
892
|
@vals = map { s/s\+$//; $_ } |
|
|
270
|
|
|
|
|
1113
|
|
|
1971
|
270
|
|
|
|
|
6348
|
split( quotemeta($config->{metadata}->{separator}), $vals[0] ); |
|
1972
|
|
|
|
|
|
|
} |
|
1973
|
|
|
|
|
|
|
else { |
|
1974
|
7
|
50
|
|
|
|
21
|
pop(@vals) if $vals[0] eq ''; |
|
1975
|
|
|
|
|
|
|
} |
|
1976
|
277
|
|
|
|
|
851
|
my $m = $self->{meta}; |
|
1977
|
|
|
|
|
|
|
|
|
1978
|
|
|
|
|
|
|
# User and instrument cannot be set here. |
|
1979
|
277
|
50
|
33
|
|
|
1689
|
if ( $key eq "user" || $key eq "instrument" ) { |
|
1980
|
0
|
|
|
|
|
0
|
do_warn("\"$key\" can be set from config only.\n"); |
|
1981
|
0
|
|
|
|
|
0
|
return 1; |
|
1982
|
|
|
|
|
|
|
} |
|
1983
|
|
|
|
|
|
|
|
|
1984
|
277
|
|
|
|
|
787
|
for my $val ( @vals ) { |
|
1985
|
|
|
|
|
|
|
|
|
1986
|
277
|
100
|
|
|
|
849
|
if ( $key eq "key" ) { |
|
1987
|
93
|
|
|
|
|
344
|
$val =~ s/[\[\]]//g; |
|
1988
|
93
|
|
|
|
|
187
|
my $info = do { |
|
1989
|
|
|
|
|
|
|
# When transcoding to nash/roman, parse_chord will |
|
1990
|
|
|
|
|
|
|
# complain about a missing key. Fake one. |
|
1991
|
93
|
|
|
|
|
546
|
local( $self->{meta}->{key} ) = [ '_dummy_' ]; |
|
1992
|
93
|
|
|
|
|
700
|
local( $self->{chordsinfo}->{_dummy_} ) = { root_ord => 0 }; |
|
1993
|
93
|
|
|
|
|
549
|
$self->parse_chord($val); |
|
1994
|
|
|
|
|
|
|
}; |
|
1995
|
93
|
50
|
|
|
|
321
|
do_warn("Illegal key: \"$val\"\n"), next unless $info; |
|
1996
|
93
|
|
|
|
|
295
|
my $name = $info->name; |
|
1997
|
93
|
|
|
|
|
222
|
my $act = $name; |
|
1998
|
|
|
|
|
|
|
$info->{key} = $name |
|
1999
|
93
|
50
|
|
|
|
369
|
unless $config->{settings}->{'enharmonic-transpose'}; |
|
2000
|
|
|
|
|
|
|
|
|
2001
|
93
|
50
|
|
|
|
326
|
if ( $capo ) { |
|
2002
|
0
|
|
|
|
|
0
|
$act = $self->add_chord( $info->transpose($capo) ); |
|
2003
|
0
|
0
|
|
|
|
0
|
$name = $act if $decapo; |
|
2004
|
|
|
|
|
|
|
} |
|
2005
|
|
|
|
|
|
|
|
|
2006
|
93
|
|
|
|
|
209
|
push( @{ $m->{key} }, $name ); |
|
|
93
|
|
|
|
|
482
|
|
|
2007
|
93
|
|
|
|
|
443
|
$m->{key_actual} = [ $act ]; |
|
2008
|
|
|
|
|
|
|
# warn("XX key=$name act=$act capo=", |
|
2009
|
|
|
|
|
|
|
# $capo//""," decapo=$decapo\n"); |
|
2010
|
93
|
|
|
|
|
850
|
return 1; |
|
2011
|
|
|
|
|
|
|
} |
|
2012
|
|
|
|
|
|
|
|
|
2013
|
|
|
|
|
|
|
|
|
2014
|
184
|
100
|
100
|
|
|
921
|
if ( $key eq "capo" ) { |
|
|
|
100
|
|
|
|
|
|
|
2015
|
|
|
|
|
|
|
do_warn("Multiple capo settings may yield surprising results.") |
|
2016
|
17
|
100
|
|
|
|
70
|
if exists $m->{capo}; |
|
2017
|
|
|
|
|
|
|
|
|
2018
|
17
|
|
100
|
|
|
87
|
$capo = $val || undef; |
|
2019
|
17
|
50
|
66
|
|
|
172
|
if ( $capo && $m->{key} ) { |
|
2020
|
16
|
100
|
|
|
|
62
|
if ( $decapo ) { |
|
2021
|
|
|
|
|
|
|
my $key = $self->store_chord |
|
2022
|
4
|
|
|
|
|
37
|
($self->{chordsinfo}->{$m->{key}->[-1]} |
|
2023
|
|
|
|
|
|
|
->transpose($val)); |
|
2024
|
4
|
|
|
|
|
15
|
$m->{key}->[-1] = $key; |
|
2025
|
|
|
|
|
|
|
$key = $self->store_chord |
|
2026
|
4
|
|
|
|
|
22
|
($self->{chordsinfo}->{$m->{key}->[-1]} |
|
2027
|
|
|
|
|
|
|
->transpose($xpose)); |
|
2028
|
4
|
|
|
|
|
24
|
$m->{key_actual} = [ $key ]; |
|
2029
|
|
|
|
|
|
|
} |
|
2030
|
|
|
|
|
|
|
else { |
|
2031
|
12
|
|
|
|
|
45
|
my $act = $m->{key_actual}->[-1]; |
|
2032
|
12
|
|
|
|
|
53
|
$m->{key_from} = [ $act ]; |
|
2033
|
|
|
|
|
|
|
my $key = $self->store_chord |
|
2034
|
12
|
|
|
|
|
151
|
($self->{chordsinfo}->{$act}->transpose($val)); |
|
2035
|
12
|
|
|
|
|
98
|
$m->{key_actual} = [ $key ]; |
|
2036
|
|
|
|
|
|
|
} |
|
2037
|
|
|
|
|
|
|
} |
|
2038
|
|
|
|
|
|
|
} |
|
2039
|
|
|
|
|
|
|
|
|
2040
|
|
|
|
|
|
|
elsif ( $key eq "duration" && $val ) { |
|
2041
|
9
|
|
|
|
|
43
|
$val = duration($val); |
|
2042
|
|
|
|
|
|
|
} |
|
2043
|
|
|
|
|
|
|
|
|
2044
|
184
|
50
|
33
|
|
|
798
|
if ( $config->{metadata}->{strict} |
|
2045
|
1565
|
|
|
1565
|
|
2823
|
&& ! any { $_ eq $key } @{ $config->{metadata}->{keys} } ) { |
|
|
184
|
|
|
|
|
912
|
|
|
2046
|
|
|
|
|
|
|
# Unknown, and strict. |
|
2047
|
|
|
|
|
|
|
do_warn("Unknown metadata item: $key") |
|
2048
|
0
|
0
|
|
|
|
0
|
if $config->{settings}->{strict}; |
|
2049
|
0
|
|
|
|
|
0
|
return; |
|
2050
|
|
|
|
|
|
|
} |
|
2051
|
|
|
|
|
|
|
|
|
2052
|
184
|
50
|
|
|
|
962
|
if ( defined $val ) { |
|
2053
|
|
|
|
|
|
|
$self->{meta}->{$key} = [ $self->{meta}->{$key} ] |
|
2054
|
184
|
50
|
66
|
|
|
844
|
if $self->{meta}->{$key} && !is_arrayref($self->{meta}->{$key}); |
|
2055
|
184
|
|
|
|
|
349
|
push( @{ $self->{meta}->{$key} }, $val ); |
|
|
184
|
|
|
|
|
1089
|
|
|
2056
|
|
|
|
|
|
|
} |
|
2057
|
|
|
|
|
|
|
} |
|
2058
|
|
|
|
|
|
|
} |
|
2059
|
|
|
|
|
|
|
else { |
|
2060
|
|
|
|
|
|
|
do_warn("Incomplete meta directive: $dir $arg\n") |
|
2061
|
0
|
0
|
|
|
|
0
|
if $config->{settings}->{strict}; |
|
2062
|
0
|
|
|
|
|
0
|
return; |
|
2063
|
|
|
|
|
|
|
} |
|
2064
|
184
|
|
|
|
|
1390
|
return 1; |
|
2065
|
|
|
|
|
|
|
} |
|
2066
|
|
|
|
|
|
|
|
|
2067
|
|
|
|
|
|
|
# Song / Global settings. |
|
2068
|
|
|
|
|
|
|
|
|
2069
|
|
|
|
|
|
|
sub dir_titles { |
|
2070
|
23
|
|
|
23
|
0
|
91
|
my ( $self, $dir, $arg ) = @_; |
|
2071
|
|
|
|
|
|
|
|
|
2072
|
23
|
50
|
|
|
|
219
|
unless ( $arg =~ /^(left|right|center|centre)$/i ) { |
|
2073
|
0
|
|
|
|
|
0
|
do_warn("Invalid argument for titles directive: $arg\n"); |
|
2074
|
0
|
|
|
|
|
0
|
return 1; |
|
2075
|
|
|
|
|
|
|
} |
|
2076
|
23
|
100
|
|
|
|
253
|
$self->{settings}->{titles} = lc($1) eq "centre" ? "center" : lc($1); |
|
2077
|
23
|
|
|
|
|
148
|
return 1; |
|
2078
|
|
|
|
|
|
|
} |
|
2079
|
|
|
|
|
|
|
|
|
2080
|
|
|
|
|
|
|
sub dir_columns { |
|
2081
|
20
|
|
|
20
|
0
|
81
|
my ( $self, $dir, $arg ) = @_; |
|
2082
|
|
|
|
|
|
|
|
|
2083
|
20
|
50
|
|
|
|
166
|
unless ( $arg =~ /^(\d+)$/ ) { |
|
2084
|
0
|
|
|
|
|
0
|
do_warn("Invalid argument for columns directive: $arg (should be a number)\n"); |
|
2085
|
0
|
|
|
|
|
0
|
return 1; |
|
2086
|
|
|
|
|
|
|
} |
|
2087
|
|
|
|
|
|
|
# If there a column specifications in the config, retain them |
|
2088
|
|
|
|
|
|
|
# if the number of columns match. |
|
2089
|
20
|
50
|
33
|
|
|
131
|
unless( ref($config->{settings}->{columns}) eq 'ARRAY' |
|
2090
|
0
|
|
|
|
|
0
|
&& $arg == @{$config->{settings}->{columns}} |
|
2091
|
|
|
|
|
|
|
) { |
|
2092
|
20
|
|
|
|
|
75
|
$self->{settings}->{columns} = $arg; |
|
2093
|
|
|
|
|
|
|
} |
|
2094
|
20
|
|
|
|
|
117
|
return 1; |
|
2095
|
|
|
|
|
|
|
} |
|
2096
|
|
|
|
|
|
|
|
|
2097
|
|
|
|
|
|
|
sub dir_papersize { |
|
2098
|
4
|
|
|
4
|
0
|
13
|
my ( $self, $dir, $arg ) = @_; |
|
2099
|
4
|
|
|
|
|
16
|
$self->{settings}->{papersize} = $arg; |
|
2100
|
4
|
|
|
|
|
21
|
return 1; |
|
2101
|
|
|
|
|
|
|
} |
|
2102
|
|
|
|
|
|
|
|
|
2103
|
|
|
|
|
|
|
sub dir_diagrams { # AKA grid |
|
2104
|
3
|
|
|
3
|
0
|
13
|
my ( $self, $dir, $arg ) = @_; |
|
2105
|
|
|
|
|
|
|
|
|
2106
|
3
|
50
|
|
|
|
12
|
if ( $arg ne "" ) { |
|
2107
|
3
|
|
|
|
|
18
|
$self->{settings}->{diagrams} = !!is_true($arg); |
|
2108
|
3
|
100
|
|
|
|
16
|
$self->{settings}->{diagrampos} = lc($arg) |
|
2109
|
|
|
|
|
|
|
if $arg =~ /^(right|bottom|top|below)$/i; |
|
2110
|
|
|
|
|
|
|
} |
|
2111
|
|
|
|
|
|
|
else { |
|
2112
|
0
|
|
|
|
|
0
|
$self->{settings}->{diagrams} = 1; |
|
2113
|
|
|
|
|
|
|
} |
|
2114
|
3
|
|
|
|
|
16
|
return 1; |
|
2115
|
|
|
|
|
|
|
} |
|
2116
|
|
|
|
|
|
|
|
|
2117
|
|
|
|
|
|
|
sub dir_grid { |
|
2118
|
2
|
|
|
2
|
0
|
10
|
my ( $self, $dir, $arg ) = @_; |
|
2119
|
2
|
|
|
|
|
7
|
$self->{settings}->{diagrams} = 1; |
|
2120
|
2
|
|
|
|
|
11
|
return 1; |
|
2121
|
|
|
|
|
|
|
} |
|
2122
|
|
|
|
|
|
|
|
|
2123
|
|
|
|
|
|
|
sub dir_no_grid { |
|
2124
|
5
|
|
|
5
|
0
|
21
|
my ( $self, $dir, $arg ) = @_; |
|
2125
|
5
|
|
|
|
|
26
|
$self->{settings}->{diagrams} = 0; |
|
2126
|
5
|
|
|
|
|
28
|
return 1; |
|
2127
|
|
|
|
|
|
|
} |
|
2128
|
|
|
|
|
|
|
|
|
2129
|
|
|
|
|
|
|
sub dir_transpose { |
|
2130
|
55
|
|
|
55
|
0
|
218
|
my ( $self, $dir, $arg ) = @_; |
|
2131
|
|
|
|
|
|
|
|
|
2132
|
55
|
|
100
|
|
|
376
|
$propstack{transpose} //= []; |
|
2133
|
|
|
|
|
|
|
|
|
2134
|
55
|
100
|
|
|
|
372
|
if ( $arg =~ /^([-+]?\d+)\s*$/ ) { |
|
2135
|
34
|
|
|
|
|
151
|
my $new = $1; |
|
2136
|
34
|
|
|
|
|
85
|
push( @{ $propstack{transpose} }, [ $xpose, $xpose_dir ] ); |
|
|
34
|
|
|
|
|
226
|
|
|
2137
|
34
|
|
|
|
|
285
|
my %a = ( type => "control", |
|
2138
|
|
|
|
|
|
|
name => "transpose", |
|
2139
|
|
|
|
|
|
|
previous => [ $xpose, $xpose_dir ] |
|
2140
|
|
|
|
|
|
|
); |
|
2141
|
34
|
|
|
|
|
138
|
$xpose += $new; |
|
2142
|
34
|
|
|
|
|
93
|
$xpose_dir = $new <=> 0; |
|
2143
|
34
|
|
|
|
|
115
|
my $m = $self->{meta}; |
|
2144
|
34
|
100
|
|
|
|
182
|
if ( $m->{key} ) { |
|
2145
|
23
|
|
|
|
|
83
|
my $key = $m->{key}->[-1]; |
|
2146
|
23
|
|
|
|
|
56
|
my $xp = $xpose; |
|
2147
|
23
|
100
|
|
|
|
75
|
$xp += $capo if $capo; |
|
2148
|
23
|
|
|
|
|
210
|
my $xpk = $self->{chordsinfo}->{$key}->transpose($xp, $xp <=> 0); |
|
2149
|
23
|
|
|
|
|
141
|
$self->{chordsinfo}->{$xpk->name} = $xpk; |
|
2150
|
23
|
|
|
|
|
135
|
$m->{key_from} = [ $m->{key_actual}->[0] ]; |
|
2151
|
23
|
|
|
|
|
82
|
$m->{key_actual} = [ $xpk->name ]; |
|
2152
|
|
|
|
|
|
|
} |
|
2153
|
34
|
50
|
|
|
|
223
|
$self->add( %a, value => $xpose, dir => $xpose_dir ) |
|
2154
|
|
|
|
|
|
|
if $no_transpose; |
|
2155
|
|
|
|
|
|
|
} |
|
2156
|
|
|
|
|
|
|
else { |
|
2157
|
21
|
|
|
|
|
146
|
my %a = ( type => "control", |
|
2158
|
|
|
|
|
|
|
name => "transpose", |
|
2159
|
|
|
|
|
|
|
previous => [ $xpose, $xpose_dir ] |
|
2160
|
|
|
|
|
|
|
); |
|
2161
|
21
|
|
|
|
|
78
|
my $m = $self->{meta}; |
|
2162
|
21
|
|
|
|
|
52
|
my ( $new, $dir ); |
|
2163
|
21
|
50
|
|
|
|
45
|
if ( @{ $propstack{transpose} } ) { |
|
|
21
|
|
|
|
|
93
|
|
|
2164
|
21
|
|
|
|
|
43
|
( $new, $dir ) = @{ pop( @{ $propstack{transpose} } ) }; |
|
|
21
|
|
|
|
|
44
|
|
|
|
21
|
|
|
|
|
127
|
|
|
2165
|
|
|
|
|
|
|
} |
|
2166
|
|
|
|
|
|
|
else { |
|
2167
|
0
|
|
|
|
|
0
|
$new = 0; |
|
2168
|
0
|
|
|
|
|
0
|
$dir = $config->{settings}->{transpose} <=> 0; |
|
2169
|
|
|
|
|
|
|
} |
|
2170
|
21
|
|
|
|
|
59
|
$xpose = $new; |
|
2171
|
21
|
|
|
|
|
52
|
$xpose_dir = $dir; |
|
2172
|
21
|
100
|
|
|
|
105
|
if ( $m->{key} ) { |
|
2173
|
15
|
|
|
|
|
86
|
$m->{key_from} = [ $m->{key_actual}->[0] ]; |
|
2174
|
15
|
|
|
|
|
109
|
my $xp = $xpose; |
|
2175
|
15
|
50
|
66
|
|
|
88
|
$xp += $capo if $capo && $decapo; |
|
2176
|
|
|
|
|
|
|
$m->{key_actual} = |
|
2177
|
15
|
|
|
|
|
120
|
[ $self->{chordsinfo}->{$m->{key}->[-1]}->transpose($xp)->name ]; |
|
2178
|
|
|
|
|
|
|
} |
|
2179
|
21
|
100
|
|
|
|
275
|
if ( !@{ $propstack{transpose} } ) { |
|
|
21
|
|
|
|
|
121
|
|
|
2180
|
12
|
|
|
|
|
62
|
delete $m->{$_} for qw( key_from ); |
|
2181
|
|
|
|
|
|
|
} |
|
2182
|
21
|
50
|
|
|
|
223
|
$self->add( %a, value => $xpose, dir => $dir ) |
|
2183
|
|
|
|
|
|
|
if $no_transpose; |
|
2184
|
|
|
|
|
|
|
} |
|
2185
|
55
|
|
|
|
|
618
|
return 1; |
|
2186
|
|
|
|
|
|
|
} |
|
2187
|
|
|
|
|
|
|
|
|
2188
|
|
|
|
|
|
|
#### End of directive handlers #### |
|
2189
|
|
|
|
|
|
|
|
|
2190
|
|
|
|
|
|
|
sub propset { |
|
2191
|
87
|
|
|
87
|
0
|
225
|
my ( $self, $item, $prop, $value ) = @_; |
|
2192
|
87
|
100
|
|
|
|
172
|
$prop = "color" if $prop eq "colour"; |
|
2193
|
87
|
|
|
|
|
217
|
my $name = "$item-$prop"; |
|
2194
|
87
|
|
100
|
|
|
372
|
$propstack{$name} //= []; |
|
2195
|
|
|
|
|
|
|
|
|
2196
|
87
|
100
|
|
|
|
185
|
if ( $value eq "" ) { |
|
2197
|
2
|
|
|
|
|
3
|
my @toadd; |
|
2198
|
|
|
|
|
|
|
# Pop current value from stack. |
|
2199
|
2
|
50
|
|
|
|
2
|
if ( @{ $propstack{$name} } ) { |
|
|
2
|
|
|
|
|
5
|
|
|
2200
|
2
|
|
|
|
|
3
|
my $old = pop( @{ $propstack{$name} } ); |
|
|
2
|
|
|
|
|
3
|
|
|
2201
|
|
|
|
|
|
|
# A trailing number after a font directive means there |
|
2202
|
|
|
|
|
|
|
# was also a size saved. Pop it. |
|
2203
|
2
|
50
|
33
|
|
|
6
|
if ( $prop eq "font" && $old =~ /\s(\d+(?:\.\d+)?)$/ ) { |
|
2204
|
0
|
|
|
|
|
0
|
pop( @{ $propstack{"$item-size"} } ); |
|
|
0
|
|
|
|
|
0
|
|
|
2205
|
|
|
|
|
|
|
# Resetting the size must follow the font reset. |
|
2206
|
|
|
|
|
|
|
push( @toadd, type => "control", |
|
2207
|
|
|
|
|
|
|
name => "$item-size", |
|
2208
|
|
|
|
|
|
|
value => |
|
2209
|
0
|
|
|
|
|
0
|
@{ $propstack{"$item-size"} } |
|
2210
|
0
|
0
|
|
|
|
0
|
? $propstack{"$item-size"}->[-1] |
|
2211
|
|
|
|
|
|
|
: undef ); |
|
2212
|
|
|
|
|
|
|
} |
|
2213
|
|
|
|
|
|
|
} |
|
2214
|
|
|
|
|
|
|
else { |
|
2215
|
0
|
|
|
|
|
0
|
do_warn("No saved value for property $item$prop\n" ) |
|
2216
|
|
|
|
|
|
|
} |
|
2217
|
|
|
|
|
|
|
# Use new current value, if any. |
|
2218
|
2
|
50
|
|
|
|
3
|
if ( @{ $propstack{$name} } ) { |
|
|
2
|
|
|
|
|
3
|
|
|
2219
|
2
|
|
|
|
|
3
|
$value = $propstack{$name}->[-1] |
|
2220
|
|
|
|
|
|
|
} |
|
2221
|
|
|
|
|
|
|
else { |
|
2222
|
0
|
|
|
|
|
0
|
$value = undef; |
|
2223
|
|
|
|
|
|
|
} |
|
2224
|
2
|
|
|
|
|
5
|
$self->add( type => "control", |
|
2225
|
|
|
|
|
|
|
name => $name, |
|
2226
|
|
|
|
|
|
|
value => $value ); |
|
2227
|
2
|
50
|
|
|
|
4
|
$self->add( @toadd ) if @toadd; |
|
2228
|
2
|
|
|
|
|
3
|
return 1; |
|
2229
|
|
|
|
|
|
|
} |
|
2230
|
|
|
|
|
|
|
|
|
2231
|
85
|
100
|
|
|
|
154
|
if ( $prop eq "size" ) { |
|
2232
|
24
|
50
|
|
|
|
153
|
unless ( $value =~ /^\d+(?:\.\d+)?\%?$/ ) { |
|
2233
|
0
|
|
|
|
|
0
|
do_warn("Illegal value \"$value\" for $item$prop\n"); |
|
2234
|
0
|
|
|
|
|
0
|
return 1; |
|
2235
|
|
|
|
|
|
|
} |
|
2236
|
|
|
|
|
|
|
} |
|
2237
|
85
|
100
|
|
|
|
149
|
if ( $prop eq "color" ) { |
|
2238
|
37
|
|
|
|
|
55
|
my $v; |
|
2239
|
37
|
50
|
|
|
|
86
|
unless ( $v = get_color($value) ) { |
|
2240
|
0
|
|
|
|
|
0
|
do_warn("Illegal value \"$value\" for $item$prop\n"); |
|
2241
|
0
|
|
|
|
|
0
|
return 1; |
|
2242
|
|
|
|
|
|
|
} |
|
2243
|
37
|
|
|
|
|
58
|
$value = $v; |
|
2244
|
|
|
|
|
|
|
} |
|
2245
|
85
|
100
|
|
|
|
191
|
$value = $prop eq "font" ? $value : lc($value); |
|
2246
|
85
|
|
|
|
|
260
|
$self->add( type => "control", |
|
2247
|
|
|
|
|
|
|
name => $name, |
|
2248
|
|
|
|
|
|
|
value => $value ); |
|
2249
|
85
|
|
|
|
|
175
|
push( @{ $propstack{$name} }, $value ); |
|
|
85
|
|
|
|
|
197
|
|
|
2250
|
|
|
|
|
|
|
|
|
2251
|
|
|
|
|
|
|
# A trailing number after a font directive is an implicit size |
|
2252
|
|
|
|
|
|
|
# directive. |
|
2253
|
85
|
50
|
66
|
|
|
1033
|
if ( $prop eq 'font' && $value =~ /\s(\d+(?:\.\d+)?)$/ ) { |
|
2254
|
0
|
|
|
|
|
0
|
$self->add( type => "control", |
|
2255
|
|
|
|
|
|
|
name => "$item-size", |
|
2256
|
|
|
|
|
|
|
value => $1 ); |
|
2257
|
0
|
|
|
|
|
0
|
push( @{ $propstack{"$item-size"} }, $1 ); |
|
|
0
|
|
|
|
|
0
|
|
|
2258
|
|
|
|
|
|
|
} |
|
2259
|
|
|
|
|
|
|
} |
|
2260
|
|
|
|
|
|
|
|
|
2261
|
|
|
|
|
|
|
sub add_chord { |
|
2262
|
1046
|
|
|
1046
|
0
|
2679
|
my ( $self, $info, $new_id ) = @_; |
|
2263
|
|
|
|
|
|
|
|
|
2264
|
1046
|
100
|
|
|
|
2426
|
if ( $new_id ) { |
|
2265
|
989
|
100
|
|
|
|
2615
|
if ( $new_id eq "1" ) { |
|
2266
|
11
|
|
|
|
|
41
|
state $id = "ch0000"; |
|
2267
|
11
|
|
|
|
|
22
|
$new_id = " $id"; |
|
2268
|
11
|
|
|
|
|
46
|
$id++; |
|
2269
|
|
|
|
|
|
|
} |
|
2270
|
|
|
|
|
|
|
} |
|
2271
|
|
|
|
|
|
|
else { |
|
2272
|
57
|
|
|
|
|
178
|
$new_id = $info->name; |
|
2273
|
|
|
|
|
|
|
} |
|
2274
|
1046
|
|
|
|
|
3438
|
$self->{chordsinfo}->{$new_id} = $info->new($info); |
|
2275
|
|
|
|
|
|
|
|
|
2276
|
1046
|
|
|
|
|
4892
|
return $new_id; |
|
2277
|
|
|
|
|
|
|
} |
|
2278
|
|
|
|
|
|
|
|
|
2279
|
|
|
|
|
|
|
sub define_chord { |
|
2280
|
72
|
|
|
72
|
0
|
277
|
my ( $self, $dir, $args ) = @_; |
|
2281
|
|
|
|
|
|
|
|
|
2282
|
|
|
|
|
|
|
# Split the arguments and keep a copy for error messages. |
|
2283
|
|
|
|
|
|
|
# Note that quotewords returns an empty result if it gets confused, |
|
2284
|
|
|
|
|
|
|
# so fall back to the ancient split method if so. |
|
2285
|
72
|
|
|
|
|
256
|
$args =~ s/^\s+//; |
|
2286
|
72
|
|
|
|
|
413
|
$args =~ s/\s+$//; |
|
2287
|
72
|
|
|
|
|
355
|
my @a = quotewords( '[: ]+', 0, $args ); |
|
2288
|
72
|
100
|
|
|
|
26100
|
@a = split( /[: ]+/, $args ) unless @a; |
|
2289
|
|
|
|
|
|
|
|
|
2290
|
72
|
|
|
|
|
329
|
my @orig = @a; |
|
2291
|
72
|
|
|
|
|
230
|
my $show = $dir eq "chord"; |
|
2292
|
72
|
|
|
|
|
139
|
my $fail = 0; |
|
2293
|
72
|
|
|
|
|
159
|
my $name = shift(@a); |
|
2294
|
72
|
|
|
|
|
430
|
my $strings = $config->diagram_strings; |
|
2295
|
|
|
|
|
|
|
|
|
2296
|
|
|
|
|
|
|
# Process the options. |
|
2297
|
72
|
|
|
|
|
288
|
my %kv = ( name => $name ); |
|
2298
|
72
|
|
|
|
|
210
|
while ( @a ) { |
|
2299
|
162
|
|
|
|
|
311
|
my $a = shift(@a); |
|
2300
|
|
|
|
|
|
|
|
|
2301
|
|
|
|
|
|
|
# Copy existing definition. |
|
2302
|
162
|
100
|
66
|
|
|
1418
|
if ( $a eq "copy" || $a eq "copyall" ) { |
|
|
|
100
|
66
|
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
2303
|
11
|
50
|
|
|
|
53
|
if ( my $i = ChordPro::Chords::known_chord($a[0]) ) { |
|
2304
|
11
|
|
|
|
|
35
|
$kv{$a} = $a[0]; |
|
2305
|
11
|
|
|
|
|
27
|
$kv{orig} = $i; |
|
2306
|
11
|
|
|
|
|
29
|
shift(@a); |
|
2307
|
|
|
|
|
|
|
} |
|
2308
|
|
|
|
|
|
|
else { |
|
2309
|
0
|
|
|
|
|
0
|
do_warn("Unknown chord to copy: $a[0]\n"); |
|
2310
|
0
|
|
|
|
|
0
|
$fail++; |
|
2311
|
0
|
|
|
|
|
0
|
last; |
|
2312
|
|
|
|
|
|
|
} |
|
2313
|
|
|
|
|
|
|
} |
|
2314
|
|
|
|
|
|
|
|
|
2315
|
|
|
|
|
|
|
# display |
|
2316
|
|
|
|
|
|
|
elsif ( $a eq "display" && @a ) { |
|
2317
|
2
|
|
|
|
|
15
|
$kv{display} = demarkup($a[0]); |
|
2318
|
|
|
|
|
|
|
do_warn( "\"display\" should not contain markup, use \"format\"" ) |
|
2319
|
2
|
50
|
|
|
|
13
|
unless $kv{display} eq shift(@a); |
|
2320
|
2
|
|
|
|
|
15
|
$kv{display} = $self->parse_chord($kv{display},1); |
|
2321
|
2
|
50
|
|
|
|
20
|
delete $kv{display} unless defined $kv{display}; |
|
2322
|
|
|
|
|
|
|
} |
|
2323
|
|
|
|
|
|
|
|
|
2324
|
|
|
|
|
|
|
# format |
|
2325
|
|
|
|
|
|
|
elsif ( $a eq "format" && @a ) { |
|
2326
|
9
|
|
|
|
|
39
|
$kv{format} = shift(@a); |
|
2327
|
|
|
|
|
|
|
} |
|
2328
|
|
|
|
|
|
|
|
|
2329
|
|
|
|
|
|
|
# base-fret N |
|
2330
|
|
|
|
|
|
|
elsif ( $a eq "base-fret" ) { |
|
2331
|
46
|
50
|
|
|
|
302
|
if ( $a[0] =~ /^\d+$/ ) { |
|
2332
|
46
|
|
|
|
|
608
|
$kv{base} = shift(@a); |
|
2333
|
|
|
|
|
|
|
} |
|
2334
|
|
|
|
|
|
|
else { |
|
2335
|
0
|
|
|
|
|
0
|
do_warn("Invalid base-fret value: $a[0]\n"); |
|
2336
|
0
|
|
|
|
|
0
|
$fail++; |
|
2337
|
0
|
|
|
|
|
0
|
last; |
|
2338
|
|
|
|
|
|
|
} |
|
2339
|
|
|
|
|
|
|
} |
|
2340
|
|
|
|
|
|
|
# frets N N ... N |
|
2341
|
|
|
|
|
|
|
elsif ( $a eq "frets" ) { |
|
2342
|
57
|
|
|
|
|
130
|
my @f; |
|
2343
|
57
|
|
100
|
|
|
514
|
while ( @a && $a[0] =~ /^(?:-?[0-9]+|[-xXN])$/ && @f < $strings ) { |
|
|
|
|
66
|
|
|
|
|
|
2344
|
342
|
|
|
|
|
1877
|
push( @f, shift(@a) ); |
|
2345
|
|
|
|
|
|
|
} |
|
2346
|
57
|
50
|
|
|
|
142
|
if ( @f == $strings ) { |
|
2347
|
57
|
100
|
|
|
|
144
|
$kv{frets} = [ map { $_ =~ /^\d+/ ? $_ : -1 } @f ]; |
|
|
342
|
|
|
|
|
1275
|
|
|
2348
|
|
|
|
|
|
|
} |
|
2349
|
|
|
|
|
|
|
else { |
|
2350
|
0
|
|
|
|
|
0
|
do_warn("Incorrect number of fret positions (" . |
|
2351
|
|
|
|
|
|
|
scalar(@f) . ", should be $strings)\n"); |
|
2352
|
0
|
|
|
|
|
0
|
$fail++; |
|
2353
|
0
|
|
|
|
|
0
|
last; |
|
2354
|
|
|
|
|
|
|
} |
|
2355
|
|
|
|
|
|
|
} |
|
2356
|
|
|
|
|
|
|
|
|
2357
|
|
|
|
|
|
|
# fingers N N ... N |
|
2358
|
|
|
|
|
|
|
elsif ( $a eq "fingers" ) { |
|
2359
|
16
|
|
|
|
|
77
|
my @f; |
|
2360
|
|
|
|
|
|
|
# It is tempting to limit the fingers to 1..5 ... |
|
2361
|
16
|
|
100
|
|
|
118
|
while ( @a && @f < $strings ) { |
|
2362
|
96
|
|
|
|
|
194
|
local $_ = shift(@a); |
|
2363
|
96
|
100
|
|
|
|
314
|
if ( /^[0-9]+$/ ) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
2364
|
90
|
|
|
|
|
415
|
push( @f, 0 + $_ ); |
|
2365
|
|
|
|
|
|
|
} |
|
2366
|
|
|
|
|
|
|
elsif ( /^[A-MO-WYZ]$/ ) { |
|
2367
|
0
|
|
|
|
|
0
|
push( @f, $_ ); |
|
2368
|
|
|
|
|
|
|
} |
|
2369
|
|
|
|
|
|
|
elsif ( /^[-xNX]$/ ) { |
|
2370
|
6
|
|
|
|
|
38
|
push( @f, -1 ); |
|
2371
|
|
|
|
|
|
|
} |
|
2372
|
|
|
|
|
|
|
else { |
|
2373
|
0
|
|
|
|
|
0
|
unshift( @a, $_ ); |
|
2374
|
0
|
|
|
|
|
0
|
last; |
|
2375
|
|
|
|
|
|
|
} |
|
2376
|
|
|
|
|
|
|
} |
|
2377
|
16
|
50
|
|
|
|
49
|
if ( @f == $strings ) { |
|
2378
|
16
|
|
|
|
|
121
|
$kv{fingers} = \@f; |
|
2379
|
|
|
|
|
|
|
} |
|
2380
|
|
|
|
|
|
|
else { |
|
2381
|
0
|
|
|
|
|
0
|
do_warn("Incorrect number of finger settings (" . |
|
2382
|
|
|
|
|
|
|
scalar(@f) . ", should be $strings)\n"); |
|
2383
|
0
|
|
|
|
|
0
|
$fail++; |
|
2384
|
0
|
|
|
|
|
0
|
last; |
|
2385
|
|
|
|
|
|
|
} |
|
2386
|
|
|
|
|
|
|
} |
|
2387
|
|
|
|
|
|
|
|
|
2388
|
|
|
|
|
|
|
# keys N N ... N |
|
2389
|
|
|
|
|
|
|
elsif ( $a eq "keys" ) { |
|
2390
|
8
|
|
|
|
|
15
|
my @f; |
|
2391
|
8
|
|
100
|
|
|
42
|
while ( @a && $a[0] =~ /^[0-9]+$/ ) { |
|
2392
|
24
|
|
|
|
|
113
|
push( @f, shift(@a) ); |
|
2393
|
|
|
|
|
|
|
} |
|
2394
|
8
|
50
|
|
|
|
21
|
if ( @f ) { |
|
2395
|
8
|
|
|
|
|
27
|
$kv{keys} = \@f; |
|
2396
|
|
|
|
|
|
|
} |
|
2397
|
|
|
|
|
|
|
else { |
|
2398
|
0
|
|
|
|
|
0
|
do_warn("Invalid or missing keys\n"); |
|
2399
|
0
|
|
|
|
|
0
|
$fail++; |
|
2400
|
0
|
|
|
|
|
0
|
last; |
|
2401
|
|
|
|
|
|
|
} |
|
2402
|
|
|
|
|
|
|
} |
|
2403
|
|
|
|
|
|
|
|
|
2404
|
|
|
|
|
|
|
elsif ( $a eq "diagram" && @a > 0 ) { |
|
2405
|
13
|
50
|
33
|
|
|
50
|
if ( $show && !is_true($a[0]) ) { |
|
2406
|
0
|
|
|
|
|
0
|
do_warn("Useless diagram suppression"); |
|
2407
|
0
|
|
|
|
|
0
|
next; |
|
2408
|
|
|
|
|
|
|
} |
|
2409
|
13
|
|
|
|
|
47
|
$kv{diagram} = shift(@a); |
|
2410
|
|
|
|
|
|
|
} |
|
2411
|
|
|
|
|
|
|
|
|
2412
|
|
|
|
|
|
|
# Wrong... |
|
2413
|
|
|
|
|
|
|
else { |
|
2414
|
|
|
|
|
|
|
# Insert a marker to show how far we got. |
|
2415
|
0
|
|
|
|
|
0
|
splice( @orig, @orig-@a, 0, "<<<" ); |
|
2416
|
0
|
|
|
|
|
0
|
splice( @orig, @orig-@a-2, 0, ">>>" ); |
|
2417
|
0
|
|
|
|
|
0
|
do_warn("Invalid chord definition: @orig\n"); |
|
2418
|
0
|
|
|
|
|
0
|
$fail++; |
|
2419
|
0
|
|
|
|
|
0
|
last; |
|
2420
|
|
|
|
|
|
|
} |
|
2421
|
|
|
|
|
|
|
} |
|
2422
|
|
|
|
|
|
|
|
|
2423
|
72
|
50
|
|
|
|
191
|
return 1 if $fail; |
|
2424
|
|
|
|
|
|
|
# All options are verified and stored in %kv; |
|
2425
|
|
|
|
|
|
|
|
|
2426
|
|
|
|
|
|
|
# Result structure. |
|
2427
|
72
|
|
|
|
|
297
|
my $res = { name => $name }; |
|
2428
|
|
|
|
|
|
|
|
|
2429
|
|
|
|
|
|
|
# Try to find info. |
|
2430
|
72
|
|
|
|
|
321
|
my $info = $self->parse_chord( $name, "def" ); |
|
2431
|
72
|
100
|
|
|
|
206
|
if ( $info ) { |
|
2432
|
|
|
|
|
|
|
# Copy the chord info. |
|
2433
|
|
|
|
|
|
|
$res->{$_} //= $info->{$_} // '' |
|
2434
|
71
|
|
100
|
|
|
2877
|
for qw( parser root qual ext bass |
|
|
|
|
66
|
|
|
|
|
|
2435
|
|
|
|
|
|
|
root_canon qual_canon ext_canon bass_canon |
|
2436
|
|
|
|
|
|
|
root_ord root_mod bass_ord bass_mod |
|
2437
|
|
|
|
|
|
|
); |
|
2438
|
71
|
100
|
|
|
|
222
|
if ( $show ) { |
|
2439
|
|
|
|
|
|
|
$res->{$_} //= $info->{$_} |
|
2440
|
9
|
|
66
|
|
|
99
|
for qw( base frets fingers keys ); |
|
2441
|
|
|
|
|
|
|
} |
|
2442
|
|
|
|
|
|
|
} |
|
2443
|
|
|
|
|
|
|
else { |
|
2444
|
1
|
|
|
|
|
6
|
$res->{parser} = ChordPro::Chords::get_parser(); |
|
2445
|
|
|
|
|
|
|
} |
|
2446
|
|
|
|
|
|
|
|
|
2447
|
|
|
|
|
|
|
# Copy existing definition. |
|
2448
|
72
|
|
66
|
|
|
378
|
for ( $kv{copyall} // $kv{copy} ) { |
|
2449
|
72
|
100
|
|
|
|
210
|
next unless defined; |
|
2450
|
11
|
|
|
|
|
35
|
$res->{copy} = $_; |
|
2451
|
11
|
|
|
|
|
25
|
my $orig = $res->{orig} = $kv{orig}; |
|
2452
|
|
|
|
|
|
|
$res->{$_} //= $orig->{$_} |
|
2453
|
11
|
|
33
|
|
|
128
|
for qw( base frets fingers keys ); |
|
2454
|
11
|
50
|
|
|
|
41
|
if ( $kv{copyall} ) { |
|
2455
|
|
|
|
|
|
|
$res->{$_} //= $orig->{$_} |
|
2456
|
0
|
|
0
|
|
|
0
|
for qw( display format ); |
|
2457
|
|
|
|
|
|
|
} |
|
2458
|
|
|
|
|
|
|
} |
|
2459
|
72
|
|
|
|
|
149
|
for ( qw( display format ) ) { |
|
2460
|
144
|
100
|
|
|
|
441
|
$res->{$_} = $kv{$_} if defined $kv{$_}; |
|
2461
|
|
|
|
|
|
|
} |
|
2462
|
|
|
|
|
|
|
|
|
2463
|
|
|
|
|
|
|
# If we've got diagram visibility, remove it if true. |
|
2464
|
72
|
100
|
|
|
|
199
|
if ( defined $kv{diagram} ) { |
|
2465
|
13
|
|
|
|
|
35
|
for ( my $v = $kv{diagram} ) { |
|
2466
|
13
|
100
|
|
|
|
61
|
if ( is_true($v) ) { |
|
2467
|
7
|
100
|
|
|
|
26
|
if ( is_ttrue($v) ) { |
|
2468
|
6
|
|
|
|
|
19
|
next; |
|
2469
|
|
|
|
|
|
|
} |
|
2470
|
|
|
|
|
|
|
} |
|
2471
|
|
|
|
|
|
|
else { |
|
2472
|
6
|
|
|
|
|
16
|
$v = 0; |
|
2473
|
|
|
|
|
|
|
} |
|
2474
|
7
|
|
|
|
|
26
|
$res->{diagram} = $v; |
|
2475
|
|
|
|
|
|
|
} |
|
2476
|
|
|
|
|
|
|
} |
|
2477
|
|
|
|
|
|
|
|
|
2478
|
|
|
|
|
|
|
# Copy rest of options. |
|
2479
|
72
|
|
|
|
|
151
|
for ( qw( base frets fingers keys display format ) ) { |
|
2480
|
432
|
100
|
|
|
|
984
|
next unless defined $kv{$_}; |
|
2481
|
138
|
|
|
|
|
315
|
$res->{$_} = $kv{$_}; |
|
2482
|
|
|
|
|
|
|
} |
|
2483
|
|
|
|
|
|
|
|
|
2484
|
|
|
|
|
|
|
# At this time, $res is still just a hash. Time to make a chord. |
|
2485
|
72
|
|
100
|
|
|
226
|
$res->{base} ||= 1; |
|
2486
|
72
|
100
|
|
|
|
1409
|
$res = ChordPro::Chord::Common->new |
|
2487
|
|
|
|
|
|
|
( { %$res, origin => $show ? "inline" : "song" } ); |
|
2488
|
72
|
|
33
|
|
|
501
|
$res->{parser} //= ChordPro::Chords::get_parser(); |
|
2489
|
|
|
|
|
|
|
|
|
2490
|
72
|
100
|
|
|
|
188
|
if ( $show) { |
|
2491
|
9
|
|
|
|
|
70
|
my $ci = $res->clone; |
|
2492
|
9
|
|
|
|
|
7951
|
my $chidx = $self->add_chord( $ci, 1 ); |
|
2493
|
|
|
|
|
|
|
# Combine consecutive entries. |
|
2494
|
9
|
100
|
66
|
|
|
70
|
if ( defined($self->{body}) |
|
2495
|
|
|
|
|
|
|
&& $self->{body}->[-1]->{type} eq "diagrams" ) { |
|
2496
|
2
|
|
|
|
|
3
|
push( @{ $self->{body}->[-1]->{chords} }, $chidx ); |
|
|
2
|
|
|
|
|
7
|
|
|
2497
|
|
|
|
|
|
|
} |
|
2498
|
|
|
|
|
|
|
else { |
|
2499
|
7
|
|
|
|
|
36
|
$self->add( type => "diagrams", |
|
2500
|
|
|
|
|
|
|
show => "user", |
|
2501
|
|
|
|
|
|
|
origin => "chord", |
|
2502
|
|
|
|
|
|
|
chords => [ $chidx ] ); |
|
2503
|
|
|
|
|
|
|
} |
|
2504
|
9
|
|
|
|
|
141
|
return 1; |
|
2505
|
|
|
|
|
|
|
} |
|
2506
|
|
|
|
|
|
|
|
|
2507
|
63
|
|
|
|
|
142
|
my $def = {}; |
|
2508
|
63
|
|
|
|
|
182
|
for ( qw( name base frets fingers keys display format diagram ) ) { |
|
2509
|
504
|
100
|
|
|
|
1228
|
next unless defined $res->{$_}; |
|
2510
|
248
|
|
|
|
|
615
|
$def->{$_} = $res->{$_}; |
|
2511
|
|
|
|
|
|
|
} |
|
2512
|
63
|
|
|
|
|
140
|
push( @{$self->{define}}, $def ); |
|
|
63
|
|
|
|
|
214
|
|
|
2513
|
63
|
|
|
|
|
283
|
my $ret = ChordPro::Chords::add_song_chord($res); |
|
2514
|
63
|
50
|
|
|
|
168
|
if ( $ret ) { |
|
2515
|
0
|
|
|
|
|
0
|
do_warn("Invalid chord: ", $res->{name}, ": ", $ret, "\n"); |
|
2516
|
0
|
|
|
|
|
0
|
return 1; |
|
2517
|
|
|
|
|
|
|
} |
|
2518
|
63
|
|
|
|
|
279
|
$info = ChordPro::Chords::known_chord($res->{name}); |
|
2519
|
63
|
50
|
|
|
|
182
|
croak("We just entered it?? ", $res->{name}) unless $info; |
|
2520
|
|
|
|
|
|
|
|
|
2521
|
63
|
50
|
|
|
|
275
|
$info->dump if $config->{debug}->{x1}; |
|
2522
|
|
|
|
|
|
|
|
|
2523
|
63
|
|
|
|
|
1015
|
return 1; |
|
2524
|
|
|
|
|
|
|
} |
|
2525
|
|
|
|
|
|
|
|
|
2526
|
|
|
|
|
|
|
sub duration { |
|
2527
|
9
|
|
|
9
|
0
|
32
|
my ( $dur ) = @_; |
|
2528
|
|
|
|
|
|
|
|
|
2529
|
9
|
50
|
|
|
|
107
|
if ( $dur =~ /(?:(?:(\d+):)?(\d+):)?(\d+)/ ) { |
|
2530
|
9
|
50
|
|
|
|
70
|
$dur = $3 + ( $2 ? 60 * $2 :0 ) + ( $1 ? 3600 * $1 : 0 ); |
|
|
|
50
|
|
|
|
|
|
|
2531
|
|
|
|
|
|
|
} |
|
2532
|
9
|
|
|
|
|
77
|
my $res = sprintf( "%d:%02d:%02d", |
|
2533
|
|
|
|
|
|
|
int( $dur / 3600 ), |
|
2534
|
|
|
|
|
|
|
int( ( $dur % 3600 ) / 60 ), |
|
2535
|
|
|
|
|
|
|
$dur % 60 ); |
|
2536
|
9
|
|
|
|
|
39
|
$res =~ s/^[0:]+//; |
|
2537
|
9
|
|
|
|
|
27
|
return $res; |
|
2538
|
|
|
|
|
|
|
} |
|
2539
|
|
|
|
|
|
|
|
|
2540
|
|
|
|
|
|
|
sub get_color { |
|
2541
|
37
|
|
|
37
|
0
|
94
|
$_[0]; |
|
2542
|
|
|
|
|
|
|
} |
|
2543
|
|
|
|
|
|
|
|
|
2544
|
|
|
|
|
|
|
sub _diag { |
|
2545
|
23
|
|
|
23
|
|
32455
|
my ( $self, %d ) = @_; |
|
2546
|
23
|
|
|
|
|
213
|
$diag->{$_} = $d{$_} for keys(%d); |
|
2547
|
|
|
|
|
|
|
} |
|
2548
|
|
|
|
|
|
|
|
|
2549
|
|
|
|
|
|
|
sub msg { |
|
2550
|
4
|
|
|
4
|
0
|
18
|
my $m = join("", @_); |
|
2551
|
4
|
|
|
|
|
26
|
$m =~ s/\n+$//; |
|
2552
|
4
|
|
|
|
|
18
|
my $t = $diag->{format}; |
|
2553
|
4
|
|
|
|
|
11
|
$t =~ s/\\n/\n/g; |
|
2554
|
4
|
|
|
|
|
11
|
$t =~ s/\\t/\t/g; |
|
2555
|
4
|
|
|
|
|
29
|
$t =~ s/\%f/$diag->{file}/g; |
|
2556
|
4
|
|
|
|
|
27
|
$t =~ s/\%n/$diag->{line}/g; |
|
2557
|
4
|
|
|
|
|
17
|
$t =~ s/\%l/$diag->{orig}/g; |
|
2558
|
4
|
|
|
|
|
22
|
$t =~ s/\%m/$m/g; |
|
2559
|
4
|
|
|
|
|
34
|
$t; |
|
2560
|
|
|
|
|
|
|
} |
|
2561
|
|
|
|
|
|
|
|
|
2562
|
|
|
|
|
|
|
sub do_warn { |
|
2563
|
4
|
|
|
4
|
0
|
48
|
warn(msg(@_)."\n"); |
|
2564
|
|
|
|
|
|
|
} |
|
2565
|
|
|
|
|
|
|
|
|
2566
|
|
|
|
|
|
|
# Parse a chord. |
|
2567
|
|
|
|
|
|
|
# Handles transpose/transcode. |
|
2568
|
|
|
|
|
|
|
# Returns the chord object. |
|
2569
|
|
|
|
|
|
|
# No parens or annotations, please. |
|
2570
|
|
|
|
|
|
|
sub parse_chord { |
|
2571
|
1169
|
|
|
1169
|
0
|
3202
|
my ( $self, $chord, $def ) = @_; |
|
2572
|
|
|
|
|
|
|
|
|
2573
|
1169
|
|
|
|
|
4277
|
my $debug = $config->{debug}->{chords}; |
|
2574
|
|
|
|
|
|
|
|
|
2575
|
1169
|
50
|
|
|
|
2895
|
warn("Parsing chord: \"$chord\"\n") if $debug; |
|
2576
|
1169
|
|
|
|
|
1850
|
my $info; |
|
2577
|
1169
|
|
|
|
|
3407
|
my $xp = $xpose + $config->{settings}->{transpose}; |
|
2578
|
1169
|
100
|
100
|
|
|
3518
|
$xp += $capo if $capo && $decapo; |
|
2579
|
1169
|
|
|
|
|
2787
|
my $xc = $config->{settings}->{transcode}; |
|
2580
|
1169
|
|
|
|
|
2816
|
my $global_dir = $config->{settings}->{transpose} <=> 0; |
|
2581
|
1169
|
|
|
|
|
1798
|
my $unk; |
|
2582
|
|
|
|
|
|
|
|
|
2583
|
|
|
|
|
|
|
# When called from {define} ignore xc/xp. |
|
2584
|
1169
|
100
|
|
|
|
2729
|
$xc = $xp = '' if $def; |
|
2585
|
|
|
|
|
|
|
|
|
2586
|
1169
|
|
|
|
|
4571
|
$info = ChordPro::Chords::known_chord($chord); |
|
2587
|
1169
|
100
|
|
|
|
3025
|
if ( $info ) { |
|
2588
|
|
|
|
|
|
|
warn( "Parsing chord: \"$chord\" found \"", |
|
2589
|
881
|
50
|
|
|
|
2268
|
$info->name, "\" in ", $info->{_via}, "\n" ) if $debug > 1; |
|
2590
|
881
|
100
|
|
|
|
3674
|
return ChordPro::Chord::NC->new( { name => $info->name } ) |
|
2591
|
|
|
|
|
|
|
if $info->is_nc; |
|
2592
|
874
|
50
|
|
|
|
2154
|
$info->dump if $debug > 1; |
|
2593
|
|
|
|
|
|
|
} |
|
2594
|
|
|
|
|
|
|
else { |
|
2595
|
288
|
|
|
|
|
975
|
$info = ChordPro::Chords::parse_chord($chord); |
|
2596
|
|
|
|
|
|
|
warn( "Parsing chord: \"$chord\" parsed ok [", |
|
2597
|
|
|
|
|
|
|
$info->{system}, |
|
2598
|
288
|
50
|
66
|
|
|
1808
|
"]\n" ) if $info && $debug > 1; |
|
2599
|
|
|
|
|
|
|
} |
|
2600
|
1162
|
|
|
|
|
2665
|
$unk = !defined $info; |
|
2601
|
|
|
|
|
|
|
|
|
2602
|
1162
|
100
|
100
|
|
|
7597
|
if ( ( $def || $xp || $xc ) |
|
|
|
|
66
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
2603
|
|
|
|
|
|
|
&& |
|
2604
|
|
|
|
|
|
|
! ($info && $info->is_xpxc ) ) { |
|
2605
|
22
|
|
|
|
|
78
|
local $::config->{settings}->{chordnames} = "relaxed"; |
|
2606
|
22
|
|
|
|
|
62
|
$info = ChordPro::Chords::parse_chord($chord); |
|
2607
|
|
|
|
|
|
|
} |
|
2608
|
|
|
|
|
|
|
|
|
2609
|
1162
|
100
|
66
|
|
|
4782
|
unless ( ( $info && $info->is_xpxc ) |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
2610
|
|
|
|
|
|
|
|| |
|
2611
|
|
|
|
|
|
|
( $def && !( $xc || $xp ) ) ) { |
|
2612
|
|
|
|
|
|
|
do_warn( "Cannot parse", |
|
2613
|
|
|
|
|
|
|
$xp ? "/transpose" : "", |
|
2614
|
|
|
|
|
|
|
$xc ? "/transcode" : "", |
|
2615
|
|
|
|
|
|
|
" chord \"$chord\"\n" ) |
|
2616
|
8
|
0
|
33
|
|
|
85
|
if $xp || $xc || $config->{debug}->{chords}; |
|
|
|
0
|
33
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
2617
|
|
|
|
|
|
|
} |
|
2618
|
|
|
|
|
|
|
|
|
2619
|
1162
|
50
|
66
|
|
|
4262
|
if ( $xp && $info |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
2620
|
|
|
|
|
|
|
&& !( $xc && ( $xc eq "nashville" || $xc eq "roman" ) ) ) { |
|
2621
|
|
|
|
|
|
|
# For transpose/transcode, chord must be wellformed. |
|
2622
|
158
|
|
100
|
|
|
916
|
my $i = $info->transpose( $xp, |
|
2623
|
|
|
|
|
|
|
$xpose_dir // $global_dir); |
|
2624
|
|
|
|
|
|
|
# Prevent self-references. |
|
2625
|
158
|
50
|
|
|
|
986
|
$i->{xp} = $info unless $i eq $info; |
|
2626
|
158
|
|
|
|
|
299
|
$info = $i; |
|
2627
|
|
|
|
|
|
|
warn( "Parsing chord: \"$chord\" transposed ", |
|
2628
|
|
|
|
|
|
|
sprintf("%+d", $xp), " to \"", |
|
2629
|
|
|
|
|
|
|
$info->name, "\"", |
|
2630
|
158
|
0
|
|
|
|
602
|
( $self->{meta}->{key} ? (" key ".$self->{meta}->{key}->[-1]) : ()), |
|
|
|
50
|
|
|
|
|
|
|
2631
|
|
|
|
|
|
|
"\n" ) if $debug > 1; |
|
2632
|
|
|
|
|
|
|
} |
|
2633
|
|
|
|
|
|
|
# else: warning has been given. |
|
2634
|
|
|
|
|
|
|
|
|
2635
|
1162
|
100
|
|
|
|
2630
|
if ( $info ) { # TODO roman? |
|
2636
|
|
|
|
|
|
|
# Look it up now, the name may change by transcode. |
|
2637
|
1153
|
100
|
33
|
|
|
3355
|
if ( my $i = ChordPro::Chords::known_chord($info) ) { |
|
|
|
50
|
|
|
|
|
|
|
2638
|
|
|
|
|
|
|
warn( "Parsing chord: \"$chord\" found ", |
|
2639
|
|
|
|
|
|
|
$i->name, " for ", $info->name, |
|
2640
|
879
|
50
|
|
|
|
2049
|
" in ", $i->{_via}, "\n" ) if $debug > 1; |
|
2641
|
|
|
|
|
|
|
$info = $i->new({ %$i, name => $info->name, |
|
2642
|
|
|
|
|
|
|
$info->{xp} ? ( xp => $info->{xp} ) : (), |
|
2643
|
879
|
100
|
|
|
|
6153
|
$info->{xc} ? ( xc => $info->{xc} ) : (), |
|
|
|
50
|
|
|
|
|
|
|
2644
|
|
|
|
|
|
|
}) ; |
|
2645
|
879
|
|
|
|
|
6380
|
$unk = 0; |
|
2646
|
|
|
|
|
|
|
} |
|
2647
|
|
|
|
|
|
|
elsif ( $config->{instrument}->{type} eq 'keyboard' |
|
2648
|
|
|
|
|
|
|
&& ( my $k = ChordPro::Chords::get_keys($info) ) ) { |
|
2649
|
0
|
0
|
|
|
|
0
|
warn( "Parsing chord: \"$chord\" \"", $info->name, "\" not found ", |
|
2650
|
|
|
|
|
|
|
"but we know what to do\n" ) if $debug > 1; |
|
2651
|
0
|
|
|
|
|
0
|
$info = $info->new({ %$info, keys => $k }) ; |
|
2652
|
0
|
|
|
|
|
0
|
$unk = 0; |
|
2653
|
|
|
|
|
|
|
} |
|
2654
|
|
|
|
|
|
|
else { |
|
2655
|
274
|
50
|
|
|
|
704
|
warn( "Parsing chord: \"$chord\" \"", $info->name, |
|
2656
|
|
|
|
|
|
|
"\" not found in song/config chords\n" ) if $debug; |
|
2657
|
|
|
|
|
|
|
# warn("XX \'", $info->agnostic, "\'\n"); |
|
2658
|
274
|
|
|
|
|
660
|
$unk = 1; |
|
2659
|
|
|
|
|
|
|
} |
|
2660
|
|
|
|
|
|
|
} |
|
2661
|
|
|
|
|
|
|
|
|
2662
|
1162
|
100
|
66
|
|
|
3774
|
if ( $xc && $info ) { |
|
2663
|
20
|
|
|
|
|
40
|
my $key_ord; |
|
2664
|
|
|
|
|
|
|
$key_ord = $self->{chordsinfo}->{$self->{meta}->{key}->[-1]}->{root_ord} |
|
2665
|
20
|
50
|
|
|
|
145
|
if $self->{meta}->{key}; |
|
2666
|
20
|
50
|
33
|
|
|
117
|
if ( $xcmov && !defined $key_ord ) { |
|
2667
|
0
|
|
|
|
|
0
|
do_warn("Warning: Transcoding to $xc without key may yield unexpected results\n"); |
|
2668
|
0
|
|
|
|
|
0
|
undef $xcmov; |
|
2669
|
|
|
|
|
|
|
} |
|
2670
|
20
|
|
|
|
|
108
|
my $i = $info->transcode( $xc, $key_ord ); |
|
2671
|
|
|
|
|
|
|
# Prevent self-references. |
|
2672
|
20
|
50
|
|
|
|
136
|
$i->{xc} = $info unless $i eq $info; |
|
2673
|
20
|
|
|
|
|
41
|
$info = $i; |
|
2674
|
|
|
|
|
|
|
warn( "Parsing chord: \"$chord\" transcoded to ", |
|
2675
|
|
|
|
|
|
|
$info->name, |
|
2676
|
|
|
|
|
|
|
" (", $info->{system}, ")", |
|
2677
|
20
|
0
|
|
|
|
87
|
defined($key_ord) ? " key ".$self->{meta}->{key}->[-1] : "", |
|
|
|
50
|
|
|
|
|
|
|
2678
|
|
|
|
|
|
|
"\n" ) if $debug > 1; |
|
2679
|
20
|
100
|
|
|
|
87
|
if ( my $i = ChordPro::Chords::known_chord($info) ) { |
|
2680
|
8
|
50
|
|
|
|
17
|
warn( "Parsing chord: \"$chord\" found \"", |
|
2681
|
|
|
|
|
|
|
$info->name, "\" in song/config chords\n" ) if $debug > 1; |
|
2682
|
8
|
|
|
|
|
21
|
$unk = 0; |
|
2683
|
|
|
|
|
|
|
} |
|
2684
|
|
|
|
|
|
|
} |
|
2685
|
|
|
|
|
|
|
# else: warning has been given. |
|
2686
|
|
|
|
|
|
|
|
|
2687
|
1162
|
100
|
|
|
|
2879
|
if ( ! $info ) { |
|
2688
|
9
|
50
|
|
|
|
35
|
if ( my $i = ChordPro::Chords::known_chord($chord) ) { |
|
2689
|
0
|
|
|
|
|
0
|
$info = $i; |
|
2690
|
|
|
|
|
|
|
warn( "Parsing chord: \"$chord\" found \"", |
|
2691
|
|
|
|
|
|
|
$chord, "\" in ", |
|
2692
|
0
|
0
|
|
|
|
0
|
$i->{_via}, "\n" ) if $debug > 1; |
|
2693
|
0
|
|
|
|
|
0
|
$unk = 0; |
|
2694
|
|
|
|
|
|
|
} |
|
2695
|
|
|
|
|
|
|
} |
|
2696
|
|
|
|
|
|
|
|
|
2697
|
1162
|
100
|
100
|
|
|
3251
|
unless ( $info || $def ) { |
|
2698
|
8
|
100
|
66
|
|
|
46
|
if ( $config->{debug}->{chords} || ! $warned_chords{$chord}++ ) { |
|
2699
|
1
|
50
|
|
|
|
3
|
warn("Parsing chord: \"$chord\" unknown\n") if $debug; |
|
2700
|
1
|
50
|
|
|
|
7
|
do_warn( "Unknown chord: \"$chord\"\n" ) |
|
2701
|
|
|
|
|
|
|
unless $chord =~ /^n\.?c\.?$/i; |
|
2702
|
|
|
|
|
|
|
} |
|
2703
|
|
|
|
|
|
|
} |
|
2704
|
|
|
|
|
|
|
|
|
2705
|
1162
|
100
|
|
|
|
2824
|
if ( $info ) { |
|
2706
|
|
|
|
|
|
|
$info->{key} = $self->{meta}->{key}->[-1] |
|
2707
|
1153
|
50
|
|
|
|
6710
|
unless $config->{settings}->{'enharmonic-transpose'}; |
|
2708
|
1153
|
0
|
|
|
|
3053
|
warn( "Parsing chord: \"$chord\" okay: \"", |
|
|
|
50
|
|
|
|
|
|
|
2709
|
|
|
|
|
|
|
$info->name, "\" \"", |
|
2710
|
|
|
|
|
|
|
$info->chord_display, "\"", |
|
2711
|
|
|
|
|
|
|
$unk ? " but unknown" : "", |
|
2712
|
|
|
|
|
|
|
"\n" ) if $debug > 1; |
|
2713
|
1153
|
|
|
|
|
4701
|
$self->store_chord($info); |
|
2714
|
1153
|
|
|
|
|
3824
|
return $info; |
|
2715
|
|
|
|
|
|
|
} |
|
2716
|
|
|
|
|
|
|
|
|
2717
|
9
|
50
|
|
|
|
23
|
warn( "Parsing chord: \"$chord\" not found\n" ) if $debug; |
|
2718
|
9
|
|
|
|
|
30
|
return; |
|
2719
|
|
|
|
|
|
|
} |
|
2720
|
|
|
|
|
|
|
|
|
2721
|
|
|
|
|
|
|
sub store_chord { |
|
2722
|
1173
|
|
|
1173
|
0
|
2613
|
my ( $self, $info ) = @_; |
|
2723
|
1173
|
|
|
|
|
4349
|
$self->{chordsinfo}->{$info->name} = $info; |
|
2724
|
1173
|
|
|
|
|
3605
|
$info->name; |
|
2725
|
|
|
|
|
|
|
} |
|
2726
|
|
|
|
|
|
|
|
|
2727
|
|
|
|
|
|
|
sub structurize { |
|
2728
|
13
|
|
|
13
|
0
|
38
|
my ( $self ) = @_; |
|
2729
|
|
|
|
|
|
|
|
|
2730
|
13
|
50
|
|
|
|
54
|
return if $self->{structure} eq "structured"; |
|
2731
|
|
|
|
|
|
|
|
|
2732
|
13
|
|
|
|
|
47
|
my @body; |
|
2733
|
13
|
|
|
|
|
36
|
my $context = $def_context; |
|
2734
|
|
|
|
|
|
|
|
|
2735
|
13
|
|
|
|
|
22
|
foreach my $item ( @{ $self->{body} } ) { |
|
|
13
|
|
|
|
|
52
|
|
|
2736
|
239
|
100
|
66
|
|
|
724
|
if ( $item->{type} eq "empty" && $item->{context} eq $def_context ) { |
|
2737
|
56
|
|
|
|
|
103
|
$context = $def_context; |
|
2738
|
56
|
|
|
|
|
125
|
next; |
|
2739
|
|
|
|
|
|
|
} |
|
2740
|
183
|
100
|
100
|
|
|
520
|
if ( $item->{type} eq "songline" && $item->{context} eq '' ){ # A songline should have a context - non means verse |
|
2741
|
36
|
|
|
|
|
58
|
$item->{context} = 'verse'; |
|
2742
|
|
|
|
|
|
|
} |
|
2743
|
183
|
100
|
|
|
|
386
|
if ( $context ne $item->{context} ) { |
|
2744
|
43
|
|
|
|
|
175
|
push( @body, { type => $context = $item->{context}, body => [] } ); |
|
2745
|
|
|
|
|
|
|
} |
|
2746
|
183
|
100
|
|
|
|
309
|
if ( $context ) { |
|
2747
|
135
|
|
|
|
|
185
|
push( @{ $body[-1]->{body} }, $item ); |
|
|
135
|
|
|
|
|
304
|
|
|
2748
|
|
|
|
|
|
|
} |
|
2749
|
|
|
|
|
|
|
else { |
|
2750
|
48
|
|
|
|
|
84
|
push( @body, $item ); |
|
2751
|
|
|
|
|
|
|
} |
|
2752
|
|
|
|
|
|
|
} |
|
2753
|
13
|
|
|
|
|
103
|
$self->{body} = [ @body ]; |
|
2754
|
13
|
|
|
|
|
67
|
$self->{structure} = "structured"; |
|
2755
|
|
|
|
|
|
|
} |
|
2756
|
|
|
|
|
|
|
|
|
2757
|
|
|
|
|
|
|
sub dump { |
|
2758
|
0
|
|
|
0
|
0
|
|
my ( $self, $full ) = @_; |
|
2759
|
0
|
|
0
|
|
|
|
$full ||= 0; |
|
2760
|
|
|
|
|
|
|
|
|
2761
|
0
|
0
|
|
|
|
|
if ( $full == 2 ) { |
|
2762
|
0
|
|
|
|
|
|
return ::dump($self->{body}); |
|
2763
|
|
|
|
|
|
|
} |
|
2764
|
0
|
|
|
|
|
|
my $a = dclone($self); |
|
2765
|
0
|
|
|
|
|
|
$a->{config} = ref(delete($a->{config})); |
|
2766
|
0
|
0
|
|
|
|
|
unless ( $full ) { |
|
2767
|
0
|
|
|
|
|
|
for my $ci ( keys %{$a->{chordsinfo}} ) { |
|
|
0
|
|
|
|
|
|
|
|
2768
|
0
|
|
|
|
|
|
$a->{chordsinfo}{$ci} = $a->{chordsinfo}{$ci}->simplify; |
|
2769
|
|
|
|
|
|
|
} |
|
2770
|
|
|
|
|
|
|
} |
|
2771
|
0
|
|
|
|
|
|
::dump($a); |
|
2772
|
|
|
|
|
|
|
} |
|
2773
|
|
|
|
|
|
|
|
|
2774
|
|
|
|
|
|
|
1; |