The raw AWE32 samples are hot and everything plays at full per-source gain, so the mix was loud. Set alListenerf(AL_GAIN) once at device init -- it scales EVERY source (master volume). Default 0.6; override with BT_AUDIO_VOLUME=<0.0..1.0+> (0 = mute, 1 = full, >1 = boost). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1431 lines
37 KiB
C++
1431 lines
37 KiB
C++
#include <cstdlib>
|
|
#include "mungal4.h"
|
|
#pragma hdrstop
|
|
|
|
#include "l4audrnd.h"
|
|
#include "..\munga\notation.h"
|
|
#include "openal/alc.h"
|
|
|
|
//
|
|
//#############################################################################
|
|
// L4AudioRenderer
|
|
//#############################################################################
|
|
//
|
|
L4AudioRenderer::L4AudioRenderer(
|
|
RendererRate render_rate,
|
|
Logical mission_review_mode
|
|
):
|
|
AudioRenderer(render_rate),
|
|
runningAudioSourceSocket(NULL, False),
|
|
dormantAudioSourceSocket(NULL),
|
|
mixingAudioSourceSocket(NULL, False)
|
|
{
|
|
missionReviewMode = mission_review_mode;
|
|
nextCalculateMixFrame = NullAudioFrameCount;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ~L4AudioRenderer
|
|
//#############################################################################
|
|
//
|
|
L4AudioRenderer::~L4AudioRenderer()
|
|
{
|
|
//Close OpenAL
|
|
ALCcontext *context = alcGetCurrentContext();
|
|
ALCdevice *device = alcGetContextsDevice(context);
|
|
alcMakeContextCurrent(NULL);
|
|
alcDestroyContext(context);
|
|
alcCloseDevice(device);
|
|
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Verify that all of entities have become uninteresting
|
|
//----------------------------------------------------------------------
|
|
//
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
RunningSourceIterator iterator(&runningAudioSourceSocket);
|
|
Verify(iterator.GetSize() == 0);
|
|
}
|
|
{
|
|
DormantSourceIterator iterator(&dormantAudioSourceSocket);
|
|
Verify(iterator.GetSize() == 0);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Unload resources
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Check(&audioResourceManager);
|
|
// audioResourceManager.UnloadResources(&audioHardware);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Close audio hardware
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// Check(&audioHardware);
|
|
// audioHardware.Close();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// TestInstance
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
L4AudioRenderer::TestInstance() const
|
|
{
|
|
AudioRenderer::TestInstance();
|
|
|
|
Check(&audioHardware);
|
|
Check(&runningAudioSourceSocket);
|
|
Check(&dormantAudioSourceSocket);
|
|
Check(&mixingAudioSourceSocket);
|
|
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// Initialize
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::Initialize()
|
|
{
|
|
Check(this);
|
|
if (getenv("BT_AUDIO_LOG")) DEBUG_STREAM << "[audio] L4AudioRenderer::Initialize ENTERED\n" << std::flush;
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Call inherited method
|
|
//----------------------------------------------------------------------
|
|
//
|
|
AudioRenderer::Initialize();
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Get the audio head
|
|
//----------------------------------------------------------------------
|
|
//
|
|
AudioHead *audio_head = GetAudioHead();
|
|
Check(audio_head);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Open the audio renderer notation file
|
|
//----------------------------------------------------------------------
|
|
//
|
|
int ret;
|
|
CString audio_file_name;
|
|
|
|
if (missionReviewMode)
|
|
{
|
|
audio_file_name = "audio\\audiomr.ini";
|
|
}
|
|
else
|
|
{
|
|
audio_file_name = "audio\\audio.ini";
|
|
}
|
|
|
|
NotationFile notation_file(audio_file_name);
|
|
Check(¬ation_file);
|
|
if (getenv("BT_AUDIO_LOG")) DEBUG_STREAM << "[audio] notation file opened; about to read scalars\n" << std::flush;
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Define clipping sphere (m)
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar clipping_radius;
|
|
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"clipping_radius",
|
|
&clipping_radius
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - clipping_radius not defined");
|
|
}
|
|
Check(audio_head);
|
|
audio_head->DefineClippingSphere(clipping_radius);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Distance to speakers or width of head entity (m)
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar distance_between_ears;
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"distance_between_ears",
|
|
&distance_between_ears
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - distance_between_ears not defined");
|
|
}
|
|
Check(audio_head);
|
|
audio_head->SetDistanceBetweenEars(distance_between_ears);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Define amplitude rolloff characteristics
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar amplitude_rolloff;
|
|
Scalar amplitude_rolloff_knee;
|
|
Scalar amplitude_rolloff_distance_scale;
|
|
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"amplitude_rolloff",
|
|
&litude_rolloff
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - amplitude_rolloff not defined");
|
|
}
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"amplitude_rolloff_distance_scale",
|
|
&litude_rolloff_distance_scale
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - amplitude_rolloff_distance_scale not defined");
|
|
}
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"amplitude_rolloff_knee",
|
|
&litude_rolloff_knee
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - amplitude_rolloff_knee not defined");
|
|
}
|
|
|
|
Check(audio_head);
|
|
audio_head->ControlAmplitudeRollOff(
|
|
amplitude_rolloff,
|
|
amplitude_rolloff_knee,
|
|
amplitude_rolloff_distance_scale
|
|
);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Define high frequency rolloff characteristics
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar high_frequency_rolloff;
|
|
Scalar high_frequency_rolloff_knee;
|
|
Scalar high_frequency_rolloff_distance_scale;
|
|
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"high_frequency_rolloff",
|
|
&high_frequency_rolloff
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - high_frequency_rolloff not defined");
|
|
}
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"high_frequency_rolloff_knee",
|
|
&high_frequency_rolloff_knee
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - high_frequency_rolloff_knee not defined");
|
|
}
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"high_frequency_rolloff_distance_scale",
|
|
&high_frequency_rolloff_distance_scale
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - high_frequency_rolloff_distance_scale not defined");
|
|
}
|
|
|
|
Check(audio_head);
|
|
audio_head->ControlHighFrequencyRollOff(
|
|
high_frequency_rolloff,
|
|
high_frequency_rolloff_knee,
|
|
high_frequency_rolloff_distance_scale
|
|
);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Define Doppler characterisitics
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar doppler_range;
|
|
Scalar speed_of_sound;
|
|
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"doppler_range",
|
|
&doppler_range
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - doppler_range not defined");
|
|
}
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"speed_of_sound",
|
|
&speed_of_sound
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - speed_of_sound not defined");
|
|
}
|
|
|
|
Check(audio_head);
|
|
audio_head->ControlDopplerEffect(
|
|
doppler_range,
|
|
speed_of_sound
|
|
);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Define source compression characterisitics
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar compression_exponent;
|
|
Scalar compression_scale;
|
|
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"compression_exponent",
|
|
&compression_exponent
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - compression_exponent not defined");
|
|
}
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"compression_scale",
|
|
&compression_scale
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - compression_scale not defined");
|
|
}
|
|
|
|
Check(audio_head);
|
|
audio_head->ControlSourceCompression(
|
|
compression_exponent,
|
|
compression_scale
|
|
);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Set ITD difference
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar itd_difference;
|
|
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"itd_difference",
|
|
&itd_difference
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - itd_difference not defined");
|
|
}
|
|
|
|
Check(audio_head);
|
|
audio_head->SetITDDifference(itd_difference);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Set global reverb scale
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar global_reverb_scale;
|
|
|
|
ret = notation_file.GetEntry(
|
|
"AudioRenderer",
|
|
"global_reverb_scale",
|
|
&global_reverb_scale
|
|
);
|
|
if (!ret)
|
|
{
|
|
Fail("L4AudioRenderer::Initialize - global_reverb_scale not defined");
|
|
}
|
|
|
|
Check(audio_head);
|
|
audio_head->SetGlobalReverbScale(global_reverb_scale);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Initialize the audio hardware
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// Check(&audioHardware);
|
|
// audioHardware.Initialize();
|
|
|
|
//Start OpenAL
|
|
ALCdevice *device = alcOpenDevice(NULL);
|
|
if (device)
|
|
{
|
|
ALCcontext *context = alcCreateContext(device,NULL);
|
|
alcMakeContextCurrent(context);
|
|
// Master volume: AL_GAIN on the listener scales EVERY source. Default 0.6
|
|
// (the raw samples are hot); override with BT_AUDIO_VOLUME=<0.0..1.0+>.
|
|
float masterVol = 0.6f;
|
|
if (const char *v = getenv("BT_AUDIO_VOLUME")) { float f = (float)atof(v); if (f >= 0.0f) masterVol = f; }
|
|
alListenerf(AL_GAIN, masterVol);
|
|
if (getenv("BT_AUDIO_LOG")) DEBUG_STREAM << "[audio] OpenAL device OPENED + context current; master gain=" << masterVol << "\n" << std::flush;
|
|
}
|
|
else
|
|
{
|
|
if (getenv("BT_AUDIO_LOG")) DEBUG_STREAM << "[audio] OpenAL device FAILED to open (alcOpenDevice NULL)\n" << std::flush;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Preload resources
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Check(&audioResourceManager);
|
|
audioResourceManager.PreloadResources(¬ation_file);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// NotifyOfNewInterestingEntity
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::NotifyOfNewInterestingEntity(Entity *interesting_entity)
|
|
{
|
|
SET_AUDIO_RENDERER();
|
|
SET_AUDIO_RENDERER_CREATE_OBJECTS();
|
|
|
|
Check(this);
|
|
Check(interesting_entity);
|
|
|
|
Entity *linked_entity = GetLinkedEntity();
|
|
Check(linked_entity);
|
|
|
|
AudioRepresentation audio_representation =
|
|
(AudioRepresentation)interesting_entity->GetAudioRepresentation(
|
|
linked_entity
|
|
);
|
|
|
|
Check(&audioResourceManager);
|
|
audioResourceManager.CreateEntityAudioObjects(
|
|
interesting_entity,
|
|
audio_representation
|
|
);
|
|
|
|
CLEAR_AUDIO_RENDERER_CREATE_OBJECTS();
|
|
CLEAR_AUDIO_RENDERER();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// NotifyOfBecomingUninterestingEntity
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::NotifyOfBecomingUninterestingEntity(
|
|
Entity *uninteresting_entity
|
|
)
|
|
{
|
|
SET_AUDIO_RENDERER();
|
|
SET_AUDIO_RENDERER_DESTROY_OBJECTS();
|
|
|
|
Check(this);
|
|
Check(uninteresting_entity);
|
|
|
|
Check(&audioResourceManager);
|
|
audioResourceManager.DestroyEntityAudioObjects(uninteresting_entity);
|
|
|
|
CLEAR_AUDIO_RENDERER_DESTROY_OBJECTS();
|
|
CLEAR_AUDIO_RENDERER();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// LoadMissionImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::LoadMissionImplementation(Mission*)
|
|
{
|
|
Check(this);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ShutdownImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::ShutdownImplementation()
|
|
{
|
|
Check(this);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// SuspendImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::SuspendImplementation()
|
|
{
|
|
Check(this);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ResumeImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::ResumeImplementation()
|
|
{
|
|
Check(this);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// StartRequest
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::StartRequest(L4AudioSource *audio_source)
|
|
{
|
|
Check(this);
|
|
Check(audio_source);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Verify that the source is stopped
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Verify(audio_source->GetAudioSourceState() == StoppedAudioSourceState);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Request audio resources
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Logical
|
|
resources_available;
|
|
/* AudioChannelSet
|
|
channel_set_result;*/
|
|
SourceSet *source_result = audio_source->GetAudioChannelSet();
|
|
|
|
resources_available =
|
|
RequestAudioResources(
|
|
audio_source,
|
|
source_result
|
|
);
|
|
if (!resources_available)
|
|
{
|
|
if (audio_source->GetAudioRenderType() == SustainedAudioRenderType)
|
|
{
|
|
//
|
|
// Set state to dormant and add the source to the dormant list
|
|
//
|
|
audio_source->AssignAudioSourceState(DormantAudioSourceState);
|
|
dormantAudioSourceSocket.Add(audio_source);
|
|
}
|
|
return;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Start the audio source
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
|
|
//
|
|
// Update source spatial model
|
|
// Set channel set of source
|
|
// Source start implementation
|
|
// Set state of source to running
|
|
//
|
|
audio_source->UpdateSpatialModel(GetAudioHead());
|
|
audio_source->SetAudioChannelSet(*source_result);
|
|
audio_source->StartImplementation();
|
|
audio_source->AssignAudioSourceState(RunningAudioSourceState);
|
|
|
|
//
|
|
// Add source to the running list and the mixing list
|
|
//
|
|
runningAudioSourceSocket.AddValue(
|
|
audio_source,
|
|
audio_source->CalculateAudioWeighting()
|
|
);
|
|
if (
|
|
audio_source->GetAudioSourceMixPresence() !=
|
|
ManualAudioSourceMixPresence
|
|
)
|
|
{
|
|
mixingAudioSourceSocket.AddValue(
|
|
audio_source,
|
|
audio_source->GetAudioSourceMixPresence()
|
|
);
|
|
}
|
|
#ifdef LAB_ONLY
|
|
sourceStartCount++;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// StopRequest
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::StopRequest(L4AudioSource *audio_source)
|
|
{
|
|
Check(this);
|
|
Check(audio_source);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Verify that the source is not stopped
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Verify(audio_source->GetAudioSourceState() != StoppedAudioSourceState);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If state of the source is dormant or suspended
|
|
// Then set source to stop state and remove from dormant list
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (
|
|
audio_source->GetAudioSourceState() == DormantAudioSourceState ||
|
|
audio_source->GetAudioSourceState() == SuspendedAudioSourceState
|
|
)
|
|
{
|
|
//
|
|
// Set state of source to stopped
|
|
// Verify that source is in dormant list
|
|
// Remove source from dormant list
|
|
// Verify that source is not in dormant list
|
|
//
|
|
audio_source->AssignAudioSourceState(StoppedAudioSourceState);
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
DormantSourceIterator iterator(&dormantAudioSourceSocket);
|
|
Verify(iterator.IsPlugMember(audio_source) == True);
|
|
}
|
|
#endif
|
|
PlugIterator remover(audio_source);
|
|
remover.RemoveSocket(&dormantAudioSourceSocket);
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
DormantSourceIterator iterator(&dormantAudioSourceSocket);
|
|
Verify(iterator.IsPlugMember(audio_source) == False);
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Stop the source
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
AudioSourceStopMaintenance(audio_source);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ExecuteImplementation
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::ExecuteImplementation(
|
|
RendererComplexity complexity_update,
|
|
RendererOrigin::InterestingEntityIterator *iterator
|
|
)
|
|
{
|
|
SET_AUDIO_RENDERER();
|
|
SET_AUDIO_RENDERER_EXECUTE();
|
|
|
|
Check(this);
|
|
|
|
AudioRenderer::ExecuteImplementation(complexity_update, iterator);
|
|
CalculateMix();
|
|
RunningSourceCheckup();
|
|
// RunningAudioSourceSort();
|
|
DormantSourceCheckup();
|
|
|
|
CLEAR_AUDIO_RENDERER_EXECUTE();
|
|
CLEAR_AUDIO_RENDERER();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// CalculateMix
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::CalculateMix()
|
|
{
|
|
SET_AUDIO_CALCULATE_MIX();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Is this the next calculate mix frame
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
#if 0
|
|
if (nextCalculateMixFrame > GetAudioFrameCount())
|
|
{
|
|
CLEAR_AUDIO_CALCULATE_MIX();
|
|
return;
|
|
}
|
|
nextCalculateMixFrame = GetAudioFrameCount() + DefaultAudioFrameDelay;
|
|
#endif
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Calculate the compression volume scaling
|
|
//
|
|
// For each source, calculate the amount to scale its volume by
|
|
// according to those sources which have a higher mix presence
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
{
|
|
AudioControlValue
|
|
compression_scale = 1.0f;
|
|
AudioControlValue
|
|
compression_effect = 0.0f;
|
|
AudioSourceMixPresence
|
|
current_mix_presence = MedAudioSourceMixPresence;
|
|
|
|
MixingSourceIterator
|
|
iterator(&mixingAudioSourceSocket);
|
|
L4AudioSource
|
|
*audio_source;
|
|
|
|
Check(GetAudioHead());
|
|
Scalar comp_scale = GetAudioHead()->GetSourceCompressionScale();
|
|
Scalar comp_exp = GetAudioHead()->GetSourceCompressionExponent();
|
|
|
|
//
|
|
// Get mix presence of the first source
|
|
//
|
|
if ((audio_source = iterator.GetCurrent()) != NULL)
|
|
{
|
|
Check(audio_source);
|
|
current_mix_presence = audio_source->GetAudioSourceMixPresence();
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// For each audio source, calculate the compression scale
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
while ((audio_source = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
Check(audio_source);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If the mix presence of this source is greater then the last
|
|
// source then we have stepped to a lower mix presence.
|
|
//--------------------------------------------------------------------
|
|
//
|
|
if (
|
|
current_mix_presence !=
|
|
audio_source->GetAudioSourceMixPresence()
|
|
)
|
|
{
|
|
Verify(
|
|
current_mix_presence <
|
|
audio_source->GetAudioSourceMixPresence()
|
|
);
|
|
current_mix_presence = audio_source->GetAudioSourceMixPresence();
|
|
|
|
//
|
|
// Calculate the compression scale for this mix presence
|
|
//
|
|
// scale = 1 - ((comp_scale*x)^comp_exp)
|
|
//
|
|
compression_scale =
|
|
1.0f - pow(comp_scale * compression_effect, comp_exp);
|
|
Verify(compression_scale >= 0.0f && compression_scale <= 1.0f);
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Set the compression scale for this source
|
|
//--------------------------------------------------------------------
|
|
//
|
|
audio_source->SetVolumeCompressionScale(compression_scale);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Calculate the compression effect for this source
|
|
//--------------------------------------------------------------------
|
|
//
|
|
AudioControlValue source_compression_effect;
|
|
|
|
source_compression_effect =
|
|
audio_source->CalculateSourceCompressionEffect();
|
|
Verify(
|
|
source_compression_effect >= 0.0f &&
|
|
source_compression_effect <= 1.0f
|
|
);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Update the compression effect for this mix presence
|
|
//--------------------------------------------------------------------
|
|
//
|
|
compression_effect =
|
|
Max(compression_effect, source_compression_effect);
|
|
Verify(compression_effect >= 0.0f && compression_effect <= 1.0f);
|
|
|
|
#if 0
|
|
Tell(
|
|
"compression_effect " << compression_effect <<
|
|
" : compression_scale " << compression_scale << "\n"
|
|
);
|
|
#endif
|
|
}
|
|
}
|
|
CLEAR_AUDIO_CALCULATE_MIX();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// RunningSourceCheckup
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::RunningSourceCheckup()
|
|
{
|
|
SET_AUDIO_RUNNING_SOURCES();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Iterate through all running sources
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
ChainOf<L4AudioSource*>
|
|
resort_socket(NULL);
|
|
RunningSourceIterator
|
|
running_iterator(&runningAudioSourceSocket);
|
|
L4AudioSource
|
|
*audio_source;
|
|
|
|
while ((audio_source = running_iterator.GetCurrent()) != NULL)
|
|
{
|
|
Check(audio_source);
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// If the source does not require maintenance then skip it
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (!audio_source->RequiresMaintenance(GetAudioHead()))
|
|
{
|
|
running_iterator.Next();
|
|
continue;
|
|
}
|
|
|
|
//Update object position
|
|
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// If the source is clipped then either stop or suspend
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (audio_source->IsAudioSourceClipped(GetAudioHead()))
|
|
{
|
|
if (audio_source->GetAudioRenderType() == TransientAudioRenderType)
|
|
{
|
|
AudioSourceStopMaintenance(audio_source);
|
|
}
|
|
else
|
|
{
|
|
Verify(
|
|
audio_source->GetAudioRenderType() ==
|
|
SustainedAudioRenderType
|
|
);
|
|
AudioSourceSuspendMaintenance(audio_source);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// If the source is finished playing then stop it
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (audio_source->IsFinishedPlaying())
|
|
{
|
|
AudioSourceStopMaintenance(audio_source);
|
|
continue;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Execute
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
SET_AUDIO_EXECUTE_SOURCES();
|
|
audio_source->Execute();
|
|
CLEAR_AUDIO_EXECUTE_SOURCES();
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Add to resort list if weight has changed
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (
|
|
audio_source->CalculateAudioWeighting() !=
|
|
running_iterator.GetValue()
|
|
)
|
|
{
|
|
running_iterator.Remove();
|
|
resort_socket.Add(audio_source);
|
|
}
|
|
else
|
|
{
|
|
running_iterator.Next();
|
|
}
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Sort resort socket back into running socket
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
ChainIteratorOf<L4AudioSource*>
|
|
resort_iterator(&resort_socket);
|
|
|
|
while ((audio_source = resort_iterator.ReadAndNext()) != NULL)
|
|
{
|
|
Check(audio_source);
|
|
Verify(running_iterator.IsPlugMember(audio_source) == False);
|
|
runningAudioSourceSocket.AddValue(
|
|
audio_source,
|
|
audio_source->CalculateAudioWeighting()
|
|
);
|
|
}
|
|
|
|
CLEAR_AUDIO_RUNNING_SOURCES();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// DormantSourceCheckup
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::DormantSourceCheckup()
|
|
{
|
|
SET_AUDIO_DORMANT_SOURCES();
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Assemble the request socket
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
VChainOf<L4AudioSource*, AudioWeighting>
|
|
resume_request_socket(NULL, False);
|
|
DormantSourceIterator
|
|
dormant_iterator(&dormantAudioSourceSocket);
|
|
L4AudioSource
|
|
*audio_source;
|
|
|
|
while ((audio_source = dormant_iterator.ReadAndNext()) != NULL)
|
|
{
|
|
Check(audio_source);
|
|
|
|
//
|
|
// If the suspend gate is not up then it can not resume
|
|
//
|
|
if (!audio_source->CanResume(GetAudioHead()))
|
|
continue;
|
|
|
|
//
|
|
// If the audio source is clipped then it can not resume
|
|
//
|
|
if (audio_source->IsAudioSourceClipped(GetAudioHead()))
|
|
continue;
|
|
|
|
//
|
|
// Add to request socket
|
|
//
|
|
resume_request_socket.AddValue(
|
|
audio_source,
|
|
audio_source->CalculateAudioWeighting()
|
|
);
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Iterate through request socket
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
VChainIteratorOf<L4AudioSource*, AudioWeighting>
|
|
resume_request_iterator(&resume_request_socket);
|
|
|
|
while ((audio_source = resume_request_iterator.GetCurrent()) != NULL)
|
|
{
|
|
Check(audio_source);
|
|
|
|
//
|
|
// Request audio resources
|
|
//
|
|
/* AudioChannelSet
|
|
channel_set_result;*/
|
|
|
|
SourceSet *source_result = audio_source->GetAudioChannelSet();
|
|
|
|
if (!RequestAudioResources(audio_source,
|
|
source_result)
|
|
)
|
|
break;
|
|
|
|
//
|
|
// Remove from dormant list and request list
|
|
//
|
|
PlugIterator remover(audio_source);
|
|
remover.RemoveSocket(&dormantAudioSourceSocket);
|
|
AudioWeighting audio_weighting = resume_request_iterator.GetValue();
|
|
resume_request_iterator.Remove();
|
|
|
|
//
|
|
// Update Spatial model
|
|
//
|
|
audio_source->UpdateSpatialModel(GetAudioHead());
|
|
|
|
//
|
|
// Set channel set of source
|
|
// Source resume implementation
|
|
// Set state of source to running
|
|
//
|
|
audio_source->SetAudioChannelSet(*source_result);
|
|
audio_source->ResumeImplementation();
|
|
audio_source->AssignAudioSourceState(RunningAudioSourceState);
|
|
|
|
//
|
|
// Put on running socket and mixing socket
|
|
//
|
|
runningAudioSourceSocket.AddValue(audio_source, audio_weighting);
|
|
if (
|
|
audio_source->GetAudioSourceMixPresence() !=
|
|
ManualAudioSourceMixPresence
|
|
)
|
|
{
|
|
mixingAudioSourceSocket.AddValue(
|
|
audio_source,
|
|
audio_source->GetAudioSourceMixPresence()
|
|
);
|
|
}
|
|
}
|
|
CLEAR_AUDIO_DORMANT_SOURCES();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// AudioSourceStopMaintenance
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::AudioSourceStopMaintenance(L4AudioSource *source)
|
|
{
|
|
Check(source);
|
|
Verify(source->GetAudioSourceState() == RunningAudioSourceState);
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
RunningSourceIterator iterator(&runningAudioSourceSocket);
|
|
Verify(iterator.IsPlugMember(source) == True);
|
|
}
|
|
#endif
|
|
|
|
source->StopImplementation();
|
|
source->AssignAudioSourceState(StoppedAudioSourceState);
|
|
source->ReleaseChannels();
|
|
|
|
PlugIterator remover(source);
|
|
remover.RemoveSocket(&runningAudioSourceSocket);
|
|
remover.RemoveSocket(&mixingAudioSourceSocket);
|
|
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
RunningSourceIterator iterator(&runningAudioSourceSocket);
|
|
Verify(iterator.IsPlugMember(source) == False);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// AudioSourceSuspendMaintenance
|
|
//#############################################################################
|
|
//
|
|
void
|
|
L4AudioRenderer::AudioSourceSuspendMaintenance(L4AudioSource *source)
|
|
{
|
|
Check(source);
|
|
Verify(source->GetAudioRenderType() == SustainedAudioRenderType);
|
|
Verify(source->GetAudioSourceState() == RunningAudioSourceState);
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
RunningSourceIterator iterator(&runningAudioSourceSocket);
|
|
Verify(iterator.IsPlugMember(source) == True);
|
|
}
|
|
#endif
|
|
|
|
source->SuspendImplementation();
|
|
source->AssignAudioSourceState(SuspendedAudioSourceState);
|
|
source->ReleaseChannels();
|
|
|
|
PlugIterator remover(source);
|
|
remover.RemoveSocket(&runningAudioSourceSocket);
|
|
remover.RemoveSocket(&mixingAudioSourceSocket);
|
|
|
|
#if DEBUG_LEVEL>0
|
|
{
|
|
RunningSourceIterator iterator(&runningAudioSourceSocket);
|
|
Verify(iterator.IsPlugMember(source) == False);
|
|
}
|
|
#endif
|
|
|
|
dormantAudioSourceSocket.Add(source);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// RequestAudioResources
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
L4AudioRenderer::RequestAudioResources(
|
|
L4AudioSource *audio_source,
|
|
SourceSet *source_result
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(audio_source);
|
|
Check(source_result);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// This source must be within the culling volume
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (audio_source->IsAudioSourceClipped(GetAudioHead()))
|
|
return False;
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Request audio channels for this source
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Logical
|
|
resources_available;
|
|
|
|
source_result = audio_source->GetAudioChannelSet();
|
|
Check(source_result);
|
|
|
|
resources_available =
|
|
RequestAudioChannels(
|
|
audio_source->GetAudioVoiceCount(),
|
|
source_result
|
|
);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If audio channels are not available then check running sources for
|
|
// lower weight sources
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (!resources_available)
|
|
{
|
|
RunningSourceIterator
|
|
iterator(&runningAudioSourceSocket);
|
|
L4AudioSource
|
|
*running_audio_source;
|
|
AudioWeighting
|
|
audio_source_weighting;
|
|
|
|
iterator.Last();
|
|
running_audio_source = iterator.GetCurrent();
|
|
audio_source_weighting = audio_source->CalculateAudioWeighting();
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// while
|
|
// request for channels is not satisfied and
|
|
// there exist running sources that have lower weighting
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
while (
|
|
!resources_available &&
|
|
running_audio_source != NULL &&
|
|
audio_source_weighting < iterator.GetValue() // Actual value is inverted
|
|
)
|
|
{
|
|
Check(running_audio_source);
|
|
Verify(
|
|
running_audio_source->GetAudioSourcePriority() <=
|
|
audio_source->GetAudioSourcePriority()
|
|
);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Collect Statistics
|
|
//--------------------------------------------------------------------
|
|
//
|
|
#ifdef LAB_ONLY
|
|
if (
|
|
running_audio_source->GetAudioSourcePriority() <
|
|
audio_source->GetAudioSourcePriority()
|
|
)
|
|
{
|
|
resourceStealPriorityCount++;
|
|
}
|
|
else
|
|
{
|
|
Verify(
|
|
running_audio_source->GetAudioSourcePriority() ==
|
|
audio_source->GetAudioSourcePriority()
|
|
);
|
|
Warn(
|
|
running_audio_source->CalculateSourceVolumeScale() >=
|
|
audio_source->CalculateSourceVolumeScale()
|
|
);
|
|
resourceStealVolumeCount++;
|
|
}
|
|
AudioTime duration_cutoff(0.1f);
|
|
if (
|
|
running_audio_source->GetCurrentRunningTime() <=
|
|
duration_cutoff
|
|
)
|
|
{
|
|
stealShortDurationCount++;
|
|
}
|
|
resourceStealCount++;
|
|
#endif
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If running source is transient
|
|
// Then stop the source
|
|
// Else suspend the source
|
|
//--------------------------------------------------------------------
|
|
//
|
|
if (
|
|
running_audio_source->GetAudioRenderType() ==
|
|
TransientAudioRenderType
|
|
)
|
|
{
|
|
AudioSourceStopMaintenance(running_audio_source);
|
|
}
|
|
else
|
|
{
|
|
Verify(
|
|
running_audio_source->GetAudioRenderType() ==
|
|
SustainedAudioRenderType
|
|
);
|
|
AudioSourceSuspendMaintenance(running_audio_source);
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Request resources again
|
|
//--------------------------------------------------------------------
|
|
//
|
|
resources_available =
|
|
RequestAudioChannels(
|
|
audio_source->GetAudioVoiceCount(),
|
|
source_result
|
|
);
|
|
|
|
iterator.Last();
|
|
running_audio_source = iterator.GetCurrent();
|
|
}
|
|
}
|
|
return resources_available;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// RequestAudioChannels
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
L4AudioRenderer::RequestAudioChannels(
|
|
int voices_requested,
|
|
SourceSet *source_request
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(source_request);
|
|
|
|
//Do we have enough?
|
|
int requested = source_request->count;
|
|
|
|
bool failed = true;
|
|
|
|
alGetError();
|
|
|
|
for (int i = 0; i < requested; i++)
|
|
{
|
|
if (!alIsSource(source_request->sources[i]))
|
|
{
|
|
alGenSources(1, source_request->sources + i);
|
|
}
|
|
}
|
|
|
|
ALenum error = alGetError();
|
|
if (error == AL_NO_ERROR)
|
|
{
|
|
failed = false;
|
|
}
|
|
|
|
if (failed)
|
|
{
|
|
return False;
|
|
}
|
|
|
|
return True;
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Attempt to allocate channels from audio hardware
|
|
//----------------------------------------------------------------------
|
|
//
|
|
/* AudioChannel *channel;
|
|
Logical success = False;
|
|
AudioCard *front_card = GetFrontCard();
|
|
AudioCard *rear_card = GetRearCard();
|
|
|
|
Check(front_card);
|
|
Check(rear_card);
|
|
|
|
while (True)
|
|
{
|
|
if (channel_set_request->IsFrontLeftEnabled())
|
|
{
|
|
if ((channel =
|
|
front_card->RequestAudioChannel(voices_requested)) == NULL)
|
|
break;
|
|
Check(channel);
|
|
channel_set_request->SetFrontLeft(channel);
|
|
}
|
|
|
|
if (channel_set_request->IsFrontRightEnabled())
|
|
{
|
|
if ((channel =
|
|
front_card->RequestAudioChannel(voices_requested)) == NULL)
|
|
break;
|
|
Check(channel);
|
|
channel_set_request->SetFrontRight(channel);
|
|
}
|
|
|
|
if (channel_set_request->IsRearLeftEnabled())
|
|
{
|
|
if ((channel =
|
|
rear_card->RequestAudioChannel(voices_requested)) == NULL)
|
|
break;
|
|
Check(channel);
|
|
channel_set_request->SetRearLeft(channel);
|
|
}
|
|
|
|
if (channel_set_request->IsRearRightEnabled())
|
|
{
|
|
if ((channel =
|
|
rear_card->RequestAudioChannel(voices_requested)) == NULL)
|
|
break;
|
|
Check(channel);
|
|
channel_set_request->SetRearRight(channel);
|
|
}
|
|
|
|
success = True;
|
|
break;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// If did not succeed then release allocated channels
|
|
//----------------------------------------------------------------------
|
|
//
|
|
if (!success)
|
|
{
|
|
channel_set_request->ReleaseAll();
|
|
return False;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// else, return allocated channels
|
|
//----------------------------------------------------------------------
|
|
//
|
|
return True;*/
|
|
}
|
|
|
|
void L4AudioRenderer::ReleaseSourceSet(SourceSet &sourceSet)
|
|
{
|
|
for (int i = 0; i < sourceSet.count; i++)
|
|
{
|
|
ALenum state;
|
|
alGetSourcei(sourceSet.sources[i], AL_SOURCE_STATE, &state);
|
|
|
|
if (state == AL_PLAYING)
|
|
{
|
|
alSourceStop(sourceSet.sources[i]);
|
|
}
|
|
}
|
|
|
|
alDeleteSources(sourceSet.count, sourceSet.sources);
|
|
|
|
for (int i = 0; i < sourceSet.count; i++)
|
|
{
|
|
sourceSet.sources[i] = -1;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~ L4AudioRenderer profile bits ~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#if defined(TRACE_AUDIO_RENDERER_RUNNING_SOURCES)
|
|
BitTrace Audio_Renderer_Running_Sources("Audio Renderer Running Sources");
|
|
#endif
|
|
|
|
#if defined(TRACE_AUDIO_RENDERER_RUNNING_SORT)
|
|
BitTrace Audio_Renderer_Running_Sort("Audio Renderer Running Sort");
|
|
#endif
|
|
|
|
#if defined(TRACE_AUDIO_RENDERER_CALCULATE_MIX)
|
|
BitTrace Audio_Renderer_Calculate_Mix("Audio Renderer Calculate Mix");
|
|
#endif
|
|
|
|
#if defined(TRACE_AUDIO_RENDERER_EXECUTE_SOURCES)
|
|
BitTrace Audio_Renderer_Execute_Sources("Audio Renderer Execute Sources");
|
|
#endif
|
|
|
|
#if defined(TRACE_AUDIO_RENDERER_DORMANT_SOURCES)
|
|
BitTrace Audio_Renderer_Dormant_Sources("Audio Renderer Dormant Sources");
|
|
#endif
|