File Coverage

blib/lib/MIDI/Drummer/Tiny/Syncopate.pm
Criterion Covered Total %
statement 15 42 35.7
branch 0 8 0.0
condition 0 24 0.0
subroutine 5 9 55.5
pod 2 2 100.0
total 22 85 25.8


line stmt bran cond sub pod time code
1             package MIDI::Drummer::Tiny::Syncopate;
2             $MIDI::Drummer::Tiny::Syncopate::VERSION = '0.7002';
3             our $AUTHORITY = 'cpan:GENE';
4              
5             # ABSTRACT: Syncopation logic
6              
7 1     1   307343 use Algorithm::Combinatorics qw(variations_with_repetition);
  1         3841  
  1         69  
8 1     1   460 use MIDI::Util qw(dura_size);
  1         29940  
  1         85  
9              
10 1     1   617 use Moo;
  1         8051  
  1         5  
11 1     1   1623 use strictures 2;
  1         1351  
  1         31  
12 1     1   684 use namespace::clean;
  1         13304  
  1         5  
13              
14             extends 'MIDI::Drummer::Tiny';
15              
16             #pod =head1 SYNOPSIS
17             #pod
18             #pod use MIDI::Drummer::Tiny::Syncopate;
19             #pod
20             #pod my $d = MIDI::Drummer::Tiny::Syncopate->new(
21             #pod file => 'syncopate.mid',
22             #pod reverb => 15,
23             #pod );
24             #pod
25             #pod $d->combinatorial( $d->snare, {
26             #pod repeat => 2,
27             #pod patterns => [qw(0101 1001)],
28             #pod });
29             #pod
30             #pod # Play parts simultaneously
31             #pod $d->sync( \&snare, \&kick, \&hhat );
32             #pod sub snare { $d->combinatorial( $d->snare, { count => 1 } ) }
33             #pod sub kick { $d->combinatorial( $d->kick, { negate => 1 } ) }
34             #pod sub hhat { $d->steady( $d->closed_hh ) }
35             #pod
36             #pod $d->write;
37             #pod
38             #pod =head1 DESCRIPTION
39             #pod
40             #pod C provides methods to use in the
41             #pod F lessons.
42             #pod
43             #pod =cut
44              
45             #pod =head1 ATTRIBUTES
46             #pod
47             #pod =head2 duration
48             #pod
49             #pod $duration = $d->duration;
50             #pod
51             #pod Default: C
52             #pod
53             #pod =cut
54              
55             has duration => (
56             is => 'ro',
57             default => sub { 'qn' },
58             );
59              
60             #pod =head2 repeat
61             #pod
62             #pod $repeat = $d->repeat;
63             #pod
64             #pod Default: C<4>
65             #pod
66             #pod =cut
67              
68             has repeat => (
69             is => 'ro',
70             default => sub { 4 },
71             );
72              
73             #pod =head1 METHODS
74             #pod
75             #pod =head2 new
76             #pod
77             #pod $d = MIDI::Drummer::Tiny::Syncopate->new(%arguments);
78             #pod
79             #pod Return a new C object.
80             #pod
81             #pod =head2 steady
82             #pod
83             #pod $d->steady;
84             #pod $d->steady( $d->kick );
85             #pod $d->steady( $d->kick, { duration => $d->eighth } );
86             #pod
87             #pod Play a steady beat with the given B and optional
88             #pod B, for the number of beats accumulated in the object's
89             #pod B attribute.
90             #pod
91             #pod Defaults:
92             #pod
93             #pod instrument: closed_hh
94             #pod Options:
95             #pod duration: given by constructor
96             #pod
97             #pod =cut
98              
99             sub steady {
100 0     0 1   my ( $self, $instrument, $opts ) = @_;
101              
102 0   0       $instrument ||= $self->closed_hh;
103              
104 0   0       $opts->{duration} ||= $self->duration;
105              
106             # XXX This is not right
107 0           for my $n ( 1 .. $self->counter ) {
108 0           $self->note( $opts->{duration}, $instrument );
109             }
110             }
111              
112             #pod =head2 combinatorial
113             #pod
114             #pod $d->combinatorial;
115             #pod $d->combinatorial( $d->kick );
116             #pod $d->combinatorial( $d->kick, \%options );
117             #pod
118             #pod Play a beat pattern with the given B, given by
119             #pod L.
120             #pod
121             #pod This method accumulates beats in the object's B attribute if
122             #pod the B option is set.
123             #pod
124             #pod The B option is a hashref of coderefs, keyed by single character
125             #pod tokens, like the digits, 0-9. The coderef durations should add up to
126             #pod the B option.
127             #pod
128             #pod Defaults:
129             #pod
130             #pod instrument: snare
131             #pod Options:
132             #pod duration: given by constructor
133             #pod count: 0
134             #pod negate: 0
135             #pod beats: given by constructor
136             #pod repeat: given by constructor
137             #pod vary:
138             #pod 0 => sub { $self->rest( $options->{duration} ) },
139             #pod 1 => sub { $self->note( $options->{duration}, $instrument ) },
140             #pod patterns: undef
141             #pod
142             #pod =cut
143              
144             sub combinatorial {
145 0     0 1   my ( $self, $instrument, $opts ) = @_;
146              
147 0   0       $instrument ||= $self->snare;
148              
149 0   0       $opts->{negate} ||= 0;
150 0   0       $opts->{count} ||= 0;
151 0   0       $opts->{beats} ||= $self->beats;
152 0   0       $opts->{repeat} ||= $self->repeat;
153 0   0       $opts->{duration} ||= $self->duration;
154             $opts->{vary} ||= {
155 0     0     0 => sub { $self->rest( $opts->{duration} ) },
156 0     0     1 => sub { $self->note( $opts->{duration}, $instrument ) },
157 0   0       };
158              
159 0           my $size = dura_size( $opts->{duration} );
160              
161             my @items = $opts->{patterns}
162 0           ? @{ $opts->{patterns} }
163 0           : sort map { join '', @$_ }
164 0 0         variations_with_repetition( [ keys %{ $opts->{vary} } ], $opts->{beats} );
  0            
165              
166 0           for my $pattern (@items) {
167 0 0         next if $pattern =~ /^0+$/;
168              
169 0 0         $pattern =~ tr/01/10/ if $opts->{negate};
170              
171 0           for ( 1 .. $opts->{repeat} ) {
172 0           for my $bit ( split //, $pattern ) {
173 0           $opts->{vary}{$bit}->($self);
174 0 0         $self->counter( $self->counter + $size ) if $opts->{count};
175             }
176             }
177             }
178             }
179              
180             1;
181              
182             __END__