ref: c73268a2551636b16dbae7652697361fed4e6e9f
dir: /ext/midi/midi_file.c/
/*
*
* 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;
}