Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.
Layout:
engine/ MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
models) + image codec; the minimal rp/ headers the audio HAL needs
game/ reconstructed BT logic + surviving-original BT source + fwd shims
+ WinMain launcher
content/ full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
docs/ format specs + reconstruction ledgers
reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
tools/ MP console emulator + map/resource scanners
One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
991 lines
22 KiB
C++
991 lines
22 KiB
C++
#include "mungal4.h"
|
|
#pragma hdrstop
|
|
|
|
#include "l4audhdw.h"
|
|
|
|
//#if defined(AUDIO_HARDWARE)
|
|
// #include "sos\bc4\sosmawe.h"
|
|
//#endif
|
|
|
|
#if defined(TRACE_AUDIO_RENDERER_MIDI)
|
|
BitTrace Audio_Renderer_MIDI("Audio Renderer MIDI");
|
|
#endif
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MPU Emulation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#define _inp inportb
|
|
#define _outp outportb
|
|
|
|
#define MPUPort(x) ((x)+mpuAddx)
|
|
#define SBCPort(x) ((x)+srcAddx)
|
|
|
|
/* DSP defines */
|
|
#define MPU_ACK_OK 0xfe
|
|
#define MPU_RESET_CMD 0xff
|
|
#define MPU_ENTER_UART 0x3f
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDIMessage ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
//
|
|
//#############################################################################
|
|
// TestInstance
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
MIDIMessage::TestInstance() const
|
|
{
|
|
return True;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
std::ostream&
|
|
operator << (
|
|
std::ostream &strm,
|
|
const MIDIMessage &midi_message
|
|
)
|
|
{
|
|
Check(&midi_message);
|
|
|
|
strm << "[";
|
|
strm << (int)(midi_message.message[0] & 0x0F) << ",";
|
|
strm << (int)(midi_message.message[0] >> 4) << ",";
|
|
strm << (int)midi_message.message[1] << ",";
|
|
strm << (int)midi_message.message[2] << "]";
|
|
|
|
return strm;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioChannel ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
/*AudioChannel::AudioChannel(
|
|
AudioChannelType audio_channel_type,
|
|
MIDIChannel midi_channel,
|
|
AudioCard *audio_card
|
|
)
|
|
{
|
|
audioChannelType = audio_channel_type;
|
|
midiChannel = midi_channel;
|
|
voicesUsed = 0;
|
|
Check(audio_card);
|
|
audioCard = audio_card;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioChannel::~AudioChannel()
|
|
{
|
|
Verify(voicesUsed == 0);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// TestInstance
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
AudioChannel::TestInstance() const
|
|
{
|
|
Plug::TestInstance();
|
|
Verify(voicesUsed >=0 && voicesUsed <= AWE_VOICE_COUNT);
|
|
Check(audioCard);
|
|
return True;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioCard ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
//
|
|
//#############################################################################
|
|
// AudioCard
|
|
//#############################################################################
|
|
//
|
|
AudioCard::AudioCard():
|
|
idleChannelSocket(NULL),
|
|
percussionChannelSocket(NULL)
|
|
{
|
|
MIDIChannel channel;
|
|
|
|
idleVoices = AWE_VOICE_COUNT;
|
|
for (channel = 0; channel < AWE_CHANNEL_COUNT; channel++)
|
|
{
|
|
AudioChannel *audio_channel;
|
|
|
|
if (channel == AWE_PERCUSSIVE_CHANNEL)
|
|
{
|
|
audio_channel =
|
|
new AudioChannel(
|
|
PercussionAudioChannelType,
|
|
channel,
|
|
this
|
|
);
|
|
Register_Object(audio_channel);
|
|
Check(&percussionChannelSocket);
|
|
percussionChannelSocket.Add(audio_channel);
|
|
}
|
|
else
|
|
{
|
|
audio_channel =
|
|
new AudioChannel(
|
|
InstrumentAudioChannelType,
|
|
channel,
|
|
this
|
|
);
|
|
Register_Object(audio_channel);
|
|
Check(&idleChannelSocket);
|
|
idleChannelSocket.Add(audio_channel);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ~AudioCard
|
|
//#############################################################################
|
|
//
|
|
AudioCard::~AudioCard()
|
|
{
|
|
//
|
|
// Delete the idle channels
|
|
//
|
|
{
|
|
ChainIteratorOf<AudioChannel*> iterator(&idleChannelSocket);
|
|
|
|
Check(&iterator);
|
|
Verify(idleVoices == AWE_VOICE_COUNT);
|
|
Verify(iterator.GetSize() == AWE_CHANNEL_COUNT - 1);
|
|
iterator.DeletePlugs();
|
|
}
|
|
|
|
//
|
|
// Delete the percussive channel
|
|
//
|
|
{
|
|
ChainIteratorOf<AudioChannel*> iterator(&percussionChannelSocket);
|
|
|
|
Check(&iterator);
|
|
Verify(idleVoices == AWE_VOICE_COUNT);
|
|
Verify(iterator.GetSize() == 1);
|
|
iterator.DeletePlugs();
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// TestInstance
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
AudioCard::TestInstance() const
|
|
{
|
|
Node::TestInstance();
|
|
Verify(idleVoices >= 0 && idleVoices <= AWE_VOICE_COUNT);
|
|
Check(&idleChannelSocket);
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Initialize
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::Initialize()
|
|
{
|
|
#ifdef AUDIO_HARDWARE
|
|
Tell("AudioCard::Initialize - Start\n");
|
|
|
|
WORD wError;
|
|
|
|
//
|
|
// Set the selector of the pointer to the driver memory for the
|
|
// MIDI driver to _NULL. this will tell the load driver routine
|
|
// to allocate new memory for the MIDI driver
|
|
//
|
|
#if defined(__BCPLUSPLUS__)
|
|
sSOSMIDIInitDriver.lpDriverMemory.sel = _NULL;
|
|
#else
|
|
sSOSMIDIInitDriver.lpDriverMemory = _NULL;
|
|
#endif
|
|
|
|
//
|
|
// Setup the port for the MIDI driver to use
|
|
//
|
|
// sSOSMIDIHardware.wPort = _MIDI_DRIVER_PORT;
|
|
sSOSMIDIHardware.wPort = emuAddx;
|
|
|
|
//
|
|
// Load and initialize the MIDI driver
|
|
//
|
|
wError = sosMIDIInitDriver(
|
|
_MIDI_DRIVER_TYPE,
|
|
&sSOSMIDIHardware,
|
|
&sSOSMIDIInitDriver,
|
|
&wDriverHandle
|
|
);
|
|
if (wError)
|
|
{
|
|
std::cout << sosGetErrorString(wError);
|
|
Fail("AudioCard::Initialize - sosMIDIInitDriver failed\n");
|
|
}
|
|
|
|
Tell("AudioCard::Initialize - Finish\n");
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Close
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::Close()
|
|
{
|
|
#ifdef AUDIO_HARDWARE
|
|
Check(this);
|
|
|
|
sosMIDIAWE32ReleaseSBKFiles(wDriverHandle);
|
|
|
|
//
|
|
// Reset the midi driver
|
|
//
|
|
sosMIDIResetDriver(wDriverHandle);
|
|
|
|
//
|
|
// Uninitialize the midi driver and tell it to free the memory allocated
|
|
// for the driver
|
|
//
|
|
sosMIDIUnInitDriver(wDriverHandle, _TRUE);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// GetEnvironmentSettings
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::GetEnvironmentSettings(char *env_str)
|
|
{
|
|
WORD src_addx = 0xffff;
|
|
WORD irq_interrupt = 0xffff;
|
|
WORD dma_channel = 0xffff;
|
|
WORD mpu_addx = 0xffff;
|
|
WORD emu_addx = 0xffff;
|
|
|
|
const char *sz_blaster;
|
|
|
|
sz_blaster = getenv(env_str);
|
|
Check_Pointer(sz_blaster);
|
|
|
|
while (*sz_blaster) {
|
|
if (*sz_blaster == 'a' || *sz_blaster == 'A') {
|
|
++sz_blaster;
|
|
if (*sz_blaster) {
|
|
if (*sz_blaster == ':')
|
|
++sz_blaster;
|
|
src_addx = GetHexWord(&sz_blaster);
|
|
}
|
|
}
|
|
if (*sz_blaster == 'i' || *sz_blaster == 'I') {
|
|
++sz_blaster;
|
|
if (*sz_blaster) {
|
|
if (*sz_blaster == ':')
|
|
++sz_blaster;
|
|
irq_interrupt = GetHexWord(&sz_blaster);
|
|
}
|
|
}
|
|
if (*sz_blaster == 'd' || *sz_blaster == 'D') {
|
|
++sz_blaster;
|
|
if (*sz_blaster) {
|
|
if (*sz_blaster == ':')
|
|
++sz_blaster;
|
|
dma_channel = GetHexWord(&sz_blaster);
|
|
}
|
|
}
|
|
if (*sz_blaster == 'e' || *sz_blaster == 'E') {
|
|
++sz_blaster;
|
|
if (*sz_blaster) {
|
|
if (*sz_blaster == ':')
|
|
++sz_blaster;
|
|
emu_addx = GetHexWord(&sz_blaster);
|
|
}
|
|
}
|
|
if (*sz_blaster == 'p' || *sz_blaster == 'P') {
|
|
++sz_blaster;
|
|
if (*sz_blaster) {
|
|
if (*sz_blaster == ':')
|
|
++sz_blaster;
|
|
mpu_addx = GetHexWord(&sz_blaster);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Skip until blank or end of string
|
|
//
|
|
while (*sz_blaster && *sz_blaster != ' ')
|
|
++sz_blaster;
|
|
while (*sz_blaster == ' ')
|
|
++sz_blaster;
|
|
}
|
|
|
|
if (emu_addx == 0xffff)
|
|
emu_addx = (WORD)(src_addx + 0x400);
|
|
|
|
srcAddx = src_addx;
|
|
irqInterrupt = irq_interrupt;
|
|
dmaChannel = dma_channel;
|
|
mpuAddx = mpu_addx;
|
|
emuAddx = emu_addx;
|
|
|
|
#if DEBUG_LEVEL>0
|
|
Tell("AudioCard::GetEnvironmentSettings: " << env_str << "\n");
|
|
#if 0
|
|
Dump(srcAddx);
|
|
Dump(irqInterrupt);
|
|
Dump(dmaChannel);
|
|
Dump(mpuAddx);
|
|
Dump(emuAddx);
|
|
#endif
|
|
printf(" srcAddx = %x\n", (unsigned)srcAddx);
|
|
printf(" irqInterrupt = %x\n", (unsigned)irqInterrupt);
|
|
printf(" dmaChannel = %x\n", (unsigned)dmaChannel);
|
|
printf(" mpuAddx = %x\n", (unsigned)mpuAddx);
|
|
printf(" emuAddx = %x\n", (unsigned)emuAddx);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// InitMIDIReceive
|
|
// Initializes Sound Blaster to ready to receive data state.
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
AudioCard::InitMIDIReceive()
|
|
{
|
|
#if 0
|
|
volatile DWORD dwCount;
|
|
|
|
for (dwCount=0; dwCount<0x2000; dwCount++) ;
|
|
dwCount = 0x2000;
|
|
while (dwCount && _inp(MPUPort(1)) & 0x40) --dwCount;
|
|
_outp(MPUPort(1), MPU_RESET_CMD);
|
|
|
|
for (dwCount=0; dwCount<0x2000; dwCount++) ;
|
|
dwCount = 0x2000;
|
|
while (dwCount && _inp(MPUPort(1)) & 0x80) --dwCount;
|
|
_inp(MPUPort(0));
|
|
|
|
for (dwCount=0; dwCount<0x2000; dwCount++) ;
|
|
dwCount = 0x2000;
|
|
while (dwCount && _inp(MPUPort(1)) & 0x40) --dwCount;
|
|
_outp(MPUPort(1), MPU_RESET_CMD);
|
|
|
|
for (dwCount=0; dwCount<0x2000; dwCount++) ;
|
|
dwCount = 0x2000;
|
|
while (dwCount && _inp(MPUPort(1)) & 0x80) --dwCount;
|
|
_inp(MPUPort(0));
|
|
|
|
for (dwCount=0; dwCount<0x2000; dwCount++) ;
|
|
dwCount = 0x2000;
|
|
while (dwCount && _inp(MPUPort(1)) & 0x40) --dwCount;
|
|
_outp(MPUPort(1), MPU_ENTER_UART);
|
|
|
|
for (dwCount=0; dwCount<0x2000; dwCount++) ;
|
|
dwCount = 0x2000;
|
|
while (dwCount && _inp(MPUPort(1)) & 0x80) --dwCount;
|
|
if (!dwCount) return True;
|
|
if (_inp(MPUPort(0)) != MPU_ACK_OK) return True;
|
|
|
|
// mask MPU-401 interrupt
|
|
_outp(SBCPort(0x4), 0x83);
|
|
_outp(SBCPort(0x5), _inp(SBCPort(0x5)) & ~0x04);
|
|
#endif
|
|
return False;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// CloseMIDIReceive
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::CloseMIDIReceive()
|
|
{
|
|
#if 0
|
|
volatile DWORD dwCount;
|
|
|
|
for (dwCount=0; dwCount<0x2000; dwCount++) ;
|
|
dwCount = 0x2000;
|
|
while (dwCount && _inp(MPUPort(1)) & 0x40) --dwCount;
|
|
_outp(MPUPort(1), MPU_RESET_CMD);
|
|
for (dwCount=0; dwCount<0x2000; dwCount++) ;
|
|
_inp(MPUPort(0));
|
|
|
|
for (dwCount=0; dwCount<0x2000; dwCount++) ;
|
|
dwCount = 0x2000;
|
|
while (dwCount && _inp(MPUPort(1)) & 0x40) --dwCount;
|
|
_outp(MPUPort(1), MPU_RESET_CMD);
|
|
for (dwCount=0; dwCount<0x2000; dwCount++) ;
|
|
_inp(MPUPort(0));
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// IsMIDIAvailable
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
AudioCard::IsMIDIAvailable()
|
|
{
|
|
#if 0
|
|
return ((_inp(MPUPort(1)) & 0x80) == 0);
|
|
#endif
|
|
return False;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// GetMIDIByte
|
|
//#############################################################################
|
|
//
|
|
MIDIValue
|
|
AudioCard::GetMIDIByte()
|
|
{
|
|
#if 0
|
|
return (_inp(MPUPort(0)));
|
|
#endif
|
|
return (MIDIValue)0;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// InitializeChannels
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::InitializeChannels()
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
// Initialize idle channels
|
|
//
|
|
{
|
|
ChainIteratorOf<AudioChannel*> iterator(&idleChannelSocket);
|
|
AudioChannel *channel;
|
|
|
|
Check(&iterator);
|
|
Verify(iterator.GetSize() == (AWE_CHANNEL_COUNT-1));
|
|
while ((channel = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
Check(channel);
|
|
channel->SendPitchBendRange(MIDI_PITCH_BEND_RANGE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize percussive channels
|
|
//
|
|
{
|
|
ChainIteratorOf<AudioChannel*> iterator(&percussionChannelSocket);
|
|
AudioChannel *channel;
|
|
|
|
Check(&iterator);
|
|
Verify(iterator.GetSize() == 1);
|
|
channel = iterator.GetCurrent();
|
|
Check(channel);
|
|
channel->SendPitchBendRange(MIDI_PITCH_BEND_RANGE);
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// LoadSBK
|
|
//#############################################################################
|
|
//
|
|
int
|
|
AudioCard::LoadSBK(const char *file_name)
|
|
{
|
|
#ifdef AUDIO_HARDWARE
|
|
Check(this);
|
|
Check_Pointer(file_name);
|
|
|
|
int ret;
|
|
|
|
Tell("AudioCard::LoadSBK - " << file_name << "\n");
|
|
ret = sosMIDIAWE32SetSBKFile(wDriverHandle, file_name);
|
|
Verify_And_Dump(ret >= 0, ret);
|
|
Tell("AudioCard::LoadSBK - Finished\n");
|
|
return ret;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ReleaseAllBanks
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::ReleaseAllBanks()
|
|
{
|
|
#ifdef AUDIO_HARDWARE
|
|
Check(this);
|
|
sosMIDIAWE32ReleaseSBKFiles(wDriverHandle);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// MIDI methods
|
|
//#############################################################################
|
|
//
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::SendPitchBend(
|
|
MIDIChannel channel,
|
|
MIDIPitchBend pitch_bend
|
|
)
|
|
{
|
|
MIDIMessage
|
|
message(
|
|
MIDI_PITCH_BEND_STATUS,
|
|
channel,
|
|
(MIDIValue)((pitch_bend+8192)%128),
|
|
(MIDIValue)((pitch_bend+8192)/128)
|
|
);
|
|
SendMIDIMessage(&message, 3);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::SendPitchBendRange(
|
|
MIDIChannel channel,
|
|
MIDIValue sensitivity
|
|
)
|
|
{
|
|
SendController(channel, (MIDIValue)101, (MIDIValue)0);
|
|
SendController(channel, (MIDIValue)100, (MIDIValue)0);
|
|
SendController(channel, (MIDIValue)6, sensitivity);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::SendNRPN(
|
|
MIDIChannel channel,
|
|
MIDIValue param_number,
|
|
MIDINRPNValue value
|
|
)
|
|
{
|
|
//
|
|
// Set sound parameter
|
|
//
|
|
SendController(channel, (MIDIValue)99, (MIDIValue)127);
|
|
SendController(channel, (MIDIValue)98, param_number);
|
|
|
|
//
|
|
// Set parameter value
|
|
//
|
|
SendController(channel, (MIDIValue)6, (MIDIValue)((value+8192)/128) );
|
|
SendController(channel, (MIDIValue)38, (MIDIValue)((value+8192)%128) );
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
#define SYSEX_MACRO_LENGTH 11
|
|
|
|
Byte
|
|
chorus_sysex[SYSEX_MACRO_LENGTH] =
|
|
{
|
|
0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x38, 0x00, 0x00, 0xF7
|
|
};
|
|
|
|
Byte
|
|
reverb_sysex[SYSEX_MACRO_LENGTH] =
|
|
{
|
|
0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x30, 0x00, 0x00, 0xF7
|
|
};
|
|
|
|
void
|
|
AudioCard::SelectEffect(
|
|
MIDIEffectType type,
|
|
MIDIValue effect
|
|
)
|
|
{
|
|
switch (type)
|
|
{
|
|
case ChorusMIDIEffectType:
|
|
chorus_sysex[8] = effect;
|
|
SendSysex(chorus_sysex, SYSEX_MACRO_LENGTH);
|
|
break;
|
|
|
|
case ReverbMIDIEffectType:
|
|
reverb_sysex[8] = effect;
|
|
SendSysex(reverb_sysex, SYSEX_MACRO_LENGTH);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::SendSysex(
|
|
void *data,
|
|
size_t length
|
|
)
|
|
{
|
|
#ifdef AUDIO_HARDWARE
|
|
WORD wError;
|
|
|
|
//
|
|
// Note - HMI can specify sysex channel via F0, F1, ..., FF
|
|
//
|
|
#if defined(__BCPLUSPLUS__)
|
|
wError = sosMIDISendMIDIData(
|
|
wDriverHandle,
|
|
(char*)data,
|
|
length
|
|
);
|
|
#else
|
|
wError = sosMIDISendMIDIData(
|
|
wDriverHandle,
|
|
(Byte*)data,
|
|
length
|
|
);
|
|
#endif
|
|
if (wError != _ERR_NO_ERROR)
|
|
{
|
|
Fail("AudioCard::SendSysex - wError != _ERR_NO_ERROR");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::SendMIDIMessage(MIDIMessage *midi_message)
|
|
{
|
|
#ifdef AUDIO_HARDWARE
|
|
SET_AUDIO_RENDERER_MIDI();
|
|
|
|
Check(midi_message);
|
|
|
|
#if 0
|
|
long now = Now();
|
|
Tell(now << "\t" << (int)srcAddx << "\t" << *midi_message << "\t");
|
|
Tell(idleVoices << "\n");
|
|
#endif
|
|
|
|
switch (midi_message->message[0] >> 4) {
|
|
case MIDI_NOTE_ON_STATUS:
|
|
case MIDI_NOTE_OFF_STATUS:
|
|
case MIDI_POLYKEY_STATUS:
|
|
case MIDI_CONTROLLER_STATUS:
|
|
case MIDI_PITCH_BEND_STATUS:
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
Word wError = sosMIDISendMIDIData(
|
|
wDriverHandle,
|
|
(char*)midi_message->message,
|
|
3
|
|
);
|
|
Verify(wError == _ERR_NO_ERROR);
|
|
}
|
|
#else
|
|
sosMIDISendMIDIData(
|
|
wDriverHandle,
|
|
(char*)midi_message->message,
|
|
3
|
|
);
|
|
#endif
|
|
break;
|
|
|
|
case MIDI_PROGRAM_CHANGE_STATUS:
|
|
case MIDI_CHANNEL_PRESSURE_STATUS:
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
Word wError = sosMIDISendMIDIData(
|
|
wDriverHandle,
|
|
(char*)midi_message->message,
|
|
2
|
|
);
|
|
Verify(wError == _ERR_NO_ERROR);
|
|
}
|
|
#else
|
|
sosMIDISendMIDIData(
|
|
wDriverHandle,
|
|
(char*)midi_message->message,
|
|
2
|
|
);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Should never reach here
|
|
//
|
|
Fail("Should never reach here");
|
|
break;
|
|
}
|
|
CLEAR_AUDIO_RENDERER_MIDI();
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//#############################################################################
|
|
// Channel methods
|
|
//#############################################################################
|
|
//
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioChannel*
|
|
AudioCard::RequestAudioChannel(AudioVoiceCount voice_count)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
// Monitor resource usage
|
|
//
|
|
#if 0
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
ChainIteratorOf<AudioChannel*> iterator(&idleChannelSocket);
|
|
|
|
if (voice_count > idleVoices || iterator.GetSize() == 0)
|
|
{
|
|
Tell("RequestAudioChannel - voices: " << idleVoices);
|
|
Tell(" channels: " << iterator.GetSize());
|
|
Tell("\n");
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
//
|
|
// Are there enough voices to allocate
|
|
//
|
|
if (voice_count > idleVoices)
|
|
return NULL;
|
|
|
|
//
|
|
// Allocate a channel
|
|
//
|
|
ChainIteratorOf<AudioChannel*> iterator(&idleChannelSocket);
|
|
AudioChannel *audio_channel;
|
|
|
|
Check(&iterator);
|
|
if ((audio_channel = iterator.GetCurrent()) != NULL)
|
|
{
|
|
Check(audio_channel);
|
|
iterator.Remove();
|
|
idleVoices -= voice_count;
|
|
audio_channel->SetVoicesUsed(voice_count);
|
|
|
|
return audio_channel;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioCard::ReleaseAudioChannel(AudioChannel *audio_channel)
|
|
{
|
|
Check(this);
|
|
Check(audio_channel);
|
|
|
|
#if 0
|
|
Tell("AudioCard::ReleaseAudioChannel - Released channel\n");
|
|
Tell(" ");
|
|
Dump((int)audio_channel->GetChannelNumber());
|
|
#endif
|
|
|
|
//
|
|
// Add to idle list
|
|
//
|
|
idleVoices += audio_channel->GetVoicesUsed();
|
|
Verify(idleVoices > 0 && idleVoices <= AWE_VOICE_COUNT);
|
|
audio_channel->SetVoicesUsed(0);
|
|
|
|
idleChannelSocket.Add(audio_channel);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// GetHexWord
|
|
//#############################################################################
|
|
//
|
|
WORD
|
|
AudioCard::GetHexWord(const char ** s)
|
|
{
|
|
WORD n = 0;
|
|
|
|
while (**s) {
|
|
if (**s >= '0' && **s <= '9')
|
|
n = (WORD)(n * 16 + (*(*s)++ - '0'));
|
|
else
|
|
if (**s >= 'a' && **s <= 'f')
|
|
n = (WORD)(n * 16 + (*(*s)++ - 'a') + 10);
|
|
else
|
|
if (**s >= 'A' && **s <= 'F')
|
|
n = (WORD)(n * 16 + (*(*s)++ - 'A') + 10);
|
|
else
|
|
break;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioHardware ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
//
|
|
//#############################################################################
|
|
// AudioHardware
|
|
//#############################################################################
|
|
//
|
|
AudioHardware::AudioHardware()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ~AudioHardware
|
|
//#############################################################################
|
|
//
|
|
AudioHardware::~AudioHardware()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// TestInstance
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
AudioHardware::TestInstance() const
|
|
{
|
|
Node::TestInstance();
|
|
Check(&frontCard);
|
|
Check(&rearCard);
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Initialize
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioHardware::Initialize()
|
|
{
|
|
#ifdef AUDIO_HARDWARE
|
|
Tell("AudioHardware::Initialize - Start\n");
|
|
|
|
#if 0
|
|
//
|
|
// initialize the TIMER system and tell it to call the DOS timer
|
|
// at a rate of 19 times/second. the actual rate is 18.2, but
|
|
// 19 is a good approximation.
|
|
//
|
|
if (getenv(TIMER_ENV) == NULL) // L4Time is not setting up timer
|
|
{
|
|
sosTIMERInitSystem(_TIMER_DOS_RATE, _SOS_DEBUG_NORMAL);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// initialize the MIDI system
|
|
//
|
|
sosMIDIInitSystem((PSTR)_SOS_DRIVER_PATH, _SOS_DEBUG_NORMAL);
|
|
|
|
//
|
|
// initialize the cards
|
|
//
|
|
frontCard.GetEnvironmentSettings(FRONT_CARD_ENV_VAR);
|
|
rearCard.GetEnvironmentSettings(REAR_CARD_ENV_VAR);
|
|
frontCard.Initialize();
|
|
rearCard.Initialize();
|
|
|
|
//
|
|
// Setup standard channel attributes
|
|
//
|
|
frontCard.InitializeChannels();
|
|
rearCard.InitializeChannels();
|
|
|
|
Tell("AudioHardware::Initialize - Finish\n");
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Close
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioHardware::Close()
|
|
{
|
|
#ifdef AUDIO_HARDWARE
|
|
Check(this);
|
|
|
|
//
|
|
// Close the cards
|
|
//
|
|
frontCard.Close();
|
|
rearCard.Close();
|
|
|
|
#if 0
|
|
//
|
|
// Uninitialize the TIMER system and pass it the value to write to the
|
|
// timer chip to reset the timer, 0 is equivalent to 18.2 times/second
|
|
// which is the DOS clock rate. the calculation for the value to pass
|
|
// is 1193180/RATE.
|
|
//
|
|
if (getenv(TIMER_ENV) == NULL) // L4Time is not setting up timer
|
|
{
|
|
sosTIMERUnInitSystem(0);
|
|
}
|
|
sosTIMERUnInitSystem(0);
|
|
#endif
|
|
|
|
//
|
|
// Uninitialize the MIDI system
|
|
//
|
|
sosMIDIUnInitSystem();
|
|
#endif
|
|
}*/
|
|
|
|
#ifdef TEST_CLASS
|
|
# include "l4audhdw.tcp"
|
|
#endif
|