Path: blob/master/libs/fluidsynth/src/midi/fluid_midi.c
4396 views
/* FluidSynth - A Software Synthesizer1*2* Copyright (C) 2003 Peter Hanappe and others.3*4* This library is free software; you can redistribute it and/or5* modify it under the terms of the GNU Lesser General Public License6* as published by the Free Software Foundation; either version 2.1 of7* the License, or (at your option) any later version.8*9* This library is distributed in the hope that it will be useful, but10* WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12* Lesser General Public License for more details.13*14* You should have received a copy of the GNU Lesser General Public15* License along with this library; if not, write to the Free16* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA17* 02110-1301, USA18*/1920#include "fluid_midi.h"21#include "fluid_sys.h"22#include "fluid_synth.h"23#include "fluid_settings.h"242526static int fluid_midi_event_length(unsigned char event);27static int fluid_isasciistring(char *s);28static long fluid_getlength(const unsigned char *s);293031/* Read the entire contents of a file into memory, allocating enough memory32* for the file, and returning the length and the buffer.33* Note: This rewinds the file to the start before reading.34* Returns NULL if there was an error reading or allocating memory.35*/36typedef FILE *fluid_file;37static char *fluid_file_read_full(fluid_file fp, size_t *length);38static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic);39static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size);40#define READ_FULL_INITIAL_BUFLEN 10244142static fluid_track_t *new_fluid_track(int num);43static void delete_fluid_track(fluid_track_t *track);44static int fluid_track_set_name(fluid_track_t *track, char *name);45static int fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt);46static fluid_midi_event_t *fluid_track_next_event(fluid_track_t *track);47static int fluid_track_get_duration(fluid_track_t *track);48static int fluid_track_reset(fluid_track_t *track);4950static int fluid_player_add_track(fluid_player_t *player, fluid_track_t *track);51static int fluid_player_callback(void *data, unsigned int msec);52static int fluid_player_reset(fluid_player_t *player);53static int fluid_player_load(fluid_player_t *player, fluid_playlist_item *item);54static void fluid_player_advancefile(fluid_player_t *player);55static void fluid_player_playlist_load(fluid_player_t *player, unsigned int msec);56static void fluid_player_update_tempo(fluid_player_t *player);5758static fluid_midi_file *new_fluid_midi_file(const char *buffer, size_t length);59static void delete_fluid_midi_file(fluid_midi_file *mf);60static int fluid_midi_file_read_mthd(fluid_midi_file *midifile);61static int fluid_midi_file_load_tracks(fluid_midi_file *midifile, fluid_player_t *player);62static int fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num);63static int fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track);64static int fluid_midi_file_read_varlen(fluid_midi_file *mf);65static int fluid_midi_file_getc(fluid_midi_file *mf);66static int fluid_midi_file_push(fluid_midi_file *mf, int c);67static int fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len);68static int fluid_midi_file_skip(fluid_midi_file *mf, int len);69static int fluid_midi_file_eof(fluid_midi_file *mf);70static int fluid_midi_file_read_tracklen(fluid_midi_file *mf);71static int fluid_midi_file_eot(fluid_midi_file *mf);72static int fluid_midi_file_get_division(fluid_midi_file *midifile);737475/***************************************************************76*77* MIDIFILE78*/7980/**81* Check if a file is a MIDI file.82* @param filename Path to the file to check83* @return TRUE if it could be a MIDI file, FALSE otherwise84*85* The current implementation only checks for the "MThd" header in the file.86* It is useful only to distinguish between SoundFont and MIDI files.87*/88int fluid_is_midifile(const char *filename)89{90FILE *fp;91uint32_t id;92int retcode = FALSE;9394do95{96if((fp = fluid_file_open(filename, NULL)) == NULL)97{98return retcode;99}100101if(FLUID_FREAD(&id, sizeof(id), 1, fp) != 1)102{103break;104}105106retcode = (id == FLUID_FOURCC('M', 'T', 'h', 'd'));107}108while(0);109110FLUID_FCLOSE(fp);111112return retcode;113}114115/**116* Return a new MIDI file handle for parsing an already-loaded MIDI file.117* @internal118* @param buffer Pointer to full contents of MIDI file (borrows the pointer).119* The caller must not free buffer until after the fluid_midi_file is deleted.120* @param length Size of the buffer in bytes.121* @return New MIDI file handle or NULL on error.122*/123fluid_midi_file *124new_fluid_midi_file(const char *buffer, size_t length)125{126fluid_midi_file *mf;127128if(length > INT_MAX)129{130FLUID_LOG(FLUID_ERR, "Refusing to open a MIDI file which is bigger than 2GiB");131return NULL;132}133134mf = FLUID_NEW(fluid_midi_file);135if(mf == NULL)136{137FLUID_LOG(FLUID_ERR, "Out of memory");138return NULL;139}140141FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file));142143mf->c = -1;144mf->running_status = 0;145146mf->buffer = buffer;147mf->buf_len = (int)length;148mf->buf_pos = 0;149mf->eof = FALSE;150151if(fluid_midi_file_read_mthd(mf) != FLUID_OK)152{153FLUID_FREE(mf);154return NULL;155}156157return mf;158}159160static char *161fluid_file_read_full(fluid_file fp, size_t *length)162{163size_t buflen;164char *buffer;165size_t n;166167/* Work out the length of the file in advance */168if(FLUID_FSEEK(fp, 0, SEEK_END) != 0)169{170FLUID_LOG(FLUID_ERR, "File load: Could not seek within file");171return NULL;172}173174buflen = ftell(fp);175176if(FLUID_FSEEK(fp, 0, SEEK_SET) != 0)177{178FLUID_LOG(FLUID_ERR, "File load: Could not seek within file");179return NULL;180}181182FLUID_LOG(FLUID_DBG, "File load: Allocating %lu bytes", (unsigned long)buflen);183buffer = FLUID_MALLOC(buflen);184185if(buffer == NULL)186{187FLUID_LOG(FLUID_PANIC, "Out of memory");188return NULL;189}190191n = FLUID_FREAD(buffer, 1, buflen, fp);192193if(n != buflen)194{195FLUID_LOG(FLUID_ERR, "Only read %lu bytes; expected %lu", (unsigned long)n,196(unsigned long)buflen);197FLUID_FREE(buffer);198return NULL;199};200201*length = n;202203return buffer;204}205206/**207* Delete a MIDI file handle.208* @internal209* @param mf MIDI file handle to close and free.210*/211void212delete_fluid_midi_file(fluid_midi_file *mf)213{214fluid_return_if_fail(mf != NULL);215216FLUID_FREE(mf);217}218219/*220* Gets the next byte in a MIDI file, taking into account previous running status.221*222* returns -1 if EOF or read error223*/224int225fluid_midi_file_getc(fluid_midi_file *mf)226{227unsigned char c;228229if(mf->c >= 0)230{231c = mf->c;232mf->c = -1;233}234else235{236if(mf->buf_pos >= mf->buf_len)237{238mf->eof = TRUE;239return -1;240}241242c = mf->buffer[mf->buf_pos++];243mf->trackpos++;244}245246return (int) c;247}248249/*250* Saves a byte to be returned the next time fluid_midi_file_getc() is called,251* when it is necessary according to running status.252*/253int254fluid_midi_file_push(fluid_midi_file *mf, int c)255{256mf->c = c;257return FLUID_OK;258}259260/*261* fluid_midi_file_read262*/263int264fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len)265{266int num = len < mf->buf_len - mf->buf_pos267? len : mf->buf_len - mf->buf_pos;268269if(num != len)270{271mf->eof = TRUE;272}273274if(num < 0)275{276num = 0;277}278279/* Note: Read bytes, even if there aren't enough, but only increment280* trackpos if successful (emulates old behaviour of fluid_midi_file_read)281*/282FLUID_MEMCPY(buf, mf->buffer + mf->buf_pos, num);283mf->buf_pos += num;284285if(num == len)286{287mf->trackpos += num;288}289290#if DEBUG291else292{293FLUID_LOG(FLUID_DBG, "Could not read the requested number of bytes");294}295296#endif297return (num != len) ? FLUID_FAILED : FLUID_OK;298}299300/*301* fluid_midi_file_skip302*/303int304fluid_midi_file_skip(fluid_midi_file *mf, int skip)305{306int new_pos = mf->buf_pos + skip;307308/* Mimic the behaviour of fseek: Error to seek past the start of file, but309* OK to seek past end (this just puts it into the EOF state). */310if(new_pos < 0)311{312FLUID_LOG(FLUID_ERR, "Failed to seek position in file");313return FLUID_FAILED;314}315316/* Clear the EOF flag, even if moved past the end of the file (this is317* consistent with the behaviour of fseek). */318mf->eof = FALSE;319mf->buf_pos = new_pos;320return FLUID_OK;321}322323/*324* fluid_midi_file_eof325*/326int fluid_midi_file_eof(fluid_midi_file *mf)327{328/* Note: This does not simply test whether the file read pointer is past329* the end of the file. It mimics the behaviour of feof by actually330* testing the stateful EOF condition, which is set to TRUE if getc or331* fread have attempted to read past the end (but not if they have332* precisely reached the end), but reset to FALSE upon a successful seek.333*/334return mf->eof;335}336337/*338* fluid_midi_file_read_mthd339*/340int341fluid_midi_file_read_mthd(fluid_midi_file *mf)342{343char mthd[14];344345if(fluid_midi_file_read(mf, mthd, sizeof(mthd)) != FLUID_OK)346{347return FLUID_FAILED;348}349350if((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6)351|| (mthd[9] > 2))352{353FLUID_LOG(FLUID_ERR,354"Doesn't look like a MIDI file: invalid MThd header");355return FLUID_FAILED;356}357358mf->type = mthd[9];359if(!(mf->type == 0 || mf->type == 1))360{361FLUID_LOG(FLUID_ERR,362"Sorry, but MIDI Format %d is not supported by this player", mf->type);363return FLUID_FAILED;364}365mf->ntracks = (signed)((unsigned)(mthd[10]) << 8 | (unsigned) mthd[11]);366367if((signed char)mthd[12] < 0)368{369mf->uses_smpte = 1;370mf->smpte_fps = -(signed char)mthd[12];371mf->smpte_res = (unsigned) mthd[13];372FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet");373return FLUID_FAILED;374}375else376{377mf->uses_smpte = 0;378mf->division = ((unsigned)mthd[12] << 8) | ((unsigned)mthd[13] & 0xff);379FLUID_LOG(FLUID_DBG, "Division=%d", mf->division);380}381382return FLUID_OK;383}384385/*386* fluid_midi_file_load_tracks387*/388int389fluid_midi_file_load_tracks(fluid_midi_file *mf, fluid_player_t *player)390{391int i;392393for(i = 0; i < mf->ntracks; i++)394{395if(fluid_midi_file_read_track(mf, player, i) != FLUID_OK)396{397return FLUID_FAILED;398}399}400401return FLUID_OK;402}403404/*405* fluid_isasciistring406*/407int408fluid_isasciistring(char *s)409{410/* From ctype.h */411#define fluid_isascii(c) (((c) & ~0x7f) == 0)412413size_t i, len = FLUID_STRLEN(s);414415for(i = 0; i < len; i++)416{417if(!fluid_isascii(s[i]))418{419return 0;420}421}422423return 1;424425#undef fluid_isascii426}427428/*429* fluid_getlength430*/431long432fluid_getlength(const unsigned char *s)433{434long i = 0;435i = s[3] | (s[2] << 8) | (s[1] << 16) | (s[0] << 24);436return i;437}438439/*440* fluid_midi_file_read_tracklen441*/442int443fluid_midi_file_read_tracklen(fluid_midi_file *mf)444{445unsigned char length[5];446447if(fluid_midi_file_read(mf, length, 4) != FLUID_OK)448{449return FLUID_FAILED;450}451452mf->tracklen = fluid_getlength(length);453mf->trackpos = 0;454mf->eot = 0;455return FLUID_OK;456}457458/*459* fluid_midi_file_eot460*/461int462fluid_midi_file_eot(fluid_midi_file *mf)463{464#if DEBUG465466if(mf->trackpos > mf->tracklen)467{468printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen);469}470471#endif472return mf->eot || (mf->trackpos >= mf->tracklen);473}474475/*476* fluid_midi_file_read_track477*/478int479fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num)480{481fluid_track_t *track;482unsigned char id[5], length[5];483int found_track = 0;484int skip;485486if(fluid_midi_file_read(mf, id, 4) != FLUID_OK)487{488return FLUID_FAILED;489}490491id[4] = '\0';492mf->dtime = 0;493494while(!found_track)495{496497if(fluid_isasciistring((char *) id) == 0)498{499FLUID_LOG(FLUID_ERR,500"A non-ascii track header found, corrupt file");501return FLUID_FAILED;502503}504else if(FLUID_STRCMP((char *) id, "MTrk") == 0)505{506507found_track = 1;508509if(fluid_midi_file_read_tracklen(mf) != FLUID_OK)510{511return FLUID_FAILED;512}513514track = new_fluid_track(num);515516if(track == NULL)517{518FLUID_LOG(FLUID_ERR, "Out of memory");519return FLUID_FAILED;520}521522while(!fluid_midi_file_eot(mf))523{524if(fluid_midi_file_read_event(mf, track) != FLUID_OK)525{526delete_fluid_track(track);527return FLUID_FAILED;528}529}530531/* Skip remaining track data, if any */532if(mf->trackpos < mf->tracklen)533{534if(fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos) != FLUID_OK)535{536delete_fluid_track(track);537return FLUID_FAILED;538}539}540541if(fluid_player_add_track(player, track) != FLUID_OK)542{543delete_fluid_track(track);544return FLUID_FAILED;545}546547}548else549{550found_track = 0;551552if(fluid_midi_file_read(mf, length, 4) != FLUID_OK)553{554return FLUID_FAILED;555}556557skip = fluid_getlength(length);558559/* fseek(mf->fp, skip, SEEK_CUR); */560if(fluid_midi_file_skip(mf, skip) != FLUID_OK)561{562return FLUID_FAILED;563}564}565}566567if(fluid_midi_file_eof(mf))568{569FLUID_LOG(FLUID_ERR, "Unexpected end of file");570return FLUID_FAILED;571}572573return FLUID_OK;574}575576/*577* fluid_midi_file_read_varlen578*/579int580fluid_midi_file_read_varlen(fluid_midi_file *mf)581{582int i;583int c;584mf->varlen = 0;585586for(i = 0;; i++)587{588if(i == 4)589{590FLUID_LOG(FLUID_ERR, "Invalid variable length number");591return FLUID_FAILED;592}593594c = fluid_midi_file_getc(mf);595596if(c < 0)597{598FLUID_LOG(FLUID_ERR, "Unexpected end of file");599return FLUID_FAILED;600}601602if(c & 0x80)603{604mf->varlen |= (int)(c & 0x7F);605mf->varlen <<= 7;606}607else608{609mf->varlen += c;610break;611}612}613614return FLUID_OK;615}616617/*618* fluid_midi_file_read_event619*/620int621fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track)622{623int status;624int type;625int tempo;626unsigned char *metadata = NULL;627unsigned char *dyn_buf = NULL;628unsigned char static_buf[256];629int nominator, denominator, clocks, notes;630fluid_midi_event_t *evt;631int channel = 0;632int param1 = 0;633int param2 = 0;634int size;635636/* read the delta-time of the event */637if(fluid_midi_file_read_varlen(mf) != FLUID_OK)638{639FLUID_LOG(FLUID_DBG, "Reading delta-time failed unexpectedly (track=%d)", track->num);640return FLUID_FAILED;641}642643mf->dtime += mf->varlen;644645/* read the status byte */646status = fluid_midi_file_getc(mf);647648if(status < 0)649{650FLUID_LOG(FLUID_ERR, "Unexpected end of file");651return FLUID_FAILED;652}653654/* not a valid status byte: use the running status instead */655if((status & 0x80) == 0)656{657if((mf->running_status & 0x80) == 0)658{659FLUID_LOG(FLUID_ERR, "Undefined status and invalid running status (0x%X, 0x%X)", status, mf->running_status);660return FLUID_FAILED;661}662663fluid_midi_file_push(mf, status);664status = mf->running_status;665}666667/* check what message we have */668if(status == MIDI_SYSEX) /* system exclusive */669{670/* read the length of the message */671if(fluid_midi_file_read_varlen(mf) != FLUID_OK)672{673FLUID_LOG(FLUID_DBG, "Failed to read length of SYSEX msg (track=%d)", track->num);674return FLUID_FAILED;675}676677if(mf->varlen)678{679FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__,680__LINE__, mf->varlen);681metadata = FLUID_MALLOC(mf->varlen + 1);682683if(metadata == NULL)684{685FLUID_LOG(FLUID_PANIC, "Out of memory");686return FLUID_FAILED;687}688689/* read the data of the message */690if(fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK)691{692FLUID_LOG(FLUID_DBG, "Failed to read data of SYSEX msg (track=%d)", track->num);693FLUID_FREE(metadata);694return FLUID_FAILED;695}696697evt = new_fluid_midi_event();698699if(evt == NULL)700{701FLUID_LOG(FLUID_ERR, "Out of memory");702FLUID_FREE(metadata);703return FLUID_FAILED;704}705706evt->dtime = mf->dtime;707size = mf->varlen;708709if(metadata[mf->varlen - 1] == MIDI_EOX)710{711size--;712}713714/* Add SYSEX event and indicate that its dynamically allocated and should be freed with event */715fluid_midi_event_set_sysex(evt, metadata, size, TRUE);716fluid_track_add_event(track, evt);717mf->dtime = 0;718}719720return FLUID_OK;721722}723else if(status == MIDI_META_EVENT) /* meta events */724{725726int result = FLUID_OK;727728/* get the type of the meta message */729type = fluid_midi_file_getc(mf);730731if(type < 0)732{733FLUID_LOG(FLUID_ERR, "Unexpected end of file");734return FLUID_FAILED;735}736737/* get the length of the data part */738if(fluid_midi_file_read_varlen(mf) != FLUID_OK)739{740FLUID_LOG(FLUID_DBG, "Failed to read length of META msg (track=%d)", track->num);741return FLUID_FAILED;742}743744if(mf->varlen < 255)745{746metadata = &static_buf[0];747}748else749{750FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__,751__LINE__, mf->varlen);752dyn_buf = FLUID_MALLOC(mf->varlen + 1);753754if(dyn_buf == NULL)755{756FLUID_LOG(FLUID_PANIC, "Out of memory");757return FLUID_FAILED;758}759760metadata = dyn_buf;761}762763/* read the data */764if(mf->varlen)765{766if(fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK)767{768if(dyn_buf)769{770FLUID_FREE(dyn_buf);771}772773FLUID_LOG(FLUID_DBG, "Failed to read meta msg (track=%d)", track->num);774return FLUID_FAILED;775}776}777778/* handle meta data */779switch(type)780{781782case MIDI_COPYRIGHT:783metadata[mf->varlen] = 0;784break;785786case MIDI_TRACK_NAME:787metadata[mf->varlen] = 0;788fluid_track_set_name(track, (char *) metadata);789break;790791case MIDI_INST_NAME:792metadata[mf->varlen] = 0;793break;794795case MIDI_LYRIC:796case MIDI_TEXT:797{798void *tmp;799int size = mf->varlen + 1;800801/* NULL terminate strings for safety */802metadata[size - 1] = '\0';803804evt = new_fluid_midi_event();805806if(evt == NULL)807{808FLUID_LOG(FLUID_ERR, "Out of memory");809result = FLUID_FAILED;810break;811}812813evt->dtime = mf->dtime;814815tmp = FLUID_MALLOC(size);816817if(tmp == NULL)818{819FLUID_LOG(FLUID_PANIC, "Out of memory");820delete_fluid_midi_event(evt);821evt = NULL;822result = FLUID_FAILED;823break;824}825826FLUID_MEMCPY(tmp, metadata, size);827828fluid_midi_event_set_sysex_LOCAL(evt, type, tmp, size, TRUE);829fluid_track_add_event(track, evt);830mf->dtime = 0;831}832break;833834case MIDI_MARKER:835break;836837case MIDI_CUE_POINT:838break; /* don't care much for text events */839840case MIDI_EOT:841if(mf->varlen != 0)842{843FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event");844result = FLUID_FAILED;845break;846}847848mf->eot = 1;849evt = new_fluid_midi_event();850851if(evt == NULL)852{853FLUID_LOG(FLUID_ERR, "Out of memory");854result = FLUID_FAILED;855break;856}857858evt->dtime = mf->dtime;859evt->type = MIDI_EOT;860fluid_track_add_event(track, evt);861mf->dtime = 0;862break;863864case MIDI_SET_TEMPO:865if(mf->varlen != 3)866{867FLUID_LOG(FLUID_ERR,868"Invalid length for SetTempo meta event");869result = FLUID_FAILED;870break;871}872873tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2];874evt = new_fluid_midi_event();875876if(evt == NULL)877{878FLUID_LOG(FLUID_ERR, "Out of memory");879result = FLUID_FAILED;880break;881}882883evt->dtime = mf->dtime;884evt->type = MIDI_SET_TEMPO;885evt->channel = 0;886evt->param1 = tempo;887evt->param2 = 0;888fluid_track_add_event(track, evt);889mf->dtime = 0;890break;891892case MIDI_SMPTE_OFFSET:893if(mf->varlen != 5)894{895FLUID_LOG(FLUID_ERR,896"Invalid length for SMPTE Offset meta event");897result = FLUID_FAILED;898break;899}900901break; /* we don't use smtp */902903case MIDI_TIME_SIGNATURE:904if(mf->varlen != 4)905{906FLUID_LOG(FLUID_ERR,907"Invalid length for TimeSignature meta event");908result = FLUID_FAILED;909break;910}911912nominator = metadata[0];913denominator = pow(2.0, (double) metadata[1]);914clocks = metadata[2];915notes = metadata[3];916917FLUID_LOG(FLUID_DBG,918"signature=%d/%d, metronome=%d, 32nd-notes=%d",919nominator, denominator, clocks, notes);920921break;922923case MIDI_KEY_SIGNATURE:924if(mf->varlen != 2)925{926FLUID_LOG(FLUID_ERR,927"Invalid length for KeySignature meta event");928result = FLUID_FAILED;929break;930}931932/* We don't care about key signatures anyway */933/* sf = metadata[0];934mi = metadata[1]; */935break;936937case MIDI_SEQUENCER_EVENT:938break;939940default:941FLUID_LOG(FLUID_INFO, "Ignoring unrecognized meta event type 0x%X", type);942break;943}944945if(dyn_buf)946{947FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__);948FLUID_FREE(dyn_buf);949}950951return result;952953}954else /* channel messages */955{956// remember current status (not applicable to SYSEX and META events!)957mf->running_status = status;958type = status & 0xf0;959channel = status & 0x0f;960961/* all channel message have at least 1 byte of associated data */962if((param1 = fluid_midi_file_getc(mf)) < 0)963{964FLUID_LOG(FLUID_ERR, "Unexpected end of file");965return FLUID_FAILED;966}967968switch(type)969{970971case NOTE_ON:972if((param2 = fluid_midi_file_getc(mf)) < 0)973{974FLUID_LOG(FLUID_ERR, "Unexpected end of file");975return FLUID_FAILED;976}977978break;979980case NOTE_OFF:981if((param2 = fluid_midi_file_getc(mf)) < 0)982{983FLUID_LOG(FLUID_ERR, "Unexpected end of file");984return FLUID_FAILED;985}986987break;988989case KEY_PRESSURE:990if((param2 = fluid_midi_file_getc(mf)) < 0)991{992FLUID_LOG(FLUID_ERR, "Unexpected end of file");993return FLUID_FAILED;994}995996break;997998case CONTROL_CHANGE:999if((param2 = fluid_midi_file_getc(mf)) < 0)1000{1001FLUID_LOG(FLUID_ERR, "Unexpected end of file");1002return FLUID_FAILED;1003}10041005break;10061007case PROGRAM_CHANGE:1008break;10091010case CHANNEL_PRESSURE:1011break;10121013case PITCH_BEND:1014if((param2 = fluid_midi_file_getc(mf)) < 0)1015{1016FLUID_LOG(FLUID_ERR, "Unexpected end of file");1017return FLUID_FAILED;1018}10191020param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f);1021param2 = 0;1022break;10231024default:1025/* Can't possibly happen !? */1026FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event 0x%X", status);1027return FLUID_FAILED;1028}10291030evt = new_fluid_midi_event();10311032if(evt == NULL)1033{1034FLUID_LOG(FLUID_ERR, "Out of memory");1035return FLUID_FAILED;1036}10371038evt->dtime = mf->dtime;1039evt->type = type;1040evt->channel = channel;1041evt->param1 = param1;1042evt->param2 = param2;1043fluid_track_add_event(track, evt);1044mf->dtime = 0;1045}10461047return FLUID_OK;1048}10491050/*1051* fluid_midi_file_get_division1052*/1053int1054fluid_midi_file_get_division(fluid_midi_file *midifile)1055{1056return midifile->division;1057}10581059/******************************************************1060*1061* fluid_track_t1062*/10631064/**1065* Create a MIDI event structure.1066* @return New MIDI event structure or NULL when out of memory.1067*/1068fluid_midi_event_t *1069new_fluid_midi_event(void)1070{1071fluid_midi_event_t *evt;1072evt = FLUID_NEW(fluid_midi_event_t);10731074if(evt == NULL)1075{1076FLUID_LOG(FLUID_ERR, "Out of memory");1077return NULL;1078}10791080evt->dtime = 0;1081evt->type = 0;1082evt->channel = 0;1083evt->param1 = 0;1084evt->param2 = 0;1085evt->next = NULL;1086evt->paramptr = NULL;1087return evt;1088}10891090/**1091* Delete MIDI event structure.1092* @param evt MIDI event structure1093*/1094void1095delete_fluid_midi_event(fluid_midi_event_t *evt)1096{1097fluid_midi_event_t *temp;1098fluid_return_if_fail(evt != NULL);10991100while(evt)1101{1102temp = evt->next;11031104/* Dynamic SYSEX event? - free (param2 indicates if dynamic) */1105if((evt->type == MIDI_SYSEX || (evt-> type == MIDI_TEXT) || (evt->type == MIDI_LYRIC)) &&1106evt->paramptr && evt->param2)1107{1108FLUID_FREE(evt->paramptr);1109}11101111FLUID_FREE(evt);1112evt = temp;1113}1114}11151116/**1117* Get the event type field of a MIDI event structure.1118* @param evt MIDI event structure1119* @return Event type field (MIDI status byte without channel)1120*/1121int1122fluid_midi_event_get_type(const fluid_midi_event_t *evt)1123{1124return evt->type;1125}11261127/**1128* Set the event type field of a MIDI event structure.1129* @param evt MIDI event structure1130* @param type Event type field (MIDI status byte without channel)1131* @return Always returns #FLUID_OK1132*/1133int1134fluid_midi_event_set_type(fluid_midi_event_t *evt, int type)1135{1136evt->type = type;1137return FLUID_OK;1138}11391140/**1141* Get the channel field of a MIDI event structure.1142* @param evt MIDI event structure1143* @return Channel field1144*/1145int1146fluid_midi_event_get_channel(const fluid_midi_event_t *evt)1147{1148return evt->channel;1149}11501151/**1152* Set the channel field of a MIDI event structure.1153* @param evt MIDI event structure1154* @param chan MIDI channel field1155* @return Always returns #FLUID_OK1156*/1157int1158fluid_midi_event_set_channel(fluid_midi_event_t *evt, int chan)1159{1160evt->channel = chan;1161return FLUID_OK;1162}11631164/**1165* Get the key field of a MIDI event structure.1166* @param evt MIDI event structure1167* @return MIDI note number (0-127)1168*/1169int1170fluid_midi_event_get_key(const fluid_midi_event_t *evt)1171{1172return evt->param1;1173}11741175/**1176* Set the key field of a MIDI event structure.1177* @param evt MIDI event structure1178* @param v MIDI note number (0-127)1179* @return Always returns #FLUID_OK1180*/1181int1182fluid_midi_event_set_key(fluid_midi_event_t *evt, int v)1183{1184evt->param1 = v;1185return FLUID_OK;1186}11871188/**1189* Get the velocity field of a MIDI event structure.1190* @param evt MIDI event structure1191* @return MIDI velocity number (0-127)1192*/1193int1194fluid_midi_event_get_velocity(const fluid_midi_event_t *evt)1195{1196return evt->param2;1197}11981199/**1200* Set the velocity field of a MIDI event structure.1201* @param evt MIDI event structure1202* @param v MIDI velocity value1203* @return Always returns #FLUID_OK1204*/1205int1206fluid_midi_event_set_velocity(fluid_midi_event_t *evt, int v)1207{1208evt->param2 = v;1209return FLUID_OK;1210}12111212/**1213* Get the control number of a MIDI event structure.1214* @param evt MIDI event structure1215* @return MIDI control number1216*/1217int1218fluid_midi_event_get_control(const fluid_midi_event_t *evt)1219{1220return evt->param1;1221}12221223/**1224* Set the control field of a MIDI event structure.1225* @param evt MIDI event structure1226* @param v MIDI control number1227* @return Always returns #FLUID_OK1228*/1229int1230fluid_midi_event_set_control(fluid_midi_event_t *evt, int v)1231{1232evt->param1 = v;1233return FLUID_OK;1234}12351236/**1237* Get the value field from a MIDI event structure.1238* @param evt MIDI event structure1239* @return Value field1240*/1241int1242fluid_midi_event_get_value(const fluid_midi_event_t *evt)1243{1244return evt->param2;1245}12461247/**1248* Set the value field of a MIDI event structure.1249* @param evt MIDI event structure1250* @param v Value to assign1251* @return Always returns #FLUID_OK1252*/1253int1254fluid_midi_event_set_value(fluid_midi_event_t *evt, int v)1255{1256evt->param2 = v;1257return FLUID_OK;1258}12591260/**1261* Get the program field of a MIDI event structure.1262* @param evt MIDI event structure1263* @return MIDI program number (0-127)1264*/1265int1266fluid_midi_event_get_program(const fluid_midi_event_t *evt)1267{1268return evt->param1;1269}12701271/**1272* Set the program field of a MIDI event structure.1273* @param evt MIDI event structure1274* @param val MIDI program number (0-127)1275* @return Always returns #FLUID_OK1276*/1277int1278fluid_midi_event_set_program(fluid_midi_event_t *evt, int val)1279{1280evt->param1 = val;1281return FLUID_OK;1282}12831284/**1285* Get the pitch field of a MIDI event structure.1286* @param evt MIDI event structure1287* @return Pitch value (14 bit value, 0-16383, 8192 is center)1288*/1289int1290fluid_midi_event_get_pitch(const fluid_midi_event_t *evt)1291{1292return evt->param1;1293}12941295/**1296* Set the pitch field of a MIDI event structure.1297* @param evt MIDI event structure1298* @param val Pitch value (14 bit value, 0-16383, 8192 is center)1299* @return Always returns FLUID_OK1300*/1301int1302fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val)1303{1304evt->param1 = val;1305return FLUID_OK;1306}13071308/**1309* Assign sysex data to a MIDI event structure.1310* @param evt MIDI event structure1311* @param data Pointer to SYSEX data1312* @param size Size of SYSEX data in bytes1313* @param dynamic TRUE if the SYSEX data has been dynamically allocated and1314* should be freed when the event is freed (only applies if event gets destroyed1315* with delete_fluid_midi_event())1316* @return Always returns #FLUID_OK1317*/1318int1319fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic)1320{1321fluid_midi_event_set_sysex_LOCAL(evt, MIDI_SYSEX, data, size, dynamic);1322return FLUID_OK;1323}13241325/**1326* Assign text data to a MIDI event structure.1327* @param evt MIDI event structure1328* @param data Pointer to text data1329* @param size Size of text data in bytes1330* @param dynamic TRUE if the data has been dynamically allocated and1331* should be freed when the event is freed via delete_fluid_midi_event()1332* @return Always returns #FLUID_OK1333*1334* @since 2.0.01335*/1336int1337fluid_midi_event_set_text(fluid_midi_event_t *evt, void *data, int size, int dynamic)1338{1339fluid_midi_event_set_sysex_LOCAL(evt, MIDI_TEXT, data, size, dynamic);1340return FLUID_OK;1341}13421343/**1344* Get the text of a MIDI event structure.1345* @param evt MIDI event structure1346* @param data Pointer to return text data on.1347* @param size Pointer to return text size on.1348* @return Returns #FLUID_OK if \p data and \p size previously set by1349* fluid_midi_event_set_text() have been successfully retrieved.1350* Else #FLUID_FAILED is returned and \p data and \p size are not changed.1351* @since 2.0.31352*/1353int fluid_midi_event_get_text(fluid_midi_event_t *evt, void **data, int *size)1354{1355fluid_return_val_if_fail(evt != NULL, FLUID_FAILED);1356fluid_return_val_if_fail(evt->type == MIDI_TEXT, FLUID_FAILED);13571358fluid_midi_event_get_sysex_LOCAL(evt, data, size);1359return FLUID_OK;1360}13611362/**1363* Assign lyric data to a MIDI event structure.1364* @param evt MIDI event structure1365* @param data Pointer to lyric data1366* @param size Size of lyric data in bytes1367* @param dynamic TRUE if the data has been dynamically allocated and1368* should be freed when the event is freed via delete_fluid_midi_event()1369* @return Always returns #FLUID_OK1370*1371* @since 2.0.01372*/1373int1374fluid_midi_event_set_lyrics(fluid_midi_event_t *evt, void *data, int size, int dynamic)1375{1376fluid_midi_event_set_sysex_LOCAL(evt, MIDI_LYRIC, data, size, dynamic);1377return FLUID_OK;1378}13791380/**1381* Get the lyric of a MIDI event structure.1382* @param evt MIDI event structure1383* @param data Pointer to return lyric data on.1384* @param size Pointer to return lyric size on.1385* @return Returns #FLUID_OK if \p data and \p size previously set by1386* fluid_midi_event_set_lyrics() have been successfully retrieved.1387* Else #FLUID_FAILED is returned and \p data and \p size are not changed.1388* @since 2.0.31389*/1390int fluid_midi_event_get_lyrics(fluid_midi_event_t *evt, void **data, int *size)1391{1392fluid_return_val_if_fail(evt != NULL, FLUID_FAILED);1393fluid_return_val_if_fail(evt->type == MIDI_LYRIC, FLUID_FAILED);13941395fluid_midi_event_get_sysex_LOCAL(evt, data, size);1396return FLUID_OK;1397}13981399static void fluid_midi_event_set_sysex_LOCAL(fluid_midi_event_t *evt, int type, void *data, int size, int dynamic)1400{1401evt->type = type;1402evt->paramptr = data;1403evt->param1 = size;1404evt->param2 = dynamic;1405}14061407static void fluid_midi_event_get_sysex_LOCAL(fluid_midi_event_t *evt, void **data, int *size)1408{1409if(data)1410{1411*data = evt->paramptr;1412}14131414if(size)1415{1416*size = evt->param1;1417}1418}14191420/******************************************************1421*1422* fluid_track_t1423*/14241425/*1426* new_fluid_track1427*/1428fluid_track_t *1429new_fluid_track(int num)1430{1431fluid_track_t *track;1432track = FLUID_NEW(fluid_track_t);14331434if(track == NULL)1435{1436return NULL;1437}14381439track->name = NULL;1440track->num = num;1441track->first = NULL;1442track->cur = NULL;1443track->last = NULL;1444track->ticks = 0;1445return track;1446}14471448/*1449* delete_fluid_track1450*/1451void1452delete_fluid_track(fluid_track_t *track)1453{1454fluid_return_if_fail(track != NULL);14551456FLUID_FREE(track->name);1457delete_fluid_midi_event(track->first);1458FLUID_FREE(track);1459}14601461/*1462* fluid_track_set_name1463*/1464int1465fluid_track_set_name(fluid_track_t *track, char *name)1466{1467size_t len;14681469if(track->name != NULL)1470{1471FLUID_FREE(track->name);1472}14731474if(name == NULL)1475{1476track->name = NULL;1477return FLUID_OK;1478}14791480len = FLUID_STRLEN(name);1481track->name = FLUID_MALLOC(len + 1);14821483if(track->name == NULL)1484{1485FLUID_LOG(FLUID_ERR, "Out of memory");1486return FLUID_FAILED;1487}14881489FLUID_STRCPY(track->name, name);1490return FLUID_OK;1491}14921493/*1494* fluid_track_get_duration1495*/1496int1497fluid_track_get_duration(fluid_track_t *track)1498{1499int time = 0;1500fluid_midi_event_t *evt = track->first;15011502while(evt != NULL)1503{1504time += evt->dtime;1505evt = evt->next;1506}15071508return time;1509}15101511/*1512* fluid_track_add_event1513*/1514int1515fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt)1516{1517evt->next = NULL;15181519if(track->first == NULL)1520{1521track->first = evt;1522track->cur = evt;1523track->last = evt;1524}1525else1526{1527track->last->next = evt;1528track->last = evt;1529}15301531return FLUID_OK;1532}15331534/*1535* fluid_track_next_event1536*/1537fluid_midi_event_t *1538fluid_track_next_event(fluid_track_t *track)1539{1540if(track->cur != NULL)1541{1542track->cur = track->cur->next;1543}15441545return track->cur;1546}15471548/*1549* fluid_track_reset1550*/1551int1552fluid_track_reset(fluid_track_t *track)1553{1554track->ticks = 0;1555track->cur = track->first;1556return FLUID_OK;1557}15581559/*1560* fluid_track_send_events1561*/1562static void1563fluid_track_send_events(fluid_track_t *track,1564fluid_synth_t *synth,1565fluid_player_t *player,1566unsigned int ticks,1567int seek_ticks1568)1569{1570fluid_midi_event_t *event;1571int seeking = seek_ticks >= 0;15721573if(seeking)1574{1575ticks = seek_ticks; /* update target ticks */15761577if(track->ticks > ticks)1578{1579fluid_track_reset(track); /* reset track if seeking backwards */1580}1581}15821583while(1)1584{15851586event = track->cur;15871588if(event == NULL)1589{1590return;1591}15921593/* printf("track=%02d\tticks=%05u\ttrack=%05u\tdtime=%05u\tnext=%05u\n", */1594/* track->num, */1595/* ticks, */1596/* track->ticks, */1597/* event->dtime, */1598/* track->ticks + event->dtime); */15991600if(track->ticks + event->dtime > ticks)1601{1602return;1603}16041605track->ticks += event->dtime;16061607if(!player || event->type == MIDI_EOT)1608{1609/* don't send EOT events to the callback */1610}1611else if(seeking && track->ticks != ticks && (event->type == NOTE_ON || event->type == NOTE_OFF))1612{1613/* skip on/off messages */1614}1615else1616{1617if(player->playback_callback)1618{1619player->playback_callback(player->playback_userdata, event);1620if(event->type == NOTE_ON && event->param2 != 0 && !player->channel_isplaying[event->channel])1621{1622player->channel_isplaying[event->channel] = TRUE;1623}1624}1625}16261627if(event->type == MIDI_SET_TEMPO && player != NULL)1628{1629/* memorize the tempo change value coming from the MIDI file */1630fluid_atomic_int_set(&player->miditempo, event->param1);1631fluid_player_update_tempo(player);1632}16331634fluid_track_next_event(track);16351636}1637}16381639/******************************************************1640*1641* fluid_player1642*/1643static void1644fluid_player_handle_reset_synth(void *data, const char *name, int value)1645{1646fluid_player_t *player = data;1647fluid_return_if_fail(player != NULL);16481649player->reset_synth_between_songs = value;1650}16511652static int check_for_on_notes(fluid_synth_t *synth)1653{1654fluid_voice_t* v[1024];1655unsigned int i, res=FALSE;1656fluid_synth_get_voicelist(synth, v, FLUID_N_ELEMENTS(v), -1);1657for(i=0; i<FLUID_N_ELEMENTS(v) && v[i] != NULL; i++)1658{1659fluid_voice_t *vv = v[i];1660if(vv != NULL && fluid_voice_is_on(vv))1661{1662res = TRUE;1663FLUID_LOG(FLUID_DBG, "Voice is on! channel %d, key %d", fluid_voice_get_channel(vv), fluid_voice_get_key(vv));1664}1665}1666return res;1667}16681669/**1670* Create a new MIDI player.1671* @param synth Fluid synthesizer instance to create player for1672* @return New MIDI player instance or NULL on error (out of memory)1673*/1674fluid_player_t *1675new_fluid_player(fluid_synth_t *synth)1676{1677int i;1678fluid_player_t *player;1679player = FLUID_NEW(fluid_player_t);16801681if(player == NULL)1682{1683FLUID_LOG(FLUID_ERR, "Out of memory");1684return NULL;1685}16861687fluid_atomic_int_set(&player->status, FLUID_PLAYER_READY);1688fluid_atomic_int_set(&player->stopping, 0);1689player->loop = 1;1690player->ntracks = 0;16911692for(i = 0; i < MAX_NUMBER_OF_TRACKS; i++)1693{1694player->track[i] = NULL;1695}16961697player->synth = synth;1698player->system_timer = NULL;1699player->sample_timer = NULL;1700player->playlist = NULL;1701player->currentfile = NULL;1702player->division = 0;17031704/* internal tempo (from MIDI file) in micro seconds per quarter note */1705player->sync_mode = 1; /* the player follows internal tempo change */1706player->miditempo = 500000;1707/* external tempo in micro seconds per quarter note */1708player->exttempo = 500000;1709/* tempo multiplier */1710player->multempo = 1.0F;17111712player->deltatime = 4.0;1713player->cur_msec = 0;1714player->cur_ticks = 0;1715player->end_msec = -1;1716player->end_pedals_disabled = 0;1717player->last_callback_ticks = -1;1718fluid_atomic_int_set(&player->seek_ticks, -1);1719fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth);1720fluid_player_set_tick_callback(player, NULL, NULL);1721player->use_system_timer = fluid_settings_str_equal(synth->settings,1722"player.timing-source", "system");1723if(player->use_system_timer)1724{1725player->system_timer = new_fluid_timer((int) player->deltatime,1726fluid_player_callback, player, TRUE, FALSE, TRUE);17271728if(player->system_timer == NULL)1729{1730goto err;1731}1732}1733else1734{1735player->sample_timer = new_fluid_sample_timer(player->synth,1736fluid_player_callback, player);17371738if(player->sample_timer == NULL)1739{1740goto err;1741}1742}17431744fluid_settings_getint(synth->settings, "player.reset-synth", &i);1745fluid_player_handle_reset_synth(player, NULL, i);17461747fluid_settings_callback_int(synth->settings, "player.reset-synth",1748fluid_player_handle_reset_synth, player);17491750return player;17511752err:1753delete_fluid_player(player);1754return NULL;1755}17561757/**1758* Delete a MIDI player instance.1759* @param player MIDI player instance1760* @warning Do not call when the synthesizer associated to this \p player renders audio,1761* i.e. an audio driver is running or any other synthesizer thread concurrently calls1762* fluid_synth_process() or fluid_synth_nwrite_float() or fluid_synth_write_*() !1763*/1764void1765delete_fluid_player(fluid_player_t *player)1766{1767fluid_list_t *q;1768fluid_playlist_item *pi;17691770fluid_return_if_fail(player != NULL);17711772fluid_settings_callback_int(player->synth->settings, "player.reset-synth",1773NULL, NULL);17741775fluid_player_stop(player);1776fluid_player_reset(player);17771778delete_fluid_timer(player->system_timer);1779delete_fluid_sample_timer(player->synth, player->sample_timer);17801781while(player->playlist != NULL)1782{1783q = player->playlist->next;1784pi = (fluid_playlist_item *) player->playlist->data;1785FLUID_FREE(pi->filename);1786FLUID_FREE(pi->buffer);1787FLUID_FREE(pi);1788delete1_fluid_list(player->playlist);1789player->playlist = q;1790}17911792FLUID_FREE(player);1793}17941795/**1796* Registers settings related to the MIDI player1797*/1798void1799fluid_player_settings(fluid_settings_t *settings)1800{1801/* player.timing-source can be either "system" (use system timer)1802or "sample" (use timer based on number of written samples) */1803fluid_settings_register_str(settings, "player.timing-source", "sample", 0);1804fluid_settings_add_option(settings, "player.timing-source", "sample");1805fluid_settings_add_option(settings, "player.timing-source", "system");18061807/* Selects whether the player should reset the synth between songs, or not. */1808fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1, FLUID_HINT_TOGGLED);1809}181018111812int1813fluid_player_reset(fluid_player_t *player)1814{1815int i;18161817for(i = 0; i < MAX_NUMBER_OF_TRACKS; i++)1818{1819if(player->track[i] != NULL)1820{1821delete_fluid_track(player->track[i]);1822player->track[i] = NULL;1823}1824}18251826for(i = 0; i < MAX_NUMBER_OF_CHANNELS; i++)1827{1828player->channel_isplaying[i] = FALSE;1829}18301831/* player->current_file = NULL; */1832/* player->status = FLUID_PLAYER_READY; */1833/* player->loop = 1; */1834player->ntracks = 0;1835player->division = 0;1836player->miditempo = 500000;1837player->deltatime = 4.0;1838return 0;1839}18401841/*1842* fluid_player_add_track1843*/1844int1845fluid_player_add_track(fluid_player_t *player, fluid_track_t *track)1846{1847if(player->ntracks < MAX_NUMBER_OF_TRACKS)1848{1849player->track[player->ntracks++] = track;1850return FLUID_OK;1851}1852else1853{1854return FLUID_FAILED;1855}1856}18571858/**1859* Change the MIDI callback function.1860*1861* @param player MIDI player instance1862* @param handler Pointer to callback function1863* @param handler_data Parameter sent to the callback function1864* @returns FLUID_OK1865*1866* This is usually set to fluid_synth_handle_midi_event(), but can optionally1867* be changed to a user-defined function instead, for intercepting all MIDI1868* messages sent to the synth. You can also use a midi router as the callback1869* function to modify the MIDI messages before sending them to the synth.1870*1871* @since 1.1.41872*/1873int1874fluid_player_set_playback_callback(fluid_player_t *player,1875handle_midi_event_func_t handler, void *handler_data)1876{1877player->playback_callback = handler;1878player->playback_userdata = handler_data;1879return FLUID_OK;1880}18811882/**1883* Add a listener function for every MIDI tick change.1884*1885* @param player MIDI player instance1886* @param handler Pointer to callback function1887* @param handler_data Opaque parameter to be sent to the callback function1888* @returns #FLUID_OK1889*1890* This callback is not set by default, but can optionally1891* be changed to a user-defined function for intercepting all MIDI1892* tick changes and react to them with precision.1893*1894* @since 2.2.01895*/1896int1897fluid_player_set_tick_callback(fluid_player_t *player, handle_midi_tick_func_t handler, void *handler_data)1898{1899player->tick_callback = handler;1900player->tick_userdata = handler_data;1901return FLUID_OK;1902}19031904/**1905* Add a MIDI file to a player queue.1906* @param player MIDI player instance1907* @param midifile File name of the MIDI file to add1908* @return #FLUID_OK or #FLUID_FAILED1909*/1910int1911fluid_player_add(fluid_player_t *player, const char *midifile)1912{1913fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item));1914char *f = FLUID_STRDUP(midifile);19151916if(!pi || !f)1917{1918FLUID_FREE(pi);1919FLUID_FREE(f);1920FLUID_LOG(FLUID_PANIC, "Out of memory");1921return FLUID_FAILED;1922}19231924pi->filename = f;1925pi->buffer = NULL;1926pi->buffer_len = 0;1927player->playlist = fluid_list_append(player->playlist, pi);1928return FLUID_OK;1929}19301931/**1932* Add a MIDI file to a player queue, from a buffer in memory.1933* @param player MIDI player instance1934* @param buffer Pointer to memory containing the bytes of a complete MIDI1935* file. The data is copied, so the caller may free or modify it immediately1936* without affecting the playlist.1937* @param len Length of the buffer, in bytes.1938* @return #FLUID_OK or #FLUID_FAILED1939*/1940int1941fluid_player_add_mem(fluid_player_t *player, const void *buffer, size_t len)1942{1943/* Take a copy of the buffer, so the caller can free immediately. */1944fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item));1945void *buf_copy = FLUID_MALLOC(len);19461947if(!pi || !buf_copy)1948{1949FLUID_FREE(pi);1950FLUID_FREE(buf_copy);1951FLUID_LOG(FLUID_PANIC, "Out of memory");1952return FLUID_FAILED;1953}19541955FLUID_MEMCPY(buf_copy, buffer, len);1956pi->filename = NULL;1957pi->buffer = buf_copy;1958pi->buffer_len = len;1959player->playlist = fluid_list_append(player->playlist, pi);1960return FLUID_OK;1961}19621963/*1964* fluid_player_load1965*/1966int1967fluid_player_load(fluid_player_t *player, fluid_playlist_item *item)1968{1969fluid_midi_file *midifile;1970char *buffer;1971size_t buffer_length;1972int buffer_owned;19731974if(item->filename != NULL)1975{1976fluid_file fp;1977/* This file is specified by filename; load the file from disk */1978FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__,1979item->filename);1980/* Read the entire contents of the file into the buffer */1981fp = FLUID_FOPEN(item->filename, "rb");19821983if(fp == NULL)1984{1985FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file");1986return FLUID_FAILED;1987}19881989buffer = fluid_file_read_full(fp, &buffer_length);19901991FLUID_FCLOSE(fp);19921993if(buffer == NULL)1994{1995return FLUID_FAILED;1996}19971998buffer_owned = 1;1999}2000else2001{2002/* This file is specified by a pre-loaded buffer; load from memory */2003FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile from memory (%p)",2004__FILE__, __LINE__, item->buffer);2005buffer = (char *) item->buffer;2006buffer_length = item->buffer_len;2007/* Do not free the buffer (it is owned by the playlist) */2008buffer_owned = 0;2009}20102011midifile = new_fluid_midi_file(buffer, buffer_length);20122013if(midifile == NULL)2014{2015if(buffer_owned)2016{2017FLUID_FREE(buffer);2018}20192020return FLUID_FAILED;2021}20222023player->division = fluid_midi_file_get_division(midifile);2024fluid_player_update_tempo(player); // Update deltatime2025/*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */20262027if(fluid_midi_file_load_tracks(midifile, player) != FLUID_OK)2028{2029if(buffer_owned)2030{2031FLUID_FREE(buffer);2032}20332034delete_fluid_midi_file(midifile);2035return FLUID_FAILED;2036}20372038delete_fluid_midi_file(midifile);20392040if(buffer_owned)2041{2042FLUID_FREE(buffer);2043}20442045return FLUID_OK;2046}20472048void2049fluid_player_advancefile(fluid_player_t *player)2050{2051if(player->playlist == NULL)2052{2053return; /* No files to play */2054}20552056if(player->currentfile != NULL)2057{2058player->currentfile = fluid_list_next(player->currentfile);2059}20602061if(player->currentfile == NULL)2062{2063if(player->loop == 0)2064{2065return; /* We're done playing */2066}20672068if(player->loop > 0)2069{2070player->loop--;2071}20722073player->currentfile = player->playlist;2074}2075}20762077void2078fluid_player_playlist_load(fluid_player_t *player, unsigned int msec)2079{2080fluid_playlist_item *current_playitem;2081int i;20822083do2084{2085fluid_player_advancefile(player);20862087if(player->currentfile == NULL)2088{2089/* Failed to find next song, probably since we're finished */2090fluid_atomic_int_set(&player->status, FLUID_PLAYER_DONE);2091return;2092}20932094fluid_player_reset(player);2095current_playitem = (fluid_playlist_item *) player->currentfile->data;2096}2097while(fluid_player_load(player, current_playitem) != FLUID_OK);20982099/* Successfully loaded midi file */21002101player->begin_msec = msec;2102player->start_msec = msec;2103player->start_ticks = 0;2104player->cur_ticks = 0;21052106for(i = 0; i < player->ntracks; i++)2107{2108if(player->track[i] != NULL)2109{2110fluid_track_reset(player->track[i]);2111}2112}2113}21142115/*2116* fluid_player_callback2117*/2118int2119fluid_player_callback(void *data, unsigned int msec)2120{2121int i;2122int loadnextfile;2123int status = FLUID_PLAYER_DONE;2124fluid_midi_event_t mute_event;2125fluid_player_t *player;2126fluid_synth_t *synth;2127player = (fluid_player_t *) data;2128synth = player->synth;21292130loadnextfile = player->currentfile == NULL ? 1 : 0;21312132fluid_midi_event_set_type(&mute_event, CONTROL_CHANGE);2133mute_event.param1 = ALL_SOUND_OFF;2134mute_event.param2 = 1;21352136if(fluid_player_get_status(player) != FLUID_PLAYER_PLAYING)2137{2138if(fluid_atomic_int_get(&player->stopping))2139{2140for(i = 0; i < synth->midi_channels; i++)2141{2142if(player->channel_isplaying[i])2143{2144fluid_midi_event_set_channel(&mute_event, i);2145player->playback_callback(player->playback_userdata, &mute_event);2146player->channel_isplaying[i] = FALSE;2147}2148}2149fluid_atomic_int_set(&player->stopping, 0);2150}2151return 1;2152}2153do2154{2155float deltatime;2156int seek_ticks;21572158if(loadnextfile)2159{2160loadnextfile = 0;2161fluid_player_playlist_load(player, msec);21622163if(player->currentfile == NULL)2164{2165return 0;2166}2167}21682169if(msec < player->cur_msec)2170{2171// overflow of fluid_synth_get_ticks()2172FLUID_LOG(FLUID_ERR, "The maximum playback duration has been reached. Terminating player!");2173fluid_player_stop(player);2174fluid_player_seek(player, 0);2175player->cur_ticks = 0;2176return 0;2177}21782179player->cur_msec = msec;2180deltatime = fluid_atomic_float_get(&player->deltatime);2181player->cur_ticks = (player->start_ticks2182+ (int)((double)(player->cur_msec - player->start_msec)2183/ deltatime + 0.5)); /* 0.5 to average overall error when casting */21842185seek_ticks = fluid_atomic_int_get(&player->seek_ticks);2186if(seek_ticks >= 0)2187{2188for(i = 0; i < synth->midi_channels; i++)2189{2190if(player->channel_isplaying[i])2191{2192fluid_midi_event_set_channel(&mute_event, i);2193player->playback_callback(player->playback_userdata, &mute_event);2194player->channel_isplaying[i] = FALSE;2195}2196}2197}21982199for(i = 0; i < player->ntracks; i++)2200{2201fluid_track_send_events(player->track[i], synth, player, player->cur_ticks, seek_ticks);2202if(!fluid_track_eot(player->track[i]))2203{2204status = FLUID_PLAYER_PLAYING;2205}2206}22072208if(seek_ticks >= 0)2209{2210player->start_ticks = seek_ticks; /* tick position of last tempo value (which is now) */2211player->cur_ticks = seek_ticks;2212player->begin_msec = msec; /* only used to calculate the duration of playing */2213player->start_msec = msec; /* should be the (synth)-time of the last tempo change */2214fluid_atomic_int_set(&player->seek_ticks, -1); /* clear seek_ticks */2215}22162217if(fluid_list_next(player->currentfile) == NULL && player->loop == 0)2218{2219/* Once we've run out of MIDI events, keep playing until no voices are active */2220if(status == FLUID_PLAYER_DONE && fluid_synth_get_active_voice_count(player->synth) > 0)2221{2222/* The first time we notice we've run out of MIDI events but there are still active voices, disable all hold pedals */2223if(!player->end_pedals_disabled)2224{2225if(check_for_on_notes(synth))2226{2227FLUID_LOG(FLUID_WARN, "End of the MIDI file reached, but not all notes have received a note off event! OFFing them now! Run with --verbose to spot pending voices.");2228}22292230for(i = 0; i < synth->midi_channels; i++)2231{2232fluid_synth_cc(player->synth, i, SUSTAIN_SWITCH, 0);2233fluid_synth_cc(player->synth, i, SOSTENUTO_SWITCH, 0);2234fluid_synth_cc(player->synth, i, ALL_NOTES_OFF, 0);2235}22362237player->end_pedals_disabled = 1;2238}22392240status = FLUID_PLAYER_PLAYING;2241}22422243/* Once no voices are active, if end_msec hasn't been scheduled, schedule it so we wait for reverb, etc to finish */2244if(status == FLUID_PLAYER_DONE && player->end_msec < 0)2245{2246player->end_msec = msec + FLUID_PLAYER_STOP_GRACE_MS;2247}2248/* If end_msec has been scheduled and is in the future, keep playing */2249if (player->end_msec >= 0 && msec < (unsigned int) player->end_msec)2250{2251status = FLUID_PLAYER_PLAYING;2252}2253}22542255/* Once there's no reason to keep playing, we're actually done */2256if(status == FLUID_PLAYER_DONE)2257{2258FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__,2259__LINE__, (msec - player->begin_msec) / 1000.0);22602261if(player->reset_synth_between_songs)2262{2263fluid_synth_system_reset(player->synth);2264}22652266loadnextfile = 1;2267}22682269if (player->tick_callback != NULL && player->last_callback_ticks != player->cur_ticks) {2270player->tick_callback(player->tick_userdata, player->cur_ticks);2271player->last_callback_ticks = player->cur_ticks;2272}2273}2274while(loadnextfile);22752276/* do not update the status if the player has been stopped already */2277fluid_atomic_int_compare_and_exchange(&player->status, FLUID_PLAYER_PLAYING, status);22782279return 1;2280}22812282/**2283* Activates play mode for a MIDI player if not already playing.2284* @param player MIDI player instance2285* @return #FLUID_OK on success, #FLUID_FAILED otherwise2286*2287* If the list of files added to the player has completed its requested number of loops,2288* the playlist will be restarted from the beginning with a loop count of 1.2289*/2290int2291fluid_player_play(fluid_player_t *player)2292{2293if(fluid_player_get_status(player) == FLUID_PLAYER_PLAYING ||2294player->playlist == NULL)2295{2296return FLUID_OK;2297}22982299if(!player->use_system_timer)2300{2301fluid_sample_timer_reset(player->synth, player->sample_timer);2302player->cur_msec = 0;2303}23042305/* If we're at the end of the playlist and there are no loops left, loop once */2306if(player->currentfile == NULL && player->loop == 0)2307{2308player->loop = 1;2309}23102311player->end_msec = -1;2312player->end_pedals_disabled = 0;23132314fluid_atomic_int_set(&player->status, FLUID_PLAYER_PLAYING);23152316return FLUID_OK;2317}23182319/**2320* Pauses the MIDI playback.2321*2322* @param player MIDI player instance2323* @return Always returns #FLUID_OK2324*2325* It will not rewind to the beginning of the file, use fluid_player_seek() for this purpose.2326*/2327int2328fluid_player_stop(fluid_player_t *player)2329{2330fluid_atomic_int_set(&player->status, FLUID_PLAYER_DONE);2331fluid_atomic_int_set(&player->stopping, 1);2332fluid_player_seek(player, fluid_player_get_current_tick(player));2333return FLUID_OK;2334}23352336/**2337* Get MIDI player status.2338* @param player MIDI player instance2339* @return Player status (#fluid_player_status)2340* @since 1.1.02341*/2342int2343fluid_player_get_status(fluid_player_t *player)2344{2345return fluid_atomic_int_get(&player->status);2346}23472348/**2349* Seek in the currently playing file.2350*2351* @param player MIDI player instance2352* @param ticks the position to seek to in the current file2353* @return #FLUID_FAILED if ticks is negative or after the latest tick of the file2354* [or, since 2.1.3, if another seek operation is currently in progress],2355* #FLUID_OK otherwise.2356*2357* The actual seek will be performed when the synth calls back the player (i.e. a few2358* levels above the player's callback set with fluid_player_set_playback_callback()).2359* If the player's status is #FLUID_PLAYER_PLAYING and a previous seek operation has2360* not been completed yet, #FLUID_FAILED is returned.2361*2362* @since 2.0.02363*/2364int fluid_player_seek(fluid_player_t *player, int ticks)2365{2366if(ticks < 0 || (fluid_player_get_status(player) != FLUID_PLAYER_READY && ticks > fluid_player_get_total_ticks(player)))2367{2368return FLUID_FAILED;2369}23702371if(fluid_player_get_status(player) == FLUID_PLAYER_PLAYING)2372{2373if(fluid_atomic_int_compare_and_exchange(&player->seek_ticks, -1, ticks))2374{2375// new seek position has been set, as no previous seek was in progress2376return FLUID_OK;2377}2378}2379else2380{2381// If the player is not currently playing, a new seek position can be set at any time. This allows2382// the user to do:2383// fluid_player_stop();2384// fluid_player_seek(0); // to beginning2385fluid_atomic_int_set(&player->seek_ticks, ticks);2386return FLUID_OK;2387}23882389// a previous seek is still in progress or hasn't been processed yet2390return FLUID_FAILED;2391}239223932394/**2395* Enable looping of a MIDI player2396*2397* @param player MIDI player instance2398* @param loop Times left to loop the playlist. -1 means loop infinitely.2399* @return Always returns #FLUID_OK2400*2401* For example, if you want to loop the playlist twice, set loop to 22402* and call this function before you start the player.2403*2404* @since 1.1.02405*/2406int fluid_player_set_loop(fluid_player_t *player, int loop)2407{2408player->loop = loop;2409return FLUID_OK;2410}24112412/**2413* update the MIDI player internal deltatime dependent of actual tempo.2414* @param player MIDI player instance2415*/2416static void fluid_player_update_tempo(fluid_player_t *player)2417{2418int tempo; /* tempo in micro seconds by quarter note */2419float deltatime;24202421/* do nothing if the division is still unknown to avoid a div by zero */2422if(player->division == 0)2423{2424return;2425}24262427if(fluid_atomic_int_get(&player->sync_mode))2428{2429/* take internal tempo from MIDI file */2430tempo = fluid_atomic_int_get(&player->miditempo);2431/* compute deltattime (in ms) from current tempo and apply tempo multiplier */2432deltatime = (float)tempo / (float)player->division / (float)1000.0;2433deltatime /= fluid_atomic_float_get(&player->multempo); /* multiply tempo */2434}2435else2436{2437/* take external tempo */2438tempo = fluid_atomic_int_get(&player->exttempo);2439/* compute deltattime (in ms) from current tempo */2440deltatime = (float)tempo / (float)player->division / (float)1000.0;2441}24422443fluid_atomic_float_set(&player->deltatime, deltatime);24442445player->start_msec = player->cur_msec;2446player->start_ticks = player->cur_ticks;24472448FLUID_LOG(FLUID_DBG,2449"tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d",2450tempo, player->deltatime, player->cur_msec, player->cur_ticks);24512452}24532454/**2455* Set the tempo of a MIDI player.2456* The player can be controlled by internal tempo coming from MIDI file tempo2457* change or controlled by external tempo expressed in BPM or in micro seconds2458* per quarter note.2459*2460* @param player MIDI player instance. Must be a valid pointer.2461* @param tempo_type Must a be value of #fluid_player_set_tempo_type and indicates the2462* meaning of tempo value and how the player will be controlled, see below.2463* @param tempo Tempo value or multiplier.2464*2465* #FLUID_PLAYER_TEMPO_INTERNAL, the player will be controlled by internal2466* MIDI file tempo changes. If there are no tempo change in the MIDI file a default2467* value of 120 bpm is used. The @c tempo parameter is used as a multiplier factor2468* that must be in the range (0.001 to 1000).2469* For example, if the current MIDI file tempo is 120 bpm and the multiplier2470* value is 0.5 then this tempo will be slowed down to 60 bpm.2471* At creation, the player is set to be controlled by internal tempo with a2472* multiplier factor set to 1.0.2473*2474* #FLUID_PLAYER_TEMPO_EXTERNAL_BPM, the player will be controlled by the2475* external tempo value provided by the tempo parameter in bpm2476* (i.e in quarter notes per minute) which must be in the range (1 to 60000000).2477*2478* #FLUID_PLAYER_TEMPO_EXTERNAL_MIDI, similar as FLUID_PLAYER_TEMPO_EXTERNAL_BPM,2479* but the tempo parameter value is in micro seconds per quarter note which2480* must be in the range (1 to 60000000).2481* Using micro seconds per quarter note is convenient when the tempo value is2482* derived from MIDI clock realtime messages.2483*2484* @note When the player is controlled by an external tempo2485* (#FLUID_PLAYER_TEMPO_EXTERNAL_BPM or #FLUID_PLAYER_TEMPO_EXTERNAL_MIDI) it2486* continues to memorize the most recent internal tempo change coming from the2487* MIDI file so that next call to fluid_player_set_tempo() with2488* #FLUID_PLAYER_TEMPO_INTERNAL will set the player to follow this internal2489* tempo.2490*2491* @warning If the function is called when no MIDI file is loaded or currently playing, it2492* would have caused a division by zero in fluidsynth 2.2.7 and earlier. Starting with 2.2.8, the2493* new tempo change will be stashed and applied later.2494*2495* @return #FLUID_OK if success or #FLUID_FAILED otherwise (incorrect parameters).2496* @since 2.2.02497*/2498int fluid_player_set_tempo(fluid_player_t *player, int tempo_type, double tempo)2499{2500fluid_return_val_if_fail(player != NULL, FLUID_FAILED);2501fluid_return_val_if_fail(tempo_type >= FLUID_PLAYER_TEMPO_INTERNAL, FLUID_FAILED);2502fluid_return_val_if_fail(tempo_type < FLUID_PLAYER_TEMPO_NBR, FLUID_FAILED);25032504switch(tempo_type)2505{2506/* set the player to be driven by internal tempo coming from MIDI file */2507case FLUID_PLAYER_TEMPO_INTERNAL:2508/* check if the multiplier is in correct range */2509fluid_return_val_if_fail(tempo >= MIN_TEMPO_MULTIPLIER, FLUID_FAILED);2510fluid_return_val_if_fail(tempo <= MAX_TEMPO_MULTIPLIER, FLUID_FAILED);25112512/* set the tempo multiplier */2513fluid_atomic_float_set(&player->multempo, (float)tempo);2514fluid_atomic_int_set(&player->sync_mode, 1); /* set internal mode */2515break;25162517/* set the player to be driven by external tempo */2518case FLUID_PLAYER_TEMPO_EXTERNAL_BPM: /* value in bpm */2519case FLUID_PLAYER_TEMPO_EXTERNAL_MIDI: /* value in us/quarter note */2520/* check if tempo is in correct range */2521fluid_return_val_if_fail(tempo >= MIN_TEMPO_VALUE, FLUID_FAILED);2522fluid_return_val_if_fail(tempo <= MAX_TEMPO_VALUE, FLUID_FAILED);25232524/* set the tempo value */2525if(tempo_type == FLUID_PLAYER_TEMPO_EXTERNAL_BPM)2526{2527tempo = 60000000L / tempo; /* convert tempo in us/quarter note */2528}2529fluid_atomic_int_set(&player->exttempo, (int)tempo);2530fluid_atomic_int_set(&player->sync_mode, 0); /* set external mode */2531break;25322533default: /* shouldn't happen */2534break;2535}25362537/* update deltatime depending of current tempo */2538fluid_player_update_tempo(player);25392540return FLUID_OK;2541}25422543/**2544* Set the tempo of a MIDI player.2545* @param player MIDI player instance2546* @param tempo Tempo to set playback speed to (in microseconds per quarter note, as per MIDI file spec)2547* @return Always returns #FLUID_OK2548* @note Tempo change events contained in the MIDI file can override the specified tempo at any time!2549* @deprecated Use fluid_player_set_tempo() instead.2550*/2551int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo)2552{2553player->miditempo = tempo;25542555fluid_player_update_tempo(player);2556return FLUID_OK;2557}25582559/**2560* Set the tempo of a MIDI player in beats per minute.2561* @param player MIDI player instance2562* @param bpm Tempo in beats per minute2563* @return Always returns #FLUID_OK2564* @note Tempo change events contained in the MIDI file can override the specified BPM at any time!2565* @deprecated Use fluid_player_set_tempo() instead.2566*/2567int fluid_player_set_bpm(fluid_player_t *player, int bpm)2568{2569if(bpm <= 0)2570{2571return FLUID_FAILED; /* to avoid a division by 0 */2572}25732574return fluid_player_set_midi_tempo(player, 60000000L / bpm);2575}25762577/**2578* Wait for a MIDI player until the playback has been stopped.2579* @param player MIDI player instance2580* @return Always #FLUID_OK2581*2582* @warning The player may still be used by a concurrently running synth context. Hence it is2583* not safe to immediately delete the player afterwards. Also refer to delete_fluid_player().2584*/2585int2586fluid_player_join(fluid_player_t *player)2587{2588while(fluid_player_get_status(player) != FLUID_PLAYER_DONE)2589{2590fluid_msleep(10);2591}2592return FLUID_OK;2593}25942595/**2596* Get the number of tempo ticks passed.2597* @param player MIDI player instance2598* @return The number of tempo ticks passed2599* @since 1.1.72600*/2601int fluid_player_get_current_tick(fluid_player_t *player)2602{2603return player->cur_ticks;2604}26052606/**2607* Looks through all available MIDI tracks and gets the absolute tick of the very last event to play.2608* @param player MIDI player instance2609* @return Total tick count of the sequence2610* @since 1.1.72611*/2612int fluid_player_get_total_ticks(fluid_player_t *player)2613{2614int i;2615int maxTicks = 0;26162617for(i = 0; i < player->ntracks; i++)2618{2619if(player->track[i] != NULL)2620{2621int ticks = fluid_track_get_duration(player->track[i]);26222623if(ticks > maxTicks)2624{2625maxTicks = ticks;2626}2627}2628}26292630return maxTicks;2631}26322633/**2634* Get the tempo currently used by a MIDI player.2635* The player can be controlled by internal tempo coming from MIDI file tempo2636* change or controlled by external tempo see fluid_player_set_tempo().2637* @param player MIDI player instance. Must be a valid pointer.2638* @return MIDI player tempo in BPM or FLUID_FAILED if error.2639* @since 1.1.72640*/2641int fluid_player_get_bpm(fluid_player_t *player)2642{26432644int midi_tempo = fluid_player_get_midi_tempo(player);26452646if(midi_tempo > 0)2647{2648midi_tempo = 60000000L / midi_tempo; /* convert in bpm */2649}26502651return midi_tempo;2652}26532654/**2655* Get the division currently used by a MIDI player.2656* The player can be controlled by internal tempo coming from MIDI file tempo2657* change or controlled by external tempo see fluid_player_set_tempo().2658* @param player MIDI player instance. Must be a valid pointer.2659* @return MIDI player division or FLUID_FAILED if error.2660* @since 2.3.22661*/2662int fluid_player_get_division(fluid_player_t *player)2663{2664return player->division;2665}26662667/**2668* Get the tempo currently used by a MIDI player.2669* The player can be controlled by internal tempo coming from MIDI file tempo2670* change or controlled by external tempo see fluid_player_set_tempo().26712672* @param player MIDI player instance. Must be a valid pointer.2673* @return Tempo of the MIDI player (in microseconds per quarter note, as per2674* MIDI file spec) or FLUID_FAILED if error.2675* @since 1.1.72676*/2677int fluid_player_get_midi_tempo(fluid_player_t *player)2678{2679int midi_tempo; /* value to return */26802681fluid_return_val_if_fail(player != NULL, FLUID_FAILED);26822683midi_tempo = fluid_atomic_int_get(&player->exttempo);2684/* look if the player is internally synced */2685if(fluid_atomic_int_get(&player->sync_mode))2686{2687midi_tempo = (int)((float)fluid_atomic_int_get(&player->miditempo)/2688fluid_atomic_float_get(&player->multempo));2689}26902691return midi_tempo;2692}26932694/************************************************************************2695* MIDI PARSER2696*2697*/26982699/*2700* new_fluid_midi_parser2701*/2702fluid_midi_parser_t *2703new_fluid_midi_parser(void)2704{2705fluid_midi_parser_t *parser;2706parser = FLUID_NEW(fluid_midi_parser_t);27072708if(parser == NULL)2709{2710FLUID_LOG(FLUID_ERR, "Out of memory");2711return NULL;2712}27132714parser->status = 0; /* As long as the status is 0, the parser won't do anything -> no need to initialize all the fields. */2715return parser;2716}27172718/*2719* delete_fluid_midi_parser2720*/2721void2722delete_fluid_midi_parser(fluid_midi_parser_t *parser)2723{2724fluid_return_if_fail(parser != NULL);27252726FLUID_FREE(parser);2727}27282729/**2730* Parse a MIDI stream one character at a time.2731* @param parser Parser instance2732* @param c Next character in MIDI stream2733* @return A parsed MIDI event or NULL if none. Event is internal and should2734* not be modified or freed and is only valid until next call to this function.2735* @internal Do not expose this function to the public API. It would allow downstream2736* apps to abuse fluidsynth as midi parser, e.g. feeding it with rawmidi and pull out2737* the needed midi information using the getter functions of fluid_midi_event_t.2738* This parser however is incomplete as it e.g. only provides a limited buffer to2739* store and process SYSEX data (i.e. doesn't allow arbitrary lengths)2740*/2741fluid_midi_event_t *2742fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)2743{2744fluid_midi_event_t *event;27452746/* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle2747* of another message. */2748if(c >= 0xF8)2749{2750parser->event.type = c;2751parser->status = 0; /* clear the status */2752return &parser->event;2753}27542755/* Status byte? - If previous message not yet complete, it is discarded (re-sync). */2756if(c & 0x80)2757{2758/* Any status byte terminates SYSEX messages (not just 0xF7) */2759if(parser->status == MIDI_SYSEX && parser->nr_bytes > 0)2760{2761event = &parser->event;2762fluid_midi_event_set_sysex(event, parser->data, parser->nr_bytes,2763FALSE);2764}2765else2766{2767event = NULL;2768}27692770if(c < 0xF0) /* Voice category message? */2771{2772parser->channel = c & 0x0F;2773parser->status = c & 0xF0;27742775/* The event consumes x bytes of data... (subtract 1 for the status byte) */2776parser->nr_bytes_total = fluid_midi_event_length(parser->status)2777- 1;27782779parser->nr_bytes = 0; /* 0 bytes read so far */2780}2781else if(c == MIDI_SYSEX)2782{2783parser->status = MIDI_SYSEX;2784parser->nr_bytes = 0;2785}2786else2787{2788parser->status = 0; /* Discard other system messages (0xF1-0xF7) */2789}27902791return event; /* Return SYSEX event or NULL */2792}27932794/* Data/parameter byte */27952796/* Discard data bytes for events we don't care about */2797if(parser->status == 0)2798{2799return NULL;2800}28012802/* Max data size exceeded? (SYSEX messages only really) */2803if(parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE)2804{2805parser->status = 0; /* Discard the rest of the message */2806return NULL;2807}28082809/* Store next byte */2810parser->data[parser->nr_bytes++] = c;28112812/* Do we still need more data to get this event complete? */2813if(parser->status == MIDI_SYSEX || parser->nr_bytes < parser->nr_bytes_total)2814{2815return NULL;2816}28172818/* Event is complete, return it.2819* Running status byte MIDI feature is also handled here. */2820parser->event.type = parser->status;2821parser->event.channel = parser->channel;2822parser->nr_bytes = 0; /* Reset data size, in case there are additional running status messages */28232824switch(parser->status)2825{2826case NOTE_OFF:2827case NOTE_ON:2828case KEY_PRESSURE:2829case CONTROL_CHANGE:2830case PROGRAM_CHANGE:2831case CHANNEL_PRESSURE:2832parser->event.param1 = parser->data[0]; /* For example key number */2833parser->event.param2 = parser->data[1]; /* For example velocity */2834break;28352836case PITCH_BEND:2837/* Pitch-bend is transmitted with 14-bit precision. */2838parser->event.param1 = (parser->data[1] << 7) | parser->data[0];2839break;28402841default: /* Unlikely */2842return NULL;2843}28442845return &parser->event;2846}28472848/* Purpose:2849* Returns the length of a MIDI message. */2850static int2851fluid_midi_event_length(unsigned char event)2852{2853switch(event & 0xF0)2854{2855case NOTE_OFF:2856case NOTE_ON:2857case KEY_PRESSURE:2858case CONTROL_CHANGE:2859case PITCH_BEND:2860return 3;28612862case PROGRAM_CHANGE:2863case CHANNEL_PRESSURE:2864return 2;2865}28662867switch(event)2868{2869case MIDI_TIME_CODE:2870case MIDI_SONG_SELECT:2871case 0xF4:2872case 0xF5:2873return 2;28742875case MIDI_TUNE_REQUEST:2876return 1;28772878case MIDI_SONG_POSITION:2879return 3;2880}28812882return 1;2883}288428852886