line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package MIDI::Simple::Drummer; |
2
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:GENE'; |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
# ABSTRACT: An algorithmic MIDI drummer |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.0811'; |
7
|
|
|
|
|
|
|
|
8
|
5
|
|
|
5
|
|
2552
|
use strict; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
120
|
|
9
|
5
|
|
|
5
|
|
20
|
use warnings; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
97
|
|
10
|
|
|
|
|
|
|
|
11
|
5
|
|
|
5
|
|
2531
|
use MIDI::Simple (); |
|
5
|
|
|
|
|
87080
|
|
|
5
|
|
|
|
|
120
|
|
12
|
5
|
|
|
5
|
|
1982
|
use Music::Duration (); |
|
5
|
|
|
|
|
1559
|
|
|
5
|
|
|
|
|
300
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
BEGIN { |
15
|
|
|
|
|
|
|
# Define a division structure to use for durations. |
16
|
5
|
|
|
|
|
1320
|
use constant DIVISION => { |
17
|
|
|
|
|
|
|
w => { number => 1, ordinal => '1st', name => 'whole' }, |
18
|
|
|
|
|
|
|
h => { number => 2, ordinal => '2nd', name => 'half' }, |
19
|
|
|
|
|
|
|
q => { number => 4, ordinal => '4th', name => 'quarter' }, |
20
|
|
|
|
|
|
|
e => { number => 8, ordinal => '8th', name => 'eighth' }, |
21
|
|
|
|
|
|
|
s => { number => 16, ordinal => '16th', name => 'sixteenth' }, |
22
|
|
|
|
|
|
|
x => { number => 32, ordinal => '32nd', name => 'thirtysecond' }, |
23
|
|
|
|
|
|
|
y => { number => 64, ordinal => '64th', name => 'sixtyfourth' }, |
24
|
|
|
|
|
|
|
z => { number => 128, ordinal => '128th', name => 'onetwentyeighth' }, |
25
|
5
|
|
|
5
|
|
28
|
}; |
|
5
|
|
|
|
|
10
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# Add constants for each known duration. |
28
|
5
|
|
|
5
|
|
39
|
for my $n (keys %MIDI::Simple::Length) { |
29
|
|
|
|
|
|
|
# Get the duration part of the note name. |
30
|
160
|
50
|
|
|
|
699
|
my $name = $n =~ /([whqesxyz])n$/ ? $1 : ''; |
31
|
|
|
|
|
|
|
|
32
|
160
|
50
|
|
|
|
237
|
if ($name) { |
33
|
|
|
|
|
|
|
# Create a meaningful prefix for the named constant. |
34
|
160
|
|
|
|
|
183
|
my $prefix = ''; |
35
|
160
|
100
|
|
|
|
316
|
$prefix .= 'triplet' if $n =~ /t\w/; |
36
|
160
|
100
|
|
|
|
279
|
$prefix .= 'double_dotted' if $n =~ /^dd/; |
37
|
160
|
100
|
|
|
|
271
|
$prefix .= 'dotted' if $n =~ /^d[^d]/; |
38
|
160
|
100
|
|
|
|
248
|
$prefix .= '_' if $prefix; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# Add name-based duration. |
41
|
160
|
|
|
|
|
304
|
my $key = uc($prefix . DIVISION->{$name}{name}); |
42
|
160
|
|
|
|
|
2646
|
constant->import($key => $n); # LeoNerd++ clue |
43
|
|
|
|
|
|
|
# Add a _prefix for numeric duration constants. |
44
|
160
|
100
|
|
|
|
377
|
$prefix .= '_' unless $prefix; |
45
|
|
|
|
|
|
|
# Add number-based duration. |
46
|
160
|
|
|
|
|
304
|
$key = uc($prefix . DIVISION->{$name}{ordinal}); |
47
|
160
|
|
|
|
|
18000
|
constant->import($key => $n); |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
else { |
50
|
0
|
|
|
|
|
0
|
warn "ERROR: Unknown note value '$n' - Skipping." |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub new { # Is there a drummer in the house? |
56
|
9
|
|
|
9
|
1
|
1377
|
my $class = shift; |
57
|
|
|
|
|
|
|
# Our drummer is a set of attributes. |
58
|
9
|
|
|
|
|
82
|
my $self = { |
59
|
|
|
|
|
|
|
# MIDI |
60
|
|
|
|
|
|
|
-channel => 9, |
61
|
|
|
|
|
|
|
-volume => 100, |
62
|
|
|
|
|
|
|
-pan => 64, |
63
|
|
|
|
|
|
|
-pan_width => 0, |
64
|
|
|
|
|
|
|
-patch => 0, |
65
|
|
|
|
|
|
|
-reverb => 20, |
66
|
|
|
|
|
|
|
-chorus => 0, |
67
|
|
|
|
|
|
|
# Rhythm |
68
|
|
|
|
|
|
|
-accent => 30, |
69
|
|
|
|
|
|
|
-bpm => 120, |
70
|
|
|
|
|
|
|
-phrases => 4, |
71
|
|
|
|
|
|
|
-bars => 4, |
72
|
|
|
|
|
|
|
-beats => 4, |
73
|
|
|
|
|
|
|
-divisions => 4, |
74
|
|
|
|
|
|
|
-signature => '', |
75
|
|
|
|
|
|
|
# The Goods™ |
76
|
|
|
|
|
|
|
-score => undef, |
77
|
|
|
|
|
|
|
-file => 'Drummer.mid', |
78
|
|
|
|
|
|
|
-kit => undef, |
79
|
|
|
|
|
|
|
-patterns => undef, |
80
|
|
|
|
|
|
|
@_ # Capture any override or extra arguments. |
81
|
|
|
|
|
|
|
}; |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# Make our drummer a proper object. |
84
|
9
|
|
|
|
|
17
|
bless $self, $class; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# Perform any pre-flight default setting. |
87
|
9
|
|
|
|
|
29
|
$self->_setup; |
88
|
|
|
|
|
|
|
|
89
|
9
|
|
|
|
|
26
|
return $self; |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub _setup { # Where's my roadies, Man? |
93
|
9
|
|
|
9
|
|
15
|
my $self = shift; |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# Give unto us, a score with which to fondle. |
96
|
9
|
|
33
|
|
|
69
|
$self->{-score} ||= MIDI::Simple->new_score; |
97
|
9
|
|
|
|
|
790
|
$self->{-score}->noop('c'.$self->{-channel}, 'V'.$self->{-volume}); |
98
|
9
|
|
|
|
|
451
|
$self->{-score}->set_tempo(int(60_000_000 / $self->{-bpm})); |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# Give unto us a drum, so that we might bang upon it all day, instead of working. |
101
|
9
|
|
33
|
|
|
189
|
$self->{-kit} ||= $self->_default_kit; |
102
|
9
|
|
33
|
|
|
39
|
$self->{-patterns} ||= $self->_default_patterns; |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# Set the groove dimensions if a time signature is given. |
105
|
9
|
100
|
|
|
|
21
|
if ($self->{-signature}) { |
106
|
1
|
|
|
|
|
4
|
$self->signature($self->{-signature}); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
else { |
109
|
|
|
|
|
|
|
# If no signature is provided, assume 4/4. |
110
|
8
|
|
50
|
|
|
22
|
$self->{-beats} ||= 4; |
111
|
8
|
|
50
|
|
|
19
|
$self->{-divisions} ||= 4; |
112
|
8
|
|
|
|
|
24
|
$self->{-signature} = "$self->{-beats}/$self->{-divisions}"; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
$self->{-score}->time_signature( |
116
|
|
|
|
|
|
|
$self->{-beats}, |
117
|
|
|
|
|
|
|
sqrt( $self->{-divisions} ), |
118
|
9
|
50
|
|
|
|
47
|
( $self->{-divisions} == 8 ? 24 : 18 ), |
119
|
|
|
|
|
|
|
8 |
120
|
|
|
|
|
|
|
); |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# Reset the backbeat if the signature is a 3 multiple. |
123
|
9
|
|
|
|
|
139
|
my $x = $self->{-beats} / 3; |
124
|
9
|
100
|
|
|
|
71
|
if ($x !~ /\./) { |
125
|
1
|
|
|
|
|
3
|
$self->backbeat('Acoustic Bass Drum', 'Acoustic Snare', 'Acoustic Bass Drum'); |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Set the method name for the division metric. Ex: QUARTER for 4. |
129
|
9
|
|
|
|
|
16
|
for my $note (keys %{+DIVISION}) { |
|
9
|
|
|
|
|
49
|
|
130
|
72
|
100
|
|
|
|
140
|
if (DIVISION->{$note}{number} == $self->{-divisions}) { |
131
|
9
|
|
|
|
|
53
|
$self->div_name(uc DIVISION->{$note}{name}); |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# Set effects. |
136
|
9
|
|
|
|
|
44
|
$self->reverb; |
137
|
9
|
|
|
|
|
42
|
$self->chorus; |
138
|
9
|
|
|
|
|
31
|
$self->pan_width; |
139
|
|
|
|
|
|
|
|
140
|
9
|
|
|
|
|
11
|
return $self; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# Convenience functions: |
144
|
7
|
|
|
7
|
|
34729
|
sub _durations { return \%MIDI::Simple::Length } |
145
|
0
|
|
|
0
|
|
0
|
sub _n2p { return \%MIDI::notenum2percussion } |
146
|
0
|
|
|
0
|
|
0
|
sub _p2n { return \%MIDI::percussion2notenum } |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# Accessors: |
149
|
|
|
|
|
|
|
sub channel { # The general MIDI drumkit is often channel 9. |
150
|
3
|
|
|
3
|
1
|
1188
|
my $self = shift; |
151
|
3
|
100
|
|
|
|
10
|
$self->{-channel} = shift if @_; |
152
|
3
|
|
|
|
|
11
|
return $self->{-channel}; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
sub patch { # Drum kit |
155
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
156
|
0
|
0
|
|
|
|
0
|
$self->{-patch} = shift if @_; |
157
|
0
|
|
|
|
|
0
|
$self->{-score}->patch_change($self->{-channel}, $self->{-patch}); |
158
|
0
|
|
|
|
|
0
|
return $self->{-patch}; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
sub reverb { # [0 .. 127] |
161
|
9
|
|
|
9
|
1
|
14
|
my $self = shift; |
162
|
9
|
50
|
|
|
|
25
|
$self->{-reverb} = shift if @_; |
163
|
9
|
|
|
|
|
48
|
$self->{-score}->control_change($self->{-channel}, 91, $self->{-reverb}); |
164
|
9
|
|
|
|
|
133
|
return $self->{-reverb}; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
sub chorus { # [0 .. 127] |
167
|
9
|
|
|
9
|
1
|
16
|
my $self = shift; |
168
|
9
|
50
|
|
|
|
20
|
$self->{-chorus} = shift if @_; |
169
|
9
|
|
|
|
|
27
|
$self->{-score}->control_change($self->{-channel}, 93, $self->{-chorus}); |
170
|
9
|
|
|
|
|
118
|
return $self->{-chorus}; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
sub pan { # [0 Left-Middle-Right 127] |
173
|
8
|
|
|
8
|
1
|
10
|
my $self = shift; |
174
|
8
|
50
|
|
|
|
13
|
$self->{-pan} = shift if @_; |
175
|
8
|
|
|
|
|
21
|
$self->{-score}->control_change($self->{-channel}, 10, $self->{-pan}); |
176
|
8
|
|
|
|
|
147
|
return $self->{-pan}; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
sub pan_width { # [0 .. 64] from center |
179
|
17
|
|
|
17
|
1
|
29
|
my $self = shift; |
180
|
17
|
50
|
|
|
|
40
|
$self->{-pan_width} = shift if @_; |
181
|
17
|
|
|
|
|
36
|
return $self->{-pan_width}; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
sub bpm { # Beats per minute |
184
|
2
|
|
|
2
|
1
|
390
|
my $self = shift; |
185
|
2
|
100
|
|
|
|
7
|
$self->{-bpm} = shift if @_; |
186
|
2
|
|
|
|
|
3
|
return $self->{-bpm}; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
sub volume { # TURN IT DOWN IN THERE! |
189
|
22
|
|
|
22
|
1
|
818
|
my $self = shift; |
190
|
22
|
100
|
|
|
|
41
|
$self->{-volume} = shift if @_; |
191
|
22
|
|
|
|
|
43
|
return $self->{-volume}; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
sub phrases { # o/` How many more times? Treat me the way you wanna do? |
194
|
6
|
|
|
6
|
1
|
1101
|
my $self = shift; |
195
|
6
|
100
|
|
|
|
19
|
$self->{-phrases} = shift if @_; |
196
|
6
|
|
|
|
|
25
|
return $self->{-phrases}; |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
sub bars { # Number of measures |
199
|
2
|
|
|
2
|
1
|
797
|
my $self = shift; |
200
|
2
|
100
|
|
|
|
7
|
$self->{-bars} = shift if @_; |
201
|
2
|
|
|
|
|
4
|
return $self->{-bars}; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
sub beats { # Beats per measure |
204
|
78
|
|
|
78
|
1
|
1227
|
my $self = shift; |
205
|
78
|
100
|
|
|
|
128
|
$self->{-beats} = shift if @_; |
206
|
78
|
|
|
|
|
185
|
return $self->{-beats}; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
sub divisions { # The division of the measure that is "the pulse." |
209
|
3
|
|
|
3
|
1
|
1260
|
my $self = shift; |
210
|
3
|
100
|
|
|
|
14
|
$self->{-divisions} = shift if @_; |
211
|
3
|
|
|
|
|
8
|
return $self->{-divisions}; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
sub signature { # The ratio of discipline |
214
|
3
|
|
|
3
|
1
|
823
|
my $self = shift; |
215
|
3
|
100
|
|
|
|
8
|
if (@_) { |
216
|
|
|
|
|
|
|
# Set the argument to the signature string. |
217
|
2
|
|
|
|
|
5
|
$self->{-signature} = shift; |
218
|
|
|
|
|
|
|
# Set the rhythm metrics. |
219
|
2
|
|
|
|
|
9
|
($self->{-beats}, $self->{-divisions}) = split /\//, $self->{-signature}, 2; |
220
|
|
|
|
|
|
|
} |
221
|
3
|
|
|
|
|
8
|
return $self->{-signature}; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
sub div_name { # The name of the denominator of the time signature. |
224
|
12
|
|
|
12
|
1
|
18
|
my $self = shift; |
225
|
12
|
100
|
|
|
|
30
|
$self->{-div_name} = shift if @_; |
226
|
12
|
|
|
|
|
25
|
return $self->{-div_name}; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
sub file { # The name of the MIDI file output |
229
|
2
|
|
|
2
|
1
|
826
|
my $self = shift; |
230
|
2
|
100
|
|
|
|
8
|
$self->{-file} = shift if @_; |
231
|
2
|
|
|
|
|
5
|
return $self->{-file}; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
sub score { # The MIDI::Simple score with no-op-ability |
235
|
21
|
|
|
21
|
1
|
414
|
my $self = shift; |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# If we are presented with a M::S object, assign it as the score. |
238
|
21
|
50
|
|
|
|
37
|
$self->{-score} = shift if ref $_[0] eq 'MIDI::Simple'; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# Set any remaining arguments as score no-ops. |
241
|
21
|
|
|
|
|
48
|
$self->{-score}->noop($_) for @_; |
242
|
|
|
|
|
|
|
|
243
|
21
|
|
|
|
|
385
|
return $self->{-score}; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub accent_note { # Accent a single note. |
247
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
248
|
0
|
|
|
|
|
0
|
my $note = shift; |
249
|
0
|
|
|
|
|
0
|
$self->score('V' . $self->accent); # Accent! |
250
|
0
|
|
|
|
|
0
|
$self->note($note, $self->strike); |
251
|
0
|
|
|
|
|
0
|
$self->score('V' . $self->volume); # Reset the note volume. |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# API: Subclass and redefine to emit nuance. |
255
|
|
|
|
|
|
|
sub accent { # Pump up the Volume! |
256
|
11
|
|
|
11
|
1
|
723
|
my $self = shift; |
257
|
11
|
100
|
|
|
|
21
|
$self->{-accent} = shift if @_; |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
# Add a bit of volume. |
260
|
11
|
|
|
|
|
24
|
my $accent = $self->{-accent} + $self->volume; |
261
|
|
|
|
|
|
|
# But don't try to go above the top. |
262
|
|
|
|
|
|
|
$accent = $MIDI::Simple::Volume{fff} |
263
|
11
|
100
|
|
|
|
26
|
if $accent > $MIDI::Simple::Volume{fff}; |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# Hand back the new volume. |
266
|
11
|
|
|
|
|
31
|
return $accent; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
# API: Subclass and redefine to emit nuance. |
269
|
|
|
|
|
|
|
sub duck { # Drop the volume. |
270
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
271
|
0
|
0
|
|
|
|
0
|
$self->{-accent} = shift if @_; |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# Subtract a bit of volume. |
274
|
0
|
|
|
|
|
0
|
my $duck = $self->volume - $self->{-accent}; |
275
|
|
|
|
|
|
|
# But don't try to go below the bottom. |
276
|
|
|
|
|
|
|
$duck = $MIDI::Simple::Volume{ppp} |
277
|
0
|
0
|
|
|
|
0
|
if $duck > $MIDI::Simple::Volume{ppp}; |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# Hand back the new volume. |
280
|
0
|
|
|
|
|
0
|
return $duck; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
sub kit { # Arrayrefs of patches |
284
|
120
|
|
|
120
|
1
|
1242
|
my $self = shift; |
285
|
120
|
|
|
|
|
189
|
return $self->_type('-kit', @_); |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
sub patterns { # Coderefs of patterns |
288
|
65
|
|
|
65
|
1
|
4882
|
my $self = shift; |
289
|
65
|
|
|
|
|
131
|
return $self->_type('-patterns', @_); |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub _type { # Both kit and pattern access |
293
|
185
|
|
|
185
|
|
209
|
my $self = shift; |
294
|
185
|
|
50
|
|
|
304
|
my $type = shift || return; |
295
|
|
|
|
|
|
|
|
296
|
185
|
100
|
33
|
|
|
406
|
if (!@_) { # If there are no arguments, return all known types. |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
297
|
52
|
|
|
|
|
187
|
return $self->{$type}; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
elsif (@_ == 1) { # Return a single named type with either name=>value or just value. |
300
|
122
|
|
|
|
|
136
|
my $i = shift; |
301
|
|
|
|
|
|
|
return wantarray |
302
|
|
|
|
|
|
|
? ($i => $self->{$type}{$i}) |
303
|
122
|
50
|
|
|
|
377
|
: $self->{$type}{$i}; |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
elsif (@_ > 1 && !(@_ % 2)) { # Add new types if given an even list. |
306
|
11
|
|
|
|
|
29
|
my %args = @_; |
307
|
11
|
|
|
|
|
18
|
my @t = (); |
308
|
|
|
|
|
|
|
|
309
|
11
|
|
|
|
|
38
|
while (my ($i, $v) = each %args) { |
310
|
11
|
|
|
|
|
23
|
$self->{$type}{$i} = $v; |
311
|
11
|
|
|
|
|
34
|
push @t, $i; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
# Return the named types. |
314
|
|
|
|
|
|
|
return wantarray |
315
|
0
|
|
|
|
|
0
|
? (map { $_ => $self->{$type}{$_} } @t) # Hash of named types. |
316
|
|
|
|
|
|
|
: @t > 1 # More than one? |
317
|
0
|
|
|
|
|
0
|
? [map { $self->{$type}{$_} } @t] # Arrayref of types. |
318
|
11
|
50
|
|
|
|
137
|
: $self->{$type}{$t[0]}; # Else single type. |
|
|
50
|
|
|
|
|
|
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
else { # Unlikely to ever be triggered. |
321
|
0
|
|
|
|
|
0
|
warn 'WARNING: Mystery arguments. Giving up.'; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub name_of { # Return instrument name(s) given kit keys. |
326
|
1
|
|
|
1
|
1
|
471
|
my $self = shift; |
327
|
1
|
|
50
|
|
|
4
|
my $key = shift || return; |
328
|
|
|
|
|
|
|
return wantarray |
329
|
0
|
|
|
|
|
0
|
? @{$self->kit($key)} # List of names |
330
|
1
|
50
|
|
|
|
4
|
: join ',', @{$self->kit($key)}; # CSV of names |
|
1
|
|
|
|
|
3
|
|
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
sub _set_get { # Internal kit access |
334
|
57
|
|
|
57
|
|
73
|
my $self = shift; |
335
|
57
|
|
50
|
|
|
169
|
my $key = shift || return; |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
# Set the kit event. |
338
|
57
|
100
|
|
|
|
95
|
$self->kit($key => [@_]) if @_; |
339
|
|
|
|
|
|
|
|
340
|
57
|
|
|
|
|
68
|
return $self->option_strike(@{$self->kit($key)}); |
|
57
|
|
|
|
|
102
|
|
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
# API: Add other keys to your kit & patterns, in a subclass. |
344
|
2
|
|
|
2
|
1
|
452
|
sub backbeat { return shift->_set_get('backbeat', @_) } |
345
|
39
|
|
|
39
|
1
|
1049
|
sub snare { return shift->_set_get('snare', @_) } |
346
|
8
|
|
|
8
|
1
|
564
|
sub kick { return shift->_set_get('kick', @_) } |
347
|
4
|
|
|
4
|
1
|
440
|
sub tick { return shift->_set_get('tick', @_) } |
348
|
1
|
|
|
1
|
1
|
469
|
sub hhat { return shift->_set_get('hhat', @_) } |
349
|
1
|
|
|
1
|
1
|
466
|
sub crash { return shift->_set_get('crash', @_) } |
350
|
1
|
|
|
1
|
1
|
452
|
sub ride { return shift->_set_get('ride', @_) } |
351
|
1
|
|
|
1
|
1
|
430
|
sub tom { return shift->_set_get('tom', @_) } |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
sub strike { # Return note values. |
354
|
139
|
|
|
139
|
1
|
2178
|
my $self = shift; |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# Set the patches, default snare. |
357
|
139
|
100
|
|
|
|
302
|
my @patches = @_ ? @_ : @{$self->kit('snare')}; |
|
21
|
|
|
|
|
54
|
|
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
# Build MIDI::Simple note names from the patch numbers. |
360
|
139
|
|
|
|
|
200
|
my @notes = map { 'n' . $MIDI::percussion2notenum{$_} } @patches; |
|
141
|
|
|
|
|
402
|
|
361
|
|
|
|
|
|
|
|
362
|
139
|
100
|
|
|
|
450
|
return wantarray ? @notes : join(',', @notes); |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
# API: Redefine this method to use a different decision than rand(). |
365
|
|
|
|
|
|
|
sub option_strike { # When in doubt, crash. |
366
|
61
|
|
|
61
|
1
|
1379
|
my $self = shift; |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# Set the patches, default crashes. |
369
|
61
|
100
|
|
|
|
146
|
my @patches = @_ ? @_ : @{$self->kit('crash')}; |
|
1
|
|
|
|
|
3
|
|
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
# Choose a random patch! |
372
|
61
|
|
|
|
|
183
|
return $self->strike($patches[int(rand @patches)]); |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
sub rotate { # Rotate through a list of patches. |
376
|
23
|
|
|
23
|
1
|
2556
|
my $self = shift; |
377
|
23
|
|
100
|
|
|
48
|
my $beat = shift || 1; # Assume that we are on the first beat if none is given. |
378
|
23
|
|
66
|
|
|
53
|
my $patches = shift || $self->kit('backbeat'); # Default backbeat. |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# Strike a note from the patches, based on the beat. |
381
|
23
|
|
|
|
|
59
|
return $self->strike($patches->[$beat % @$patches]); |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
sub backbeat_rhythm { # AC/DC forever |
384
|
|
|
|
|
|
|
# Rotate the backbeat with tick & post-fill strike. |
385
|
8
|
|
|
8
|
1
|
3424
|
my $self = shift; |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# Set the default parameters with an argument override. |
388
|
8
|
|
|
|
|
15
|
my %args = ( |
389
|
|
|
|
|
|
|
-beat => 1, |
390
|
|
|
|
|
|
|
-fill => 0, |
391
|
|
|
|
|
|
|
-backbeat => scalar $self->kit('backbeat'), |
392
|
|
|
|
|
|
|
-tick => scalar $self->kit('tick'), |
393
|
|
|
|
|
|
|
-patches => scalar $self->kit('crash'), |
394
|
|
|
|
|
|
|
@_ # Capture any override or extra arguments. |
395
|
|
|
|
|
|
|
); |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# Strike a cymbal or use the provided patches. |
398
|
|
|
|
|
|
|
my $c = $args{-beat} == 1 && $args{-fill} |
399
|
1
|
|
|
|
|
3
|
? $self->option_strike(@{$args{-patches}}) |
400
|
8
|
100
|
100
|
|
|
30
|
: $self->strike(@{$args{-tick}}); |
|
7
|
|
|
|
|
15
|
|
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# Rotate the backbeat. |
403
|
8
|
|
|
|
|
17
|
my $n = $self->rotate($args{-beat}, $args{-backbeat}); |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# Return the cymbal and backbeat note. |
406
|
8
|
50
|
|
|
|
27
|
return wantarray ? ($n, $c) : join(',', $n, $c); |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# Readable, MIDI score pass-throughs. |
410
|
|
|
|
|
|
|
sub note { |
411
|
129
|
|
|
129
|
1
|
602
|
my $self = shift; |
412
|
|
|
|
|
|
|
#use Data::Dumper;warn Data::Dumper->new([@_])->Indent(1)->Terse(1)->Sortkeys(1)->Dump; |
413
|
129
|
|
|
|
|
286
|
return $self->{-score}->n(@_) |
414
|
|
|
|
|
|
|
} |
415
|
8
|
|
|
8
|
1
|
25
|
sub rest { return shift->{-score}->r(@_) } |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
sub count_in { # And-a one, and-a two... |
418
|
3
|
|
|
3
|
1
|
306
|
my $self = shift; |
419
|
3
|
|
100
|
|
|
9
|
my $bars = shift || 1; # Assume that we are on the first bar if none is given. |
420
|
3
|
|
|
|
|
7
|
my $div = $self->div_name; |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
# Define the note to strike with the given patch. Default 'tick' patch. |
423
|
3
|
100
|
|
|
|
10
|
my $strike = @_ ? $self->strike(@_) : $self->tick; |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
# Play the number of bars with a single strike. |
426
|
3
|
|
|
|
|
12
|
for my $i (1 .. $self->beats * $bars) { |
427
|
|
|
|
|
|
|
# Accent if we are on the first beat. |
428
|
32
|
100
|
|
|
|
47
|
$self->score('V'.$self->accent) if $i % $self->beats == 1; |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# Add a note to the score. |
431
|
32
|
|
|
|
|
86
|
$self->note($self->$div, $strike); |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# Reset the note volume if we just played the first beat. |
434
|
32
|
100
|
|
|
|
1469
|
$self->score('V'.$self->volume) if $i % $self->beats == 1; |
435
|
|
|
|
|
|
|
} |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
# Hand back the note we just used. |
438
|
3
|
|
|
|
|
6
|
return $strike; |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
sub metronome { # Keep time with a single patch. Default: Pedal Hi-Hat |
441
|
2
|
|
|
2
|
1
|
737
|
my $self = shift; |
442
|
|
|
|
|
|
|
# A metronome is just a count-in over the number of phrases |
443
|
2
|
|
50
|
|
|
7
|
return $self->count_in($self->phrases, shift || 'Pedal Hi-Hat'); |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
sub beat { # Pattern selector method |
447
|
25
|
|
|
25
|
1
|
8645
|
my $self = shift; |
448
|
|
|
|
|
|
|
# Receive or default arguments. |
449
|
25
|
|
|
|
|
132
|
my %args = ( |
450
|
|
|
|
|
|
|
-name => 0, # Provide a pattern name |
451
|
|
|
|
|
|
|
-fill => 0, # Provide a fill pattern name |
452
|
|
|
|
|
|
|
-last => 0, # Is this the last beat? |
453
|
|
|
|
|
|
|
-type => '', # Is this a fill if not named in -fill? |
454
|
|
|
|
|
|
|
-time => $self->QUARTER, # Default duration is a quarter note. |
455
|
|
|
|
|
|
|
@_ |
456
|
|
|
|
|
|
|
); |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
# Bail out unless we have a proper repertoire. |
459
|
25
|
50
|
|
|
|
57
|
return undef unless ref($self->patterns) eq 'HASH'; |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
# Get the names of the known patterns. |
462
|
25
|
|
|
|
|
33
|
my @k = keys %{$self->patterns}; |
|
25
|
|
|
|
|
34
|
|
463
|
|
|
|
|
|
|
# Bail out if we know nothing. |
464
|
25
|
50
|
|
|
|
49
|
return undef unless @k; |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# Do we want a certain type that isn't already in the given name? |
467
|
|
|
|
|
|
|
my $n = $args{-name} && $args{-type} && $args{-name} !~ /^.+\s+$args{-type}$/ |
468
|
25
|
100
|
66
|
|
|
130
|
? "$args{-name} $args{-type}" : $args{-name}; |
469
|
|
|
|
|
|
|
|
470
|
25
|
50
|
|
|
|
48
|
if (@k == 1) { # Return the pattern if there is only one. |
471
|
0
|
|
|
|
|
0
|
$n = $k[0]; |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
else { # Otherwise choose a different pattern. |
474
|
25
|
|
100
|
|
|
105
|
while ($n eq 0 || $n eq $args{-last}) { |
475
|
|
|
|
|
|
|
# TODO API: Allow custom decision method. |
476
|
28
|
|
|
|
|
108
|
$n = $k[int(rand @k)]; |
477
|
28
|
100
|
|
|
|
82
|
if ($args{-type}) { |
478
|
15
|
|
|
|
|
173
|
(my $t = $n) =~ s/^.+\s+($args{-type})$/$1/; |
479
|
|
|
|
|
|
|
# Skip if this is not a type for which we are looking. |
480
|
15
|
100
|
|
|
|
67
|
$n = 0 unless $t eq $args{-type}; |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
# Beat it - i.e. add the pattern to the score. |
486
|
25
|
|
|
|
|
106
|
$self->{-patterns}{$n}->($self, %args); |
487
|
|
|
|
|
|
|
# Return the beat note. |
488
|
25
|
|
|
|
|
1622
|
return $n; |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
sub fill { |
491
|
3
|
|
|
3
|
1
|
736
|
my $self = shift; |
492
|
|
|
|
|
|
|
# Add the beat pattern to the score. |
493
|
3
|
|
|
|
|
12
|
return $self->beat(@_, -type => 'fill'); |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
sub sync_tracks { |
497
|
2
|
|
|
2
|
1
|
18
|
my $self = shift; |
498
|
2
|
|
|
|
|
7
|
$self->{-score}->synch(@_); |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
sub write { # You gotta get it out there, you know. Make some buzz, Man. |
502
|
7
|
|
|
7
|
1
|
1604
|
my $self = shift; |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# Set the file if provided or use the default. |
505
|
7
|
|
66
|
|
|
23
|
my $file = shift || $self->{-file}; |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
# Write the score to the file! |
508
|
7
|
|
|
|
|
28
|
$self->{-score}->write_score($file); |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
# Return the filename if it was created or zero if not. |
511
|
|
|
|
|
|
|
# XXX Check file-size not existance. |
512
|
7
|
50
|
|
|
|
11148
|
return -e $file ? $file : 0; |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
# API: Redefine these methods in a subclass. |
516
|
|
|
|
|
|
|
sub _default_kit { |
517
|
9
|
|
|
9
|
|
16
|
my $self = shift; |
518
|
|
|
|
|
|
|
# Hand back a set of instruments as lists of GM named patches. |
519
|
|
|
|
|
|
|
return { |
520
|
9
|
|
|
|
|
123
|
backbeat => ['Acoustic Snare', 'Acoustic Bass Drum'], |
521
|
|
|
|
|
|
|
snare => ['Acoustic Snare'], # 38 |
522
|
|
|
|
|
|
|
kick => ['Acoustic Bass Drum'], # 35 |
523
|
|
|
|
|
|
|
tick => ['Closed Hi-Hat'], |
524
|
|
|
|
|
|
|
hhat => ['Closed Hi-Hat', # 42 |
525
|
|
|
|
|
|
|
'Open Hi-Hat', # 46 |
526
|
|
|
|
|
|
|
'Pedal Hi-Hat', # 44 |
527
|
|
|
|
|
|
|
], |
528
|
|
|
|
|
|
|
crash => ['Chinese Cymbal', # 52 |
529
|
|
|
|
|
|
|
'Crash Cymbal 1', # 49 |
530
|
|
|
|
|
|
|
'Crash Cymbal 2', # 57 |
531
|
|
|
|
|
|
|
'Splash Cymbal', # 55 |
532
|
|
|
|
|
|
|
], |
533
|
|
|
|
|
|
|
ride => ['Ride Bell', # 53 |
534
|
|
|
|
|
|
|
'Ride Cymbal 1', # 51 |
535
|
|
|
|
|
|
|
'Ride Cymbal 2', # 59 |
536
|
|
|
|
|
|
|
], |
537
|
|
|
|
|
|
|
tom => ['High Tom', # 50 |
538
|
|
|
|
|
|
|
'Hi-Mid Tom', # 48 |
539
|
|
|
|
|
|
|
'Low-Mid Tom', # 47 |
540
|
|
|
|
|
|
|
'Low Tom', # 45 |
541
|
|
|
|
|
|
|
'High Floor Tom', # 43 |
542
|
|
|
|
|
|
|
'Low Floor Tom', # 41 |
543
|
|
|
|
|
|
|
], |
544
|
|
|
|
|
|
|
}; |
545
|
|
|
|
|
|
|
} |
546
|
|
|
|
|
|
|
# There are no known patterns. We are a wannabe at this point. |
547
|
|
|
|
|
|
|
sub _default_patterns { |
548
|
5
|
|
|
5
|
|
8
|
my $self = shift; |
549
|
5
|
|
|
|
|
19
|
return {}; |
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
1; |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
__END__ |