nuclear@0: /*- nuclear@0: * Copyright (c) 2007, 2008 Edward Tomasz NapieraƂa nuclear@0: * All rights reserved. nuclear@0: * nuclear@0: * Redistribution and use in source and binary forms, with or without nuclear@0: * modification, are permitted provided that the following conditions nuclear@0: * are met: nuclear@0: * 1. Redistributions of source code must retain the above copyright nuclear@0: * notice, this list of conditions and the following disclaimer. nuclear@0: * 2. Redistributions in binary form must reproduce the above copyright nuclear@0: * notice, this list of conditions and the following disclaimer in the nuclear@0: * documentation and/or other materials provided with the distribution. nuclear@0: * nuclear@0: * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE nuclear@0: * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, nuclear@0: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY nuclear@0: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL nuclear@0: * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED nuclear@0: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, nuclear@0: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY nuclear@0: * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING nuclear@0: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS nuclear@0: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: * nuclear@0: */ nuclear@0: nuclear@0: /** nuclear@0: * \file nuclear@0: * nuclear@0: * Various functions. nuclear@0: * nuclear@0: */ nuclear@0: nuclear@0: /* Reference: http://www.borg.com/~jglatt/tech/midifile.htm */ nuclear@0: nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #ifdef __MINGW32__ nuclear@0: #include nuclear@0: #else /* ! __MINGW32__ */ nuclear@0: #include nuclear@0: #endif /* ! __MINGW32__ */ nuclear@0: #include "smf.h" nuclear@0: #include "smf_private.h" nuclear@0: nuclear@0: /** nuclear@0: * Allocates new smf_t structure. nuclear@0: * \return pointer to smf_t or NULL. nuclear@0: */ nuclear@0: smf_t * nuclear@0: smf_new(void) nuclear@0: { nuclear@0: int cantfail; nuclear@0: nuclear@0: smf_t *smf = malloc(sizeof(smf_t)); nuclear@0: if (smf == NULL) { nuclear@0: fg_critical("Cannot allocate smf_t structure: %s", strerror(errno)); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: memset(smf, 0, sizeof(smf_t)); nuclear@0: nuclear@0: smf->tracks_array = fg_ptr_array_new(); nuclear@0: assert(smf->tracks_array); nuclear@0: nuclear@0: smf->tempo_array = fg_ptr_array_new(); nuclear@0: assert(smf->tempo_array); nuclear@0: nuclear@0: cantfail = smf_set_ppqn(smf, 120); nuclear@0: assert(!cantfail); nuclear@0: nuclear@0: cantfail = smf_set_format(smf, 0); nuclear@0: assert(!cantfail); nuclear@0: nuclear@0: smf_init_tempo(smf); nuclear@0: nuclear@0: return (smf); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Frees smf and all it's descendant structures. nuclear@0: */ nuclear@0: void nuclear@0: smf_delete(smf_t *smf) nuclear@0: { nuclear@0: /* Remove all the tracks, from last to first. */ nuclear@0: while (smf->tracks_array->len > 0) nuclear@0: smf_track_delete(fg_ptr_array_index(smf->tracks_array, smf->tracks_array->len - 1)); nuclear@0: nuclear@0: smf_fini_tempo(smf); nuclear@0: nuclear@0: assert(smf->tracks_array->len == 0); nuclear@0: assert(smf->number_of_tracks == 0); nuclear@0: fg_ptr_array_free(smf->tracks_array, TRUE); nuclear@0: fg_ptr_array_free(smf->tempo_array, TRUE); nuclear@0: nuclear@0: memset(smf, 0, sizeof(smf_t)); nuclear@0: free(smf); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Allocates new smf_track_t structure. nuclear@0: * \return pointer to smf_track_t or NULL. nuclear@0: */ nuclear@0: smf_track_t * nuclear@0: smf_track_new(void) nuclear@0: { nuclear@0: smf_track_t *track = malloc(sizeof(smf_track_t)); nuclear@0: if (track == NULL) { nuclear@0: fg_critical("Cannot allocate smf_track_t structure: %s", strerror(errno)); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: memset(track, 0, sizeof(smf_track_t)); nuclear@0: track->next_event_number = -1; nuclear@0: nuclear@0: track->events_array = fg_ptr_array_new(); nuclear@0: assert(track->events_array); nuclear@0: nuclear@0: return (track); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Detaches track from its smf and frees it. nuclear@0: */ nuclear@0: void nuclear@0: smf_track_delete(smf_track_t *track) nuclear@0: { nuclear@0: assert(track); nuclear@0: assert(track->events_array); nuclear@0: nuclear@0: /* Remove all the events, from last to first. */ nuclear@0: while (track->events_array->len > 0) nuclear@0: smf_event_delete(fg_ptr_array_index(track->events_array, track->events_array->len - 1)); nuclear@0: nuclear@0: if (track->smf) nuclear@0: smf_track_remove_from_smf(track); nuclear@0: nuclear@0: assert(track->events_array->len == 0); nuclear@0: assert(track->number_of_events == 0); nuclear@0: fg_ptr_array_free(track->events_array, TRUE); nuclear@0: nuclear@0: memset(track, 0, sizeof(smf_track_t)); nuclear@0: free(track); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: /** nuclear@0: * Appends smf_track_t to smf. nuclear@0: */ nuclear@0: void nuclear@0: smf_add_track(smf_t *smf, smf_track_t *track) nuclear@0: { nuclear@0: int cantfail; nuclear@0: nuclear@0: assert(track->smf == NULL); nuclear@0: nuclear@0: track->smf = smf; nuclear@0: fg_ptr_array_add(smf->tracks_array, track); nuclear@0: nuclear@0: smf->number_of_tracks++; nuclear@0: track->track_number = smf->number_of_tracks; nuclear@0: nuclear@0: if (smf->number_of_tracks > 1) { nuclear@0: cantfail = smf_set_format(smf, 1); nuclear@0: assert(!cantfail); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Detaches track from the smf. nuclear@0: */ nuclear@0: void nuclear@0: smf_track_remove_from_smf(smf_track_t *track) nuclear@0: { nuclear@0: int i, j; nuclear@0: smf_track_t *tmp; nuclear@0: smf_event_t *ev; nuclear@0: nuclear@0: assert(track->smf != NULL); nuclear@0: nuclear@0: track->smf->number_of_tracks--; nuclear@0: nuclear@0: assert(track->smf->tracks_array); nuclear@0: fg_ptr_array_remove(track->smf->tracks_array, track); nuclear@0: nuclear@0: /* Renumber the rest of the tracks, so they are consecutively numbered. */ nuclear@0: for (i = track->track_number; i <= track->smf->number_of_tracks; i++) { nuclear@0: tmp = smf_get_track_by_number(track->smf, i); nuclear@0: tmp->track_number = i; nuclear@0: nuclear@0: /* nuclear@0: * Events have track numbers too. I guess this wasn't a wise nuclear@0: * decision. ;-/ nuclear@0: */ nuclear@0: for (j = 1; j <= tmp->number_of_events; j++) { nuclear@0: ev = smf_track_get_event_by_number(tmp, j); nuclear@0: ev->track_number = i; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: track->track_number = -1; nuclear@0: track->smf = NULL; nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Allocates new smf_event_t structure. The caller is responsible for allocating nuclear@0: * event->midi_buffer, filling it with MIDI data and setting event->midi_buffer_length properly. nuclear@0: * Note that event->midi_buffer will be freed by smf_event_delete. nuclear@0: * \return pointer to smf_event_t or NULL. nuclear@0: */ nuclear@0: smf_event_t * nuclear@0: smf_event_new(void) nuclear@0: { nuclear@0: smf_event_t *event = malloc(sizeof(smf_event_t)); nuclear@0: if (event == NULL) { nuclear@0: fg_critical("Cannot allocate smf_event_t structure: %s", strerror(errno)); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: memset(event, 0, sizeof(smf_event_t)); nuclear@0: nuclear@0: event->delta_time_pulses = -1; nuclear@0: event->time_pulses = -1; nuclear@0: event->time_seconds = -1.0; nuclear@0: event->track_number = -1; nuclear@0: nuclear@0: return (event); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Allocates an smf_event_t structure and fills it with "len" bytes copied nuclear@0: * from "midi_data". nuclear@0: * \param midi_data Pointer to MIDI data. It sill be copied to the newly allocated event->midi_buffer. nuclear@0: * \param len Length of the buffer. It must be proper MIDI event length, e.g. 3 for Note On event. nuclear@0: * \return Event containing MIDI data or NULL. nuclear@0: */ nuclear@0: smf_event_t * nuclear@0: smf_event_new_from_pointer(void *midi_data, int len) nuclear@0: { nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: event = smf_event_new(); nuclear@0: if (event == NULL) nuclear@0: return (NULL); nuclear@0: nuclear@0: event->midi_buffer_length = len; nuclear@0: event->midi_buffer = malloc(event->midi_buffer_length); nuclear@0: if (event->midi_buffer == NULL) { nuclear@0: fg_critical("Cannot allocate MIDI buffer structure: %s", strerror(errno)); nuclear@0: smf_event_delete(event); nuclear@0: nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: memcpy(event->midi_buffer, midi_data, len); nuclear@0: nuclear@0: return (event); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Allocates an smf_event_t structure and fills it with at most three bytes of data. nuclear@0: * For example, if you need to create Note On event, do something like this: nuclear@0: * nuclear@0: * smf_event_new_from_bytes(0x90, 0x3C, 0x7f); nuclear@0: * nuclear@0: * To create event for MIDI message that is shorter than three bytes, do something nuclear@0: * like this: nuclear@0: * nuclear@0: * smf_event_new_from_bytes(0xC0, 0x42, -1); nuclear@0: * nuclear@0: * \param first_byte First byte of MIDI message. Must be valid status byte. nuclear@0: * \param second_byte Second byte of MIDI message or -1, if message is one byte long. nuclear@0: * \param third_byte Third byte of MIDI message or -1, if message is two bytes long. nuclear@0: * \return Event containing MIDI data or NULL. nuclear@0: */ nuclear@0: smf_event_t * nuclear@0: smf_event_new_from_bytes(int first_byte, int second_byte, int third_byte) nuclear@0: { nuclear@0: int len; nuclear@0: nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: event = smf_event_new(); nuclear@0: if (event == NULL) nuclear@0: return (NULL); nuclear@0: nuclear@0: if (first_byte < 0) { nuclear@0: fg_critical("First byte of MIDI message cannot be < 0"); nuclear@0: smf_event_delete(event); nuclear@0: nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: if (first_byte > 255) { nuclear@0: fg_critical("smf_event_new_from_bytes: first byte is %d, which is larger than 255.", first_byte); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: if (!is_status_byte(first_byte)) { nuclear@0: fg_critical("smf_event_new_from_bytes: first byte is not a valid status byte."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: if (second_byte < 0) nuclear@0: len = 1; nuclear@0: else if (third_byte < 0) nuclear@0: len = 2; nuclear@0: else nuclear@0: len = 3; nuclear@0: nuclear@0: if (len > 1) { nuclear@0: if (second_byte > 255) { nuclear@0: fg_critical("smf_event_new_from_bytes: second byte is %d, which is larger than 255.", second_byte); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: if (is_status_byte(second_byte)) { nuclear@0: fg_critical("smf_event_new_from_bytes: second byte cannot be a status byte."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (len > 2) { nuclear@0: if (third_byte > 255) { nuclear@0: fg_critical("smf_event_new_from_bytes: third byte is %d, which is larger than 255.", third_byte); nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: if (is_status_byte(third_byte)) { nuclear@0: fg_critical("smf_event_new_from_bytes: third byte cannot be a status byte."); nuclear@0: return (NULL); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: event->midi_buffer_length = len; nuclear@0: event->midi_buffer = malloc(event->midi_buffer_length); nuclear@0: if (event->midi_buffer == NULL) { nuclear@0: fg_critical("Cannot allocate MIDI buffer structure: %s", strerror(errno)); nuclear@0: smf_event_delete(event); nuclear@0: nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: event->midi_buffer[0] = first_byte; nuclear@0: if (len > 1) nuclear@0: event->midi_buffer[1] = second_byte; nuclear@0: if (len > 2) nuclear@0: event->midi_buffer[2] = third_byte; nuclear@0: nuclear@0: return (event); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Detaches event from its track and frees it. nuclear@0: */ nuclear@0: void nuclear@0: smf_event_delete(smf_event_t *event) nuclear@0: { nuclear@0: if (event->track != NULL) nuclear@0: smf_event_remove_from_track(event); nuclear@0: nuclear@0: if (event->midi_buffer != NULL) { nuclear@0: memset(event->midi_buffer, 0, event->midi_buffer_length); nuclear@0: free(event->midi_buffer); nuclear@0: } nuclear@0: nuclear@0: memset(event, 0, sizeof(smf_event_t)); nuclear@0: free(event); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Used for sorting track->events_array. nuclear@0: */ nuclear@0: static gint nuclear@0: events_array_compare_function(gconstpointer aa, gconstpointer bb) nuclear@0: { nuclear@0: smf_event_t *a, *b; nuclear@0: nuclear@0: /* "The comparison function for fg_ptr_array_sort() doesn't take the pointers nuclear@0: from the array as arguments, it takes pointers to the pointers in the array." */ nuclear@0: a = (smf_event_t *)*(gpointer *)aa; nuclear@0: b = (smf_event_t *)*(gpointer *)bb; nuclear@0: nuclear@0: if (a->time_pulses < b->time_pulses) nuclear@0: return (-1); nuclear@0: nuclear@0: if (a->time_pulses > b->time_pulses) nuclear@0: return (1); nuclear@0: nuclear@0: /* nuclear@0: * We need to preserve original order, otherwise things will break nuclear@0: * when there are several events with the same ->time_pulses. nuclear@0: * XXX: This is an ugly hack; we should remove sorting altogether. nuclear@0: */ nuclear@0: nuclear@0: if (a->event_number < b->event_number) nuclear@0: return (-1); nuclear@0: nuclear@0: if (a->event_number > b->event_number) nuclear@0: return (1); nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: /* nuclear@0: * An assumption here is that if there is an EOT event, it will be at the end of the track. nuclear@0: */ nuclear@0: static void nuclear@0: remove_eot_if_before_pulses(smf_track_t *track, int pulses) nuclear@0: { nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: event = smf_track_get_last_event(track); nuclear@0: nuclear@0: if (event == NULL) nuclear@0: return; nuclear@0: nuclear@0: if (!smf_event_is_eot(event)) nuclear@0: return; nuclear@0: nuclear@0: if (event->time_pulses > pulses) nuclear@0: return; nuclear@0: nuclear@0: smf_event_remove_from_track(event); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Adds the event to the track and computes ->delta_pulses. Note that it is faster nuclear@0: * to append events to the end of the track than to insert them in the middle. nuclear@0: * Usually you want to use smf_track_add_event_seconds or smf_track_add_event_pulses nuclear@0: * instead of this one. Event needs to have ->time_pulses and ->time_seconds already set. nuclear@0: * If you try to add event after an EOT, EOT event will be automatically deleted. nuclear@0: */ nuclear@0: void nuclear@0: smf_track_add_event(smf_track_t *track, smf_event_t *event) nuclear@0: { nuclear@0: int i, last_pulses = 0; nuclear@0: nuclear@0: assert(track->smf != NULL); nuclear@0: assert(event->track == NULL); nuclear@0: assert(event->delta_time_pulses == -1); nuclear@0: assert(event->time_pulses >= 0); nuclear@0: assert(event->time_seconds >= 0.0); nuclear@0: nuclear@0: remove_eot_if_before_pulses(track, event->time_pulses); nuclear@0: nuclear@0: event->track = track; nuclear@0: event->track_number = track->track_number; nuclear@0: nuclear@0: if (track->number_of_events == 0) { nuclear@0: assert(track->next_event_number == -1); nuclear@0: track->next_event_number = 1; nuclear@0: } nuclear@0: nuclear@0: if (track->number_of_events > 0) nuclear@0: last_pulses = smf_track_get_last_event(track)->time_pulses; nuclear@0: nuclear@0: track->number_of_events++; nuclear@0: nuclear@0: /* Are we just appending element at the end of the track? */ nuclear@0: if (last_pulses <= event->time_pulses) { nuclear@0: event->delta_time_pulses = event->time_pulses - last_pulses; nuclear@0: assert(event->delta_time_pulses >= 0); nuclear@0: fg_ptr_array_add(track->events_array, event); nuclear@0: event->event_number = track->number_of_events; nuclear@0: nuclear@0: /* We need to insert in the middle of the track. XXX: This is slow. */ nuclear@0: } else { nuclear@0: /* Append, then sort according to ->time_pulses. */ nuclear@0: fg_ptr_array_add(track->events_array, event); nuclear@0: fg_ptr_array_sort(track->events_array, events_array_compare_function); nuclear@0: nuclear@0: /* Renumber entries and fix their ->delta_pulses. */ nuclear@0: for (i = 1; i <= track->number_of_events; i++) { nuclear@0: smf_event_t *tmp = smf_track_get_event_by_number(track, i); nuclear@0: tmp->event_number = i; nuclear@0: nuclear@0: if (tmp->delta_time_pulses != -1) nuclear@0: continue; nuclear@0: nuclear@0: if (i == 1) { nuclear@0: tmp->delta_time_pulses = tmp->time_pulses; nuclear@0: } else { nuclear@0: tmp->delta_time_pulses = tmp->time_pulses - nuclear@0: smf_track_get_event_by_number(track, i - 1)->time_pulses; nuclear@0: assert(tmp->delta_time_pulses >= 0); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: /* Adjust ->delta_time_pulses of the next event. */ nuclear@0: if (event->event_number < track->number_of_events) { nuclear@0: smf_event_t *next_event = smf_track_get_event_by_number(track, event->event_number + 1); nuclear@0: assert(next_event); nuclear@0: assert(next_event->time_pulses >= event->time_pulses); nuclear@0: next_event->delta_time_pulses -= event->delta_time_pulses; nuclear@0: assert(next_event->delta_time_pulses >= 0); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (smf_event_is_tempo_change_or_time_signature(event)) { nuclear@0: if (smf_event_is_last(event)) nuclear@0: maybe_add_to_tempo_map(event); nuclear@0: else nuclear@0: smf_create_tempo_map_and_compute_seconds(event->track->smf); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Add End Of Track metaevent. Using it is optional, libsmf will automatically nuclear@0: * add EOT to the tracks during smf_save, with delta_pulses 0. If you try to add EOT nuclear@0: * in the middle of the track, it will fail and nonzero value will be returned. nuclear@0: * If you try to add EOT after another EOT event, it will be added, but the existing nuclear@0: * EOT event will be removed. nuclear@0: * nuclear@0: * \return 0 if everything went ok, nonzero otherwise. nuclear@0: */ nuclear@0: int nuclear@0: smf_track_add_eot_delta_pulses(smf_track_t *track, int delta) nuclear@0: { nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: event = smf_event_new_from_bytes(0xFF, 0x2F, 0x00); nuclear@0: if (event == NULL) nuclear@0: return (-1); nuclear@0: nuclear@0: smf_track_add_event_delta_pulses(track, event, delta); nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: int nuclear@0: smf_track_add_eot_pulses(smf_track_t *track, int pulses) nuclear@0: { nuclear@0: smf_event_t *event, *last_event; nuclear@0: nuclear@0: last_event = smf_track_get_last_event(track); nuclear@0: if (last_event != NULL) { nuclear@0: if (last_event->time_pulses > pulses) nuclear@0: return (-2); nuclear@0: } nuclear@0: nuclear@0: event = smf_event_new_from_bytes(0xFF, 0x2F, 0x00); nuclear@0: if (event == NULL) nuclear@0: return (-3); nuclear@0: nuclear@0: smf_track_add_event_pulses(track, event, pulses); nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: int nuclear@0: smf_track_add_eot_seconds(smf_track_t *track, double seconds) nuclear@0: { nuclear@0: smf_event_t *event, *last_event; nuclear@0: nuclear@0: last_event = smf_track_get_last_event(track); nuclear@0: if (last_event != NULL) { nuclear@0: if (last_event->time_seconds > seconds) nuclear@0: return (-2); nuclear@0: } nuclear@0: nuclear@0: event = smf_event_new_from_bytes(0xFF, 0x2F, 0x00); nuclear@0: if (event == NULL) nuclear@0: return (-1); nuclear@0: nuclear@0: smf_track_add_event_seconds(track, event, seconds); nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Detaches event from its track. nuclear@0: */ nuclear@0: void nuclear@0: smf_event_remove_from_track(smf_event_t *event) nuclear@0: { nuclear@0: int i, was_last; nuclear@0: smf_event_t *tmp; nuclear@0: smf_track_t *track; nuclear@0: nuclear@0: assert(event->track != NULL); nuclear@0: assert(event->track->smf != NULL); nuclear@0: nuclear@0: track = event->track; nuclear@0: was_last = smf_event_is_last(event); nuclear@0: nuclear@0: /* Adjust ->delta_time_pulses of the next event. */ nuclear@0: if (event->event_number < track->number_of_events) { nuclear@0: tmp = smf_track_get_event_by_number(track, event->event_number + 1); nuclear@0: assert(tmp); nuclear@0: tmp->delta_time_pulses += event->delta_time_pulses; nuclear@0: } nuclear@0: nuclear@0: track->number_of_events--; nuclear@0: fg_ptr_array_remove(track->events_array, event); nuclear@0: nuclear@0: if (track->number_of_events == 0) nuclear@0: track->next_event_number = -1; nuclear@0: nuclear@0: /* Renumber the rest of the events, so they are consecutively numbered. */ nuclear@0: for (i = event->event_number; i <= track->number_of_events; i++) { nuclear@0: tmp = smf_track_get_event_by_number(track, i); nuclear@0: tmp->event_number = i; nuclear@0: } nuclear@0: nuclear@0: if (smf_event_is_tempo_change_or_time_signature(event)) { nuclear@0: /* XXX: This will cause problems, when there is more than one Tempo Change event at a given time. */ nuclear@0: if (was_last) nuclear@0: remove_last_tempo_with_pulses(event->track->smf, event->time_pulses); nuclear@0: else nuclear@0: smf_create_tempo_map_and_compute_seconds(track->smf); nuclear@0: } nuclear@0: nuclear@0: event->track = NULL; nuclear@0: event->event_number = -1; nuclear@0: event->delta_time_pulses = -1; nuclear@0: event->time_pulses = -1; nuclear@0: event->time_seconds = -1.0; nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Nonzero if event is Tempo Change or Time Signature metaevent. nuclear@0: */ nuclear@0: int nuclear@0: smf_event_is_tempo_change_or_time_signature(const smf_event_t *event) nuclear@0: { nuclear@0: if (!smf_event_is_metadata(event)) nuclear@0: return (0); nuclear@0: nuclear@0: assert(event->midi_buffer_length >= 2); nuclear@0: nuclear@0: if (event->midi_buffer[1] == 0x51 || event->midi_buffer[1] == 0x58) nuclear@0: return (1); nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Sets "Format" field of MThd header to the specified value. Note that you nuclear@0: * don't really need to use this, as libsmf will automatically change format nuclear@0: * from 0 to 1 when you add the second track. nuclear@0: * \param smf SMF. nuclear@0: * \param format 0 for one track per file, 1 for several tracks per file. nuclear@0: */ nuclear@0: int nuclear@0: smf_set_format(smf_t *smf, int format) nuclear@0: { nuclear@0: assert(format == 0 || format == 1); nuclear@0: nuclear@0: if (smf->number_of_tracks > 1 && format == 0) { nuclear@0: fg_critical("There is more than one track, cannot set format to 0."); nuclear@0: return (-1); nuclear@0: } nuclear@0: nuclear@0: smf->format = format; nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Sets the PPQN ("Division") field of MThd header. This is mandatory, you nuclear@0: * should call it right after smf_new. Note that changing PPQN will change time_seconds nuclear@0: * of all the events. nuclear@0: * \param smf SMF. nuclear@0: * \param ppqn New PPQN. nuclear@0: */ nuclear@0: int nuclear@0: smf_set_ppqn(smf_t *smf, int ppqn) nuclear@0: { nuclear@0: assert(ppqn > 0); nuclear@0: nuclear@0: smf->ppqn = ppqn; nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Returns next event from the track given and advances next event counter. nuclear@0: * Do not depend on End Of Track event being the last event on the track - it nuclear@0: * is possible that the track will not end with EOT if you haven't added it nuclear@0: * yet. EOTs are added automatically during smf_save(). nuclear@0: * nuclear@0: * \return Event or NULL, if there are no more events left in this track. nuclear@0: */ nuclear@0: smf_event_t * nuclear@0: smf_track_get_next_event(smf_track_t *track) nuclear@0: { nuclear@0: smf_event_t *event, *next_event; nuclear@0: nuclear@0: /* End of track? */ nuclear@0: if (track->next_event_number == -1) nuclear@0: return (NULL); nuclear@0: nuclear@0: assert(track->next_event_number >= 1); nuclear@0: assert(track->number_of_events > 0); nuclear@0: nuclear@0: event = smf_track_get_event_by_number(track, track->next_event_number); nuclear@0: nuclear@0: assert(event != NULL); nuclear@0: nuclear@0: /* Is this the last event in the track? */ nuclear@0: if (track->next_event_number < track->number_of_events) { nuclear@0: next_event = smf_track_get_event_by_number(track, track->next_event_number + 1); nuclear@0: assert(next_event); nuclear@0: nuclear@0: track->time_of_next_event = next_event->time_pulses; nuclear@0: track->next_event_number++; nuclear@0: } else { nuclear@0: track->next_event_number = -1; nuclear@0: } nuclear@0: nuclear@0: return (event); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Returns next event from the track given. Does not change next event counter, nuclear@0: * so repeatedly calling this routine will return the same event. nuclear@0: * \return Event or NULL, if there are no more events left in this track. nuclear@0: */ nuclear@0: static smf_event_t * nuclear@0: smf_peek_next_event_from_track(smf_track_t *track) nuclear@0: { nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: /* End of track? */ nuclear@0: if (track->next_event_number == -1) nuclear@0: return (NULL); nuclear@0: nuclear@0: assert(track->next_event_number >= 1); nuclear@0: assert(track->events_array->len != 0); nuclear@0: nuclear@0: event = smf_track_get_event_by_number(track, track->next_event_number); nuclear@0: nuclear@0: return (event); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Track with a given number or NULL, if there is no such track. nuclear@0: * Tracks are numbered consecutively starting from one. nuclear@0: */ nuclear@0: smf_track_t * nuclear@0: smf_get_track_by_number(const smf_t *smf, int track_number) nuclear@0: { nuclear@0: smf_track_t *track; nuclear@0: nuclear@0: assert(track_number >= 1); nuclear@0: nuclear@0: if (track_number > smf->number_of_tracks) nuclear@0: return (NULL); nuclear@0: nuclear@0: track = (smf_track_t *)fg_ptr_array_index(smf->tracks_array, track_number - 1); nuclear@0: nuclear@0: assert(track); nuclear@0: nuclear@0: return (track); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Event with a given number or NULL, if there is no such event. nuclear@0: * Events are numbered consecutively starting from one. nuclear@0: */ nuclear@0: smf_event_t * nuclear@0: smf_track_get_event_by_number(const smf_track_t *track, int event_number) nuclear@0: { nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: assert(event_number >= 1); nuclear@0: nuclear@0: if (event_number > track->number_of_events) nuclear@0: return (NULL); nuclear@0: nuclear@0: event = fg_ptr_array_index(track->events_array, event_number - 1); nuclear@0: nuclear@0: assert(event); nuclear@0: nuclear@0: return (event); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Last event on the track or NULL, if track is empty. nuclear@0: */ nuclear@0: smf_event_t * nuclear@0: smf_track_get_last_event(const smf_track_t *track) nuclear@0: { nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: if (track->number_of_events == 0) nuclear@0: return (NULL); nuclear@0: nuclear@0: event = smf_track_get_event_by_number(track, track->number_of_events); nuclear@0: nuclear@0: return (event); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Searches for track that contains next event, in time order. In other words, nuclear@0: * returns the track that contains event that should be played next. nuclear@0: * \return Track with next event or NULL, if there are no events left. nuclear@0: */ nuclear@0: smf_track_t * nuclear@0: smf_find_track_with_next_event(smf_t *smf) nuclear@0: { nuclear@0: int i, min_time = 0; nuclear@0: smf_track_t *track = NULL, *min_time_track = NULL; nuclear@0: nuclear@0: /* Find track with event that should be played next. */ nuclear@0: for (i = 1; i <= smf->number_of_tracks; i++) { nuclear@0: track = smf_get_track_by_number(smf, i); nuclear@0: nuclear@0: assert(track); nuclear@0: nuclear@0: /* No more events in this track? */ nuclear@0: if (track->next_event_number == -1) nuclear@0: continue; nuclear@0: nuclear@0: if (track->time_of_next_event < min_time || min_time_track == NULL) { nuclear@0: min_time = track->time_of_next_event; nuclear@0: min_time_track = track; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return (min_time_track); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Next event, in time order, or NULL, if there are none left. nuclear@0: */ nuclear@0: smf_event_t * nuclear@0: smf_get_next_event(smf_t *smf) nuclear@0: { nuclear@0: smf_event_t *event; nuclear@0: smf_track_t *track = smf_find_track_with_next_event(smf); nuclear@0: nuclear@0: if (track == NULL) { nuclear@0: #if 0 nuclear@0: g_debug("End of the song."); nuclear@0: #endif nuclear@0: nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: event = smf_track_get_next_event(track); nuclear@0: nuclear@0: assert(event != NULL); nuclear@0: nuclear@0: event->track->smf->last_seek_position = -1.0; nuclear@0: nuclear@0: return (event); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Advance the "next event counter". This is functionally the same as calling nuclear@0: * smf_get_next_event and ignoring the return value. nuclear@0: */ nuclear@0: void nuclear@0: smf_skip_next_event(smf_t *smf) nuclear@0: { nuclear@0: void *notused; nuclear@0: nuclear@0: notused = smf_get_next_event(smf); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Next event, in time order, or NULL, if there are none left. Does nuclear@0: * not advance position in song. nuclear@0: */ nuclear@0: smf_event_t * nuclear@0: smf_peek_next_event(smf_t *smf) nuclear@0: { nuclear@0: smf_event_t *event; nuclear@0: smf_track_t *track = smf_find_track_with_next_event(smf); nuclear@0: nuclear@0: if (track == NULL) { nuclear@0: #if 0 nuclear@0: g_debug("End of the song."); nuclear@0: #endif nuclear@0: nuclear@0: return (NULL); nuclear@0: } nuclear@0: nuclear@0: event = smf_peek_next_event_from_track(track); nuclear@0: nuclear@0: assert(event != NULL); nuclear@0: nuclear@0: return (event); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Rewinds the SMF. What that means is, after calling this routine, smf_get_next_event nuclear@0: * will return first event in the song. nuclear@0: */ nuclear@0: void nuclear@0: smf_rewind(smf_t *smf) nuclear@0: { nuclear@0: int i; nuclear@0: smf_track_t *track = NULL; nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: assert(smf); nuclear@0: nuclear@0: smf->last_seek_position = 0.0; nuclear@0: nuclear@0: for (i = 1; i <= smf->number_of_tracks; i++) { nuclear@0: track = smf_get_track_by_number(smf, i); nuclear@0: nuclear@0: assert(track != NULL); nuclear@0: nuclear@0: if (track->number_of_events > 0) { nuclear@0: track->next_event_number = 1; nuclear@0: event = smf_peek_next_event_from_track(track); nuclear@0: assert(event); nuclear@0: track->time_of_next_event = event->time_pulses; nuclear@0: } else { nuclear@0: track->next_event_number = -1; nuclear@0: track->time_of_next_event = 0; nuclear@0: #if 0 nuclear@0: g_warning("Warning: empty track."); nuclear@0: #endif nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Seeks the SMF to the given event. After calling this routine, smf_get_next_event nuclear@0: * will return the event that was the second argument of this call. nuclear@0: */ nuclear@0: int nuclear@0: smf_seek_to_event(smf_t *smf, const smf_event_t *target) nuclear@0: { nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: smf_rewind(smf); nuclear@0: nuclear@0: #if 0 nuclear@0: g_debug("Seeking to event %d, track %d.", target->event_number, target->track->track_number); nuclear@0: #endif nuclear@0: nuclear@0: for (;;) { nuclear@0: event = smf_peek_next_event(smf); nuclear@0: nuclear@0: /* There can't be NULL here, unless "target" is not in this smf. */ nuclear@0: assert(event); nuclear@0: nuclear@0: if (event != target) nuclear@0: smf_skip_next_event(smf); nuclear@0: else nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: smf->last_seek_position = event->time_seconds; nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Seeks the SMF to the given position. For example, after seeking to 1.0 seconds, nuclear@0: * smf_get_next_event will return first event that happens after the first second of song. nuclear@0: */ nuclear@0: int nuclear@0: smf_seek_to_seconds(smf_t *smf, double seconds) nuclear@0: { nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: assert(seconds >= 0.0); nuclear@0: nuclear@0: if (seconds == smf->last_seek_position) { nuclear@0: #if 0 nuclear@0: g_debug("Avoiding seek to %f seconds.", seconds); nuclear@0: #endif nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: smf_rewind(smf); nuclear@0: nuclear@0: #if 0 nuclear@0: g_debug("Seeking to %f seconds.", seconds); nuclear@0: #endif nuclear@0: nuclear@0: for (;;) { nuclear@0: event = smf_peek_next_event(smf); nuclear@0: nuclear@0: if (event == NULL) { nuclear@0: fg_critical("Trying to seek past the end of song."); nuclear@0: return (-1); nuclear@0: } nuclear@0: nuclear@0: if (event->time_seconds < seconds) nuclear@0: smf_skip_next_event(smf); nuclear@0: else nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: smf->last_seek_position = seconds; nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * Seeks the SMF to the given position. For example, after seeking to 10 pulses, nuclear@0: * smf_get_next_event will return first event that happens after the first ten pulses. nuclear@0: */ nuclear@0: int nuclear@0: smf_seek_to_pulses(smf_t *smf, int pulses) nuclear@0: { nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: assert(pulses >= 0); nuclear@0: nuclear@0: smf_rewind(smf); nuclear@0: nuclear@0: #if 0 nuclear@0: g_debug("Seeking to %d pulses.", pulses); nuclear@0: #endif nuclear@0: nuclear@0: for (;;) { nuclear@0: event = smf_peek_next_event(smf); nuclear@0: nuclear@0: if (event == NULL) { nuclear@0: fg_critical("Trying to seek past the end of song."); nuclear@0: return (-1); nuclear@0: } nuclear@0: nuclear@0: if (event->time_pulses < pulses) nuclear@0: smf_skip_next_event(smf); nuclear@0: else nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: smf->last_seek_position = event->time_seconds; nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Length of SMF, in pulses. nuclear@0: */ nuclear@0: int nuclear@0: smf_get_length_pulses(const smf_t *smf) nuclear@0: { nuclear@0: int pulses = 0, i; nuclear@0: nuclear@0: for (i = 1; i <= smf->number_of_tracks; i++) { nuclear@0: smf_track_t *track; nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: track = smf_get_track_by_number(smf, i); nuclear@0: assert(track); nuclear@0: nuclear@0: event = smf_track_get_last_event(track); nuclear@0: /* Empty track? */ nuclear@0: if (event == NULL) nuclear@0: continue; nuclear@0: nuclear@0: if (event->time_pulses > pulses) nuclear@0: pulses = event->time_pulses; nuclear@0: } nuclear@0: nuclear@0: return (pulses); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Length of SMF, in seconds. nuclear@0: */ nuclear@0: double nuclear@0: smf_get_length_seconds(const smf_t *smf) nuclear@0: { nuclear@0: int i; nuclear@0: double seconds = 0.0; nuclear@0: nuclear@0: for (i = 1; i <= smf->number_of_tracks; i++) { nuclear@0: smf_track_t *track; nuclear@0: smf_event_t *event; nuclear@0: nuclear@0: track = smf_get_track_by_number(smf, i); nuclear@0: assert(track); nuclear@0: nuclear@0: event = smf_track_get_last_event(track); nuclear@0: /* Empty track? */ nuclear@0: if (event == NULL) nuclear@0: continue; nuclear@0: nuclear@0: if (event->time_seconds > seconds) nuclear@0: seconds = event->time_seconds; nuclear@0: } nuclear@0: nuclear@0: return (seconds); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Nonzero, if there are no events in the SMF after this one. nuclear@0: * Note that may be more than one "last event", if they occur at the same time. nuclear@0: */ nuclear@0: int nuclear@0: smf_event_is_last(const smf_event_t *event) nuclear@0: { nuclear@0: if (smf_get_length_pulses(event->track->smf) <= event->time_pulses) nuclear@0: return (1); nuclear@0: nuclear@0: return (0); nuclear@0: } nuclear@0: nuclear@0: /** nuclear@0: * \return Version of libsmf. nuclear@0: */ nuclear@0: const char * nuclear@0: smf_get_version(void) nuclear@0: { nuclear@0: return (SMF_VERSION); nuclear@0: } nuclear@0: