/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : WWAudio * * * * $Archive:: /Commando/Code/WWAudio/WWAudio.cpp $* * * * Author:: Patrick Smith * * * * $Modtime:: 1/30/02 2:47p $* * * * $Revision:: 76 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "always.h" #include #include "WWAudio.H" #include "WWDebug.H" #include "Utils.H" #include "RealCRC.H" #include "SoundBuffer.H" #include "AudibleSound.H" #include "Sound3D.H" #include "RawFile.H" #include "WW3D.H" #include "SoundScene.H" #include "SoundPseudo3D.H" #include "FFactory.H" #include "Registry.H" #include "Threads.H" #include "LogicalSound.h" #include "LogicalListener.h" #include "definitionclassids.h" #include "wwmemlog.h" #include "wwprofile.h" #include "Ini.h" #ifdef G_CODE_BASE #include "..\wwlib\argv.h" #endif //////////////////////////////////////////////////////////////////////////////////////////////// // Static member initialization //////////////////////////////////////////////////////////////////////////////////////////////// WWAudioClass *WWAudioClass::_theInstance = NULL; HANDLE WWAudioClass::_TimerSyncEvent = NULL; //////////////////////////////////////////////////////////////////////////////////////////////// // Registry value names //////////////////////////////////////////////////////////////////////////////////////////////// const char *VALUE_NAME_IS_STEREO = "stereo"; const char *VALUE_NAME_BITS = "bits"; const char *VALUE_NAME_HERTZ = "hertz"; const char *VALUE_NAME_DEVICE_NAME = "device name"; const char *VALUE_NAME_MUSIC_ENABLED = "music enabled"; const char *VALUE_NAME_SOUND_ENABLED = "sound enabled"; const char *VALUE_NAME_DIALOG_ENABLED = "dialog enabled"; const char *VALUE_NAME_CINEMATIC_ENABLED = "cinematic enabled"; const char *VALUE_NAME_MUSIC_VOL = "music volume"; const char *VALUE_NAME_SOUND_VOL = "sound volume"; const char *VALUE_NAME_DIALOG_VOL = "dialog volume"; const char *VALUE_NAME_CINEMATIC_VOL = "cinematic volume"; const char *VALUE_NAME_SPEAKER_TYPE = "speaker type"; const int MAX_VIRTUAL_CHANNELS = 100; //////////////////////////////////////////////////////////////////////////////////////////////// // INI names //////////////////////////////////////////////////////////////////////////////////////////////// const char *WWAUDIO_INI_FILENAME = "WWAudio.ini"; const char *WWAUDIO_INI_RELATIVE_PATHNAME = "Data\\WWAudio.ini"; const char *INI_DEFAULT_VOLUME_SECTION = "Default Volume"; const char *INI_MUSIC_VOLUME_ENTRY = "MUSIC_VOLUME"; const char *INI_SOUND_VOLUME_ENTRY = "SOUND_VOLUME"; const char *INI_DIALOG_VOLUME_ENTRY = "DIALOG_VOLUME"; const char *INI_CINEMATIC_VOLUME_ENTRY = "CINEMATIC_VOLUME"; //////////////////////////////////////////////////////////////////////////////////////////////// // Local inlines //////////////////////////////////////////////////////////////////////////////////////////////// __inline bool WWAudioClass::Is_OK_To_Give_Handle (const AudibleSoundClass &sound_obj) { bool is_ok = false; AudibleSoundClass::SOUND_TYPE type = sound_obj.Get_Type (); if (m_AreNewSoundsEnabled) { if (((type == AudibleSoundClass::TYPE_SOUND_EFFECT) && m_AreSoundEffectsEnabled) || ((type == AudibleSoundClass::TYPE_MUSIC) && m_IsMusicEnabled) || ((type == AudibleSoundClass::TYPE_DIALOG) && m_IsDialogEnabled) || ((type == AudibleSoundClass::TYPE_CINEMATIC) && m_IsCinematicSoundEnabled)) { is_ok = true; } } return is_ok; } //////////////////////////////////////////////////////////////////////////////////////////////// // // WWAudioClass // // 1/25/2002 12:13PM ST. Added the 'lite' flag which causes us to go through the motions of // creating the object (including theInstance) so it exists but disables // the call that initialises Miles and doesn't create a sound scene. // //////////////////////////////////////////////////////////////////////////////////////////////// WWAudioClass::WWAudioClass (bool lite) : m_Driver2D (NULL), m_Driver3D (NULL), m_PlaybackRate (44100), m_PlaybackBits (16), m_PlaybackStereo (true), m_SpeakerType (0), m_ReverbFilter (INVALID_MILES_HANDLE), m_UpdateTimer (-1), m_Driver3DPseudo (NULL), m_MusicVolume (DEF_MUSIC_VOL), m_SoundVolume (DEF_SFX_VOL), m_RealMusicVolume (DEF_MUSIC_VOL), m_RealSoundVolume (DEF_SFX_VOL), m_MaxCacheSize (DEF_CACHE_SIZE * 1024), m_CurrentCacheSize (0), m_Max2DSamples (DEF_2D_SAMPLE_COUNT), m_Max3DSamples (DEF_3D_SAMPLE_COUNT), m_Max2DBufferSize (DEF_MAX_2D_BUFFER_SIZE), m_Max3DBufferSize (DEF_MAX_3D_BUFFER_SIZE), m_SoundScene (NULL), m_IsMusicEnabled (true), m_IsDialogEnabled (true), m_IsCinematicSoundEnabled (true), m_AreSoundEffectsEnabled (true), m_FileFactory (NULL), m_EffectsLevel (0), m_CurrPage (PAGE_PRIMARY), m_AreNewSoundsEnabled (true), m_BackgroundMusic (NULL), m_ReverbRoomType (ENVIRONMENT_GENERIC), m_NonDialogFadeTime (DEF_FADE_TIME), m_FadeType (FADE_NONE), m_FadeTimer (0), m_CachedIsMusicEnabled (true), m_CachedIsDialogEnabled (true), m_CachedIsCinematicSoundEnabled (true), m_CachedAreSoundEffectsEnabled (true), AudioIni (NULL) { ::InitializeCriticalSection (&MMSLockClass::_MSSLockCriticalSection); m_ForceDisable = lite; // // Start Miles Sound System // if (!lite) { AIL_startup (); } _theInstance = this; _TimerSyncEvent = ::CreateEvent (NULL, TRUE, FALSE, "WWAUDIO_TIMER_SYNC"); // // Set some default values // Set_Sound_Effects_Volume (); Set_Music_Volume (); // // Allocate the virtual channels // for (int index = 0; index < MAX_VIRTUAL_CHANNELS; index ++) { m_VirtualChannels.Add (NULL); } // Create a new sound scene to manage our 3D sounds... if (!lite) { m_SoundScene = new SoundSceneClass; } m_Max3DBufferSize = m_Max3DBufferSize * 2.0F; return; } //////////////////////////////////////////////////////////////////////////////////////////////// // // ~WWAudioClass // //////////////////////////////////////////////////////////////////////////////////////////////// WWAudioClass::~WWAudioClass (void) { // // Stop the background music // Set_Background_Music (NULL); // // Make sure the delayed-release thread is terminated // before we exit (otherwise the process will crash). // WWAudioThreadsClass::End_Delayed_Release_Thread (); Shutdown (); _theInstance = NULL; ::CloseHandle(_TimerSyncEvent); _TimerSyncEvent = NULL; ::DeleteCriticalSection (&MMSLockClass::_MSSLockCriticalSection); // // Free the list of logical "types". // Reset_Logical_Types (); if (AudioIni != NULL) delete AudioIni; return; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Flush_Cache // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Flush_Cache (void) { // Loop through all the hash indicies for (int hash_index = 0; hash_index < MAX_CACHE_HASH; hash_index ++) { // Loop through all the buffers at this hash index and free them all for (int index = 0; index < m_CachedBuffers[hash_index].Count (); index ++) { CACHE_ENTRY_STRUCT &info = m_CachedBuffers[hash_index][index]; // Free the buffer data SAFE_FREE (info.string_id); REF_PTR_RELEASE (info.buffer); } // Remove all the entries for this hash index m_CachedBuffers[hash_index].Delete_All (); } m_CurrentCacheSize = 0; return; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Open_2D_Device // //////////////////////////////////////////////////////////////////////////////////////////////// WWAudioClass::DRIVER_TYPE_2D WWAudioClass::Open_2D_Device (LPWAVEFORMAT format) { MMSLockClass lock; // // Store the playback settings for future reference // m_PlaybackRate = format->nSamplesPerSec; m_PlaybackBits = (format->nAvgBytesPerSec << 3) / (format->nChannels * format->nSamplesPerSec); m_PlaybackStereo = bool(format->nChannels > 1); // // Assume we will open the DirectSound driver // DRIVER_TYPE_2D type = DRIVER2D_DSOUND; // First close the current 2D device and take // all the sound handles away from the sound objects. Close_2D_Device (); AIL_set_preference (AIL_LOCK_PROTECTION, NO); // Try to use DirectSound if possible S32 success = ::AIL_set_preference (DIG_USE_WAVEOUT, FALSE); //WWASSERT (success == AIL_NO_ERROR); // This assert fires if there is no sound card. // Open the driver success = ::AIL_waveOutOpen (&m_Driver2D, NULL, 0, format); // Do we need to switch from direct sound to waveout? if ((success == AIL_NO_ERROR) && (m_Driver2D != NULL) && (m_Driver2D->emulated_ds == TRUE)) { ::AIL_waveOutClose (m_Driver2D); success = 2; WWDEBUG_SAY (("WWAudio: Detected 2D DirectSound emulation, switching to WaveOut.\r\n")); } // If we couldn't open the direct sound device, then use the // default wave out device if (success != AIL_NO_ERROR) { // Try to use the default wave out driver success = ::AIL_set_preference (DIG_USE_WAVEOUT, TRUE); //WWASSERT (success == AIL_NO_ERROR); // This assert fires if there is no sound card. // Open the driver success = ::AIL_waveOutOpen (&m_Driver2D, NULL, 0, format); type = (success == AIL_NO_ERROR) ? DRIVER2D_WAVEOUT : DRIVER2D_ERROR; } // Allocate all the available handles if we were successful if (success == AIL_NO_ERROR) { Allocate_2D_Handles (); ReAssign_2D_Handles (); } else { Close_2D_Device (); WWDEBUG_SAY (("WWAudio: Error initializing 2D device.\r\n")); } // Return the opened device type return type; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Open_2D_Device // //////////////////////////////////////////////////////////////////////////////////////////////// WWAudioClass::DRIVER_TYPE_2D WWAudioClass::Open_2D_Device ( bool stereo, int bits, int hertz ) { // Build a wave format structure from the params PCMWAVEFORMAT wave_format = { 0 }; wave_format.wf.wFormatTag = WAVE_FORMAT_PCM; wave_format.wf.nChannels = stereo ? 2 : 1; wave_format.wf.nSamplesPerSec = hertz; wave_format.wf.nAvgBytesPerSec = (wave_format.wf.nChannels * wave_format.wf.nSamplesPerSec * bits) >> 3; wave_format.wf.nBlockAlign = (wave_format.wf.nChannels * bits) >> 3; wave_format.wBitsPerSample = bits; DRIVER_TYPE_2D type = DRIVER2D_ERROR; while (((type = Open_2D_Device ((LPWAVEFORMAT)&wave_format)) == DRIVER2D_ERROR) && (wave_format.wf.nSamplesPerSec >= 11025)) { // // Cut the playback rate in half and try again // wave_format.wf.nSamplesPerSec = wave_format.wf.nSamplesPerSec >> 1; wave_format.wf.nAvgBytesPerSec = (wave_format.wf.nChannels * wave_format.wf.nSamplesPerSec * bits) >> 3; wave_format.wf.nBlockAlign = (wave_format.wf.nChannels * bits) >> 3; } // Pass this structure onto the function that actually opens the device return type; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Close_2D_Device // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Close_2D_Device (void) { MMSLockClass lock; // // Note: We MUST close the 3D device when we close the 2D device... // Close_3D_Device (); // // Free any 2D sound handles // Remove_2D_Sound_Handles (); Release_2D_Handles (); // // Do we have an open driver handle to close? // bool retval = false; if (m_Driver2D != NULL) { // // Close the driver // ::AIL_waveOutClose (m_Driver2D); m_Driver2D = NULL; retval = true; } return retval; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Close_3D_Device // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Close_3D_Device (void) { MMSLockClass lock; bool retval = false; // // Remove all the handles // Remove_3D_Sound_Handles (); Release_3D_Handles (); // // Do we have an open driver handle to close? // if (m_Driver3D != NULL) { ::AIL_close_3D_provider (m_Driver3D); m_Driver3D = NULL; retval = true; } return retval; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Get_Sound_Buffer // //////////////////////////////////////////////////////////////////////////////////////////////// SoundBufferClass * WWAudioClass::Get_Sound_Buffer (const char *filename, bool is_3d) { WWPROFILE ("Get_Sound_Buffer"); // // Try to find the buffer in our cache, otherwise create a new buffer. // SoundBufferClass *buffer = Find_Cached_Buffer (filename); if (buffer == NULL) { FileClass *file = Get_File (filename); if (file != NULL && file->Is_Available ()) { buffer = Create_Sound_Buffer (*file, filename, is_3d); } else { static int count = 0; if ( ++count < 10 ) { WWDEBUG_SAY(( "Sound \"%s\" not found\r\n", filename )); } } Return_File (file); } return buffer; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Get_Sound_Buffer // //////////////////////////////////////////////////////////////////////////////////////////////// SoundBufferClass * WWAudioClass::Get_Sound_Buffer (FileClass &file, const char *string_id, bool is_3d) { WWMEMLOG(MEM_SOUND); // // Try to find the buffer in our cache, otherwise create a new buffer. // SoundBufferClass *buffer = Find_Cached_Buffer (string_id); if (buffer == NULL) { buffer = Create_Sound_Buffer (file, string_id, is_3d); } return buffer; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Find_Cached_Buffer // //////////////////////////////////////////////////////////////////////////////////////////////// SoundBufferClass * WWAudioClass::Find_Cached_Buffer (const char *string_id) { WWPROFILE ("Find_Cached_Buffer"); SoundBufferClass *sound_buffer = NULL; // Param OK? WWASSERT (string_id != NULL); if (string_id != NULL) { // // Determine which index in our hash table to use // int hash_index = ::CRC_Stringi (string_id) & CACHE_HASH_MASK; // // Loop through all the buffers at this hash index and try to find // one that matches the requested name // for (int index = 0; index < m_CachedBuffers[hash_index].Count (); index ++) { // // Is this the sound buffer we were looking for? // CACHE_ENTRY_STRUCT &info = m_CachedBuffers[hash_index][index]; if (::lstrcmpi (info.string_id, string_id) == 0) { sound_buffer = info.buffer; sound_buffer->Add_Ref (); break; } } } // // Return a pointer to the cached sound buffer // return sound_buffer; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Free_Cache_Space // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Free_Cache_Space (int bytes) { int bytes_freed = 0; // Loop through all the hash indicies for (int hash_index = 0; (hash_index < MAX_CACHE_HASH) && (bytes_freed < bytes); hash_index ++) { // Loop through all the buffers at this hash index for (int index = 0; (index < m_CachedBuffers[hash_index].Count ()) && (bytes_freed < bytes); index ++) { // Can we free this cached buffer? CACHE_ENTRY_STRUCT &info = m_CachedBuffers[hash_index][index]; if ((info.buffer != NULL) && (info.buffer->Num_Refs () == 1)) { // Add the size of this buffer to our count of bytes freed bytes_freed += info.buffer->Get_Raw_Length (); // Free the buffer data SAFE_FREE (info.string_id); REF_PTR_RELEASE (info.buffer); // Remove this entry from the hash table m_CachedBuffers[hash_index].Delete (index); index --; } } } // Make sure to recompute out current cache size m_CurrentCacheSize -= bytes_freed; WWASSERT (m_CurrentCacheSize >= 0); // Return true if we freed enough bytes in the cache return (bytes_freed >= bytes); } //////////////////////////////////////////////////////////////////////////////////////////////// // // Cache_Buffer // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Cache_Buffer ( SoundBufferClass *buffer, const char *string_id ) { WWPROFILE ("Cache_Buffer"); // Assume failure bool retval = false; // Params OK? WWASSERT (buffer != NULL); WWASSERT (string_id != NULL); if ((buffer != NULL) && (string_id != NULL)) { // // Attempt to free space in the cache (if needed) // /*int space_needed = (m_CurrentCacheSize + buffer->Get_Raw_Length ()) - (int)m_MaxCacheSize; if (space_needed > 0) { Free_Cache_Space (space_needed); }*/ // Do we have enough space in the cache for this buffer? //space_needed = (m_CurrentCacheSize + buffer->Get_Raw_Length ()) - (int)m_MaxCacheSize; //if (space_needed <= 0) { // // Determine which index in our hash table to use // int hash_index = ::CRC_Stringi (string_id) & CACHE_HASH_MASK; // // Add this buffer to the hash table at the given index. // Note: The assignment operator caused by the Add call // will add a reference to the sound buffer. // CACHE_ENTRY_STRUCT info; info.string_id = (char *)string_id; info.buffer = buffer; m_CachedBuffers[hash_index].Add (info); // Update our current cache size m_CurrentCacheSize += buffer->Get_Raw_Length (); retval = true; //} } if (retval == false) { WWDEBUG_SAY (("Unable to cache sound: %s.\r\n", string_id)); } // Return the true/false result code return retval; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_Sound_Buffer // //////////////////////////////////////////////////////////////////////////////////////////////// SoundBufferClass * WWAudioClass::Create_Sound_Buffer ( FileClass & file, const char *string_id, bool is_3d ) { WWPROFILE ("Create_Sound_Buffer"); SoundBufferClass *sound_buffer = NULL; // // Determine how large this buffer can be // int max_size = is_3d ? m_Max3DBufferSize : m_Max2DBufferSize; // // Create a streaming sound buffer object if the // file is too large to preload. // if (file.Size () > max_size) { sound_buffer = new StreamSoundBufferClass; } else { sound_buffer = new SoundBufferClass; } SET_REF_OWNER(sound_buffer); // // Create a new sound buffer from the provided file // bool success = sound_buffer->Load_From_File (file); sound_buffer->Set_Filename (string_id); WWASSERT (success); // If we were successful in creating the sound buffer, then // try to cache it as well, otherwise free the buffer and return NULL. if (success && (string_id != NULL)) { Cache_Buffer (sound_buffer, string_id); } else if (success == false) { REF_PTR_RELEASE (sound_buffer); } // Return a pointer to the new sound buffer return sound_buffer; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_Sound_Buffer // //////////////////////////////////////////////////////////////////////////////////////////////// SoundBufferClass * WWAudioClass::Create_Sound_Buffer ( unsigned char * file_image, unsigned long bytes, const char * string_id, bool is_3d ) { WWPROFILE ("Create_Sound_Buffer"); // Create a new sound buffer from the provided file SoundBufferClass *sound_buffer = new SoundBufferClass; SET_REF_OWNER(sound_buffer); // // Initialize the sound from this piece of memory // bool success = sound_buffer->Load_From_Memory (file_image, bytes); sound_buffer->Set_Filename (string_id); WWASSERT (success); // If we were successful in creating the sound buffer, then // try to cache it as well, otherwise free the buffer and return NULL. if (success && (string_id != NULL)) { Cache_Buffer (sound_buffer, string_id); } else if (success == false) { REF_PTR_RELEASE (sound_buffer); } // Return a pointer to the new sound buffer return sound_buffer; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_Sound_Effect // //////////////////////////////////////////////////////////////////////////////////////////////// AudibleSoundClass * WWAudioClass::Create_Sound_Effect (FileClass &file, const char *string_id) { WWPROFILE ("Create_Sound_Effect"); // Create a new sound object AudibleSoundClass *sound_obj = NEW_REF( AudibleSoundClass, () ); if (Is_Disabled () == false) { // Try to find the buffer in our cache, otherwise create a new buffer. SoundBufferClass *buffer = Get_Sound_Buffer (file, string_id, false); // Pass the actual sound data onto the sound object sound_obj->Set_Buffer (buffer); REF_PTR_RELEASE (buffer); } // Return a pointer to the sound effect return sound_obj; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_Sound_Effect // //////////////////////////////////////////////////////////////////////////////////////////////// AudibleSoundClass * WWAudioClass::Create_Sound_Effect (const char *filename) { WWPROFILE ("Create_Sound_Effect"); // Assume failure AudibleSoundClass *sound_obj = NULL; if (Is_Disabled () == false) { // Param OK? WWASSERT (filename != NULL); if (filename != NULL) { // Create a file object and pass it onto the appropriate function FileClass *file = Get_File (filename); if (file && file->Is_Available()) { sound_obj = Create_Sound_Effect (*file, filename); } else { WWDEBUG_SAY(( "Sound %s not found\r\n", filename )); } Return_File (file); } } // Return a pointer to the sound effect return sound_obj; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_Sound_Effect // //////////////////////////////////////////////////////////////////////////////////////////////// AudibleSoundClass * WWAudioClass::Create_Sound_Effect ( const char * string_id, unsigned char *raw_wave_data, unsigned long bytes ) { WWPROFILE ("Create_Sound_Effect"); // Create a new sound object AudibleSoundClass *sound_obj = NEW_REF( AudibleSoundClass, () ); if (Is_Disabled () == false) { // Try to find the buffer in our cache, otherwise create a new buffer. SoundBufferClass *buffer = Find_Cached_Buffer (string_id); if (buffer == NULL) { buffer = Create_Sound_Buffer (raw_wave_data, bytes, string_id, false); } // Pass the actual sound data onto the sound object sound_obj->Set_Buffer (buffer); REF_PTR_RELEASE (buffer); } // Return a pointer to the sound effect return sound_obj; } ///////////////////////////////////////////////////////////////////////////////// // // Create_3D_Sound // //////////////////////////////////////////////////////////////////////////////////////////////// Sound3DClass * WWAudioClass::Create_3D_Sound (FileClass &file, const char *string_id, int classid_hint) { WWPROFILE ("Create_3D_Sound (FileClass)"); Sound3DClass *sound_obj = NULL; if (Is_Disabled () == false) { // Try to find the buffer in our cache, otherwise create a new buffer. SoundBufferClass *buffer = Get_Sound_Buffer (file, string_id, true); // // What type of sound object should we create? A true 3D sound or one of // our pseudo-3d sounds? (volume and panning only) // if ( classid_hint == CLASSID_PSEUDO3D || Validate_3D_Sound_Buffer (buffer) == false) { sound_obj = new SoundPseudo3DClass; sound_obj->Set_Buffer (buffer); } else if (buffer != NULL) { sound_obj = new Sound3DClass; sound_obj->Set_Buffer (buffer); } REF_PTR_RELEASE (buffer); } // Return a pointer to the sound effect return sound_obj; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_3D_Sound // //////////////////////////////////////////////////////////////////////////////////////////////// Sound3DClass * WWAudioClass::Create_3D_Sound ( const char * filename, int classid_hint ) { WWPROFILE ("Create_3D_Sound (filename)"); WWMEMLOG(MEM_SOUND); // Assume failure Sound3DClass *sound_obj = NULL; if (Is_Disabled () == false) { // Param OK? WWASSERT (filename != NULL); if (filename != NULL) { // Try to find the buffer in our cache, otherwise create a new buffer. SoundBufferClass *buffer = Get_Sound_Buffer (filename, true); // // What type of sound object should we create? A true 3D sound or one of // our pseudo-3d sounds? (volume and panning only) // if ( classid_hint == CLASSID_PSEUDO3D || Validate_3D_Sound_Buffer (buffer) == false) { sound_obj = new SoundPseudo3DClass; sound_obj->Set_Buffer (buffer); } else if (buffer != NULL) { sound_obj = new Sound3DClass; sound_obj->Set_Buffer (buffer); } else { static int count = 0; if ( ++count < 10 ) { WWDEBUG_SAY(( "Sound File not Found \"%s\"\r\n", filename )); } } REF_PTR_RELEASE (buffer); } } // Return a pointer to the sound effect return sound_obj; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_3D_Sound // //////////////////////////////////////////////////////////////////////////////////////////////// Sound3DClass * WWAudioClass::Create_3D_Sound ( const char * string_id, unsigned char * raw_wave_data, unsigned long bytes, int classid_hint ) { WWPROFILE ("Create_3D_Sound (Raw)"); Sound3DClass *sound_obj = NULL; if (Is_Disabled () == false) { // // Try to find the buffer in our cache, otherwise create a new buffer. // SoundBufferClass *buffer = Find_Cached_Buffer (string_id); if (buffer == NULL) { buffer = Create_Sound_Buffer (raw_wave_data, bytes, string_id, true); } // // What type of sound object should we create? A true 3D sound or one of // our pseudo-3d sounds? (volume and panning only) // if ( classid_hint == CLASSID_PSEUDO3D || Validate_3D_Sound_Buffer (buffer) == false) { sound_obj = new SoundPseudo3DClass; sound_obj->Set_Buffer (buffer); } else if (buffer != NULL) { sound_obj = new Sound3DClass; sound_obj->Set_Buffer (buffer); } REF_PTR_RELEASE (buffer); } // Return a pointer to the sound effect return sound_obj; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_Sound // //////////////////////////////////////////////////////////////////////////////////////////////// AudibleSoundClass * WWAudioClass::Create_Sound ( int definition_id, RefCountClass *user_obj, uint32 user_data, int classid_hint ) { WWPROFILE ("Create_Sound"); AudibleSoundClass *sound = NULL; // // Find the definition // DefinitionClass *definition = DefinitionMgrClass::Find_Definition (definition_id); if (definition != NULL ) { // // Make sure this is really a sound definition // WWASSERT (definition->Get_Class_ID () == CLASSID_SOUND); if (definition->Get_Class_ID () == CLASSID_SOUND) { AudibleSoundDefinitionClass *sound_def = reinterpret_cast (definition); // // Create an instance of the sound // sound = sound_def->Create_Sound (classid_hint); if (sound != NULL) { sound->Set_User_Data (user_obj, user_data); } } } return sound; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_Sound // //////////////////////////////////////////////////////////////////////////////////////////////// AudibleSoundClass * WWAudioClass::Create_Sound ( const char * def_name, RefCountClass *user_obj, uint32 user_data, int classid_hint ) { WWPROFILE ("Create_Sound"); AudibleSoundClass *sound = NULL; // // Find the definition // DefinitionClass *definition = DefinitionMgrClass::Find_Typed_Definition (def_name, CLASSID_SOUND, true); if (definition != NULL ) { // // Make sure this is really a sound definition // WWASSERT (definition->Get_Class_ID () == CLASSID_SOUND); if (definition->Get_Class_ID () == CLASSID_SOUND) { AudibleSoundDefinitionClass *sound_def = reinterpret_cast (definition); // // Create an instance of the sound // sound = sound_def->Create_Sound (classid_hint); if (sound != NULL) { sound->Set_User_Data (user_obj, user_data); } } } return sound; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_Continuous_Sound // //////////////////////////////////////////////////////////////////////////////////////////////// AudibleSoundClass * WWAudioClass::Create_Continuous_Sound ( int definition_id, RefCountClass *user_obj, uint32 user_data, int classid_hint ) { WWPROFILE ("Create_Continuous_Sound"); // // Create an instance of the sound and play it // AudibleSoundClass *sound = Create_Sound (definition_id, user_obj, user_data, classid_hint); if (sound != NULL) { if (sound->Get_Loop_Count () != INFINITE_LOOPS) { WWDEBUG_SAY (("Audio Error: Creating a continuous sound with a finite loop count!\r\n")); } } return sound; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_Instant_Sound // //////////////////////////////////////////////////////////////////////////////////////////////// int WWAudioClass::Create_Instant_Sound ( int definition_id, const Matrix3D & tm, RefCountClass * user_obj, uint32 user_data, int classid_hint ) { WWPROFILE ("Create_Instant_Sound"); int sound_id = 0; // // Create an instance of the sound and play it // AudibleSoundClass *sound = Create_Sound (definition_id, user_obj, user_data, classid_hint); if (sound != NULL) { if (sound->Get_Loop_Count () == INFINITE_LOOPS) { WWDEBUG_SAY (("Audio Error: Creating an instant sound %s with an infinite loop count!\r\n",sound->Get_Definition()->Get_Name())); } sound_id = sound->Get_ID (); sound->Set_Transform (tm); sound->Add_To_Scene (); sound->Release_Ref (); } return sound_id; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_Continuous_Sound // //////////////////////////////////////////////////////////////////////////////////////////////// AudibleSoundClass * WWAudioClass::Create_Continuous_Sound ( const char * def_name, RefCountClass *user_obj, uint32 user_data, int classid_hint ) { WWPROFILE ("Create_Continuous_Sound"); // // Create an instance of the sound and play it // AudibleSoundClass *sound = Create_Sound (def_name, user_obj, user_data, classid_hint); if (sound != NULL) { if (sound->Get_Loop_Count () != INFINITE_LOOPS) { WWDEBUG_SAY (("Audio Error: Creating a continuous sound with a finite loop count!\r\n")); } } return sound; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Create_Instant_Sound // //////////////////////////////////////////////////////////////////////////////////////////////// int WWAudioClass::Create_Instant_Sound ( const char * def_name, const Matrix3D & tm, RefCountClass * user_obj, uint32 user_data, int classid_hint ) { WWPROFILE ("Create_Instant_Sound"); int sound_id = 0; // // Create an instance of the sound and play it // AudibleSoundClass *sound = Create_Sound (def_name, user_obj, user_data, classid_hint); if (sound != NULL) { if (sound->Get_Loop_Count () == INFINITE_LOOPS) { WWDEBUG_SAY (("Audio Error: Creating an instant sound %s with an infinite loop count!\r\n",sound->Get_Definition()->Get_Name())); } sound_id = sound->Get_ID (); sound->Set_Transform (tm); sound->Add_To_Scene (); sound->Release_Ref (); } return sound_id; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Flush_Playlist // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Flush_Playlist (SOUND_PAGE page) { // // Loop through all the entries in this playlist // for (int index = 0; index < m_Playlist[page].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[page][index]; if (sound_obj != NULL) { sound_obj->Stop (); sound_obj->Remove_From_Scene (); } //REF_PTR_RELEASE (sound_obj); } // // Now, make sure to free any completed sounds // Free_Completed_Sounds (); // // Free the list structure // m_Playlist[page].Delete_All (); return ; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Flush_Playlist // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Flush_Playlist (void) { Flush_Playlist (PAGE_PRIMARY); Flush_Playlist (PAGE_SECONDARY); return ; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Free_Completed_Sounds // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Free_Completed_Sounds (void) { if (m_CompletedSounds.Count () > 0) { // // Loop through all the entries in the completed sounds list // for (int index = 0; index < m_CompletedSounds.Count (); index ++) { AudibleSoundClass *sound_obj = m_CompletedSounds[index]; WWASSERT(sound_obj != NULL); //TSS 05/24/99 // // Be careful not to remove the sound from the playlist unless // its really done playing // if (sound_obj->Get_State () == AudibleSoundClass::STATE_STOPPED) { // // Remove this sound from the playlist // bool found = false; for (int page = 0; page < PAGE_COUNT && !found; page ++) { for (int play_index = 0; (play_index < m_Playlist[page].Count ()) && !found; play_index ++) { if (m_Playlist[page][play_index] == sound_obj) { // // Free our hold on this sound object // m_Playlist[page].Delete (play_index); REF_PTR_RELEASE (sound_obj); found = true; } } } } } // // Free the list structure // m_CompletedSounds.Delete_All (); // // Try to give a play-handle back to a sound that was priority-bumped. // Reprioritize_Playlist (); } return; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Get_Playlist_Entry // //////////////////////////////////////////////////////////////////////////////////////////////// AudibleSoundClass * WWAudioClass::Get_Playlist_Entry (int index) const { AudibleSoundClass *sound_obj = NULL; // Params OK? WWASSERT (index >= 0 && index < m_Playlist[m_CurrPage].Count ()); if ((index >= 0) && (index < m_Playlist[m_CurrPage].Count ())) { m_Playlist[m_CurrPage][index]->Add_Ref (); m_Playlist[m_CurrPage][index]; } // Return a pointer to the sound object return sound_obj; } //////////////////////////////////////////////////////////////////////////////////////////// // // Add_To_Playlist // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Add_To_Playlist (AudibleSoundClass *sound) { bool retval = false; WWASSERT (sound != NULL); if (sound != NULL) { // // Loop through all the entries in the playlist // bool already_added = false; for (int index = 0; (index < m_Playlist[m_CurrPage].Count ()) && (already_added == false); index ++) { already_added = (sound == m_Playlist[m_CurrPage][index]); } // // Add this sound to our playlist // if (already_added == false) { sound->Add_Ref (); m_Playlist[m_CurrPage].Add (sound); } } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Remove_From_Playlist // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Remove_From_Playlist (AudibleSoundClass *sound_obj) { bool retval = false; WWASSERT (sound_obj != NULL); if (sound_obj != NULL) { // // Loop through all the entries in the playlist // for (int page = 0; page < PAGE_COUNT && !retval; page ++) { for (int index = 0; (index < m_Playlist[page].Count ()) && !retval; index ++) { // // Is this the entry we are looking for? // if (sound_obj == m_Playlist[page][index]) { // // Add this sound to the 'completed' list // m_CompletedSounds.Add (sound_obj); retval = true; } } } // // Notify any callbacks that this sound is ending... // if (sound_obj->Get_Loop_Count () != INFINITE_LOOPS) { for (int index = 0; index < m_EOSCallbackList.Count (); index ++) { uint32 user_data = NULL; LPFNEOSCALLBACK callback = m_EOSCallbackList.Get_Callback (index, &user_data); if (callback != NULL) { (*callback) (sound_obj, user_data); } } } } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Is_Sound_In_Playlist // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Is_Sound_In_Playlist (AudibleSoundClass *sound_obj) { // Assume failure bool retval = false; // Loop through all the entries in the playlist for (int index = 0; (index < m_Playlist[m_CurrPage].Count ()) && (retval == false); index ++) { if (sound_obj == m_Playlist[m_CurrPage][index]) { retval = true; } } // Return the true/false result code return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Reprioritize_Playlist // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Reprioritize_Playlist (void) { AudibleSoundClass *sound_to_get_handle = NULL; float hightest_priority = 0; // // Loop through all the entries in the playlist // for (int index = 0; index < m_Playlist[m_CurrPage].Count (); index ++) { // // Is this the highest priority without a miles handle? // AudibleSoundClass *sound_obj = m_Playlist[m_CurrPage][index]; if ((sound_obj->Get_Miles_Handle () == NULL) && (sound_obj->Is_Sound_Culled () == false) && (sound_obj->Get_Priority () > hightest_priority)) { // // This is now the highest priority sound effect without // a play-handle. // sound_to_get_handle = sound_obj; hightest_priority = sound_obj->Get_Priority (); } } // // Get a new handle for this sound if necessary // if (sound_to_get_handle != NULL) { sound_to_get_handle->Allocate_Miles_Handle (); } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // On_Frame_Update // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::On_Frame_Update (unsigned int milliseconds) { // // Free any sounds we completed last frame // Free_Completed_Sounds (); // // Calculate the time in ms since the last frame // unsigned int time_delta = milliseconds; if (time_delta == 0) { time_delta = WW3D::Get_Frame_Time (); } // // Update the sound scene as necessary // if (m_CurrPage == PAGE_PRIMARY && m_SoundScene != NULL) { m_SoundScene->On_Frame_Update (milliseconds); m_SoundScene->Collect_Logical_Sounds (); } //int dialog_count = 0; // // Loop through all the entries in the playlist // for (int index = 0; index < m_Playlist[m_CurrPage].Count (); index ++) { // // Update this sound object // AudibleSoundClass *sound_obj = m_Playlist[m_CurrPage][index]; sound_obj->On_Frame_Update (time_delta); // // Is this an important piece of dialog? // /*if ( sound_obj->Is_Sound_Culled () == false && sound_obj->Get_Type () == AudibleSoundClass::TYPE_DIALOG && sound_obj->Get_Priority () > 0.5F) { dialog_count ++; }*/ } // // Fade sound fx and music when there's important dialog playing. // /*if (dialog_count == 0) { Fade_Non_Dialog_In (); } else { Fade_Non_Dialog_Out (); }*/ // // Update any fading we have going on // //Update_Fade (); return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Release_2D_Handles // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Release_2D_Handles (void) { MMSLockClass lock; // Release our hold on all the samples we've allocated for (int index = 0; index < m_2DSampleHandles.Count (); index ++) { HSAMPLE sample = m_2DSampleHandles[index]; if (sample != NULL) { ::AIL_release_sample_handle (sample); } } m_2DSampleHandles.Delete_All (); return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Allocate_2D_Handles // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Allocate_2D_Handles (void) { MMSLockClass lock; // Start fresh Release_2D_Handles (); if (m_Driver2D != NULL) { // Attempt to allocate our share of 2D sample handles for (int index = 0; index < m_Max2DSamples; index ++) { HSAMPLE sample = ::AIL_allocate_sample_handle (m_Driver2D); if (sample != NULL) { ::AIL_set_sample_user_data (sample, INFO_OBJECT_PTR, NULL); m_2DSampleHandles.Add (sample); } } // Record our actual number of available 2D sample handles m_Max2DSamples = m_2DSampleHandles.Count (); } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Get_2D_Sample // //////////////////////////////////////////////////////////////////////////////////////////// HSAMPLE WWAudioClass::Get_2D_Sample (const AudibleSoundClass &sound_obj) { if (Is_OK_To_Give_Handle (sound_obj) == false) { return (HSAMPLE)INVALID_MILES_HANDLE; } MMSLockClass lock; float lowest_priority = sound_obj.Get_Priority (); float lowest_runtime_priority = sound_obj.Get_Runtime_Priority (); AudibleSoundClass *lowest_pri_sound = NULL; HSAMPLE lowest_pri_sample = NULL; HSAMPLE free_sample = (HSAMPLE)INVALID_MILES_HANDLE; // Loop through all the available sample handles and try to find // one that isn't being used to play a sound. bool found = false; for (int index = 0; (index < m_2DSampleHandles.Count ()) && !found; index ++) { HSAMPLE sample = m_2DSampleHandles[index]; if (sample != NULL) { // Get a pointer to the object that is currently using this sample AudibleSoundClass *sound_obj = (AudibleSoundClass *)::AIL_sample_user_data (sample, INFO_OBJECT_PTR); if (sound_obj == NULL) { // Return this sample handle to the caller free_sample = sample; found = true; } else { // // Determine if this sound's priority is lesser then the sound we want to play. // This is done by comparing both the designer-specified priority and the current // runtime priority (which is calculated by distance to the listener). // float priority = sound_obj->Get_Priority (); float runtime_priority = sound_obj->Get_Runtime_Priority (); if ( (priority < lowest_priority) || (priority == lowest_priority && runtime_priority <= lowest_runtime_priority)) { lowest_priority = priority; lowest_pri_sound = sound_obj; lowest_pri_sample = sample; lowest_runtime_priority = runtime_priority; } } } } // Steal the sample handle from the lower priority // sound and return the handle to the caller. if ((found == false) && (lowest_pri_sound != NULL)) { lowest_pri_sound->Free_Miles_Handle (); free_sample = lowest_pri_sample; } // Return the free sample handle if we found one return free_sample; } //////////////////////////////////////////////////////////////////////////////////////////// // // Get_3D_Sample // //////////////////////////////////////////////////////////////////////////////////////////////// H3DSAMPLE WWAudioClass::Get_3D_Sample (const Sound3DClass &sound_obj) { if (Is_OK_To_Give_Handle (sound_obj) == false) { return (H3DSAMPLE)INVALID_MILES_HANDLE; } MMSLockClass lock; float lowest_priority = sound_obj.Get_Priority (); float lowest_runtime_priority = sound_obj.Get_Runtime_Priority (); AudibleSoundClass *lowest_pri_sound = NULL; H3DSAMPLE lowest_pri_sample = NULL; H3DSAMPLE free_sample = (H3DSAMPLE)INVALID_MILES_HANDLE; // Loop through all the available sample handles and try to find // one that isn't being used to play a sound. bool found = false; for (int index = 0; (index < m_3DSampleHandles.Count ()) && !found; index ++) { H3DSAMPLE sample = m_3DSampleHandles[index]; if (sample != NULL) { // Get a pointer to the object that is currently using this sample AudibleSoundClass *sound_obj = (AudibleSoundClass *)::AIL_3D_object_user_data (sample, INFO_OBJECT_PTR); if (sound_obj == NULL) { // Return this sample handle to the caller free_sample = sample; found = true; } else { // // Determine if this sound's priority is lesser then the sound we want to play. // This is done by comparing both the designer-specified priority and the current // runtime priority (which is calculated by distance to the listener). // float priority = sound_obj->Get_Priority (); float runtime_priority = sound_obj->Get_Runtime_Priority (); if ( (priority < lowest_priority) || (priority == lowest_priority && runtime_priority <= lowest_runtime_priority)) { lowest_priority = priority; lowest_pri_sound = sound_obj; lowest_pri_sample = sample; lowest_runtime_priority = runtime_priority; } } } } // Steal the sample handle from the lower priority // sound and return the handle to the caller. if ((found == false) && (lowest_pri_sound != NULL)) { lowest_pri_sound->Free_Miles_Handle (); free_sample = lowest_pri_sample; } // Return the free sample handle if we found one return free_sample; } //////////////////////////////////////////////////////////////////////////////////////////// // // Get_Listener_Handle // //////////////////////////////////////////////////////////////////////////////////////////////// H3DPOBJECT WWAudioClass::Get_Listener_Handle (void) { MMSLockClass lock; return ::AIL_3D_open_listener (m_Driver3D); } //////////////////////////////////////////////////////////////////////////////////////////// // // Build_3D_Driver_List // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Build_3D_Driver_List (void) { MMSLockClass lock; HPROENUM next = HPROENUM_FIRST; HPROVIDER provider = NULL; char *name = NULL; while (::AIL_enumerate_3D_providers (&next, &provider, &name) > 0) { // Can we successfully open this provider? if (::AIL_open_3D_provider (provider) == M3D_NOERR) { DRIVER_INFO_STRUCT *info = new DRIVER_INFO_STRUCT; info->driver = provider; info->name = ::strdup (name); m_Driver3DList.Add (info); ::AIL_close_3D_provider (provider); } else { char *error_info = ::AIL_last_error (); WWDEBUG_SAY (("WWAudio: Unable to open %s.\r\n", name)); WWDEBUG_SAY (("WWAudio: Reason %s.\r\n", error_info)); } } // // Attempt to select one of the known drivers (in the following order). // if ( (Select_3D_Device (DRIVER3D_PSEUDO) == false) && (Select_3D_Device (DRIVER3D_EAX) == false) && (Select_3D_Device (DRIVER3D_A3D) == false) && (Select_3D_Device (DRIVER3D_D3DSOUND) == false) && (Select_3D_Device (DRIVER3D_DOLBY) == false)) { // // Couldn't select a known driver, so just use the first possible. // if (m_Driver3DList.Count () > 0) { Select_3D_Device ((int)0); } } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Free_3D_Driver_List // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Free_3D_Driver_List (void) { MMSLockClass lock; // // Remove all the handles // Remove_3D_Sound_Handles (); Release_3D_Handles (); // // Loop through all the driver entries and free them all // for (int index = 0; index < m_Driver3DList.Count (); index ++) { DRIVER_INFO_STRUCT *info = m_Driver3DList[index]; if (info != NULL) { // // Free the information we have stored with this driver // if (info->name != NULL) { ::free (info->name); } delete info; } } if (m_Driver3D != NULL) { ::AIL_close_3D_provider (m_Driver3D); m_Driver3D = NULL; } // // Clear the list // m_Driver3DList.Delete_All (); return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Select_3D_Device // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Select_3D_Device (const char *device_name) { bool retval = false; // // Loop through all the drivers until we've found the one we want // for (int index = 0; index < m_Driver3DList.Count (); index ++) { DRIVER_INFO_STRUCT *info = m_Driver3DList[index]; if (info != NULL) { // // Is this the device we were looking for? // if (::lstrcmpi (info->name, device_name) == 0) { retval = Select_3D_Device (device_name, info->driver); break; } } } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Select_3D_Device // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Select_3D_Device (const char *device_name, HPROVIDER provider) { bool retval = false; if ((provider != NULL) && (provider != m_Driver3D)) { Close_3D_Device (); // // Select this device and re-allocate all handles // if (::AIL_open_3D_provider (provider) == M3D_NOERR) { m_Driver3D = provider; m_SoundScene->Initialize (); Allocate_3D_Handles (); AIL_set_3D_speaker_type (provider, AIL_3D_2_SPEAKER); // // Adjust the effects level to 1.0 if this is an EAX based driver // StringClass lower_name = device_name; ::strlwr (lower_name.Peek_Buffer ()); if (::strstr (device_name, "eax") != 0) { m_EffectsLevel = 1.0F; } else { m_EffectsLevel = 0.0F; } m_Driver3DName = device_name; } retval = true; } // Return true if we successfully selected the device return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Select_3D_Device // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Select_3D_Device (int index) { bool retval = false; // // Index valid? // if ((index >= 0) && (index < m_Driver3DList.Count ())) { Select_3D_Device (m_Driver3DList[index]->name, m_Driver3DList[index]->driver); WWDEBUG_SAY (("WWAudio: Selecting 3D sound device: %s.\r\n", m_Driver3DList[index]->name)); retval = true; } // // Return true if we successfully selected the device // return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Select_3D_Device // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Select_3D_Device (DRIVER_TYPE_3D type) { // Return true if we successfully selected the device return Select_3D_Device (Find_3D_Device (type)); } //////////////////////////////////////////////////////////////////////////////////////////// // // Find_3D_Device // //////////////////////////////////////////////////////////////////////////////////////////////// int WWAudioClass::Find_3D_Device (DRIVER_TYPE_3D type) { // Determine which substring to search for in the // name of the driver. const char *sub_string = "RSX"; switch (type) { case DRIVER3D_D3DSOUND: sub_string = "DirectSound"; break; case DRIVER3D_EAX: sub_string = "EAX"; break; case DRIVER3D_A3D: sub_string = "A3D"; break; case DRIVER3D_PSEUDO: sub_string = "Fast"; break; case DRIVER3D_DOLBY: sub_string = "Dolby"; break; } // Loop through all the driver entries and free them all int driver_index = -1; for (int index = 0; (index < m_Driver3DList.Count ()) && (driver_index == -1); index ++) { DRIVER_INFO_STRUCT *info = m_Driver3DList[index]; if ((info != NULL) && (info->name != NULL)) { // Is this the driver we were looking for? if (::strstr (info->name, sub_string) != NULL) { driver_index = index; } } } // Return -1 if not found, otherwise the 0 based index return driver_index; } //////////////////////////////////////////////////////////////////////////////////////////// // // Allocate_3D_Handles // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Allocate_3D_Handles (void) { MMSLockClass lock; // Start fresh Release_3D_Handles (); if (m_Driver3D != NULL) { // Attempt to allocate our share of 3D sample handles for (int index = 0; index < m_Max3DSamples; index ++) { H3DSAMPLE sample = ::AIL_allocate_3D_sample_handle (m_Driver3D); if (sample != NULL) { ::AIL_set_3D_object_user_data (sample, INFO_OBJECT_PTR, NULL); m_3DSampleHandles.Add (sample); } } } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Release_3D_Handles // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Release_3D_Handles (void) { MMSLockClass lock; // // Release our hold on all the samples we've allocated // for (int index = 0; index < m_3DSampleHandles.Count (); index ++) { H3DSAMPLE sample = m_3DSampleHandles[index]; if (sample != NULL) { ::AIL_release_3D_sample_handle (sample); } } m_3DSampleHandles.Delete_All (); return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Validate_3D_Sound_Buffer // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Validate_3D_Sound_Buffer (SoundBufferClass *buffer) { bool retval = false; // // 3D sound buffer MUST be uncompressed mono WAV data // if ((buffer != NULL) && (buffer->Get_Channels () == 1) && (buffer->Get_Type () == WAVE_FORMAT_PCM) && (buffer->Is_Streaming () == false)) { retval = true; } // Return a true/false result code return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // ReAssign_2D_Handles // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::ReAssign_2D_Handles (void) { // Loop through all the entries in the playlist for (int index = 0; index < m_Playlist[m_CurrPage].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[m_CurrPage][index]; // If this is a 2D sound effect, then force it to 'get' a new // sound handle. if ((sound_obj->Get_Class_ID () == CLASSID_2D) || (sound_obj->Get_Class_ID () == CLASSID_PSEUDO3D) || (sound_obj->Get_Class_ID () == CLASSID_2DTRIGGER)) { sound_obj->Free_Miles_Handle (); sound_obj->Allocate_Miles_Handle (); } } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // ReAssign_3D_Handles // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::ReAssign_3D_Handles (void) { // Loop through all the entries in the playlist for (int index = 0; index < m_Playlist[m_CurrPage].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[m_CurrPage][index]; // If this is a 3D sound effect, then force it to 'get' a new // sound handle. if (sound_obj->Get_Class_ID () == CLASSID_3D) { sound_obj->Free_Miles_Handle (); sound_obj->Allocate_Miles_Handle (); } } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Remove_2D_Sound_Handles // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Remove_2D_Sound_Handles (void) { // // Loop over all the 2D handles // for (int index = 0; index < m_2DSampleHandles.Count (); index ++) { HSAMPLE sample = m_2DSampleHandles[index]; if (sample != NULL) { // // Get a pointer to the object that is currently using this sample // AudibleSoundClass *sound_obj = (AudibleSoundClass *)::AIL_sample_user_data (sample, INFO_OBJECT_PTR); if (sound_obj != NULL) { sound_obj->Free_Miles_Handle (); } } } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Remove_3D_Sound_Handles // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Remove_3D_Sound_Handles (void) { // // Loop over all the 3D handles // for (int index = 0; index < m_3DSampleHandles.Count (); index ++) { H3DSAMPLE sample = m_3DSampleHandles[index]; if (sample != NULL) { // // Get a pointer to the object that is currently using this sample // AudibleSoundClass *sound_obj = (AudibleSoundClass *)::AIL_3D_object_user_data (sample, INFO_OBJECT_PTR); if (sound_obj != NULL) { sound_obj->Free_Miles_Handle (); } } } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Set_Dialog_Volume // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Set_Dialog_Volume (float volume) { m_DialogVolume = volume; m_DialogVolume = min (1.0F, m_DialogVolume); m_DialogVolume = max (0.0F, m_DialogVolume); // Update all the currently playing 'Dialog' to // reflect this new volume for (int index = 0; index < m_Playlist[m_CurrPage].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[m_CurrPage][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_DIALOG) { sound_obj->Update_Volume (); } } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Set_Cinematic_Volume // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Set_Cinematic_Volume (float volume) { m_CinematicVolume = volume; m_CinematicVolume = min (1.0F, m_CinematicVolume); m_CinematicVolume = max (0.0F, m_CinematicVolume); // // Update all the currently playing cinematic-counds to // reflect this new volume // for (int index = 0; index < m_Playlist[m_CurrPage].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[m_CurrPage][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_CINEMATIC) { sound_obj->Update_Volume (); } } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Set_Sound_Effects_Volume // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Set_Sound_Effects_Volume (float volume) { m_RealSoundVolume = volume; m_RealSoundVolume = min (1.0F, m_RealSoundVolume); m_RealSoundVolume = max (0.0F, m_RealSoundVolume); Internal_Set_Sound_Effects_Volume (m_RealSoundVolume); return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Set_Music_Volume // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Set_Music_Volume (float volume) { m_RealMusicVolume = volume; m_RealMusicVolume = min (1.0F, m_RealMusicVolume); m_RealMusicVolume = max (0.0F, m_RealMusicVolume); Internal_Set_Music_Volume (m_RealMusicVolume); return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Internal_Set_Sound_Effects_Volume // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Internal_Set_Sound_Effects_Volume (float volume) { m_SoundVolume = volume; m_SoundVolume = min (1.0F, m_SoundVolume); m_SoundVolume = max (0.0F, m_SoundVolume); // Update all the currently playing 'Sound Effects' to // reflect this new volume for (int index = 0; index < m_Playlist[m_CurrPage].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[m_CurrPage][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_SOUND_EFFECT) { sound_obj->Update_Volume (); } } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Internal_Set_Music_Volume // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Internal_Set_Music_Volume (float volume) { m_MusicVolume = volume; m_MusicVolume = min (1.0F, m_MusicVolume); m_MusicVolume = max (0.0F, m_MusicVolume); // Update all currently playing music to // reflect this new volume for (int index = 0; index < m_Playlist[m_CurrPage].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[m_CurrPage][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_MUSIC) { sound_obj->Update_Volume (); } } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Is_Disabled // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Is_Disabled (void) const { static bool _firsttime = true; static bool _disabled = false; if (_firsttime) { _firsttime = false; #ifdef G_CODE_BASE // // Use command line arguement. // ArgvClass argv; if (argv.Find("-NOAUDIO")) { _disabled = true; } #endif // // Read the disabled key from the registry // RegistryClass registry ("SOFTWARE\\Westwood\\WWAudio"); if (registry.Is_Valid ()) { if (registry.Get_Int ("Disabled", 0) == 1) { _disabled = true; WWDEBUG_SAY (("WWAudio: Audio system disabled in registry.\r\n")); } } } return (_disabled | m_ForceDisable); } //////////////////////////////////////////////////////////////////////////////////////////// // // Initialize // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Initialize (const char *registry_subkey_name) { WWMEMLOG(MEM_SOUND); if (Is_Disabled () == false) { // // Initialize the audio system from the registry settings // Load_From_Registry (registry_subkey_name); // // Grab the first (and only) filter for use with our 'tinny' effect. // HPROENUM next = HPROENUM_FIRST; char *name = NULL; if (::AIL_enumerate_filters (&next, &m_ReverbFilter, &name) == 0) { m_ReverbFilter = INVALID_MILES_HANDLE; } m_RealMusicVolume = m_MusicVolume; m_RealSoundVolume = m_SoundVolume; } // // Register the file callbacks so we can support streaming from MIX files... // ::AIL_set_file_callbacks (File_Open_Callback, File_Close_Callback, File_Seek_Callback, File_Read_Callback); return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Initialize // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Initialize ( bool stereo, int bits, int hertz ) { // Open the default 2D device, then build a list of 3D // devices and open the default. if (Is_Disabled () == false) { Open_2D_Device (stereo, bits, hertz); Build_3D_Driver_List (); // // Grab the first (and only) filter for use with our 'tinny' effect. // HPROENUM next = HPROENUM_FIRST; char *name = NULL; if (::AIL_enumerate_filters (&next, &m_ReverbFilter, &name) == 0) { m_ReverbFilter = INVALID_MILES_HANDLE; } } // // Register the file callbacks so we can support streaming from MIX files... // ::AIL_set_file_callbacks (File_Open_Callback, File_Close_Callback, File_Seek_Callback, File_Read_Callback); return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Shutdown // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Shutdown (void) { // // If there is a timer running, then stop the timer... // if (m_UpdateTimer != -1) { // Kill the timer ::AIL_stop_timer (m_UpdateTimer); ::AIL_release_timer_handle (m_UpdateTimer); m_UpdateTimer = -1; // Wait for the timer callback function to end ::WaitForSingleObject (_TimerSyncEvent, 20000); ::CloseHandle (_TimerSyncEvent); _TimerSyncEvent = NULL; } // // Stop the background music // Set_Background_Music (NULL); // // Stop all sounds from playing // Flush_Playlist (); if (m_SoundScene != NULL) { m_SoundScene->Flush_Scene (); } // // Free all our cached sound buffers // Flush_Cache (); // // Close-out our hold on any driver resources // Remove_2D_Sound_Handles (); Remove_3D_Sound_Handles (); Release_2D_Handles (); Release_3D_Handles (); Free_3D_Driver_List (); SAFE_DELETE (m_SoundScene); Close_2D_Device (); // // Shutdown Miles Sound System // ::AIL_shutdown (); return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Register_EOS_Callback // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Register_EOS_Callback (LPFNEOSCALLBACK callback, DWORD user_param) { m_EOSCallbackList.Add_Callback (callback, user_param); return; } //////////////////////////////////////////////////////////////////////////////////////////// // // UnRegister_EOS_Callback // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::UnRegister_EOS_Callback (LPFNEOSCALLBACK callback) { m_EOSCallbackList.Remove_Callback (callback); return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Register_Text_Callback // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Register_Text_Callback (LPFNTEXTCALLBACK callback, DWORD user_param) { m_TextCallbackList.Add_Callback (callback, user_param); return; } //////////////////////////////////////////////////////////////////////////////////////////// // // UnRegister_Text_Callback // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::UnRegister_Text_Callback (LPFNTEXTCALLBACK callback) { m_TextCallbackList.Remove_Callback (callback); return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Fire_Text_Callback // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Fire_Text_Callback (AudibleSoundClass *sound_obj, const StringClass &text) { if (text.Get_Length () > 0) { // // Loop over all the text-callbacks that have been registered // for (int index = 0; index < m_TextCallbackList.Count (); index ++) { uint32 user_data = 0L; LPFNTEXTCALLBACK callback = m_TextCallbackList.Get_Callback (index, &user_data); if (callback != NULL) { // // Fire the notification // (*callback) (sound_obj, text, user_data); } } } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Allow_Sound_Effects // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Allow_Sound_Effects (bool onoff) { // // Is the state changing? // if (m_AreSoundEffectsEnabled != onoff) { m_AreSoundEffectsEnabled = onoff; // // Update all the currently playing 'Sound Effects' to // reflect this new state. // if (m_AreSoundEffectsEnabled) { for (int page = 0; page < PAGE_COUNT; page ++) { Push_Active_Sound_Page ((WWAudioClass::SOUND_PAGE)page); for (int index = 0; index < m_Playlist[page].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[page][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_SOUND_EFFECT) { sound_obj->Allocate_Miles_Handle (); } } Pop_Active_Sound_Page (); } } else { for (int page = 0; page < PAGE_COUNT; page ++) { for (int index = 0; index < m_Playlist[page].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[page][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_SOUND_EFFECT) { sound_obj->Free_Miles_Handle (); } } } } } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Allow_Music // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Allow_Music (bool onoff) { // Is the state changing? if (m_IsMusicEnabled != onoff) { m_IsMusicEnabled = onoff; // // Update all the currently playing 'music tracks' to // reflect this new state. // if (m_IsMusicEnabled) { for (int page = 0; page < PAGE_COUNT; page ++) { Push_Active_Sound_Page ((WWAudioClass::SOUND_PAGE)page); for (int index = 0; index < m_Playlist[page].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[page][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_MUSIC) { sound_obj->Stop (false); sound_obj->Play (); } } Pop_Active_Sound_Page (); } } else { for (int page = 0; page < PAGE_COUNT; page ++) { for (int index = 0; index < m_Playlist[page].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[page][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_MUSIC) { sound_obj->Free_Miles_Handle (); } } } } } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Allow_Dialog // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Allow_Dialog (bool onoff) { // Is the state changing? if (m_IsDialogEnabled != onoff) { m_IsDialogEnabled = onoff; // // Update all the currently playing 'dialog' to // reflect this new state. // if (m_IsDialogEnabled) { for (int page = 0; page < PAGE_COUNT; page ++) { Push_Active_Sound_Page ((WWAudioClass::SOUND_PAGE)page); for (int index = 0; index < m_Playlist[page].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[page][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_DIALOG) { sound_obj->Stop (false); sound_obj->Play (); } } Pop_Active_Sound_Page (); } } else { for (int page = 0; page < PAGE_COUNT; page ++) { for (int index = 0; index < m_Playlist[page].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[page][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_DIALOG) { sound_obj->Free_Miles_Handle (); } } } } } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Allow_Cinematic_Sound // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Allow_Cinematic_Sound (bool onoff) { // Is the state changing? if (m_IsCinematicSoundEnabled != onoff) { m_IsCinematicSoundEnabled = onoff; // // Update all the currently playing 'dialog' to // reflect this new state. // if (m_IsCinematicSoundEnabled) { for (int page = 0; page < PAGE_COUNT; page ++) { Push_Active_Sound_Page ((WWAudioClass::SOUND_PAGE)page); for (int index = 0; index < m_Playlist[page].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[page][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_CINEMATIC) { sound_obj->Stop (false); sound_obj->Play (); } } Pop_Active_Sound_Page (); } } else { for (int page = 0; page < PAGE_COUNT; page ++) { for (int index = 0; index < m_Playlist[page].Count (); index ++) { AudibleSoundClass *sound_obj = m_Playlist[page][index]; if (sound_obj->Get_Type () == AudibleSoundClass::TYPE_CINEMATIC) { sound_obj->Free_Miles_Handle (); } } } } } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Simple_Play_2D_Sound_Effect // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Simple_Play_2D_Sound_Effect ( const char *filename, float priority, float volume ) { bool retval = false; AudibleSoundClass *sound = Create_Sound_Effect (filename); if (sound != NULL) { sound->Set_Priority (priority); sound->Set_Loop_Count (1); sound->Set_Volume(volume); sound->Play (); sound->Release_Ref (); sound = NULL; retval = true; } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Simple_Play_2D_Sound_Effect // //////////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Simple_Play_2D_Sound_Effect ( FileClass &file, float priority, float volume ) { bool retval = false; AudibleSoundClass *sound = Create_Sound_Effect (file); if (sound != NULL) { sound->Set_Priority (priority); sound->Set_Loop_Count (1); sound->Set_Volume(volume); sound->Play (); sound->Release_Ref (); sound = NULL; retval = true; } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Get_File // //////////////////////////////////////////////////////////////////////////////////////////////// FileClass * WWAudioClass::Get_File (LPCTSTR filename) { FileClass *file = NULL; if (m_FileFactory != NULL) { file = m_FileFactory->Get_File (filename); } else { file = _TheFileFactory->Get_File(filename); } // Return a pointer to the file return file; } //////////////////////////////////////////////////////////////////////////////////////////// // // Return_File // //////////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Return_File (FileClass *file) { if (m_FileFactory != NULL) { m_FileFactory->Return_File (file); } else { SAFE_DELETE (file); } return; } //////////////////////////////////////////////////////////////////////////////////////////// // // Create_Logical_Sound // //////////////////////////////////////////////////////////////////////////////////////////// LogicalSoundClass * WWAudioClass::Create_Logical_Sound (void) { return new LogicalSoundClass; } //////////////////////////////////////////////////////////////////////////////////////////// // // Create_Logical_Listener // //////////////////////////////////////////////////////////////////////////////////////////// LogicalListenerClass * WWAudioClass::Create_Logical_Listener (void) { return new LogicalListenerClass; } //////////////////////////////////////////////////////////////////////////////////////////// // // Add_Logical_Type // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Add_Logical_Type (int id, LPCTSTR display_name) { m_LogicalTypes.Add (LOGICAL_TYPE_STRUCT (id, display_name)); return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Reset_Logical_Types // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Reset_Logical_Types (void) { m_LogicalTypes.Delete_All (); return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Get_Logical_Type // //////////////////////////////////////////////////////////////////////////////////////////// int WWAudioClass::Get_Logical_Type (int index, StringClass &name) { int type_id = 0; WWASSERT (index >= 0 && index < m_LogicalTypes.Count ()); if (index >= 0 && index < m_LogicalTypes.Count ()) { type_id = m_LogicalTypes[index].id; name = m_LogicalTypes[index].display_name; } return type_id; } //////////////////////////////////////////////////////////////////////////////////////////// // // Find_Sound_Object // //////////////////////////////////////////////////////////////////////////////////////////// SoundSceneObjClass * WWAudioClass::Find_Sound_Object (uint32 sound_obj_id) { SoundSceneObjClass *sound_obj = NULL; // // Lookup the sound object and return it to the caller // int index = 0; if (SoundSceneObjClass::Find_Sound_Object (sound_obj_id, &index)) { sound_obj = SoundSceneObjClass::m_GlobalSoundList[index]; } return sound_obj; } //////////////////////////////////////////////////////////////////////////////////////////// // // Load_From_Registry // //////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Load_From_Registry (const char *subkey_name) { bool retval = true; StringClass device_name; bool is_stereo = true; int bits = 16; int hertz = 44100; // // Load the settings from the registry // if (Load_From_Registry (subkey_name, device_name, is_stereo, bits, hertz, m_AreSoundEffectsEnabled, m_IsMusicEnabled, m_IsDialogEnabled, m_IsCinematicSoundEnabled, m_SoundVolume, m_MusicVolume, m_DialogVolume, m_CinematicVolume, m_SpeakerType)) { // // Close any open devices // Free_3D_Driver_List (); Close_2D_Device (); // // Open the 2D device as specified // Open_2D_Device (is_stereo, bits, hertz); // // Find and open the 3D device specified // Build_3D_Driver_List (); Select_3D_Device (device_name); retval = true; // // Select the speaker type // Set_Speaker_Type (m_SpeakerType); } m_RealMusicVolume = m_MusicVolume; m_RealSoundVolume = m_SoundVolume; return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Load_From_Registry // //////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Load_From_Registry ( const char * subkey_name, StringClass & device_name, bool & is_stereo, int & bits, int & hertz, bool & sound_enabled, bool & music_enabled, bool & dialog_enabled, bool & cinematic_enabled, float & sound_volume, float & music_volume, float & dialog_volume, float & cinematic_volume, int & speaker_type ) { bool retval = false; // // Attempt to open the registry key // RegistryClass registry (subkey_name); if (registry.Is_Valid ()) { int defaultmusicvolume, defaultsoundvolume, defaultdialogvolume, defaultcinematicvolume; // // Read the device name into a string object // char temp_buffer[256] = { 0 }; registry.Get_String (VALUE_NAME_DEVICE_NAME, temp_buffer, sizeof (temp_buffer)); device_name = temp_buffer; // // Read the 2D settings // is_stereo = (registry.Get_Int (VALUE_NAME_IS_STEREO, true) == 1); bits = registry.Get_Int (VALUE_NAME_BITS, 16); hertz = registry.Get_Int (VALUE_NAME_HERTZ, 44100); // // Read the sound/music enabled settings // music_enabled = (registry.Get_Int (VALUE_NAME_MUSIC_ENABLED, 1) == 1); sound_enabled = (registry.Get_Int (VALUE_NAME_SOUND_ENABLED, 1) == 1); dialog_enabled = (registry.Get_Int (VALUE_NAME_DIALOG_ENABLED, 1) == 1); cinematic_enabled = (registry.Get_Int (VALUE_NAME_CINEMATIC_ENABLED, 1) == 1); Load_Default_Volume (defaultmusicvolume, defaultsoundvolume, defaultdialogvolume, defaultcinematicvolume); // // Read the volume information // music_volume = registry.Get_Int (VALUE_NAME_MUSIC_VOL, defaultmusicvolume) / 100.0F; sound_volume = registry.Get_Int (VALUE_NAME_SOUND_VOL, defaultsoundvolume) / 100.0F; dialog_volume = registry.Get_Int (VALUE_NAME_DIALOG_VOL, defaultdialogvolume) / 100.0F; cinematic_volume = registry.Get_Int (VALUE_NAME_CINEMATIC_VOL, defaultcinematicvolume) / 100.0F; music_volume = WWMath::Clamp (music_volume, 0, 1.0F); sound_volume = WWMath::Clamp (sound_volume, 0, 1.0F); dialog_volume = WWMath::Clamp (dialog_volume, 0, 1.0F); cinematic_volume = WWMath::Clamp (cinematic_volume, 0, 1.0F); // // Misc // speaker_type = registry.Get_Int (VALUE_NAME_SPEAKER_TYPE, 0); retval = true; } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Save_To_Registry // //////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Save_To_Registry (const char *subkey_name) { StringClass device_name; // // Get the name of the current 3D driver // for (int index = 0; index < m_Driver3DList.Count (); index ++) { DRIVER_INFO_STRUCT *info = m_Driver3DList[index]; // // Is this the device we were looking for? // if (info != NULL && info->driver == m_Driver3D) { device_name = info->name; break; } } // // Save these settings to the registry // return Save_To_Registry (subkey_name, device_name, m_PlaybackStereo, m_PlaybackBits, m_PlaybackRate, m_AreSoundEffectsEnabled, m_IsMusicEnabled, m_IsDialogEnabled, m_IsCinematicSoundEnabled, m_SoundVolume, m_MusicVolume, m_DialogVolume, m_CinematicVolume, m_SpeakerType); } //////////////////////////////////////////////////////////////////////////////////////////// // // Save_To_Registry // //////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Save_To_Registry ( const char * subkey_name, const StringClass & device_name, bool is_stereo, int bits, int hertz, bool sound_enabled, bool music_enabled, bool dialog_enabled, bool cinematic_enabled, float sound_volume, float music_volume, float dialog_volume, float cinematic_volume, int speaker_type ) { bool retval = false; // // Attempt to open the registry key // RegistryClass registry (subkey_name); if (registry.Is_Valid ()) { // // Save the settings to the registry // registry.Set_String (VALUE_NAME_DEVICE_NAME, device_name); registry.Set_Int (VALUE_NAME_IS_STEREO, is_stereo); registry.Set_Int (VALUE_NAME_BITS, bits); registry.Set_Int (VALUE_NAME_HERTZ, hertz); registry.Set_Int (VALUE_NAME_MUSIC_ENABLED, music_enabled); registry.Set_Int (VALUE_NAME_SOUND_ENABLED, sound_enabled); registry.Set_Int (VALUE_NAME_DIALOG_ENABLED, dialog_enabled); registry.Set_Int (VALUE_NAME_CINEMATIC_ENABLED, cinematic_enabled); registry.Set_Int (VALUE_NAME_MUSIC_VOL, music_volume * 100); registry.Set_Int (VALUE_NAME_SOUND_VOL, sound_volume * 100); registry.Set_Int (VALUE_NAME_DIALOG_VOL, dialog_volume * 100); registry.Set_Int (VALUE_NAME_CINEMATIC_VOL, cinematic_volume * 100); registry.Set_Int (VALUE_NAME_SPEAKER_TYPE, speaker_type); retval = true; } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // File_Open_Callback // //////////////////////////////////////////////////////////////////////////////////////////// U32 AILCALLBACK WWAudioClass::File_Open_Callback (char const *filename, U32 *file_handle) { U32 retval = false; if (Get_Instance () != NULL) { // // Open the file // FileClass *file = Get_Instance ()->Get_File (filename); if (file != NULL && file->Open ()) { (*file_handle) = (U32)file; retval = true; } } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // File_Close_Callback // //////////////////////////////////////////////////////////////////////////////////////////// void AILCALLBACK WWAudioClass::File_Close_Callback (U32 file_handle) { if (Get_Instance () != NULL) { // // Close the file (if necessary) // FileClass *file = reinterpret_cast (file_handle); if (file != NULL) { Get_Instance ()->Return_File (file); } } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // File_Seek_Callback // //////////////////////////////////////////////////////////////////////////////////////////// S32 AILCALLBACK WWAudioClass::File_Seek_Callback (U32 file_handle, S32 offset, U32 type) { S32 retval = 0; // // Convert the handle to a file handle type // FileClass *file = reinterpret_cast (file_handle); if (file != NULL) { // // Convert the Miles seek type to one of our own // int seek_type = SEEK_CUR; switch (type) { case AIL_FILE_SEEK_BEGIN: seek_type = SEEK_SET; break; case AIL_FILE_SEEK_CURRENT: seek_type = SEEK_CUR; break; case AIL_FILE_SEEK_END: seek_type = SEEK_END; break; } // // Perform the seek // retval = file->Seek (offset, seek_type); } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // File_Read_Callback // //////////////////////////////////////////////////////////////////////////////////////////// U32 AILCALLBACK WWAudioClass::File_Read_Callback (U32 file_handle, void *buffer, U32 bytes) { U32 retval = 0; // // Convert the handle to a file handle type // FileClass *file = reinterpret_cast (file_handle); if (file != NULL) { // // Read the bytes from the file // retval = file->Read (buffer, bytes); } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Fade_Background_Music // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Fade_Background_Music (const char *filename, int fade_out_time, int fade_in_time) { // // Fade-out the background music (as necessary) // if (m_BackgroundMusic != NULL) { m_BackgroundMusic->Fade_Out (fade_out_time); REF_PTR_RELEASE (m_BackgroundMusic); } m_BackgroundMusicName = filename; if (filename != NULL) { // // Create the sound // m_BackgroundMusic = Create_Sound_Effect (filename); if (m_BackgroundMusic != NULL) { // // Configure the sound and start playing it // m_BackgroundMusic->Set_Priority (1.0F); m_BackgroundMusic->Set_Runtime_Priority (1.0F); m_BackgroundMusic->Set_Loop_Count (INFINITE_LOOPS); m_BackgroundMusic->Set_Type (AudibleSoundClass::TYPE_MUSIC); m_BackgroundMusic->Cull_Sound (false); m_BackgroundMusic->Fade_In (fade_in_time); } } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Set_Background_Music // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Set_Background_Music (const char *filename) { // // Stop the background music // if (m_BackgroundMusic != NULL) { m_BackgroundMusic->Stop (); REF_PTR_RELEASE (m_BackgroundMusic); } m_BackgroundMusicName = filename; if (filename != NULL) { // // Create the sound // m_BackgroundMusic = Create_Sound_Effect (filename); if (m_BackgroundMusic != NULL) { // // Configure the sound and start playing it // m_BackgroundMusic->Set_Priority (1.0F); m_BackgroundMusic->Set_Runtime_Priority (1.0F); m_BackgroundMusic->Set_Loop_Count (INFINITE_LOOPS); m_BackgroundMusic->Set_Type (AudibleSoundClass::TYPE_MUSIC); m_BackgroundMusic->Cull_Sound (false); m_BackgroundMusic->Play (); } } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Set_Active_Sound_Page // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Set_Active_Sound_Page (SOUND_PAGE page) { if (page == m_CurrPage) { return ; } // // Pause any sounds that are playing in the old page // for (int index = 0; index < m_Playlist[m_CurrPage].Count ();index ++) { m_Playlist[m_CurrPage][index]->Pause (); } // // Resume any sounds that are playing in the new page // for (index = 0; index < m_Playlist[page].Count ();index ++) { m_Playlist[page][index]->Resume (); } m_CurrPage = page; return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Fade_Non_Dialog_In // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Fade_Non_Dialog_In (void) { if (m_FadeType == FADE_IN || m_FadeType == FADE_NONE) { return ; } m_FadeType = FADE_IN; m_FadeTimer = m_NonDialogFadeTime; return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Fade_Non_Dialog_Out // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Fade_Non_Dialog_Out (void) { if (m_FadeType == FADE_OUT || m_FadeType == FADED_OUT) { return ; } m_FadeType = FADE_OUT; m_FadeTimer = m_NonDialogFadeTime; return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Update_Fade // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Update_Fade (void) { if (m_FadeType == FADE_NONE || m_FadeType == FADED_OUT) { return ; } m_FadeTimer -= (WW3D::Get_Frame_Time () / 1000.0F); m_FadeTimer = max (m_FadeTimer, 0.0F); // // Determine what percent we should ramp up or down to... // float percent = (m_FadeTimer / m_NonDialogFadeTime); percent = WWMath::Clamp (percent, 0.0F, 1.0F); // // Invert the percent if we're fading out // if (m_FadeType == FADE_IN) { percent = 1.0F - percent; } // // Determine what the current percent is // const float FADE_MAX = 0.6F; percent = (1.0F - FADE_MAX) + (percent * FADE_MAX); // // Re-adjust the music and sound effect volumes // Internal_Set_Music_Volume (m_RealMusicVolume * percent); Internal_Set_Sound_Effects_Volume (m_RealSoundVolume * percent); // // If we've done the fade, then return to the "no fade" stage // if (m_FadeTimer == 0) { if (m_FadeType == FADE_OUT) { m_FadeType = FADED_OUT; } else { m_FadeType = FADE_NONE; } } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Peek_2D_Sample // //////////////////////////////////////////////////////////////////////////////////////////// AudibleSoundClass * WWAudioClass::Peek_2D_Sample (int index) { if (index < 0 || index > m_2DSampleHandles.Count ()) { return NULL; } MMSLockClass lock; AudibleSoundClass *retval = NULL; // // Try to get the sound object associated with this handle // HSAMPLE sample = m_2DSampleHandles[index]; if (sample != NULL) { retval = (AudibleSoundClass *)::AIL_sample_user_data (sample, INFO_OBJECT_PTR); } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Peek_3D_Sample // //////////////////////////////////////////////////////////////////////////////////////////// AudibleSoundClass * WWAudioClass::Peek_3D_Sample (int index) { if (index < 0 || index > m_3DSampleHandles.Count ()) { return NULL; } MMSLockClass lock; AudibleSoundClass *retval = NULL; // // Try to get the sound object associated with this handle // H3DSAMPLE sample = m_3DSampleHandles[index]; if (sample != NULL) { retval = (AudibleSoundClass *)::AIL_3D_object_user_data (sample, INFO_OBJECT_PTR); } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Acquire_Virtual_Channel // //////////////////////////////////////////////////////////////////////////////////////////// bool WWAudioClass::Acquire_Virtual_Channel (AudibleSoundClass *sound_obj, int channel_index) { // // Verify parameters // channel_index --; if (sound_obj == NULL || channel_index < 0 || channel_index >= MAX_VIRTUAL_CHANNELS) { return false; } // // Is there already a sound playing on this channel? // bool retval = true; if (m_VirtualChannels[channel_index] != NULL) { AudibleSoundClass *curr_sound = m_VirtualChannels[channel_index]; // // If the new sound has overriding priority, then stop the sound // that's currently playing on this channel // if (sound_obj->Get_Priority () >= curr_sound->Get_Priority ()) { m_VirtualChannels[channel_index] = NULL; curr_sound->Stop (); REF_PTR_RELEASE (curr_sound); } else { retval = false; } } // // Store this sound in the virtual channel // if (retval) { m_VirtualChannels[channel_index] = sound_obj; sound_obj->Add_Ref (); } return retval; } //////////////////////////////////////////////////////////////////////////////////////////// // // Release_Virtual_Channel // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Release_Virtual_Channel (AudibleSoundClass *sound_obj, int channel_index) { // // Verify parameters // channel_index --; if (sound_obj == NULL || channel_index < 0 || channel_index >= MAX_VIRTUAL_CHANNELS) { return ; } // // Check to ensure this sound has control of the virtual channel // if (m_VirtualChannels[channel_index] == sound_obj) { // // Free the channel // m_VirtualChannels[channel_index] = NULL; REF_PTR_RELEASE (sound_obj); } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Set_Speaker_Type // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Set_Speaker_Type (int speaker_type) { m_SpeakerType = speaker_type; // // Pass the new speaker type onto miles // if (m_Driver3D != NULL) { ::AIL_set_3D_speaker_type (m_Driver3D, speaker_type); } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Get_Speaker_Type // //////////////////////////////////////////////////////////////////////////////////////////// int WWAudioClass::Get_Speaker_Type (void) const { return m_SpeakerType; } //////////////////////////////////////////////////////////////////////////////////////////// // // Push_Active_Sound_Page // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Push_Active_Sound_Page (SOUND_PAGE page) { m_PageStack.Add (m_CurrPage); Set_Active_Sound_Page (page); return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Pop_Active_Sound_Page // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Pop_Active_Sound_Page (void) { if (m_PageStack.Count () > 0) { SOUND_PAGE new_page = m_PageStack[m_PageStack.Count () - 1]; m_PageStack.Delete (m_PageStack.Count () - 1); Set_Active_Sound_Page (new_page); } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Temp_Disable_Audio // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Temp_Disable_Audio (bool onoff) { if (onoff) { m_CachedIsMusicEnabled = m_IsMusicEnabled; m_CachedIsDialogEnabled = m_IsDialogEnabled; m_CachedIsCinematicSoundEnabled = m_IsCinematicSoundEnabled; m_CachedAreSoundEffectsEnabled = m_AreSoundEffectsEnabled; Allow_Sound_Effects (false); Allow_Music (false); Allow_Dialog (false); Allow_Cinematic_Sound (false); } else { Allow_Sound_Effects (m_CachedAreSoundEffectsEnabled); Allow_Music (m_CachedIsMusicEnabled); Allow_Dialog (m_CachedIsDialogEnabled); Allow_Cinematic_Sound (m_CachedIsCinematicSoundEnabled); } return ; } //////////////////////////////////////////////////////////////////////////////////////////// // // Load_Default_Volume // //////////////////////////////////////////////////////////////////////////////////////////// void WWAudioClass::Load_Default_Volume (int &defaultmusicvolume, int &defaultsoundvolume, int &defaultdialogvolume, int &defaultcinematicvolume) { const int minsetting = 0; const int maxsetting = 100; // IML: If the audio INI has not yet been loaded then do it now. if (AudioIni == NULL) { AudioIni = new INIClass; if (!AudioIni->Load (WWAUDIO_INI_FILENAME)) { AudioIni->Load (WWAUDIO_INI_RELATIVE_PATHNAME); } } defaultmusicvolume = MIN (maxsetting, MAX (minsetting, AudioIni->Get_Int (INI_DEFAULT_VOLUME_SECTION, INI_MUSIC_VOLUME_ENTRY, 31))); defaultsoundvolume = MIN (maxsetting, MAX (minsetting, AudioIni->Get_Int (INI_DEFAULT_VOLUME_SECTION, INI_SOUND_VOLUME_ENTRY, 43))); defaultdialogvolume = MIN (maxsetting, MAX (minsetting, AudioIni->Get_Int (INI_DEFAULT_VOLUME_SECTION, INI_DIALOG_VOLUME_ENTRY, 50))); defaultcinematicvolume = MIN (maxsetting, MAX (minsetting, AudioIni->Get_Int (INI_DEFAULT_VOLUME_SECTION, INI_CINEMATIC_VOLUME_ENTRY, 100))); }