libsmf
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
42static double seconds_from_pulses(const smf_t *smf, int pulses);
43
49static smf_tempo_t *
50new_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) {
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
94static int
95add_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
106static int
107add_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
124void
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 if (event->midi_buffer_length < 6) {
137 g_critical("Tempo Change event seems truncated.");
138 return;
139 }
140
141 int new_tempo = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5];
142 if (new_tempo <= 0) {
143 g_critical("Ignoring invalid tempo change.");
144 return;
145 }
146
147 add_tempo(event->track->smf, event->time_pulses, new_tempo);
148 }
149
150 /* Time Signature? */
151 if (event->midi_buffer[1] == 0x58) {
152 int numerator, denominator, clocks_per_click, notes_per_note;
153
154 if (event->midi_buffer_length < 7) {
155 g_critical("Time Signature event seems truncated.");
156 return;
157 }
158
159 numerator = event->midi_buffer[3];
160 denominator = (int)pow(2, event->midi_buffer[4]);
161 clocks_per_click = event->midi_buffer[5];
162 notes_per_note = event->midi_buffer[6];
163
164 add_time_signature(event->track->smf, event->time_pulses, numerator, denominator, clocks_per_click, notes_per_note);
165 }
166
167 return;
168}
169
177void
179{
180 smf_tempo_t *tempo;
181
182 /* XXX: This is a partial workaround for the following problem: we have two tempo-related
183 events, A and B, that occur at the same time. We remove B, then try to remove
184 A. However, both tempo changes got coalesced in new_tempo(), so it is impossible
185 to remove B. */
186 if (smf->tempo_array->len == 0)
187 return;
188
189 tempo = smf_get_last_tempo(smf);
190
191 /* Workaround part two. */
192 if (tempo->time_pulses != pulses)
193 return;
194
195 memset(tempo, 0, sizeof(smf_tempo_t));
196 free(tempo);
197
198 g_ptr_array_remove_index(smf->tempo_array, smf->tempo_array->len - 1);
199}
200
201static double
202seconds_from_pulses(const smf_t *smf, int pulses)
203{
204 double seconds;
205 smf_tempo_t *tempo;
206
207 tempo = smf_get_tempo_by_pulses(smf, pulses);
208 assert(tempo);
209 assert(tempo->time_pulses <= pulses);
210
211 seconds = tempo->time_seconds + (double)(pulses - tempo->time_pulses) *
212 (tempo->microseconds_per_quarter_note / ((double)smf->ppqn * 1000000.0));
213
214 return (seconds);
215}
216
217static int
218pulses_from_seconds(const smf_t *smf, double seconds)
219{
220 int pulses = 0;
221 smf_tempo_t *tempo;
222
223 tempo = smf_get_tempo_by_seconds(smf, seconds);
224 assert(tempo);
225 assert(tempo->time_seconds <= seconds);
226
227 pulses = tempo->time_pulses + (seconds - tempo->time_seconds) *
228 ((double)smf->ppqn * 1000000.0 / tempo->microseconds_per_quarter_note);
229
230 return (pulses);
231}
232
239void
241{
242 smf_event_t *event;
243
246
247 for (;;) {
248 event = smf_get_next_event(smf);
249
250 if (event == NULL)
251 return;
252
254
255 event->time_seconds = seconds_from_pulses(smf, event->time_pulses);
256 }
257
258 /* Not reached. */
259}
260
263{
264 assert(number >= 0);
265
266 if (number >= smf->tempo_array->len)
267 return (NULL);
268
269 return (g_ptr_array_index(smf->tempo_array, number));
270}
271
277{
278 int i;
279 smf_tempo_t *tempo;
280
281 assert(pulses >= 0);
282
283 if (pulses == 0)
284 return (smf_get_tempo_by_number(smf, 0));
285
286 assert(smf->tempo_array != NULL);
287
288 for (i = smf->tempo_array->len - 1; i >= 0; i--) {
289 tempo = smf_get_tempo_by_number(smf, i);
290
291 assert(tempo);
292 if (tempo->time_pulses < pulses)
293 return (tempo);
294 }
295
296 return (NULL);
297}
298
303smf_get_tempo_by_seconds(const smf_t *smf, double seconds)
304{
305 int i;
306 smf_tempo_t *tempo;
307
308 assert(seconds >= 0.0);
309
310 if (seconds == 0.0)
311 return (smf_get_tempo_by_number(smf, 0));
312
313 assert(smf->tempo_array != NULL);
314
315 for (i = smf->tempo_array->len - 1; i >= 0; i--) {
316 tempo = smf_get_tempo_by_number(smf, i);
317
318 assert(tempo);
319 if (tempo->time_seconds < seconds)
320 return (tempo);
321 }
322
323 return (NULL);
324}
325
326
332{
333 smf_tempo_t *tempo;
334
335 tempo = smf_get_tempo_by_number(smf, smf->tempo_array->len - 1);
336 assert(tempo);
337
338 return (tempo);
339}
340
346void
348{
349 smf_tempo_t *tempo;
350
351 while (smf->tempo_array->len > 0) {
352 tempo = g_ptr_array_index(smf->tempo_array, smf->tempo_array->len - 1);
353 assert(tempo);
354
355 memset(tempo, 0, sizeof(smf_tempo_t));
356 free(tempo);
357
358 g_ptr_array_remove_index(smf->tempo_array, smf->tempo_array->len - 1);
359 }
360
361 assert(smf->tempo_array->len == 0);
362}
363
371void
373{
374 smf_tempo_t *tempo;
375
377
378 tempo = new_tempo(smf, 0);
379 if (tempo == NULL)
380 g_error("tempo_init failed, sorry.");
381}
382
386static int
387last_event_pulses(const smf_track_t *track)
388{
389 /* Get time of last event on this track. */
390 if (track->number_of_events > 0) {
391 smf_event_t *previous_event = smf_track_get_last_event(track);
392 assert(previous_event);
393 assert(previous_event->time_pulses >= 0);
394
395 return (previous_event->time_pulses);
396 }
397
398 return (0);
399}
400
407void
409{
410 assert(delta >= 0);
411 assert(event->time_pulses == -1);
412 assert(event->time_seconds == -1.0);
413 assert(track->smf != NULL);
414
415 smf_track_add_event_pulses(track, event, last_event_pulses(track) + delta);
416}
417
423void
425{
426 assert(pulses >= 0);
427 assert(event->time_pulses == -1);
428 assert(event->time_seconds == -1.0);
429 assert(track->smf != NULL);
430
431 event->time_pulses = pulses;
432 event->time_seconds = seconds_from_pulses(track->smf, pulses);
433 smf_track_add_event(track, event);
434}
435
441void
443{
444 assert(seconds >= 0.0);
445 assert(event->time_pulses == -1);
446 assert(event->time_seconds == -1.0);
447 assert(track->smf != NULL);
448
449 event->time_seconds = seconds;
450 event->time_pulses = pulses_from_seconds(track->smf, seconds);
451 smf_track_add_event(track, event);
452}
453
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_event_t * smf_track_get_last_event(const smf_track_t *track)
Definition: smf.c:789
smf_event_t * smf_get_next_event(smf_t *smf)
Definition: smf.c:835
void smf_rewind(smf_t *smf)
Rewinds the SMF.
Definition: smf.c:899
Public interface declaration for libsmf, Standard MIDI File format library.
int smf_event_is_metadata(const smf_event_t *event) WARN_UNUSED_RESULT
Definition: smf_decode.c:57
Private header.
void maybe_add_to_tempo_map(smf_event_t *event)
Definition: smf_tempo.c:125
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:424
void smf_init_tempo(smf_t *smf)
Definition: smf_tempo.c:372
void smf_create_tempo_map_and_compute_seconds(smf_t *smf)
Definition: smf_tempo.c:240
smf_tempo_t * smf_get_tempo_by_seconds(const smf_t *smf, double seconds)
Return last tempo (i.e.
Definition: smf_tempo.c:303
void remove_last_tempo_with_pulses(smf_t *smf, int pulses)
Definition: smf_tempo.c:178
smf_tempo_t * smf_get_tempo_by_pulses(const smf_t *smf, int pulses)
Return last tempo (i.e.
Definition: smf_tempo.c:276
void smf_fini_tempo(smf_t *smf)
Definition: smf_tempo.c:347
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:442
smf_tempo_t * smf_get_last_tempo(const smf_t *smf)
Return last tempo.
Definition: smf_tempo.c:331
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:408
smf_tempo_t * smf_get_tempo_by_number(const smf_t *smf, int number)
Definition: smf_tempo.c:262
smf_t * smf
Definition: smfsh.c:56
Represents a single MIDI event or metaevent.
Definition: smf.h:301
int time_pulses
Time, in pulses, since the start of the song.
Definition: smf.h:313
unsigned char * midi_buffer
Pointer to the buffer containing MIDI message.
Definition: smf.h:322
double time_seconds
Time, in seconds, since the start of the song.
Definition: smf.h:316
int midi_buffer_length
Length of the MIDI message in the buffer, in bytes.
Definition: smf.h:325
smf_track_t * track
Pointer to the track, or NULL if event is not attached.
Definition: smf.h:303
Represents a "song", that is, collection of one or more tracks.
Definition: smf.h:230
GPtrArray * tempo_array
Private, used by smf_tempo.c.
Definition: smf.h:252
int ppqn
These fields are extracted from "division" field of MThd header.
Definition: smf.h:234
Describes a single tempo or time signature change.
Definition: smf.h:258
int clocks_per_click
Definition: smf.h:264
int time_pulses
Definition: smf.h:259
int denominator
Definition: smf.h:263
int microseconds_per_quarter_note
Definition: smf.h:261
double time_seconds
Definition: smf.h:260
int notes_per_note
Definition: smf.h:265
int numerator
Definition: smf.h:262
Represents a single track.
Definition: smf.h:271
smf_t * smf
Definition: smf.h:272
int number_of_events
Definition: smf.h:275