Files
BT412/engine/MUNGA_L4/L4AUDHDW.h
T
arcattackandClaude Opus 4.8 7b7d465e5e Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
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>
2026-07-05 21:03:40 -05:00

531 lines
14 KiB
C++

#pragma once
#include "..\munga\style.h"
#include "..\munga\node.h"
#include "..\munga\schain.h"
#include "..\munga\audio.h"
#include "openal/al.h"
struct SourceSet
{
int count;
ALuint sources[5];
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDI types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
typedef unsigned char MIDIStatus;
typedef unsigned char MIDIChannel;
typedef unsigned char MIDIValue;
typedef int MIDIPitchBend;
typedef int MIDINRPNValue;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDI status ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define MIDI_CHANNEL_COUNT (16)
#define MIDI_NOTE_OFF_STATUS ((MIDIValue)0x8) // 8
#define MIDI_NOTE_ON_STATUS ((MIDIValue)0x9) // 9
#define MIDI_POLYKEY_STATUS ((MIDIValue)0xa) // 10
#define MIDI_CONTROLLER_STATUS ((MIDIValue)0xb) // 11
#define MIDI_PROGRAM_CHANGE_STATUS ((MIDIValue)0xc) // 12
#define MIDI_CHANNEL_PRESSURE_STATUS ((MIDIValue)0xd) // 13
#define MIDI_PITCH_BEND_STATUS ((MIDIValue)0xe) // 14
//~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDI controllers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define MIDI_MAX_CONTROL_VALUE ((MIDIValue)127)
#define MIDI_MIN_CONTROL_VALUE ((MIDIValue)0)
#define MIDI_LEFT_PAN_VALUE ((MIDIValue)0)
#define MIDI_CENTER_PAN_VALUE ((MIDIValue)63)
#define MIDI_RIGHT_PAN_VALUE ((MIDIValue)127)
#define MIDI_VOLUME_CONTROL ((MIDIValue)7)
#define MIDI_PAN_CONTROL ((MIDIValue)10)
#define MIDI_REVERB_CONTROL ((MIDIValue)91)
#define MIDI_CHORUS_CONTROL ((MIDIValue)93)
#define MIDI_CONTROL_RESET ((MIDIValue)121)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDI notes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define MIDI_DEFAULT_NOTE ((MIDIValue)60)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDI pitch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define MIDI_PITCH_BEND_MAX ((MIDIPitchBend)8191)
#define MIDI_PITCH_BEND_RANGE ((MIDIValue)24)
#define MIDI_PITCH_BEND_CENTS (24*100)
#define MIDI_PITCH_PER_CENTS ((Scalar)MIDI_PITCH_BEND_MAX / (Scalar)MIDI_PITCH_BEND_CENTS)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AWE NRPN ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define AWE_VOL_ATTACK_TIME_NRPN ((MIDIValue)11)
#define AWE_PITCH_NRPN ((MIDIValue)16)
#define AWE_FILTER_CUTOFF_NRPN ((MIDIValue)21)
#define AWE_VOL_ATTACK_TIME_RANGE ((MIDINRPNValue)5940) // 0...5.9 secs
#define AWE_FILTER_CUTOFF_RANGE ((MIDINRPNValue)127) // 100...8000 Hz
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AWE effects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
typedef enum
{
ChorusMIDIEffectType,
ReverbMIDIEffectType
} MIDIEffectType;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AWE resources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define AWE_VOICE_COUNT (32)
#define AWE_CHANNEL_COUNT (16)
#define AWE_PERCUSSIVE_CHANNEL (9)
//
// Driver type to use
//
#define _MIDI_DRIVER_TYPE _MIDI_AWE32
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDIMessage ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define MIDI_MESSAGE_SIZE (3)
class MIDIMessage SIGNATURED
{
friend class AudioCard;
friend std::ostream& operator << (std::ostream &strm, const MIDIMessage &midi_message);
public:
MIDIMessage(MIDIStatus status, MIDIChannel channel, MIDIValue byte1, MIDIValue byte2 = 0);
~MIDIMessage() {}
Logical TestInstance() const;
private:
MIDIValue message[MIDI_MESSAGE_SIZE];
};
inline MIDIMessage::MIDIMessage(MIDIStatus status, MIDIChannel channel, MIDIValue byte1, MIDIValue byte2)
{
message[0] = (MIDIValue)((status << 4) | channel);
message[1] = byte1;
message[2] = byte2;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioChannel ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
typedef enum
{
InstrumentAudioChannelType = 0,
PercussionAudioChannelType
} AudioChannelType;
class AudioCard;
/*class AudioChannel : public Plug
{
public:
//
//--------------------------------------------------------------------
// Constructor, Destructor, Testing
//--------------------------------------------------------------------
//
AudioChannel(AudioChannelType audio_channel_type, MIDIChannel midi_channel, AudioCard *audio_card);
~AudioChannel();
Logical TestInstance() const;
//
//--------------------------------------------------------------------
// Accessors
//--------------------------------------------------------------------
//
AudioChannelType GetAudioChannelType() { return audioChannelType; }
MIDIChannel GetChannelNumber() { return midiChannel; }
void SetVoicesUsed(AudioVoiceCount voices_used) { voicesUsed = voices_used; }
AudioVoiceCount GetVoicesUsed() { return voicesUsed; }
//
//--------------------------------------------------------------------
// MIDI methods
//--------------------------------------------------------------------
//
void SendNoteOn(MIDIValue key, MIDIValue velocity);
MIDIValue myPreset;
MIDIValue myBank;
void SendNoteOff(MIDIValue key, MIDIValue velocity);
void SendProgramChange(MIDIValue program);
void SendController(MIDIValue controller, MIDIValue value);
void SendPolyKeyPressure(MIDIValue key, MIDIValue value);
void SendChannelPressure(MIDIValue value);
void SendPitchBend(MIDIPitchBend pitch_bend);
void SendPitchBendRange(MIDIValue sensitivity);
void SendNRPN(MIDIValue paramNumber, MIDINRPNValue value);
void SelectEffect(MIDIEffectType type, MIDIValue effect);
void SelectBank(MIDIValue bank);
//
//--------------------------------------------------------------------
// Release
//--------------------------------------------------------------------
//
void Release();
private:
//
//--------------------------------------------------------------------
// Private data
//--------------------------------------------------------------------
//
AudioChannelType
audioChannelType;
MIDIChannel midiChannel;
AudioVoiceCount voicesUsed;
AudioCard *audioCard;
};*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioCard ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*class AudioCard : public Node
{
friend class AudioHardware;
public:
//
//--------------------------------------------------------------------
// Constructor, Destructor, Testing
//--------------------------------------------------------------------
//
AudioCard();
~AudioCard();
Logical TestInstance() const;
//
//--------------------------------------------------------------------
// Public methods
//--------------------------------------------------------------------
//
void Initialize();
void Close();
void GetEnvironmentSettings(char *env_str);
Logical InitMIDIReceive();
void CloseMIDIReceive();
Logical IsMIDIAvailable();
MIDIValue GetMIDIByte();
void InitializeChannels();
int LoadSBK(const char *file_name);
void ReleaseAllBanks();
//
//--------------------------------------------------------------------
// MIDI methods
//--------------------------------------------------------------------
//
void SendNoteOn(MIDIChannel channel, MIDIValue key, MIDIValue velocity);
void SendNoteOff(MIDIChannel channel, MIDIValue key, MIDIValue velocity);
void SendProgramChange(MIDIChannel channel, MIDIValue program);
void SendController(MIDIChannel channel, MIDIValue controller, MIDIValue value);
void SendPolyKeyPressure(MIDIChannel channel, MIDIValue key, MIDIValue value);
void SendChannelPressure(MIDIChannel channel, MIDIValue value);
void SendPitchBendRange(MIDIChannel channel, MIDIValue sensitivity);
void SendPitchBend(MIDIChannel channel, MIDIPitchBend pitch_bend);
void SendSysex(void *data, size_t length);
void SendNRPN(MIDIChannel channel, MIDIValue paramNumber, MIDINRPNValue value);
void SelectEffect(MIDIEffectType type, MIDIValue effect);
void SelectBank(MIDIChannel channel, MIDIValue bank);
void SendMIDIMessage(MIDIMessage *midi_message, Word length);
//
//--------------------------------------------------------------------
// Channel methods
//--------------------------------------------------------------------
//
AudioChannel* RequestAudioChannel(AudioVoiceCount voice_count);
void ReleaseAudioChannel(AudioChannel *audio_channel);
private:
//
//--------------------------------------------------------------------
// Private methods
//--------------------------------------------------------------------
//
WORD GetHexWord(const char ** s);
//
//--------------------------------------------------------------------
// Private data
//--------------------------------------------------------------------
//
//
// Card addresses
//
WORD srcAddx;
WORD irqInterrupt;
WORD dmaChannel;
WORD mpuAddx;
WORD emuAddx;
//
// Structure for the MIDI driver initialization function
// Structure for the MIDI driver hardware
//
#ifdef AUDIO_HARDWARE
_SOS_MIDI_INIT_DRIVER sSOSMIDIInitDriver;
_SOS_MIDI_HARDWARE sSOSMIDIHardware;
#endif
//
// Handle for the loaded MIDI driver
//
WORD wDriverHandle;
//
// Number of free voices
//
AudioVoiceCount
idleVoices;
//
// Idle channels
//
ChainOf<AudioChannel*> idleChannelSocket;
ChainOf<AudioChannel*> percussionChannelSocket;
};*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioHardware ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*class AudioHardware : public Node
{
public:
//
//--------------------------------------------------------------------
// Constructor, Destructor, Testing
//--------------------------------------------------------------------
//
AudioHardware();
~AudioHardware();
Logical TestInstance() const;
static Logical TestClass();
static Logical ProfileClass();
//
//--------------------------------------------------------------------
// Public methods
//--------------------------------------------------------------------
//
void Initialize();
void Close();
//
//--------------------------------------------------------------------
// Card Access
//--------------------------------------------------------------------
//
AudioCard* GetFrontCard();
AudioCard* GetRearCard();
private:
//
//--------------------------------------------------------------------
// Private methods
//--------------------------------------------------------------------
//
//
//--------------------------------------------------------------------
// Private data
//--------------------------------------------------------------------
//
AudioCard frontCard;
AudioCard rearCard;
};*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioCard inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*inline void AudioCard::SendMIDIMessage(MIDIMessage *midi_message, Word length)
{
#ifdef AUDIO_HARDWARE
SET_AUDIO_RENDERER_MIDI();
Check_Signature(midi_message);
#if DEBUG_LEVEL>0
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:
Verify(length == 3);
break;
case MIDI_PROGRAM_CHANGE_STATUS:
case MIDI_CHANNEL_PRESSURE_STATUS:
Verify(length == 2);
break;
default:
Fail("Should never reach here");
break;
}
#endif
#if DEBUG_LEVEL>0
{
Word wError = sosMIDISendMIDIData(wDriverHandle, (char*)midi_message->message, length);
Verify(wError == _ERR_NO_ERROR);
}
#else
sosMIDISendMIDIData(wDriverHandle, (char*)midi_message->message, length);
#endif
CLEAR_AUDIO_RENDERER_MIDI();
#endif
}
//
//--------------------------------------------------------------------
// MIDI methods
//--------------------------------------------------------------------
//
inline void AudioCard::SendNoteOn(MIDIChannel channel, MIDIValue key, MIDIValue velocity)
{
MIDIMessage message(MIDI_NOTE_ON_STATUS, channel, key, velocity);
SendMIDIMessage(&message, 3);
}
inline void AudioCard::SendNoteOff(MIDIChannel channel, MIDIValue key, MIDIValue velocity)
{
MIDIMessage message(MIDI_NOTE_OFF_STATUS, channel, key, velocity);
SendMIDIMessage(&message, 3);
}
inline void AudioCard::SendProgramChange(MIDIChannel channel, MIDIValue program)
{
MIDIMessage message(MIDI_PROGRAM_CHANGE_STATUS, channel, program);
SendMIDIMessage(&message, 2);
}
inline void AudioCard::SendController(MIDIChannel channel, MIDIValue controller, MIDIValue value)
{
MIDIMessage message(MIDI_CONTROLLER_STATUS, channel, controller, value);
SendMIDIMessage(&message, 3);
}
inline void AudioCard::SendPolyKeyPressure(MIDIChannel channel, MIDIValue key, MIDIValue value)
{
MIDIMessage message(MIDI_POLYKEY_STATUS, channel, key, value);
SendMIDIMessage(&message, 3);
}
inline void AudioCard::SendChannelPressure(MIDIChannel channel, MIDIValue value)
{
MIDIMessage message(MIDI_CHANNEL_PRESSURE_STATUS, channel, value);
SendMIDIMessage(&message, 2);
}
inline void AudioCard::SelectBank(MIDIChannel channel, MIDIValue bank)
{
SendController(channel, (MIDIValue)0, bank);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioChannel inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//--------------------------------------------------------------------
// MIDI methods
//--------------------------------------------------------------------
//
inline void AudioChannel::SendNoteOn(MIDIValue key, MIDIValue velocity)
{
DEBUG_STREAM << "Sending note " << (int)key << " for bank " << (int)myBank << ", preset " << (int)myPreset << "." << std::endl << std::flush;
//Check(audioCard);
//audioCard->SendNoteOn(midiChannel,key,velocity);
}
inline void AudioChannel::SendNoteOff(MIDIValue key, MIDIValue velocity)
{
Check(audioCard);
audioCard->SendNoteOff(midiChannel,key,velocity);
}
inline void AudioChannel::SendProgramChange(MIDIValue program)
{
Check(audioCard);
myPreset = program;
audioCard->SendProgramChange(midiChannel,program);
}
inline void AudioChannel::SendController(MIDIValue controller, MIDIValue value)
{
Check(audioCard);
audioCard->SendController(midiChannel,controller,value);
}
inline void AudioChannel::SendPolyKeyPressure(MIDIValue key, MIDIValue value)
{
Check(audioCard);
audioCard->SendPolyKeyPressure(midiChannel,key,value);
}
inline void AudioChannel::SendChannelPressure(MIDIValue value)
{
Check(audioCard);
audioCard->SendChannelPressure(midiChannel,value);
}
inline void AudioChannel::SendPitchBend(MIDIPitchBend pitch_bend)
{
Check(audioCard);
audioCard->SendPitchBend(midiChannel,pitch_bend);
}
inline void AudioChannel::SendPitchBendRange(MIDIValue sensitivity)
{
Check(audioCard);
audioCard->SendPitchBendRange(midiChannel,sensitivity);
}
inline void AudioChannel::SelectEffect(MIDIEffectType type, MIDIValue effect)
{
Check(audioCard);
audioCard->SelectEffect(type,effect);
}
inline void AudioChannel::SendNRPN(MIDIValue paramNumber, MIDINRPNValue value)
{
Check(audioCard);
audioCard->SendNRPN(midiChannel,paramNumber,value);
}
inline void AudioChannel::SelectBank(MIDIValue bank)
{
Check(audioCard);
myBank = bank;
audioCard->SelectBank(midiChannel,bank);
}
inline void AudioChannel::Release()
{
Check(audioCard);
audioCard->ReleaseAudioChannel(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~ AudioHardware inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~
inline AudioCard* AudioHardware::GetFrontCard()
{
Check(this);
return &frontCard;
}
inline AudioCard* AudioHardware::GetRearCard()
{
Check(this);
return &rearCard;
}*/