#include "munga.h" #pragma hdrstop #include "audloc.h" #include "objstrm.h" #include "namelist.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioLocation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // //############################################################################# //############################################################################# // AudioLocation::AudioLocation( PlugStream *stream, Entity *entity ): AudioComponent(stream), vectorToSource(0.0f, 0.0f, 0.0f), locationOffset(0.0f, 0.0f, 0.0f) { AudioFrameCount next_update_delay; Scalar clipping_scale; MemoryStream_Read(stream, &locationOffset); MemoryStream_Read(stream, &next_update_delay); MemoryStream_Read(stream, &clipping_scale); AudioLocationX(entity, next_update_delay, clipping_scale); } // //############################################################################# //############################################################################# // AudioLocation::~AudioLocation() { } // //############################################################################# //############################################################################# // void AudioLocation::AudioLocationX( Entity *entity, AudioFrameCount next_update_delay, Scalar clipping_scale ) { Check(entity); linkedEntity = entity; entity->AddAudioComponent(this); clippingScale = clipping_scale; isHeadSource = False; distanceToSource = 1.0f; dopplerCents = 0; #if 0 angleOffOrientation = 0.0f; #endif azimuthOfSource = 0.0f; distanceVolumeScale = 1.0f; highFreqCutoffScale = 1.0f; #if 0 reverbVolumeScale = 0.0f; #endif nextUpdateDelay = next_update_delay; nextUpdateFrame = NullAudioFrameCount; } // //############################################################################# //############################################################################# // Logical AudioLocation::TestInstance() const { Component::TestInstance(); return True; } // //############################################################################# //############################################################################# // void AudioLocation::BuildFromPage( PlugStream *stream, NameList *name_list, ClassID class_ID, ObjectID object_ID ) { AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID); MEM_STRM_WRITE_ENTRY(*stream, name_list, Point3D, location_offset); // // Read update delay // CString update_delay_string("update_delay"); AudioFrameCount update_delay = DefaultAudioFrameDelay; if (name_list->FindData(update_delay_string) != NULL) { Check_Pointer(name_list->FindData(update_delay_string)); Convert_From_Ascii( (const char *)name_list->FindData(update_delay_string), &update_delay ); } MemoryStream_Write(stream, &update_delay); // // Read culling scale // CString clipping_scale_string("clipping_scale"); Scalar clipping_scale = 1.0f; if (name_list->FindData(clipping_scale_string) != NULL) { Check_Pointer(name_list->FindData(clipping_scale_string)); Convert_From_Ascii( (const char *)name_list->FindData(clipping_scale_string), &clipping_scale ); } MemoryStream_Write(stream, &clipping_scale); } // //############################################################################# //############################################################################# // void AudioLocation::ReceiveControl( AudioControlID, AudioControlValue ) { } // //############################################################################# //############################################################################# // Logical AudioLocation::IsAudioLocationClipped(AudioHead *audio_head) { Check(this); Check(audio_head); // // HACK - This is a rough approximation in that it does not // take into account the offset of the location from the // center of the entity. But, this is more efficient and is OK // as long as the clipping sphere does not exclude the location // unnaturally // Entity *head_entity; Vector3D difference; Scalar scaled_radius; head_entity = audio_head->GetHeadEntity(); Check(head_entity); Check(linkedEntity); difference.Subtract( head_entity->localOrigin.linearPosition, linkedEntity->localOrigin.linearPosition ); scaled_radius = audio_head->GetClippingRadius() * clippingScale; return difference.LengthSquared() > scaled_radius * scaled_radius; } // //############################################################################# //############################################################################# // void AudioLocation::UpdateSpatialModelImplementation(AudioHead *audio_head) { Check(this); Check(audio_head); // //-------------------------------------------------------------------------- // Get head and source entity //-------------------------------------------------------------------------- // Entity *source_entity = GetLinkedEntity(); Entity *head_entity = audio_head->GetHeadEntity(); Check(source_entity); Check(head_entity); // //-------------------------------------------------------------------------- // Catch case of head == source at origin //-------------------------------------------------------------------------- // if ( head_entity == source_entity && locationOffset == Vector3D::Identity ) { isHeadSource = True; vectorToSource = Vector3D::Identity; distanceToSource = 0.0f; dopplerCents = 0; #if 0 angleOffOrientation = 0.0f; #endif azimuthOfSource = 0.0f; distanceVolumeScale = 1.0f; highFreqCutoffScale = 1.0f; #if 0 reverbVolumeScale = audio_head->GetReverbToDryRatio(); #endif return; } isHeadSource = False; // //-------------------------------------------------------------------------- // Get the ear to world linear matrix //-------------------------------------------------------------------------- // LinearMatrix ear_to_world = audio_head->GetEarToWorld(); // //-------------------------------------------------------------------------- // Calculate vector to source with respect to local coordinate system // of the ears //-------------------------------------------------------------------------- // { // // Get the source location offset from the entity position in // world coordinates // Vector3D world_location_offset; Vector3D world_source_location; world_location_offset.Multiply( locationOffset, source_entity->localToWorld ); Check(&world_location_offset); world_source_location.Add( source_entity->localOrigin.linearPosition, world_location_offset ); Check(&world_source_location); // // Get the vector from the ear to the source, note that the vector // is not calculated by the ears offset but from the entities location // Vector3D world_ear_to_source_vector; world_ear_to_source_vector.Subtract( world_source_location, head_entity->localOrigin.linearPosition ); Check(&world_ear_to_source_vector); // // Transform the vector into ear coordinates // vectorToSource.MultiplyByInverse( world_ear_to_source_vector, ear_to_world ); Check(&vectorToSource); } // //-------------------------------------------------------------------------- // Calculate distance to source //-------------------------------------------------------------------------- // distanceToSource = vectorToSource.Length(); // //-------------------------------------------------------------------------- // Calculate normal to source //-------------------------------------------------------------------------- // Vector3D normal_to_source(0.0f, 0.0f, -1.0f); if (!Small_Enough(distanceToSource)) { normal_to_source.Normalize(vectorToSource); Check(&normal_to_source); } #if 0 // //-------------------------------------------------------------------------- // Calculate angle between the orientation of the head and source //-------------------------------------------------------------------------- // if (Small_Enough(distanceToSource)) { angleOffOrientation = 0.0f; } else { UnitVector direction; Scalar cosine; ear_to_world.GetToAxis(Z_Axis, &direction); Check(&direction); Check(&normal_to_source); cosine = normal_to_source * direction; Clamp(cosine, -1.0f, 1.0f); angleOffOrientation = Arccos(cosine); angleOffOrientation.Normalize(); } Verify( angleOffOrientation <= 180.0f*RAD_PER_DEG && angleOffOrientation >= -180.0f*RAD_PER_DEG ); #endif // //-------------------------------------------------------------------------- // Calculate azimuth //-------------------------------------------------------------------------- // if (Small_Enough(vectorToSource.x) && Small_Enough(vectorToSource.z)) { azimuthOfSource = 0.0f; } else { Verify(!(Small_Enough(vectorToSource.x) && Small_Enough(vectorToSource.z))); azimuthOfSource = Arctan(vectorToSource.x, vectorToSource.z); } Verify( azimuthOfSource <= 180.0f*RAD_PER_DEG && azimuthOfSource >= -180.0f*RAD_PER_DEG ); // //-------------------------------------------------------------------------- // Calculate distance volume scaling // Apply clipping scale to rolloff distance scale, thereby giving this // audio location a specifc rolloff characteristic //-------------------------------------------------------------------------- // if (distanceToSource > audio_head->GetAmplitudeRollOffKnee()) { // // y = 1 / 1 + (K(x - knee))^exp // Verify(!Small_Enough(clippingScale)); const Scalar temp = (audio_head->GetAmplitudeRollOffDistanceScale() / clippingScale) * (distanceToSource - audio_head->GetAmplitudeRollOffKnee()); const Scalar temp2 = 1.0f + pow(temp, audio_head->GetAmplitudeRollOffExponent()); Verify(!Small_Enough(temp2)); distanceVolumeScale = 1.0f / temp2; } else { distanceVolumeScale = 1.0f; } Verify(distanceVolumeScale >= 0.0f && distanceVolumeScale <= 1.0f); // //-------------------------------------------------------------------------- // Calculate high frequency cutoff scaling // Apply clipping scale to high frequency rolloff distance scale, thereby // giving this audio location a specifc rolloff characteristic //-------------------------------------------------------------------------- // if (distanceToSource > audio_head->GetHighFrequencyRollOffKnee()) { // // y = 1 / 1 + (K(x - knee))^exp // Verify(!Small_Enough(clippingScale)); const Scalar temp = (audio_head->GetHighFrequencyRollOffDistanceScale() / clippingScale) * (distanceToSource - audio_head->GetHighFrequencyRollOffKnee()); const Scalar temp2 = 1.0f + pow(temp, audio_head->GetHighFrequencyRollOffExponent()); Verify(!Small_Enough(temp2)); highFreqCutoffScale = 1.0f / temp2; } else { highFreqCutoffScale = 1.0f; } Verify(highFreqCutoffScale >= 0.0f && highFreqCutoffScale <= 1.0f); #if 0 // //-------------------------------------------------------------------------- // Calculate reverb volume scaling //-------------------------------------------------------------------------- // if (distanceToSource > audio_head->GetAmplitudeRollOffKnee()) { const Scalar temp = distanceToSource - audio_head->GetAmplitudeRollOffKnee() + 1.0f; reverbVolumeScale = audio_head->GetReverbToDryRatio() * pow(temp, audio_head->GetAmplitudeRollOffExponent()); if (reverbVolumeScale > 1.0f) reverbVolumeScale = 1.0f; } else { reverbVolumeScale = audio_head->GetReverbToDryRatio(); } Verify(reverbVolumeScale >= 0.0f && reverbVolumeScale <= 1.0f); #endif // //-------------------------------------------------------------------------- // Calculate relative velocity of source //-------------------------------------------------------------------------- // Vector3D relative_velocity_temp; Vector3D relative_velocity; relative_velocity_temp.Subtract( source_entity->GetWorldLinearVelocity(), head_entity->GetWorldLinearVelocity() ); Check(&relative_velocity_temp); relative_velocity.MultiplyByInverse( relative_velocity_temp, head_entity->localToWorld ); Check(&relative_velocity); // //-------------------------------------------------------------------------- // Calculate doppler shift in cents // cents = (c / (c - v)) / ScaleRatio //-------------------------------------------------------------------------- // if (!Small_Enough(distanceToSource)) { AudioPitchCents cents; Scalar speed_of_source = normal_to_source * relative_velocity; const Scalar ear_radius = audio_head->GetDistanceBetweenEars() * 0.5f; if (distanceToSource < ear_radius) { Verify(!Small_Enough(ear_radius)); speed_of_source *= (distanceToSource / ear_radius); } if (CalculateDoppler(audio_head, speed_of_source, ¢s)) { dopplerCents = cents; } } else { dopplerCents = 0; } } // //############################################################################# //############################################################################# // Logical AudioLocation::CalculateDoppler( AudioHead *audio_head, Scalar speed, AudioPitchCents *result ) { Check(this); Check(audio_head); Check_Pointer(result); Scalar speed_of_sound = audio_head->GetAudioSoundSpeed(); Clamp(speed, -speed_of_sound, speed_of_sound); if (!Small_Enough(speed_of_sound - speed)) { *result = audio_head->GetAudioDopplerConstant() * ( 1 - speed_of_sound / (speed_of_sound - speed) ); return True; } return False; }