Files
RP411/MUNGA/AUDMIDI.cpp
T
CydandClaude Opus 4.8 4abbf8879f Initial import of Red Planet v4.10 Win32 source
Imports the current Win32 source for the pod-racing game 'Red Planet',
built on the MUNGA engine and its L4 (Win32/DirectX) platform layer:

- MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend
- RP / RP_L4: Red Planet game logic and Win32 application
- DivLoader, Setup1: asset loader and installer project
- lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies

Removed stale Subversion metadata and added .gitignore/.gitattributes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 07:59:51 -05:00

691 lines
14 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "audmidi.h"
//#############################################################################
//########################### MidiParse #################################
//#############################################################################
#define NOTEOFF (0x80)
#define NOTEON (0x90)
#define PRESSURE (0xa0)
#define CONTROLLER (0xb0)
#define PITCHBEND (0xe0)
#define PROGRAM (0xc0)
#define CHANPRESSURE (0xd0)
#define METATEXT "Text Event"
#define METACOPYRIGHT "Copyright Notice"
#define METASEQUENCE "Sequence/Track Name"
#define METAINSTRUMENT "Instrument Name"
#define METALYRIC "Lyric"
#define METAMARKER "Marker"
#define METACUE "Cue Point"
#define METAUNRECOGNIZED "Unrecognized"
#define CHAR_BUFF_SIZE (32)
//
//#############################################################################
//#############################################################################
//
MidiParse::MidiParse()
{
Mf_nomerge = 0; // 1 => continue'ed system exclusives are not collapsed.
Mf_currtime = 0L; // current time in delta-time units
Mf_skipinit = 0; // 1 if initial garbage should be skipped
Mf_toberead = 0L;
Msgbuff = NULL; // message buffer
Msgsize = 0; // Size of currently allocated Msg
Msgindex = 0; // index of next available location in Msg
}
//
//#############################################################################
//#############################################################################
//
MidiParse::~MidiParse()
{
if (Msgbuff != NULL)
{
Unregister_Pointer(Msgbuff);
#if 0
free(Msgbuff);
#else
delete[] Msgbuff;
#endif
Msgbuff = NULL;
}
}
//
//#############################################################################
//#############################################################################
//
Logical
MidiParse::TestInstance() const
{
Node::TestInstance();
return True;
}
//
//#############################################################################
//#############################################################################
//
MidiParse::SignedLongWord
MidiParse::CurTime()
{
Check(this);
return Mf_currtime;
}
//
//#############################################################################
//#############################################################################
//
void
MidiParse::Parse()
{
Check(this);
SignedWord ntrks;
ntrks = readheader();
Verify(ntrks > 0);
while (ntrks-- > 0)
readtrack();
}
//
//#############################################################################
//#############################################################################
//
MidiParse::SignedWord
MidiParse::readmt(
char *s,
SignedWord skip
)
{
Check(this);
SignedWord nread = 0;
char b[4];
char buff[CHAR_BUFF_SIZE];
SignedWord c;
char *errmsg = "expecting ";
// read through the "MThd" or "MTrk" header string
// if 1, we attempt to skip initial garbage.
retry:
while ( nread<4 )
{
c = Mf_getc();
if ( c == EOF )
{
errmsg = "EOF while expecting ";
goto err;
}
b[nread++] = (char)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:
Str_Copy(buff, errmsg, CHAR_BUFF_SIZE);
Str_Cat(buff, s, CHAR_BUFF_SIZE);
mferror(buff);
return 0;
}
//
//#############################################################################
//#############################################################################
//
MidiParse::SignedWord
MidiParse::egetc()
{
Check(this);
// read a single character and abort on EOF
SignedWord c = Mf_getc();
if ( c == EOF )
mferror("premature EOF");
Mf_toberead--;
return c;
}
//
//#############################################################################
//#############################################################################
//
MidiParse::SignedWord
MidiParse::readheader()
{
Check(this);
// read a header chunk
SignedWord
format, ntrks, division;
if ( readmt("MThd", Mf_skipinit) == EOF )
return 0;
Mf_toberead = read32bit();
format = read16bit();
ntrks = read16bit();
division = read16bit();
Mf_header(format,ntrks,division);
// flush any extra stuff, in case the length of header is not 6
while ( Mf_toberead > 0 )
egetc();
return ntrks;
}
//
//#############################################################################
//#############################################################################
//
void
MidiParse::readtrack()
{
Check(this);
// 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 SignedWord chantype[] =
{
0, 0, 0, 0, 0, 0, 0, 0, // 0x00 through 0x70
2, 2, 2, 2, 1, 1, 2, 0 // 0x80 through 0xf0
};
SignedLongWord lookfor, lng;
SignedWord c, c1, type;
SignedWord sysexcontinue = 0; // 1 if last message was an unfinished sysex
SignedWord running; // 1 when running status used
SignedWord status = 0; // (possibly running) status byte
SignedWord needed;
if ( readmt("MTrk",0) == EOF )
return;
Mf_toberead = read32bit();
Mf_currtime = 0;
Mf_starttrack();
while ( Mf_toberead > 0 )
{
Mf_currtime += readvarinum(); // delta time
c = egetc();
if ( sysexcontinue && c != 0xf7 )
mferror("didn't find expected continuation of a sysex");
if ( (c & 0x80) == 0 ) // running status?
{
if ( status == 0 )
mferror("unexpected running status");
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();
chanmessage( status, c1, (needed>1) ? egetc() : (SignedWord)0 );
continue;
}
switch ( c )
{
case 0xff: // meta event
type = egetc();
// watch out - Don't combine the next 2 statements
lng = readvarinum();
lookfor = Mf_toberead - lng;
msginit();
while ( Mf_toberead > lookfor )
msgadd(egetc());
metaevent(type);
break;
case 0xf0: // start of system exclusive
// watch out - Don't combine the next 2 statements
lng = readvarinum();
lookfor = Mf_toberead - lng;
msginit();
msgadd(0xf0);
while ( Mf_toberead > lookfor )
msgadd(c=egetc());
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();
lookfor = Mf_toberead - lng;
if ( ! sysexcontinue )
msginit();
while ( Mf_toberead > lookfor )
msgadd(c=egetc());
if ( ! sysexcontinue )
{
Mf_arbitrary(msgleng(),msg());
}
else if ( c == 0xf7 )
{
sysex();
sysexcontinue = 0;
}
break;
default:
badbyte(c);
break;
}
}
Mf_endtrack();
return;
}
//
//#############################################################################
//#############################################################################
//
void
MidiParse::badbyte(SignedWord c)
{
Check(this);
char buff[CHAR_BUFF_SIZE];
sprintf(buff,"unexpected byte: 0x%02x",c);
mferror(buff);
}
//
//#############################################################################
//#############################################################################
//
void
MidiParse::metaevent(SignedWord type)
{
Check(this);
SignedWord leng = msgleng();
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 0x2f: // End of Track
Mf_eot();
break;
case 0x51: // Set tempo
Mf_tempo(to32bit((SignedWord)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
MidiParse::sysex()
{
Check(this);
Mf_sysex(msgleng(),msg());
}
//
//#############################################################################
//#############################################################################
//
void
MidiParse::chanmessage(
SignedWord status,
SignedWord c1,
SignedWord c2
)
{
Check(this);
SignedWord chan = status & (SignedWord)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;
}
}
//
//#############################################################################
//#############################################################################
//
MidiParse::SignedLongWord
MidiParse::readvarinum()
{
Check(this);
// readvarinum - read a varying-length number, and return the
// number of characters it took.
SignedLongWord value;
SignedWord c;
c = egetc();
value = c;
if (c & 0x80)
{
value &= 0x7f;
do
{
c = egetc();
value <<= 7;
value += (c & 0x7f);
} while (c & 0x80);
}
return value;
}
//
//#############################################################################
//#############################################################################
//
MidiParse::SignedLongWord
MidiParse::to32bit(SignedWord c1, SignedWord c2,SignedWord c3, SignedWord c4)
{
Check(this);
SignedLongWord value;
value = (c1 & 0xff);
value = (value<<8) + (c2 & 0xff);
value = (value<<8) + (c3 & 0xff);
value = (value<<8) + (c4 & 0xff);
return value;
}
//
//#############################################################################
//#############################################################################
//
MidiParse::SignedWord
MidiParse::to16bit(SignedWord c1,SignedWord c2)
{
Check(this);
SignedWord value;
value = (c1 & (SignedWord)0xff);
value = (SignedWord)((SignedWord)(value<<8) + (c2 & (SignedWord)0xff));
return value;
}
//
//#############################################################################
//#############################################################################
//
MidiParse::SignedLongWord
MidiParse::read32bit()
{
Check(this);
SignedWord c1, c2, c3, c4;
c1 = egetc();
c2 = egetc();
c3 = egetc();
c4 = egetc();
return to32bit(c1,c2,c3,c4);
}
//
//#############################################################################
//#############################################################################
//
MidiParse::SignedWord
MidiParse::read16bit()
{
Check(this);
SignedWord c1, c2;
c1 = egetc();
c2 = egetc();
return to16bit(c1,c2);
}
//
//#############################################################################
//#############################################################################
//
void
MidiParse::mferror(char *s)
{
Check(this);
Mf_error(s);
}
// 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 ((SignedWord)128)
//
//#############################################################################
//#############################################################################
//
void
MidiParse::msginit()
{
Check(this);
Msgindex = 0;
}
//
//#############################################################################
//#############################################################################
//
char*
MidiParse::msg()
{
Check(this);
return Msgbuff;
}
//
//#############################################################################
//#############################################################################
//
MidiParse::SignedWord
MidiParse::msgleng()
{
Check(this);
return Msgindex;
}
//
//#############################################################################
//#############################################################################
//
void
MidiParse::msgadd(SignedWord c)
{
Check(this);
// If necessary, allocate larger message buffer.
if ( Msgindex >= Msgsize )
msgenlarge();
Msgbuff[Msgindex++] = (char)c;
}
//
//#############################################################################
//#############################################################################
//
void
MidiParse::msgenlarge()
{
Check(this);
char *newmess;
char *oldmess = Msgbuff;
SignedWord oldleng = Msgsize;
Msgsize += MSGINCREMENT;
#if 0
newmess = (char*)malloc( sizeof(char)*Msgsize );
#else
newmess = new char[Msgsize];
#endif
Register_Pointer(newmess);
// copy old message into larger new one
if ( oldmess != 0 )
{
char *p = newmess;
char *q = oldmess;
char *endq = &oldmess[oldleng];
for ( ; q!=endq ; p++,q++ )
*p = *q;
Unregister_Pointer(oldmess);
#if 0
free(oldmess);
#else
delete[] oldmess;
#endif
}
Msgbuff = newmess;
}
// METHODS TO OVERRIDE
//
//#############################################################################
//#############################################################################
//
MidiParse::SignedWord
MidiParse::Mf_getc()
{
Fail("MidiParse::Mf_getc - Should never reach here");
return 0;
}
//
//#############################################################################
//#############################################################################
//
void
MidiParse::Mf_error(char*)
{
Fail("MidiParse::Mf_error - Should never reach here");
}