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 
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) {
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
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
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
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 *
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
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
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
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
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 
void smf_rewind(smf_t *smf)
Rewinds the SMF.
Definition: smf.c:899
int clocks_per_click
Definition: smf.h:264
double time_seconds
Definition: smf.h:260
void smf_track_add_event(smf_track_t *track, smf_event_t *event)
Adds the event to the track and computes -&gt;delta_pulses.
Definition: smf.c:441
int numerator
Definition: smf.h:262
void smf_track_add_event_delta_pulses(smf_track_t *track, smf_event_t *event, int pulses)
Adds event to the track at the time &quot;pulses&quot; clocks from the previous event in this track...
Definition: smf_tempo.c:403
GPtrArray * tempo_array
Private, used by smf_tempo.c.
Definition: smf.h:252
unsigned char * midi_buffer
Pointer to the buffer containing MIDI message.
Definition: smf.h:322
Represents a &quot;song&quot;, that is, collection of one or more tracks.
Definition: smf.h:230
Represents a single track.
Definition: smf.h:271
int ppqn
These fields are extracted from &quot;division&quot; field of MThd header.
Definition: smf.h:234
Describes a single tempo or time signature change.
Definition: smf.h:258
int smf_event_is_metadata(const smf_event_t *event) WARN_UNUSED_RESULT
Definition: smf_decode.c:57
int denominator
Definition: smf.h:263
smf_tempo_t * smf_get_tempo_by_number(const smf_t *smf, int number) WARN_UNUSED_RESULT
Definition: smf_tempo.c:257
int notes_per_note
Definition: smf.h:265
void smf_init_tempo(smf_t *smf)
Definition: smf_tempo.c:367
smf_tempo_t * smf_get_tempo_by_seconds(const smf_t *smf, double seconds) WARN_UNUSED_RESULT
Return last tempo (i.e.
Definition: smf_tempo.c:298
void smf_create_tempo_map_and_compute_seconds(smf_t *smf)
Definition: smf_tempo.c:235
smf_t * smf
Definition: smf.h:272
void maybe_add_to_tempo_map(smf_event_t *event)
Definition: smf_tempo.c:125
int time_pulses
Time, in pulses, since the start of the song.
Definition: smf.h:313
int midi_buffer_length
Length of the MIDI message in the buffer, in bytes.
Definition: smf.h:325
void remove_last_tempo_with_pulses(smf_t *smf, int pulses)
Definition: smf_tempo.c:173
void smf_track_add_event_pulses(smf_track_t *track, smf_event_t *event, int pulses)
Adds event to the track at the time &quot;pulses&quot; clocks from the start of song.
Definition: smf_tempo.c:419
int number_of_events
Definition: smf.h:275
Public interface declaration for libsmf, Standard MIDI File format library.
double time_seconds
Time, in seconds, since the start of the song.
Definition: smf.h:316
int microseconds_per_quarter_note
Definition: smf.h:261
Represents a single MIDI event or metaevent.
Definition: smf.h:301
smf_event_t * smf_track_get_last_event(const smf_track_t *track)
Definition: smf.c:789
Private header.
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 &quot;seconds&quot; seconds from the start of song.
Definition: smf_tempo.c:437
smf_tempo_t * smf_get_last_tempo(const smf_t *smf) WARN_UNUSED_RESULT
Return last tempo.
Definition: smf_tempo.c:326
smf_tempo_t * smf_get_tempo_by_pulses(const smf_t *smf, int pulses) WARN_UNUSED_RESULT
Return last tempo (i.e.
Definition: smf_tempo.c:271
int time_pulses
Definition: smf.h:259
smf_track_t * track
Pointer to the track, or NULL if event is not attached.
Definition: smf.h:303
smf_t * smf
Definition: smfsh.c:56
smf_event_t * smf_get_next_event(smf_t *smf)
Definition: smf.c:835