line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
# Time-stamp: "2013-02-01 22:40:45 conklin" |
3
|
|
|
|
|
|
|
require 5; |
4
|
|
|
|
|
|
|
package MIDI::Score; |
5
|
11
|
|
|
11
|
|
63
|
use strict; |
|
11
|
|
|
|
|
22
|
|
|
11
|
|
|
|
|
504
|
|
6
|
11
|
|
|
11
|
|
155
|
use vars qw($Debug $VERSION); |
|
11
|
|
|
|
|
23
|
|
|
11
|
|
|
|
|
1603
|
|
7
|
11
|
|
|
11
|
|
59
|
use Carp; |
|
11
|
|
|
|
|
21
|
|
|
11
|
|
|
|
|
22511
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
$VERSION = '0.83'; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
MIDI::Score - MIDI scores |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# it's a long story; see below |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 DESCRIPTION |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
This module provides functions to do with MIDI scores. |
22
|
|
|
|
|
|
|
It is used as the basis for all the functions in MIDI::Simple. |
23
|
|
|
|
|
|
|
(Incidentally, MIDI::Opus's draw() method also uses some of the |
24
|
|
|
|
|
|
|
functions in here.) |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
Whereas the events in a MIDI event structure are items whose timing |
27
|
|
|
|
|
|
|
is expressed in delta-times, the timing of items in a score is |
28
|
|
|
|
|
|
|
expressed as an absolute number of ticks from the track's start time. |
29
|
|
|
|
|
|
|
Moreover, pairs of 'note_on' and 'note_off' events in an event structure |
30
|
|
|
|
|
|
|
are abstracted into a single 'note' item in a score structure. |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
'note' takes the following form: |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
('note_on', I, I, I, I, I) |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
The problem that score structures are meant to solve is that 1) |
37
|
|
|
|
|
|
|
people definitely don't think in delta-times -- they think in absolute |
38
|
|
|
|
|
|
|
times or in structures based on that (like 'time from start of measure'); |
39
|
|
|
|
|
|
|
2) people think in notes, not note_on and note_off events. |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
So, given this event structure: |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
['text_event', 0, 'www.ely.anglican.org/parishes/camgsm/chimes.html'], |
44
|
|
|
|
|
|
|
['text_event', 0, 'Lord through this hour/ be Thou our guide'], |
45
|
|
|
|
|
|
|
['text_event', 0, 'so, by Thy power/ no foot shall slide'], |
46
|
|
|
|
|
|
|
['patch_change', 0, 1, 8], |
47
|
|
|
|
|
|
|
['note_on', 0, 1, 25, 96], |
48
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
49
|
|
|
|
|
|
|
['note_on', 0, 1, 29, 96], |
50
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
51
|
|
|
|
|
|
|
['note_on', 0, 1, 27, 96], |
52
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
53
|
|
|
|
|
|
|
['note_on', 0, 1, 20, 96], |
54
|
|
|
|
|
|
|
['note_off', 192, 0, 1, 0], |
55
|
|
|
|
|
|
|
['note_on', 0, 1, 25, 96], |
56
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
57
|
|
|
|
|
|
|
['note_on', 0, 1, 27, 96], |
58
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
59
|
|
|
|
|
|
|
['note_on', 0, 1, 29, 96], |
60
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
61
|
|
|
|
|
|
|
['note_on', 0, 1, 25, 96], |
62
|
|
|
|
|
|
|
['note_off', 192, 0, 1, 0], |
63
|
|
|
|
|
|
|
['note_on', 0, 1, 29, 96], |
64
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
65
|
|
|
|
|
|
|
['note_on', 0, 1, 25, 96], |
66
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
67
|
|
|
|
|
|
|
['note_on', 0, 1, 27, 96], |
68
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
69
|
|
|
|
|
|
|
['note_on', 0, 1, 20, 96], |
70
|
|
|
|
|
|
|
['note_off', 192, 0, 1, 0], |
71
|
|
|
|
|
|
|
['note_on', 0, 1, 20, 96], |
72
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
73
|
|
|
|
|
|
|
['note_on', 0, 1, 27, 96], |
74
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
75
|
|
|
|
|
|
|
['note_on', 0, 1, 29, 96], |
76
|
|
|
|
|
|
|
['note_off', 96, 0, 1, 0], |
77
|
|
|
|
|
|
|
['note_on', 0, 1, 25, 96], |
78
|
|
|
|
|
|
|
['note_off', 192, 0, 1, 0], |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
here is the corresponding score structure: |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
['text_event', 0, 'www.ely.anglican.org/parishes/camgsm/chimes.html'], |
83
|
|
|
|
|
|
|
['text_event', 0, 'Lord through this hour/ be Thou our guide'], |
84
|
|
|
|
|
|
|
['text_event', 0, 'so, by Thy power/ no foot shall slide'], |
85
|
|
|
|
|
|
|
['patch_change', 0, 1, 8], |
86
|
|
|
|
|
|
|
['note', 0, 96, 1, 25, 96], |
87
|
|
|
|
|
|
|
['note', 96, 96, 1, 29, 96], |
88
|
|
|
|
|
|
|
['note', 192, 96, 1, 27, 96], |
89
|
|
|
|
|
|
|
['note', 288, 192, 1, 20, 96], |
90
|
|
|
|
|
|
|
['note', 480, 96, 1, 25, 96], |
91
|
|
|
|
|
|
|
['note', 576, 96, 1, 27, 96], |
92
|
|
|
|
|
|
|
['note', 672, 96, 1, 29, 96], |
93
|
|
|
|
|
|
|
['note', 768, 192, 1, 25, 96], |
94
|
|
|
|
|
|
|
['note', 960, 96, 1, 29, 96], |
95
|
|
|
|
|
|
|
['note', 1056, 96, 1, 25, 96], |
96
|
|
|
|
|
|
|
['note', 1152, 96, 1, 27, 96], |
97
|
|
|
|
|
|
|
['note', 1248, 192, 1, 20, 96], |
98
|
|
|
|
|
|
|
['note', 1440, 96, 1, 20, 96], |
99
|
|
|
|
|
|
|
['note', 1536, 96, 1, 27, 96], |
100
|
|
|
|
|
|
|
['note', 1632, 96, 1, 29, 96], |
101
|
|
|
|
|
|
|
['note', 1728, 192, 1, 25, 96] |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Note also that scores aren't crucially ordered. So this: |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
['note', 768, 192, 1, 25, 96], |
106
|
|
|
|
|
|
|
['note', 960, 96, 1, 29, 96], |
107
|
|
|
|
|
|
|
['note', 1056, 96, 1, 25, 96], |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
means the same thing as: |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
['note', 960, 96, 1, 29, 96], |
112
|
|
|
|
|
|
|
['note', 768, 192, 1, 25, 96], |
113
|
|
|
|
|
|
|
['note', 1056, 96, 1, 25, 96], |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
The only exception to this is in the case of things like: |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
['patch_change', 200, 2, 15], |
118
|
|
|
|
|
|
|
['note', 200, 96, 2, 25, 96], |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
where two (or more) score items happen I and where one |
121
|
|
|
|
|
|
|
affects the meaning of the other. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=head1 WHAT CAN BE IN A SCORE |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
Besides the new score structure item C (covered above), |
126
|
|
|
|
|
|
|
the possible contents of a score structure can be summarized thus: |
127
|
|
|
|
|
|
|
Whatever can appear in an event structure can appear in a score |
128
|
|
|
|
|
|
|
structure, save that its second parameter denotes not a |
129
|
|
|
|
|
|
|
delta-time in ticks, but instead denotes the absolute number of ticks |
130
|
|
|
|
|
|
|
from the start of the track. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
To avoid the long periphrase "items in a score structure", I will |
133
|
|
|
|
|
|
|
occasionally refer to items in a score structure as "notes", whether or |
134
|
|
|
|
|
|
|
not they are actually C commands. This leaves "event" to |
135
|
|
|
|
|
|
|
unambiguously denote items in an event structure. |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
These, below, are all the items that can appear in a score. |
138
|
|
|
|
|
|
|
This is basically just a repetition of the table in |
139
|
|
|
|
|
|
|
L, with starttime substituting for dtime -- |
140
|
|
|
|
|
|
|
so refer to L for an explanation of what the data types |
141
|
|
|
|
|
|
|
(like "velocity" or "pitch_wheel"). |
142
|
|
|
|
|
|
|
As far as order, the first items are generally the most important: |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=over |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=item ('note', I, I, I, I, I) |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=item ('key_after_touch', I, I, I, I) |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=item ('control_change', I, I, I, I) |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=item ('patch_change', I, I, I) |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=item ('channel_after_touch', I, I, I) |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=item ('pitch_wheel_change', I, I, I) |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=item ('set_sequence_number', I, I) |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=item ('text_event', I, I) |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=item ('copyright_text_event', I, I) |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=item ('track_name', I, I) |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=item ('instrument_name', I, I) |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=item ('lyric', I, I) |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=item ('marker', I, I) |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=item ('cue_point', I, I) |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=item ('text_event_08', I, I) |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=item ('text_event_09', I, I) |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=item ('text_event_0a', I, I) |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=item ('text_event_0b', I, I) |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=item ('text_event_0c', I, I) |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=item ('text_event_0d', I, I) |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
=item ('text_event_0e', I, I) |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=item ('text_event_0f', I, I) |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=item ('end_track', I) |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=item ('set_tempo', I, I) |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=item ('smpte_offset', I, I , I, I, I, I) |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=item ('time_signature', I, I, I, I, I) |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=item ('key_signature', I, I, I) |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=item ('sequencer_specific', I, I) |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=item ('raw_meta_event', I, I(0-255), I) |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=item ('sysex_f0', I, I) |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=item ('sysex_f7', I, I) |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=item ('song_position', I) |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=item ('song_select', I, I) |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=item ('tune_request', I) |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=item ('raw_data', I, I) |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=back |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head1 FUNCTIONS |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
This module provides these functions: |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=over |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=item $score2_r = MIDI::Score::copy_structure($score_r) |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
This takes a I to a score structure, and returns a |
228
|
|
|
|
|
|
|
I to a copy of it. Example usage: |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
@new_score = @{ MIDI::Score::copy_structure( \@old_score ) }; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=cut |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
sub copy_structure { |
235
|
0
|
|
|
0
|
1
|
0
|
return &MIDI::Event::copy_structure(@_); |
236
|
|
|
|
|
|
|
# hey, a LoL is an LoL |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
########################################################################## |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=item $events_r = MIDI::Score::score_r_to_events_r( $score_r ) |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=item ($events_r, $ticks) = MIDI::Score::score_r_to_events_r( $score_r ) |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
This takes a I to a score structure, and converts it to an |
245
|
|
|
|
|
|
|
event structure, which it returns a I to. In list context, |
246
|
|
|
|
|
|
|
also returns a second value, a count of the number of ticks that |
247
|
|
|
|
|
|
|
structure takes to play (i.e., the end-time of the temporally last |
248
|
|
|
|
|
|
|
item). |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=cut |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
sub score_r_to_events_r { |
253
|
|
|
|
|
|
|
# list context: Returns the events_r AND the total tick time |
254
|
|
|
|
|
|
|
# scalar context: Returns events_r |
255
|
3
|
|
|
3
|
1
|
360
|
my $score_r = $_[0]; |
256
|
3
|
|
|
|
|
7
|
my $time = 0; |
257
|
3
|
|
|
|
|
7
|
my @events = (); |
258
|
3
|
50
|
|
|
|
15
|
croak "MIDI::Score::score_r_to_events_r's first arg must be a listref" |
259
|
|
|
|
|
|
|
unless ref($score_r); |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# First, turn instances of 'note' into 'note_on' and 'note_off': |
262
|
3
|
|
|
|
|
7
|
foreach my $note_r (@$score_r) { |
263
|
46
|
50
|
|
|
|
89
|
next unless ref $note_r; |
264
|
46
|
100
|
|
|
|
90
|
if($note_r->[0] eq 'note') { |
265
|
38
|
|
|
|
|
81
|
my @note_on = @$note_r; |
266
|
|
|
|
|
|
|
#print "In: ", map("<$_>", @note_on), "\n"; |
267
|
38
|
|
|
|
|
44
|
$note_on[0] = 'note_on'; |
268
|
38
|
|
|
|
|
53
|
my $duration = splice(@note_on, 2, 1); |
269
|
|
|
|
|
|
|
|
270
|
38
|
|
|
|
|
91
|
my @note_off = @note_on; # /now/ copy it |
271
|
38
|
|
|
|
|
43
|
$note_off[0] = 'note_off'; |
272
|
38
|
|
|
|
|
42
|
$note_off[1] += $duration; |
273
|
38
|
|
|
|
|
35
|
$note_off[4] = 0; # set volume to 0 |
274
|
38
|
|
|
|
|
116
|
push(@events, \@note_on, \@note_off); |
275
|
|
|
|
|
|
|
#print "on: ", map("<$_>", @note_on), "\n"; |
276
|
|
|
|
|
|
|
#print "off: ", map("<$_>", @note_off), "\n"; |
277
|
|
|
|
|
|
|
} else { |
278
|
8
|
|
|
|
|
24
|
push(@events, [@$note_r]); |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
# warn scalar(@events), " events in $score_r"; |
282
|
3
|
|
|
|
|
14
|
$score_r = sort_score_r(\@events); |
283
|
|
|
|
|
|
|
# warn scalar(@$score_r), " events in $score_r"; |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
# Now we turn it into an event structure by fiddling the timing |
286
|
3
|
|
|
|
|
15
|
$time = 0; |
287
|
3
|
|
|
|
|
11
|
foreach my $event (@$score_r) { |
288
|
84
|
50
|
33
|
|
|
275
|
next unless ref($event) && @$event; |
289
|
84
|
|
|
|
|
96
|
my $delta = $event->[1] - $time; # Figure out the delta |
290
|
84
|
|
|
|
|
83
|
$time = $event->[1]; # Move it forward |
291
|
84
|
|
|
|
|
99
|
$event->[1] = $delta; # Swap it in |
292
|
|
|
|
|
|
|
} |
293
|
3
|
100
|
|
|
|
24
|
return($score_r, $time) if wantarray; |
294
|
2
|
|
|
|
|
7
|
return $score_r; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
########################################################################### |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=item $score2_r = MIDI::Score::sort_score_r( $score_r) |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
This takes a I to a score structure, and returns a |
301
|
|
|
|
|
|
|
I to a sorted (by time) copy of it. Example usage: |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
@sorted_score = @{ MIDI::Score::sort_score_r( \@old_score ) }; |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=cut |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
sub sort_score_r { |
308
|
|
|
|
|
|
|
# take a reference to a score LoL, and sort it by note start time, |
309
|
|
|
|
|
|
|
# and return a reference to that sorted LoL. Notes from the same |
310
|
|
|
|
|
|
|
# time must be left in the order they're found!!!! That's why we can't |
311
|
|
|
|
|
|
|
# just use sort { $a->[1] <=> $b->[1] } (@$score_r) |
312
|
3
|
|
|
3
|
1
|
8
|
my $score_r = $_[0]; |
313
|
3
|
|
|
|
|
13
|
my %timing = (); |
314
|
3
|
|
|
|
|
7
|
foreach my $note_r (@$score_r) { |
315
|
|
|
|
|
|
|
push( |
316
|
84
|
50
|
|
|
|
162
|
@{$timing{ |
317
|
84
|
|
|
|
|
291
|
$note_r->[1] |
318
|
|
|
|
|
|
|
}}, |
319
|
|
|
|
|
|
|
$note_r |
320
|
|
|
|
|
|
|
) if ref($note_r); |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
# warn scalar(@$score_r), " events in $score_r"; |
323
|
|
|
|
|
|
|
#print "sequencing for times: ", map("<$_> ", |
324
|
|
|
|
|
|
|
# sort {$a <=> $b} keys(%timing) |
325
|
|
|
|
|
|
|
# ), "\n"; |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
return |
328
|
|
|
|
|
|
|
[ |
329
|
51
|
|
|
|
|
144
|
map(@{ $timing{$_} }, |
|
168
|
|
|
|
|
163
|
|
330
|
3
|
|
|
|
|
42
|
sort {$a <=> $b} keys(%timing) |
331
|
|
|
|
|
|
|
) |
332
|
|
|
|
|
|
|
]; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
########################################################################### |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=item $score_r = MIDI::Score::events_r_to_score_r( $events_r ) |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=item ($score_r, $ticks) = MIDI::Score::events_r_to_score_r( $events_r ) |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
This takes a I to an event structure, converts it to a |
341
|
|
|
|
|
|
|
score structure, which it returns a I to. If called in |
342
|
|
|
|
|
|
|
list context, also returns a count of the number of ticks that |
343
|
|
|
|
|
|
|
structure takes to play (i.e., the end-time of the temporally last |
344
|
|
|
|
|
|
|
item). |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=cut |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
sub events_r_to_score_r { |
349
|
|
|
|
|
|
|
# Returns the score_r AND the total tick time |
350
|
6
|
|
|
6
|
1
|
11
|
my $events_r = $_[0]; |
351
|
6
|
50
|
|
|
|
14
|
croak "first argument to MIDI::Score::events_to_score is not a listref!" |
352
|
|
|
|
|
|
|
unless $events_r; |
353
|
6
|
50
|
|
|
|
16
|
my $options_r = ref($_[1]) ? $_[1] : {}; |
354
|
|
|
|
|
|
|
|
355
|
6
|
|
|
|
|
9
|
my $time = 0; |
356
|
6
|
50
|
|
|
|
15
|
if( $options_r->{'no_note_abstraction'} ) { |
357
|
0
|
|
|
|
|
0
|
my $score_r = MIDI::Event::copy_structure($events_r); |
358
|
0
|
|
|
|
|
0
|
foreach my $event_r (@$score_r) { |
359
|
|
|
|
|
|
|
# print join(' ', @$event_r), "\n"; |
360
|
0
|
0
|
|
|
|
0
|
$event_r->[1] = ($time += $event_r->[1]) if ref($event_r); |
361
|
|
|
|
|
|
|
} |
362
|
0
|
0
|
|
|
|
0
|
return($score_r, $time) if wantarray; |
363
|
0
|
|
|
|
|
0
|
return $score_r; |
364
|
|
|
|
|
|
|
} else { |
365
|
6
|
|
|
|
|
10
|
my %note = (); |
366
|
|
|
|
|
|
|
my @score = |
367
|
|
|
|
|
|
|
map |
368
|
|
|
|
|
|
|
{ |
369
|
6
|
50
|
|
|
|
16
|
if(!ref($_)) { |
|
112
|
|
|
|
|
185
|
|
370
|
0
|
|
|
|
|
0
|
(); |
371
|
|
|
|
|
|
|
} else { |
372
|
|
|
|
|
|
|
# 0.82: the following must be declared local |
373
|
112
|
|
|
|
|
317
|
local $_ = [@$_]; # copy. |
374
|
112
|
50
|
|
|
|
270
|
$_->[1] = ($time += $_->[1]) if ref($_); |
375
|
|
|
|
|
|
|
|
376
|
112
|
100
|
66
|
|
|
463
|
if($_->[0] eq 'note_off' |
|
|
100
|
66
|
|
|
|
|
377
|
|
|
|
|
|
|
or($_->[0] eq 'note_on' && |
378
|
|
|
|
|
|
|
$_->[4] == 0) ) |
379
|
|
|
|
|
|
|
{ # End of a note |
380
|
|
|
|
|
|
|
# print "Note off : @$_\n"; |
381
|
|
|
|
|
|
|
# 0.82: handle multiple prior events with same chan/note. |
382
|
42
|
50
|
33
|
|
|
46
|
if ((exists $note{pack 'CC', @{$_}[2,3]}) && (@{$note{pack 'CC', @{$_}[2,3]}})) { |
|
42
|
|
|
|
|
128
|
|
|
42
|
|
|
|
|
48
|
|
|
42
|
|
|
|
|
154
|
|
383
|
42
|
|
|
|
|
38
|
shift(@{$note{pack 'CC', @{$_}[2,3]}})->[2] += $time; |
|
42
|
|
|
|
|
54
|
|
|
42
|
|
|
|
|
111
|
|
384
|
42
|
100
|
|
|
|
42
|
unless(@{$note{pack 'CC', @{$_}[2,3]}}) {delete $note{pack 'CC', @{$_}[2,3]};} |
|
42
|
|
|
|
|
66
|
|
|
42
|
|
|
|
|
108
|
|
|
41
|
|
|
|
|
42
|
|
|
41
|
|
|
|
|
90
|
|
385
|
|
|
|
|
|
|
} |
386
|
42
|
|
|
|
|
91
|
(); # Erase this event. |
387
|
|
|
|
|
|
|
} elsif ($_->[0] eq 'note_on') { |
388
|
|
|
|
|
|
|
# Start of a note |
389
|
42
|
|
|
|
|
86
|
$_ = [@$_]; |
390
|
|
|
|
|
|
|
|
391
|
42
|
|
|
|
|
55
|
push(@{$note{ pack 'CC', @{$_}[2,3] }},$_); |
|
42
|
|
|
|
|
46
|
|
|
42
|
|
|
|
|
133
|
|
392
|
42
|
|
|
|
|
98
|
splice(@$_, 2, 0, -$time); |
393
|
42
|
|
|
|
|
59
|
$_->[0] = 'note'; |
394
|
|
|
|
|
|
|
# ('note', Starttime, Duration, Channel, Note, Veloc) |
395
|
42
|
|
|
|
|
75
|
$_; |
396
|
|
|
|
|
|
|
} else { |
397
|
28
|
|
|
|
|
81
|
$_; |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
@$events_r |
402
|
|
|
|
|
|
|
; |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
#print "notes remaining on stack: ", scalar(values %note), "\n" |
405
|
|
|
|
|
|
|
# if values %note; |
406
|
|
|
|
|
|
|
# 0.82: clean up pending events gracefully |
407
|
6
|
|
|
|
|
23
|
foreach my $k (keys %note) { |
408
|
0
|
|
|
|
|
0
|
foreach my $one (@{$note{$k}}) { |
|
0
|
|
|
|
|
0
|
|
409
|
0
|
|
|
|
|
0
|
$one->[2] += $time; |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
} |
412
|
6
|
50
|
|
|
|
18
|
return(\@score, $time) if wantarray; |
413
|
6
|
|
|
|
|
25
|
return \@score; |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
########################################################################### |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=item $ticks = MIDI::Score::score_r_time( $score_r ) |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
This takes a I to a score structure, and returns |
421
|
|
|
|
|
|
|
a count of the number of ticks that structure takes to play |
422
|
|
|
|
|
|
|
(i.e., the end-time of the temporally last item). |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=cut |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
sub score_r_time { |
427
|
|
|
|
|
|
|
# returns the duration of the score you pass a reference to |
428
|
2
|
|
|
2
|
1
|
12
|
my $score_r = $_[0]; |
429
|
2
|
50
|
|
|
|
20
|
croak "arg 1 of MIDI::Score::score_r_time isn't a ref" unless ref $score_r; |
430
|
2
|
|
|
|
|
4
|
my $track_time = 0; |
431
|
2
|
|
|
|
|
5
|
foreach my $event_r (@$score_r) { |
432
|
31
|
50
|
|
|
|
53
|
next unless @$event_r; |
433
|
31
|
100
|
|
|
|
51
|
my $event_end_time = ($event_r->[0] eq 'note') ? |
434
|
|
|
|
|
|
|
($event_r->[1] + $event_r->[2]) : $event_r->[1] ; |
435
|
|
|
|
|
|
|
#print "event_end_time: $event_end_time\n"; |
436
|
31
|
100
|
|
|
|
80
|
$track_time = $event_end_time if $event_end_time > $track_time; |
437
|
|
|
|
|
|
|
} |
438
|
2
|
|
|
|
|
12
|
return $track_time; |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
########################################################################### |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=item MIDI::Score::dump_score( $score_r ) |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
This dumps (via C) a text representation of the contents of |
445
|
|
|
|
|
|
|
the event structure you pass a reference to. |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=cut |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
sub dump_score { |
450
|
2
|
|
|
2
|
1
|
10
|
my $score_r = $_[0]; |
451
|
2
|
|
|
|
|
533
|
print "\@notes = ( # ", scalar(@$score_r), " notes...\n"; |
452
|
2
|
|
|
|
|
7
|
foreach my $note_r (@$score_r) { |
453
|
27
|
50
|
|
|
|
119
|
print " [", &MIDI::_dump_quote(@$note_r), "],\n" if @$note_r; |
454
|
|
|
|
|
|
|
} |
455
|
2
|
|
|
|
|
220
|
print ");\n"; |
456
|
2
|
|
|
|
|
7
|
return; |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
########################################################################### |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=item MIDI::Score::quantize( $score_r ) |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
This takes a I to a score structure, performs a grid |
464
|
|
|
|
|
|
|
quantize on all events, returning a new score reference with new |
465
|
|
|
|
|
|
|
quantized events. Two parameters to the method are: 'grid': the |
466
|
|
|
|
|
|
|
quantization grid, and 'durations': whether or not to also quantize |
467
|
|
|
|
|
|
|
event durations (default off). |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
When durations of note events are quantized, they can get 0 duration. |
470
|
|
|
|
|
|
|
These events are I from the returned score, and it is the |
471
|
|
|
|
|
|
|
responsiblity of the caller to deal with them. |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=cut |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
# new in 0.82! |
476
|
|
|
|
|
|
|
sub quantize { |
477
|
1
|
|
|
1
|
1
|
3
|
my $score_r = $_[0]; |
478
|
1
|
50
|
|
|
|
4
|
my $options_r = ref($_[1]) eq 'HASH' ? $_[1] : {}; |
479
|
1
|
|
|
|
|
3
|
my $grid = $options_r->{grid}; |
480
|
1
|
50
|
|
|
|
4
|
if ($grid < 1) {carp "bad grid $grid in MIDI::Score::quantize!"; $grid = 1;} |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
481
|
1
|
|
|
|
|
3
|
my $qd = $options_r->{durations}; # quantize durations? |
482
|
1
|
|
|
|
|
2
|
my $new_score_r = []; |
483
|
1
|
|
|
|
|
1
|
my $n_event_r; |
484
|
1
|
|
|
|
|
2
|
foreach my $event_r (@{$score_r}) { |
|
1
|
|
|
|
|
2
|
|
485
|
25
|
|
|
|
|
34
|
my $n_event_r = []; |
486
|
25
|
|
|
|
|
24
|
@{$n_event_r} = @{$event_r}; |
|
25
|
|
|
|
|
65
|
|
|
25
|
|
|
|
|
28
|
|
487
|
25
|
|
|
|
|
55
|
$n_event_r->[1] = $grid * int(($n_event_r->[1] / $grid) + 0.5); |
488
|
25
|
100
|
66
|
|
|
102
|
if ($qd && $n_event_r->[0] eq 'note') { |
489
|
20
|
|
|
|
|
38
|
$n_event_r->[2] = $grid * int(($n_event_r->[2] / $grid) + 0.5); |
490
|
|
|
|
|
|
|
} |
491
|
25
|
|
|
|
|
28
|
push @{$new_score_r}, $n_event_r; |
|
25
|
|
|
|
|
52
|
|
492
|
|
|
|
|
|
|
} |
493
|
1
|
|
|
|
|
4
|
$new_score_r; |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
########################################################################### |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=item MIDI::Score::skyline( $score_r ) |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
This takes a I to a score structure, performs skyline |
501
|
|
|
|
|
|
|
(create a monophonic track by extracting the event with highest pitch |
502
|
|
|
|
|
|
|
at unique onset times) on the score, returning a new score reference. |
503
|
|
|
|
|
|
|
The parameters to the method is: 'clip': whether durations of events |
504
|
|
|
|
|
|
|
are preserved or possibly clipped and modified. |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
To explain this, consider the following (from Bach 2 part invention |
507
|
|
|
|
|
|
|
no.6 in E major): |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
|------e------|-------ds--------|-------d------|... |
510
|
|
|
|
|
|
|
|****--E-----|-------Fs-------|------Gs-----|... |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
Without duration cliping, the skyline is E, Fs, Gs... |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
With duration clipping, the skyline is E, e, ds, d..., where the |
515
|
|
|
|
|
|
|
duration of E is clipped to just the * portion above |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
=cut |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
# new in 0.83! author DC |
520
|
|
|
|
|
|
|
sub skyline { |
521
|
0
|
|
|
0
|
1
|
|
my $score_r = $_[0]; |
522
|
0
|
0
|
|
|
|
|
my $options_r = ref($_[1]) eq 'HASH' ? $_[1] : {}; |
523
|
0
|
|
|
|
|
|
my $clip = $options_r->{clip}; |
524
|
0
|
|
|
|
|
|
my $new_score_r = []; |
525
|
0
|
|
|
|
|
|
my %events = (); |
526
|
0
|
|
|
|
|
|
my $n_event_r; |
527
|
0
|
|
|
|
|
|
my ($typeidx,$stidx,$duridx,$pitchidx) = (0,1,2,4); # create some nicer event indices |
528
|
|
|
|
|
|
|
# gather all note events into an onset-index hash. push all others directly into the new score. |
529
|
0
|
|
|
|
|
|
foreach my $event_r (@{$score_r}) { |
|
0
|
|
|
|
|
|
|
530
|
0
|
0
|
|
|
|
|
if ($event_r->[$typeidx] eq "note") {push @{$events{$event_r->[$stidx]}}, $event_r;} |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
531
|
0
|
|
|
|
|
|
else {push @{$new_score_r}, $event_r;} |
532
|
|
|
|
|
|
|
} |
533
|
0
|
|
|
|
|
|
my $loff = 0; my $lev = []; |
|
0
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
# iterate over increasing onsets |
535
|
0
|
|
|
|
|
|
foreach my $onset (sort {$a<=>$b} (keys %events)) { |
|
0
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
# find highest pitch at this onset |
537
|
0
|
|
|
|
|
|
my $ev = (sort {$b->[$pitchidx] <=> $a->[$pitchidx]} (@{$events{$onset}}))[0]; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
538
|
0
|
0
|
|
|
|
|
if ($onset >= ($lev->[$stidx] + $lev->[$duridx])) { |
|
|
0
|
|
|
|
|
|
539
|
0
|
|
|
|
|
|
push @{$new_score_r}, $ev; |
|
0
|
|
|
|
|
|
|
540
|
0
|
|
|
|
|
|
$lev = $ev; |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
elsif ($clip) { |
543
|
0
|
0
|
|
|
|
|
if ($ev->[$pitchidx] > $lev->[$pitchidx]) { |
544
|
0
|
|
|
|
|
|
$lev->[$duridx] = $ev->[$stidx] - $lev->[$stidx]; |
545
|
0
|
|
|
|
|
|
push @{$new_score_r}, $ev; |
|
0
|
|
|
|
|
|
|
546
|
0
|
|
|
|
|
|
$lev = $ev; |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
} |
550
|
0
|
|
|
|
|
|
$new_score_r; |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
########################################################################### |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
=back |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=head1 COPYRIGHT |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
Copyright (c) 1998-2002 Sean M. Burke. All rights reserved. |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or |
562
|
|
|
|
|
|
|
modify it under the same terms as Perl itself. |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
=head1 AUTHORS |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
Sean M. Burke C (until 2010) |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
Darrell Conklin C (from 2010) |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
=cut |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
1; |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
__END__ |