Files
BT411/engine/MUNGA_L4/L4AUDHDW.h
T
arcattackandClaude Fable 5 a0cec48e3f Death-crash FIXED: 25-voice explosion preset overflowed the 5-slot audio SourceSet (Gitea #12)
The weekend's crash family root-caused under cdb: SourceSet.sources[5]
receives the AllExplosion death preset's 25 streamed voices --
RequestAudioChannels wrote 20 OpenAL source ids past the array, smashing
the neighbouring heap object (Release: a vtable overwritten with a source
id -> delayed silent AV at AudioControlEvent::Send; Debug: CRT heap-
corruption abort in the death Explosion's audio teardown).  Explains all
three #12 crash flavors (solo enemy kill -- stack-confirmed; MP self-
death; MP peer PEER_DOWN cascade -- same generic teardown).

Fix: sources[] sized to AUDIO_SOURCESET_CAPACITY=25, static_assert
lockstep with MAX_PRESET_SAMPLES, + defence-in-depth clamps at the ctor
and acquisition sites.  Soak: 26+ deaths across glass AND pod builds
under cdb, zero faults, full wreck lifecycle every time.  Gotchas S21:
the fixed-array-vs-streamed-count overflow class + sweep note.

Awaiting human verification: the MP death-and-survive session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 14:51:35 -05:00

545 lines
16 KiB
C++

#pragma once
#include "..\munga\style.h"
#include "..\munga\node.h"
#include "..\munga\schain.h"
#include "..\munga\audio.h"
#include "openal/al.h"
// SourceSet capacity (Gitea #12 death-crash fix): the OpenAL sources array
// must hold as many voices as a preset can carry. channelSet.count is set
// from GetAudioVoiceCount() (the streamed voiceCount), and a full-zone preset
// legitimately carries up to MAX_PRESET_SAMPLES (== 25, L4AUDLVL.h) sample
// zones -- one OpenAL source each (RequestAudioChannels alGenSources into
// sources[0..count-1], SetupPatch/PlayNote index sources[i] parallel to the
// samples[] zones). This array was 5 wide, so the death-boom preset (>5
// voices) overwrote adjacent heap on channel acquisition -- corrupting the
// next AudioComponent's vtable / the heap guard bytes and crashing seconds
// later at the death Explosion's audio-entity teardown (DestroyEntityAudio-
// Objects) or the next sequenced virtual dispatch. L4AUDLVL.h static_asserts
// MAX_PRESET_SAMPLES <= AUDIO_SOURCESET_CAPACITY to keep the two in lockstep.
const int AUDIO_SOURCESET_CAPACITY = 25;
struct SourceSet
{
int count;
ALuint sources[AUDIO_SOURCESET_CAPACITY];
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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;
}*/