Archival snapshot of the Virtual World Entertainment Tesla cockpit software, 1994-1996: MUNGA engine and L4 pod layer source (Borland C++ 5.0), BT/RP game code, and game content (models, audio, maps, gauges, Division renderer data). Includes third-party libraries: Division dVS/DPL graphics, HMI SOS audio, WATTCP networking. Files are preserved byte-for-byte (.gitattributes disables all line-ending conversion). README.md documents the layout, target hardware, and toolchain. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
707 lines
16 KiB
C++
707 lines
16 KiB
C++
//===========================================================================//
|
|
// File: audmidi.cpp //
|
|
// Project: MUNGA Brick: Audio manager //
|
|
// Contents: //
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// 10/23/95 ECH Initial code
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc. //
|
|
// All Rights reserved worldwide //
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
#include <munga.hpp>
|
|
#pragma hdrstop
|
|
|
|
#if !defined(AUDMIDI_HPP)
|
|
# include <audmidi.hpp>
|
|
#endif
|
|
|
|
//#############################################################################
|
|
//########################### 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");
|
|
}
|