Files
CydandClaude Opus 4.8 4abbf8879f Initial import of Red Planet v4.10 Win32 source
Imports the current Win32 source for the pod-racing game 'Red Planet',
built on the MUNGA engine and its L4 (Win32/DirectX) platform layer:

- MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend
- RP / RP_L4: Red Planet game logic and Win32 application
- DivLoader, Setup1: asset loader and installer project
- lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies

Removed stale Subversion metadata and added .gitignore/.gitattributes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 07:59:51 -05:00

818 lines
21 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "audsrc.h"
#include "audrend.h"
#include "objstrm.h"
#include "app.h"
#include "namelist.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioSourceStartState ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//#############################################################################
//#############################################################################
//
#define DEFAULT_NOTE (60)
AudioSourceStartState::AudioSourceStartState()
{
noteValue = DEFAULT_NOTE;
isDurationSet = False;
hasDuration = False;
duration = 0.0f;
startTime = 0.0f;
endTime = 0.0f;
}
//
//#############################################################################
//#############################################################################
//
AudioSourceStartState::~AudioSourceStartState()
{
}
//
//#############################################################################
//#############################################################################
//
Logical
AudioSourceStartState::TestInstance() const
{
if (isDurationSet || hasDuration)
{
Verify(duration >= 0.0f);
}
if (hasDuration)
{
Verify(endTime >= startTime);
}
return True;
}
//
//#############################################################################
//#############################################################################
//
void
AudioSourceStartState::CalculateDuration(AudioControlValue control_value)
{
//
// Calculate duration
//
if (control_value > 0.0f)
{
hasDuration = True;
endTime = AudioTime::Now();
endTime += control_value;
}
else if (isDurationSet)
{
hasDuration = True;
endTime = AudioTime::Now();
endTime += duration;
isDurationSet = False;
}
else
{
hasDuration = False;
endTime = 0.0f;
}
}
#if 0
//
//#############################################################################
//#############################################################################
//
AudioTime
AudioSourceStartState::GetCurrentRunningTime()
{
Check(this);
AudioTime current_duration = AudioTime::Now();
return (current_duration -= startTime);
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioSource ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//#############################################################################
//#############################################################################
//
const Receiver::HandlerEntry
AudioSource::MessageHandlerEntries[]=
{
{
AudioSource::StartMessageID,
"Start",
(AudioSource::Handler)&AudioSource::StartMessageHandler
},
{
AudioSource::StopMessageID,
"Stop",
(AudioSource::Handler)&AudioSource::StopMessageHandler
}
};
AudioSource::MessageHandlerSet& AudioSource::GetMessageHandlers()
{
static AudioSource::MessageHandlerSet messageHandlers(ELEMENTS(AudioSource::MessageHandlerEntries), AudioSource::MessageHandlerEntries, AudioComponent::GetMessageHandlers());
return messageHandlers;
}
//
//#############################################################################
//#############################################################################
//
Derivation* AudioSource::GetClassDerivations()
{
static Derivation classDerivations(AudioComponent::GetClassDerivations(), "AudioSource");
return &classDerivations;
}
AudioSource::SharedData
AudioSource::DefaultData(
AudioSource::GetClassDerivations(),
AudioSource::GetMessageHandlers()
);
//
//#############################################################################
//#############################################################################
//
AudioSource::AudioSource(
PlugStream *stream,
Entity *entity
):
AudioComponent(stream, DefaultData)
{
AudioResource *audio_resource;
AudioLocation *audio_location;
AudioSourcePriority priority;
AudioSourceMixPresence mix_presence;
AudioControlValue volume_mix_level;
AudioPitchCents pitch_mix_offset;
AudioControlValue brightness_mix_level;
Logical has_compression_curve;
Scalar seconds;
AudioTime compression_duration;
Logical use_brightness_scale;
Logical use_attack_time_scale;
PlugStream_ReadObjectIDAndFindPlug(stream, &audio_resource);
PlugStream_ReadObjectIDAndFindPlug(stream, &audio_location);
MemoryStream_Read(stream, &priority);
MemoryStream_Read(stream, &mix_presence);
MemoryStream_Read(stream, &volume_mix_level);
MemoryStream_Read(stream, &pitch_mix_offset);
MemoryStream_Read(stream, &brightness_mix_level);
MemoryStream_Read(stream, &has_compression_curve);
MemoryStream_Read(stream, &seconds);
compression_duration = seconds;
MemoryStream_Read(stream, &use_brightness_scale);
MemoryStream_Read(stream, &use_attack_time_scale);
AudioSourceX(
audio_resource,
audio_location,
entity,
priority,
mix_presence,
volume_mix_level,
pitch_mix_offset,
brightness_mix_level,
has_compression_curve,
compression_duration,
use_brightness_scale,
use_attack_time_scale
);
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::BuildFromPage(
PlugStream *stream,
NameList *name_list,
ClassID class_ID,
ObjectID object_ID
)
{
AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);
//
// Store fields
//
CString audio_resource_name("resource");
PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_resource_name);
CString audio_location_name("location");
PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_location_name);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, priority);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, mix_presence);
MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioControlValue, volume_mix_level);
MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioPitchCents, pitch_mix_offset);
MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioControlValue, brightness_mix_level);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, has_compression_curve);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Scalar, compression_duration);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, use_brightness_scale);
MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, use_attack_time_scale);
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::AudioSourceX(
AudioResource *audio_resource,
AudioLocation *audio_location,
Entity *entity,
AudioSourcePriority priority,
AudioSourceMixPresence mix_presence,
AudioControlValue volume_mix_level,
AudioPitchCents pitch_mix_offset,
AudioControlValue brightness_mix_level,
Logical has_compression_curve,
const AudioTime &compression_duration,
Logical use_brightness_scale,
Logical use_attack_time_scale
)
{
Check(audio_resource);
Check(audio_location);
Check(entity);
audioLocation = audio_location;
audioResource = audio_resource;
audioSourceState = StoppedAudioSourceState;
attackVolumeScale = 1.0f;
useAttackTimeScale = use_attack_time_scale;
attackTimeScale = 0.0f;
audioSourcePriority = priority;
audioSourceMixPresence = mix_presence;
volumeCompressionScale = 1.0f;
hasCompressionCurve = has_compression_curve;
compressionDuration = compression_duration;
volumeMixScale = volume_mix_level;
pitchMixOffset = pitch_mix_offset;
useBrightnessScale = use_brightness_scale;
brightnessMixScale = brightness_mix_level;
volumeScale = 1.0f;
pitchOffset = 0.0f;
brightnessScale = 1.0f;
nextExecuteFrame = NullAudioFrameCount;
suspendFinishedFrame = NullAudioFrameCount;
entity->AddAudioComponent(this);
}
//
//#############################################################################
//#############################################################################
//
AudioSource::~AudioSource()
{
Verify(audioSourceState == StoppedAudioSourceState);
}
//
//#############################################################################
//#############################################################################
//
Logical
AudioSource::TestInstance() const
{
AudioComponent::TestInstance();
return True;
}
//
// Resource methods
//
//
//#############################################################################
//#############################################################################
//
void
AudioSource::SetAudioResource(AudioResource *audio_resource)
{
Check(this);
Check(audio_resource);
Verify(audioSourceState == StoppedAudioSourceState);
Check(audioResource);
audioResource = audio_resource;
}
//
// Current State/Status Accessors
//
//
//#############################################################################
//#############################################################################
//
void
AudioSource::AssignAudioSourceState(AudioSourceState audio_source_state)
{
Check(this);
//
// Initialize the next start state if the source is now running
//
if (audio_source_state == RunningAudioSourceState)
{
AudioSourceStartState null_state;
nextStartState = null_state;
}
audioSourceState = audio_source_state;
}
//
// Mixing & Logical compression methods
//
//
//#############################################################################
//#############################################################################
//
AudioControlValue
AudioSource::CalculateSourceCompressionEffect()
{
Check(this);
//
// Get current volume level
//
AudioControlValue current_volume_level;
current_volume_level = CalculateSourceVolumeScale();
Verify(current_volume_level >= 0.0f && current_volume_level <= 1.0f);
//
// Calculate the amount of this to apply to compression
//
if (hasCompressionCurve)
{
//
// scale = 1 / (1 + ((1/comp_dur) * duration)^2)
//
Scalar current_running_time = GetCurrentRunningTime();
Scalar compression_duration = compressionDuration;
Scalar duration_factor;
Scalar denominator;
AudioControlValue compression_effect;
Verify(!Small_Enough(compression_duration));
duration_factor = pow((1.0f/compression_duration) * current_running_time, 2.0f);
// Warn(!(duration_factor > 0.0f));
denominator = 1.0f + duration_factor;
Verify(!Small_Enough(denominator));
compression_effect = 1.0f / denominator;
Verify(compression_effect >= 0.0f && compression_effect <=1.0f);
return compression_effect * current_volume_level;
}
return current_volume_level;
}
//
//#############################################################################
//#############################################################################
//
AudioControlValue
AudioSource::CalculateSourceVolumeScale()
{
Check(this);
//
// Execute the watchers that will update the volume related
// member parameters
//
ExecuteWatchers();
//
// Calculate the resulting volume scale
//
AudioControlValue
volume_scale;
volume_scale = volumeScale * volumeMixScale * volumeCompressionScale;
Clamp(volume_scale, MinAudioVolume, MaxAudioVolume);
return volume_scale;
}
//
//#############################################################################
//#############################################################################
//
AudioControlValue
AudioSource::CalculateSourceBrightnessScale()
{
Check(this);
AudioControlValue brightness_mix_scale;
brightness_mix_scale = brightnessScale * brightnessMixScale;
Clamp(brightness_mix_scale, MinAudioBrightness, MaxAudioBrightness);
return brightness_mix_scale;
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::ReceiveControl(
AudioControlID control_ID,
AudioControlValue control_value
)
{
Check(this);
switch (control_ID)
{
//
//----------------------------------------------------------------------
// StartAudioControlID
//----------------------------------------------------------------------
//
case StartAudioControlID:
{
StartMessage message(control_value);
Check(application);
Check(application->GetAudioRenderer());
application->GetAudioRenderer()->PostAudioRequestMessage(
this,
&message
);
}
break;
//
//----------------------------------------------------------------------
// StopAudioControlID
//----------------------------------------------------------------------
//
case StopAudioControlID:
{
StopMessage message;
Check(application);
Check(application->GetAudioRenderer());
application->GetAudioRenderer()->PostAudioRequestMessage(
this,
&message
);
}
break;
//
//----------------------------------------------------------------------
// Misc...
//----------------------------------------------------------------------
//
case VolumeAudioControlID:
VolumeAudioHandler(control_value);
break;
case PitchAudioControlID:
PitchAudioHandler(control_value);
break;
case BrightnessAudioControlID:
BrightnessAudioHandler(control_value);
break;
case AttackVolumeAudioControlID:
AttackVolumeAudioHandler(control_value);
break;
case AttackTimeAudioControlID:
AttackTimeAudioHandler(control_value);
break;
case NoteAudioControlID:
NoteAudioHandler(control_value);
break;
case DurationAudioControlID:
DurationAudioHandler(control_value);
break;
case FlushMessagesAudioControlID:
Check(application);
Check(application->GetAudioRenderer());
application->GetAudioRenderer()->FlushAudioMessages(this);
break;
case NullAudioControlID:
case IdleAudioControlID:
break;
default:
Fail("AudioSource::ReceiveControl - Should never reach here");
break;
}
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::StartMessageHandler(StartMessage *message)
{
SET_AUDIO_RENDERER();
SET_AUDIO_RENDERER_START_HANDLER();
Check(this);
Check(message);
Verify(message->messageID == StartMessageID);
//
//--------------------------------------------------------------------------
// if double trigger condition exists
// then return
//--------------------------------------------------------------------------
//
AudioTime double_trigger_cutoff(AudioDoubleTriggerCutoff);
#if 0
double_trigger_cutoff = AudioDoubleTriggerCutoff;
#endif
if (
audioSourceState == RunningAudioSourceState &&
GetCurrentRunningTime() <= double_trigger_cutoff
)
{
#ifdef LAB_ONLY
Check(application);
Check(application->GetAudioRenderer());
application->GetAudioRenderer()->doubleTriggerCount++;
#endif
CLEAR_AUDIO_RENDERER_START_HANDLER();
CLEAR_AUDIO_RENDERER();
return;
}
//
//--------------------------------------------------------------------------
// If the source is not stopped then stop it
//--------------------------------------------------------------------------
//
if (audioSourceState != StoppedAudioSourceState)
{
StopMessage stop_message;
StopMessageHandler(&stop_message);
}
//
//--------------------------------------------------------------------------
// Verify that the state is stopped
// Copy the next start state into the current start state
// Calculate duration
//--------------------------------------------------------------------------
//
Verify(audioSourceState == StoppedAudioSourceState);
currentStartState = nextStartState;
currentStartState.CalculateDuration(message->controlValue);
//
//--------------------------------------------------------------------------
// Implementation specific start request
//--------------------------------------------------------------------------
//
StartMessageImplementation();
CLEAR_AUDIO_RENDERER_START_HANDLER();
CLEAR_AUDIO_RENDERER();
}
//
//#############################################################################
//#############################################################################
//
void
#if DEBUG_LEVEL>0
AudioSource::StopMessageHandler(StopMessage *message)
#else
AudioSource::StopMessageHandler(StopMessage*)
#endif
{
SET_AUDIO_RENDERER();
SET_AUDIO_RENDERER_STOP_HANDLER();
Check(this);
Check(message);
Verify(message->messageID == StopMessageID);
//
// if the source is stopped
// then return
//
if (audioSourceState == StoppedAudioSourceState)
{
CLEAR_AUDIO_RENDERER_STOP_HANDLER();
CLEAR_AUDIO_RENDERER();
return;
}
//
// Implementation specific stop request
//
StopMessageImplementation();
CLEAR_AUDIO_RENDERER_STOP_HANDLER();
CLEAR_AUDIO_RENDERER();
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::VolumeAudioHandler(AudioControlValue control_value)
{
Check(this);
Clamp(control_value, MinAudioVolume, MaxAudioVolume);
volumeScale = control_value / (MaxAudioVolume - MinAudioVolume);
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::PitchAudioHandler(AudioControlValue control_value)
{
Check(this);
pitchOffset = control_value;
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::BrightnessAudioHandler(AudioControlValue control_value)
{
Check(this);
Clamp(control_value, MinAudioBrightness, MaxAudioBrightness);
brightnessScale = control_value / (MaxAudioBrightness - MinAudioBrightness);
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::AttackVolumeAudioHandler(AudioControlValue control_value)
{
Check(this);
Clamp(control_value, MinAudioAttack, MaxAudioAttack);
attackVolumeScale = control_value / (MaxAudioAttack - MinAudioAttack);
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::AttackTimeAudioHandler(AudioControlValue control_value)
{
Check(this);
Clamp(control_value, MinAudioAttack, MaxAudioAttack);
attackTimeScale = control_value / (MaxAudioAttack - MinAudioAttack);
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::NoteAudioHandler(AudioControlValue control_value)
{
Check(this);
nextStartState.SetNoteValue(control_value);
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::DurationAudioHandler(AudioControlValue control_value)
{
Check(this);
nextStartState.SetDurationValue(control_value);
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::StartImplementation()
{
currentStartState.Start();
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::StopImplementation()
{
Fail("AudioSource::StopImplementation - Should never reach here");
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::SuspendImplementation()
{
StopImplementation();
Check(application);
Check(application->GetAudioRenderer());
Check(audioResource);
suspendFinishedFrame =
application->GetAudioRenderer()->GetAudioFrameCount() +
audioResource->GetSuspendDelay();
#ifdef LAB_ONLY
application->GetAudioRenderer()->sourceSuspendCount++;
#endif
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::ResumeImplementation()
{
StartImplementation();
suspendFinishedFrame = NullAudioFrameCount;
#ifdef LAB_ONLY
Check(application);
Check(application->GetAudioRenderer());
application->GetAudioRenderer()->sourceResumeCount++;
#endif
}
//
//#############################################################################
//#############################################################################
//
void
AudioSource::Execute()
{
Check(this);
//
// Execute source if this is the next execution frame
//
AudioRenderer
*audio_renderer;
Check(application);
audio_renderer = application->GetAudioRenderer();
Check(audio_renderer);
if (nextExecuteFrame <= audio_renderer->GetAudioFrameCount())
{
nextExecuteFrame =
audio_renderer->GetAudioFrameCount() + DefaultAudioFrameDelay;
//
// Update the sources spatial model
// Execute inherited method
// Execute this sources sound model
//
UpdateSpatialModel(audio_renderer->GetAudioHead());
AudioComponent::Execute();
ExecuteModel(False);
}
}