File Coverage

blib/lib/App/Music/PlayTab/LyChord.pm
Criterion Covered Total %
statement 122 133 91.7
branch 41 52 78.8
condition 8 14 57.1
subroutine 6 6 100.0
pod 0 1 0.0
total 177 206 85.9


line stmt bran cond sub pod time code
1             #! perl
2              
3             # Author : Johan Vromans
4             # Created On : Tue Jan 15 15:59:16 2008
5             # Last Modified By: Johan Vromans
6             # Last Modified On: Wed Oct 5 12:00:16 2011
7             # Update Count : 10
8             # Status : Unknown, Use with caution!
9              
10             package App::Music::PlayTab::LyChord;
11              
12 2     2   15873 use strict;
  2         5  
  2         52  
13 2     2   9 use warnings;
  2         3  
  2         82  
14              
15             our $VERSION = "1.006";
16              
17 2     2   661 use App::Music::PlayTab::Note;
  2         4  
  2         46  
18 2     2   10 use Carp;
  2         4  
  2         86  
19 2     2   11 use base qw(App::Music::PlayTab::Chord);
  2         4  
  2         740  
20              
21             sub parse {
22 132     132 0 29763 my ($self, $chord) = @_;
23              
24 132 50       408 $self = $self->new unless ref($self);
25              
26 132         276 $self->{_unparsed} = $chord;
27 132 50       349 $self->{_debug} = 1 if $chord =~ s/^\?//;
28 132         235 $self->{_isrest} = 0;
29 132         218 delete( $self->{bass} );
30              
31 132         228 my $key = $chord;
32 132         213 my $mod = '';
33              
34             # Catch rests.
35 132 100       543 if ( $chord =~ /(^[rs])(\d+\.*)?/ ) {
36 7         13 $self->{_isrest} = 1;
37 7 100       20 if ( defined $2 ) {
38 6         21 my ($dur, $xt) = $2 =~ /^(\d+)(\.*)$/;
39 6         17 $dur = $self->duration_base / $dur;
40 6         16 $dur *= 1.5 foreach split(//, $xt);
41 6         12 $self->{duration} = $dur;
42             }
43 7         18 return $self;
44             }
45              
46             # Treat power chords as modifications.
47 125         272 $chord =~ s;^([[a-g](?:es|is)?(\d+\.*)?)/;$1:/;;
48              
49             # Separate the chord key from the modifications.
50 125 50       621 if ( $chord =~ /(^[a-g](?:es|is)?)(\d+\.*)?(?::(.*))?/ ) {
51 125         312 $key = $1;
52 125         227 $mod = $3;
53 125 100       305 if ( defined $2 ) {
54 75         295 my ($dur, $xt) = $2 =~ /^(\d+)(\.*)$/;
55 75         239 $dur = $self->duration_base / $dur;
56 75         221 $dur *= 1.5 foreach split(//, $xt);
57 75         148 $self->{duration} = $dur;
58             }
59             }
60              
61             # Parse key.
62 125         215 eval { $self->{key} = App::Music::PlayTab::Note->parse($key) };
  125         406  
63             croak("Unrecognized pitch in chord: ".$self->{_unparsed})
64 125 50       424 unless defined $self->{key};
65              
66             # Encodings: a bit is set in $chflags for every note in the chord.
67             # The corresponding element of $chmods is 0 (natural), -1
68             # (lowered), 1 (raised) or undef (suppressed).
69              
70 125         231 my $chflags = '';
71 125         374 my @chmods = (0) x 14;
72              
73             # Assume major triad.
74 125         377 vec($chflags,3,1) = 1;
75 125         308 vec($chflags,5,1) = 1;
76 125         252 $chmods[3] = 0;
77 125         201 $chmods[5] = 0;
78              
79             # Then other modifications.
80 125   66     445 while ( $mod && $mod ne '' ) {
81              
82 58 100       150 if ( $mod =~ /^maj7?(?:\.?(.*))?/ ) { # Maj7
83 3         10 $mod = $+;
84 3         10 vec($chflags,7,1) = 1;
85 3         8 $chmods[7] = 1;
86 3         13 next;
87             }
88 55 100       138 if ( $mod =~ /^m(?:in)?7(?:\.?(.*))?/ ) { # Minor triad 7
89 1         3 $mod = $+;
90 1         3 vec($chflags,7,1) = 1;
91 1         2 $chmods[7] = 0;
92 1         3 vec($chflags,3,1) = 1;
93 1         2 $chmods[3] = -1;
94 1         4 next;
95             }
96 54 100       215 if ( $mod =~ /^m(?:\.?(.*))?/ ) { # Minor triad
97 36         94 $mod = $+;
98 36         88 vec($chflags,3,1) = 1;
99 36         79 $chmods[3] = -1;
100 36         111 next;
101             }
102              
103             # Transform 7sus4 into something we can parse.
104 18         36 $mod =~ s/^(\d+)sus(\d?)/sus$2.$1/;
105              
106 18 100       45 if ( $mod =~ /^sus2(?:\.(.*))?/ ) { # Suspended second
107 1         2 $mod = $+;
108 1         3 vec($chflags,3,1) = 0;
109 1         3 undef $chmods[3];
110 1         4 next;
111             }
112 17 100       53 if ( $mod =~ /^sus4?(?:\.(.*))?/ ) { # Suspended fourth
113 3         11 $mod = $+;
114 3         14 vec($chflags,4,1) = 1; # does it?
115 3         9 undef $chmods[3];
116 3         8 $chmods[4] = 0;
117 3         13 next;
118             }
119 14 100       33 if ( $mod =~ /^aug(?:\.?(.*))?/ ) { # Augmented
120 1         3 $mod = $+;
121 1         10 vec($chflags,5,1) = 1;
122 1         3 $chmods[5] = 1;
123 1         3 next;
124             }
125 13 100       32 if ( $mod =~ /^dim(?:\.?(.*))?/ ) { # Diminished
126 1         4 $mod = $+;
127 1         3 vec($chflags,3,1) = 1;
128 1         3 vec($chflags,5,1) = 1;
129 1         2 vec($chflags,7,1) = 1;
130 1         3 $chmods[3] = -1;
131 1         2 $chmods[5] = -1;
132 1         2 $chmods[7] = -1;
133 1         3 next;
134             }
135             # if ( $mod =~ /^%(.*)/ ) { # half-diminished 7
136             # $mod = $+;
137             # $chflags = '';
138             # vec($chflags,3,1) = 1;
139             # vec($chflags,5,1) = 1;
140             # vec($chflags,7,1) = 1;
141             # $chmods[3] = -1;
142             # $chmods[5] = -1;
143             # $chmods[7] = 0;
144             # next;
145             # }
146 12 100       55 if ( $mod =~ /^(2|5|6|7|9|10|11|13)([-+])?(?:\.(.*))?/ ) { # addition
147 10         25 $mod = $3;
148             # 13th implies 11th implies 9th implies 7th...
149 10 100 66     52 if ( $1 > 7 && !(vec($chflags,7,1)) ) {
150 5         17 vec($chflags,7,1) = 1;
151 5         10 $chmods[7] = 0;
152             }
153 10 100 66     48 if ( $1 > 10 && !(vec($chflags,9,1)) ) {
154 3         10 vec($chflags,9,1) = 1;
155 3         7 $chmods[9] = 0;
156             }
157 10 100 66     36 if ( $1 > 11 && !(vec($chflags,11,1)) ) {
158 2         5 vec($chflags,11,1) = 1;
159 2         6 $chmods[11] = 1;
160             }
161 10         27 vec($chflags,$1,1) = 1;
162 10         24 $chmods[$1] = 0;
163 10 100       34 if ( defined $2 ) {
164 1 50       5 $chmods[$1] = ($2 eq '+') ? 1 : -1;
165             }
166 10         30 next;
167             }
168 2 50       10 if ( $mod =~ /^\^(\d+)(?:\.(.*))?/ ) {
169 2         5 $mod = $2;
170 2         7 vec($chflags,$1,1) = 1;
171 2         6 undef $chmods[$1];
172 2         7 next;
173             }
174              
175             # Power chords.
176 0 0       0 if ( $mod =~ /^\/(.+)/ ) {
177 0         0 my @ch = split(/\//, $1);
178 0         0 foreach my $c ( @ch ) {
179             # my $p = eval { App::Music::PlayTab::Note->parse($c) };
180 0         0 my $p = eval { App::Music::PlayTab::Chord->parse($c) };
  0         0  
181             croak("Unrecognized bass of chord: ".$self->{_unparsed})
182 0 0       0 unless defined $p;
183 0   0     0 $self->{bass} ||= [];
184 0         0 push(@{$self->{bass}}, $p);
  0         0  
185             }
186 0         0 last;
187             }
188 0         0 croak("Unrecognized modification of chord: ".$self->{_unparsed});
189             }
190              
191 125         261 my @vec = (0);
192 125         279 for ( 1..13 ) {
193 1625 100       3594 next unless vec($chflags,$_,1);
194 277 100       635 next unless defined $chmods[$_];
195 272         566 push (@vec, (0,0,2,4,5,7,9,10,12,14,16,17,19,21)[$_]+$chmods[$_]);
196             }
197              
198 125         324 $self->{vec} = [@vec];
199              
200             warn("=> Chord ", $self->{_unparsed}, ": ", $self->{key}->key,
201             " (", $self->{key}->name, ") [ @vec ]\n")
202 125 50       350 if $self->{_debug};
203              
204 125         401 $self;
205             }
206              
207             1;
208              
209             __END__