/* * * 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 * 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. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307, USA */ /* this file originally taken from FluidSynth - A Software Synthesizer * Copyright (C) 2003 Peter Hanappe and others. * * \note fixed some track names causing segfault */ #include "aubio_priv.h" #include "midi.h" #include "midi_event.h" #include "midi_track.h" #include "midi_player.h" #include "midi_file.h" /** aubio_midi_file */ struct _aubio_midi_file_t{ //aubio_file_t fp; FILE *fp; int running_status; int c; int type; int ntracks; int uses_smpte; unsigned int smpte_fps; unsigned int smpte_res; unsigned int division; /* If uses_SMPTE == 0 then division is ticks per beat (quarter-note) */ double tempo; /* Beats per second (SI rules =) */ int tracklen; int trackpos; int eot; int varlen; }; /*************************************************************** * * MIDIFILE */ /** new_aubio_midi_file */ aubio_midi_file_t * new_aubio_midi_file(char* filename) { aubio_midi_file_t* mf; mf = AUBIO_NEW(aubio_midi_file_t); if (mf == NULL) { AUBIO_ERR( "Out of memory"); return NULL; } AUBIO_MEMSET(mf, 0, sizeof(aubio_midi_file_t)); mf->c = -1; mf->running_status = -1; mf->fp = AUBIO_FOPEN(filename, "rb"); if (mf->fp == NULL) { AUBIO_ERR( "Couldn't open the MIDI file !\n"); AUBIO_FREE(mf); return NULL; } if (aubio_midi_file_read_mthd(mf) != AUBIO_OK) { AUBIO_FREE(mf); return NULL; } return mf; } /** del_aubio_midi_file */ void del_aubio_midi_file(aubio_midi_file_t* mf) { if (mf == NULL) { return; } if (mf->fp != NULL) { AUBIO_FCLOSE(mf->fp); } AUBIO_FREE(mf); return; } /** aubio_midi_file_getc */ int aubio_midi_file_getc(aubio_midi_file_t* mf) { unsigned char c; int n; if (mf->c >= 0) { c = mf->c; mf->c = -1; } else { n = AUBIO_FREAD(&c, 1, 1, mf->fp); mf->trackpos++; } return (int) c; } /** aubio_midi_file_push */ int aubio_midi_file_push(aubio_midi_file_t* mf, int c) { mf->c = c; return AUBIO_OK; } /** aubio_midi_file_read */ int aubio_midi_file_read(aubio_midi_file_t* mf, void* buf, int len) { int num = AUBIO_FREAD(buf, 1, len, mf->fp); mf->trackpos += num; #if DEBUG if (num != len) { AUBIO_DBG( "Coulnd't read the requested number of bytes"); } #endif return (num != len)? AUBIO_FAIL : AUBIO_OK; } /** aubio_midi_file_skip */ int aubio_midi_file_skip(aubio_midi_file_t* mf, int skip) { int err = AUBIO_FSEEK(mf->fp, skip, SEEK_CUR); if (err) { AUBIO_ERR( "FAIL to seek position in file"); return AUBIO_FAIL; } return AUBIO_OK; } /** aubio_midi_file_read_mthd */ int aubio_midi_file_read_mthd(aubio_midi_file_t* mf) { signed char mthd[15]; if (aubio_midi_file_read(mf, mthd, 14) != AUBIO_OK) { return AUBIO_FAIL; } if ((AUBIO_STRNCMP((const char*)mthd, "MThd", 4) != 0) || (mthd[7] != 6) || (mthd[9] > 2)) { AUBIO_ERR( "Doesn't look like a MIDI file: invalid MThd header"); return AUBIO_FAIL; } mf->type = mthd[9]; mf->ntracks = (unsigned) mthd[11]; mf->ntracks += (unsigned int) (mthd[10]) << 16; /** \bug: smpte timing not yet implemented */ if((int)(mthd[12]) < 0){ /*if((mthd[12]) < 0){*/ mf->uses_smpte = 1; mf->smpte_fps = -mthd[12]; mf->smpte_res = (unsigned) mthd[13]; AUBIO_ERR( "File uses SMPTE timing -- Not implemented yet"); return AUBIO_FAIL; } else { mf->uses_smpte = 0; mf->division = (mthd[12] << 8) | (mthd[13] & 0xff); } return AUBIO_OK; } /** aubio_midi_file_load_tracks */ int aubio_midi_file_load_tracks(aubio_midi_file_t* mf, aubio_midi_player_t* player) { int i; for (i = 0; i < mf->ntracks; i++) { if (aubio_midi_file_read_track(mf, player, i) != AUBIO_OK) { return AUBIO_FAIL; } } return AUBIO_OK; } /** aubio_midi_file_read_tracklen */ int aubio_midi_file_read_tracklen(aubio_midi_file_t* mf) { unsigned char length[5]; if (aubio_midi_file_read(mf, length, 4) != AUBIO_OK) { return AUBIO_FAIL; } mf->tracklen = aubio_getlength(length); mf->trackpos = 0; mf->eot = 0; return AUBIO_OK; } /** aubio_midi_file_eot */ int aubio_midi_file_eot(aubio_midi_file_t* mf) { #if DEBUG if (mf->trackpos > mf->tracklen) { printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen); } #endif return mf->eot || (mf->trackpos >= mf->tracklen); } /** aubio_midi_file_read_track */ int aubio_midi_file_read_track(aubio_midi_file_t* mf, aubio_midi_player_t* player, int num) { aubio_track_t* track; unsigned char id[5], length[5]; int found_track = 0; int skip; AUBIO_DBG("Loading track %d\n",num); if (aubio_midi_file_read(mf, id, 4) != AUBIO_OK) { AUBIO_DBG("Failed loading track %d\n",num); return AUBIO_FAIL; } id[4]='\0'; while (!found_track){ if (aubio_isasciistring((char*) id) == 0) { AUBIO_ERR( "An non-ascii track header found, currupt file"); return AUBIO_FAIL; } else if (strcmp((char*) id, "MTrk") == 0) { found_track = 1; if (aubio_midi_file_read_tracklen(mf) != AUBIO_OK) { return AUBIO_FAIL; } track = new_aubio_track(num); if (track == NULL) { AUBIO_ERR( "Out of memory"); return AUBIO_FAIL; } while (!aubio_midi_file_eot(mf)) { if (aubio_midi_file_read_event(mf, track) != AUBIO_OK) { return AUBIO_FAIL; } } aubio_midi_player_add_track(player, track); } else { found_track = 0; if (aubio_midi_file_read(mf, length, 4) != AUBIO_OK) { return AUBIO_FAIL; } skip = aubio_getlength(length); /* fseek(mf->fp, skip, SEEK_CUR); */ if (aubio_midi_file_skip(mf, skip) != AUBIO_OK) { return AUBIO_FAIL; } } } if (feof(mf->fp)) { AUBIO_ERR( "Unexpected end of file"); return AUBIO_FAIL; } AUBIO_DBG("Loaded track %d\n",num); return AUBIO_OK; } /** aubio_midi_file_read_varlen */ int aubio_midi_file_read_varlen(aubio_midi_file_t* mf) { int i; int c; mf->varlen = 0; for (i = 0;;i++) { if (i == 4) { AUBIO_ERR( "Invalid variable length number"); return AUBIO_FAIL; } c = aubio_midi_file_getc(mf); if (c < 0) { AUBIO_ERR( "Unexpected end of file"); return AUBIO_FAIL; } if (c & 0x80){ mf->varlen |= (int) (c & 0x7F); mf->varlen <<= 7; } else { mf->varlen += c; break; } } return AUBIO_OK; } /** aubio_midi_file_read_event */ int aubio_midi_file_read_event(aubio_midi_file_t* mf, aubio_track_t* track) { int dtime; int status; int type; int tempo; unsigned char* metadata = NULL; unsigned char* dyn_buf = NULL; unsigned char static_buf[256]; int nominator, denominator, clocks, notes, sf, mi; aubio_midi_event_t* evt; int channel = 0; int param1 = 0; int param2 = 0; /* read the delta-time of the event */ if (aubio_midi_file_read_varlen(mf) != AUBIO_OK) { return AUBIO_FAIL; } dtime = mf->varlen; /* read the status byte */ status = aubio_midi_file_getc(mf); if (status < 0) { AUBIO_ERR( "Unexpected end of file"); return AUBIO_FAIL; } /* not a valid status byte: use the running status instead */ if ((status & 0x80) == 0) { if ((mf->running_status & 0x80) == 0) { AUBIO_ERR( "Undefined status and invalid running status"); return AUBIO_FAIL; } aubio_midi_file_push(mf, status); status = mf->running_status; } /* check what message we have */ if (status & 0x80) { mf->running_status = status; if ((status == MIDI_SYSEX) || (status == MIDI_EOX)) { /* system exclusif */ /** \bug Sysex messages are not handled yet */ /* read the length of the message */ if (aubio_midi_file_read_varlen(mf) != AUBIO_OK) { return AUBIO_FAIL; } if (mf->varlen < 255) { metadata = &static_buf[0]; } else { AUBIO_DBG( "%s: %d: alloc metadata, len = %d", __FILE__, __LINE__, mf->varlen); dyn_buf = AUBIO_MALLOC(mf->varlen + 1); if (dyn_buf == NULL) { //AUBIO_LOG(AUBIO_PANIC, "Out of memory"); AUBIO_ERR("Out of memory"); return AUBIO_FAIL; } metadata = dyn_buf; } /* read the data of the message */ if (mf->varlen) { if (aubio_midi_file_read(mf, metadata, mf->varlen) != AUBIO_OK) { if (dyn_buf) { AUBIO_FREE(dyn_buf); } return AUBIO_FAIL; } if (dyn_buf) { AUBIO_DBG( "%s: %d: free metadata", __FILE__, __LINE__); AUBIO_FREE(dyn_buf); } } return AUBIO_OK; } else if (status == MIDI_META_EVENT) { /* meta events */ int result = AUBIO_OK; /* get the type of the meta message */ type = aubio_midi_file_getc(mf); if (type < 0) { AUBIO_ERR( "Unexpected end of file"); return AUBIO_FAIL; } /* get the length of the data part */ if (aubio_midi_file_read_varlen(mf) != AUBIO_OK) { return AUBIO_FAIL; } if (mf->varlen) { if (mf->varlen < 255) { metadata = &static_buf[0]; } else { AUBIO_DBG( "%s: %d: alloc metadata, len = %d", __FILE__, __LINE__, mf->varlen); dyn_buf = AUBIO_MALLOC(mf->varlen + 1); if (dyn_buf == NULL) { AUBIO_ERR("Out of memory"); return AUBIO_FAIL; } metadata = dyn_buf; } /* read the data */ if (aubio_midi_file_read(mf, metadata, mf->varlen) != AUBIO_OK) { if (dyn_buf) { AUBIO_FREE(dyn_buf); } return AUBIO_FAIL; } } /* handle meta data */ switch (type) { case MIDI_COPYRIGHT: metadata[mf->varlen] = 0; break; case MIDI_TRACK_NAME: if (metadata != NULL) /* avoids crashes on empty tracks */ metadata[mf->varlen] = 0; aubio_track_set_name(track, (char*) metadata); break; case MIDI_INST_NAME: metadata[mf->varlen] = 0; break; case MIDI_LYRIC: break; case MIDI_MARKER: break; case MIDI_CUE_POINT: break; /* don't care much for text events */ case MIDI_EOT: if (mf->varlen != 0) { AUBIO_ERR("Invalid length for EndOfTrack event"); result = AUBIO_FAIL; break; } mf->eot = 1; break; case MIDI_SET_TEMPO: if (mf->varlen != 3) { AUBIO_ERR("Invalid length for SetTempo meta event"); result = AUBIO_FAIL; break; } tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2]; evt = new_aubio_midi_event(); if (evt == NULL) { AUBIO_ERR( "Out of memory"); result = AUBIO_FAIL; break; } evt->dtime = dtime; evt->type = MIDI_SET_TEMPO; evt->channel = 0; evt->param1 = tempo; evt->param2 = 0; aubio_track_add_event(track, evt); break; case MIDI_SMPTE_OFFSET: if (mf->varlen != 5) { AUBIO_ERR("Invalid length for SMPTE Offset meta event"); result = AUBIO_FAIL; break; } break; /* we don't use smtp */ case MIDI_TIME_SIGNATURE: if (mf->varlen != 4) { AUBIO_ERR("Invalid length for TimeSignature meta event"); result = AUBIO_FAIL; break; } nominator = metadata[0]; denominator = pow(2.0, (double) metadata[1]); clocks = metadata[2]; notes = metadata[3]; AUBIO_DBG("signature=%d/%d, metronome=%d, 32nd-notes=%d\n", nominator, denominator, clocks, notes); break; case MIDI_KEY_SIGNATURE: if (mf->varlen != 2) { AUBIO_ERR( "Invalid length for KeySignature meta event"); result = AUBIO_FAIL; break; } sf = metadata[0]; mi = metadata[1]; break; case MIDI_SEQUENCER_EVENT: AUBIO_DBG("Sequencer event ignored\n"); break; default: break; } if (dyn_buf) { AUBIO_DBG( "%s: %d: free metadata", __FILE__, __LINE__); AUBIO_FREE(dyn_buf); } return result; } else { /* channel messages */ type = status & 0xf0; channel = status & 0x0f; /* all channel message have at least 1 byte of associated data */ if ((param1 = aubio_midi_file_getc(mf)) < 0) { AUBIO_ERR( "Unexpected end of file"); return AUBIO_FAIL; } switch (type) { case NOTE_ON: if ((param2 = aubio_midi_file_getc(mf)) < 0) { AUBIO_ERR( "Unexpected end of file"); return AUBIO_FAIL; } break; case NOTE_OFF: if ((param2 = aubio_midi_file_getc(mf)) < 0) { AUBIO_ERR( "Unexpected end of file"); return AUBIO_FAIL; } break; case KEY_PRESSURE: if ((param2 = aubio_midi_file_getc(mf)) < 0) { AUBIO_ERR( "Unexpected end of file"); return AUBIO_FAIL; } break; case CONTROL_CHANGE: if ((param2 = aubio_midi_file_getc(mf)) < 0) { AUBIO_ERR( "Unexpected end of file"); return AUBIO_FAIL; } break; case PROGRAM_CHANGE: break; case CHANNEL_PRESSURE: break; case PITCH_BEND: if ((param2 = aubio_midi_file_getc(mf)) < 0) { AUBIO_ERR( "Unexpected end of file"); return AUBIO_FAIL; } param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f); param2 = 0; break; default: /* Can't possibly happen !? */ AUBIO_ERR( "Unrecognized MIDI event"); return AUBIO_FAIL; } evt = new_aubio_midi_event(); if (evt == NULL) { AUBIO_ERR( "Out of memory"); return AUBIO_FAIL; } evt->dtime = dtime; evt->type = type; evt->channel = channel; evt->param1 = param1; evt->param2 = param2; aubio_track_add_event(track, evt); } } return AUBIO_OK; } /** aubio_midi_file_get_division */ int aubio_midi_file_get_division(aubio_midi_file_t* midifile) { return midifile->division; } /** aubio_isasciistring */ int aubio_isasciistring(char* s) { int i; int len = (int) AUBIO_STRLEN(s); for (i = 0; i < len; i++) { if (!aubio_isascii(s[i])) { return 0; } } return 1; } /** aubio_getlength */ long aubio_getlength(unsigned char *s) { long i = 0; i = s[3] | (s[2]<<8) | (s[1]<<16) | (s[0]<<24); return i; }