File Coverage

blib/lib/App/Music/PlayTab/Chord.pm
Criterion Covered Total %
statement 263 270 97.4
branch 114 132 86.3
condition 9 13 69.2
subroutine 12 12 100.0
pod 0 8 0.0
total 398 435 91.4


line stmt bran cond sub pod time code
1             #! perl
2              
3             # Author : Johan Vromans
4             # Created On : Wed Aug 22 22:33:31 2007
5             # Last Modified By: Johan Vromans
6             # Last Modified On: Mon Jan 16 17:01:50 2012
7             # Update Count : 29
8             # Status : Unknown, Use with caution!
9              
10             package App::Music::PlayTab::Chord;
11              
12 5     5   15933 use strict;
  5         6  
  5         131  
13 5     5   19 use warnings;
  5         5  
  5         186  
14              
15             our $VERSION = "1.010";
16              
17 5     5   691 use App::Music::PlayTab::Note;
  5         6  
  5         83  
18 5     5   17 use Carp;
  5         6  
  5         13333  
19              
20             sub new {
21 240     240 0 15596 my $pkg = shift;
22 240         542 bless {}, $pkg;
23             }
24              
25             sub parse {
26 343     343 0 17775 my ($self, $chord) = @_;
27              
28 343 50       541 $self = $self->new unless ref($self);
29 343         297 delete $self->{bass};
30 343         246 delete $self->{high};
31 343         368 $self->{_unparsed} = $chord;
32 343         393 $chord = lc($chord);
33 343         414 $self->{_debug} = $chord =~ s/^\?//;
34 343         235 my $key = $chord;
35 343         317 my $mod = '';
36              
37             # Separate the chord key from the modifications.
38 343 50       1169 if ( $chord =~ /(^[a-g*](\#|b|s(?!us)|es|is)?)(.*)/ ) {
39 343         400 $key = $1;
40 343         284 $mod = $3;
41             }
42              
43             # Parse key.
44 343         281 eval { $self->{key} = App::Music::PlayTab::Note->parse($key) };
  343         658  
45             croak("Unrecognized pitch in chord: ".$self->{_unparsed})
46 343 50       568 unless defined $self->{key};
47              
48             # Encodings: a bit is set in $chflags for every note in the chord.
49             # The corresponding element of $chmods is 0 (natural), -1
50             # (lowered), 1 (raised) or undef (suppressed).
51              
52 343         300 my $chflags = '';
53 343         608 my @chmods = (0) x 14;
54              
55             # Assume major triad.
56 343         584 vec($chflags,3,1) = 1;
57 343         427 vec($chflags,5,1) = 1;
58 343         270 $chmods[3] = 0;
59 343         244 $chmods[5] = 0;
60              
61 343         295 $mod =~ s/^-/min/; # Minor triad
62 343         227 $mod =~ s/^\+/aug/; # Augmented triad
63 343         212 $mod =~ s/^0/dim/; # Diminished
64              
65             # Then other modifications.
66 343         507 while ( $mod ne '' ) {
67              
68 99 100       176 if ( $mod =~ /^[() ._](.*)/ ) { # syntactic sugar
69 11         16 $mod = $+;
70 11         19 next;
71             }
72 88 100       116 if ( $mod =~ /^maj7?(.*)/ ) { # Maj7
73 8         11 $mod = $+;
74 8         10 vec($chflags,7,1) = 1;
75 8         8 $chmods[7] = 1;
76 8         14 next;
77             }
78 80 100       134 if ( $mod =~ /^(min|m)7(.*)/ ) { # Minor triad + 7
79 7         10 $mod = $+;
80 7         10 vec($chflags,7,1) = 1;
81 7         8 $chmods[7] = 0;
82 7         8 vec($chflags,3,1) = 1;
83 7         7 $chmods[3] = -1;
84 7         11 next;
85             }
86 73 100       113 if ( $mod =~ /^(min|m)(.*)/ ) { # Minor triad
87 12         15 $mod = $+;
88 12         38 vec($chflags,3,1) = 1;
89 12         13 $chmods[3] = -1;
90 12         23 next;
91             }
92 61 100       105 if ( $mod =~ /^sus2(.*)/ ) { # Suspended second
93 1         2 $mod = $+;
94 1         2 vec($chflags,3,1) = 0;
95 1         2 undef $chmods[3];
96 1         2 next;
97             }
98 60 100       79 if ( $mod =~ /^sus4?(.*)/ ) { # Suspended fourth
99 3         5 $mod = $+;
100 3         6 vec($chflags,4,1) = 1; # does it?
101 3         4 undef $chmods[3];
102 3         3 $chmods[4] = 0;
103 3         6 next;
104             }
105 57 100       67 if ( $mod =~ /^aug(.*)/ ) { # Augmented
106 2         3 $mod = $+;
107 2         3 vec($chflags,5,1) = 1;
108 2         3 $chmods[5] = 1;
109 2         2 next;
110             }
111 55 100       93 if ( $mod =~ /^(o|dim)(.*)/ ) { # Diminished
112 3         5 $mod = $+;
113 3         4 vec($chflags,3,1) = 1;
114 3         6 vec($chflags,5,1) = 1;
115 3         4 vec($chflags,7,1) = 1;
116 3         3 $chmods[3] = -1;
117 3         4 $chmods[5] = -1;
118 3         2 $chmods[7] = -1;
119 3         6 next;
120             }
121 52 100       73 if ( $mod =~ /^%(.*)/ ) { # half-diminished 7
122 6         9 $mod = $+;
123 6         6 $chflags = '';
124 6         8 vec($chflags,3,1) = 1;
125 6         8 vec($chflags,5,1) = 1;
126 6         8 vec($chflags,7,1) = 1;
127 6         5 $chmods[3] = -1;
128 6         6 $chmods[5] = -1;
129 6         3 $chmods[7] = 0;
130 6         12 next;
131             }
132 46 100       114 if ( $mod =~ /^([\#b])?(2|5|6|7|9|10|11|13)(.*)/ ) { # addition
133 36         39 $mod = $+;
134             # 13th implies 11th implies 9th implies 7th...
135 36 100 100     120 if ( $2 > 7 && !(vec($chflags,7,1)) ) {
136 14         16 vec($chflags,7,1) = 1;
137 14         15 $chmods[7] = 0;
138             }
139 36 100 66     85 if ( $2 > 10 && !(vec($chflags,9,1)) ) {
140 13         16 vec($chflags,9,1) = 1;
141 13         12 $chmods[9] = 0;
142             }
143 36 100 66     91 if ( $2 > 11 && !(vec($chflags,11,1)) ) {
144 8         10 vec($chflags,11,1) = 1;
145 8         6 $chmods[11] = 1;
146             }
147 36         54 vec($chflags,$2,1) = 1;
148 36         45 $chmods[$2] = 0;
149 36 100       48 if ( defined $1 ) {
150 16 100       29 $chmods[$2] = ($1 eq '#') ? 1 : -1;
151             }
152 36         64 next;
153             }
154 10 100       97 if ( $mod =~ /^no\s*(\d+)(st|nd|rd|th)?(.*)/ ) {
155 6         9 $mod = $+;
156 6         15 vec($chflags,$1,1) = 1;
157 6         12 undef $chmods[$1];
158 6         10 next;
159             }
160              
161             # High add-ons.
162 4 100       11 if ( $mod =~ /^\\([^\/]+)(.*)/ ) {
163 2         5 my @ch = split(/\\/, $1);
164 2         3 $mod = $2;
165 2         4 foreach my $c ( @ch ) {
166 2         3 my $p = eval { __PACKAGE__->new->parse($c) };
  2         4  
167             croak("Unrecognized add of chord: ".$self->{_unparsed})
168 2 50       4 unless defined $p;
169 2   50     11 $self->{high} ||= [];
170 2         2 push(@{$self->{high}}, $p);
  2         5  
171             }
172 2         2 last;
173             }
174              
175             # Power chords.
176 2 50       6 if ( $mod =~ /^\/(.+)/ ) {
177 2         6 my @ch = split(/\//, $1);
178 2         4 foreach my $c ( @ch ) {
179 2         3 my $p = eval { __PACKAGE__->new->parse($c) };
  2         6  
180             croak("Unrecognized bass of chord: ".$self->{_unparsed})
181 2 50       4 unless defined $p;
182 2   50     8 $self->{bass} ||= [];
183 2         2 push(@{$self->{bass}}, $p);
  2         3  
184             }
185 2         3 last;
186             }
187 0         0 croak("Unrecognized modification of chord: ".$self->{_unparsed});
188             }
189              
190 343         302 my @vec = (0);
191 343         391 for ( 1..13 ) {
192 4459 100       5183 next unless vec($chflags,$_,1);
193 781 100       926 next unless defined $chmods[$_];
194 772         804 push (@vec, (0,0,2,4,5,7,9,10,12,14,16,17,19,21)[$_]+$chmods[$_]);
195             }
196              
197 343         465 $self->{vec} = [@vec];
198              
199             warn("=> Chord ", $self->{_unparsed}, ": ", $self->{key}->key,
200             " (", $self->{key}->name, ") [ @vec ]\n")
201 343 50       496 if $self->{_debug};
202              
203             # Traditional chords do not include a duration.
204 343         271 $self->{duration} = 0;
205              
206 343         576 $self;
207             }
208              
209             sub transpose {
210 179     179 0 378 my ($self, $xp) = @_;
211 179 100       220 return $self unless $xp;
212 173         281 $self->{key}->transpose($xp);
213 173 50       227 if ( $self->{bass} ) {
214 0         0 $_->transpose($xp) for @{$self->{bass}};
  0         0  
215             }
216 173 50       216 if ( $self->{high} ) {
217 0         0 $_->transpose($xp) for @{$self->{high}};
  0         0  
218             }
219 173         151 $self;
220             }
221              
222             sub name {
223 342     342 0 660 my ($self) = @_;
224 342         579 my $res = $self->{key}->name;
225              
226 342         314 my @v = @{$self->{vec}};
  342         500  
227 342         683 my $v = "@v ";
228 342         264 shift (@v);
229              
230 342 100       1325 if ( $v =~ s/^0 (2 )?4 (6|7|8) / / ) {
    100          
    100          
231 295 100       564 $res .= $2 == 8 ? '+' : '';
232 295 50       503 $v = ' 6' . $v if $2 == 6;
233 295 50       461 $v = ' 2' . $v if defined $1;
234             }
235             elsif ( $v =~ s/^0 3 6 9 / / ) {
236 4         6 $res .= 'o';
237             }
238             elsif ( $v =~ s/^0 (2 )?3 (6|7|8) / / ) {
239 27 100       65 if ( $2 == 6 ) {
240 3 100       8 $res .= ( $v =~ s/^ 10 // ) ? '%' : 'o';
241             }
242             else {
243 24         22 $res .= 'm';
244             }
245 27 50       53 $v = ' 8' . $v if $2 == 8;
246 27 100       50 $v = ' 2' . $v if defined $1;
247             }
248 342         319 $v =~ s/^0 5 7 / 5 7 /;
249 342         246 $v =~ s/ 10 14 18 (21) / $1 /; # 13
250 342         223 $v =~ s/ 10 14 18 (20|22) / 10 $1 /; # 7#13 7b13
251 342         224 $v =~ s/ 10 14 (17) / $1 /; # 11
252 342         213 $v =~ s/ 10 14 (18) / 10 $1 /; # 7#11
253 342         239 $v =~ s/ 10 (14) / $1 /; # 9
254 342         206 $v =~ s/ 10 (15) / 10 $1 /; # 7#9
255 342         218 $v =~ s/ 11 14 18 (21|22) / $1 11 /; # 13#5
256 342         271 $v =~ s/ 11 14 (17|18) / $1 11 /; # 11#5
257 342         205 $v =~ s/ 11 (14|15) / $1 11 /; # 9#5
258 342 100       571 if ( $v =~ s/ 10 / / ) {
    100          
259 14         14 $res .= '7';
260             }
261             elsif ( $v =~ s/^( \d| 10|) 11 / $1/ ) {
262 3         5 $res .= 'maj7';
263             }
264 342 100       713 if ( $v =~ s/ 5 7 / / ) {
    100          
    100          
265 6         9 $res .= 'sus4';
266             }
267             elsif ( $v =~ s/^0 7 / / ) {
268 6         5 $res .= 'sus2';
269             }
270             elsif ( $v =~ s/^0 4 / / ) {
271 4         4 $res .= 'no5';
272             }
273 342         251 my $res1 = $res; # for debug
274              
275 342         293 chop ($v);
276 342         244 $v =~ s/^ //;
277 342         471 @v = split(' ', $v);
278 342         363 foreach ( @v ) {
279 28         58 $res .= '('.('1','b2','2','b3','3','4','b5','5','#5','6','7','#7','8','b9','9','#9','b11','11','#11','12','b13','13')[$_].')';
280             }
281 342         246 my $res0 = $res;
282 342         301 $res =~ s/^([^\(]*[^\d])?\((\d+)\)([^\d][^\(]*|)$/$1$2$3/;
283 342         437 $res =~ s/7?(6|\(6\))(9|\(9\))/6.9/;
284 342         414 $res =~ s/(4|\(4\))(5|\(5\))/sus4/;
285 342         297 $res =~ s/(1|\(1\))(5|\(5\))/sus2/;
286              
287 2         3 $res = join("\\", $res, map { $_->name } @{$self->{high}})
  2         3  
288 342 100       499 if $self->{high};
289 2         18 $res = join("/", $res, map { $_->name } @{$self->{bass}})
  2         5  
290 342 100       418 if $self->{bass};
291              
292             warn("=> Chord ", $self->{_unparsed}, ": ", $self->{key}->key,
293 0         0 " (", $self->{key}->name, ") [ @{$self->{vec}} ] ->",
294             " $res1 [ $v ] -> $res0 -> $res\n")
295 342 50       402 if $self->{_debug};
296              
297 342         717 return $res;
298             }
299              
300             sub duration {
301 169     169 0 14370 my ($self) = @_;
302 169         465 $self->{duration};
303             }
304              
305             sub duration_base {
306 162     162 0 110 my ($self) = @_;
307 162         304 16;
308             }
309              
310             sub is_rest {
311 118     118 0 189 shift->{_isrest};
312             }
313              
314             sub ps {
315 249     249 0 20961 my ($self) = @_;
316 249         481 my $res = $self->{key}->ps;
317              
318 249         208 my @v = @{$self->{vec}};
  249         389  
319 249         556 my $v = "@v ";
320 249         205 shift (@v);
321              
322 249 100       1142 if ( $v =~ s/^0 (2 )?4 (6|7|8) / / ) {
    100          
    100          
323 167 100       399 $res .= $2 == 8 ? ' plus' : '';
324 167 50       276 $v = ' 6' . $v if $2 == 6;
325 167 50       242 $v = ' 2' . $v if defined $1;
326             }
327             elsif ( $v =~ s/^0 3 6 9 / / ) {
328 4         6 $res .= ' dim';
329             }
330             elsif ( $v =~ s/^0 (2 )?3 (6|7|8) / / ) {
331 62 100       131 if ( $2 == 6 ) {
332 8 100       22 $res .= ( $v =~ s/^ 10 // ) ? ' hdim' : ' dim';
333             }
334             else {
335 54         73 $res .= ' minus';
336             }
337 62 50       103 $v = ' 8' . $v if $2 == 8;
338 62 100       90 $v = ' 2' . $v if defined $1;
339             }
340 249         239 $v =~ s/^0 5 7 / 5 7 /;
341 249         172 $v =~ s/ 10 14 18 (21) / $1 /; # 13
342 249         200 $v =~ s/ 10 14 18 (20|22) / 10 $1 /; # 7#13 7b13
343 249         173 $v =~ s/ 10 14 (17) / $1 /; # 11
344 249         166 $v =~ s/ 10 14 (18) / 10 $1 /; # 7#11
345 249         189 $v =~ s/ 10 (14) / $1 /; # 9
346 249         164 $v =~ s/ 10 (15) / 10 $1 /; # 7#9
347 249         184 $v =~ s/ 11 14 18 (21|22) / $1 11 /; # 13#5
348 249         179 $v =~ s/ 11 14 (17|18) / $1 11 /; # 11#5
349 249         174 $v =~ s/ 11 (14|15) / $1 11 /; # 9#5
350 249 100       497 if ( $v =~ s/ 10 / / ) {
    100          
351 31         34 $res .= ' (7) addn';
352             }
353             elsif ( $v =~ s/^( \d| 10|) 11 / $1/ ) {
354 11 50       26 $res .= ' -2 0 rmoveto' if $res =~ / flat$/;
355 11         13 $res .= ' delta';
356             }
357 249 100       542 if ( $v =~ s/ 5 7 / / ) {
    100          
    100          
358 6         9 $res .= ' (4) susp';
359             }
360             elsif ( $v =~ s/^0 7 / / ) {
361 6         8 $res .= ' (2) susp';
362             }
363             elsif ( $v =~ s/^0 4 / / ) {
364 4         7 $res .= ' (no5) addn';
365             }
366 249         199 my $res1 = $res; # for debug
367              
368 249         224 chop ($v);
369 249         212 $v =~ s/^ //;
370 249         352 @v = split(' ', $v);
371 249         310 foreach ( @v ) {
372 31         34 $res .= ' ';
373 31         66 $res .= ( '(1) addn', '(2) addf', '(2) addn', '(3) addf', '(3) addn',
374             '(4) addn', '(5) addf', '(5) addn', '(5) adds', '(6) addn',
375             '(7) addn', '(7) adds', '(8) addn', '(9) addf', '(9) addn',
376             '(9) adds','(11) addf','(11) addn','(11) adds',
377             '(12) addn','(13) addf','(13) addn' )[$_];
378             }
379              
380 249 100       358 if ( $self->{high} ) {
381 2         4 my $t = join(" bslash ", map { $_->ps } @{$self->{high}});
  2         4  
  2         4  
382 2         5 $t =~ s/root/hroot/g;
383 2         6 $res = join(" bslash ", $res, $t);
384             }
385              
386 249 100       325 if ( $self->{bass} ) {
387 2         3 my $t = join(" slash ", map { $_->ps } @{$self->{bass}});
  2         9  
  2         4  
388 2         4 $t =~ s/root/hroot/g;
389 2         6 $res = join(" slash ", $res, $t);
390             }
391              
392             warn("=> Chord ", $self->{_unparsed}, ": ", $self->{key}->key,
393 0         0 " (", $self->{key}->name, ") [ @{$self->{vec}} ] ->",
394             " $res1 [ $v ] -> $res\n")
395 249 50       321 if $self->{_debug};
396              
397 249         557 return $res;
398             }
399              
400             1;
401              
402             __END__