X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fevoral%2Fsrc%2Flibsmf%2Fsmf.h;h=83112548416bd25c266bf24d52044861a7118802;hb=dffd30baf9f9db9310ddf82a5a7d05f96354acd6;hp=e2a0c3b8a4cf9cb632ea8fc5d97e952c029f960a;hpb=13bcd4342315eb20d5a27a06ba03f6890e20478a;p=ardour.git diff --git a/libs/evoral/src/libsmf/smf.h b/libs/evoral/src/libsmf/smf.h index e2a0c3b8a4..8311254841 100644 --- a/libs/evoral/src/libsmf/smf.h +++ b/libs/evoral/src/libsmf/smf.h @@ -46,8 +46,8 @@ * of these types, i.e. never do something like this: "smf_t smf;". Always use * "smf_t *smf = smf_new();". The same applies to smf_track_t and smf_event_t. * - * Say you want to load a Standard MIDI File (.mid) file and play it back somehow. This is (roughly) - * how you do this: + * Say you want to load a Standard MIDI File (.mid) file and play it back somehow. + * This is (roughly) how you do this: * * \code * smf_t *smf; @@ -115,11 +115,11 @@ * * \endcode * - * There are two basic ways of getting MIDI data out of smf - sequential or by track/event number. You may - * mix them if you need to. First one is used in the example above - seek to the point from which you want - * the playback to start (using smf_seek_to_seconds(), smf_seek_to_pulses() or smf_seek_to_event()) and then - * do smf_get_next_event() in loop, until it returns NULL. Calling smf_load() causes the smf to be rewound - * to the start of the song. + * There are two basic ways of getting MIDI data out of smf - sequential or by track/event number. + * You may mix them if you need to. First one is used in the example above - seek to the point + * from which you want the playback to start (using smf_seek_to_seconds(), smf_seek_to_pulses() + * or smf_seek_to_event()) and then do smf_get_next_event() in loop, until it returns NULL. + * Calling smf_load() causes the smf to be rewound to the start of the song. * * Getting events by number works like this: * @@ -130,83 +130,86 @@ * * \endcode * - * To create new event, use smf_event_new(), smf_event_new_from_pointer() or smf_event_new_from_bytes(). - * First one creates an empty event - you need to manually allocate (using malloc(3)) buffer for - * MIDI data, write MIDI data into it, put the address of that buffer into event->midi_buffer, - * and the length of MIDI data into event->midi_buffer_length. Note that deleting the event - * (using smf_event_delete()) will free the buffer. + * To create new event, use smf_event_new(), smf_event_new_from_pointer() or + * smf_event_new_from_bytes(). First one creates an empty event - you need to manually allocate + * (using malloc(3)) buffer for MIDI data, write MIDI data into it, put the address of that + * buffer into event->midi_buffer, and the length of MIDI data into event->midi_buffer_length. + * Note that deleting the event (using smf_event_delete()) will free the buffer. * * Second form does most of this for you: it takes an address of the buffer containing MIDI data, * allocates storage and copies MIDI data into it. * * Third form is useful for manually creating short events, up to three bytes in length, for - * example Note On or Note Off events. It simply takes three bytes and creates MIDI event containing - * them. If you need to create MIDI message that takes only two bytes, pass -1 as the third byte. - * For one byte message (System Realtime), pass -1 as second and third byte. + * example Note On or Note Off events. It simply takes three bytes and creates MIDI event + * containing them. If you need to create MIDI message that takes only two bytes, pass -1 as + * the third byte. For one byte message (System Realtime), pass -1 as second and third byte. * * To free an event, use smf_event_delete(). * * To add event to the track, use smf_track_add_event_delta_pulses(), smf_track_add_event_pulses(), - * or smf_track_add_event_seconds(). The difference between them is in the way you specify the time of - * the event - with the first one, you specify it as an interval, in pulses, from the previous event - * in this track; with the second one, you specify it as pulses from the start of the song, and with the - * last one, you specify it as seconds from the start of the song. Obviously, the first version can - * only append events at the end of the track. + * or smf_track_add_event_seconds(). The difference between them is in the way you specify the + * time of the event - with the first one, you specify it as an interval, in pulses, from the + * previous event in this track; with the second one, you specify it as pulses from the start + * of the song, and with the last one, you specify it as seconds from the start of the song. + * Obviously, the first version can only append events at the end of the track. * - * To remove an event from the track it's attached to, use smf_event_remove_from_track(). You may - * want to free the event (using smf_event_delete()) afterwards. + * To remove an event from the track it's attached to, use smf_event_remove_from_track(). + * You may want to free the event (using smf_event_delete()) afterwards. * * To create new track, use smf_track_new(). To add track to the smf, use smf_add_track(). * To remove track from its smf, use smf_track_remove_from_smf(). To free the track structure, * use smf_track_delete(). * - * Note that libsmf keeps things consistent. If you free (using smf_track_delete()) a track that - * is attached to an smf and contains events, libsmf will detach the events, free them, detach - * the track, free it etc. - * - * Tracks and events are numbered consecutively, starting from one. If you remove a track or event, - * the rest of tracks/events will get renumbered. To get the number of a given event in its track, use event->event_number. - * To get the number of track in its smf, use track->track_number. To get the number of events in the track, - * use track->number_of_events. To get the number of tracks in the smf, use smf->number_of_tracks. - * - * In SMF File Format, each track has to end with End Of Track metaevent. If you load SMF file using smf_load(), - * that will be the case. If you want to create or edit an SMF, you don't need to worry about EOT events; - * libsmf automatically takes care of them for you. If you try to save an SMF with tracks that do not end - * with EOTs, smf_save() will append them. If you try to add event that happens after EOT metaevent, libsmf - * will remove the EOT. If you want to add EOT manually, you can, of course, using smf_track_add_eot_seconds() + * Note that libsmf keeps things consistent. If you free (using smf_track_delete()) a track + * that is attached to an smf and contains events, libsmf will detach the events, free them, + * detach the track, free it etc. + * + * Tracks and events are numbered consecutively, starting from one. If you remove a track + * or event, the rest of tracks/events will get renumbered. To get the number of a given + * event in its track, use event->event_number. To get the number of track in its smf, use + * track->track_number. To get the number of events in the track, use track->number_of_events. + * To get the number of tracks in the smf, use smf->number_of_tracks. + * + * In SMF File Format, each track has to end with End Of Track metaevent. If you load SMF file + * using smf_load(), that will be the case. If you want to create or edit an SMF, you don't + * need to worry about EOT events; libsmf automatically takes care of them for you. If you + * try to save an SMF with tracks that do not end with EOTs, smf_save() will append them. + * If you try to add event that happens after EOT metaevent, libsmf will remove the EOT. + * If you want to add EOT manually, you can, of course, using smf_track_add_eot_seconds() * or smf_track_add_eot_pulses(). * - * Each event carries three time values - event->time_seconds, which is seconds since the start of the song, - * event->time_pulses, which is PPQN clocks since the start of the song, and event->delta_pulses, which is PPQN clocks - * since the previous event in that track. These values are invalid if the event is not attached to the track. - * If event is attached, all three values are valid. Time of the event is specified when adding the event - * (using smf_track_add_event_seconds(), smf_track_add_event_pulses() or smf_track_add_event_delta_pulses()); the remaining - * two values are computed from that. - * - * Tempo related stuff happens automatically - when you add a metaevent that - * is Tempo Change or Time Signature, libsmf adds that event to the tempo map. If you remove - * Tempo Change event that is in the middle of the song, the rest of the events will have their - * event->time_seconds recomputed from event->time_pulses before smf_event_remove_from_track() function returns. + * Each event carries three time values - event->time_seconds, which is seconds since + * the start of the song, event->time_pulses, which is PPQN clocks since the start of + * the song, and event->delta_pulses, which is PPQN clocks since the previous event + * in that track. These values are invalid if the event is not attached to the track. + * If event is attached, all three values are valid. Time of the event is specified when + * adding the event (using smf_track_add_event_seconds(), smf_track_add_event_pulses() or + * smf_track_add_event_delta_pulses()); the remaining two values are computed from that. + * + * Tempo related stuff happens automatically - when you add a metaevent that is Tempo Change or + * Time Signature, libsmf adds that event to the tempo map. If you remove Tempo Change event + * that is in the middle of the song, the rest of the events will have their event->time_seconds + * recomputed from event->time_pulses before smf_event_remove_from_track() function returns. * Adding Tempo Change in the middle of the song works in a similar way. - * - * MIDI data (event->midi_buffer) is always kept in normalized form - it always begins with status byte - * (no running status), there are no System Realtime events embedded in them etc. Events like SysExes - * are in "on the wire" form, without embedded length that is used in SMF file format. Obviously - * libsmf "normalizes" MIDI data during loading and "denormalizes" (adding length to SysExes, escaping - * System Common and System Realtime messages etc) during writing. + * + * MIDI data (event->midi_buffer) is always kept in normalized form - it always begins with + * status byte (no running status), there are no System Realtime events embedded in them etc. + * Events like SysExes are in "on the wire" form, without embedded length that is used in SMF + * file format. Obviously libsmf "normalizes" MIDI data during loading and "denormalizes" (adding + * length to SysExes, escaping System Common and System Realtime messages etc) during writing. * * Note that you always have to first add the track to smf, and then add events to the track. - * Doing it the other way around will trip asserts. Also, try to add events at the end of the track and remove - * them from the end of the track, that's much more efficient. - * - * All the libsmf functions have prefix "smf_". First argument for routines whose names start with - * "smf_event_" is "smf_event_t *", for routines whose names start with "smf_track_" - "smf_track_t *", - * and for plain "smf_" - "smf_t *". The only exception are smf_whatever_new routines. - * Library does not use any global variables and is thread-safe, - * as long as you don't try to work on the same SMF (smf_t and its descendant tracks and events) from several - * threads at once without protecting it with mutex. Library depends on glib and nothing else. License is - * BSD, two clause, which basically means you can use it freely in your software, both Open Source (including - * GPL) and closed source. + * Doing it the other way around will trip asserts. Also, try to add events at the end of the + * track and remove them from the end of the track, that's much more efficient. + * + * All the libsmf functions have prefix "smf_". First argument for routines whose names start + * with "smf_event_" is "smf_event_t *", for routines whose names start with "smf_track_" - + * "smf_track_t *", and for plain "smf_" - "smf_t *". The only exception are smf_whatever_new + * routines. Library does not use any global variables and is thread-safe, as long as you + * don't try to work on the same SMF (smf_t and its descendant tracks and events) from several + * threads at once without protecting it with mutex. Library depends on glib and nothing else. + * License is BSD, two clause, which basically means you can use it freely in your software, + * both Open Source (including GPL) and closed source. * */ @@ -218,6 +221,7 @@ extern "C" { #endif #include +#include #include #if defined(__GNUC__) && __GNUC__ >= 4 @@ -228,65 +232,66 @@ extern "C" { /** Represents a "song", that is, collection of one or more tracks. */ struct smf_struct { - int format; + int format; - /** These fields are extracted from "division" field of MThd header. Valid is _either_ ppqn or frames_per_second/resolution. */ - int ppqn; - int frames_per_second; - int resolution; - int number_of_tracks; + /** These fields are extracted from "division" field of MThd header. + * Valid is _either_ ppqn or frames_per_second/resolution. */ + uint16_t ppqn; + int frames_per_second; + int resolution; + int number_of_tracks; /** These are private fields using only by loading and saving routines. */ - FILE *stream; - void *file_buffer; - int file_buffer_length; - int next_chunk_offset; - int expected_number_of_tracks; + FILE *stream; + void *file_buffer; + size_t file_buffer_length; + size_t next_chunk_offset; + int expected_number_of_tracks; /** Private, used by smf.c. */ - GPtrArray *tracks_array; - double last_seek_position; + GPtrArray *tracks_array; + double last_seek_position; /** Private, used by smf_tempo.c. */ /** Array of pointers to smf_tempo_struct. */ - GPtrArray *tempo_array; + GPtrArray *tempo_array; }; typedef struct smf_struct smf_t; /** Describes a single tempo or time signature change. */ struct smf_tempo_struct { - int time_pulses; + size_t time_pulses; double time_seconds; - int microseconds_per_quarter_note; - int numerator; - int denominator; - int clocks_per_click; - int notes_per_note; + int microseconds_per_quarter_note; + int numerator; + int denominator; + int clocks_per_click; + int notes_per_note; }; typedef struct smf_tempo_struct smf_tempo_t; /** Represents a single track. */ struct smf_track_struct { - smf_t *smf; + smf_t *smf; - int track_number; - int number_of_events; + int track_number; + size_t number_of_events; /** These are private fields using only by loading and saving routines. */ - void *file_buffer; - int file_buffer_length; - int last_status; /* Used for "running status". */ + void *file_buffer; + size_t file_buffer_length; + int last_status; /* Used for "running status". */ /** Private, used by smf.c. */ /** Offset into buffer, used in parse_next_event(). */ - int next_event_offset; - int next_event_number; + size_t next_event_offset; + size_t next_event_number; /** Absolute time of next event on events_queue. */ - int time_of_next_event; - GPtrArray *events_array; + size_t time_of_next_event; + GPtrArray *events_array; }; typedef struct smf_track_struct smf_track_t; @@ -294,39 +299,39 @@ typedef struct smf_track_struct smf_track_t; /** Represents a single MIDI event or metaevent. */ struct smf_event_struct { /** Pointer to the track, or NULL if event is not attached. */ - smf_track_t *track; + smf_track_t *track; - /** Number of this event in the track. Events are numbered consecutively, starting from one. */ - int event_number; + /** Number of this event in the track. Events are numbered consecutively, starting from 1. */ + size_t event_number; /** Note that the time fields are invalid, if event is not attached to a track. */ /** Time, in pulses, since the previous event on this track. */ - int delta_time_pulses; + int32_t delta_time_pulses; /** Time, in pulses, since the start of the song. */ - int time_pulses; + size_t time_pulses; /** Time, in seconds, since the start of the song. */ - double time_seconds; + double time_seconds; /** Tracks are numbered consecutively, starting from 1. */ - int track_number; + int track_number; /** Pointer to the buffer containing MIDI message. This is freed by smf_event_delete. */ - unsigned char *midi_buffer; + uint8_t *midi_buffer; /** Length of the MIDI message in the buffer, in bytes. */ - int midi_buffer_length; + size_t midi_buffer_length; }; typedef struct smf_event_struct smf_event_t; /* Routines for manipulating smf_t. */ smf_t *smf_new(void) WARN_UNUSED_RESULT; -void smf_delete(smf_t *smf); +void smf_delete(smf_t *smf); int smf_set_format(smf_t *smf, int format) WARN_UNUSED_RESULT; -int smf_set_ppqn(smf_t *smf, int format) WARN_UNUSED_RESULT; +int smf_set_ppqn(smf_t *smf, uint16_t ppqn) WARN_UNUSED_RESULT; char *smf_decode(const smf_t *smf) WARN_UNUSED_RESULT; @@ -334,64 +339,64 @@ smf_track_t *smf_get_track_by_number(const smf_t *smf, int track_number) WARN_UN smf_event_t *smf_peek_next_event(smf_t *smf) WARN_UNUSED_RESULT; smf_event_t *smf_get_next_event(smf_t *smf) WARN_UNUSED_RESULT; -void smf_skip_next_event(smf_t *smf); +void smf_skip_next_event(smf_t *smf); void smf_rewind(smf_t *smf); -int smf_seek_to_seconds(smf_t *smf, double seconds) WARN_UNUSED_RESULT; -int smf_seek_to_pulses(smf_t *smf, int pulses) WARN_UNUSED_RESULT; -int smf_seek_to_event(smf_t *smf, const smf_event_t *event) WARN_UNUSED_RESULT; +int smf_seek_to_seconds(smf_t *smf, double seconds) WARN_UNUSED_RESULT; +int smf_seek_to_pulses(smf_t *smf, size_t pulses) WARN_UNUSED_RESULT; +int smf_seek_to_event(smf_t *smf, const smf_event_t *event) WARN_UNUSED_RESULT; -int smf_get_length_pulses(const smf_t *smf) WARN_UNUSED_RESULT; +size_t smf_get_length_pulses(const smf_t *smf) WARN_UNUSED_RESULT; double smf_get_length_seconds(const smf_t *smf) WARN_UNUSED_RESULT; -int smf_event_is_last(const smf_event_t *event) WARN_UNUSED_RESULT; +int smf_event_is_last(const smf_event_t *event) WARN_UNUSED_RESULT; void smf_add_track(smf_t *smf, smf_track_t *track); void smf_track_remove_from_smf(smf_track_t *track); /* Routines for manipulating smf_track_t. */ smf_track_t *smf_track_new(void) WARN_UNUSED_RESULT; -void smf_track_delete(smf_track_t *track); +void smf_track_delete(smf_track_t *track); smf_event_t *smf_track_get_next_event(smf_track_t *track) WARN_UNUSED_RESULT; -smf_event_t *smf_track_get_event_by_number(const smf_track_t *track, int event_number) WARN_UNUSED_RESULT; +smf_event_t *smf_track_get_event_by_number(const smf_track_t *track, size_t num) WARN_UNUSED_RESULT; smf_event_t *smf_track_get_last_event(const smf_track_t *track) WARN_UNUSED_RESULT; -void smf_track_add_event_delta_pulses(smf_track_t *track, smf_event_t *event, int pulses); -void smf_track_add_event_pulses(smf_track_t *track, smf_event_t *event, int pulses); +void smf_track_add_event_delta_pulses(smf_track_t *track, smf_event_t *event, uint32_t delta); +void smf_track_add_event_pulses(smf_track_t *track, smf_event_t *event, size_t pulses); void smf_track_add_event_seconds(smf_track_t *track, smf_event_t *event, double seconds); -int smf_track_add_eot_delta_pulses(smf_track_t *track, int delta) WARN_UNUSED_RESULT; -int smf_track_add_eot_pulses(smf_track_t *track, int pulses) WARN_UNUSED_RESULT; -int smf_track_add_eot_seconds(smf_track_t *track, double seconds) WARN_UNUSED_RESULT; +int smf_track_add_eot_delta_pulses(smf_track_t *track, uint32_t delta) WARN_UNUSED_RESULT; +int smf_track_add_eot_pulses(smf_track_t *track, size_t pulses) WARN_UNUSED_RESULT; +int smf_track_add_eot_seconds(smf_track_t *track, double seconds) WARN_UNUSED_RESULT; void smf_event_remove_from_track(smf_event_t *event); /* Routines for manipulating smf_event_t. */ smf_event_t *smf_event_new(void) WARN_UNUSED_RESULT; -smf_event_t *smf_event_new_from_pointer(void *midi_data, int len) WARN_UNUSED_RESULT; -smf_event_t *smf_event_new_from_bytes(int first_byte, int second_byte, int third_byte) WARN_UNUSED_RESULT; +smf_event_t *smf_event_new_from_pointer(const void *midi_data, size_t len) WARN_UNUSED_RESULT; +smf_event_t *smf_event_new_from_bytes(int byte1, int byte2, int byte3) WARN_UNUSED_RESULT; smf_event_t *smf_event_new_textual(int type, const char *text); -void smf_event_delete(smf_event_t *event); - -int smf_event_is_valid(const smf_event_t *event) WARN_UNUSED_RESULT; -int smf_event_is_metadata(const smf_event_t *event) WARN_UNUSED_RESULT; -int smf_event_is_system_realtime(const smf_event_t *event) WARN_UNUSED_RESULT; -int smf_event_is_system_common(const smf_event_t *event) WARN_UNUSED_RESULT; -int smf_event_is_sysex(const smf_event_t *event) WARN_UNUSED_RESULT; -int smf_event_is_eot(const smf_event_t *event) WARN_UNUSED_RESULT; -int smf_event_is_textual(const smf_event_t *event) WARN_UNUSED_RESULT; +void smf_event_delete(smf_event_t *event); + +int smf_event_is_valid(const smf_event_t *event) WARN_UNUSED_RESULT; +int smf_event_is_metadata(const smf_event_t *event) WARN_UNUSED_RESULT; +int smf_event_is_system_realtime(const smf_event_t *event) WARN_UNUSED_RESULT; +int smf_event_is_system_common(const smf_event_t *event) WARN_UNUSED_RESULT; +int smf_event_is_sysex(const smf_event_t *event) WARN_UNUSED_RESULT; +int smf_event_is_eot(const smf_event_t *event) WARN_UNUSED_RESULT; +int smf_event_is_textual(const smf_event_t *event) WARN_UNUSED_RESULT; char *smf_event_decode(const smf_event_t *event) WARN_UNUSED_RESULT; char *smf_event_extract_text(const smf_event_t *event) WARN_UNUSED_RESULT; /* Routines for loading SMF files. */ smf_t *smf_load(const char *file_name) WARN_UNUSED_RESULT; -smf_t *smf_load_from_memory(const void *buffer, const int buffer_length) WARN_UNUSED_RESULT; +smf_t *smf_load_from_memory(const void *buffer, const size_t buffer_length) WARN_UNUSED_RESULT; /* Routine for writing SMF files. */ int smf_save(smf_t *smf, const char *file_name) WARN_UNUSED_RESULT; /* Routines for manipulating smf_tempo_t. */ -smf_tempo_t *smf_get_tempo_by_pulses(const smf_t *smf, int pulses) WARN_UNUSED_RESULT; +smf_tempo_t *smf_get_tempo_by_pulses(const smf_t *smf, size_t pulses) WARN_UNUSED_RESULT; smf_tempo_t *smf_get_tempo_by_seconds(const smf_t *smf, double seconds) WARN_UNUSED_RESULT; -smf_tempo_t *smf_get_tempo_by_number(const smf_t *smf, int number) WARN_UNUSED_RESULT; +smf_tempo_t *smf_get_tempo_by_number(const smf_t *smf, size_t number) WARN_UNUSED_RESULT; smf_tempo_t *smf_get_last_tempo(const smf_t *smf) WARN_UNUSED_RESULT; const char *smf_get_version(void) WARN_UNUSED_RESULT;