File Coverage

blib/lib/MIDI/Drummer/Tiny.pm
Criterion Covered Total %
statement 141 283 49.8
branch 25 64 39.0
condition 15 75 20.0
subroutine 26 41 63.4
pod 23 24 95.8
total 230 487 47.2


line stmt bran cond sub pod time code
1             package MIDI::Drummer::Tiny;
2             our $AUTHORITY = 'cpan:GENE';
3              
4             # ABSTRACT: Glorified metronome
5              
6             our $VERSION = '0.4012';
7              
8 1     1   1361 use strictures 2;
  1         1596  
  1         39  
9 1     1   186 use Data::Dumper::Compact qw(ddc);
  1         2  
  1         11  
10 1     1   99 use List::Util qw(sum0);
  1         2  
  1         55  
11 1     1   440 use Math::Bezier ();
  1         914  
  1         26  
12 1     1   475 use MIDI::Util qw(dura_size reverse_dump set_chan_patch set_time_signature);
  1         24681  
  1         73  
13 1     1   535 use Moo;
  1         11311  
  1         4  
14 1     1   1837 use Music::Duration ();
  1         456  
  1         30  
15 1     1   456 use Music::RhythmSet::Util qw(upsize);
  1         4337  
  1         68  
16 1     1   465 use namespace::clean;
  1         11370  
  1         9  
17              
18 1     1   356 use constant TICKS => 96; # Per quarter note
  1         2  
  1         5785  
