2005-04-15 06:08:55 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
2007-09-24 15:57:32 +00:00
|
|
|
* $Id$
|
2005-04-15 06:08:55 +00:00
|
|
|
*
|
|
|
|
* Copyright (C) 2005 Stepan Moskovchenko
|
|
|
|
*
|
2008-06-28 18:10:04 +00:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2005-04-15 06:08:55 +00:00
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
2007-09-24 15:57:32 +00:00
|
|
|
#include "plugin.h"
|
|
|
|
#include "midiutil.h"
|
2012-02-18 11:06:48 +00:00
|
|
|
#include "midifile.h"
|
2005-04-15 06:08:55 +00:00
|
|
|
|
|
|
|
struct Track * readTrack(int file);
|
|
|
|
int readID(int file);
|
|
|
|
|
2007-10-03 20:38:04 +00:00
|
|
|
struct MIDIfile midi_file IBSS_ATTR;
|
|
|
|
|
2008-05-13 09:57:56 +00:00
|
|
|
struct MIDIfile * loadFile(const char * filename)
|
2005-04-15 06:08:55 +00:00
|
|
|
{
|
2006-05-01 23:22:59 +00:00
|
|
|
struct MIDIfile * mfload;
|
2005-04-20 21:07:13 +00:00
|
|
|
int file = rb->open (filename, O_RDONLY);
|
|
|
|
|
2009-12-06 13:52:28 +00:00
|
|
|
if(file < 0)
|
2005-04-20 21:07:13 +00:00
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Could not open file");
|
2006-05-08 02:43:29 +00:00
|
|
|
return NULL;
|
2005-04-20 21:07:13 +00:00
|
|
|
}
|
|
|
|
|
2007-10-03 20:38:04 +00:00
|
|
|
mfload = &midi_file;
|
2005-04-20 21:07:13 +00:00
|
|
|
|
2006-05-01 23:22:59 +00:00
|
|
|
rb->memset(mfload, 0, sizeof(struct MIDIfile));
|
2005-04-20 21:07:13 +00:00
|
|
|
|
2006-05-08 02:43:29 +00:00
|
|
|
int fileID = readID(file);
|
|
|
|
if(fileID != ID_MTHD)
|
2005-04-20 21:07:13 +00:00
|
|
|
{
|
2006-05-08 02:43:29 +00:00
|
|
|
if(fileID == ID_RIFF)
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Detected RMID file");
|
|
|
|
midi_debug("Looking for MThd header");
|
2006-05-08 02:43:29 +00:00
|
|
|
char dummy[17];
|
|
|
|
rb->read(file, &dummy, 16);
|
|
|
|
if(readID(file) != ID_MTHD)
|
|
|
|
{
|
|
|
|
rb->close(file);
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Invalid MIDI header within RIFF.");
|
2006-05-08 02:43:29 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
rb->close(file);
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Invalid file header chunk.");
|
2006-05-08 02:43:29 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2005-04-20 21:07:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(readFourBytes(file)!=6)
|
|
|
|
{
|
|
|
|
rb->close(file);
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Header chunk size invalid.");
|
2006-05-08 02:43:29 +00:00
|
|
|
return NULL;
|
2005-04-20 21:07:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(readTwoBytes(file)==2)
|
|
|
|
{
|
|
|
|
rb->close(file);
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("MIDI file type 2 not supported");
|
2006-05-08 02:43:29 +00:00
|
|
|
return NULL;
|
2005-04-20 21:07:13 +00:00
|
|
|
}
|
|
|
|
|
2006-05-01 23:22:59 +00:00
|
|
|
mfload->numTracks = readTwoBytes(file);
|
|
|
|
mfload->div = readTwoBytes(file);
|
2005-04-20 21:07:13 +00:00
|
|
|
|
|
|
|
int track=0;
|
|
|
|
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("File has %d tracks.", mfload->numTracks);
|
2005-04-20 21:07:13 +00:00
|
|
|
|
2006-05-01 23:22:59 +00:00
|
|
|
while(! eof(file) && track < mfload->numTracks)
|
2005-04-20 21:07:13 +00:00
|
|
|
{
|
|
|
|
unsigned char id = readID(file);
|
|
|
|
|
|
|
|
|
|
|
|
if(id == ID_EOF)
|
|
|
|
{
|
2006-05-01 23:22:59 +00:00
|
|
|
if(mfload->numTracks != track)
|
2005-04-20 21:07:13 +00:00
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Warning: file claims to have %d tracks. I only see %d here.", mfload->numTracks, track);
|
2006-05-01 23:22:59 +00:00
|
|
|
mfload->numTracks = track;
|
2005-04-20 21:07:13 +00:00
|
|
|
}
|
2008-01-02 06:40:15 +00:00
|
|
|
rb->close(file);
|
2006-05-01 23:22:59 +00:00
|
|
|
return mfload;
|
2005-04-20 21:07:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(id == ID_MTRK)
|
|
|
|
{
|
2006-05-01 23:22:59 +00:00
|
|
|
mfload->tracks[track] = readTrack(file);
|
2005-04-20 21:07:13 +00:00
|
|
|
track++;
|
|
|
|
} else
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("SKIPPING TRACK");
|
2005-04-20 21:07:13 +00:00
|
|
|
int len = readFourBytes(file);
|
|
|
|
while(--len)
|
|
|
|
readChar(file);
|
|
|
|
}
|
|
|
|
}
|
2006-05-03 05:18:18 +00:00
|
|
|
|
|
|
|
rb->close(file);
|
2006-05-01 23:22:59 +00:00
|
|
|
return mfload;
|
2005-04-15 06:08:55 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2006-05-03 05:18:18 +00:00
|
|
|
/* Global again. Not static. What if track 1 ends on a running status event
|
|
|
|
* and then track 2 starts loading */
|
2005-04-15 06:08:55 +00:00
|
|
|
|
2006-05-03 05:18:18 +00:00
|
|
|
int rStatus = 0;
|
2005-04-20 21:07:13 +00:00
|
|
|
/* Returns 0 if done, 1 if keep going */
|
2012-02-18 11:06:48 +00:00
|
|
|
static int readEvent(int file, void * dest)
|
2005-04-15 06:08:55 +00:00
|
|
|
{
|
2005-04-20 21:07:13 +00:00
|
|
|
struct Event dummy;
|
|
|
|
struct Event * ev = (struct Event *) dest;
|
|
|
|
|
|
|
|
if(ev == NULL)
|
|
|
|
ev = &dummy; /* If we are just counting events instead of loading them */
|
|
|
|
|
|
|
|
ev->delta = readVarData(file);
|
|
|
|
|
|
|
|
|
|
|
|
int t=readChar(file);
|
|
|
|
|
|
|
|
if((t&0x80) == 0x80) /* if not a running status event */
|
|
|
|
{
|
|
|
|
ev->status = t;
|
|
|
|
if(t == 0xFF)
|
|
|
|
{
|
|
|
|
ev->d1 = readChar(file);
|
|
|
|
ev->len = readVarData(file);
|
|
|
|
|
|
|
|
/* Allocate and read in the data block */
|
|
|
|
if(dest != NULL)
|
|
|
|
{
|
2008-01-02 07:09:17 +00:00
|
|
|
/* Null-terminate for text events */
|
|
|
|
ev->evData = malloc(ev->len+1); /* Extra byte for the null termination */
|
|
|
|
|
|
|
|
rb->read(file, ev->evData, ev->len);
|
|
|
|
ev->evData[ev->len] = 0;
|
|
|
|
|
|
|
|
switch(ev->d1)
|
|
|
|
{
|
|
|
|
case 0x01: /* Generic text */
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Text: %s", ev->evData);
|
2008-01-02 07:09:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x02: /* A copyright string within the file */
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Copyright: %s", ev->evData);
|
2008-01-02 07:09:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x03: /* Sequence of track name */
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Name: %s", ev->evData);
|
2008-01-02 07:09:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x04: /* Instrument (synth) name */
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Instrument: %s", ev->evData);
|
2008-01-02 07:09:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x05: /* Lyrics. These appear on the tracks at the right times */
|
|
|
|
{ /* Usually only a small 'piece' of the lyrics. */
|
|
|
|
/* Maybe the sequencer should print these at play time? */
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Lyric: %s", ev->evData);
|
2008-01-02 07:09:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x06: /* Text marker */
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Marker: %s", ev->evData);
|
2008-01-02 07:09:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 0x07: /* Cue point */
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Cue point: %s", ev->evData);
|
2008-01-02 07:09:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x08: /* Program name */
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Patch: %s", ev->evData);
|
2008-01-02 07:09:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case 0x09: /* Device name. Very much irrelevant here, though. */
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Port: %s", ev->evData);
|
2008-01-02 07:09:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-04-20 21:07:13 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2006-05-03 05:18:18 +00:00
|
|
|
* Don't allocate anything, just see how much it would take
|
2005-04-20 21:07:13 +00:00
|
|
|
* To make memory usage efficient
|
|
|
|
*/
|
|
|
|
unsigned int a=0;
|
|
|
|
for(a=0; a<ev->len; a++)
|
|
|
|
readChar(file); //Skip skip
|
|
|
|
}
|
|
|
|
|
|
|
|
if(ev->d1 == 0x2F)
|
|
|
|
{
|
|
|
|
return 0; /* Termination meta-event */
|
|
|
|
}
|
|
|
|
} else /* If part of a running status event */
|
|
|
|
{
|
|
|
|
rStatus = t;
|
|
|
|
ev->status = t;
|
|
|
|
ev->d1 = readChar(file);
|
|
|
|
|
|
|
|
if ( ((t & 0xF0) != 0xD0) && ((t & 0xF0) != 0xC0) && ((t & 0xF0) > 0x40) )
|
|
|
|
{
|
|
|
|
ev->d2 = readChar(file);
|
|
|
|
} else
|
|
|
|
ev->d2 = 127;
|
|
|
|
}
|
|
|
|
} else /* Running Status */
|
|
|
|
{
|
|
|
|
ev->status = rStatus;
|
|
|
|
ev->d1 = t;
|
|
|
|
if ( ((rStatus & 0xF0) != 0xD0) && ((rStatus & 0xF0) != 0xC0) && ((rStatus & 0xF0) > 0x40) )
|
|
|
|
{
|
|
|
|
ev->d2 = readChar(file);
|
|
|
|
} else
|
|
|
|
ev->d2 = 127;
|
|
|
|
}
|
|
|
|
return 1;
|
2005-04-15 06:08:55 +00:00
|
|
|
}
|
|
|
|
|
2007-10-04 21:01:40 +00:00
|
|
|
int curr_track = 0;
|
|
|
|
struct Track tracks[48] IBSS_ATTR;
|
|
|
|
|
2005-04-15 06:08:55 +00:00
|
|
|
struct Track * readTrack(int file)
|
|
|
|
{
|
2007-10-04 21:01:40 +00:00
|
|
|
struct Track * trk = &tracks[curr_track++];
|
2005-04-20 21:07:13 +00:00
|
|
|
rb->memset(trk, 0, sizeof(struct Track));
|
2005-04-15 06:08:55 +00:00
|
|
|
|
2005-04-20 21:07:13 +00:00
|
|
|
trk->size = readFourBytes(file);
|
|
|
|
trk->pos = 0;
|
|
|
|
trk->delta = 0;
|
2005-04-15 06:08:55 +00:00
|
|
|
|
2005-04-20 21:07:13 +00:00
|
|
|
int numEvents=0;
|
2005-04-15 06:08:55 +00:00
|
|
|
|
2005-04-20 21:07:13 +00:00
|
|
|
int pos = rb->lseek(file, 0, SEEK_CUR);
|
2005-04-15 06:08:55 +00:00
|
|
|
|
2005-04-20 21:07:13 +00:00
|
|
|
while(readEvent(file, NULL)) /* Memory saving technique */
|
|
|
|
numEvents++; /* Attempt to read in events, count how many */
|
|
|
|
/* THEN allocate memory and read them in */
|
|
|
|
rb->lseek(file, pos, SEEK_SET);
|
2005-04-15 06:08:55 +00:00
|
|
|
|
2005-04-20 21:07:13 +00:00
|
|
|
int trackSize = (numEvents+1) * sizeof(struct Event);
|
2006-05-01 23:22:59 +00:00
|
|
|
void * dataPtr = malloc(trackSize);
|
2005-04-20 21:07:13 +00:00
|
|
|
trk->dataBlock = dataPtr;
|
2005-04-15 06:08:55 +00:00
|
|
|
|
2005-04-20 21:07:13 +00:00
|
|
|
numEvents=0;
|
2005-04-15 06:08:55 +00:00
|
|
|
|
2005-04-20 21:07:13 +00:00
|
|
|
while(readEvent(file, dataPtr))
|
|
|
|
{
|
|
|
|
if(trackSize < dataPtr-trk->dataBlock)
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("Track parser memory out of bounds");
|
2005-04-20 21:07:13 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
dataPtr+=sizeof(struct Event);
|
|
|
|
numEvents++;
|
|
|
|
}
|
|
|
|
trk->numEvents = numEvents;
|
2005-04-15 06:08:55 +00:00
|
|
|
|
2005-04-20 21:07:13 +00:00
|
|
|
return trk;
|
2005-04-15 06:08:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int readID(int file)
|
|
|
|
{
|
2005-04-20 21:07:13 +00:00
|
|
|
char id[5];
|
|
|
|
id[4]=0;
|
|
|
|
BYTE a;
|
|
|
|
|
|
|
|
for(a=0; a<4; a++)
|
|
|
|
id[a]=readChar(file);
|
|
|
|
if(eof(file))
|
|
|
|
{
|
2010-07-25 19:47:07 +00:00
|
|
|
midi_debug("End of file reached.");
|
2005-04-20 21:07:13 +00:00
|
|
|
return ID_EOF;
|
|
|
|
}
|
|
|
|
if(rb->strcmp(id, "MThd")==0)
|
|
|
|
return ID_MTHD;
|
|
|
|
if(rb->strcmp(id, "MTrk")==0)
|
|
|
|
return ID_MTRK;
|
2006-05-08 02:43:29 +00:00
|
|
|
if(rb->strcmp(id, "RIFF")==0)
|
|
|
|
return ID_RIFF;
|
2005-04-20 21:07:13 +00:00
|
|
|
return ID_UNKNOWN;
|
2005-04-15 06:08:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int readFourBytes(int file)
|
|
|
|
{
|
2005-04-20 21:07:13 +00:00
|
|
|
int data=0;
|
|
|
|
BYTE a=0;
|
|
|
|
for(a=0; a<4; a++)
|
|
|
|
data=(data<<8)+readChar(file);
|
|
|
|
return data;
|
2005-04-15 06:08:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int readTwoBytes(int file)
|
|
|
|
{
|
2005-04-20 21:07:13 +00:00
|
|
|
int data=(readChar(file)<<8)+readChar(file);
|
|
|
|
return data;
|
2005-04-15 06:08:55 +00:00
|
|
|
}
|
|
|
|
|
2005-04-20 21:07:13 +00:00
|
|
|
/* This came from the MIDI file format guide */
|
2005-04-15 06:08:55 +00:00
|
|
|
int readVarData(int file)
|
|
|
|
{
|
|
|
|
unsigned int value;
|
|
|
|
char c;
|
|
|
|
if ( (value = readChar(file)) & 0x80 )
|
|
|
|
{
|
|
|
|
value &= 0x7F;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
value = (value << 7) + ((c = readChar(file)) & 0x7F);
|
|
|
|
} while (c & 0x80);
|
|
|
|
}
|
|
|
|
return(value);
|
|
|
|
}
|
|
|
|
|
2005-04-15 20:27:04 +00:00
|
|
|
|
|
|
|
/*
|
2005-04-15 06:08:55 +00:00
|
|
|
void unloadFile(struct MIDIfile * mf)
|
|
|
|
{
|
2005-04-20 21:07:13 +00:00
|
|
|
if(mf == NULL)
|
|
|
|
return;
|
|
|
|
int a=0;
|
|
|
|
//Unload each track
|
|
|
|
for(a=0; a<mf->numTracks; a++)
|
|
|
|
{
|
|
|
|
int b=0;
|
|
|
|
|
|
|
|
if(mf->tracks[a] != NULL)
|
|
|
|
for(b=0; b<mf->tracks[a]->numEvents; b++)
|
|
|
|
{
|
|
|
|
if(((struct Event*)((mf->tracks[a]->dataBlock)+b*sizeof(struct Event)))->evData!=NULL)
|
|
|
|
free(((struct Event*)((mf->tracks[a]->dataBlock)+b*sizeof(struct Event)))->evData);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(mf->tracks[a]!=NULL && mf->tracks[a]->dataBlock != NULL)
|
|
|
|
free(mf->tracks[a]->dataBlock); //Unload the event block
|
|
|
|
|
|
|
|
if(mf->tracks[a]!=NULL)
|
|
|
|
free(mf->tracks[a]); //Unload the track structure itself
|
|
|
|
}
|
|
|
|
free(mf); //Unload the main struct
|
2005-04-15 06:08:55 +00:00
|
|
|
}
|
2005-04-15 20:27:04 +00:00
|
|
|
*/
|
2006-05-08 02:43:29 +00:00
|
|
|
|