#include #include "munga.h" #pragma hdrstop #include "audio.h" #include "audrend.h" #include "app.h" #include "..\munga_l4\openal\al.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Audio Constants ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ const Scalar AudioDoubleTriggerCutoff = 0.2f; const AudioControlValue MaxAudioVolume = 1.0f; const AudioControlValue MinAudioVolume = 0.0f; const AudioControlValue LowAudioVolumeThreshold = 0.3f; const AudioControlValue MaxAudioBrightness = 1.0f; const AudioControlValue MinAudioBrightness = 0.0f; const AudioControlValue MaxAudioAttack = 1.0f; const AudioControlValue MinAudioAttack = 0.0f; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioHead ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // //############################################################################# //############################################################################# // AudioHead::AudioHead(): headEntitySocket(NULL) { // // Start frame counter at 0, other counts should be at // NullAudioFrameCount // audioFrameCount = 0; // // Other audio parameters // clippingRadius = 100.0f; distanceBetweenEars = 1.0f; amplitudeRollOffExponent = 1.0f; amplitudeRollOffKnee = 10.0f, amplitudeRollOffDistanceScale = 0.05f; highFrequencyRollOffExponent = 0.5f; highFrequencyRollOffKnee = 10.0f; highFrequencyRollOffDistanceScale = 0.05f; reverbToDryRatio = 0.1f; audioSoundSpeed = 345.0f; audioDopplerConstant = 20.0f; sourceCompressionExponent = 0.5f; sourceCompressionScale = 0.5f; Check_Fpu(); } // //############################################################################# //############################################################################# // AudioHead::~AudioHead() { Check(this); Check_Fpu(); } // //############################################################################# //############################################################################# // Logical AudioHead::TestInstance() const { Check(&headEntitySocket); return True; } // //############################################################################# //############################################################################# // void AudioHead::LinkToEntity(Entity *entity) { Check(this); Check(entity); // // Remove existing entity, add new one // if (headEntitySocket.GetCurrent() != NULL) { headEntitySocket.Remove(); } headEntitySocket.Add(entity); // FIDELITY (AUDIO_FIDELITY.md F3/F10): the engine computes the AUTHORED // distance-attenuation curve (AUDIO.INI amplitude_rolloff knee/exponent -> // AudioLocation::distanceVolumeScale) and the authored doppler-cents model. // Disable OpenAL's own models so they can't double-apply / fight them: // AL_LINEAR_DISTANCE made far battle audio fade to zero on a straight line // (-6 dB vs authored at 300u), and AL doppler ran with the wrong constants // AND a sign-inverted velocity feed (approaching sources pitched DOWN). alDistanceModel(AL_NONE); alDopplerFactor(0.0f); #if 0 // // Make the new clipping sphere // clippingSphere.SetCurrentOrigin(entity->localOrigin.linearPosition); #endif } // //############################################################################# //############################################################################# // LinearMatrix AudioHead::GetEarToWorld() { Check(this); Entity *entity = headEntitySocket.GetCurrent(); Check(entity); return entity->localToWorld; } // //############################################################################# //############################################################################# // void AudioHead::Execute() { Check(this); // // Increment frame counter // // AUDIO CLOCK CALIBRATION FIX: AudioTime consumers (sequence event timing, // compression curves) assume audioFrameCount advances at the renderer's // calibrationRate (DefaultRendererRate = 30 frames/sec: // Seconds_To_Frames = s * rate). The WinTesla port set raw Now().ticks here // (~hundreds/sec), running sequences ~18x off the authored timing -- the // AudioControlSequence events carrying the real footstep volumes never // landed where authored. Convert ticks -> calibrated frames properly. { double tps = (double)SystemClock::GetTicksPerSecond(); if (tps <= 0.0) tps = 1000.0; // GetTickCount ms fallback (static not yet measured) audioFrameCount = (AudioFrameCount)( (double)Now().ticks * (double)application->GetAudioRenderer()->GetCalibrationRate() / tps); } Verify(audioFrameCount < LONG_MAX); if (getenv("BT_AUDIO_SPATIAL")) { static long s_hx=0; if ((++s_hx % 300)==0) DEBUG_STREAM << "[audioclock] frame=" << audioFrameCount << " execs=" << s_hx << "\n" << std::flush; } // (task #50, AUDIO_FIDELITY F13) service the authored release fades // registered by PatchLevelOfDetail::StopNote (dB-linear note-off ramps). { extern void PRESET_serviceReleaseFades(float elapsed_seconds); static long s_lastFadeTicks = 0; long now_ticks = Now().ticks; if (s_lastFadeTicks != 0 && now_ticks > s_lastFadeTicks) { double tps = (double)SystemClock::GetTicksPerSecond(); if (tps <= 0.0) tps = 1000.0; PRESET_serviceReleaseFades((float)((now_ticks - s_lastFadeTicks) / tps)); } s_lastFadeTicks = now_ticks; } //set current listener orientation Vector3D headVelocity; headVelocity.MultiplyByInverse(this->GetHeadEntity()->GetWorldLinearVelocity(), this->GetHeadEntity()->localToWorld); alListener3f(AL_VELOCITY, -headVelocity.x, -headVelocity.y, -headVelocity.z); // LIVE-PLAYING DUMP (BT_AUDIO_DUMP): once a second list every playing AL // source with its sample name / gain / pitch / loop -- catches "mystery // sounds" (wrong sample, wrong pitch, chopped) red-handed. if (getenv("BT_AUDIO_DUMP")) { static long s_dumpTick = 0; if ((++s_dumpTick % 45) == 0) { extern ALuint *g_buffers; extern int g_numBuffers; extern const char *g_bufferNames[512]; for (ALuint sid = 1; sid <= 40; ++sid) { if (!alIsSource(sid)) continue; ALint st = 0; alGetSourcei(sid, AL_SOURCE_STATE, &st); if (st != AL_PLAYING) continue; ALint buf = 0, looping = 0; ALfloat gain = 0, pitch = 0; alGetSourcei(sid, AL_BUFFER, &buf); alGetSourcei(sid, AL_LOOPING, &looping); alGetSourcef(sid, AL_GAIN, &gain); alGetSourcef(sid, AL_PITCH, &pitch); const char *nm = "?"; for (int b = 0; b < g_numBuffers && b < 512; ++b) if ((ALint)g_buffers[b] == buf) { nm = g_bufferNames[b] ? g_bufferNames[b] : "?"; break; } DEBUG_STREAM << "[playing] src=" << sid << " " << nm << " gain=" << gain << " pitch=" << pitch << " loop=" << looping << "\n" << std::flush; } } } #if 0 // // Get the entity // Entity *entity = headEntitySocket.GetCurrent(); Check(entity); // // Make the new clipping sphere // clippingSphere.SetCurrentOrigin(entity->localOrigin.linearPosition); #endif } // //############################################################################# //############################################################################# // void AudioHead::DefineClippingSphere(Scalar radius) { Check(this); clippingRadius = radius; #if 0 // // Get entity position // Entity *entity; Point3D position(0.0f, 0.0f, 0.0f); if ((entity = headEntitySocket.GetCurrent()) != NULL) { Check(entity); position = entity->localOrigin.linearPosition; } // // Make the new clipping sphere // clippingSphere.SetCurrentOrigin(position); clippingSphere.SetReferenceRadius(radius); #endif } // //############################################################################# //############################################################################# // Logical AudioHead::IsPointClipped(const Point3D &point) { Check(this); Entity *entity; Vector3D difference; entity = headEntitySocket.GetCurrent(); Check(entity); difference.Subtract(entity->localOrigin.linearPosition, point); return difference.LengthSquared() > clippingRadius*clippingRadius; } // //############################################################################# //############################################################################# // void AudioHead::ControlDopplerEffect( Scalar doppler_constant, Scalar sound_speed ) { Check(this); audioDopplerConstant = doppler_constant; audioSoundSpeed = sound_speed; Check_Fpu(); } // //############################################################################# //############################################################################# // void AudioHead::ControlAmplitudeRollOff( Scalar exponent, Scalar amplitude_rolloff_knee, Scalar distance_scale ) { Check(this); amplitudeRollOffExponent = exponent; amplitudeRollOffKnee = amplitude_rolloff_knee; amplitudeRollOffDistanceScale = distance_scale; Check_Fpu(); } // //############################################################################# //############################################################################# // void AudioHead::ControlHighFrequencyRollOff( Scalar exponent, Scalar high_frequency_rolloff_knee, Scalar distance_scale ) { Check(this); highFrequencyRollOffExponent = exponent; highFrequencyRollOffKnee = high_frequency_rolloff_knee; highFrequencyRollOffDistanceScale = distance_scale; Check_Fpu(); } // //############################################################################# //############################################################################# // void AudioHead::ControlSourceCompression( Scalar exponent, Scalar scale ) { Check(this); sourceCompressionExponent = exponent; sourceCompressionScale = scale; Check_Fpu(); } // //############################################################################# //############################################################################# // void AudioHead::SetPositionalCulling(Logical) { Check(this); Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioComponent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // //############################################################################# //############################################################################# // const Receiver::HandlerEntry AudioComponent::MessageHandlerEntries[]= { { AudioComponent::ReceiveControlMessageID, "ReceiveControl", (AudioComponent::Handler)&AudioComponent::ReceiveControlMessageHandler } }; AudioComponent::MessageHandlerSet& AudioComponent::GetMessageHandlers() { static AudioComponent::MessageHandlerSet messageHandlers(ELEMENTS(AudioComponent::MessageHandlerEntries), AudioComponent::MessageHandlerEntries, Component::GetMessageHandlers()); return messageHandlers; } // //############################################################################# //############################################################################# // Derivation* AudioComponent::GetClassDerivations() { static Derivation classDerivations(Component::GetClassDerivations(), "AudioComponent"); return &classDerivations; } AudioComponent::SharedData AudioComponent::DefaultData( AudioComponent::GetClassDerivations(), AudioComponent::GetMessageHandlers() ); // //############################################################################# //############################################################################# // AudioComponent::AudioComponent( PlugStream *stream, SharedData &shared_data ): Component(stream, shared_data), audioWatcherSocket(NULL) { nextExecuteWatcherFrame = NullAudioFrameCount; } // //############################################################################# //############################################################################# // AudioComponent::~AudioComponent() { } // //############################################################################# //############################################################################# // Logical AudioComponent::TestInstance() const { if (!IsDerivedFrom(*GetClassDerivations())) { return False; } Check(&audioWatcherSocket); return True; } // //############################################################################# //############################################################################# // void AudioComponent::ExecuteWatchers() { Check(this); // // Execute watchers if this is the next watcher frame // AudioFrameCount audio_frame_count; Check(application); Check(application->GetAudioRenderer()); audio_frame_count = application->GetAudioRenderer()->GetAudioFrameCount(); if (nextExecuteWatcherFrame <= audio_frame_count) { nextExecuteWatcherFrame = audio_frame_count + DefaultAudioFrameDelay; // // Execute watchers // ChainIteratorOf iterator(&audioWatcherSocket); Component *component; while ((component = iterator.ReadAndNext()) != NULL) { Check(component); component->Execute(); } } } // //############################################################################# //############################################################################# // void AudioComponent::Execute() { Check(this); ExecuteWatchers(); Check_Fpu(); } // //############################################################################# //############################################################################# // void AudioComponent::ReceiveControl( AudioControlID, AudioControlValue ) { Fail("AudioComponent::ReceiveControl - Should never reach here"); } // //############################################################################# //############################################################################# // void AudioComponent::PostReceiveControl( AudioControlID control_ID, AudioControlValue control_value ) { Check(this); ReceiveControlMessage message(control_ID, control_value); Check(application); // ECH 1/26/96 - application->Post(HighEventPriority, this, &message); application->Post(DefaultEventPriority, this, &message); Check_Fpu(); } // //############################################################################# //############################################################################# // void AudioComponent::ReceiveControlMessageHandler(ReceiveControlMessage *message) { Check(this); Check(message); ReceiveControl(message->controlID, message->controlValue); Check_Fpu(); } //~~~~~~~~~~~~~~~~~~ AudioComponent__ReceiveControlMessage ~~~~~~~~~~~~~~~~~~~~ AudioComponent__ReceiveControlMessage::AudioComponent__ReceiveControlMessage( AudioControlID control_ID, AudioControlValue control_value ): Receiver::Message( AudioComponent::ReceiveControlMessageID, sizeof(AudioComponent__ReceiveControlMessage) ) { controlID = control_ID; controlValue = control_value; }