Files
firestorm/Gameleap/code/mw4/Libraries/Adept/AudioRenderer.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

624 lines
17 KiB
C++

//===========================================================================//
// File: AudioRenderer.cpp //
// Project: MUNGA Brick: Video Renderer //
// Contents: Abstracted Base Video Renderer //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 03/04/97 JTR Initial coding. //
//---------------------------------------------------------------------------//
// Copyright (C) 1997, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include "AdeptHeaders.hpp"
#include "ComponentHeaders.hpp"
#include "RendererManager.hpp"
#include "AudioRenderer.hpp"
#include "Application.hpp"
#include "Interface.hpp"
#include "Map.hpp"
#include "Application.hpp"
#include "SpatializedChannel.hpp"
#include "AudioCommand.hpp"
#include "AudioSample.hpp"
#include "AudioFXComponent.hpp"
#include "EarComponent.hpp"
#include "VOChannel.hpp"
Stuff::Scalar ChannelType::s_musicVolume = 1.0f;
Stuff::Scalar ChannelType::s_sfxVolume = 1.0f;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ChannelType::ChannelType():
m_queued(false),
m_volume(1.0f)
{
m_type = -1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ChannelType::SetChannelActive(AudioChannel *channel)
{
Check_Object(this);
Check_Object(channel);
Verify(m_type != -1);
Verify(m_inactiveChannels.IsPlugMember(channel));
m_inactiveChannels.RemovePlug(channel);
Verify(!m_activeChannels.IsPlugMember(channel));
m_activeChannels.Add(channel);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ChannelType::SetChannelInactive(AudioChannel *channel)
{
Check_Object(this);
Check_Object(channel);
Verify(m_type != -1);
Verify(m_activeChannels.IsPlugMember(channel));
m_activeChannels.RemovePlug(channel);
Verify(!m_inactiveChannels.IsPlugMember(channel));
m_inactiveChannels.Add(channel);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ChannelType::SetVolume(Scalar volume)
{
Check_Object(this);
Verify(m_type != -1);
if (m_type == AudioRenderer::MusicType)
volume *= s_musicVolume;
else if (m_type == AudioRenderer::GeneralType)
volume *= s_sfxVolume;
m_volume = volume;
ChainIteratorOf<AudioChannel*> actives(&m_activeChannels);
AudioChannel *channel;
while ((channel = actives.ReadAndNext()) != NULL)
{
Check_Object(channel);
channel->SetVolume();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ChannelType::StopAll()
{
Check_Object(this);
Verify(m_type != -1);
ChainIteratorOf<AudioChannel*> actives(&m_activeChannels);
AudioChannel *channel;
while ((channel = actives.ReadAndNext()) != NULL)
{
Check_Object(channel);
channel->Stop();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ChannelType::PauseAll()
{
Check_Object(this);
ChainIteratorOf<AudioChannel*> actives(&m_activeChannels);
AudioChannel *channel;
while ((channel = actives.ReadAndNext()) != NULL)
{
Check_Object(channel);
gosAudio_SetChannelPlayMode(channel->m_channel, gosAudio_Pause);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ChannelType::ResumeAll()
{
Check_Object(this);
ChainIteratorOf<AudioChannel*> actives(&m_activeChannels);
AudioChannel *channel;
while ((channel = actives.ReadAndNext()) != NULL)
{
Check_Object(channel);
gosAudio_SetChannelPlayMode(channel->m_channel, gosAudio_Continue);
}
}
//##########################################################################
//########################### AudioRenderer ##########################
//##########################################################################
AudioRenderer::ClassData*
AudioRenderer::DefaultData = NULL;
AudioRenderer
*AudioRenderer::Instance = NULL;
HGOSHEAP
AudioRenderer::s_Heap = NULL;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioRenderer::InitializeClass()
{
Verify(!s_Heap);
s_Heap = gos_CreateMemoryHeap("Audio Renderer", 0, g_LibraryHeap);
Check_Pointer(s_Heap);
Verify(!DefaultData);
DefaultData =
new ClassData(
AudioRendererClassID,
"Adept::AudioRenderer",
BaseClass::DefaultData,
0,
NULL
);
Check_Object(DefaultData);
ChannelType::s_musicVolume = 1.0f;
ChannelType::s_sfxVolume = 1.0f;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioRenderer::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
Check_Pointer(s_Heap);
gos_DestroyMemoryHeap(s_Heap);
s_Heap = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AudioRenderer::AudioRenderer():
Renderer(
DefaultData,
RendererManager::AudioRendererType,
"AudioRenderer"
),
m_channels(Environment.soundChannels),
m_sampleCache(101,NULL,true),
m_channelTypes(TypeCount),
iface(NULL)
{
Check_Pointer(this);
//
//----------------------------
// Zero out the channel arrays
//----------------------------
//
memset(m_channels.GetData(), 0, m_channels.GetSize());
Verify(m_channels.GetLength() >= 32);
m_cacheSize = 0;
Check_Object(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AudioRenderer::~AudioRenderer()
{
Check_Object(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioRenderer::SetRendererStatus(RendererStatus status)
{
Check_Object(this);
switch (status)
{
case ActiveRendererStatus:
{
//
//----------------
// Setup the mixer
//----------------
//
gosAudio_SetChannelSlider(gosAudio_Mixer, gosAudio_Rolloff, 1.0f);
gosAudio_SetChannelSlider(gosAudio_Mixer, gosAudio_Reverb, 1.0f);
gosAudio_SetChannelSlider(gosAudio_Mixer, gosAudio_Decay, 0.1f);
gosAudio_SetChannelSlider(gosAudio_Mixer, gosAudio_Distance, 1.0f);
gosAudio_SetChannelSlider(gosAudio_Mixer, gosAudio_Doppler, 1.0f);
gosAudio_SetChannelSlider(gosAudio_Mixer, gosAudio_Volume, 1.0f);
//
//---------------------------
// Make the voiceover channel
//---------------------------
//
int channel_id = 0;
AudioChannel *channel;
m_channelTypes[VOType].m_queued = true;
m_channelTypes[VOType].m_volume = 0.0f;
m_channelTypes[VOType].m_type = VOType;
channel = new VOChannel(channel_id, VOType);
Check_Object(channel);
m_channels[channel_id] = channel;
m_channelTypes[VOType].m_inactiveChannels.Add(channel);
channel_id = 2;
//
//--------------------------------
// Allocate the two music channels
//--------------------------------
//
m_channelTypes[MusicType].m_type = MusicType;
m_channelTypes[MusicType].m_volume = ChannelType::s_musicVolume;
for (int i=0; i<2; ++i)
{
channel =
new AudioChannel(
AudioChannel::DefaultData,
channel_id,
MusicType,
gosAudio_Volume
);
Check_Object(channel);
m_channels[channel_id++] = channel;
m_channelTypes[MusicType].m_inactiveChannels.Add(channel);
}
Verify(channel_id == 4);
//
//-----------------------
// Make the betty channel
//-----------------------
//
m_channelTypes[BettyType].m_queued = true;
m_channelTypes[BettyType].m_type = BettyType;
m_channelTypes[BettyType].m_volume = 1.0f;
channel =
new AudioChannel(
AudioChannel::DefaultData,
channel_id,
BettyType,
gosAudio_Common|gosAudio_Volume
);
Check_Object(channel);
m_channels[channel_id++] = channel;
m_channelTypes[BettyType].m_inactiveChannels.Add(channel);
Verify(channel_id == 5);
//
//---------------------------------
// Make the six mechanical channels
//---------------------------------
//
m_channelTypes[MechanicalType].m_type = MechanicalType;
m_channelTypes[MechanicalType].m_volume = 1.0f;
for (i=0; i<6; ++i)
{
channel =
new AudioChannel(
AudioChannel::DefaultData,
channel_id,
MechanicalType,
gosAudio_Common|gosAudio_Volume
);
Check_Object(channel);
m_channels[channel_id++] = channel;
m_channelTypes[MechanicalType].m_inactiveChannels.Add(channel);
}
Verify(channel_id == 11);
//
//------------------------------
// Allocate the general channels
//------------------------------
//
m_channelTypes[GeneralType].m_type = GeneralType;
m_channelTypes[GeneralType].m_volume = ChannelType::s_sfxVolume;
int remainder = Environment.soundChannels - channel_id;
for (i=0; i<remainder; ++i)
{
channel = new SpatializedChannel(SpatializedChannel::DefaultData, channel_id, GeneralType);
Check_Object(channel);
m_channels[channel_id++] = channel;
m_channelTypes[GeneralType].m_inactiveChannels.Add(channel);
}
Verify(channel_id == Environment.soundChannels);
#if defined(_ARMOR)
for (i=0; i<TypeCount; ++i)
{
ChannelType &type = m_channelTypes[i];
Verify(type.m_activeChannels.IsEmpty());
Verify(type.m_queuedCommands.IsEmpty());
}
Verify(m_executingCommands.IsEmpty());
#endif
}
break;
//
//--------------------------------------------------------------------------------
// If we are going inactive, stop all the channels. Then tell the channeltypes to
// destroy all looped commands
//--------------------------------------------------------------------------------
//
case InactiveRendererStatus:
{
for (int i=0; i<m_channels.GetLength(); ++i)
{
if (m_channels[i])
{
Check_Object(m_channels[i]);
if (m_channels[i]->m_command)
m_channels[i]->Stop();
delete m_channels[i];
m_channels[i] = NULL;
}
}
Verify(m_channelTypes.GetLength() == TypeCount);
for (i=0; i<TypeCount; ++i)
{
ChannelType &type = m_channelTypes[i];
Verify(type.m_activeChannels.IsEmpty());
type.m_queuedCommands.DeletePlugs();
type.m_loopCommands.DeletePlugs();
}
m_sampleCache.DeletePlugs();
m_executingCommands.DeletePlugs();
m_newCommands.DeletePlugs();
}
break;
}
BaseClass::SetRendererStatus(status);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioRenderer::PauseAll()
{
for (int i=0; i<TypeCount; ++i)
m_channelTypes[i].PauseAll();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioRenderer::ResumeAll()
{
for (int i=0; i<TypeCount; ++i)
m_channelTypes[i].ResumeAll();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AudioSample* AudioRenderer::UseSample(const ResourceID &handle_id)
{
Check_Object(this);
AudioSample *sample = m_sampleCache.Find(handle_id);
if (!sample)
{
Resource hint(handle_id);
Verify(hint.DoesResourceExist());
sample = new AudioSample(&hint);
Check_Object(sample);
m_sampleCache.AddValue(sample, handle_id);
}
Check_Object(sample);
return sample;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioRenderer::ExecuteImplementation(Time target_render_time)
{
//
//-----------------------------------
// Execute the executeable components
//-----------------------------------
//
LOG_BLOCK("Update Renderers::Audio");
BaseClass::ExecuteImplementation(target_render_time);
//
//---------------------------------------------------------------------------------------
// Find where the ear is in the world, then tell GOS about it, negating X to convert from
// right handed system to left handed one
//---------------------------------------------------------------------------------------
//
Interface *intface = GetInterface();
if (!intface)
return;
Check_Object(intface);
LinearMatrix4D ear_to_world = GetInterface()->GetLocalToWorld();
Point3D ear_in_world(ear_to_world);
UnitVector3D ear_foward_in_world;
ear_to_world.GetLocalForwardInWorld(&ear_foward_in_world);
UnitVector3D ear_up_in_world;
ear_to_world.GetLocalUpInWorld(&ear_up_in_world);
gosAudio_SetChannelSlider(gosAudio_Mixer, gosAudio_Position, -ear_in_world.x, ear_in_world.y, ear_in_world.z);
gosAudio_SetChannelSlider(gosAudio_Mixer, gosAudio_FrontOrientation, -ear_foward_in_world.x, ear_foward_in_world.y, ear_foward_in_world.z);
gosAudio_SetChannelSlider(gosAudio_Mixer, gosAudio_TopOrientation, -ear_up_in_world.x, ear_up_in_world.y, ear_up_in_world.z);
//
//----------------------------
// Execute the existing sounds
//----------------------------
//
{
AUDIO_RENDER("Execute");
ChainIteratorOf<AudioCommand*> commands(&m_executingCommands);
AudioCommand *command;
while ((command = commands.ReadAndNext()) != NULL)
{
Check_Object(command);
AudioChannel *channel = command->m_channel;
Check_Object(channel);
channel->Execute(target_render_time);
}
}
//
//---------------------------
// Process the audio commands
//---------------------------
//
{
AUDIO_RENDER("Execute::NewCommands");
ChainIteratorOf<AudioCommand*> commands(&m_newCommands);
AudioCommand *command;
while ((command = commands.GetCurrent()) != NULL)
{
Check_Object(command);
commands.Remove();
AudioSample *sample = m_sampleCache.Find(command->m_handleResourceID);
Check_Object(sample);
if (command->ConnectToChannel(sample))
{
m_executingCommands.Add(command);
command->m_minRestartDelay *= sample->m_sampleInfo.fDuration;
command->m_minReplaceDelay *= sample->m_sampleInfo.fDuration;
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioRenderer::FlushUnusedCache()
{
Check_Object(this);
ChainIteratorOf<AudioSample*> oldies(&m_unusedSamples);
AudioSample *sample;
while ((sample = oldies.ReadAndNext()) != NULL && m_cacheSize > 0x200000)
{
Check_Object(sample);
m_cacheSize -= sample->m_sampleInfo.dwSizeInBytes;
delete sample;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Component* AudioRenderer::CreateComponent(
ClassID component_class,
MemoryStream *stream,
RendererComponentWeb *rend_web,
Entity *entity
)
{
AudioComponentWeb *web = Cast_Object(AudioComponentWeb*, rend_web);
Component *component;
switch (component_class)
{
case EarComponentClassID:
component = EarComponent::Make(stream, rend_web);
break;
case AudioFXComponentClassID:
component = AudioFXComponent::Make(stream, web);
break;
default:
component =
BaseClass::CreateComponent(
component_class,
stream,
rend_web,
entity
);
break;
}
return component;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioRenderer::TestInstance()
{
Verify(IsDerivedFrom(DefaultData));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioRenderer::EntityIsInteresting(
Entity *entity,
bool render_me)
{
Check_Object(this);
Check_Object(entity);
AUDIO_LOAD("EntityIsInteresting");
//
//----------------------------------------------------------------------
// If this object already has a web, we just have to turn the web on/off
//----------------------------------------------------------------------
//
AudioComponentWeb *current_web =
static_cast<AudioComponentWeb*>(entity->GetComponentWeb(rendererType));
if (current_web)
{
Check_Object(current_web);
current_web->SetInterest(render_me);
if (!render_me)
return;
}
//
//--------------------------------------
// Create a component web for the entity
//--------------------------------------
//
const ResourceID &resources =
entity->GetRendererDataResourceID(rendererType);
if (!resources || !render_me)
return;
gos_PushCurrentHeap(s_Heap);
AudioComponentWeb* web =
new AudioComponentWeb(
AudioComponentWeb::DefaultData,
entity,
this,
resources,
NULL
);
Check_Object(web);
entity->SetComponentWeb(rendererType, web);
//
//-------------------------------------------------
// Now, load up the web from the specified resource
//-------------------------------------------------
//
Resource script(resources);
web->LoadFromStream(&script);
//
//-----------------------
// Execute the components
//-----------------------
//
{
AUDIO_LOAD("EntityIsInteresting::Execute");
web->ExecuteWatcherComponents();
}
gos_PopCurrentHeap();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioRenderer::EntityIsUninteresting(Entity *entity)
{
}