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   14876 use strict;
  5         10  
  5         120  
13 5     5   23 use warnings;
  5         32  
  5         168  
14              
15             our $VERSION = "1.010";
16              
17 5     5   608 use App::Music::PlayTab::Note;
  5         9  
  5         85  
18 5     5   22 use Carp;
  5         10  
  5         12684  
19              
20             sub new {
21 240     240 0 31784 my $pkg = shift;
22 240         804 bless {}, $pkg;
23             }
24              
25             sub parse {
26 343     343 0 34972 my ($self, $chord) = @_;
27              
28 343 50       896 $self = $self->new unless ref($self);
29 343         578 delete $self->{bass};
30 343         499 delete $self->{high};
31 343         651 $self->{_unparsed} = $chord;
32 343         652 $chord = lc($chord);
33 343         690 $self->{_debug} = $chord =~ s/^\?//;
34 343         543 my $key = $chord;
35 343         510 my $mod = '';
36              
37             # Separate the chord key from the modifications.
38 343 50       1505 if ( $chord =~ /(^[a-g*](\#|b|s(?!us)|es|is)?)(.*)/ ) {
39 343         738 $key = $1;
40 343         597 $mod = $3;
41             }
42              
43             # Parse key.
44 343         517 eval { $self->{key} = App::Music::PlayTab::Note->parse($key) };
  343         991  
45             croak("Unrecognized pitch in chord: ".$self->{_unparsed})
46 343 50       868 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         568 my $chflags = '';
53 343         992 my @chmods = (0) x 14;
54              
55             # Assume major triad.
56 343         901 vec($chflags,3,1) = 1;
57 343         772 vec($chflags,5,1) = 1;
58 343         562 $chmods[3] = 0;
59 343         468 $chmods[5] = 0;
60              
61 343         551 $mod =~ s/^-/min/; # Minor triad
62 343         488 $mod =~ s/^\+/aug/; # Augmented triad
63 343         520 $mod =~ s/^0/dim/; # Diminished
64              
65             # Then other modifications.
66 343         816 while ( $mod ne '' ) {
67              
68 99 100       261 if ( $mod =~ /^[() ._](.*)/ ) { # syntactic sugar
69 11         31 $mod = $+;
70 11         32 next;
71             }
72 88 100       204 if ( $mod =~ /^maj7?(.*)/ ) { # Maj7
73 8         19 $mod = $+;
74 8         16 vec($chflags,7,1) = 1;
75 8         14 $chmods[7] = 1;
76 8         22 next;
77             }
78 80 100       191 if ( $mod =~ /^(min|m)7(.*)/ ) { # Minor triad + 7
79 7         17 $mod = $+;
80 7         27 vec($chflags,7,1) = 1;
81 7         16 $chmods[7] = 0;
82 7         15 vec($chflags,3,1) = 1;
83 7         12 $chmods[3] = -1;
84 7         18 next;
85             }
86 73 100       175 if ( $mod =~ /^(min|m)(.*)/ ) { # Minor triad
87 12         26 $mod = $+;
88 12         36 vec($chflags,3,1) = 1;
89 12         23 $chmods[3] = -1;
90 12         30 next;
91             }
92 61 100       134 if ( $mod =~ /^sus2(.*)/ ) { # Suspended second
93 1         3 $mod = $+;
94 1         3 vec($chflags,3,1) = 0;
95 1         3 undef $chmods[3];
96 1         3 next;
97             }
98 60 100       126 if ( $mod =~ /^sus4?(.*)/ ) { # Suspended fourth
99 3         9 $mod = $+;
100 3         7 vec($chflags,4,1) = 1; # does it?
101 3         8 undef $chmods[3];
102 3         6 $chmods[4] = 0;
103 3         8 next;
104             }
105 57 100       123 if ( $mod =~ /^aug(.*)/ ) { # Augmented
106 2         5 $mod = $+;
107 2         5 vec($chflags,5,1) = 1;
108 2         4 $chmods[5] = 1;
109 2         5 next;
110             }
111 55 100       141 if ( $mod =~ /^(o|dim)(.*)/ ) { # Diminished
112 3         7 $mod = $+;
113 3         8 vec($chflags,3,1) = 1;
114 3         7 vec($chflags,5,1) = 1;
115 3         8 vec($chflags,7,1) = 1;
116 3         4 $chmods[3] = -1;
117 3         5 $chmods[5] = -1;
118 3         5 $chmods[7] = -1;
119 3         8 next;
120             }
121 52 100       115 if ( $mod =~ /^%(.*)/ ) { # half-diminished 7
122 6         17 $mod = $+;
123 6         11 $chflags = '';
124 6         13 vec($chflags,3,1) = 1;
125 6         14 vec($chflags,5,1) = 1;
126 6         12 vec($chflags,7,1) = 1;
127 6         11 $chmods[3] = -1;
128 6         8 $chmods[5] = -1;
129 6         11 $chmods[7] = 0;
130 6         16 next;
131             }
132 46 100       161 if ( $mod =~ /^([\#b])?(2|5|6|7|9|10|11|13)(.*)/ ) { # addition
133 36         75 $mod = $+;
134             # 13th implies 11th implies 9th implies 7th...
135 36 100 100     156 if ( $2 > 7 && !(vec($chflags,7,1)) ) {
136 14         32 vec($chflags,7,1) = 1;
137 14         28 $chmods[7] = 0;
138             }
139 36 100 66     114 if ( $2 > 10 && !(vec($chflags,9,1)) ) {
140 13         27 vec($chflags,9,1) = 1;
141 13         19 $chmods[9] = 0;
142             }
143 36 100 66     119 if ( $2 > 11 && !(vec($chflags,11,1)) ) {
144 8         16 vec($chflags,11,1) = 1;
145 8         14 $chmods[11] = 1;
146             }
147 36         93 vec($chflags,$2,1) = 1;
148 36         73 $chmods[$2] = 0;
149 36 100       94 if ( defined $1 ) {
150 16 100       44 $chmods[$2] = ($1 eq '#') ? 1 : -1;
151             }
152 36         86 next;
153             }
154 10 100       36 if ( $mod =~ /^no\s*(\d+)(st|nd|rd|th)?(.*)/ ) {
155 6         15 $mod = $+;
156 6         16 vec($chflags,$1,1) = 1;
157 6         13 undef $chmods[$1];
158 6         16 next;
159             }
160              
161             # High add-ons.
162 4 100       14 if ( $mod =~ /^\\([^\/]+)(.*)/ ) {
163 2         7 my @ch = split(/\\/, $1);
164 2         5 $mod = $2;
165 2         6 foreach my $c ( @ch ) {
166 2         4 my $p = eval { __PACKAGE__->new->parse($c) };
  2         8  
167             croak("Unrecognized add of chord: ".$self->{_unparsed})
168 2 50       6 unless defined $p;
169 2   50     14 $self->{high} ||= [];
170 2         3 push(@{$self->{high}}, $p);
  2         5  
171             }
172 2         5 last;
173             }
174              
175             # Power chords.
176 2 50       8 if ( $mod =~ /^\/(.+)/ ) {
177 2         7 my @ch = split(/\//, $1);
178 2         6 foreach my $c ( @ch ) {
179 2         3 my $p = eval { __PACKAGE__->new->parse($c) };
  2         7  
180             croak("Unrecognized bass of chord: ".$self->{_unparsed})
181 2 50       5 unless defined $p;
182 2   50     11 $self->{bass} ||= [];
183 2         5 push(@{$self->{bass}}, $p);
  2         4  
184             }
185 2         5 last;
186             }
187 0         0 croak("Unrecognized modification of chord: ".$self->{_unparsed});
188             }
189              
190 343         619 my @vec = (0);
191 343         690 for ( 1..13 ) {
192 4459 100       9241 next unless vec($chflags,$_,1);
193 781 100       1607 next unless defined $chmods[$_];
194 772         1425 push (@vec, (0,0,2,4,5,7,9,10,12,14,16,17,19,21)[$_]+$chmods[$_]);
195             }
196              
197 343         747 $self->{vec} = [@vec];
198              
199             warn("=> Chord ", $self->{_unparsed}, ": ", $self->{key}->key,
200             " (", $self->{key}->name, ") [ @vec ]\n")
201 343 50       803 if $self->{_debug};
202              
203             # Traditional chords do not include a duration.
204 343         534 $self->{duration} = 0;
205              
206 343         917 $self;
207             }
208              
209             sub transpose {
210 179     179 0 599 my ($self, $xp) = @_;
211 179 100       380 return $self unless $xp;
212 173         553 $self->{key}->transpose($xp);
213 173 50       376 if ( $self->{bass} ) {
214 0         0 $_->transpose($xp) for @{$self->{bass}};
  0         0  
215             }
216 173 50       350 if ( $self->{high} ) {
217 0         0 $_->transpose($xp) for @{$self->{high}};
  0         0  
218             }
219 173         303 $self;
220             }
221              
222             sub name {
223 342     342 0 1168 my ($self) = @_;
224 342         976 my $res = $self->{key}->name;
225              
226 342         906 my @v = @{$self->{vec}};
  342         764  
227 342         1029 my $v = "@v ";
228 342         543 shift (@v);
229              
230 342 100       1762 if ( $v =~ s/^0 (2 )?4 (6|7|8) / / ) {
    100          
    100          
231 295 100       935 $res .= $2 == 8 ? '+' : '';
232 295 50       714 $v = ' 6' . $v if $2 == 6;
233 295 50       706 $v = ' 2' . $v if defined $1;
234             }
235             elsif ( $v =~ s/^0 3 6 9 / / ) {
236 4         11 $res .= 'o';
237             }
238             elsif ( $v =~ s/^0 (2 )?3 (6|7|8) / / ) {
239 27 100       94 if ( $2 == 6 ) {
240 3 100       10 $res .= ( $v =~ s/^ 10 // ) ? '%' : 'o';
241             }
242             else {
243 24         50 $res .= 'm';
244             }
245 27 50       69 $v = ' 8' . $v if $2 == 8;
246 27 100       67 $v = ' 2' . $v if defined $1;
247             }
248 342         582 $v =~ s/^0 5 7 / 5 7 /;
249 342         542 $v =~ s/ 10 14 18 (21) / $1 /; # 13
250 342         525 $v =~ s/ 10 14 18 (20|22) / 10 $1 /; # 7#13 7b13
251 342         497 $v =~ s/ 10 14 (17) / $1 /; # 11
252 342         504 $v =~ s/ 10 14 (18) / 10 $1 /; # 7#11
253 342         506 $v =~ s/ 10 (14) / $1 /; # 9
254 342         508 $v =~ s/ 10 (15) / 10 $1 /; # 7#9
255 342         502 $v =~ s/ 11 14 18 (21|22) / $1 11 /; # 13#5
256 342         463 $v =~ s/ 11 14 (17|18) / $1 11 /; # 11#5
257 342         493 $v =~ s/ 11 (14|15) / $1 11 /; # 9#5
258 342 100       958 if ( $v =~ s/ 10 / / ) {
    100          
259 14         27 $res .= '7';
260             }
261             elsif ( $v =~ s/^( \d| 10|) 11 / $1/ ) {
262 3         7 $res .= 'maj7';
263             }
264 342 100       1046 if ( $v =~ s/ 5 7 / / ) {
    100          
    100          
265 6         11 $res .= 'sus4';
266             }
267             elsif ( $v =~ s/^0 7 / / ) {
268 6         15 $res .= 'sus2';
269             }
270             elsif ( $v =~ s/^0 4 / / ) {
271 4         8 $res .= 'no5';
272             }
273 342         534 my $res1 = $res; # for debug
274              
275 342         559 chop ($v);
276 342         516 $v =~ s/^ //;
277 342         790 @v = split(' ', $v);
278 342         684 foreach ( @v ) {
279 28         82 $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         534 my $res0 = $res;
282 342         606 $res =~ s/^([^\(]*[^\d])?\((\d+)\)([^\d][^\(]*|)$/$1$2$3/;
283 342         738 $res =~ s/7?(6|\(6\))(9|\(9\))/6.9/;
284 342         742 $res =~ s/(4|\(4\))(5|\(5\))/sus4/;
285 342         612 $res =~ s/(1|\(1\))(5|\(5\))/sus2/;
286              
287 2         6 $res = join("\\", $res, map { $_->name } @{$self->{high}})
  2         6  
288 342 100       802 if $self->{high};
289 2         13 $res = join("/", $res, map { $_->name } @{$self->{bass}})
  2         5  
290 342 100       745 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       736 if $self->{_debug};
296              
297 342         1083 return $res;
298             }
299              
300             sub duration {
301 169     169 0 28956 my ($self) = @_;
302 169         619 $self->{duration};
303             }
304              
305             sub duration_base {
306 162     162 0 268 my ($self) = @_;
307 162         458 16;
308             }
309              
310             sub is_rest {
311 118     118 0 300 shift->{_isrest};
312             }
313              
314             sub ps {
315 249     249 0 40145 my ($self) = @_;
316 249         694 my $res = $self->{key}->ps;
317              
318 249         405 my @v = @{$self->{vec}};
  249         637  
319 249         785 my $v = "@v ";
320 249         430 shift (@v);
321              
322 249 100       1412 if ( $v =~ s/^0 (2 )?4 (6|7|8) / / ) {
    100          
    100          
323 167 100       598 $res .= $2 == 8 ? ' plus' : '';
324 167 50       453 $v = ' 6' . $v if $2 == 6;
325 167 50       400 $v = ' 2' . $v if defined $1;
326             }
327             elsif ( $v =~ s/^0 3 6 9 / / ) {
328 4         11 $res .= ' dim';
329             }
330             elsif ( $v =~ s/^0 (2 )?3 (6|7|8) / / ) {
331 62 100       211 if ( $2 == 6 ) {
332 8 100       30 $res .= ( $v =~ s/^ 10 // ) ? ' hdim' : ' dim';
333             }
334             else {
335 54         126 $res .= ' minus';
336             }
337 62 50       165 $v = ' 8' . $v if $2 == 8;
338 62 100       154 $v = ' 2' . $v if defined $1;
339             }
340 249         442 $v =~ s/^0 5 7 / 5 7 /;
341 249         370 $v =~ s/ 10 14 18 (21) / $1 /; # 13
342 249         405 $v =~ s/ 10 14 18 (20|22) / 10 $1 /; # 7#13 7b13
343 249         376 $v =~ s/ 10 14 (17) / $1 /; # 11
344 249         374 $v =~ s/ 10 14 (18) / 10 $1 /; # 7#11
345 249         390 $v =~ s/ 10 (14) / $1 /; # 9
346 249         377 $v =~ s/ 10 (15) / 10 $1 /; # 7#9
347 249         362 $v =~ s/ 11 14 18 (21|22) / $1 11 /; # 13#5
348 249         650 $v =~ s/ 11 14 (17|18) / $1 11 /; # 11#5
349 249         373 $v =~ s/ 11 (14|15) / $1 11 /; # 9#5
350 249 100       721 if ( $v =~ s/ 10 / / ) {
    100          
351 31         58 $res .= ' (7) addn';
352             }
353             elsif ( $v =~ s/^( \d| 10|) 11 / $1/ ) {
354 11 50       42 $res .= ' -2 0 rmoveto' if $res =~ / flat$/;
355 11         20 $res .= ' delta';
356             }
357 249 100       840 if ( $v =~ s/ 5 7 / / ) {
    100          
    100          
358 6         19 $res .= ' (4) susp';
359             }
360             elsif ( $v =~ s/^0 7 / / ) {
361 6         16 $res .= ' (2) susp';
362             }
363             elsif ( $v =~ s/^0 4 / / ) {
364 4         9 $res .= ' (no5) addn';
365             }
366 249         421 my $res1 = $res; # for debug
367              
368 249         408 chop ($v);
369 249         398 $v =~ s/^ //;
370 249         581 @v = split(' ', $v);
371 249         525 foreach ( @v ) {
372 31         77 $res .= ' ';
373 31         92 $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       612 if ( $self->{high} ) {
381 2         4 my $t = join(" bslash ", map { $_->ps } @{$self->{high}});
  2         7  
  2         5  
382 2         7 $t =~ s/root/hroot/g;
383 2         9 $res = join(" bslash ", $res, $t);
384             }
385              
386 249 100       539 if ( $self->{bass} ) {
387 2         5 my $t = join(" slash ", map { $_->ps } @{$self->{bass}});
  2         10  
  2         5  
388 2         5 $t =~ s/root/hroot/g;
389 2         7 $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       523 if $self->{_debug};
396              
397 249         749 return $res;
398             }
399              
400             1;
401              
402             __END__