Files
RP412/MUNGA_L4/L4AUDIO.h
T
CydandClaude Opus 5 ce1b0ab9c3 Sounds fade, dull and doppler with distance again
The OpenAL port kept the whole authored audio model and then threw most of
its output away. Every frame the engine computed a distance-attenuation
curve, a high-frequency rolloff, doppler cents, a reverb level and a
front/rear placement, and every one of those consumers had been commented
out when the two AWE32 cards were replaced. What reached the speakers was
OpenAL's own defaults instead: a straight-line fade to silence, no
filtering, doppler at the wrong constants with an inverted velocity, no
reverb, and every cockpit sound dead centre.

Restored, per AUDIO.INI, which is byte-identical to the file that shipped
in August 1995:

  - the authored knee/rolloff distance curve, replacing AL_LINEAR_DISTANCE.
    This also un-blinds the transient cull, the voice-steal weighting and
    the mix ducking, which all key off it and were treating far sources as
    full presence
  - the CC7 squared volume law; writing the scale linearly ran everything
    about 6 dB hot at mid-scale
  - brightness and distance muffling, and the wet-exterior/dry-cockpit
    reverb split, both through a new OpenAL EFX bridge
  - doppler on the moving-source path only, as the original had it
  - front/rear placement from the authored position enum

The larger find is that AL_PITCH was never called anywhere in the tree, so
the entire pitch chain was inert - not only doppler but pitch_mix_offset,
which our own sequences author 97 times. Doppler alone would have changed
nothing audible.

Note pitch is applied for parity with the BT engine but is identity here:
our content predates NoteAudioControlID, so every source runs at note 60.

Builds clean on VS2022 Release|Win32. Smoke-tested against vRIO on COM1 -
reaches gameplay and holds a steady frame loop. ALC_EXT_EFX is present on
the build machine with all nine entry points, so the filter and reverb work
is live rather than inert. Not yet listened to on the pod, which is the
real test: the volume law changes the level of everything.

docs/SOUND.md documents the original two-card quadraphonic design, where
the surviving original assets are, and what remains.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 23:00:37 -05:00

809 lines
16 KiB
C++

