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 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
172void
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
196static double
197seconds_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
212static int
213pulses_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
234void
236{
237 smf_event_t *event;
238
241
242 for (;;) {
243 event = smf_get_next_event(smf);
244
245 if (event == NULL)
246 return;
247
249
250 event->time_seconds = seconds_from_pulses(smf, event->time_pulses);
251 }
252
253 /* Not reached. */
254}
255
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
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
298smf_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
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
341void
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
366void
368{
369 smf_tempo_t *tempo;
370
372
373 tempo = new_tempo(smf, 0);
374 if (tempo == NULL)
375 g_error("tempo_init failed, sorry.");
376}
377
381static int
382last_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
402void
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
418void
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
436void
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
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:419
void smf_init_tempo(smf_t *smf)
Definition: smf_tempo.c:367
void smf_create_tempo_map_and_compute_seconds(smf_t *smf)
Definition: smf_tempo.c:235
smf_tempo_t * smf_get_tempo_by_seconds(const smf_t *smf, double seconds)
Return last tempo (i.e.
Definition: smf_tempo.c:298
void remove_last_tempo_with_pulses(smf_t *smf, int pulses)
Definition: smf_tempo.c:173
smf_tempo_t * smf_get_tempo_by_pulses(const smf_t *smf, int pulses)
Return last tempo (i.e.
Definition: smf_tempo.c:271
void smf_fini_tempo(smf_t *smf)
Definition: smf_tempo.c:342
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_t * smf_get_last_tempo(const smf_t *smf)
Return last tempo.
Definition: smf_tempo.c:326
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_tempo_t * smf_get_tempo_by_number(const smf_t *smf, int number)
Definition: smf_tempo.c:257
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