mirror of
https://github.com/cookiengineer/audacity
synced 2026-04-24 23:13:42 +02:00
Move library tree where it belongs
This commit is contained in:
486
lib-src/portsmf/mfmidi.cpp
Normal file
486
lib-src/portsmf/mfmidi.cpp
Normal file
@@ -0,0 +1,486 @@
|
||||
/*
|
||||
* Read a Standard MIDI File. Externally-assigned function pointers are
|
||||
* called upon recognizing things in the file. See midifile(3).
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Change Log
|
||||
* Date | who : Change
|
||||
*-----------+-----------------------------------------------------------------
|
||||
* 2-Mar-92 | GWL : created changelog; MIDIFILE_ERROR to satisfy compiler
|
||||
*****************************************************************************/
|
||||
|
||||
#include "stdio.h"
|
||||
#include "mfmidi.h"
|
||||
#include "string.h"
|
||||
#include "assert.h"
|
||||
|
||||
#define MIDIFILE_ERROR -1
|
||||
|
||||
/* public stuff */
|
||||
extern int abort_flag;
|
||||
|
||||
|
||||
void Midifile_reader::midifile()
|
||||
{
|
||||
int ntrks;
|
||||
midifile_error = 0;
|
||||
|
||||
ntrks = readheader();
|
||||
if (midifile_error) return;
|
||||
if (ntrks <= 0) {
|
||||
mferror("No tracks!");
|
||||
/* no need to return since midifile_error is set */
|
||||
}
|
||||
while (ntrks-- > 0 && !midifile_error) readtrack();
|
||||
}
|
||||
|
||||
int Midifile_reader::readmt(char *s, int skip)
|
||||
/* read through the "MThd" or "MTrk" header string */
|
||||
/* if skip == 1, we attempt to skip initial garbage. */
|
||||
{
|
||||
assert(strlen(s) == 4); // must be "MThd" or "MTrk"
|
||||
int nread = 0;
|
||||
char b[4];
|
||||
char buff[32];
|
||||
int c;
|
||||
char *errmsg = "expecting ";
|
||||
|
||||
retry:
|
||||
while ( nread<4 ) {
|
||||
c = Mf_getc();
|
||||
if ( c == EOF ) {
|
||||
errmsg = "EOF while expecting ";
|
||||
goto err;
|
||||
}
|
||||
b[nread++] = c;
|
||||
}
|
||||
/* See if we found the 4 characters we're looking for */
|
||||
if ( s[0]==b[0] && s[1]==b[1] && s[2]==b[2] && s[3]==b[3] )
|
||||
return(0);
|
||||
if ( skip ) {
|
||||
/* If we are supposed to skip initial garbage, */
|
||||
/* try again with the next character. */
|
||||
b[0]=b[1];
|
||||
b[1]=b[2];
|
||||
b[2]=b[3];
|
||||
nread = 3;
|
||||
goto retry;
|
||||
}
|
||||
err:
|
||||
#pragma warning(disable: 4996) // strcpy is safe since strings have known lengths
|
||||
(void) strcpy(buff,errmsg);
|
||||
(void) strcat(buff,s);
|
||||
#pragma warning(default: 4996) // turn it back on
|
||||
mferror(buff);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int Midifile_reader::egetc()
|
||||
/* read a single character and abort on EOF */
|
||||
{
|
||||
int c = Mf_getc();
|
||||
|
||||
if ( c == EOF ) {
|
||||
mferror("premature EOF");
|
||||
return EOF;
|
||||
}
|
||||
Mf_toberead--;
|
||||
return(c);
|
||||
}
|
||||
|
||||
int Midifile_reader::readheader()
|
||||
/* read a header chunk */
|
||||
{
|
||||
int format, ntrks, division;
|
||||
|
||||
if ( readmt("MThd",Mf_skipinit) == EOF )
|
||||
return(0);
|
||||
|
||||
Mf_toberead = read32bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
format = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
ntrks = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
division = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
|
||||
Mf_header(format,ntrks,division);
|
||||
|
||||
/* flush any extra stuff, in case the length of header is not 6 */
|
||||
while ( Mf_toberead > 0 && !midifile_error)
|
||||
(void) egetc();
|
||||
return(ntrks);
|
||||
}
|
||||
|
||||
void Midifile_reader::readtrack()
|
||||
/* read a track chunk */
|
||||
{
|
||||
/* This array is indexed by the high half of a status byte. It's */
|
||||
/* value is either the number of bytes needed (1 or 2) for a channel */
|
||||
/* message, or 0 (meaning it's not a channel message). */
|
||||
static int chantype[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 through 0x70 */
|
||||
2, 2, 2, 2, 1, 1, 2, 0 /* 0x80 through 0xf0 */
|
||||
};
|
||||
long lookfor, lng;
|
||||
int c, c1, type;
|
||||
int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */
|
||||
int running = 0; /* 1 when running status used */
|
||||
int status = 0; /* (possibly running) status byte */
|
||||
int needed;
|
||||
|
||||
if ( readmt("MTrk",0) == EOF )
|
||||
return;
|
||||
|
||||
Mf_toberead = read32bit();
|
||||
|
||||
if (midifile_error) return;
|
||||
|
||||
Mf_currtime = 0L;
|
||||
|
||||
Mf_starttrack();
|
||||
|
||||
while ( Mf_toberead > 0 ) {
|
||||
|
||||
Mf_currtime += readvarinum(); /* delta time */
|
||||
if (midifile_error) return;
|
||||
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
|
||||
if ( sysexcontinue && c != 0xf7 ) {
|
||||
mferror("didn't find expected continuation of a sysex");
|
||||
return;
|
||||
}
|
||||
if ( (c & 0x80) == 0 ) { /* running status? */
|
||||
if ( status == 0 ) {
|
||||
mferror("unexpected running status");
|
||||
return;
|
||||
}
|
||||
running = 1;
|
||||
} else {
|
||||
status = c;
|
||||
running = 0;
|
||||
}
|
||||
|
||||
needed = chantype[ (status>>4) & 0xf ];
|
||||
|
||||
if ( needed ) { /* ie. is it a channel message? */
|
||||
|
||||
if ( running )
|
||||
c1 = c;
|
||||
else {
|
||||
c1 = egetc();
|
||||
if (midifile_error) return;
|
||||
}
|
||||
chanmessage( status, c1, (needed>1) ? egetc() : 0 );
|
||||
if (midifile_error) return;
|
||||
continue;;
|
||||
}
|
||||
|
||||
switch ( c ) {
|
||||
|
||||
case 0xff: /* meta event */
|
||||
|
||||
type = egetc();
|
||||
if (midifile_error) return;
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
msginit();
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
unsigned char c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
metaevent(type);
|
||||
break;
|
||||
|
||||
case 0xf0: /* start of system exclusive */
|
||||
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
msginit();
|
||||
msgadd(0xf0);
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
if ( c==0xf7 || Mf_nomerge==0 )
|
||||
sysex();
|
||||
else
|
||||
sysexcontinue = 1; /* merge into next msg */
|
||||
break;
|
||||
|
||||
case 0xf7: /* sysex continuation or arbitrary stuff */
|
||||
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
|
||||
if ( ! sysexcontinue )
|
||||
msginit();
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
if ( ! sysexcontinue ) {
|
||||
Mf_arbitrary(msgleng(), msg());
|
||||
}
|
||||
else if ( c == 0xf7 ) {
|
||||
sysex();
|
||||
sysexcontinue = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
badbyte(c);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
Mf_endtrack();
|
||||
return;
|
||||
}
|
||||
|
||||
void Midifile_reader::badbyte(int c)
|
||||
{
|
||||
char buff[32];
|
||||
#pragma warning(disable: 4996) // safe in this case
|
||||
(void) sprintf(buff,"unexpected byte: 0x%02x",c);
|
||||
#pragma warning(default: 4996)
|
||||
mferror(buff);
|
||||
}
|
||||
|
||||
void Midifile_reader::metaevent(int type)
|
||||
{
|
||||
int leng = msgleng();
|
||||
// made this unsigned to avoid sign extend
|
||||
unsigned char *m = msg();
|
||||
|
||||
switch ( type ) {
|
||||
case 0x00:
|
||||
Mf_seqnum(to16bit(m[0],m[1]));
|
||||
break;
|
||||
case 0x01: /* Text event */
|
||||
case 0x02: /* Copyright notice */
|
||||
case 0x03: /* Sequence/Track name */
|
||||
case 0x04: /* Instrument name */
|
||||
case 0x05: /* Lyric */
|
||||
case 0x06: /* Marker */
|
||||
case 0x07: /* Cue point */
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
case 0x0a:
|
||||
case 0x0b:
|
||||
case 0x0c:
|
||||
case 0x0d:
|
||||
case 0x0e:
|
||||
case 0x0f:
|
||||
/* These are all text events */
|
||||
Mf_text(type,leng,m);
|
||||
break;
|
||||
case 0x20:
|
||||
Mf_chanprefix(m[0]);
|
||||
break;
|
||||
case 0x21:
|
||||
Mf_portprefix(m[0]);
|
||||
break;
|
||||
case 0x2f: /* End of Track */
|
||||
Mf_eot();
|
||||
break;
|
||||
case 0x51: /* Set tempo */
|
||||
Mf_tempo(to32bit(0,m[0],m[1],m[2]));
|
||||
break;
|
||||
case 0x54:
|
||||
Mf_smpte(m[0],m[1],m[2],m[3],m[4]);
|
||||
break;
|
||||
case 0x58:
|
||||
Mf_timesig(m[0],m[1],m[2],m[3]);
|
||||
break;
|
||||
case 0x59:
|
||||
Mf_keysig(m[0],m[1]);
|
||||
break;
|
||||
case 0x7f:
|
||||
Mf_sqspecific(leng,m);
|
||||
break;
|
||||
default:
|
||||
Mf_metamisc(type,leng,m);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::sysex()
|
||||
{
|
||||
Mf_sysex(msgleng(), msg());
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::chanmessage(int status, int c1, int c2)
|
||||
{
|
||||
int chan = status & 0xf;
|
||||
|
||||
switch ( status & 0xf0 ) {
|
||||
case NOTEOFF:
|
||||
Mf_off(chan,c1,c2);
|
||||
break;
|
||||
case NOTEON:
|
||||
Mf_on(chan,c1,c2);
|
||||
break;
|
||||
case PRESSURE:
|
||||
Mf_pressure(chan,c1,c2);
|
||||
break;
|
||||
case CONTROLLER:
|
||||
Mf_controller(chan,c1,c2);
|
||||
break;
|
||||
case PITCHBEND:
|
||||
Mf_pitchbend(chan,c1,c2);
|
||||
break;
|
||||
case PROGRAM:
|
||||
Mf_program(chan,c1);
|
||||
break;
|
||||
case CHANPRESSURE:
|
||||
Mf_chanpressure(chan,c1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* readvarinum - read a varying-length number, and return the */
|
||||
/* number of characters it took. */
|
||||
|
||||
long Midifile_reader::readvarinum()
|
||||
{
|
||||
long value;
|
||||
int c;
|
||||
|
||||
c = egetc();
|
||||
if (midifile_error) return 0;
|
||||
|
||||
value = (long) c;
|
||||
if ( c & 0x80 ) {
|
||||
value &= 0x7f;
|
||||
do {
|
||||
c = egetc();
|
||||
if (midifile_error) return 0;
|
||||
value = (value << 7) + (c & 0x7f);
|
||||
} while (c & 0x80);
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
long Midifile_reader::to32bit(int c1, int c2, int c3, int c4)
|
||||
{
|
||||
long value = 0L;
|
||||
|
||||
value = (c1 & 0xff);
|
||||
value = (value<<8) + (c2 & 0xff);
|
||||
value = (value<<8) + (c3 & 0xff);
|
||||
value = (value<<8) + (c4 & 0xff);
|
||||
return (value);
|
||||
}
|
||||
|
||||
int Midifile_reader::to16bit(int c1, int c2)
|
||||
{
|
||||
return ((c1 & 0xff ) << 8) + (c2 & 0xff);
|
||||
}
|
||||
|
||||
long Midifile_reader::read32bit()
|
||||
{
|
||||
int c1, c2, c3, c4;
|
||||
|
||||
c1 = egetc(); if (midifile_error) return 0;
|
||||
c2 = egetc(); if (midifile_error) return 0;
|
||||
c3 = egetc(); if (midifile_error) return 0;
|
||||
c4 = egetc(); if (midifile_error) return 0;
|
||||
return to32bit(c1,c2,c3,c4);
|
||||
}
|
||||
|
||||
int Midifile_reader::read16bit()
|
||||
{
|
||||
int c1, c2;
|
||||
c1 = egetc(); if (midifile_error) return 0;
|
||||
c2 = egetc(); if (midifile_error) return 0;
|
||||
return to16bit(c1,c2);
|
||||
}
|
||||
|
||||
void Midifile_reader::mferror(char *s)
|
||||
{
|
||||
Mf_error(s);
|
||||
midifile_error = 1;
|
||||
}
|
||||
|
||||
/* The code below allows collection of a system exclusive message of */
|
||||
/* arbitrary length. The Msgbuff is expanded as necessary. The only */
|
||||
/* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */
|
||||
|
||||
#define MSGINCREMENT 128
|
||||
|
||||
Midifile_reader::Midifile_reader()
|
||||
{
|
||||
Mf_nomerge = 0;
|
||||
Mf_currtime = 0L;
|
||||
Mf_skipinit = 0;
|
||||
Mf_toberead = 0;
|
||||
|
||||
Msgbuff = 0; /* message buffer */
|
||||
Msgsize = 0; /* Size of currently allocated Msg */
|
||||
Msgindex = 0; /* index of next available location in Msg */
|
||||
}
|
||||
|
||||
void Midifile_reader::finalize()
|
||||
{
|
||||
if (Msgbuff) Mf_free(Msgbuff, Msgsize);
|
||||
Msgbuff = NULL;
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::msginit()
|
||||
{
|
||||
Msgindex = 0;
|
||||
}
|
||||
|
||||
unsigned char *Midifile_reader::msg()
|
||||
{
|
||||
return(Msgbuff);
|
||||
}
|
||||
|
||||
int Midifile_reader::msgleng()
|
||||
{
|
||||
return(Msgindex);
|
||||
}
|
||||
|
||||
void Midifile_reader::msgadd(int c)
|
||||
{
|
||||
/* If necessary, allocate larger message buffer. */
|
||||
if ( Msgindex >= Msgsize )
|
||||
msgenlarge();
|
||||
Msgbuff[Msgindex++] = c;
|
||||
}
|
||||
|
||||
void Midifile_reader::msgenlarge()
|
||||
{
|
||||
unsigned char *newmess;
|
||||
unsigned char *oldmess = Msgbuff;
|
||||
int oldleng = Msgsize;
|
||||
|
||||
Msgsize += MSGINCREMENT;
|
||||
newmess = (unsigned char *) Mf_malloc((sizeof(unsigned char) * Msgsize) );
|
||||
|
||||
/* copy old message into larger new one */
|
||||
if ( oldmess != 0 ) {
|
||||
memcpy(newmess, oldmess, oldleng);
|
||||
Mf_free(oldmess, oldleng);
|
||||
}
|
||||
Msgbuff = newmess;
|
||||
}
|
||||
Reference in New Issue
Block a user