#pragma once
#include "l4audhdw.h"
#include "..\munga\audio.h"
#include "..\munga\audloc.h"
#include "..\munga\audsrc.h"
//##########################################################################
//####################### L4AudioSpatialization ######################
//##########################################################################
class L4AudioSpatialization SIGNATURED
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Public types
//
public:
enum Quadrant
{
Quadrant1,
Quadrant2,
Quadrant3,
Quadrant4
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Contstruction, Destruction, & Testing
//
public:
L4AudioSpatialization();
~L4AudioSpatialization();
Logical
TestInstance() const;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Spatialization calculations & results
//
public:
void
CalculateSpatialization(Radian azimuth_of_source);
Radian
azimuthOfSource;
Scalar
frontLeftScale,
frontRightScale,
rearLeftScale,
rearRightScale;
Scalar
frontLeftDelay,
frontRightDelay,
rearLeftDelay,
rearRightDelay;
Quadrant
quadrant;
};
//~~~~~~~~~~~~~~~~~~~~~~~ L4AudioSpatialization inlines ~~~~~~~~~~~~~~~~~~~~
inline MemoryStream&
MemoryStream_Read(
MemoryStream* stream,
L4AudioSpatialization::Quadrant *ptr
)
{
return stream->ReadBytes(ptr, sizeof(*ptr));
}
inline MemoryStream&
MemoryStream_Write(
MemoryStream* stream,
const L4AudioSpatialization::Quadrant *ptr
)
{
return stream->WriteBytes(ptr, sizeof(*ptr));
}
//##########################################################################
//######################### L4AudioLocation ##########################
//##########################################################################
class L4AudioLocation:
public AudioLocation
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction, Testing
//
public:
L4AudioLocation(
PlugStream *stream,
Entity *entity
);
void
L4AudioLocationX();
~L4AudioLocation();
Logical
TestInstance() const;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Spatialization Accessors
//
public:
void
GetSpatialization(L4AudioSpatialization *the_spatialization)
{*the_spatialization = spatialization;}
L4AudioSpatialization::Quadrant
GetQuadrant()
{return spatialization.quadrant;}
Scalar
GetFrontLeftScale()
{return spatialization.frontLeftScale;}
Scalar
GetFrontRightScale()
{return spatialization.frontRightScale;}
Scalar
GetRearLeftScale()
{return spatialization.rearLeftScale;}
Scalar
GetRearRightScale()
{return spatialization.rearRightScale;}
Scalar
GetFrontLeftITDPitchOffset()
{return frontLeftITDPitchOffset;}
Scalar
GetFrontRightITDPitchOffset()
{return frontRightITDPitchOffset;}
Scalar
GetRearLeftITDPitchOffset()
{return rearLeftITDPitchOffset;}
Scalar
GetRearRightITDPitchOffset()
{return rearRightITDPitchOffset;}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Spatialization Implementation
//
public:
void
UpdateSpatialModelImplementation(AudioHead *audio_head);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
L4AudioSpatialization
spatialization;
Time
lastITDFrameTime;
Scalar
currentFrontLeftDelay,
currentFrontRightDelay,
currentRearLeftDelay,
currentRearRightDelay;
AudioPitchCents
frontLeftITDPitchOffset,
frontRightITDPitchOffset,
rearLeftITDPitchOffset,
rearRightITDPitchOffset;
};
//##########################################################################
//######################### AudioChannelSet ##########################
//##########################################################################
/*#define AUDIO_CHANNEL_SET_LENGTH (4)
typedef int AudioChannelSetIndex;
const AudioChannelSetIndex AudioChannelSetSize = AUDIO_CHANNEL_SET_LENGTH;
class AudioChannelSet SIGNATURED
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction, & Testing
//
public:
AudioChannelSet();
~AudioChannelSet() {}
Logical
TestInstance() const;
static void
BuildFromPage(
PlugStream *stream,
NameList *name_list,
RegisteredClass::ClassID class_ID,
ObjectID object_ID
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Channel accesors
//
public:
void
SetFrontLeft(AudioChannel *audio_channel);
void
SetFrontRight(AudioChannel *audio_channel);
void
SetRearLeft(AudioChannel *audio_channel);
void
SetRearRight(AudioChannel *audio_channel);
AudioChannel*
GetFrontLeft();
AudioChannel*
GetFrontRight();
AudioChannel*
GetRearLeft();
AudioChannel*
GetRearRight();
void
SetNth(
AudioChannelSetIndex index,
AudioChannel *audio_channel
);
AudioChannel*
GetNth(AudioChannelSetIndex index);
void
ReleaseAll();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Enable accesors
//
public:
void
EnableFrontLeft(Logical flag);
void
EnableFrontRight(Logical flag);
void
EnableRearLeft(Logical flag);
void
EnableRearRight(Logical flag);
Logical
IsFrontLeftEnabled();
Logical
IsFrontRightEnabled();
Logical
IsRearLeftEnabled();
Logical
IsRearRightEnabled();
void
EnableNth(
AudioChannelSetIndex index,
Logical flag
);
void
EnableAll();
Logical
IsNthEnabled(AudioChannelSetIndex index);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Null channel set
//
public:
static AudioChannelSet
Null;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
AudioChannel
*channels[AUDIO_CHANNEL_SET_LENGTH];
Logical
enables[AUDIO_CHANNEL_SET_LENGTH];
};*/
//##########################################################################
//########################## L4AudioSource ###########################
//##########################################################################
class L4AudioSource:
public AudioSource
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Destruction, Testing
//
public:
~L4AudioSource();
Logical
TestInstance() const;
static void
BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// AudioChannelSet methods
//
public:
/* void
SetAudioChannelSet(const AudioChannelSet &audio_channel_set);
const AudioChannelSet*
GetAudioChannelSet();
void
ReleaseChannels();*/
void SetAudioChannelSet(const SourceSet &source_set);
SourceSet *GetAudioChannelSet();
void ReleaseChannels();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Protected Constuction
//
protected:
L4AudioSource(
PlugStream *stream,
Entity *entity
);
void
L4AudioSourceX();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Protected Implementations
//
protected:
void
StopMessageImplementation();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Protected Data
//
protected:
/*AudioChannelSet
channelSet;*/
SourceSet channelSet;
};
//##########################################################################
//######################## DirectPatchSource #########################
//##########################################################################
enum DirectPatchPosition
{
FrontDirectPatchPosition = 0,
RearDirectPatchPosition = 1,
FrontLeftDirectPatchPosition = 2,
FrontRightDirectPatchPosition = 3,
RearLeftDirectPatchPosition = 4,
RearRightDirectPatchPosition = 5
};
class DirectPatchSource:
public L4AudioSource
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction, Testing
//
public:
DirectPatchSource(
PlugStream *stream,
Entity *entity
);
~DirectPatchSource();
Logical
TestInstance() const;
static void
BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// State implementations
//
public:
void
StartImplementation();
void
StopImplementation();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Protected Implementations
//
protected:
void
StartMessageImplementation();
void
ExecuteModel(Logical force_update);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
DirectPatchPosition
audioPosition;
/* AudioChannel
*channel;*/
MIDIValue
lastMIDIVolume;
MIDIPitchBend
lastMIDIPitchBend;
MIDINRPNValue
lastMIDIFilterCutoff;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~ DirectPatchSource inlines ~~~~~~~~~~~~~~~~~~~~
inline MemoryStream&
MemoryStream_Read(
MemoryStream *stream,
DirectPatchPosition *ptr
)
{
return stream->ReadBytes(ptr, sizeof(*ptr));
}
inline MemoryStream&
MemoryStream_Write(
MemoryStream *stream,
const DirectPatchPosition *ptr
)
{
return stream->WriteBytes(ptr, sizeof(*ptr));
}
//##########################################################################
//###################### Dynamic3DPatchSource ########################
//##########################################################################
class Dynamic3DPatchSource:
public L4AudioSource
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction, Testing
//
public:
Dynamic3DPatchSource(
PlugStream *stream,
Entity *entity
);
~Dynamic3DPatchSource();
Logical
TestInstance() const;
virtual Logical IsAudioSourceClipped(AudioHead *audio_head);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Mix levels
//
public:
AudioControlValue
CalculateSourceVolumeScale();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// State implementations
//
public:
void
StartImplementation();
void
StopImplementation();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Protected implementations
//
protected:
void
StartMessageImplementation();
void
ExecuteModel(Logical force_update);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
MIDIValue
lastFrontLeftMIDIVolume,
lastFrontRightMIDIVolume,
lastRearLeftMIDIVolume,
lastRearRightMIDIVolume;
MIDIPitchBend
lastFrontLeftMIDIPitchBend,
lastFrontRightMIDIPitchBend,
lastRearLeftMIDIPitchBend,
lastRearRightMIDIPitchBend;
MIDINRPNValue
lastMIDIFilterCutoff;
};
//##########################################################################
//####################### Static3DPatchSource ########################
//##########################################################################
class Static3DPatchSource:
public L4AudioSource
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction, Testing
//
public:
Static3DPatchSource(
PlugStream *stream,
Entity *entity
);
~Static3DPatchSource();
Logical
TestInstance() const;
static void
BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
);
virtual Logical IsAudioSourceClipped(AudioHead *audio_head);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Mix levels
//
public:
AudioControlValue
CalculateSourceVolumeScale();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SetPosition
//
public:
void
SetPosition(const Vector3D &vector3D);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// State implementations
//
public:
void
StartImplementation();
void
StopImplementation();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Protected implementations
//
protected:
void
StartMessageImplementation();
void
ExecuteModel(Logical force_update);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private Methods
//
private:
L4AudioSpatialization::Quadrant
GetQuadrant();
Radian
GetAzimuthOfSource();
Scalar
GetFrontLeftScale();
Scalar
GetFrontRightScale();
Scalar
GetRearLeftScale();
Scalar
GetRearRightScale();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
Vector3D
position;
Logical
useInternalSpatialization;
L4AudioSpatialization
internalSpatialization;
MIDIValue
lastFrontLeftMIDIVolume,
lastFrontRightMIDIVolume,
lastRearLeftMIDIVolume,
lastRearRightMIDIVolume;
MIDIPitchBend
lastMIDIPitchBend;
MIDINRPNValue
lastMIDIFilterCutoff;
};
//~~~~~~~~~~~~~~~~~~~~~~~~ Static3DPatchSource inlines ~~~~~~~~~~~~~~~~~~~~~
inline L4AudioSpatialization::Quadrant
Static3DPatchSource::GetQuadrant()
{
return internalSpatialization.quadrant;
}
inline Radian
Static3DPatchSource::GetAzimuthOfSource()
{
return internalSpatialization.azimuthOfSource;
}
inline Scalar
Static3DPatchSource::GetFrontLeftScale()
{
return internalSpatialization.frontLeftScale;
}
inline Scalar
Static3DPatchSource::GetFrontRightScale()
{
return internalSpatialization.frontRightScale;
}
inline Scalar
Static3DPatchSource::GetRearLeftScale()
{
return internalSpatialization.rearLeftScale;
}
inline Scalar
Static3DPatchSource::GetRearRightScale()
{
return internalSpatialization.rearRightScale;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioChannelSet inlines ~~~~~~~~~~~~~~~~~~~~~~
/*inline void
AudioChannelSet::SetFrontLeft(AudioChannel *audio_channel)
{
Check(this);
channels[0] = audio_channel;
}
inline void
AudioChannelSet::SetFrontRight(AudioChannel *audio_channel)
{
Check(this);
channels[1] = audio_channel;
}
inline void
AudioChannelSet::SetRearLeft(AudioChannel *audio_channel)
{
Check(this);
channels[2] = audio_channel;
}
inline void
AudioChannelSet::SetRearRight(AudioChannel *audio_channel)
{
Check(this);
channels[3] = audio_channel;
}
inline AudioChannel*
AudioChannelSet::GetFrontLeft()
{
Check(this);
return channels[0];
}
inline AudioChannel*
AudioChannelSet::GetFrontRight()
{
Check(this);
return channels[1];
}
inline AudioChannel*
AudioChannelSet::GetRearLeft()
{
Check(this);
return channels[2];
}
inline AudioChannel*
AudioChannelSet::GetRearRight()
{
Check(this);
return channels[3];
}
inline void
AudioChannelSet::SetNth(
AudioChannelSetIndex index,
AudioChannel *audio_channel
)
{
Check(this);
Verify(index >=0 && index < AUDIO_CHANNEL_SET_LENGTH);
channels[index] = audio_channel;
}
inline AudioChannel*
AudioChannelSet::GetNth(AudioChannelSetIndex index)
{
Check(this);
Verify(index >=0 && index < AUDIO_CHANNEL_SET_LENGTH);
return channels[index];
}
inline void
AudioChannelSet::EnableFrontLeft(Logical flag)
{
Check(this);
enables[0] = flag;
}
inline void
AudioChannelSet::EnableFrontRight(Logical flag)
{
Check(this);
enables[1] = flag;
}
inline void
AudioChannelSet::EnableRearLeft(Logical flag)
{
Check(this);
enables[2] = flag;
}
inline void
AudioChannelSet::EnableRearRight(Logical flag)
{
Check(this);
enables[3] = flag;
}
inline Logical
AudioChannelSet::IsFrontLeftEnabled()
{
Check(this);
return enables[0];
}
inline Logical
AudioChannelSet::IsFrontRightEnabled()
{
Check(this);
return enables[1];
}
inline Logical
AudioChannelSet::IsRearLeftEnabled()
{
Check(this);
return enables[2];
}
inline Logical
AudioChannelSet::IsRearRightEnabled()
{
Check(this);
return enables[3];
}
inline void
AudioChannelSet::EnableNth(
AudioChannelSetIndex index,
Logical flag
)
{
Check(this);
Verify(index >=0 && index < AUDIO_CHANNEL_SET_LENGTH);
enables[index] = flag;
}
inline Logical
AudioChannelSet::IsNthEnabled(AudioChannelSetIndex index)
{
Check(this);
Verify(index >=0 && index < AUDIO_CHANNEL_SET_LENGTH);
return enables[index];
}*/