libsmf

- Main Page
- Data Structures
- Files

- File List
- Globals

smf_tempo.c
Go to the documentation of this file.
1 /*-
2 * Copyright (c) 2007, 2008 Edward Tomasz Napierała <trasz@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE
15 * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18 * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
20 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
21 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
35 #include <stdlib.h>
36 #include <assert.h>
37 #include <math.h>
38 #include <string.h>
39 #include "smf.h"
40 #include "smf_private.h"
41
42 static double seconds_from_pulses(const smf_t *smf, int pulses);
43
49 static smf_tempo_t *
50 new_tempo(smf_t *smf, int pulses)
51 {
52 smf_tempo_t *tempo, *previous_tempo = NULL;
53
54 if (smf->tempo_array->len > 0) {
55 previous_tempo = smf_get_last_tempo(smf);
56
57 /* If previous tempo starts at the same time as new one, reuse it, updating in place. */
58 if (previous_tempo->time_pulses == pulses)
59 return (previous_tempo);
60 }
61
62 tempo = malloc(sizeof(smf_tempo_t));
63 if (tempo == NULL) {
64 g_critical("Cannot allocate smf_tempo_t.");
65 return (NULL);
66 }
67
68 tempo->time_pulses = pulses;
69
70 if (previous_tempo != NULL) {
71 tempo->microseconds_per_quarter_note = previous_tempo->microseconds_per_quarter_note;
72 tempo->numerator = previous_tempo->numerator;
73 tempo->denominator = previous_tempo->denominator;
74 tempo->clocks_per_click = previous_tempo->clocks_per_click;
75 tempo->notes_per_note = previous_tempo->notes_per_note;
76 } else {
77 tempo->microseconds_per_quarter_note = 500000; /* Initial tempo is 120 BPM. */
78 tempo->numerator = 4;
79 tempo->denominator = 4;
80 tempo->clocks_per_click = -1;
81 tempo->notes_per_note = -1;
82 }
83
84 g_ptr_array_add(smf->tempo_array, tempo);
85
86 if (pulses == 0)
87 tempo->time_seconds = 0.0;
88 else
89 tempo->time_seconds = seconds_from_pulses(smf, pulses);
90
91 return (tempo);
92 }
93
94 static int
95 add_tempo(smf_t *smf, int pulses, int tempo)
96 {
97 smf_tempo_t *smf_tempo = new_tempo(smf, pulses);
98 if (smf_tempo == NULL)
99 return (-1);
100
101 smf_tempo->microseconds_per_quarter_note = tempo;
102
103 return (0);
104 }
105
106 static int
107 add_time_signature(smf_t *smf, int pulses, int numerator, int denominator, int clocks_per_click, int notes_per_note)
108 {
109 smf_tempo_t *smf_tempo = new_tempo(smf, pulses);
110 if (smf_tempo == NULL)
111 return (-1);
112
113 smf_tempo->numerator = numerator;
114 smf_tempo->denominator = denominator;
115 smf_tempo->clocks_per_click = clocks_per_click;
116 smf_tempo->notes_per_note = notes_per_note;
117
118 return (0);
119 }
120
124 void
125 maybe_add_to_tempo_map(smf_event_t *event)
126 {
127 if (!smf_event_is_metadata(event))
128 return;
129
130 assert(event->track != NULL);
131 assert(event->track->smf != NULL);
132 assert(event->midi_buffer_length >= 1);
133
134 /* Tempo Change? */
135 if (event->midi_buffer[1] == 0x51) {
136 int new_tempo = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5];
137 if (new_tempo <= 0) {
138 g_critical("Ignoring invalid tempo change.");
139 return;
140 }
141
142 add_tempo(event->track->smf, event->time_pulses, new_tempo);
143 }
144
145 /* Time Signature? */
146 if (event->midi_buffer[1] == 0x58) {
147 int numerator, denominator, clocks_per_click, notes_per_note;
148
149 if (event->midi_buffer_length < 7) {
150 g_critical("Time Signature event seems truncated.");
151 return;
152 }
153
154 numerator = event->midi_buffer[3];
155 denominator = (int)pow(2, event->midi_buffer[4]);
156 clocks_per_click = event->midi_buffer[5];
157 notes_per_note = event->midi_buffer[6];
158
159 add_time_signature(event->track->smf, event->time_pulses, numerator, denominator, clocks_per_click, notes_per_note);
160 }
161
162 return;
163 }
164
172 void
173 remove_last_tempo_with_pulses(smf_t *smf, int pulses)
174 {
175 smf_tempo_t *tempo;
176
177 /* XXX: This is a partial workaround for the following problem: we have two tempo-related
178 events, A and B, that occur at the same time. We remove B, then try to remove
179 A. However, both tempo changes got coalesced in new_tempo(), so it is impossible
180 to remove B. */
181 if (smf->tempo_array->len == 0)
182 return;
183
184 tempo = smf_get_last_tempo(smf);
185
186 /* Workaround part two. */
187 if (tempo->time_pulses != pulses)
188 return;
189
190 memset(tempo, 0, sizeof(smf_tempo_t));
191 free(tempo);
192
193 g_ptr_array_remove_index(smf->tempo_array, smf->tempo_array->len - 1);
194 }
195
196 static double
197 seconds_from_pulses(const smf_t *smf, int pulses)
198 {
199 double seconds;
200 smf_tempo_t *tempo;
201
202 tempo = smf_get_tempo_by_pulses(smf, pulses);
203 assert(tempo);
204 assert(tempo->time_pulses <= pulses);
205
206 seconds = tempo->time_seconds + (double)(pulses - tempo->time_pulses) *
207 (tempo->microseconds_per_quarter_note / ((double)smf->ppqn * 1000000.0));
208
209 return (seconds);
210 }
211
212 static int
213 pulses_from_seconds(const smf_t *smf, double seconds)
214 {
215 int pulses = 0;
216 smf_tempo_t *tempo;
217
218 tempo = smf_get_tempo_by_seconds(smf, seconds);
219 assert(tempo);
220 assert(tempo->time_seconds <= seconds);
221
222 pulses = tempo->time_pulses + (seconds - tempo->time_seconds) *
223 ((double)smf->ppqn * 1000000.0 / tempo->microseconds_per_quarter_note);
224
225 return (pulses);
226 }
227
234 void
235 smf_create_tempo_map_and_compute_seconds(smf_t *smf)
236 {
237 smf_event_t *event;
238
239 smf_rewind(smf);
240 smf_init_tempo(smf);
241
242 for (;;) {
243 event = smf_get_next_event(smf);
244
245 if (event == NULL)
246 return;
247
248 maybe_add_to_tempo_map(event);
249
250 event->time_seconds = seconds_from_pulses(smf, event->time_pulses);
251 }
252
253 /* Not reached. */
254 }
255
256 smf_tempo_t *
257 smf_get_tempo_by_number(const smf_t *smf, int number)
258 {
259 assert(number >= 0);
260
261 if (number >= smf->tempo_array->len)
262 return (NULL);
263
264 return (g_ptr_array_index(smf->tempo_array, number));
265 }
266
270 smf_tempo_t *
271 smf_get_tempo_by_pulses(const smf_t *smf, int pulses)
272 {
273 int i;
274 smf_tempo_t *tempo;
275
276 assert(pulses >= 0);
277
278 if (pulses == 0)
279 return (smf_get_tempo_by_number(smf, 0));
280
281 assert(smf->tempo_array != NULL);
282
283 for (i = smf->tempo_array->len - 1; i >= 0; i--) {
284 tempo = smf_get_tempo_by_number(smf, i);
285
286 assert(tempo);
287 if (tempo->time_pulses < pulses)
288 return (tempo);
289 }
290
291 return (NULL);
292 }
293
297 smf_tempo_t *
298 smf_get_tempo_by_seconds(const smf_t *smf, double seconds)
299 {
300 int i;
301 smf_tempo_t *tempo;
302
303 assert(seconds >= 0.0);
304
305 if (seconds == 0.0)
306 return (smf_get_tempo_by_number(smf, 0));
307
308 assert(smf->tempo_array != NULL);
309
310 for (i = smf->tempo_array->len - 1; i >= 0; i--) {
311 tempo = smf_get_tempo_by_number(smf, i);
312
313 assert(tempo);
314 if (tempo->time_seconds < seconds)
315 return (tempo);
316 }
317
318 return (NULL);
319 }
320
321
325 smf_tempo_t *
326 smf_get_last_tempo(const smf_t *smf)
327 {
328 smf_tempo_t *tempo;
329
330 tempo = smf_get_tempo_by_number(smf, smf->tempo_array->len - 1);
331 assert(tempo);
332
333 return (tempo);
334 }
335
341 void
342 smf_fini_tempo(smf_t *smf)
343 {
344 smf_tempo_t *tempo;
345
346 while (smf->tempo_array->len > 0) {
347 tempo = g_ptr_array_index(smf->tempo_array, smf->tempo_array->len - 1);
348 assert(tempo);
349
350 memset(tempo, 0, sizeof(smf_tempo_t));
351 free(tempo);
352
353 g_ptr_array_remove_index(smf->tempo_array, smf->tempo_array->len - 1);
354 }
355
356 assert(smf->tempo_array->len == 0);
357 }
358
366 void
367 smf_init_tempo(smf_t *smf)
368 {
369 smf_tempo_t *tempo;
370
371 smf_fini_tempo(smf);
372
373 tempo = new_tempo(smf, 0);
374 if (tempo == NULL)
375 g_error("tempo_init failed, sorry.");
376 }
377
381 static int
382 last_event_pulses(const smf_track_t *track)
383 {
384 /* Get time of last event on this track. */
385 if (track->number_of_events > 0) {
386 smf_event_t *previous_event = smf_track_get_last_event(track);
387 assert(previous_event);
388 assert(previous_event->time_pulses >= 0);
389
390 return (previous_event->time_pulses);
391 }
392
393 return (0);
394 }
395
402 void
403 smf_track_add_event_delta_pulses(smf_track_t *track, smf_event_t *event, int delta)
404 {
405 assert(delta >= 0);
406 assert(event->time_pulses == -1);
407 assert(event->time_seconds == -1.0);
408 assert(track->smf != NULL);
409
410 smf_track_add_event_pulses(track, event, last_event_pulses(track) + delta);
411 }
412
418 void
419 smf_track_add_event_pulses(smf_track_t *track, smf_event_t *event, int pulses)
420 {
421 assert(pulses >= 0);
422 assert(event->time_pulses == -1);
423 assert(event->time_seconds == -1.0);
424 assert(track->smf != NULL);
425
426 event->time_pulses = pulses;
427 event->time_seconds = seconds_from_pulses(track->smf, pulses);
428 smf_track_add_event(track, event);
429 }
430
436 void
437 smf_track_add_event_seconds(smf_track_t *track, smf_event_t *event, double seconds)
438 {
439 assert(seconds >= 0.0);
440 assert(event->time_pulses == -1);
441 assert(event->time_seconds == -1.0);
442 assert(track->smf != NULL);
443
444 event->time_seconds = seconds;
445 event->time_pulses = pulses_from_seconds(track->smf, seconds);
446 smf_track_add_event(track, event);
447 }
448
smf_rewind
void smf_rewind(smf_t *smf)
Rewinds the SMF.
Definition: smf.c:899
smf_tempo_struct::clocks_per_click
int clocks_per_click
Definition: smf.h:264
smf_tempo_struct::time_seconds
double time_seconds
Definition: smf.h:260
smf_track_add_event
void smf_track_add_event(smf_track_t *track, smf_event_t *event)
Adds the event to the track and computes ->delta_pulses.
Definition: smf.c:441
smf_tempo_struct::numerator
int numerator
Definition: smf.h:262
smf_struct::tempo_array
GPtrArray * tempo_array
Private, used by smf_tempo.c.
Definition: smf.h:252
smf_event_struct::midi_buffer
unsigned char * midi_buffer
Pointer to the buffer containing MIDI message.
Definition: smf.h:322
smf_struct
Represents a "song", that is, collection of one or more tracks.
Definition: smf.h:230
smf_track_struct
Represents a single track.
Definition: smf.h:271
smf_struct::ppqn
int ppqn
These fields are extracted from "division" field of MThd header.
Definition: smf.h:234
smf_tempo_struct
Describes a single tempo or time signature change.
Definition: smf.h:258
smf_event_is_metadata
int smf_event_is_metadata(const smf_event_t *event) WARN_UNUSED_RESULT
Definition: smf_decode.c:57
smf_tempo_struct::denominator
int denominator
Definition: smf.h:263
smf_track_add_event_seconds
void smf_track_add_event_seconds(smf_track_t *track, smf_event_t *event, double seconds)
Adds event to the track at the time "seconds" seconds from the start of song.
Definition: smf_tempo.c:437
smf_tempo_struct::notes_per_note
int notes_per_note
Definition: smf.h:265
remove_last_tempo_with_pulses
void remove_last_tempo_with_pulses(smf_t *smf, int pulses)
Definition: smf_tempo.c:173
smf_init_tempo
void smf_init_tempo(smf_t *smf)
Definition: smf_tempo.c:367
smf_create_tempo_map_and_compute_seconds
void smf_create_tempo_map_and_compute_seconds(smf_t *smf)
Definition: smf_tempo.c:235
smf_track_struct::smf
smf_t * smf
Definition: smf.h:272
maybe_add_to_tempo_map
void maybe_add_to_tempo_map(smf_event_t *event)
Definition: smf_tempo.c:125
smf_event_struct::time_pulses
int time_pulses
Time, in pulses, since the start of the song.
Definition: smf.h:313
smf_get_tempo_by_pulses
smf_tempo_t * smf_get_tempo_by_pulses(const smf_t *smf, int pulses)
Return last tempo (i.e.
Definition: smf_tempo.c:271
smf_get_last_tempo
smf_tempo_t * smf_get_last_tempo(const smf_t *smf)
Return last tempo.
Definition: smf_tempo.c:326
smf_event_struct::midi_buffer_length
int midi_buffer_length
Length of the MIDI message in the buffer, in bytes.
Definition: smf.h:325
smf_track_struct::number_of_events
int number_of_events
Definition: smf.h:275
smf.h
Public interface declaration for libsmf, Standard MIDI File format library.
smf_fini_tempo
void smf_fini_tempo(smf_t *smf)
Definition: smf_tempo.c:342
smf_track_add_event_delta_pulses
void smf_track_add_event_delta_pulses(smf_track_t *track, smf_event_t *event, int delta)
Adds event to the track at the time "pulses" clocks from the previous event in this track...
Definition: smf_tempo.c:403
smf_event_struct::time_seconds
double time_seconds
Time, in seconds, since the start of the song.
Definition: smf.h:316
smf_tempo_struct::microseconds_per_quarter_note
int microseconds_per_quarter_note
Definition: smf.h:261
smf_event_struct
Represents a single MIDI event or metaevent.
Definition: smf.h:301
smf_track_get_last_event
smf_event_t * smf_track_get_last_event(const smf_track_t *track)
Definition: smf.c:789
smf_private.h
Private header.
smf_track_add_event_pulses
void smf_track_add_event_pulses(smf_track_t *track, smf_event_t *event, int pulses)
Adds event to the track at the time "pulses" clocks from the start of song.
Definition: smf_tempo.c:419
smf_get_tempo_by_seconds
smf_tempo_t * smf_get_tempo_by_seconds(const smf_t *smf, double seconds)
Return last tempo (i.e.
Definition: smf_tempo.c:298
smf_tempo_struct::time_pulses
int time_pulses
Definition: smf.h:259
smf_get_tempo_by_number
smf_tempo_t * smf_get_tempo_by_number(const smf_t *smf, int number)
Definition: smf_tempo.c:257
smf_event_struct::track
smf_track_t * track
Pointer to the track, or NULL if event is not attached.
Definition: smf.h:303
smf
smf_t * smf
Definition: smfsh.c:56
smf_get_next_event
smf_event_t * smf_get_next_event(smf_t *smf)
Definition: smf.c:835