File Coverage

blib/lib/MIDI/Drummer/Tiny.pm
Criterion Covered Total %
statement 141 278 50.7
branch 25 58 43.1
condition 15 75 20.0
subroutine 26 41 63.4
pod 23 24 95.8
total 230 476 48.3


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.4011';
7              
8 1     1   1258 use strictures 2;
  1         1624  
  1         39  
9 1     1   186 use Data::Dumper::Compact qw(ddc);
  1         3  
  1         10  
10 1     1   150 use List::Util qw(sum0);
  1         3  
  1         57  
11 1     1   439 use Math::Bezier ();
  1         843  
  1         30  
12 1     1   457 use MIDI::Util qw(dura_size reverse_dump set_chan_patch set_time_signature);
  1         23605  
  1         67  
13 1     1   507 use Moo;
  1         11112  
  1         4  
14 1     1   1845 use Music::Duration ();
  1         400  
  1         27  
15 1     1   461 use Music::RhythmSet::Util qw(upsize);
  1         4306  
  1         66  
16 1     1   449 use namespace::clean;
  1         11028  
  1         9  
17              
18 1     1   365 use constant TICKS => 96; # Per quarter note
  1         3  
  1         5540  
19              
20              
21             sub BUILD {
22 4     4 0 22 my ( $self, $args ) = @_;
23              
24 4         37 $self->score->noop( 'c' . $self->channel, 'V' . $self->volume );
25              
26 4 50       179 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         29 $self->score->set_tempo( int( 60_000_000 / $self->bpm ) );
32              
33 4         89 $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         72 $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 5818 my ($self, @spec) = @_;
144 122 50       330 my $size = $spec[0] =~ /^d(\d+)$/ ? $1 / TICKS : dura_size($spec[0]);
145             #warn __PACKAGE__,' L',__LINE__,' ',,"$spec[0] => $size\n";
146 122         1175 $self->counter( $self->counter + $size );
147 122         285 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 977 my ($self, @spec) = @_;
163 556 50       1333 my $size = $spec[0] =~ /^d(\d+)$/ ? $1 / TICKS : dura_size($spec[0]);
164             #warn __PACKAGE__,' L',__LINE__,' ',,"$spec[0] => $size\n";
165 556         5247 $self->counter( $self->counter + $size );
166 556         1222 return $self->score->r(@spec);
167             }
168              
169              
170             sub count_in {
171 0     0 1 0 my $self = shift;
172 0   0     0 my $bars = shift || $self->bars;
173 0         0 for my $i ( 1 .. $self->beats * $bars ) {
174 0         0 $self->note( $self->quarter, $self->closed_hh );
175             }
176             }
177              
178              
179             sub metronome38 {
180 0     0 1 0 my $self = shift;
181 0   0     0 my $bars = shift || $self->bars;
182              
183 0         0 for ( 1 .. $bars ) {
184 0         0 $self->note( $self->eighth, $self->closed_hh, $self->kick );
185 0         0 $self->note( $self->eighth, $self->closed_hh);
186 0         0 $self->note( $self->eighth, $self->closed_hh, $self->snare );
187             }
188             }
189              
190              
191             sub metronome34 {
192 0     0 1 0 my $self = shift;
193 0   0     0 my $bars = shift || $self->bars;
194              
195 0         0 for ( 1 .. $bars ) {
196 0         0 $self->note( $self->quarter, $self->ride1, $self->kick );
197 0         0 $self->note( $self->quarter, $self->ride1 );
198 0         0 $self->note( $self->quarter, $self->ride1, $self->snare );
199             }
200             }
201              
202              
203             sub metronome44 {
204 0     0 1 0 my $self = shift;
205 0   0     0 my $bars = shift || $self->bars;
206 0   0     0 my $flag = shift // 0;
207              
208 0         0 my $i = 0;
209              
210 0         0 for my $n ( 1 .. $self->beats * $bars ) {
211 0 0       0 if ( $n % 2 == 0 )
212             {
213 0         0 $self->note( $self->quarter, $self->closed_hh, $self->snare );
214             }
215             else {
216 0 0       0 if ( $flag == 0 )
217             {
218 0         0 $self->note( $self->quarter, $self->closed_hh, $self->kick );
219             }
220             else
221             {
222 0 0       0 if ( $i % 2 == 0 )
223             {
224 0         0 $self->note( $self->quarter, $self->closed_hh, $self->kick );
225             }
226             else
227             {
228 0         0 $self->note( $self->eighth, $self->closed_hh, $self->kick );
229 0         0 $self->note( $self->eighth, $self->kick );
230             }
231             }
232              
233 0         0 $i++;
234             }
235             }
236             }
237              
238              
239             sub metronome44swing {
240 0     0 1 0 my $self = shift;
241 0   0     0 my $bars = shift || $self->bars;
242              
243 0         0 for my $n ( 1 .. $bars ) {
244 0         0 $self->note( $self->quarter, $self->ride1, $self->kick );
245 0         0 $self->note( $self->triplet_eighth, $self->ride1 );
246 0         0 $self->rest( $self->triplet_eighth );
247 0         0 $self->note( $self->triplet_eighth, $self->ride1, $self->kick );
248 0         0 $self->note( $self->quarter, $self->ride1, $self->snare );
249 0         0 $self->note( $self->triplet_eighth, $self->ride1, $self->kick );
250 0         0 $self->rest( $self->triplet_eighth );
251 0         0 $self->note( $self->triplet_eighth, $self->ride1 );
252             }
253             }
254              
255              
256             sub metronome54 {
257 0     0 1 0 my $self = shift;
258 0   0     0 my $bars = shift || $self->bars;
259 0         0 for my $n (1 .. $bars) {
260 0         0 $self->note($self->quarter, $self->closed_hh, $self->kick);
261 0         0 $self->note($self->quarter, $self->closed_hh);
262 0         0 $self->note($self->quarter, $self->closed_hh, $self->snare);
263 0         0 $self->note($self->quarter, $self->closed_hh);
264 0 0       0 if ($n % 2) {
265 0         0 $self->note($self->quarter, $self->closed_hh);
266             }
267             else {
268 0         0 $self->note($self->eighth, $self->closed_hh);
269 0         0 $self->note($self->eighth, $self->kick);
270             }
271             }
272             }
273              
274              
275             sub metronome58 {
276 0     0 1 0 my $self = shift;
277 0   0     0 my $bars = shift || $self->bars;
278 0         0 for my $n (1 .. $bars) {
279 0         0 $self->note($self->eighth, $self->closed_hh, $self->kick);
280 0         0 $self->note($self->eighth, $self->closed_hh);
281 0         0 $self->note($self->eighth, $self->closed_hh, $self->snare);
282 0         0 $self->note($self->eighth, $self->closed_hh);
283 0         0 $self->note($self->eighth, $self->closed_hh);
284             }
285             }
286              
287              
288             sub metronome68 {
289 0     0 1 0 my $self = shift;
290 0   0     0 my $bars = shift || $self->bars;
291 0         0 for my $n (1 .. $bars) {
292 0         0 $self->note($self->eighth, $self->closed_hh, $self->kick);
293 0         0 $self->note($self->eighth, $self->closed_hh);
294 0         0 $self->note($self->eighth, $self->closed_hh);
295 0         0 $self->note($self->eighth, $self->closed_hh, $self->snare);
296 0         0 $self->note($self->eighth, $self->closed_hh);
297 0         0 $self->note($self->eighth, $self->closed_hh);
298             }
299             }
300              
301              
302             sub metronome74 {
303 0     0 1 0 my $self = shift;
304 0   0     0 my $bars = shift || $self->bars;
305 0         0 for my $n (1 .. $bars) {
306 0         0 $self->note($self->quarter, $self->closed_hh, $self->kick);
307 0         0 $self->note($self->quarter, $self->closed_hh);
308 0         0 $self->note($self->quarter, $self->closed_hh, $self->snare);
309 0         0 $self->note($self->eighth, $self->closed_hh);
310 0         0 $self->note($self->eighth, $self->kick);
311 0         0 $self->note($self->quarter, $self->closed_hh, $self->kick);
312 0         0 $self->note($self->quarter, $self->closed_hh, $self->snare);
313 0         0 $self->note($self->quarter, $self->closed_hh);
314             }
315             }
316              
317              
318             sub metronome78 {
319 0     0 1 0 my $self = shift;
320 0   0     0 my $bars = shift || $self->bars;
321 0         0 for my $n (1 .. $bars) {
322 0         0 $self->note($self->eighth, $self->closed_hh, $self->kick);
323 0         0 $self->note($self->eighth, $self->closed_hh);
324 0         0 $self->note($self->eighth, $self->closed_hh);
325 0         0 $self->note($self->eighth, $self->closed_hh, $self->kick);
326 0         0 $self->note($self->eighth, $self->closed_hh, $self->snare);
327 0         0 $self->note($self->eighth, $self->closed_hh);
328 0         0 $self->note($self->eighth, $self->closed_hh);
329             }
330             }
331              
332              
333             sub flam {
334 0     0 1 0 my ($self, $spec, $grace, $patch, $accent) = @_;
335 0   0     0 $grace ||= $self->snare;
336 0   0     0 $patch ||= $self->snare;
337 0         0 my $x = $MIDI::Simple::Length{$spec};
338 0         0 my $y = $MIDI::Simple::Length{ $self->sixtyfourth };
339 0         0 my $z = sprintf '%0.f', ($x - $y) * TICKS;
340 0   0     0 $accent ||= sprintf '%0.f', $self->score->Volume / 2;
341 0 0       0 if ($grace eq 'r') {
342 0         0 $self->rest($self->sixtyfourth);
343             }
344             else {
345 0         0 $self->accent_note($accent, $self->sixtyfourth, $grace);
346             }
347 0         0 $self->note('d' . $z, $patch);
348             }
349              
350              
351             sub roll {
352 0     0 1 0 my ($self, $length, $spec, $patch) = @_;
353 0   0     0 $patch ||= $self->snare;
354 0         0 my $x = $MIDI::Simple::Length{$length};
355 0         0 my $y = $MIDI::Simple::Length{$spec};
356 0         0 my $z = sprintf '%0.f', $x / $y;
357 0         0 $self->note($spec, $patch) for 1 .. $z;
358             }
359              
360              
361             sub crescendo_roll {
362 0     0 1 0 my ($self, $span, $length, $spec, $patch) = @_;
363 0   0     0 $patch ||= $self->snare;
364 0         0 my ($i, $j, $k) = @$span;
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       0 if ($k) {
369 0         0 my $bezier = Math::Bezier->new(
370             1, $i,
371             $z, $i,
372             $z, $j,
373             );
374 0         0 for (my $n = 0; $n <= 1; $n += (1 / ($z - 1))) {
375 0         0 my (undef, $v) = $bezier->point($n);
376 0         0 $v = sprintf '%0.f', $v;
377             # warn(__PACKAGE__,' ',__LINE__," $n INC: $v\n");
378 0         0 $self->accent_note($v, $spec, $patch);
379             }
380             }
381             else {
382 0         0 my $v = sprintf '%0.f', ($j - $i) / ($z - 1);
383             # warn(__PACKAGE__,' ',__LINE__," VALUE: $v\n");
384 0         0 for my $n (1 .. $z) {
385 0 0       0 if ($n == $z) {
386 0 0       0 if ($i < $j) {
    0          
387 0         0 $i += $j - $i;
388             }
389             elsif ($i > $j) {
390 0         0 $i -= $i - $j;
391             }
392             }
393             # warn(__PACKAGE__,' ',__LINE__," $n INC: $i\n");
394 0         0 $self->accent_note($i, $spec, $patch);
395 0         0 $i += $v;
396             }
397             }
398             }
399              
400              
401             sub pattern {
402 34     34 1 636 my ( $self, %args ) = @_;
403              
404 34   33     113 $args{instrument} ||= $self->snare;
405 34   50     75 $args{patterns} ||= [];
406 34   33     177 $args{beats} ||= $self->beats;
407 34   50     120 $args{negate} ||= 0;
408 34   50     127 $args{repeat} ||= 1;
409              
410 34 50       45 return unless @{ $args{patterns} };
  34         74  
411              
412             # set size and duration
413 34         51 my $size;
414 34 100       63 if ( $args{duration} ) {
415 33   50     86 $size = dura_size( $args{duration} ) || 1;
416             }
417             else {
418 1         5 $size = 4 / length( $args{patterns}->[0] );
419 1         6 my $dump = reverse_dump('length');
420 1   33     382 $args{duration} = $dump->{$size} || $self->quarter;
421             }
422              
423             # set the default beat-string variations
424             $args{vary} ||= {
425 556     556   994 0 => sub { $self->rest( $args{duration} ) },
426 121     121   251 1 => sub { $self->note( $args{duration}, $args{instrument} ) },
427 34   50     621 };
428              
429 34         143 set_chan_patch( $self->score, $self->channel, $args{instrument} );
430              
431 34         1765 for my $pattern (@{ $args{patterns} }) {
  34         74  
432 34 50       83 next if $pattern =~ /^0+$/;
433              
434 34 50       74 $pattern =~ tr/01/10/ if $args{negate};
435              
436 34         78 for ( 1 .. $args{repeat} ) {
437 34         177 for my $bit ( split //, $pattern ) {
438 677         21216 $args{vary}{$bit}->($self);
439             }
440             }
441             }
442             }
443              
444              
445             sub sync_patterns {
446 11     11 1 37 my ($self, %patterns) = @_;
447              
448 11         23 my $master_duration = delete $patterns{duration};
449              
450 11         17 my @subs;
451 11         25 for my $instrument (keys %patterns) {
452             push @subs, sub {
453             $self->pattern(
454             instrument => $instrument,
455 33 50   33   1303 patterns => $patterns{$instrument},
456             $master_duration ? (duration => $master_duration) : (),
457             );
458             },
459 33         133 }
460              
461 11         35 $self->sync(@subs);
462             }
463              
464              
465             sub add_fill {
466 11     11 1 11803 my ($self, $fill, %patterns) = @_;
467              
468             $fill ||= sub {
469             return {
470 10     10   43 duration => 8,
471             $self->open_hh => '000',
472             $self->snare => '111',
473             $self->kick => '000',
474             };
475 11   100     82 };
476 11         29 my $fill_patterns = $fill->($self);
477 11 50       42 print 'Fill: ', ddc($fill_patterns) if $self->verbose;
478 11   50     34 my $fill_duration = delete $fill_patterns->{duration} || 8;
479 11         29 my $fill_length = length((values %$fill_patterns)[0]);
480              
481 11         18 my %lengths;
482 11         30 for my $instrument (keys %patterns) {
483 33         46 $lengths{$instrument} = sum0 map { length $_ } @{ $patterns{$instrument} };
  33         102  
  33         53  
484             }
485              
486 11         35 my $lcm = _multilcm($fill_duration, values %lengths);
487 11 50       29 print "LCM: $lcm\n" if $self->verbose;
488              
489 11         22 my $size = 4 / $lcm;
490 11         35 my $dump = reverse_dump('length');
491 11   66     3682 my $master_duration = $dump->{$size} || $self->eighth; # XXX this || is not right
492 11 50       31 print "Size: $size, Duration: $master_duration\n" if $self->verbose;
493              
494 11 100       32 my $fill_chop = $fill_duration == $lcm
495             ? $fill_length
496             : int($lcm / $fill_length) + 1;
497 11 50       28 print "Chop: $fill_chop\n" if $self->verbose;
498              
499 11         18 my %fresh_patterns;
500 11         27 for my $instrument (keys %patterns) {
501             # get a single "flattened" pattern as an arrayref
502 33         666 my $pattern = [ map { split //, $_ } @{ $patterns{$instrument} } ];
  33         136  
  33         60  
503             # the fresh pattern is possibly upsized with the LCM
504             $fresh_patterns{$instrument} = @$pattern < $lcm
505 33 100       93 ? [ join '', @{ upsize($pattern, $lcm) } ]
  29         68  
506             : [ join '', @$pattern ];
507             }
508 11 50       359 print 'Patterns: ', ddc(\%fresh_patterns) if $self->verbose;
509              
510 11         16 my %replacement;
511 11         26 for my $instrument (keys %$fill_patterns) {
512             # get a single "flattened" pattern as a zero-pre-padded arrayref
513 33         164 my $pattern = [ split //, sprintf '%0*s', $fill_duration, $fill_patterns->{$instrument} ];
514             # the fresh pattern string is possibly upsized with the LCM
515             my $fresh = @$pattern < $lcm
516 33 100       96 ? join '', @{ upsize($pattern, $lcm) }
  15         32  
517             : join '', @$pattern;
518             # the replacement string is the tail of the fresh pattern string
519 33         568 $replacement{$instrument} = substr $fresh, -$fill_chop;
520             }
521 11 50       30 print 'Replacements: ', ddc(\%replacement) if $self->verbose;
522              
523 11         16 my %replaced;
524 11         25 for my $instrument (keys %fresh_patterns) {
525             # get the string to replace
526 33         49 my $string = join '', @{ $fresh_patterns{$instrument} };
  33         62  
527             # replace the tail of the string
528 33         50 my $pos = length $replacement{$instrument};
529 33         57 substr $string, -$pos, $pos, $replacement{$instrument};
530 33 50       73 print "$instrument: $string\n" if $self->verbose;
531             # prepare the replaced pattern for syncing
532 33         71 $replaced{$instrument} = [ $string ];
533             }
534              
535             $self->sync_patterns(
536 11         47 %replaced,
537             duration => $master_duration,
538             );
539              
540 11         666 return \%replaced;
541             }
542              
543              
544             sub set_time_sig {
545 5     5 1 1625 my ($self, $signature, $set) = @_;
546 5 50       20 $self->signature($signature) if $signature;
547 5   100     15 $set //= 1;
548 5 100       10 if ($set) {
549 4         16 my ($beats, $divisions) = split /\//, $self->signature;
550 4         10 $self->beats($beats);
551 4         10 $self->divisions($divisions);
552             }
553 5         24 set_time_signature($self->score, $self->signature);
554             }
555              
556              
557             sub set_bpm {
558 1     1 1 3455 my ($self, $bpm) = @_;
559 1         5 $self->bpm($bpm);
560 1         8 $self->score->set_tempo( int( 60_000_000 / $self->bpm ) );
561             }
562              
563              
564             sub sync {
565 11     11 1 15 my $self = shift;
566 11         48 $self->score->synch(@_);
567             }
568              
569              
570             sub write {
571 0     0 1 0 my $self = shift;
572 0         0 $self->score->write_score( $self->file );
573             }
574              
575             # lifted from https://www.perlmonks.org/?node_id=56906
576             sub _gcf {
577 33     33   59 my ($x, $y) = @_;
578 33         97 ($x, $y) = ($y, $x % $y) while $y;
579 33         96 return $x;
580             }
581             sub _lcm {
582 33     33   99 return($_[0] * $_[1] / _gcf($_[0], $_[1]));
583             }
584             sub _multilcm {
585 11     11   20 my $x = shift;
586 11         37 $x = _lcm($x, shift) while @_;
587 11         20 return $x;
588             }
589              
590             1;
591              
592             __END__