diff options
Diffstat (limited to 'libs/fluidsynth/src/fluid_midi.c')
-rw-r--r-- | libs/fluidsynth/src/fluid_midi.c | 1457 |
1 files changed, 973 insertions, 484 deletions
diff --git a/libs/fluidsynth/src/fluid_midi.c b/libs/fluidsynth/src/fluid_midi.c index fc58d839ef..bdf72dd681 100644 --- a/libs/fluidsynth/src/fluid_midi.c +++ b/libs/fluidsynth/src/fluid_midi.c @@ -3,16 +3,16 @@ * Copyright (C) 2003 Peter Hanappe and others. * * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public License - * as published by the Free Software Foundation; either version 2 of + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public + * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA @@ -25,15 +25,56 @@ static int fluid_midi_event_length(unsigned char event); +static int fluid_isasciistring(char *s); +static long fluid_getlength(unsigned char *s); + /* Read the entire contents of a file into memory, allocating enough memory * for the file, and returning the length and the buffer. * Note: This rewinds the file to the start before reading. * Returns NULL if there was an error reading or allocating memory. */ -static char* fluid_file_read_full(fluid_file fp, size_t* length); +static char *fluid_file_read_full(fluid_file fp, size_t *length); +static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic); #define READ_FULL_INITIAL_BUFLEN 1024 +static fluid_track_t *new_fluid_track(int num); +static void delete_fluid_track(fluid_track_t *track); +static int fluid_track_set_name(fluid_track_t *track, char *name); +static int fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt); +static fluid_midi_event_t *fluid_track_next_event(fluid_track_t *track); +static int fluid_track_get_duration(fluid_track_t *track); +static int fluid_track_reset(fluid_track_t *track); + +static int fluid_track_send_events(fluid_track_t *track, + fluid_synth_t *synth, + fluid_player_t *player, + unsigned int ticks); + + +static int fluid_player_add_track(fluid_player_t *player, fluid_track_t *track); +static int fluid_player_callback(void *data, unsigned int msec); +static int fluid_player_reset(fluid_player_t *player); +static int fluid_player_load(fluid_player_t *player, fluid_playlist_item *item); +static void fluid_player_advancefile(fluid_player_t *player); +static void fluid_player_playlist_load(fluid_player_t *player, unsigned int msec); + +static fluid_midi_file *new_fluid_midi_file(const char *buffer, size_t length); +static void delete_fluid_midi_file(fluid_midi_file *mf); +static int fluid_midi_file_read_mthd(fluid_midi_file *midifile); +static int fluid_midi_file_load_tracks(fluid_midi_file *midifile, fluid_player_t *player); +static int fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num); +static int fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track); +static int fluid_midi_file_read_varlen(fluid_midi_file *mf); +static int fluid_midi_file_getc(fluid_midi_file *mf); +static int fluid_midi_file_push(fluid_midi_file *mf, int c); +static int fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len); +static int fluid_midi_file_skip(fluid_midi_file *mf, int len); +static int fluid_midi_file_eof(fluid_midi_file *mf); +static int fluid_midi_file_read_tracklen(fluid_midi_file *mf); +static int fluid_midi_file_eot(fluid_midi_file *mf); +static int fluid_midi_file_get_division(fluid_midi_file *midifile); + #if 0 // disable file I/O with Ardour /*************************************************************** * @@ -49,15 +90,18 @@ static char* fluid_file_read_full(fluid_file fp, size_t* length); * @return New MIDI file handle or NULL on error. */ fluid_midi_file * -new_fluid_midi_file(const char* buffer, size_t length) +new_fluid_midi_file(const char *buffer, size_t length) { fluid_midi_file *mf; mf = FLUID_NEW(fluid_midi_file); - if (mf == NULL) { + + if(mf == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } + FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file)); mf->c = -1; @@ -68,45 +112,58 @@ new_fluid_midi_file(const char* buffer, size_t length) mf->buf_pos = 0; mf->eof = FALSE; - if (fluid_midi_file_read_mthd(mf) != FLUID_OK) { + if(fluid_midi_file_read_mthd(mf) != FLUID_OK) + { FLUID_FREE(mf); return NULL; } + return mf; } -static char* -fluid_file_read_full(fluid_file fp, size_t* length) +static char * +fluid_file_read_full(fluid_file fp, size_t *length) { size_t buflen; - char* buffer; + char *buffer; size_t n; + /* Work out the length of the file in advance */ - if (FLUID_FSEEK(fp, 0, SEEK_END) != 0) + if(FLUID_FSEEK(fp, 0, SEEK_END) != 0) { FLUID_LOG(FLUID_ERR, "File load: Could not seek within file"); return NULL; } + buflen = ftell(fp); - if (FLUID_FSEEK(fp, 0, SEEK_SET) != 0) + + if(FLUID_FSEEK(fp, 0, SEEK_SET) != 0) { FLUID_LOG(FLUID_ERR, "File load: Could not seek within file"); return NULL; } + FLUID_LOG(FLUID_DBG, "File load: Allocating %d bytes", buflen); buffer = FLUID_MALLOC(buflen); - if (buffer == NULL) { + + if(buffer == NULL) + { FLUID_LOG(FLUID_PANIC, "Out of memory"); return NULL; } + n = FLUID_FREAD(buffer, 1, buflen, fp); - if (n != buflen) { + + if(n != buflen) + { FLUID_LOG(FLUID_ERR, "Only read %d bytes; expected %d", n, buflen); FLUID_FREE(buffer); return NULL; }; + *length = n; + return buffer; } @@ -116,35 +173,40 @@ fluid_file_read_full(fluid_file fp, size_t* length) * @param mf MIDI file handle to close and free. */ void -delete_fluid_midi_file (fluid_midi_file *mf) +delete_fluid_midi_file(fluid_midi_file *mf) { - if (mf == NULL) { - return; - } + fluid_return_if_fail(mf != NULL); + FLUID_FREE(mf); - return; } /* * Gets the next byte in a MIDI file, taking into account previous running status. * - * returns FLUID_FAILED if EOF or read error + * returns -1 if EOF or read error */ int -fluid_midi_file_getc (fluid_midi_file *mf) +fluid_midi_file_getc(fluid_midi_file *mf) { unsigned char c; - if (mf->c >= 0) { + + if(mf->c >= 0) + { c = mf->c; mf->c = -1; - } else { - if (mf->buf_pos >= mf->buf_len) { + } + else + { + if(mf->buf_pos >= mf->buf_len) + { mf->eof = TRUE; - return FLUID_FAILED; + return -1; } + c = mf->buffer[mf->buf_pos++]; mf->trackpos++; } + return (int) c; } @@ -166,23 +228,35 @@ int fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len) { int num = len < mf->buf_len - mf->buf_pos - ? len : mf->buf_len - mf->buf_pos; - if (num != len) { + ? len : mf->buf_len - mf->buf_pos; + + if(num != len) + { mf->eof = TRUE; } - if (num < 0) { + + if(num < 0) + { num = 0; } + /* Note: Read bytes, even if there aren't enough, but only increment * trackpos if successful (emulates old behaviour of fluid_midi_file_read) */ - FLUID_MEMCPY(buf, mf->buffer+mf->buf_pos, num); + FLUID_MEMCPY(buf, mf->buffer + mf->buf_pos, num); mf->buf_pos += num; - if (num == len) + + if(num == len) + { mf->trackpos += num; + } + #if DEBUG else + { FLUID_LOG(FLUID_DBG, "Could not read the requested number of bytes"); + } + #endif return (num != len) ? FLUID_FAILED : FLUID_OK; } @@ -191,15 +265,18 @@ fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len) * fluid_midi_file_skip */ int -fluid_midi_file_skip (fluid_midi_file *mf, int skip) +fluid_midi_file_skip(fluid_midi_file *mf, int skip) { int new_pos = mf->buf_pos + skip; + /* Mimic the behaviour of fseek: Error to seek past the start of file, but * OK to seek past end (this just puts it into the EOF state). */ - if (new_pos < 0) { + if(new_pos < 0) + { FLUID_LOG(FLUID_ERR, "Failed to seek position in file"); return FLUID_FAILED; } + /* Clear the EOF flag, even if moved past the end of the file (this is * consistent with the behaviour of fseek). */ mf->eof = FALSE; @@ -210,15 +287,15 @@ fluid_midi_file_skip (fluid_midi_file *mf, int skip) /* * fluid_midi_file_eof */ -int fluid_midi_file_eof(fluid_midi_file* mf) +int fluid_midi_file_eof(fluid_midi_file *mf) { - /* Note: This does not simply test whether the file read pointer is past - * the end of the file. It mimics the behaviour of feof by actually - * testing the stateful EOF condition, which is set to TRUE if getc or - * fread have attempted to read past the end (but not if they have - * precisely reached the end), but reset to FALSE upon a successful seek. - */ - return mf->eof; + /* Note: This does not simply test whether the file read pointer is past + * the end of the file. It mimics the behaviour of feof by actually + * testing the stateful EOF condition, which is set to TRUE if getc or + * fread have attempted to read past the end (but not if they have + * precisely reached the end), but reset to FALSE upon a successful seek. + */ + return mf->eof; } /* @@ -227,30 +304,40 @@ int fluid_midi_file_eof(fluid_midi_file* mf) int fluid_midi_file_read_mthd(fluid_midi_file *mf) { - char mthd[15]; - if (fluid_midi_file_read(mf, mthd, 14) != FLUID_OK) { + char mthd[14]; + + if(fluid_midi_file_read(mf, mthd, sizeof(mthd)) != FLUID_OK) + { return FLUID_FAILED; } - if ((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6) - || (mthd[9] > 2)) { + + if((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6) + || (mthd[9] > 2)) + { FLUID_LOG(FLUID_ERR, - "Doesn't look like a MIDI file: invalid MThd header"); + "Doesn't look like a MIDI file: invalid MThd header"); return FLUID_FAILED; } + mf->type = mthd[9]; mf->ntracks = (unsigned) mthd[11]; - mf->ntracks += (unsigned int) (mthd[10]) << 16; - if ((mthd[12]) < 0) { + mf->ntracks += (unsigned int)(mthd[10]) << 16; + + if((signed char)mthd[12] < 0) + { mf->uses_smpte = 1; - mf->smpte_fps = -mthd[12]; + mf->smpte_fps = -(signed char)mthd[12]; mf->smpte_res = (unsigned) mthd[13]; FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet"); return FLUID_FAILED; - } else { + } + else + { mf->uses_smpte = 0; - mf->division = (mthd[12] << 8) | (mthd[13] & 0xff); + mf->division = ((unsigned)mthd[12] << 8) | ((unsigned)mthd[13] & 0xff); FLUID_LOG(FLUID_DBG, "Division=%d", mf->division); } + return FLUID_OK; } @@ -261,11 +348,15 @@ int fluid_midi_file_load_tracks(fluid_midi_file *mf, fluid_player_t *player) { int i; - for (i = 0; i < mf->ntracks; i++) { - if (fluid_midi_file_read_track(mf, player, i) != FLUID_OK) { + + for(i = 0; i < mf->ntracks; i++) + { + if(fluid_midi_file_read_track(mf, player, i) != FLUID_OK) + { return FLUID_FAILED; } } + return FLUID_OK; } @@ -275,14 +366,23 @@ fluid_midi_file_load_tracks(fluid_midi_file *mf, fluid_player_t *player) int fluid_isasciistring(char *s) { + /* From ctype.h */ +#define fluid_isascii(c) (((c) & ~0x7f) == 0) + int i; int len = (int) FLUID_STRLEN(s); - for (i = 0; i < len; i++) { - if (!fluid_isascii(s[i])) { + + for(i = 0; i < len; i++) + { + if(!fluid_isascii(s[i])) + { return 0; } } + return 1; + +#undef fluid_isascii } /* @@ -303,9 +403,12 @@ int fluid_midi_file_read_tracklen(fluid_midi_file *mf) { unsigned char length[5]; - if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) { + + if(fluid_midi_file_read(mf, length, 4) != FLUID_OK) + { return FLUID_FAILED; } + mf->tracklen = fluid_getlength(length); mf->trackpos = 0; mf->eot = 0; @@ -319,9 +422,12 @@ int fluid_midi_file_eot(fluid_midi_file *mf) { #if DEBUG - if (mf->trackpos > mf->tracklen) { + + if(mf->trackpos > mf->tracklen) + { printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen); } + #endif return mf->eot || (mf->trackpos >= mf->tracklen); } @@ -337,69 +443,93 @@ fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num) int found_track = 0; int skip; - if (fluid_midi_file_read(mf, id, 4) != FLUID_OK) { + if(fluid_midi_file_read(mf, id, 4) != FLUID_OK) + { return FLUID_FAILED; } + id[4] = '\0'; mf->dtime = 0; - while (!found_track) { + while(!found_track) + { - if (fluid_isasciistring((char *) id) == 0) { + if(fluid_isasciistring((char *) id) == 0) + { FLUID_LOG(FLUID_ERR, - "An non-ascii track header found, corrupt file"); + "An non-ascii track header found, corrupt file"); return FLUID_FAILED; - } else if (strcmp((char *) id, "MTrk") == 0) { + } + else if(FLUID_STRCMP((char *) id, "MTrk") == 0) + { found_track = 1; - if (fluid_midi_file_read_tracklen(mf) != FLUID_OK) { + if(fluid_midi_file_read_tracklen(mf) != FLUID_OK) + { return FLUID_FAILED; } track = new_fluid_track(num); - if (track == NULL) { + + if(track == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } - while (!fluid_midi_file_eot(mf)) { - if (fluid_midi_file_read_event(mf, track) != FLUID_OK) { + while(!fluid_midi_file_eot(mf)) + { + if(fluid_midi_file_read_event(mf, track) != FLUID_OK) + { delete_fluid_track(track); return FLUID_FAILED; } } /* Skip remaining track data, if any */ - if (mf->trackpos < mf->tracklen) { - if (fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos) != FLUID_OK) { + if(mf->trackpos < mf->tracklen) + { + if(fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos) != FLUID_OK) + { delete_fluid_track(track); return FLUID_FAILED; } } - if (fluid_player_add_track(player, track) != FLUID_OK) { + if(fluid_player_add_track(player, track) != FLUID_OK) + { delete_fluid_track(track); return FLUID_FAILED; } - } else { + } + else + { found_track = 0; - if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) { + + if(fluid_midi_file_read(mf, length, 4) != FLUID_OK) + { return FLUID_FAILED; } + skip = fluid_getlength(length); + /* fseek(mf->fp, skip, SEEK_CUR); */ - if (fluid_midi_file_skip(mf, skip) != FLUID_OK) { + if(fluid_midi_file_skip(mf, skip) != FLUID_OK) + { return FLUID_FAILED; } } } - if (fluid_midi_file_eof(mf)) { + + if(fluid_midi_file_eof(mf)) + { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } + return FLUID_OK; } @@ -412,24 +542,35 @@ fluid_midi_file_read_varlen(fluid_midi_file *mf) int i; int c; mf->varlen = 0; - for (i = 0;; i++) { - if (i == 4) { + + for(i = 0;; i++) + { + if(i == 4) + { FLUID_LOG(FLUID_ERR, "Invalid variable length number"); return FLUID_FAILED; } + c = fluid_midi_file_getc(mf); - if (c < 0) { + + if(c < 0) + { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } - if (c & 0x80) { - mf->varlen |= (int) (c & 0x7F); + + if(c & 0x80) + { + mf->varlen |= (int)(c & 0x7F); mf->varlen <<= 7; - } else { + } + else + { mf->varlen += c; break; } } + return FLUID_OK; } @@ -453,24 +594,31 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track) int size; /* read the delta-time of the event */ - if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { return FLUID_FAILED; } + mf->dtime += mf->varlen; /* read the status byte */ status = fluid_midi_file_getc(mf); - if (status < 0) { + + if(status < 0) + { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } /* not a valid status byte: use the running status instead */ - if ((status & 0x80) == 0) { - if ((mf->running_status & 0x80) == 0) { + if((status & 0x80) == 0) + { + if((mf->running_status & 0x80) == 0) + { FLUID_LOG(FLUID_ERR, "Undefined status and invalid running status"); return FLUID_FAILED; } + fluid_midi_file_push(mf, status); status = mf->running_status; } @@ -479,40 +627,49 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track) mf->running_status = status; - if ((status == MIDI_SYSEX)) { /* system exclusif */ + if(status == MIDI_SYSEX) /* system exclusif */ + { /* read the length of the message */ - if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { return FLUID_FAILED; } - if (mf->varlen) { + if(mf->varlen) + { FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, - __LINE__, mf->varlen); + __LINE__, mf->varlen); metadata = FLUID_MALLOC(mf->varlen + 1); - if (metadata == NULL) { + if(metadata == NULL) + { FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } /* read the data of the message */ - if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) { - FLUID_FREE (metadata); + if(fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) + { + FLUID_FREE(metadata); return FLUID_FAILED; } evt = new_fluid_midi_event(); - if (evt == NULL) { + + if(evt == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); - FLUID_FREE (metadata); + FLUID_FREE(metadata); return FLUID_FAILED; } evt->dtime = mf->dtime; size = mf->varlen; - if (metadata[mf->varlen - 1] == MIDI_EOX) + if(metadata[mf->varlen - 1] == MIDI_EOX) + { size--; + } /* Add SYSEX event and indicate that its dynamically allocated and should be freed with event */ fluid_midi_event_set_sysex(evt, metadata, size, TRUE); @@ -522,232 +679,318 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track) return FLUID_OK; - } else if (status == MIDI_META_EVENT) { /* meta events */ + } + else if(status == MIDI_META_EVENT) /* meta events */ + { int result = FLUID_OK; /* get the type of the meta message */ type = fluid_midi_file_getc(mf); - if (type < 0) { + + if(type < 0) + { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } /* get the length of the data part */ - if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { + if(fluid_midi_file_read_varlen(mf) != FLUID_OK) + { return FLUID_FAILED; } - if (mf->varlen < 255) { + if(mf->varlen < 255) + { metadata = &static_buf[0]; - } else { + } + else + { FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, - __LINE__, mf->varlen); + __LINE__, mf->varlen); dyn_buf = FLUID_MALLOC(mf->varlen + 1); - if (dyn_buf == NULL) { + + if(dyn_buf == NULL) + { FLUID_LOG(FLUID_PANIC, "Out of memory"); return FLUID_FAILED; } + metadata = dyn_buf; } /* read the data */ - if (mf->varlen) { - if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) { - if (dyn_buf) { + if(mf->varlen) + { + if(fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) + { + if(dyn_buf) + { FLUID_FREE(dyn_buf); } + return FLUID_FAILED; } } /* handle meta data */ - switch (type) { + switch(type) + { - case MIDI_COPYRIGHT: - metadata[mf->varlen] = 0; - break; + case MIDI_COPYRIGHT: + metadata[mf->varlen] = 0; + break; - case MIDI_TRACK_NAME: - metadata[mf->varlen] = 0; - fluid_track_set_name(track, (char *) metadata); - break; + case MIDI_TRACK_NAME: + metadata[mf->varlen] = 0; + fluid_track_set_name(track, (char *) metadata); + break; - case MIDI_INST_NAME: - metadata[mf->varlen] = 0; - break; + case MIDI_INST_NAME: + metadata[mf->varlen] = 0; + break; + + case MIDI_LYRIC: + case MIDI_TEXT: + { + void *tmp; + int size = mf->varlen + 1; + + /* NULL terminate strings for safety */ + metadata[size - 1] = '\0'; + + evt = new_fluid_midi_event(); - case MIDI_LYRIC: + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; break; + } + + evt->dtime = mf->dtime; - case MIDI_MARKER: + tmp = FLUID_MALLOC(size); + + if(tmp == NULL) + { + FLUID_LOG(FLUID_PANIC, "Out of memory"); + delete_fluid_midi_event(evt); + evt = NULL; + result = FLUID_FAILED; break; + } - case MIDI_CUE_POINT: - break; /* don't care much for text events */ + FLUID_MEMCPY(tmp, metadata, size); - case MIDI_EOT: - if (mf->varlen != 0) { - FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event"); - result = FLUID_FAILED; - break; - } - mf->eot = 1; - evt = new_fluid_midi_event(); - if (evt == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - result = FLUID_FAILED; - break; - } - evt->dtime = mf->dtime; - evt->type = MIDI_EOT; - fluid_track_add_event(track, evt); - mf->dtime = 0; + fluid_midi_event_set_sysex_LOCAL(evt, type, tmp, size, TRUE); + fluid_track_add_event(track, evt); + mf->dtime = 0; + } + break; + + case MIDI_MARKER: + break; + + case MIDI_CUE_POINT: + break; /* don't care much for text events */ + + case MIDI_EOT: + if(mf->varlen != 0) + { + FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event"); + result = FLUID_FAILED; break; + } - case MIDI_SET_TEMPO: - if (mf->varlen != 3) { - FLUID_LOG(FLUID_ERR, - "Invalid length for SetTempo meta event"); - result = FLUID_FAILED; - break; - } - tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2]; - evt = new_fluid_midi_event(); - if (evt == NULL) { - FLUID_LOG(FLUID_ERR, "Out of memory"); - result = FLUID_FAILED; - break; - } - evt->dtime = mf->dtime; - evt->type = MIDI_SET_TEMPO; - evt->channel = 0; - evt->param1 = tempo; - evt->param2 = 0; - fluid_track_add_event(track, evt); - mf->dtime = 0; + mf->eot = 1; + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; break; + } - case MIDI_SMPTE_OFFSET: - if (mf->varlen != 5) { - FLUID_LOG(FLUID_ERR, - "Invalid length for SMPTE Offset meta event"); - result = FLUID_FAILED; - break; - } - break; /* we don't use smtp */ - - case MIDI_TIME_SIGNATURE: - if (mf->varlen != 4) { - FLUID_LOG(FLUID_ERR, - "Invalid length for TimeSignature meta event"); - result = FLUID_FAILED; - break; - } - nominator = metadata[0]; - denominator = pow(2.0, (double) metadata[1]); - clocks = metadata[2]; - notes = metadata[3]; + evt->dtime = mf->dtime; + evt->type = MIDI_EOT; + fluid_track_add_event(track, evt); + mf->dtime = 0; + break; - FLUID_LOG(FLUID_DBG, - "signature=%d/%d, metronome=%d, 32nd-notes=%d", - nominator, denominator, clocks, notes); + case MIDI_SET_TEMPO: + if(mf->varlen != 3) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for SetTempo meta event"); + result = FLUID_FAILED; + break; + } + tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2]; + evt = new_fluid_midi_event(); + + if(evt == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + result = FLUID_FAILED; break; + } - case MIDI_KEY_SIGNATURE: - if (mf->varlen != 2) { - FLUID_LOG(FLUID_ERR, - "Invalid length for KeySignature meta event"); - result = FLUID_FAILED; - break; - } - /* We don't care about key signatures anyway */ - /* sf = metadata[0]; - mi = metadata[1]; */ + evt->dtime = mf->dtime; + evt->type = MIDI_SET_TEMPO; + evt->channel = 0; + evt->param1 = tempo; + evt->param2 = 0; + fluid_track_add_event(track, evt); + mf->dtime = 0; + break; + + case MIDI_SMPTE_OFFSET: + if(mf->varlen != 5) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for SMPTE Offset meta event"); + result = FLUID_FAILED; break; + } - case MIDI_SEQUENCER_EVENT: + break; /* we don't use smtp */ + + case MIDI_TIME_SIGNATURE: + if(mf->varlen != 4) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for TimeSignature meta event"); + result = FLUID_FAILED; break; + } - default: + nominator = metadata[0]; + denominator = pow(2.0, (double) metadata[1]); + clocks = metadata[2]; + notes = metadata[3]; + + FLUID_LOG(FLUID_DBG, + "signature=%d/%d, metronome=%d, 32nd-notes=%d", + nominator, denominator, clocks, notes); + + break; + + case MIDI_KEY_SIGNATURE: + if(mf->varlen != 2) + { + FLUID_LOG(FLUID_ERR, + "Invalid length for KeySignature meta event"); + result = FLUID_FAILED; break; + } + + /* We don't care about key signatures anyway */ + /* sf = metadata[0]; + mi = metadata[1]; */ + break; + + case MIDI_SEQUENCER_EVENT: + break; + + default: + break; } - if (dyn_buf) { + if(dyn_buf) + { FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__); FLUID_FREE(dyn_buf); } return result; - } else { /* channel messages */ + } + else /* channel messages */ + { type = status & 0xf0; channel = status & 0x0f; /* all channel message have at least 1 byte of associated data */ - if ((param1 = fluid_midi_file_getc(mf)) < 0) { + if((param1 = fluid_midi_file_getc(mf)) < 0) + { FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; } - switch (type) { + switch(type) + { - case NOTE_ON: - if ((param2 = fluid_midi_file_getc(mf)) < 0) { - FLUID_LOG(FLUID_ERR, "Unexpected end of file"); - return FLUID_FAILED; - } - break; + case NOTE_ON: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } - case NOTE_OFF: - if ((param2 = fluid_midi_file_getc(mf)) < 0) { - FLUID_LOG(FLUID_ERR, "Unexpected end of file"); - return FLUID_FAILED; - } - break; + break; - case KEY_PRESSURE: - if ((param2 = fluid_midi_file_getc(mf)) < 0) { - FLUID_LOG(FLUID_ERR, "Unexpected end of file"); - return FLUID_FAILED; - } - break; + case NOTE_OFF: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } - case CONTROL_CHANGE: - if ((param2 = fluid_midi_file_getc(mf)) < 0) { - FLUID_LOG(FLUID_ERR, "Unexpected end of file"); - return FLUID_FAILED; - } - break; + break; - case PROGRAM_CHANGE: - break; + case KEY_PRESSURE: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } - case CHANNEL_PRESSURE: - break; + break; - case PITCH_BEND: - if ((param2 = fluid_midi_file_getc(mf)) < 0) { - FLUID_LOG(FLUID_ERR, "Unexpected end of file"); - return FLUID_FAILED; - } + case CONTROL_CHANGE: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); + return FLUID_FAILED; + } - param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f); - param2 = 0; - break; + break; + + case PROGRAM_CHANGE: + break; + + case CHANNEL_PRESSURE: + break; - default: - /* Can't possibly happen !? */ - FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event"); + case PITCH_BEND: + if((param2 = fluid_midi_file_getc(mf)) < 0) + { + FLUID_LOG(FLUID_ERR, "Unexpected end of file"); return FLUID_FAILED; + } + + param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f); + param2 = 0; + break; + + default: + /* Can't possibly happen !? */ + FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event"); + return FLUID_FAILED; } + evt = new_fluid_midi_event(); - if (evt == NULL) { + + if(evt == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } + evt->dtime = mf->dtime; evt->type = type; evt->channel = channel; @@ -756,6 +999,7 @@ fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track) fluid_track_add_event(track, evt); mf->dtime = 0; } + return FLUID_OK; } @@ -779,14 +1023,17 @@ fluid_midi_file_get_division(fluid_midi_file *midifile) * @return New MIDI event structure or NULL when out of memory. */ fluid_midi_event_t * -new_fluid_midi_event () +new_fluid_midi_event() { - fluid_midi_event_t* evt; + fluid_midi_event_t *evt; evt = FLUID_NEW(fluid_midi_event_t); - if (evt == NULL) { + + if(evt == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } + evt->dtime = 0; evt->type = 0; evt->channel = 0; @@ -800,24 +1047,27 @@ new_fluid_midi_event () /** * Delete MIDI event structure. * @param evt MIDI event structure - * @return Always returns #FLUID_OK */ -int +void delete_fluid_midi_event(fluid_midi_event_t *evt) { fluid_midi_event_t *temp; + fluid_return_if_fail(evt != NULL); - while (evt) { + while(evt) + { temp = evt->next; /* Dynamic SYSEX event? - free (param2 indicates if dynamic) */ - if (evt->type == MIDI_SYSEX && evt->paramptr && evt->param2) - FLUID_FREE (evt->paramptr); + if((evt->type == MIDI_SYSEX || (evt-> type == MIDI_TEXT) || (evt->type == MIDI_LYRIC)) && + evt->paramptr && evt->param2) + { + FLUID_FREE(evt->paramptr); + } FLUID_FREE(evt); evt = temp; } - return FLUID_OK; } /** @@ -1016,22 +1266,65 @@ fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val) * Assign sysex data to a MIDI event structure. * @param evt MIDI event structure * @param data Pointer to SYSEX data - * @param size Size of SYSEX data + * @param size Size of SYSEX data in bytes * @param dynamic TRUE if the SYSEX data has been dynamically allocated and * should be freed when the event is freed (only applies if event gets destroyed * with delete_fluid_midi_event()) * @return Always returns #FLUID_OK * - * NOTE: Unlike the other event assignment functions, this one sets evt->type. + * @note Unlike the other event assignment functions, this one sets evt->type. */ int fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic) { - evt->type = MIDI_SYSEX; + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_SYSEX, data, size, dynamic); + return FLUID_OK; +} + +/** + * Assign text data to a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to text data + * @param size Size of text data in bytes + * @param dynamic TRUE if the data has been dynamically allocated and + * should be freed when the event is freed via delete_fluid_midi_event() + * @return Always returns #FLUID_OK + * + * @since 2.0.0 + * @note Unlike the other event assignment functions, this one sets evt->type. + */ +int +fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic) +{ + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_TEXT, data, size, dynamic); + return FLUID_OK; +} + +/** + * Assign lyric data to a MIDI event structure. + * @param evt MIDI event structure + * @param data Pointer to lyric data + * @param size Size of lyric data in bytes + * @param dynamic TRUE if the data has been dynamically allocated and + * should be freed when the event is freed via delete_fluid_midi_event() + * @return Always returns #FLUID_OK + * + * @since 2.0.0 + * @note Unlike the other event assignment functions, this one sets evt->type. + */ +int +fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic) +{ + fluid_midi_event_set_sysex_LOCAL(evt, MIDI_LYRIC, data, size, dynamic); + return FLUID_OK; +} + +static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic) +{ + evt->type = type; evt->paramptr = data; evt->param1 = size; evt->param2 = dynamic; - return FLUID_OK; } /****************************************************** @@ -1047,9 +1340,12 @@ new_fluid_track(int num) { fluid_track_t *track; track = FLUID_NEW(fluid_track_t); - if (track == NULL) { + + if(track == NULL) + { return NULL; } + track->name = NULL; track->num = num; track->first = NULL; @@ -1062,17 +1358,14 @@ new_fluid_track(int num) /* * delete_fluid_track */ -int +void delete_fluid_track(fluid_track_t *track) { - if (track->name != NULL) { - FLUID_FREE(track->name); - } - if (track->first != NULL) { - delete_fluid_midi_event(track->first); - } + fluid_return_if_fail(track != NULL); + + FLUID_FREE(track->name); + delete_fluid_midi_event(track->first); FLUID_FREE(track); - return FLUID_OK; } /* @@ -1082,33 +1375,32 @@ int fluid_track_set_name(fluid_track_t *track, char *name) { int len; - if (track->name != NULL) { + + if(track->name != NULL) + { FLUID_FREE(track->name); } - if (name == NULL) { + + if(name == NULL) + { track->name = NULL; return FLUID_OK; } + len = FLUID_STRLEN(name); track->name = FLUID_MALLOC(len + 1); - if (track->name == NULL) { + + if(track->name == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return FLUID_FAILED; } + FLUID_STRCPY(track->name, name); return FLUID_OK; } /* - * fluid_track_get_name - */ -char * -fluid_track_get_name(fluid_track_t *track) -{ - return track->name; -} - -/* * fluid_track_get_duration */ int @@ -1116,29 +1408,14 @@ fluid_track_get_duration(fluid_track_t *track) { int time = 0; fluid_midi_event_t *evt = track->first; - while (evt != NULL) { + + while(evt != NULL) + { time += evt->dtime; evt = evt->next; } - return time; -} -/* - * fluid_track_count_events - */ -static int -fluid_track_count_events(fluid_track_t *track, int *on, int *off) -{ - fluid_midi_event_t *evt = track->first; - while (evt != NULL) { - if (evt->type == NOTE_ON) { - (*on)++; - } else if (evt->type == NOTE_OFF) { - (*off)++; - } - evt = evt->next; - } - return FLUID_OK; + return time; } /* @@ -1148,25 +1425,20 @@ int fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt) { evt->next = NULL; - if (track->first == NULL) { + + if(track->first == NULL) + { track->first = evt; track->cur = evt; track->last = evt; - } else { + } + else + { track->last->next = evt; track->last = evt; } - return FLUID_OK; -} -/* - * fluid_track_first_event - */ -fluid_midi_event_t * -fluid_track_first_event(fluid_track_t *track) -{ - track->cur = track->first; - return track->cur; + return FLUID_OK; } /* @@ -1175,9 +1447,11 @@ fluid_track_first_event(fluid_track_t *track) fluid_midi_event_t * fluid_track_next_event(fluid_track_t *track) { - if (track->cur != NULL) { + if(track->cur != NULL) + { track->cur = track->cur->next; } + return track->cur; } @@ -1197,17 +1471,31 @@ fluid_track_reset(fluid_track_t *track) */ int fluid_track_send_events(fluid_track_t *track, - fluid_synth_t *synth, - fluid_player_t *player, - unsigned int ticks) + fluid_synth_t *synth, + fluid_player_t *player, + unsigned int ticks) { int status = FLUID_OK; fluid_midi_event_t *event; + int seeking = player->seek_ticks >= 0; - while (1) { + if(seeking) + { + ticks = player->seek_ticks; /* update target ticks */ + + if(track->ticks > ticks) + { + fluid_track_reset(track); /* reset track if seeking backwards */ + } + } + + while(1) + { event = track->cur; - if (event == NULL) { + + if(event == NULL) + { return status; } @@ -1218,25 +1506,37 @@ fluid_track_send_events(fluid_track_t *track, /* event->dtime, */ /* track->ticks + event->dtime); */ - if (track->ticks + event->dtime > ticks) { + if(track->ticks + event->dtime > ticks) + { return status; } track->ticks += event->dtime; - if (!player || event->type == MIDI_EOT) { + if(!player || event->type == MIDI_EOT) + { } - else if (event->type == MIDI_SET_TEMPO) { - fluid_player_set_midi_tempo(player, event->param1); + else if(seeking && (event->type == NOTE_ON || event->type == NOTE_OFF)) + { + /* skip on/off messages */ } - else { - if (player->playback_callback) + else + { + if(player->playback_callback) + { player->playback_callback(player->playback_userdata, event); + } + } + + if(event->type == MIDI_SET_TEMPO) + { + fluid_player_set_midi_tempo(player, event->param1); } fluid_track_next_event(track); } + return status; } @@ -1244,6 +1544,14 @@ fluid_track_send_events(fluid_track_t *track, * * fluid_player */ +static void +fluid_player_handle_reset_synth(void *data, const char *name, int value) +{ + fluid_player_t *player = data; + fluid_return_if_fail(player != NULL); + + player->reset_synth_between_songs = value; +} /** * Create a new MIDI player. @@ -1256,16 +1564,22 @@ new_fluid_player(fluid_synth_t *synth) int i; fluid_player_t *player; player = FLUID_NEW(fluid_player_t); - if (player == NULL) { + + if(player == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } + player->status = FLUID_PLAYER_READY; player->loop = 1; player->ntracks = 0; - for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) { + + for(i = 0; i < MAX_NUMBER_OF_TRACKS; i++) + { player->track[i] = NULL; } + player->synth = synth; player->system_timer = NULL; player->sample_timer = NULL; @@ -1277,13 +1591,16 @@ new_fluid_player(fluid_synth_t *synth) player->deltatime = 4.0; player->cur_msec = 0; player->cur_ticks = 0; + player->seek_ticks = -1; fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth); - player->use_system_timer = fluid_settings_str_equal(synth->settings, - "player.timing-source", "system"); + "player.timing-source", "system"); fluid_settings_getint(synth->settings, "player.reset-synth", &i); - player->reset_synth_between_songs = i; + fluid_player_handle_reset_synth(player, NULL, i); + + fluid_settings_callback_int(synth->settings, "player.reset-synth", + fluid_player_handle_reset_synth, player); return player; } @@ -1291,23 +1608,22 @@ new_fluid_player(fluid_synth_t *synth) /** * Delete a MIDI player instance. * @param player MIDI player instance - * @return Always returns #FLUID_OK */ -int +void delete_fluid_player(fluid_player_t *player) { fluid_list_t *q; - fluid_playlist_item* pi; + fluid_playlist_item *pi; + + fluid_return_if_fail(player != NULL); - if (player == NULL) { - return FLUID_OK; - } fluid_player_stop(player); fluid_player_reset(player); - while (player->playlist != NULL) { + while(player->playlist != NULL) + { q = player->playlist->next; - pi = (fluid_playlist_item*) player->playlist->data; + pi = (fluid_playlist_item *) player->playlist->data; FLUID_FREE(pi->filename); FLUID_FREE(pi->buffer); FLUID_FREE(pi); @@ -1316,7 +1632,6 @@ delete_fluid_player(fluid_player_t *player) } FLUID_FREE(player); - return FLUID_OK; } /** @@ -1327,14 +1642,12 @@ fluid_player_settings(fluid_settings_t *settings) { /* player.timing-source can be either "system" (use system timer) or "sample" (use timer based on number of written samples) */ - fluid_settings_register_str(settings, "player.timing-source", "sample", 0, - NULL, NULL); + fluid_settings_register_str(settings, "player.timing-source", "sample", 0); fluid_settings_add_option(settings, "player.timing-source", "sample"); fluid_settings_add_option(settings, "player.timing-source", "system"); /* Selects whether the player should reset the synth between songs, or not. */ - fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1, - FLUID_HINT_TOGGLED, NULL, NULL); + fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1, FLUID_HINT_TOGGLED); } @@ -1343,12 +1656,15 @@ fluid_player_reset(fluid_player_t *player) { int i; - for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) { - if (player->track[i] != NULL) { + for(i = 0; i < MAX_NUMBER_OF_TRACKS; i++) + { + if(player->track[i] != NULL) + { delete_fluid_track(player->track[i]); player->track[i] = NULL; } } + /* player->current_file = NULL; */ /* player->status = FLUID_PLAYER_READY; */ /* player->loop = 1; */ @@ -1366,52 +1682,33 @@ fluid_player_reset(fluid_player_t *player) int fluid_player_add_track(fluid_player_t *player, fluid_track_t *track) { - if (player->ntracks < MAX_NUMBER_OF_TRACKS) { + if(player->ntracks < MAX_NUMBER_OF_TRACKS) + { player->track[player->ntracks++] = track; return FLUID_OK; - } else { - return FLUID_FAILED; } -} - -/* - * fluid_player_count_tracks - */ -int -fluid_player_count_tracks(fluid_player_t *player) -{ - return player->ntracks; -} - -/* - * fluid_player_get_track - */ -fluid_track_t * -fluid_player_get_track(fluid_player_t *player, int i) -{ - if ((i >= 0) && (i < MAX_NUMBER_OF_TRACKS)) { - return player->track[i]; - } else { - return NULL; + else + { + return FLUID_FAILED; } } /** - * Change the MIDI callback function. This is usually set to + * Change the MIDI callback function. This is usually set to * fluid_synth_handle_midi_event, but can optionally be changed * to a user-defined function instead, for intercepting all MIDI - * messages sent to the synth. You can also use a midi router as + * messages sent to the synth. You can also use a midi router as * the callback function to modify the MIDI messages before sending - * them to the synth. + * them to the synth. * @param player MIDI player instance * @param handler Pointer to callback function * @param handler_data Parameter sent to the callback function * @returns FLUID_OK * @since 1.1.4 */ -int -fluid_player_set_playback_callback(fluid_player_t* player, - handle_midi_event_func_t handler, void* handler_data) +int +fluid_player_set_playback_callback(fluid_player_t *player, + handle_midi_event_func_t handler, void *handler_data) { player->playback_callback = handler; player->playback_userdata = handler_data; @@ -1428,8 +1725,10 @@ int fluid_player_add(fluid_player_t *player, const char *midifile) { fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item)); - char* f = FLUID_STRDUP(midifile); - if (!pi || !f) { + char *f = FLUID_STRDUP(midifile); + + if(!pi || !f) + { FLUID_FREE(pi); FLUID_FREE(f); FLUID_LOG(FLUID_PANIC, "Out of memory"); @@ -1453,12 +1752,14 @@ fluid_player_add(fluid_player_t *player, const char *midifile) * @return #FLUID_OK or #FLUID_FAILED */ int -fluid_player_add_mem(fluid_player_t* player, const void *buffer, size_t len) +fluid_player_add_mem(fluid_player_t *player, const void *buffer, size_t len) { /* Take a copy of the buffer, so the caller can free immediately. */ fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item)); void *buf_copy = FLUID_MALLOC(len); - if (!pi || !buf_copy) { + + if(!pi || !buf_copy) + { FLUID_FREE(pi); FLUID_FREE(buf_copy); FLUID_LOG(FLUID_PANIC, "Out of memory"); @@ -1480,28 +1781,33 @@ int fluid_player_load(fluid_player_t *player, fluid_playlist_item *item) { fluid_midi_file *midifile; - char* buffer; + char *buffer; size_t buffer_length; int buffer_owned; - if (item->filename != NULL) + if(item->filename != NULL) { fluid_file fp; /* This file is specified by filename; load the file from disk */ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__, - item->filename); + item->filename); /* Read the entire contents of the file into the buffer */ fp = FLUID_FOPEN(item->filename, "rb"); - if (fp == NULL) { + + if(fp == NULL) + { FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file"); return FLUID_FAILED; } + buffer = fluid_file_read_full(fp, &buffer_length); - if (buffer == NULL) + + if(buffer == NULL) { FLUID_FCLOSE(fp); return FLUID_FAILED; } + buffer_owned = 1; FLUID_FCLOSE(fp); } @@ -1509,7 +1815,7 @@ fluid_player_load(fluid_player_t *player, fluid_playlist_item *item) { /* This file is specified by a pre-loaded buffer; load from memory */ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile from memory (%p)", - __FILE__, __LINE__, item->buffer); + __FILE__, __LINE__, item->buffer); buffer = (char *) item->buffer; buffer_length = item->buffer_len; /* Do not free the buffer (it is owned by the playlist) */ @@ -1517,59 +1823,83 @@ fluid_player_load(fluid_player_t *player, fluid_playlist_item *item) } midifile = new_fluid_midi_file(buffer, buffer_length); - if (midifile == NULL) { - if (buffer_owned) { + + if(midifile == NULL) + { + if(buffer_owned) + { FLUID_FREE(buffer); } + return FLUID_FAILED; } + player->division = fluid_midi_file_get_division(midifile); fluid_player_set_midi_tempo(player, player->miditempo); // Update deltatime /*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */ - if (fluid_midi_file_load_tracks(midifile, player) != FLUID_OK) { - if (buffer_owned) { + if(fluid_midi_file_load_tracks(midifile, player) != FLUID_OK) + { + if(buffer_owned) + { FLUID_FREE(buffer); } + delete_fluid_midi_file(midifile); return FLUID_FAILED; } + delete_fluid_midi_file(midifile); - if (buffer_owned) { + + if(buffer_owned) + { FLUID_FREE(buffer); } + return FLUID_OK; } -static void +void fluid_player_advancefile(fluid_player_t *player) { - if (player->playlist == NULL) { + if(player->playlist == NULL) + { return; /* No files to play */ } - if (player->currentfile != NULL) { + + if(player->currentfile != NULL) + { player->currentfile = fluid_list_next(player->currentfile); } - if (player->currentfile == NULL) { - if (player->loop == 0) { + + if(player->currentfile == NULL) + { + if(player->loop == 0) + { return; /* We're done playing */ } - if (player->loop > 0) { + + if(player->loop > 0) + { player->loop--; } + player->currentfile = player->playlist; } } -static void +void fluid_player_playlist_load(fluid_player_t *player, unsigned int msec) { - fluid_playlist_item* current_playitem; + fluid_playlist_item *current_playitem; int i; - do { + do + { fluid_player_advancefile(player); - if (player->currentfile == NULL) { + + if(player->currentfile == NULL) + { /* Failed to find next song, probably since we're finished */ player->status = FLUID_PLAYER_DONE; return; @@ -1577,7 +1907,8 @@ fluid_player_playlist_load(fluid_player_t *player, unsigned int msec) fluid_player_reset(player); current_playitem = (fluid_playlist_item *) player->currentfile->data; - } while (fluid_player_load(player, current_playitem) != FLUID_OK); + } + while(fluid_player_load(player, current_playitem) != FLUID_OK); /* Successfully loaded midi file */ @@ -1586,18 +1917,20 @@ fluid_player_playlist_load(fluid_player_t *player, unsigned int msec) player->start_ticks = 0; player->cur_ticks = 0; - if (player->reset_synth_between_songs) { + if(player->reset_synth_between_songs) + { fluid_synth_system_reset(player->synth); } - for (i = 0; i < player->ntracks; i++) { - if (player->track[i] != NULL) { + for(i = 0; i < player->ntracks; i++) + { + if(player->track[i] != NULL) + { fluid_track_reset(player->track[i]); } } } - /* * fluid_player_callback */ @@ -1613,36 +1946,61 @@ fluid_player_callback(void *data, unsigned int msec) synth = player->synth; loadnextfile = player->currentfile == NULL ? 1 : 0; - do { - if (loadnextfile) { + + do + { + if(loadnextfile) + { loadnextfile = 0; fluid_player_playlist_load(player, msec); - if (player->currentfile == NULL) { + + if(player->currentfile == NULL) + { return 0; } } player->cur_msec = msec; player->cur_ticks = (player->start_ticks - + (int) ((double) (player->cur_msec - player->start_msec) - / player->deltatime)); + + (int)((double)(player->cur_msec - player->start_msec) + / player->deltatime + 0.5)); /* 0.5 to average overall error when casting */ + + if(player->seek_ticks >= 0) + { + fluid_synth_all_sounds_off(synth, -1); /* avoid hanging notes */ + } - for (i = 0; i < player->ntracks; i++) { - if (!fluid_track_eot(player->track[i])) { + for(i = 0; i < player->ntracks; i++) + { + if(!fluid_track_eot(player->track[i])) + { status = FLUID_PLAYER_PLAYING; - if (fluid_track_send_events(player->track[i], synth, player, - player->cur_ticks) != FLUID_OK) { + + if(fluid_track_send_events(player->track[i], synth, player, + player->cur_ticks) != FLUID_OK) + { /* */ } } } - if (status == FLUID_PLAYER_DONE) { + if(player->seek_ticks >= 0) + { + player->start_ticks = player->seek_ticks; /* tick position of last tempo value (which is now) */ + player->cur_ticks = player->seek_ticks; + player->begin_msec = msec; /* only used to calculate the duration of playing */ + player->start_msec = msec; /* should be the (synth)-time of the last tempo change */ + player->seek_ticks = -1; /* clear seek_ticks */ + } + + if(status == FLUID_PLAYER_DONE) + { FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__, - __LINE__, (msec - player->begin_msec) / 1000.0); + __LINE__, (msec - player->begin_msec) / 1000.0); loadnextfile = 1; } - } while (loadnextfile); + } + while(loadnextfile); player->status = status; @@ -1657,30 +2015,39 @@ fluid_player_callback(void *data, unsigned int msec) int fluid_player_play(fluid_player_t *player) { - if (player->status == FLUID_PLAYER_PLAYING) { + if(player->status == FLUID_PLAYER_PLAYING) + { return FLUID_OK; } - if (player->playlist == NULL) { + if(player->playlist == NULL) + { return FLUID_OK; } player->status = FLUID_PLAYER_PLAYING; - if (player->use_system_timer) { + if(player->use_system_timer) + { player->system_timer = new_fluid_timer((int) player->deltatime, - fluid_player_callback, (void *) player, TRUE, FALSE, TRUE); - if (player->system_timer == NULL) { + fluid_player_callback, (void *) player, TRUE, FALSE, TRUE); + + if(player->system_timer == NULL) + { return FLUID_FAILED; } - } else { + } + else + { player->sample_timer = new_fluid_sample_timer(player->synth, - fluid_player_callback, (void *) player); + fluid_player_callback, (void *) player); - if (player->sample_timer == NULL) { + if(player->sample_timer == NULL) + { return FLUID_FAILED; } } + return FLUID_OK; } @@ -1692,12 +2059,16 @@ fluid_player_play(fluid_player_t *player) int fluid_player_stop(fluid_player_t *player) { - if (player->system_timer != NULL) { + if(player->system_timer != NULL) + { delete_fluid_timer(player->system_timer); } - if (player->sample_timer != NULL) { + + if(player->sample_timer != NULL) + { delete_fluid_sample_timer(player->synth, player->sample_timer); } + player->status = FLUID_PLAYER_DONE; player->sample_timer = NULL; player->system_timer = NULL; @@ -1717,13 +2088,35 @@ fluid_player_get_status(fluid_player_t *player) } /** - * Enable looping of a MIDI player + * Seek in the currently playing file. + * @param player MIDI player instance + * @param ticks the position to seek to in the current file + * @return #FLUID_FAILED if ticks is negative or after the latest tick of the file, + * #FLUID_OK otherwise + * @since 2.0.0 + * + * The actual seek is performed during the player_callback. + */ +int fluid_player_seek(fluid_player_t *player, int ticks) +{ + if(ticks < 0 || ticks > fluid_player_get_total_ticks(player)) + { + return FLUID_FAILED; + } + + player->seek_ticks = ticks; + return FLUID_OK; +} + + +/** + * Enable looping of a MIDI player * @param player MIDI player instance * @param loop Times left to loop the playlist. -1 means loop infinitely. * @return Always returns #FLUID_OK * @since 1.1.0 * - * For example, if you want to loop the playlist twice, set loop to 2 + * For example, if you want to loop the playlist twice, set loop to 2 * and call this function before you start the player. */ int fluid_player_set_loop(fluid_player_t *player, int loop) @@ -1746,8 +2139,8 @@ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) player->start_ticks = player->cur_ticks; FLUID_LOG(FLUID_DBG, - "tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d", - tempo, player->deltatime, player->cur_msec, player->cur_ticks); + "tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d", + tempo, player->deltatime, player->cur_msec, player->cur_ticks); return FLUID_OK; } @@ -1758,10 +2151,9 @@ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo) * @param bpm Tempo in beats per minute * @return Always returns #FLUID_OK */ -int -fluid_player_set_bpm(fluid_player_t *player, int bpm) +int fluid_player_set_bpm(fluid_player_t *player, int bpm) { - return fluid_player_set_midi_tempo(player, (int) ((double) 60 * 1e6 / bpm)); + return fluid_player_set_midi_tempo(player, (int)((double) 60 * 1e6 / bpm)); } /** @@ -1772,21 +2164,82 @@ fluid_player_set_bpm(fluid_player_t *player, int bpm) int fluid_player_join(fluid_player_t *player) { - if (player->system_timer) { + if(player->system_timer) + { return fluid_timer_join(player->system_timer); - } else if (player->sample_timer) { + } + else if(player->sample_timer) + { /* Busy-wait loop, since there's no thread to wait for... */ - while (player->status != FLUID_PLAYER_DONE) { -#if defined(WIN32) - Sleep(10); -#else - usleep(10000); -#endif + while(player->status != FLUID_PLAYER_DONE) + { + fluid_msleep(10); } } + return FLUID_OK; } +/** + * Get the number of tempo ticks passed. + * @param player MIDI player instance + * @return The number of tempo ticks passed + * @since 1.1.7 + */ +int fluid_player_get_current_tick(fluid_player_t *player) +{ + return player->cur_ticks; +} + +/** + * Looks through all available MIDI tracks and gets the absolute tick of the very last event to play. + * @param player MIDI player instance + * @return Total tick count of the sequence + * @since 1.1.7 + */ +int fluid_player_get_total_ticks(fluid_player_t *player) +{ + int i; + int maxTicks = 0; + + for(i = 0; i < player->ntracks; i++) + { + if(player->track[i] != NULL) + { + int ticks = fluid_track_get_duration(player->track[i]); + + if(ticks > maxTicks) + { + maxTicks = ticks; + } + } + } + + return maxTicks; +} + +/** + * Get the tempo of a MIDI player in beats per minute. + * @param player MIDI player instance + * @return MIDI player tempo in BPM + * @since 1.1.7 + */ +int fluid_player_get_bpm(fluid_player_t *player) +{ + return (int)(60e6 / player->miditempo); +} + +/** + * Get the tempo of a MIDI player. + * @param player MIDI player instance + * @return Tempo of the MIDI player (in microseconds per quarter note, as per MIDI file spec) + * @since 1.1.7 + */ +int fluid_player_get_midi_tempo(fluid_player_t *player) +{ + return player->miditempo; +} + /************************************************************************ * MIDI PARSER * @@ -1796,14 +2249,17 @@ fluid_player_join(fluid_player_t *player) * new_fluid_midi_parser */ fluid_midi_parser_t * -new_fluid_midi_parser () +new_fluid_midi_parser() { fluid_midi_parser_t *parser; parser = FLUID_NEW(fluid_midi_parser_t); - if (parser == NULL) { + + if(parser == NULL) + { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } + parser->status = 0; /* As long as the status is 0, the parser won't do anything -> no need to initialize all the fields. */ return parser; } @@ -1811,11 +2267,12 @@ new_fluid_midi_parser () /* * delete_fluid_midi_parser */ -int +void delete_fluid_midi_parser(fluid_midi_parser_t *parser) { + fluid_return_if_fail(parser != NULL); + FLUID_FREE(parser); - return FLUID_OK; } /** @@ -1824,6 +2281,11 @@ delete_fluid_midi_parser(fluid_midi_parser_t *parser) * @param c Next character in MIDI stream * @return A parsed MIDI event or NULL if none. Event is internal and should * not be modified or freed and is only valid until next call to this function. + * @internal Do not expose this function to the public API. It would allow downstream + * apps to abuse fluidsynth as midi parser, e.g. feeding it with rawmidi and pull out + * the needed midi information using the getter functions of fluid_midi_event_t. + * This parser however is incomplete as it e.g. only provides a limited buffer to + * store and process SYSEX data (i.e. doesnt allow arbitrary lengths) */ fluid_midi_event_t * fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) @@ -1832,8 +2294,10 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) /* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle * of another message. */ - if (c >= 0xF8) { - if (c == MIDI_SYSTEM_RESET) { + if(c >= 0xF8) + { + if(c == MIDI_SYSTEM_RESET) + { parser->event.type = c; parser->status = 0; /* clear the status */ return &parser->event; @@ -1843,30 +2307,40 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) } /* Status byte? - If previous message not yet complete, it is discarded (re-sync). */ - if (c & 0x80) { + if(c & 0x80) + { /* Any status byte terminates SYSEX messages (not just 0xF7) */ - if (parser->status == MIDI_SYSEX && parser->nr_bytes > 0) { + if(parser->status == MIDI_SYSEX && parser->nr_bytes > 0) + { event = &parser->event; fluid_midi_event_set_sysex(event, parser->data, parser->nr_bytes, - FALSE); - } else + FALSE); + } + else + { event = NULL; + } - if (c < 0xF0) /* Voice category message? */ + if(c < 0xF0) /* Voice category message? */ { parser->channel = c & 0x0F; parser->status = c & 0xF0; /* The event consumes x bytes of data... (subtract 1 for the status byte) */ parser->nr_bytes_total = fluid_midi_event_length(parser->status) - - 1; + - 1; parser->nr_bytes = 0; /* 0 bytes read so far */ - } else if (c == MIDI_SYSEX) { + } + else if(c == MIDI_SYSEX) + { parser->status = MIDI_SYSEX; parser->nr_bytes = 0; - } else - parser->status = 0; /* Discard other system messages (0xF1-0xF7) */ + } + else + { + parser->status = 0; /* Discard other system messages (0xF1-0xF7) */ + } return event; /* Return SYSEX event or NULL */ } @@ -1874,11 +2348,14 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) /* Data/parameter byte */ /* Discard data bytes for events we don't care about */ - if (parser->status == 0) + if(parser->status == 0) + { return NULL; + } /* Max data size exceeded? (SYSEX messages only really) */ - if (parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE) { + if(parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE) + { parser->status = 0; /* Discard the rest of the message */ return NULL; } @@ -1887,8 +2364,10 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) parser->data[parser->nr_bytes++] = c; /* Do we still need more data to get this event complete? */ - if (parser->nr_bytes < parser->nr_bytes_total) + if(parser->status == MIDI_SYSEX || parser->nr_bytes < parser->nr_bytes_total) + { return NULL; + } /* Event is complete, return it. * Running status byte MIDI feature is also handled here. */ @@ -1896,22 +2375,25 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) parser->event.channel = parser->channel; parser->nr_bytes = 0; /* Reset data size, in case there are additional running status messages */ - switch (parser->status) { - case NOTE_OFF: - case NOTE_ON: - case KEY_PRESSURE: - case CONTROL_CHANGE: - case PROGRAM_CHANGE: - case CHANNEL_PRESSURE: - parser->event.param1 = parser->data[0]; /* For example key number */ - parser->event.param2 = parser->data[1]; /* For example velocity */ - break; - case PITCH_BEND: - /* Pitch-bend is transmitted with 14-bit precision. */ - parser->event.param1 = (parser->data[1] << 7) | parser->data[0]; - break; - default: /* Unlikely */ - return NULL; + switch(parser->status) + { + case NOTE_OFF: + case NOTE_ON: + case KEY_PRESSURE: + case CONTROL_CHANGE: + case PROGRAM_CHANGE: + case CHANNEL_PRESSURE: + parser->event.param1 = parser->data[0]; /* For example key number */ + parser->event.param2 = parser->data[1]; /* For example velocity */ + break; + + case PITCH_BEND: + /* Pitch-bend is transmitted with 14-bit precision. */ + parser->event.param1 = (parser->data[1] << 7) | parser->data[0]; + break; + + default: /* Unlikely */ + return NULL; } return &parser->event; @@ -1922,28 +2404,35 @@ fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c) static int fluid_midi_event_length(unsigned char event) { - switch (event & 0xF0) { - case NOTE_OFF: - case NOTE_ON: - case KEY_PRESSURE: - case CONTROL_CHANGE: - case PITCH_BEND: - return 3; - case PROGRAM_CHANGE: - case CHANNEL_PRESSURE: - return 2; - } - switch (event) { - case MIDI_TIME_CODE: - case MIDI_SONG_SELECT: - case 0xF4: - case 0xF5: - return 2; - case MIDI_TUNE_REQUEST: - return 1; - case MIDI_SONG_POSITION: - return 3; + switch(event & 0xF0) + { + case NOTE_OFF: + case NOTE_ON: + case KEY_PRESSURE: + case CONTROL_CHANGE: + case PITCH_BEND: + return 3; + + case PROGRAM_CHANGE: + case CHANNEL_PRESSURE: + return 2; + } + + switch(event) + { + case MIDI_TIME_CODE: + case MIDI_SONG_SELECT: + case 0xF4: + case 0xF5: + return 2; + + case MIDI_TUNE_REQUEST: + return 1; + + case MIDI_SONG_POSITION: + return 3; } + return 1; } #endif |