19              
20              
21             sub BUILD {
22 4     4 0 21 my ( $self, $args ) = @_;
23              
24 4         68 $self->score->noop( 'c' . $self->channel, 'V' . $self->volume );
25              
26 4 50       202 if ($self->kit) {
27 0         0 $self->score->control_change($self->channel, 0, 120);
28 0         0 $self->score->patch_change($self->channel, $self->kit)
29             }
30              
31 4         38 $self->score->set_tempo( int( 60_000_000 / $self->bpm ) );
32              
33 4         91 $self->score->control_change($self->channel, 91, $self->reverb);
34              
35             # Add a TS to the score but don't reset the beats if given
36 4         73 $self->set_time_sig( $self->signature, !$args->{beats} );
37             }
38              
39              
40             has verbose => ( is => 'ro', default => sub { 0 } );
41             has kit => ( is => 'ro', default => sub { 0 } );
42             has reverb => ( is => 'ro', default => sub { 15 } );
43             has channel => ( is => 'ro', default => sub { 9 } );
44             has volume => ( is => 'ro', default => sub { 100 } );
45             has bpm => ( is => 'rw', default => sub { 120 } );
46             has file => ( is => 'ro', default => sub { 'MIDI-Drummer.mid' } );
47             has bars => ( is => 'ro', default => sub { 4 } );
48             has score => ( is => 'ro', default => sub { MIDI::Simple->new_score } );
49             has signature => ( is => 'rw', default => sub { '4/4' });
50             has beats => ( is => 'rw', default => sub { 4 } );
51             has divisions => ( is => 'rw', default => sub { 4 } );
52             has counter => ( is => 'rw', default => sub { 0 } );
53              
54              
55             has click => ( is => 'ro', default => sub { 33 } );
56             has bell => ( is => 'ro', default => sub { 34 } );
57             has kick => ( is => 'ro', default => sub { 35 } ); # Alt: 36
58             has acoustic_bass => ( is => 'ro', default => sub { 35 } );
59             has electric_bass => ( is => 'ro', default => sub { 36 } );
60             has side_stick => ( is => 'ro', default => sub { 37 } );
61             has snare => ( is => 'ro', default => sub { 38 } ); # Alt: 40
62             has acoustic_snare => ( is => 'ro', default => sub { 38 } );
63             has electric_snare => ( is => 'ro', default => sub { 40 } );
64             has clap => ( is => 'ro', default => sub { 39 } );
65             has open_hh => ( is => 'ro', default => sub { 46 } );
66             has closed_hh => ( is => 'ro', default => sub { 42 } );
67             has pedal_hh => ( is => 'ro', default => sub { 44 } );
68             has crash1 => ( is => 'ro', default => sub { 49 } );
69             has crash2 => ( is => 'ro', default => sub { 57 } );
70             has splash => ( is => 'ro', default => sub { 55 } );
71             has china => ( is => 'ro', default => sub { 52 } );
72             has ride1 => ( is => 'ro', default => sub { 51 } );
73             has ride2 => ( is => 'ro', default => sub { 59 } );
74             has ride_bell => ( is => 'ro', default => sub { 53 } );
75             has hi_tom => ( is => 'ro', default => sub { 50 } );
76             has hi_mid_tom => ( is => 'ro', default => sub { 48 } );
77             has low_mid_tom => ( is => 'ro', default => sub { 47 } );
78             has low_tom => ( is => 'ro', default => sub { 45 } );
79             has hi_floor_tom => ( is => 'ro', default => sub { 43 } );
80             has low_floor_tom => ( is => 'ro', default => sub { 41 } );
81             has tambourine => ( is => 'ro', default => sub { 54 } );
82             has cowbell => ( is => 'ro', default => sub { 56 } );
83             has vibraslap => ( is => 'ro', default => sub { 58 } );
84             has hi_bongo => ( is => 'ro', default => sub { 60 } );
85             has low_bongo => ( is => 'ro', default => sub { 61 } );
86             has mute_hi_conga => ( is => 'ro', default => sub { 62 } );
87             has open_hi_conga => ( is => 'ro', default => sub { 63 } );
88             has low_conga => ( is => 'ro', default => sub { 64 } );
89             has high_timbale => ( is => 'ro', default => sub { 65 } );
90             has low_timbale => ( is => 'ro', default => sub { 66 } );
91             has high_agogo => ( is => 'ro', default => sub { 67 } );
92             has low_agogo => ( is => 'ro', default => sub { 68 } );
93             has cabasa => ( is => 'ro', default => sub { 69 } );
94             has maracas => ( is => 'ro', default => sub { 70 } );
95             has short_whistle => ( is => 'ro', default => sub { 71 } );
96             has long_whistle => ( is => 'ro', default => sub { 72 } );
97             has short_guiro => ( is => 'ro', default => sub { 73 } );
98             has long_guiro => ( is => 'ro', default => sub { 74 } );
99             has claves => ( is => 'ro', default => sub { 75 } );
100             has hi_wood_block => ( is => 'ro', default => sub { 76 } );
101             has low_wood_block => ( is => 'ro', default => sub { 77 } );
102             has mute_cuica => ( is => 'ro', default => sub { 78 } );
103             has open_cuica => ( is => 'ro', default => sub { 79 } );
104             has mute_triangle => ( is => 'ro', default => sub { 80 } );
105             has open_triangle => ( is => 'ro', default => sub { 81 } );
106              
107              
108             has whole => (is => 'ro', default => sub { 'wn' });
109             has triplet_whole => (is => 'ro', default => sub { 'twn' });
110             has dotted_whole => (is => 'ro', default => sub { 'dwn' });
111             has double_dotted_whole => (is => 'ro', default => sub { 'ddwn' });
112             has half => (is => 'ro', default => sub { 'hn' });
113             has triplet_half => (is => 'ro', default => sub { 'thn' });
114             has dotted_half => (is => 'ro', default => sub { 'dhn' });
115             has double_dotted_half => (is => 'ro', default => sub { 'ddhn' });
116             has quarter => (is => 'ro', default => sub { 'qn' });
117             has triplet_quarter => (is => 'ro', default => sub { 'tqn' });
118             has dotted_quarter => (is => 'ro', default => sub { 'dqn' });
119             has double_dotted_quarter => (is => 'ro', default => sub { 'ddqn' });
120             has eighth => (is => 'ro', default => sub { 'en' });
121             has triplet_eighth => (is => 'ro', default => sub { 'ten' });
122             has dotted_eighth => (is => 'ro', default => sub { 'den' });
123             has double_dotted_eighth => (is => 'ro', default => sub { 'dden' });
124             has sixteenth => (is => 'ro', default => sub { 'sn' });
125             has triplet_sixteenth => (is => 'ro', default => sub { 'tsn' });
126             has dotted_sixteenth => (is => 'ro', default => sub { 'dsn' });
127             has double_dotted_sixteenth => (is => 'ro', default => sub { 'ddsn' });
128             has thirtysecond => (is => 'ro', default => sub { 'xn' });
129             has triplet_thirtysecond => (is => 'ro', default => sub { 'txn' });
130             has dotted_thirtysecond => (is => 'ro', default => sub { 'dxn' });
131             has double_dotted_thirtysecond => (is => 'ro', default => sub { 'ddxn' });
132             has sixtyfourth => (is => 'ro', default => sub { 'yn' });
133             has triplet_sixtyfourth => (is => 'ro', default => sub { 'tyn' });
134             has dotted_sixtyfourth => (is => 'ro', default => sub { 'dyn' });
135             has double_dotted_sixtyfourth => (is => 'ro', default => sub { 'ddyn' });
136             has onetwentyeighth => (is => 'ro', default => sub { 'zn' });
137             has triplet_onetwentyeighth => (is => 'ro', default => sub { 'tzn' });
138             has dotted_onetwentyeighth => (is => 'ro', default => sub { 'dzn' });
139             has double_dotted_onetwentyeighth => (is => 'ro', default => sub { 'ddzn' });
140              
141              
142             sub note {
143 122     122 1 5812 my ($self, @spec) = @_;
144 122 50       348 my $size = $spec[0] =~ /^d(\d+)$/ ? $1 / TICKS : dura_size($spec[0]);
145             #warn __PACKAGE__,' L',__LINE__,' ',,"$spec[0] => $size\n";
146 122         1192 $self->counter( $self->counter + $size );
147 122         295 return $self->score->n(@spec);
148             }
149              
150              
151             sub accent_note {
152 0     0 1 0 my $self = shift;
153 0         0 my $accent = shift;
154 0         0 my $resume = $self->score->Volume;
155 0         0 $self->score->Volume($accent);
156 0         0 $self->note(@_);
157 0         0 $self->score->Volume($resume);
158             }
159              
160              
161             sub rest {
162 556     556 1 957 my ($self, @spec) = @_;
163 556 50       1320 my $size = $spec[0] =~ /^d(\d+)$/ ? $1 / TICKS : dura_size($spec[0]);
164             #warn __PACKAGE__,' L',__LINE__,' ',,"$spec[0] => $size\n";
165 556         5257 $self->counter( $self->counter + $size );
166 556         1233 return $self->score->r(@spec);
167             }
168              
169              
170             sub count_in {
171 0     0 1 0 my ($self, $args) = @_;
172              
173 0         0 my $bars = $self->bars;
174 0         0 my $patch = $self->closed_hh;
175              
176 0 0 0     0 if ($args && ref $args) {
177 0 0       0 $bars = $args->{bars} if defined $args->{bars};
178 0 0       0 $patch = $args->{patch} if defined $args->{patch};
179             }
180             else {
181 0         0 $bars = $args; # given a simple integer
182             }
183              
184 0         0 for my $i ( 1 .. $self->beats * $bars ) {
185 0         0 $self->note( $self->quarter, $patch );
186             }
187             }
188              
189              
190             sub metronome38 {
191 0     0 1 0 my $self = shift;
192 0   0     0 my $bars = shift || $self->bars;
193              
194 0         0 for ( 1 .. $bars ) {
195 0         0 $self->note( $self->eighth, $self->closed_hh, $self->kick );
196 0         0 $self->note( $self->eighth, $self->closed_hh);
197 0         0 $self->note( $self->eighth, $self->closed_hh, $self->snare );
198             }
199             }
200              
201              
202             sub metronome34 {
203 0     0 1 0 my $self = shift;
204 0   0     0 my $bars = shift || $self->bars;
205              
206 0         0 for ( 1 .. $bars ) {
207 0         0 $self->note( $self->quarter, $self->ride1, $self->kick );
208 0         0 $self->note( $self->quarter, $self->ride1 );
209 0         0 $self->note( $self->quarter, $self->ride1, $self->snare );
210             }
211             }
212              
213              
214             sub metronome44 {
215 0     0 1 0 my $self = shift;
216 0   0     0 my $bars = shift || $self->bars;
217 0   0     0 my $flag = shift // 0;
218              
219 0         0 my $i = 0;
220              
221 0         0 for my $n ( 1 .. $self->beats * $bars ) {
222 0 0       0 if ( $n % 2 == 0 )
223             {
224 0         0 $self->note( $self->quarter, $self->closed_hh, $self->snare );
225             }
226             else {
227 0 0       0 if ( $flag == 0 )
228             {
229 0         0 $self->note( $self->quarter, $self->closed_hh, $self->kick );
230             }
231             else
232             {
233 0 0       0 if ( $i % 2 == 0 )
234             {
235 0         0 $self->note( $self->quarter, $self->closed_hh, $self->kick );
236             }
237             else
238             {
239 0         0 $self->note( $self->eighth, $self->closed_hh, $self->kick );
240 0         0 $self->note( $self->eighth, $self->kick );
241             }
242             }
243              
244 0         0 $i++;
245             }
246             }
247             }
248              
249              
250             sub metronome44swing {
251 0     0 1 0 my $self = shift;
252 0   0     0 my $bars = shift || $self->bars;
253              
254 0         0 for my $n ( 1 .. $bars ) {
255 0         0 $self->note( $self->quarter, $self->ride1, $self->kick );
256 0         0 $self->note( $self->triplet_eighth, $self->ride1 );
257 0         0 $self->rest( $self->triplet_eighth );
258 0         0 $self->note( $self->triplet_eighth, $self->ride1, $self->kick );
259 0         0 $self->note( $self->quarter, $self->ride1, $self->snare );
260 0         0 $self->note( $self->triplet_eighth, $self->ride1, $self->kick );
261 0         0 $self->rest( $self->triplet_eighth );
262 0         0 $self->note( $self->triplet_eighth, $self->ride1 );
263             }
264             }
265              
266              
267             sub metronome54 {
268 0     0 1 0 my $self = shift;
269 0   0     0 my $bars = shift || $self->bars;
270 0         0 for my $n (1 .. $bars) {
271 0         0 $self->note($self->quarter, $self->closed_hh, $self->kick);
272 0         0 $self->note($self->quarter, $self->closed_hh);
273 0         0 $self->note($self->quarter, $self->closed_hh, $self->snare);
274 0         0 $self->note($self->quarter, $self->closed_hh);
275 0 0       0 if ($n % 2) {
276 0         0 $self->note($self->quarter, $self->closed_hh);
277             }
278             else {
279 0         0 $self->note($self->eighth, $self->closed_hh);
280 0         0 $self->note($self->eighth, $self->kick);
281             }
282             }
283             }
284              
285              
286             sub metronome58 {
287 0     0 1 0 my $self = shift;
288 0   0     0 my $bars = shift || $self->bars;
289 0         0 for my $n (1 .. $bars) {
290 0         0 $self->note($self->eighth, $self->closed_hh, $self->kick);
291 0         0 $self->note($self->eighth, $self->closed_hh);
292 0         0 $self->note($self->eighth, $self->closed_hh, $self->snare);
293 0         0 $self->note($self->eighth, $self->closed_hh);
294 0         0 $self->note($self->eighth, $self->closed_hh);
295             }
296             }
297              
298              
299             sub metronome68 {
300 0     0 1 0 my $self = shift;
301 0   0     0 my $bars = shift || $self->bars;
302 0         0 for my $n (1 .. $bars) {
303 0         0 $self->note($self->eighth, $self->closed_hh, $self->kick);
304 0         0 $self->note($self->eighth, $self->closed_hh);
305 0         0 $self->note($self->eighth, $self->closed_hh);
306 0         0 $self->note($self->eighth, $self->closed_hh, $self->snare);
307 0         0 $self->note($self->eighth, $self->closed_hh);
308 0         0 $self->note($self->eighth, $self->closed_hh);
309             }
310             }
311              
312              
313             sub metronome74 {
314 0     0 1 0 my $self = shift;
315 0   0     0 my $bars = shift || $self->bars;
316 0         0 for my $n (1 .. $bars) {
317 0         0 $self->note($self->quarter, $self->closed_hh, $self->kick);
318 0         0 $self->note($self->quarter, $self->closed_hh);
319 0         0 $self->note($self->quarter, $self->closed_hh, $self->snare);
320 0         0 $self->note($self->eighth, $self->closed_hh);
321 0         0 $self->note($self->eighth, $self->kick);
322 0         0 $self->note($self->quarter, $self->closed_hh, $self->kick);
323 0         0 $self->note($self->quarter, $self->closed_hh, $self->snare);
324 0         0 $self->note($self->quarter, $self->closed_hh);
325             }
326             }
327              
328              
329             sub metronome78 {
330 0     0 1 0 my $self = shift;
331 0   0     0 my $bars = shift || $self->bars;
332 0         0 for my $n (1 .. $bars) {
333 0         0 $self->note($self->eighth, $self->closed_hh, $self->kick);
334 0         0 $self->note($self->eighth, $self->closed_hh);
335 0         0 $self->note($self->eighth, $self->closed_hh);
336 0         0 $self->note($self->eighth, $self->closed_hh, $self->kick);
337 0         0 $self->note($self->eighth, $self->closed_hh, $self->snare);
338 0         0 $self->note($self->eighth, $self->closed_hh);
339 0         0 $self->note($self->eighth, $self->closed_hh);
340             }
341             }
342              
343              
344             sub flam {
345 0     0 1 0 my ($self, $spec, $grace, $patch, $accent) = @_;
346 0   0     0 $grace ||= $self->snare;
347 0   0     0 $patch ||= $self->snare;
348 0         0 my $x = $MIDI::Simple::Length{$spec};
349 0         0 my $y = $MIDI::Simple::Length{ $self->sixtyfourth };
350 0         0 my $z = sprintf '%0.f', ($x - $y) * TICKS;
351 0   0     0 $accent ||= sprintf '%0.f', $self->score->Volume / 2;
352 0 0       0 if ($grace eq 'r') {
353 0         0 $self->rest($self->sixtyfourth);
354             }
355             else {
356 0         0 $self->accent_note($accent, $self->sixtyfourth, $grace);
357             }
358 0         0 $self->note('d' . $z, $patch);
359             }
360              
361              
362             sub roll {
363 0     0 1 0 my ($self, $length, $spec, $patch) = @_;
364 0   0     0 $patch ||= $self->snare;
365 0         0 my $x = $MIDI::Simple::Length{$length};
366 0         0 my $y = $MIDI::Simple::Length{$spec};
367 0         0 my $z = sprintf '%0.f', $x / $y;
368 0         0 $self->note($spec, $patch) for 1 .. $z;
369             }
370              
371              
372             sub crescendo_roll {
373 0     0 1 0 my ($self, $span, $length, $spec, $patch) = @_;
374 0   0     0 $patch ||= $self->snare;
375 0         0 my ($i, $j, $k) = @$span;
376 0         0 my $x = $MIDI::Simple::Length{$length};
377 0         0 my $y = $MIDI::Simple::Length{$spec};
378 0         0 my $z = sprintf '%0.f', $x / $y;
379 0 0       0 if ($k) {
380 0         0 my $bezier = Math::Bezier->new(
381             1, $i,
382             $z, $i,
383             $z, $j,
384             );
385 0         0 for (my $n = 0; $n <= 1; $n += (1 / ($z - 1))) {
386 0         0 my (undef, $v) = $bezier->point($n);
387 0         0 $v = sprintf '%0.f', $v;
388             # warn(__PACKAGE__,' ',__LINE__," $n INC: $v\n");
389 0         0 $self->accent_note($v, $spec, $patch);
390             }
391             }
392             else {
393 0         0 my $v = sprintf '%0.f', ($j - $i) / ($z - 1);
394             # warn(__PACKAGE__,' ',__LINE__," VALUE: $v\n");
395 0         0 for my $n (1 .. $z) {
396 0 0       0 if ($n == $z) {
397 0 0       0 if ($i < $j) {
    0          
398 0         0 $i += $j - $i;
399             }
400             elsif ($i > $j) {
401 0         0 $i -= $i - $j;
402             }
403             }
404             # warn(__PACKAGE__,' ',__LINE__," $n INC: $i\n");
405 0         0 $self->accent_note($i, $spec, $patch);
406 0         0 $i += $v;
407             }
408             }
409             }
410              
411              
412             sub pattern {
413 34     34 1 639 my ( $self, %args ) = @_;
414              
415 34   33     87 $args{instrument} ||= $self->snare;
416 34   50     81 $args{patterns} ||= [];
417 34   33     177 $args{beats} ||= $self->beats;
418 34   50     164 $args{negate} ||= 0;
419 34   50     125 $args{repeat} ||= 1;
420              
421 34 50       45 return unless @{ $args{patterns} };
  34         78  
422              
423             # set size and duration
424 34         48 my $size;
425 34 100       65 if ( $args{duration} ) {
426 33   50     85 $size = dura_size( $args{duration} ) || 1;
427             }
428             else {
429 1         4 $size = 4 / length( $args{patterns}->[0] );
430 1         6 my $dump = reverse_dump('length');
431 1   33     407 $args{duration} = $dump->{$size} || $self->quarter;
432             }
433              
434             # set the default beat-string variations
435             $args{vary} ||= {
436 556     556   1054 0 => sub { $self->rest( $args{duration} ) },
437 121     121   257 1 => sub { $self->note( $args{duration}, $args{instrument} ) },
438 34   50     629 };
439              
440 34         138 set_chan_patch( $self->score, $self->channel, $args{instrument} );
441              
442 34         1822 for my $pattern (@{ $args{patterns} }) {
  34         71  
443 34 50       88 next if $pattern =~ /^0+$/;
444              
445 34 50       77 $pattern =~ tr/01/10/ if $args{negate};
446              
447 34         81 for ( 1 .. $args{repeat} ) {
448 34         183 for my $bit ( split //, $pattern ) {
449 677         21182 $args{vary}{$bit}->($self);
450             }
451             }
452             }
453             }
454              
455              
456             sub sync_patterns {
457 11     11 1 35 my ($self, %patterns) = @_;
458              
459 11         23 my $master_duration = delete $patterns{duration};
460              
461 11         21 my @subs;
462 11         26 for my $instrument (keys %patterns) {
463             push @subs, sub {
464             $self->pattern(
465             instrument => $instrument,
466 33 50   33   1334 patterns => $patterns{$instrument},
467             $master_duration ? (duration => $master_duration) : (),
468             );
469             },
470 33         132 }
471              
472 11         34 $self->sync(@subs);
473             }
474              
475              
476             sub add_fill {
477 11     11 1 12100 my ($self, $fill, %patterns) = @_;
478              
479             $fill ||= sub {
480             return {
481 10     10   41 duration => 8,
482             $self->open_hh => '000',
483             $self->snare => '111',
484             $self->kick => '000',
485             };
486 11   100     84 };
487 11         27 my $fill_patterns = $fill->($self);
488 11 50       46 print 'Fill: ', ddc($fill_patterns) if $self->verbose;
489 11   50     33 my $fill_duration = delete $fill_patterns->{duration} || 8;
490 11         34 my $fill_length = length((values %$fill_patterns)[0]);
491              
492 11         14 my %lengths;
493 11         28 for my $instrument (keys %patterns) {
494 33         47 $lengths{$instrument} = sum0 map { length $_ } @{ $patterns{$instrument} };
  33         103  
  33         61  
495             }
496              
497 11         34 my $lcm = _multilcm($fill_duration, values %lengths);
498 11 50       28 print "LCM: $lcm\n" if $self->verbose;
499              
500 11         22 my $size = 4 / $lcm;
501 11         30 my $dump = reverse_dump('length');
502 11   66     3629 my $master_duration = $dump->{$size} || $self->eighth; # XXX this || is not right
503 11 50       37 print "Size: $size, Duration: $master_duration\n" if $self->verbose;
504              
505 11 100       42 my $fill_chop = $fill_duration == $lcm
506             ? $fill_length
507             : int($lcm / $fill_length) + 1;
508 11 50       29 print "Chop: $fill_chop\n" if $self->verbose;
509              
510 11         19 my %fresh_patterns;
511 11         26 for my $instrument (keys %patterns) {
512             # get a single "flattened" pattern as an arrayref
513 33         671 my $pattern = [ map { split //, $_ } @{ $patterns{$instrument} } ];
  33         132  
  33         61  
514             # the fresh pattern is possibly upsized with the LCM
515             $fresh_patterns{$instrument} = @$pattern < $lcm
516 33 100       95 ? [ join '', @{ upsize($pattern, $lcm) } ]
  29         72  
517             : [ join '', @$pattern ];
518             }
519 11 50       320 print 'Patterns: ', ddc(\%fresh_patterns) if $self->verbose;
520              
521 11         18 my %replacement;
522 11         28 for my $instrument (keys %$fill_patterns) {
523             # get a single "flattened" pattern as a zero-pre-padded arrayref
524 33         176 my $pattern = [ split //, sprintf '%0*s', $fill_duration, $fill_patterns->{$instrument} ];
525             # the fresh pattern string is possibly upsized with the LCM
526             my $fresh = @$pattern < $lcm
527 33 100       96 ? join '', @{ upsize($pattern, $lcm) }
  15         33  
528             : join '', @$pattern;
529             # the replacement string is the tail of the fresh pattern string
530 33         586 $replacement{$instrument} = substr $fresh, -$fill_chop;
531             }
532 11 50       37 print 'Replacements: ', ddc(\%replacement) if $self->verbose;
533              
534 11         16 my %replaced;
535 11         26 for my $instrument (keys %fresh_patterns) {
536             # get the string to replace
537 33         48 my $string = join '', @{ $fresh_patterns{$instrument} };
  33         65  
538             # replace the tail of the string
539 33         49 my $pos = length $replacement{$instrument};
540 33         62 substr $string, -$pos, $pos, $replacement{$instrument};
541 33 50       68 print "$instrument: $string\n" if $self->verbose;
542             # prepare the replaced pattern for syncing
543 33         74 $replaced{$instrument} = [ $string ];
544             }
545              
546             $self->sync_patterns(
547 11         49 %replaced,
548             duration => $master_duration,
549             );
550              
551 11         689 return \%replaced;
552             }
553              
554              
555             sub set_time_sig {
556 5     5 1 1956 my ($self, $signature, $set) = @_;
557 5 50       19 $self->signature($signature) if $signature;
558 5   100     17 $set //= 1;
559 5 100       11 if ($set) {
560 4         17 my ($beats, $divisions) = split /\//, $self->signature;
561 4         11 $self->beats($beats);
562 4         9 $self->divisions($divisions);
563             }
564 5         21 set_time_signature($self->score, $self->signature);
565             }
566              
567              
568             sub set_bpm {
569 1     1 1 3474 my ($self, $bpm) = @_;
570 1         5 $self->bpm($bpm);
571 1         8 $self->score->set_tempo( int( 60_000_000 / $self->bpm ) );
572             }
573              
574              
575             sub sync {
576 11     11 1 18 my $self = shift;
577 11         43 $self->score->synch(@_);
578             }
579              
580              
581             sub write {
582 0     0 1 0 my $self = shift;
583 0         0 $self->score->write_score( $self->file );
584             }
585              
586             # lifted from https://www.perlmonks.org/?node_id=56906
587             sub _gcf {
588 33     33   55 my ($x, $y) = @_;
589 33         100 ($x, $y) = ($y, $x % $y) while $y;
590 33         92 return $x;
591             }
592             sub _lcm {
593 33     33   73 return($_[0] * $_[1] / _gcf($_[0], $_[1]));
594             }
595             sub _multilcm {
596 11     11   18 my $x = shift;
597 11         36 $x = _lcm($x, shift) while @_;
598 11         20 return $x;
599             }
600              
601             1;
602              
603             __END__