File Coverage

blib/lib/MIDI/Praxis/Variation.pm
Criterion Covered Total %
statement 103 104 99.0
branch 35 38 92.1
condition 18 24 75.0
subroutine 20 20 100.0
pod 16 16 100.0
total 192 202 95.0


line stmt bran cond sub pod time code
1             package MIDI::Praxis::Variation;
2             our $AUTHORITY = 'cpan:GENE';
3              
4             # ABSTRACT: Variation techniques used in music composition
5              
6 1     1   55375 use strict;
  1         7  
  1         25  
7 1     1   4 use warnings;
  1         2  
  1         29  
8              
9             our $VERSION = '0.0607';
10              
11 1     1   568 use MIDI::Simple ();
  1         17632  
  1         23  
12              
13 1     1   6 use Exporter 'import';
  1         2  
  1         1000  
14              
15             our @EXPORT = qw(
16             augmentation
17             diminution
18             dur
19             inversion
20             note_name_to_number
21             note2num
22             ntup
23             original
24             notes2nums
25             raugmentation
26             rdiminution
27             retrograde
28             retrograde_inversion
29             transposition
30             tye
31             tie_durations
32             );
33             our %EXPORT_TAGS = (all => [qw(
34             augmentation
35             diminution
36             dur
37             inversion
38             note_name_to_number
39             note2num
40             ntup
41             original
42             notes2nums
43             raugmentation
44             rdiminution
45             retrograde
46             retrograde_inversion
47             transposition
48             tye
49             tie_durations
50             )] );
51              
52              
53 1     1 1 422 sub note2num { note_name_to_number(@_) }
54              
55             sub note_name_to_number {
56 52     52 1 1249 my ($in) = @_;
57              
58 52 100       75 return () unless $in;
59              
60 51         58 my $note_number = -1;
61              
62 51 100       131 if ($in =~ /^([A-Za-z]+)(\d+)/s) {
63             $note_number = $MIDI::Simple::Note{$1} + $2 * 12
64 50 50       134 if exists $MIDI::Simple::Note{$1};
65             }
66              
67 51         81 return $note_number;
68             }
69              
70              
71              
72 1     1 1 437 sub notes2nums { original(@_) }
73              
74             sub original {
75 13     13 1 883 my @notes = @_;
76              
77 13 100       21 return () unless @notes;
78              
79 12         21 my @ret = map { note_name_to_number($_) } @notes;
  36         50  
80              
81 12         27 return @ret;
82             }
83              
84              
85              
86             sub retrograde {
87 2     2 1 855 my @notes = @_;
88              
89 2         5 my @ret = ();
90              
91 2 100       6 return () unless @notes;
92              
93 1         4 @ret = reverse original(@notes);
94              
95 1         4 return @ret;
96             }
97              
98              
99              
100             sub transposition {
101 10     10 1 1714 my ($delta, @notes) = @_;
102              
103 10 100 66     33 return () unless defined $delta && @notes;
104              
105 9         12 my @ret = ();
106              
107 9 50       27 if ($notes[0] =~ /[A-G]/) {
108 9         17 @ret = original(@notes);
109             }
110             else {
111 0         0 @ret = @notes;
112             }
113              
114 9         13 for (@ret) {
115 27         35 $_ += $delta;
116             }
117              
118 9         17 return @ret;
119             }
120              
121              
122              
123             sub inversion {
124 7     7 1 1696 my ($axis, @notes) = @_;
125              
126 7 100 66     26 return () unless $axis && @notes;
127              
128 6         13 my $center = note_name_to_number($axis);
129 6         10 my $first = note_name_to_number($notes[0]);
130 6         10 my $delta = $center - $first;
131              
132 6         18 my @transposed = transposition($delta, @notes);
133              
134             # XXX WTF?
135 6         8 my @ret = map { 2 * $center - $_ } @transposed;
  18         25  
136              
137 6         14 return @ret;
138             }
139              
140              
141              
142             sub retrograde_inversion {
143 4     4 1 1714 my ($axis, @notes) = @_;
144              
145 4 100 66     21 return () unless $axis && @notes;
146              
147 3         6 my @rev_notes = ();
148 3         4 my @ret = ();
149              
150 3         5 @rev_notes = reverse @notes;
151              
152 3         7 @ret = inversion($axis, @rev_notes);
153              
154 3         7 return @ret;
155             }
156              
157              
158              
159             sub dur {
160 24     24 1 1228 my ($tempo, $arg) = (MIDI::Simple::Tempo, @_);
161              
162 24 100       357 return () unless $arg;
163              
164 23         27 my $dur = 0;
165              
166 23 100       74 if ($arg =~ /^d(\d+)$/) {
    50          
167 3         8 $dur = 0 + $1;
168             }
169             elsif (exists $MIDI::Simple::Length{$arg}) { # length spec
170 20         29 $dur = 0 + ($tempo * $MIDI::Simple::Length{$arg});
171             }
172              
173 23         49 return $dur;
174             }
175              
176              
177              
178 1     1 1 458 sub tie_durations { tye(@_) }
179              
180             sub tye {
181 4     4 1 1178 my @dur_or_len = @_;
182              
183 4 100       12 return () unless @dur_or_len;
184              
185 3         4 my $sum = 0;
186              
187 3         5 for my $dura (@dur_or_len) {
188 5         9 $sum += dur($dura);
189             }
190              
191 3         7 return $sum;
192             }
193              
194              
195              
196             sub raugmentation {
197 9     9 1 2295 my ($ratio, @dur_or_len) = @_;
198              
199 9 100 100     46 return () unless $ratio && 1 < $ratio && @dur_or_len;
      66        
200              
201 7         9 my $sum = 0;
202              
203 7         11 for my $dura (@dur_or_len) {
204 8         13 $sum += dur($dura) * $ratio;
205             }
206              
207 7         12 return $sum;
208             }
209              
210              
211              
212             sub rdiminution {
213 9     9 1 2318 my ($ratio, @dur_or_len) = @_;
214              
215 9 100 100     43 return () unless $ratio && 1 < $ratio && @dur_or_len;
      66        
216              
217 7         15 my $sum = 0;
218              
219 7         7 for my $dura (@dur_or_len) {
220 8         17 $sum += dur($dura) / $ratio;
221             }
222              
223 7         24 return sprintf '%.0f', $sum;
224             }
225              
226              
227              
228             sub augmentation {
229 3     3 1 1124 my @dur_or_len = @_;
230              
231 3 100       10 return () unless @dur_or_len;
232              
233 2         4 my @ret = ();
234              
235 2         12 for my $dura (@dur_or_len) {
236 3         6 my $elem = 'd';
237 3         7 $elem .= raugmentation(2, $dura);
238 3         7 push @ret, $elem;
239             }
240              
241 2         7 return @ret;
242             }
243              
244              
245              
246             sub diminution {
247 3     3 1 1326 my @dur_or_len = @_;
248              
249 3 100       9 return () unless @dur_or_len;
250              
251 2         5 my @ret = ();
252              
253 2         3 for my $dura (@dur_or_len) {
254 3         5 my $elem = 'd';
255 3         5 $elem .= rdiminution(2, $dura);
256 3         6 push @ret, $elem;
257             }
258              
259 2         6 return @ret;
260             }
261              
262              
263              
264             sub ntup {
265 5     5 1 2062 my ($n, @notes) = @_;
266              
267 5 100 66     23 return () unless defined $n && @notes;
268              
269 4         7 my @ret = ();
270              
271 4 100       8 if (@notes >= $n) {
272 3         8 for my $index (0 .. @notes - $n) {
273 7         15 push @ret, @notes[$index .. $index + $n - 1];
274             }
275             }
276              
277 4         12 return @ret;
278             }
279              
280              
281             1;
282              
283             __END__