/* ** Command & Conquer Generals Zero Hour(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) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: MilesAudioManager.cpp /*---------------------------------------------------------------------------*/ /* EA Pacific */ /* Confidential Information */ /* Copyright (C) 2001 - All Rights Reserved */ /* DO NOT DISTRIBUTE */ /*---------------------------------------------------------------------------*/ /* Project: RTS3 */ /* File name: MilesAudioManager.cpp */ /* Created: John K. McDonald, Jr., 3/21/2002 */ /* Desc: This is the implementation for the MilesAudioManager, which */ /* interfaces with the Miles Sound System. */ /* Revision History: */ /* 7/18/2002 : Initial creation */ /*---------------------------------------------------------------------------*/ #include #include "Lib/Basetype.h" #include "MilesAudioDevice/MilesAudioManager.h" #include "Common/AudioAffect.h" #include "Common/AudioHandleSpecialValues.h" #include "Common/AudioRequest.h" #include "Common/AudioSettings.h" #include "Common/AsciiString.h" #include "Common/AudioEventInfo.h" #include "Common/FileSystem.h" #include "Common/GameCommon.h" #include "Common/GameSounds.h" #include "Common/CRCDebug.h" #include "Common/GlobalData.h" #include "Common/ScopedMutex.h" #include "GameClient/DebugDisplay.h" #include "GameClient/Drawable.h" #include "GameClient/GameClient.h" #include "GameClient/VideoPlayer.h" #include "GameClient/View.h" #include "GameLogic/GameLogic.h" #include "GameLogic/TerrainLogic.h" #include "Common/File.h" #ifdef _INTERNAL //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif enum { INFINITE_LOOP_COUNT = 1000000 }; // Callback functions useful for Miles //////////////////////////////////////////////////////////// static void AILCALLBACK setSampleCompleted( HSAMPLE sampleCompleted ); static void AILCALLBACK set3DSampleCompleted( H3DSAMPLE sample3DCompleted ); static void AILCALLBACK setStreamCompleted( HSTREAM streamCompleted ); static U32 AILCALLBACK streamingFileOpen(char const *fileName, U32 *file_handle); static void AILCALLBACK streamingFileClose(U32 fileHandle); static S32 AILCALLBACK streamingFileSeek(U32 fileHandle, S32 offset, U32 type); static U32 AILCALLBACK streamingFileRead(U32 fileHandle, void *buffer, U32 bytes); //------------------------------------------------------------------------------------------------- MilesAudioManager::MilesAudioManager() : m_providerCount(0), m_selectedProvider(PROVIDER_ERROR), m_selectedSpeakerType(0), m_lastProvider(PROVIDER_ERROR), m_digitalHandle(NULL), m_num2DSamples(0), m_num3DSamples(0), m_numStreams(0), m_delayFilter(NULL), m_binkHandle(NULL), m_pref3DProvider(AsciiString::TheEmptyString), m_prefSpeaker(AsciiString::TheEmptyString) { m_audioCache = NEW AudioFileCache; } //------------------------------------------------------------------------------------------------- MilesAudioManager::~MilesAudioManager() { DEBUG_ASSERTCRASH(m_binkHandle == NULL, ("Leaked a Bink handle. Chuybregts")); releaseHandleForBink(); closeDevice(); delete m_audioCache; DEBUG_ASSERTCRASH(this == TheAudio, ("Umm...\n")); TheAudio = NULL; } //------------------------------------------------------------------------------------------------- #if defined(_DEBUG) || defined(_INTERNAL) AudioHandle MilesAudioManager::addAudioEvent( const AudioEventRTS *eventToAdd ) { if (TheGlobalData->m_preloadReport) { if (!eventToAdd->getEventName().isEmpty()) { m_allEventsLoaded.insert(eventToAdd->getEventName()); } } return AudioManager::addAudioEvent(eventToAdd); } #endif #if defined(_DEBUG) || defined(_INTERNAL) //------------------------------------------------------------------------------------------------- void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FILE *fp ) { std::list::iterator it; static char buffer[128] = { 0 }; if (buffer[0] == 0) { AIL_MSS_version(buffer, 128); } Coord3D lookPos; TheTacticalView->getPosition( &lookPos ); lookPos.z = TheTerrainLogic->getGroundHeight( lookPos.x, lookPos.y ); const Coord3D *mikePos = TheAudio->getListenerPosition(); Coord3D distanceVector = TheTacticalView->get3DCameraPosition(); distanceVector.sub( mikePos ); Int now = TheGameLogic->getFrame(); static Int lastCheck = now; const Int frames = 60; static Int latency = 0; static Int worstLatency = 0; if( lastCheck + frames <= now ) { latency = AIL_get_timer_highest_delay(); if( latency > worstLatency ) { worstLatency = latency; } lastCheck = now; } if( dd ) { dd->printf("Miles Sound System version: %s ", buffer); dd->printf("Memory Usage : %d/%d\n", m_audioCache->getCurrentlyUsedSize(), m_audioCache->getMaxSize()); dd->printf("Sound: %s ", (isOn(AudioAffect_Sound) ? "Yes" : "No")); dd->printf("3DSound: %s ", (isOn(AudioAffect_Sound3D) ? "Yes" : "No")); dd->printf("Speech: %s ", (isOn(AudioAffect_Speech) ? "Yes" : "No")); dd->printf("Music: %s\n", (isOn(AudioAffect_Music) ? "Yes" : "No")); dd->printf("Channels Available: "); dd->printf("%d Sounds ", m_sound->getAvailableSamples()); dd->printf("%d(%d) 3D Sounds\n", m_sound->getAvailable3DSamples(), m_available3DSamples.size() ); dd->printf("Volume: "); dd->printf("Sound: %d ", REAL_TO_INT(m_soundVolume * 100.0f)); dd->printf("3DSound: %d ", REAL_TO_INT(m_sound3DVolume * 100.0f)); dd->printf("Speech: %d ", REAL_TO_INT(m_speechVolume * 100.0f)); dd->printf("Music: %d\n", REAL_TO_INT(m_musicVolume * 100.0f)); dd->printf("Current 3D Provider: %s ", TheAudio->getProviderName(m_selectedProvider).str()); dd->printf("Current Speaker Type: %s\n", TheAudio->translateUnsignedIntToSpeakerType(TheAudio->getSpeakerType()).str()); dd->printf( "Looking at: (%d,%d,%d) -- Microphone at: (%d,%d,%d)\n", (Int)lookPos.x, (Int)lookPos.y, (Int)lookPos.z, (Int)mikePos->x, (Int)mikePos->y, (Int)mikePos->z ); dd->printf( "Camera distance from microphone: %d -- Zoom Volume: %d%%\n", (Int)distanceVector.length(), (Int)(TheAudio->getZoomVolume()*100.0f) ); dd->printf( "Worst latency: %d -- Current latency: %d\n", worstLatency, latency ); dd->printf("-----------------------------------------------------------\n"); dd->printf("Playing Audio\n"); } if( fp ) { fprintf( fp, "Miles Sound System version: %s ", buffer ); fprintf( fp, "Memory Usage : %d/%d\n", m_audioCache->getCurrentlyUsedSize(), m_audioCache->getMaxSize() ); fprintf( fp, "Sound: %s ", (isOn(AudioAffect_Sound) ? "Yes" : "No") ); fprintf( fp, "3DSound: %s ", (isOn(AudioAffect_Sound3D) ? "Yes" : "No") ); fprintf( fp, "Speech: %s ", (isOn(AudioAffect_Speech) ? "Yes" : "No") ); fprintf( fp, "Music: %s\n", (isOn(AudioAffect_Music) ? "Yes" : "No") ); fprintf( fp, "Channels Available: " ); fprintf( fp, "%d Sounds ", m_sound->getAvailableSamples() ); fprintf( fp, "%d 3D Sounds\n", m_sound->getAvailable3DSamples() ); fprintf( fp, "Volume: "); fprintf( fp, "Sound: %d ", REAL_TO_INT(m_soundVolume * 100.0f) ); fprintf( fp, "3DSound: %d ", REAL_TO_INT(m_sound3DVolume * 100.0f) ); fprintf( fp, "Speech: %d ", REAL_TO_INT(m_speechVolume * 100.0f) ); fprintf( fp, "Music: %d\n", REAL_TO_INT(m_musicVolume * 100.0f) ); fprintf( fp, "Current 3D Provider: %s ", TheAudio->getProviderName(m_selectedProvider).str()); fprintf( fp, "Current Speaker Type: %s\n", TheAudio->translateUnsignedIntToSpeakerType(TheAudio->getSpeakerType()).str()); fprintf( fp, "Looking at: (%d,%d,%d) -- Microphone at: (%d,%d,%d)\n", (Int)lookPos.x, (Int)lookPos.y, (Int)lookPos.z, (Int)mikePos->x, (Int)mikePos->y, (Int)mikePos->z ); fprintf( fp, "Camera distance from microphone: %d -- Zoom Volume: %d%%\n", (Int)distanceVector.length(), (Int)(TheAudio->getZoomVolume()*100.0f) ); fprintf( fp, "-----------------------------------------------------------\n" ); fprintf( fp, "Playing Audio\n" ); } PlayingAudio *playing = NULL; Int channel; Int channelCount; Real volume = 0.0f; AsciiString filenameNoSlashes; const Int maxChannels = 64; PlayingAudio *playingArray[maxChannels] = { NULL }; // 2-D Sounds if( dd ) { dd->printf("-----------------------------------------------------Sounds\n"); channelCount = TheAudio->getNum2DSamples(); for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; if (!playing) { continue; } playingArray[AIL_sample_user_data(playing->m_sample, 0)] = playing; } for (Int i = 1; i <= maxChannels && i <= channelCount; ++i) { playing = playingArray[i]; if (!playing) { dd->printf("%d: Silence\n", i); continue; } filenameNoSlashes = playing->m_audioEventRTS->getFilename(); filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; // Calculate Sample volume volume = 100.0f; volume *= getEffectiveVolume(playing->m_audioEventRTS); dd->printf("%2d: %-20s - (%s) Volume: %d (2D)\n", i, playing->m_audioEventRTS->getEventName().str(), filenameNoSlashes.str(), REAL_TO_INT(volume)); playingArray[i] = NULL; } } if( fp ) { fprintf( fp, "-----------------------------------------------------Sounds\n" ); channelCount = TheAudio->getNum2DSamples(); channel = 1; for( it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { playing = *it; if( !playing ) { continue; } filenameNoSlashes = playing->m_audioEventRTS->getFilename(); filenameNoSlashes = filenameNoSlashes.reverseFind( '\\' ) + 1; // Calculate Sample volume volume = 100.0f; volume *= getEffectiveVolume( playing->m_audioEventRTS ); fprintf( fp, "%2d: %-20s - (%s) Volume: %d (2D)\n", channel++, playing->m_audioEventRTS->getEventName().str(), filenameNoSlashes.str(), REAL_TO_INT( volume ) ); } for( int i = channel; i <= channelCount; ++i ) { fprintf( fp, "%d: Silence\n", i ); } } const Coord3D *microphonePos = TheAudio->getListenerPosition(); // Now 3D Sounds if( dd ) { dd->printf("--------------------------------------------------3D Sounds\n"); channelCount = TheAudio->getNum3DSamples(); for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; if (!playing) { continue; } playingArray[AIL_3D_user_data(playing->m_3DSample, 0)] = playing; } for (Int i = 1; i <= maxChannels && i <= channelCount; ++i) { playing = playingArray[i]; if (!playing) { dd->printf("%d: Silence\n", i); continue; } filenameNoSlashes = playing->m_audioEventRTS->getFilename(); filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; // Calculate Sample volume volume = 100.0f; volume *= getEffectiveVolume(playing->m_audioEventRTS); Real dist = -1.0f; const Coord3D *pos = playing->m_audioEventRTS->getPosition(); char distStr[32]; if( pos ) { Coord3D vector = *microphonePos; vector.sub( pos ); dist = vector.length(); sprintf( distStr, "%d", REAL_TO_INT( dist ) ); } else { sprintf( distStr, "???" ); } char str[32]; switch( playing->m_audioEventRTS->getOwnerType() ) { case OT_Positional: sprintf( str, "(3D)" ); break; case OT_Object: sprintf( str, "(3DObj)" ); break; case OT_Drawable: sprintf( str, "(3DDraw)" ); break; case OT_Dead: sprintf( str, "(3DDead)" ); break; } dd->printf("%2d: %-20s - (%s) Volume: %d, Dist: %s, %s\n", i, playing->m_audioEventRTS->getEventName().str(), filenameNoSlashes.str(), REAL_TO_INT(volume), distStr, str ); playingArray[i] = NULL; } } if( fp ) { fprintf( fp, "--------------------------------------------------3D Sounds\n" ); channelCount = TheAudio->getNum3DSamples(); channel = 1; for( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { playing = *it; if( !playing ) { continue; } filenameNoSlashes = playing->m_audioEventRTS->getFilename(); filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; // Calculate Sample volume volume = 100.0f; volume *= getEffectiveVolume( playing->m_audioEventRTS ); fprintf( fp, "%2d: %-24s - (%s) Volume: %d \n", channel++, playing->m_audioEventRTS->getEventName().str(), filenameNoSlashes.str(), REAL_TO_INT( volume ) ); } for( int i = channel; i <= channelCount; ++i ) { fprintf( fp, "%2d: Silence\n", i ); } } // Now Streams if( dd ) { dd->printf("----------------------------------------------------Streams\n"); channelCount = TheAudio->getNumStreams(); channel = 1; for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (!playing) { continue; } filenameNoSlashes = playing->m_audioEventRTS->getFilename(); filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; // Calculate Sample volume volume = 100.0f; volume *= getEffectiveVolume(playing->m_audioEventRTS); dd->printf("%2d: %-24s - (%s) Volume: %d (Stream)\n", channel++, playing->m_audioEventRTS->getEventName().str(), filenameNoSlashes.str(), REAL_TO_INT(volume)); } for ( int i = channel; i <= channelCount; ++i) { dd->printf("%2d: Silence\n", i); } dd->printf("===========================================================\n"); } if( fp ) { fprintf( fp, "----------------------------------------------------Streams\n" ); channelCount = TheAudio->getNumStreams(); channel = 1; for( it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it ) { playing = *it; if( !playing ) { continue; } filenameNoSlashes = playing->m_audioEventRTS->getFilename(); filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; // Calculate Sample volume volume = 100.0f; volume *= getEffectiveVolume(playing->m_audioEventRTS); fprintf( fp, "%2d: %-24s - (%s) Volume: %d (Stream)\n", channel++, playing->m_audioEventRTS->getEventName().str(), filenameNoSlashes.str(), REAL_TO_INT( volume ) ); } for( int i = channel; i <= channelCount; ++i ) { fprintf( fp, "%2d: Silence\n", i ); } fprintf( fp, "===========================================================\n" ); } } #endif //------------------------------------------------------------------------------------------------- void MilesAudioManager::init() { AudioManager::init(); #ifdef INTENSE_DEBUG DEBUG_LOG(("Sound has temporarily been disabled in debug builds only. jkmcd\n")); // for now, _DEBUG builds only should have no sound. ask jkmcd or srj about this. return; #endif // We should now know how many samples we want to load openDevice(); m_audioCache->setMaxSize(getAudioSettings()->m_maxCacheSize); // Now, set the file callbacks to load the streams from Biggie files AIL_set_file_callbacks(streamingFileOpen, streamingFileClose, streamingFileSeek, streamingFileRead); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::postProcessLoad() { AudioManager::postProcessLoad(); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::reset() { #if defined(_DEBUG) || defined(_INTERNAL) dumpAllAssetsUsed(); m_allEventsLoaded.clear(); #endif AudioManager::reset(); stopAllAudioImmediately(); removeAllAudioRequests(); // This must come after stopAllAudioImmediately() and removeAllAudioRequests(), to ensure that // sounds pointing to the temporary AudioEventInfo handles are deleted before their info is deleted removeLevelSpecificAudioEventInfos(); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::update() { AudioManager::update(); setDeviceListenerPosition(); processRequestList(); processPlayingList(); processFadingList(); processStoppedList(); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::stopAudio( AudioAffect which ) { // All we really need to do is: // 1) Remove the EOS callback. // 2) Stop the sample, (so that when we later unload it, bad stuff doesn't happen) // 3) Set the status to stopped, so that when we next process the playing list, we will // correctly clean up the sample. std::list::iterator it; PlayingAudio *playing = NULL; if (BitTest(which, AudioAffect_Sound)) { for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; if (playing) { AIL_register_EOS_callback(playing->m_sample, NULL); AIL_stop_sample(playing->m_sample); playing->m_status = PS_Stopped; } } } if (BitTest(which, AudioAffect_Sound3D)) { for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; if (playing) { AIL_register_3D_EOS_callback(playing->m_3DSample, NULL); AIL_stop_3D_sample(playing->m_3DSample); playing->m_status = PS_Stopped; } } } if (BitTest(which, AudioAffect_Speech | AudioAffect_Music)) { for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (playing) { if (playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { if (!BitTest(which, AudioAffect_Music)) { continue; } } else { if (!BitTest(which, AudioAffect_Speech)) { continue; } } AIL_register_stream_callback(playing->m_stream, NULL); AIL_pause_stream(playing->m_stream, 1); playing->m_status = PS_Stopped; } } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::pauseAudio( AudioAffect which ) { std::list::iterator it; PlayingAudio *playing = NULL; if (BitTest(which, AudioAffect_Sound)) { for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; if (playing) { AIL_stop_sample(playing->m_sample); } } } if (BitTest(which, AudioAffect_Sound3D)) { for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; if (playing) { AIL_stop_3D_sample(playing->m_3DSample); } } } if (BitTest(which, AudioAffect_Speech | AudioAffect_Music)) { for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (playing) { if (playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { if (!BitTest(which, AudioAffect_Music)) { continue; } } else { if (!BitTest(which, AudioAffect_Speech)) { continue; } } AIL_pause_stream(playing->m_stream, 1); } } } //Get rid of PLAY audio requests when pausing audio. std::list::iterator ait; for (ait = m_audioRequests.begin(); ait != m_audioRequests.end(); /* empty */) { AudioRequest *req = (*ait); if( req && req->m_request == AR_Play ) { req->deleteInstance(); ait = m_audioRequests.erase(ait); } else { ait++; } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::resumeAudio( AudioAffect which ) { std::list::iterator it; PlayingAudio *playing = NULL; if (BitTest(which, AudioAffect_Sound)) { for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; if (playing) { AIL_resume_sample(playing->m_sample); } } } if (BitTest(which, AudioAffect_Sound3D)) { for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; if (playing) { AIL_resume_3D_sample(playing->m_3DSample); } } } if (BitTest(which, AudioAffect_Speech | AudioAffect_Music)) { for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (playing) { if (playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { if (!BitTest(which, AudioAffect_Music)) { continue; } } else { if (!BitTest(which, AudioAffect_Speech)) { continue; } } AIL_pause_stream(playing->m_stream, 0); } } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::pauseAmbient( Bool shouldPause ) { } //------------------------------------------------------------------------------------------------- void MilesAudioManager::playAudioEvent( AudioEventRTS *event ) { #ifdef INTENSIVE_AUDIO_DEBUG DEBUG_LOG(("MILES (%d) - Processing play request: %d (%s)", TheGameLogic->getFrame(), event->getPlayingHandle(), event->getEventName().str())); #endif const AudioEventInfo *info = event->getAudioEventInfo(); if (!info) { return; } std::list::iterator it; PlayingAudio *playing = NULL; AudioHandle handleToKill = event->getHandleToKill(); AsciiString fileToPlay = event->getFilename(); PlayingAudio *audio = allocatePlayingAudio(); switch(info->m_soundType) { case AT_Music: case AT_Streaming: { #ifdef INTENSIVE_AUDIO_DEBUG DEBUG_LOG(("- Stream\n")); #endif if ((info->m_soundType == AT_Streaming) && event->getUninterruptable()) { stopAllSpeech(); } Real curVolume = 1.0; if (info->m_soundType == AT_Music) { curVolume = m_musicVolume; } else { curVolume = m_speechVolume; } curVolume *= event->getVolume(); Bool foundSoundToReplace = false; if (handleToKill) { for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = (*it); if (!playing) { continue; } if (playing->m_audioEventRTS && playing->m_audioEventRTS->getPlayingHandle() == handleToKill) { //Release this streaming channel immediately because we are going to play another sound in it's place. releasePlayingAudio(playing); m_playingStreams.erase(it); foundSoundToReplace = true; break; } } } HSTREAM stream; if (!handleToKill || foundSoundToReplace) { stream = AIL_open_stream(m_digitalHandle, fileToPlay.str(), 0); } else { stream = NULL; } // Put this on here, so that the audio event RTS will be cleaned up regardless. audio->m_audioEventRTS = event; audio->m_stream = stream; audio->m_type = PAT_Stream; if (stream) { if ((info->m_soundType == AT_Streaming) && event->getUninterruptable()) { setDisallowSpeech(TRUE); } AIL_set_stream_volume_pan(stream, curVolume, 0.5f); playStream(event, stream); m_playingStreams.push_back(audio); audio = NULL; } break; } case AT_SoundEffect: { #ifdef INTENSIVE_AUDIO_DEBUG DEBUG_LOG(("- Sound")); #endif if (event->isPositionalAudio()) { // Sounds that are non-global are positional 3-D sounds. Deal with them accordingly #ifdef INTENSIVE_AUDIO_DEBUG DEBUG_LOG((" Positional")); #endif Bool foundSoundToReplace = false; if (handleToKill) { for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = (*it); if (!playing) { continue; } if( playing->m_audioEventRTS && playing->m_audioEventRTS->getPlayingHandle() == handleToKill ) { //Release this 3D sound channel immediately because we are going to play another sound in it's place. releasePlayingAudio(playing); m_playing3DSounds.erase(it); foundSoundToReplace = true; break; } } } H3DSAMPLE sample3D; if( !handleToKill || foundSoundToReplace ) { sample3D = getFirst3DSample( event ); if( !sample3D ) { //If we don't have an available sample, kill the lowest priority assuming we have one that is lower //than the sound we are trying to add. One possibility for strangeness is when an interrupt sound //that wants to kill a handle to replace it, it's possible that another request already killed it, //in which case we need to attempt to find another sound to kill. if( killLowestPrioritySoundImmediately( event ) ) { sample3D = getFirst3DSample( event ); } } } else { sample3D = NULL; } // Push it onto the list of playing things audio->m_audioEventRTS = event; audio->m_3DSample = sample3D; audio->m_file = NULL; audio->m_type = PAT_3DSample; m_playing3DSounds.push_back(audio); if (sample3D) { audio->m_file = playSample3D(event, sample3D); m_sound->notifyOf3DSampleStart(); } if( !audio->m_file ) { m_playing3DSounds.pop_back(); #ifdef INTENSIVE_AUDIO_DEBUG DEBUG_LOG((" Killed (no handles available)\n")); #endif } else { audio = NULL; #ifdef INTENSIVE_AUDIO_DEBUG DEBUG_LOG((" Playing.\n")); #endif } } else { // UI sounds are always 2-D. All other sounds should be Positional // Unit acknowledgement, etc, falls into the UI category of sound. Bool foundSoundToReplace = false; if (handleToKill) { for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = (*it); if (!playing) { continue; } if (playing->m_audioEventRTS && playing->m_audioEventRTS->getPlayingHandle() == handleToKill) { //Release this 2D sound channel immediately because we are going to play another sound in it's place. releasePlayingAudio(playing); m_playingSounds.erase(it); foundSoundToReplace = true; break; } } } HSAMPLE sample; if( !handleToKill || foundSoundToReplace ) { sample = getFirst2DSample(event); if( !sample ) { //If we don't have an available sample, kill the lowest priority assuming we have one that is lower //than the sound we are trying to add. One possibility for strangeness is when an interrupt sound //that wants to kill a handle to replace it, it's possible that another request already killed it, //in which case we need to attempt to find another sound to kill. if( killLowestPrioritySoundImmediately( event ) ) { sample = getFirst2DSample( event ); } } } else { sample = NULL; } // Push it onto the list of playing things audio->m_audioEventRTS = event; audio->m_sample = sample; audio->m_file = NULL; audio->m_type = PAT_Sample; m_playingSounds.push_back(audio); if (sample) { audio->m_file = playSample(event, sample); m_sound->notifyOf2DSampleStart(); } if (!audio->m_file) { #ifdef INTENSIVE_AUDIO_DEBUG DEBUG_LOG((" Killed (no handles available)\n")); #endif m_playingSounds.pop_back(); } else { audio = NULL; } #ifdef INTENSIVE_AUDIO_DEBUG DEBUG_LOG((" Playing.\n")); #endif } break; } } // If we were able to successfully play audio, then we set it to NULL above. (And it will be freed // later. However, if audio is non-NULL at this point, then it must be freed. if (audio) { releasePlayingAudio(audio); } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::stopAudioEvent( AudioHandle handle ) { #ifdef INTENSIVE_AUDIO_DEBUG DEBUG_LOG(("MILES (%d) - Processing stop request: %d\n", TheGameLogic->getFrame(), handle)); #endif std::list::iterator it; if ( handle == AHSV_StopTheMusic || handle == AHSV_StopTheMusicFade ) { // for music, just find the currently playing music stream and kill it. for ( it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it ) { PlayingAudio *audio = (*it); if (!audio) { continue; } if( audio->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music ) { if( handle == AHSV_StopTheMusicFade ) { m_fadingAudio.push_back(audio); } else { //m_stoppedAudio.push_back(audio); releasePlayingAudio( audio ); } m_playingStreams.erase(it); break; } } } for ( it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it ) { PlayingAudio *audio = (*it); if (!audio) { continue; } if (audio->m_audioEventRTS->getPlayingHandle() == handle) { // found it audio->m_requestStop = true; notifyOfAudioCompletion((UnsignedInt)(audio->m_stream), PAT_Stream); break; } } for ( it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { PlayingAudio *audio = (*it); if (!audio) { continue; } if (audio->m_audioEventRTS->getPlayingHandle() == handle) { audio->m_requestStop = true; break; } } for ( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { PlayingAudio *audio = (*it); if (!audio) { continue; } if (audio->m_audioEventRTS->getPlayingHandle() == handle) { #ifdef INTENSIVE_AUDIO_DEBUG DEBUG_LOG((" (%s)\n", audio->m_audioEventRTS->getEventName())); #endif audio->m_requestStop = true; break; } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::killAudioEventImmediately( AudioHandle audioEvent ) { //First look for it in the request list. std::list::iterator ait; for( ait = m_audioRequests.begin(); ait != m_audioRequests.end(); ait++ ) { AudioRequest *req = (*ait); if( req && req->m_request == AR_Play && req->m_handleToInteractOn == audioEvent ) { req->deleteInstance(); ait = m_audioRequests.erase(ait); return; } } //Look for matching 3D sound to kill std::list::iterator it; for( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); it++ ) { PlayingAudio *audio = (*it); if( !audio ) { continue; } if( audio->m_audioEventRTS->getPlayingHandle() == audioEvent ) { releasePlayingAudio( audio ); m_playing3DSounds.erase( it ); return; } } //Look for matching 2D sound to kill for( it = m_playingSounds.begin(); it != m_playingSounds.end(); it++ ) { PlayingAudio *audio = (*it); if( !audio ) { continue; } if( audio->m_audioEventRTS->getPlayingHandle() == audioEvent ) { releasePlayingAudio( audio ); m_playingSounds.erase( it ); return; } } //Look for matching steaming sound to kill for( it = m_playingStreams.begin(); it != m_playingStreams.end(); it++ ) { PlayingAudio *audio = (*it); if( !audio ) { continue; } if( audio->m_audioEventRTS->getPlayingHandle() == audioEvent ) { releasePlayingAudio( audio ); m_playingStreams.erase( it ); return; } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::pauseAudioEvent( AudioHandle handle ) { // pause audio } //------------------------------------------------------------------------------------------------- void *MilesAudioManager::loadFileForRead( AudioEventRTS *eventToLoadFrom ) { return m_audioCache->openFile(eventToLoadFrom); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::closeFile( void *fileRead ) { m_audioCache->closeFile(fileRead); } //------------------------------------------------------------------------------------------------- PlayingAudio *MilesAudioManager::allocatePlayingAudio( void ) { PlayingAudio *aud = NEW PlayingAudio; // poolify aud->m_status = PS_Playing; return aud; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::releaseMilesHandles( PlayingAudio *release ) { switch (release->m_type) { case PAT_Sample: { if (release->m_sample) { AIL_register_EOS_callback(release->m_sample, NULL); AIL_stop_sample(release->m_sample); m_availableSamples.push_back(release->m_sample); } break; } case PAT_3DSample: { if (release->m_3DSample) { AIL_register_3D_EOS_callback(release->m_3DSample, NULL); AIL_stop_3D_sample(release->m_3DSample); m_available3DSamples.push_back(release->m_3DSample); } break; } case PAT_Stream: { if (release->m_stream) { AIL_register_stream_callback(release->m_stream, NULL); AIL_close_stream(release->m_stream); } break; } } release->m_type = PAT_INVALID; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::releasePlayingAudio( PlayingAudio *release ) { if (release->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_SoundEffect) { if (release->m_type == PAT_Sample) { if (release->m_sample) { m_sound->notifyOf2DSampleCompletion(); } } else { if (release->m_3DSample) { m_sound->notifyOf3DSampleCompletion(); } } } releaseMilesHandles(release); // forces stop of this audio closeFile( release->m_file ); if (release->m_cleanupAudioEventRTS) { releaseAudioEventRTS(release->m_audioEventRTS); } delete release; release = NULL; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::stopAllAudioImmediately( void ) { std::list::iterator it; PlayingAudio *playing; for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ) { playing = *it; if (!playing) { continue; } releasePlayingAudio(playing); it = m_playingSounds.erase(it); } for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) { playing = *it; if (!playing) { continue; } releasePlayingAudio(playing); it = m_playing3DSounds.erase(it); } for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ) { playing = (*it); if (!playing) { continue; } releasePlayingAudio(playing); it = m_playingStreams.erase(it); } for (it = m_fadingAudio.begin(); it != m_fadingAudio.end(); ) { playing = (*it); if (!playing) { continue; } releasePlayingAudio(playing); it = m_fadingAudio.erase(it); } std::list::iterator hit; for (hit = m_audioForcePlayed.begin(); hit != m_audioForcePlayed.end(); ++hit) { if (*hit) { AIL_quick_unload(*hit); } } m_audioForcePlayed.clear(); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::freeAllMilesHandles( void ) { // First, we need to ensure that we don't have any sample handles open. To that end, we must stop // all of our currently playing audio. stopAllAudioImmediately(); // Walks through the available 2-D and 3-D handles and releases them std::list::iterator it; for ( it = m_availableSamples.begin(); it != m_availableSamples.end(); /* empty */ ) { HSAMPLE sample = *it; AIL_release_sample_handle(sample); it = m_availableSamples.erase(it); } m_num2DSamples = 0; std::list::iterator it3D; for ( it3D = m_available3DSamples.begin(); it3D != m_available3DSamples.end(); /* empty */ ) { H3DSAMPLE sample3D = *it3D; AIL_release_3D_sample_handle(sample3D); it3D = m_available3DSamples.erase(it3D); } m_num3DSamples = 0; m_numStreams = 0; } //------------------------------------------------------------------------------------------------- HSAMPLE MilesAudioManager::getFirst2DSample( AudioEventRTS *event ) { if (m_availableSamples.begin() != m_availableSamples.end()) { HSAMPLE retSample = *m_availableSamples.begin(); m_availableSamples.erase(m_availableSamples.begin()); return (retSample); } // Find the first sample of lower priority than my augmented priority that is interruptable and take its handle return NULL; } //------------------------------------------------------------------------------------------------- H3DSAMPLE MilesAudioManager::getFirst3DSample( AudioEventRTS *event ) { if (m_available3DSamples.begin() != m_available3DSamples.end()) { H3DSAMPLE retSample = *m_available3DSamples.begin(); m_available3DSamples.erase(m_available3DSamples.begin()); return (retSample); } // Find the first sample of lower priority than my augmented priority that is interruptable and take its handle return NULL; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::adjustPlayingVolume( PlayingAudio *audio ) { Real desiredVolume = audio->m_audioEventRTS->getVolume() * audio->m_audioEventRTS->getVolumeShift(); Real pan; if (audio->m_type == PAT_Sample) { AIL_sample_volume_pan(audio->m_sample, NULL, &pan); AIL_set_sample_volume_pan(audio->m_sample, m_soundVolume * desiredVolume, pan); } else if (audio->m_type == PAT_3DSample) { AIL_set_3D_sample_volume(audio->m_3DSample, m_sound3DVolume * desiredVolume); } else if (audio->m_type == PAT_Stream) { AIL_stream_volume_pan(audio->m_stream, NULL, &pan); if (audio->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music ) { AIL_set_stream_volume_pan(audio->m_stream, m_musicVolume * desiredVolume, pan); } else { AIL_set_stream_volume_pan(audio->m_stream, m_speechVolume * desiredVolume, pan); } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::stopAllSpeech( void ) { std::list::iterator it; PlayingAudio *playing; for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ) { playing = (*it); if (!playing) { continue; } if (playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Streaming) { releasePlayingAudio(playing); it = m_playingStreams.erase(it); } else { ++it; } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::initFilters( HSAMPLE sample, const AudioEventRTS *event ) { // set the sample volume Real volume = event->getVolume() * event->getVolumeShift() * m_soundVolume; AIL_set_sample_volume_pan(sample, volume, 0.5f); // pitch shift Real pitchShift = event->getPitchShift(); if (pitchShift == 0.0f) { DEBUG_CRASH(("Invalid Pitch shift in sound: '%s'", event->getEventName().str()) ); } else { AIL_set_sample_playback_rate(sample, REAL_TO_INT(AIL_sample_playback_rate(sample) * pitchShift)); } // set up delay filter, if applicable if (event->getDelay() > 0.0f) { Real value; value = event->getDelay(); AIL_set_sample_processor(sample, DP_FILTER, m_delayFilter); AIL_set_filter_sample_preference(sample, "Mono Delay Time", &value); value = 0.0; AIL_set_filter_sample_preference(sample, "Mono Delay", &value); AIL_set_filter_sample_preference(sample, "Mono Delay Mix", &value); } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::initFilters3D( H3DSAMPLE sample, const AudioEventRTS *event, const Coord3D *pos ) { // set the sample volume Real volume = event->getVolume() * event->getVolumeShift() * m_sound3DVolume; AIL_set_3D_sample_volume(sample, volume); // pitch shift Real pitchShift = event->getPitchShift(); if (pitchShift == 0.0f) { DEBUG_CRASH(("Invalid Pitch shift in sound: '%s'", event->getEventName().str()) ); } else { AIL_set_3D_sample_playback_rate(sample, REAL_TO_INT(AIL_3D_sample_playback_rate(sample) * pitchShift)); } // Low pass filter if (event->getAudioEventInfo()->m_lowPassFreq > 0 && !isOnScreen(pos) ) { AIL_set_3D_sample_occlusion(sample, 1.0f - event->getAudioEventInfo()->m_lowPassFreq); } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::nextMusicTrack( void ) { AsciiString trackName; std::list::iterator it; PlayingAudio *playing; for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (playing && playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { trackName = playing->m_audioEventRTS->getEventName(); } } // Stop currently playing music TheAudio->removeAudioEvent(AHSV_StopTheMusic); trackName = nextTrackName(trackName); AudioEventRTS newTrack(trackName); TheAudio->addAudioEvent(&newTrack); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::prevMusicTrack( void ) { AsciiString trackName; std::list::iterator it; PlayingAudio *playing; for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (playing && playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { trackName = playing->m_audioEventRTS->getEventName(); } } // Stop currently playing music TheAudio->removeAudioEvent(AHSV_StopTheMusic); trackName = prevTrackName(trackName); AudioEventRTS newTrack(trackName); TheAudio->addAudioEvent(&newTrack); } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::isMusicPlaying( void ) const { std::list::const_iterator it; PlayingAudio *playing; for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (playing && playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { return TRUE; } } return FALSE; } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::hasMusicTrackCompleted( const AsciiString& trackName, Int numberOfTimes ) const { std::list::const_iterator it; PlayingAudio *playing; for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (playing && playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { if (playing->m_audioEventRTS->getEventName() == trackName) { if (INFINITE_LOOP_COUNT - AIL_stream_loop_count(playing->m_stream) >= numberOfTimes) { return TRUE; } } } } return FALSE; } //------------------------------------------------------------------------------------------------- AsciiString MilesAudioManager::getMusicTrackName( void ) const { // First check the requests. If there's one there, then report that as the currently playing track. std::list::const_iterator ait; for (ait = m_audioRequests.begin(); ait != m_audioRequests.end(); ++ait) { if ((*ait)->m_request != AR_Play) { continue; } if (!(*ait)->m_usePendingEvent) { continue; } if ((*ait)->m_pendingEvent->getAudioEventInfo()->m_soundType == AT_Music) { return (*ait)->m_pendingEvent->getEventName(); } } std::list::const_iterator it; PlayingAudio *playing; for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (playing && playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { return playing->m_audioEventRTS->getEventName(); } } return AsciiString::TheEmptyString; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::openDevice( void ) { if (!TheGlobalData->m_audioOn) { return; } AIL_set_redist_directory("MSS\\"); AIL_startup(); Int retval = 0; // AIL_quick_startup should be replaced later with a call to actually pick which device to use, etc const AudioSettings *audioSettings = getAudioSettings(); m_selectedSpeakerType = TheAudio->translateSpeakerTypeToUnsignedInt(m_prefSpeaker); retval = AIL_quick_startup(audioSettings->m_useDigital, audioSettings->m_useMidi, audioSettings->m_outputRate, audioSettings->m_outputBits, audioSettings->m_outputChannels); // Quick handles tells us where to store the various devices. For now, we're only interested in the digital handle. AIL_quick_handles(&m_digitalHandle, NULL, NULL); if (retval) { buildProviderList(); } else { // if we couldn't initialize any devices, turn sound off (fail silently) setOn( false, AudioAffect_All ); } selectProvider(TheAudio->getProviderIndex(m_pref3DProvider)); // Now that we're all done, update the cached variables so that everything is in sync. TheAudio->refreshCachedVariables(); if (!isValidProvider()) { return; } initDelayFilter(); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::closeDevice( void ) { freeAllMilesHandles(); unselectProvider(); AIL_shutdown(); } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::isCurrentlyPlaying( AudioHandle handle ) { std::list::iterator it; PlayingAudio *playing; for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; if (playing && playing->m_audioEventRTS->getPlayingHandle() == handle) { return true; } } for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; if (playing && playing->m_audioEventRTS->getPlayingHandle() == handle) { return true; } } for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (playing && playing->m_audioEventRTS->getPlayingHandle() == handle) { return true; } } // if something is requested, it is also considered playing std::list::iterator ait; AudioRequest *req = NULL; for (ait = m_audioRequests.begin(); ait != m_audioRequests.end(); ++ait) { req = *ait; if (req && req->m_usePendingEvent && req->m_pendingEvent->getPlayingHandle() == handle) { return true; } } return false; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::notifyOfAudioCompletion( UnsignedInt audioCompleted, UnsignedInt flags ) { PlayingAudio *playing = findPlayingAudioFrom(audioCompleted, flags); if (!playing) { DEBUG_CRASH(("Audio has completed playing, but we can't seem to find it. - jkmcd")); return; } if (getDisallowSpeech() && playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Streaming) { setDisallowSpeech(FALSE); } if (playing->m_audioEventRTS->getAudioEventInfo()->m_control & AC_LOOP) { if (playing->m_audioEventRTS->getNextPlayPortion() == PP_Attack) { playing->m_audioEventRTS->setNextPlayPortion(PP_Sound); } if (playing->m_audioEventRTS->getNextPlayPortion() == PP_Sound) { // First, decrease the loop count. playing->m_audioEventRTS->decreaseLoopCount(); // Now, try to start the next loop if (startNextLoop(playing)) { return; } } } playing->m_audioEventRTS->advanceNextPlayPortion(); if (playing->m_audioEventRTS->getNextPlayPortion() != PP_Done) { if (playing->m_type == PAT_Sample) { closeFile(playing->m_file); // close it so as not to leak it. playing->m_file = playSample(playing->m_audioEventRTS, playing->m_sample); // If we don't have a file now, then we should drop to the stopped status so that // We correctly close this handle. if (playing->m_file) { return; } } else if (playing->m_type == PAT_3DSample) { closeFile(playing->m_file); // close it so as not to leak it. playing->m_file = playSample3D(playing->m_audioEventRTS, playing->m_3DSample); // If we don't have a file now, then we should drop to the stopped status so that // We correctly close this handle. if (playing->m_file) { return; } } } if (playing->m_type == PAT_Stream) { if (playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { playStream(playing->m_audioEventRTS, playing->m_stream); return; } } playing->m_status = PS_Stopped; // it will be cleaned up on the next frame update } //------------------------------------------------------------------------------------------------- PlayingAudio *MilesAudioManager::findPlayingAudioFrom( UnsignedInt audioCompleted, UnsignedInt flags ) { std::list::iterator it; PlayingAudio *playing; if (flags == PAT_Sample) { HSAMPLE sample = (HSAMPLE) audioCompleted; for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; if (playing && playing->m_sample == sample) { return playing; } } } if (flags == PAT_3DSample) { H3DSAMPLE sample3D = (H3DSAMPLE) audioCompleted; for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; if (playing && playing->m_3DSample == sample3D) { return playing; } } } if (flags == PAT_Stream) { HSTREAM stream = (HSTREAM) audioCompleted; for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (playing && playing->m_stream == stream) { return playing; } } } return NULL; } //------------------------------------------------------------------------------------------------- UnsignedInt MilesAudioManager::getProviderCount( void ) const { return m_providerCount; } //------------------------------------------------------------------------------------------------- AsciiString MilesAudioManager::getProviderName( UnsignedInt providerNum ) const { if (isOn(AudioAffect_Sound3D) && providerNum < m_providerCount) { return m_provider3D[providerNum].name; } return AsciiString::TheEmptyString; } //------------------------------------------------------------------------------------------------- UnsignedInt MilesAudioManager::getProviderIndex( AsciiString providerName ) const { for (UnsignedInt i = 0; i < m_providerCount; ++i) { if (providerName == m_provider3D[i].name) { return i; } } return PROVIDER_ERROR; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::selectProvider( UnsignedInt providerNdx ) { if (!isOn(AudioAffect_Sound3D)) { return; } if (providerNdx == m_selectedProvider) { return; } if (isValidProvider()) { freeAllMilesHandles(); unselectProvider(); } LPDIRECTSOUND lpDirectSoundInfo; AIL_get_DirectSound_info( NULL, (void**)&lpDirectSoundInfo, NULL ); Bool useDolby = FALSE; if( lpDirectSoundInfo ) { DWORD speakerConfig; lpDirectSoundInfo->GetSpeakerConfig( &speakerConfig ); switch( DSSPEAKER_CONFIG( speakerConfig ) ) { case DSSPEAKER_DIRECTOUT: m_selectedSpeakerType = AIL_3D_2_SPEAKER; break; case DSSPEAKER_MONO: m_selectedSpeakerType = AIL_3D_2_SPEAKER; break; case DSSPEAKER_STEREO: m_selectedSpeakerType = AIL_3D_2_SPEAKER; break; case DSSPEAKER_HEADPHONE: m_selectedSpeakerType = AIL_3D_HEADPHONE; useDolby = TRUE; break; case DSSPEAKER_QUAD: m_selectedSpeakerType = AIL_3D_4_SPEAKER; useDolby = TRUE; break; case DSSPEAKER_SURROUND: m_selectedSpeakerType = AIL_3D_SURROUND; useDolby = TRUE; break; case DSSPEAKER_5POINT1: m_selectedSpeakerType = AIL_3D_51_SPEAKER; useDolby = TRUE; break; case DSSPEAKER_7POINT1: m_selectedSpeakerType = AIL_3D_71_SPEAKER; useDolby = TRUE; break; } } Bool success = FALSE; if( useDolby ) { providerNdx = getProviderIndex( "Dolby Surround" ); } else { providerNdx = getProviderIndex( "Miles Fast 2D Positional Audio" ); } success = AIL_open_3D_provider( m_provider3D[providerNdx].id ) == 0; //if (providerNdx < m_providerCount) //{ // failed = AIL_open_3D_provider(m_provider3D[providerNdx].id); //} if( !success ) { m_selectedProvider = PROVIDER_ERROR; // try to select a failsafe providerNdx = getProviderIndex( "Miles Fast 2D Positional Audio" ); success = AIL_open_3D_provider( m_provider3D[providerNdx].id ) == 0; } if ( success ) { m_selectedProvider = providerNdx; initSamplePools(); createListener(); setSpeakerType(m_selectedSpeakerType); if (TheVideoPlayer) { TheVideoPlayer->notifyVideoPlayerOfNewProvider(TRUE); } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::unselectProvider( void ) { if (!(isOn(AudioAffect_Sound3D) && isValidProvider())) { return; } if (TheVideoPlayer) { TheVideoPlayer->notifyVideoPlayerOfNewProvider(FALSE); } AIL_close_3D_listener(m_listener); m_listener = NULL; AIL_close_3D_provider(m_provider3D[m_selectedProvider].id); m_lastProvider = m_selectedProvider; m_selectedProvider = PROVIDER_ERROR; } //------------------------------------------------------------------------------------------------- UnsignedInt MilesAudioManager::getSelectedProvider( void ) const { return m_selectedProvider; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::setSpeakerType( UnsignedInt speakerType ) { if (!isValidProvider()) { return; } AIL_set_3D_speaker_type( m_provider3D[m_selectedProvider].id, speakerType ); m_selectedSpeakerType = speakerType; } //------------------------------------------------------------------------------------------------- UnsignedInt MilesAudioManager::getSpeakerType( void ) { if (!isValidProvider()) { return 0; } return m_selectedSpeakerType; } //------------------------------------------------------------------------------------------------- UnsignedInt MilesAudioManager::getNum2DSamples( void ) const { return m_num2DSamples; } //------------------------------------------------------------------------------------------------- UnsignedInt MilesAudioManager::getNum3DSamples( void ) const { return m_num3DSamples; } //------------------------------------------------------------------------------------------------- UnsignedInt MilesAudioManager::getNumStreams( void ) const { return m_numStreams; } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::doesViolateLimit( AudioEventRTS *event ) const { Int limit = event->getAudioEventInfo()->m_limit; if (limit == 0) { return false; } Int totalCount = 0; Int totalRequestCount = 0; std::list::const_iterator it; if (!event->isPositionalAudio()) { // 2-D for ( it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { if ((*it)->m_audioEventRTS->getEventName() == event->getEventName()) { if (totalCount == 0) { // This is the oldest audio of this type playing. event->setHandleToKill((*it)->m_audioEventRTS->getPlayingHandle()); } ++totalCount; } } } else { // 3-D for ( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { if ((*it)->m_audioEventRTS->getEventName() == event->getEventName()) { if (totalCount == 0) { // This is the oldest audio of this type playing. event->setHandleToKill((*it)->m_audioEventRTS->getPlayingHandle()); } ++totalCount; } } } // Also check the request list in case we've requested to play this sound. std::list::const_iterator arIt; for (arIt = m_audioRequests.begin(); arIt != m_audioRequests.end(); ++arIt) { AudioRequest *req = (*arIt); if (req == NULL) { continue; } if( req->m_usePendingEvent ) { if( req->m_pendingEvent->getEventName() == event->getEventName() ) { totalRequestCount++; totalCount++; } } } //If our event is an interrupting type, then normally we would always add it. The exception is when we have requested //multiple sounds in the same frame and those requests violate the limit. Because we don't have any "old" sounds to //remove in the case of an interrupt, we need to catch it early and prevent the sound from being added if we already //reached the limit if( event->getAudioEventInfo()->m_control & AC_INTERRUPT ) { if( totalRequestCount < limit ) { Int totalPlayingCount = totalCount - totalRequestCount; if( totalRequestCount + totalPlayingCount < limit ) { //We aren't exceeding the actual limit, then clear the kill handle. event->setHandleToKill(0); return false; } //We are exceeding the limit - the kill handle will kill the //oldest playing sound to enforce the actual limit. return false; } } if( totalCount < limit ) { event->setHandleToKill(0); return false; } return true; } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::isPlayingAlready( AudioEventRTS *event ) const { std::list::const_iterator it; if (!event->isPositionalAudio()) { // 2-D for ( it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { if ((*it)->m_audioEventRTS->getEventName() == event->getEventName()) { return true; } } } else { // 3-D for ( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { if ((*it)->m_audioEventRTS->getEventName() == event->getEventName()) { return true; } } } return false; } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::isObjectPlayingVoice( UnsignedInt objID ) const { if (objID == 0) { return false; } std::list::const_iterator it; // 2-D for ( it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { if ((*it)->m_audioEventRTS->getObjectID() == objID && (*it)->m_audioEventRTS->getAudioEventInfo()->m_type & ST_VOICE) { return true; } } // 3-D for ( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { if ((*it)->m_audioEventRTS->getObjectID() == objID && (*it)->m_audioEventRTS->getAudioEventInfo()->m_type & ST_VOICE) { return true; } } return false; } //------------------------------------------------------------------------------------------------- AudioEventRTS* MilesAudioManager::findLowestPrioritySound( AudioEventRTS *event ) { AudioPriority priority = event->getAudioEventInfo()->m_priority; if( priority == AP_LOWEST ) { //If the event we pass in is the lowest priority, don't bother checking because //there is nothing lower priority than lowest. return NULL; } AudioEventRTS *lowestPriorityEvent = NULL; AudioPriority lowestPriority; std::list::const_iterator it; if( event->isPositionalAudio() ) { //3D for( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { AudioEventRTS *itEvent = (*it)->m_audioEventRTS; AudioPriority itPriority = itEvent->getAudioEventInfo()->m_priority; if( itPriority < priority ) { if( !lowestPriorityEvent || lowestPriority > itPriority ) { lowestPriorityEvent = itEvent; lowestPriority = itPriority; if( lowestPriority == AP_LOWEST ) { return lowestPriorityEvent; } } } } } else { //2D for( it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { AudioEventRTS *itEvent = (*it)->m_audioEventRTS; AudioPriority itPriority = itEvent->getAudioEventInfo()->m_priority; if( itPriority < priority ) { if( !lowestPriorityEvent || lowestPriority > itPriority ) { lowestPriorityEvent = itEvent; lowestPriority = itPriority; if( lowestPriority == AP_LOWEST ) { return lowestPriorityEvent; } } } } } return lowestPriorityEvent; } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::isPlayingLowerPriority( AudioEventRTS *event ) const { //We don't actually want to do anything to this CONST function. Remember, we're //just checking to see if there is a lower priority sound. AudioPriority priority = event->getAudioEventInfo()->m_priority; if( priority == AP_LOWEST ) { //If the event we pass in is the lowest priority, don't bother checking because //there is nothing lower priority than lowest. return false; } std::list::const_iterator it; if (!event->isPositionalAudio()) { // 2-D for ( it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { if ((*it)->m_audioEventRTS->getAudioEventInfo()->m_priority < priority) { //event->setHandleToKill((*it)->m_audioEventRTS->getPlayingHandle()); return true; } } } else { // 3-D for ( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { if ((*it)->m_audioEventRTS->getAudioEventInfo()->m_priority < priority) { //event->setHandleToKill((*it)->m_audioEventRTS->getPlayingHandle()); return true; } } } return false; } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::killLowestPrioritySoundImmediately( AudioEventRTS *event ) { //Actually, we want to kill the LOWEST PRIORITY SOUND, not the first "lower" priority //sound we find, because it could easily be AudioEventRTS *lowestPriorityEvent = findLowestPrioritySound( event ); if( lowestPriorityEvent ) { std::list::iterator it; if( event->isPositionalAudio() ) { for( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { PlayingAudio *playing = (*it); if( !playing ) { continue; } if( playing->m_audioEventRTS && playing->m_audioEventRTS == lowestPriorityEvent ) { //Release this 3D sound channel immediately because we are going to play another sound in it's place. releasePlayingAudio( playing ); m_playing3DSounds.erase( it ); return TRUE; } } } else { for( it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { PlayingAudio *playing = (*it); if( !playing ) { continue; } if( playing->m_audioEventRTS && playing->m_audioEventRTS == lowestPriorityEvent ) { //Release this 3D sound channel immediately because we are going to play another sound in it's place. releasePlayingAudio( playing ); m_playing3DSounds.erase( it ); return TRUE; } } } } return FALSE; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::adjustVolumeOfPlayingAudio(AsciiString eventName, Real newVolume) { Real pan; std::list::iterator it; PlayingAudio *playing = NULL; for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; if (playing && playing->m_audioEventRTS->getEventName() == eventName) { // Adjust it playing->m_audioEventRTS->setVolume(newVolume); Real desiredVolume = playing->m_audioEventRTS->getVolume() * playing->m_audioEventRTS->getVolumeShift(); AIL_sample_volume_pan(playing->m_sample, NULL, &pan); AIL_set_sample_volume_pan(playing->m_sample, desiredVolume, pan); } } for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; if (playing && playing->m_audioEventRTS->getEventName() == eventName) { // Adjust it playing->m_audioEventRTS->setVolume(newVolume); Real desiredVolume = playing->m_audioEventRTS->getVolume() * playing->m_audioEventRTS->getVolumeShift(); AIL_set_3D_sample_volume(playing->m_3DSample, desiredVolume); } } for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; if (playing && playing->m_audioEventRTS->getEventName() == eventName) { // Adjust it playing->m_audioEventRTS->setVolume(newVolume); Real desiredVolume = playing->m_audioEventRTS->getVolume() * playing->m_audioEventRTS->getVolumeShift(); AIL_stream_volume_pan(playing->m_stream, NULL, &pan); AIL_set_stream_volume_pan(playing->m_stream, desiredVolume, pan); } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::removePlayingAudio( AsciiString eventName ) { std::list::iterator it; PlayingAudio *playing = NULL; for( it = m_playingSounds.begin(); it != m_playingSounds.end(); ) { playing = *it; if( playing && playing->m_audioEventRTS->getEventName() == eventName ) { releasePlayingAudio( playing ); it = m_playingSounds.erase(it); } else { it++; } } for( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) { playing = *it; if( playing && playing->m_audioEventRTS->getEventName() == eventName ) { releasePlayingAudio( playing ); it = m_playing3DSounds.erase(it); } else { it++; } } for( it = m_playingStreams.begin(); it != m_playingStreams.end(); ) { playing = *it; if( playing && playing->m_audioEventRTS->getEventName() == eventName ) { releasePlayingAudio( playing ); it = m_playingStreams.erase(it); } else { it++; } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::removeAllDisabledAudio() { std::list::iterator it; PlayingAudio *playing = NULL; for( it = m_playingSounds.begin(); it != m_playingSounds.end(); ) { playing = *it; if( playing && playing->m_audioEventRTS->getVolume() == 0.0f ) { releasePlayingAudio( playing ); it = m_playingSounds.erase(it); } else { it++; } } for( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) { playing = *it; if( playing && playing->m_audioEventRTS->getVolume() == 0.0f ) { releasePlayingAudio( playing ); it = m_playing3DSounds.erase(it); } else { it++; } } for( it = m_playingStreams.begin(); it != m_playingStreams.end(); ) { playing = *it; if( playing && playing->m_audioEventRTS->getVolume() == 0.0f ) { releasePlayingAudio( playing ); it = m_playingStreams.erase(it); } else { it++; } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::processRequestList( void ) { std::list::iterator it; for (it = m_audioRequests.begin(); it != m_audioRequests.end(); /* empty */) { AudioRequest *req = (*it); if (req == NULL) { continue; } if (!shouldProcessRequestThisFrame(req)) { adjustRequest(req); ++it; continue; } if (!req->m_requiresCheckForSample || checkForSample(req)) { processRequest(req); } req->deleteInstance(); it = m_audioRequests.erase(it); } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::processPlayingList( void ) { // There are two types of processing we have to do here. // 1. Move the item to the stopped list if it has become stopped. // 2. Update the position of the audio if it is positional std::list::iterator it; PlayingAudio *playing; for (it = m_playingSounds.begin(); it != m_playingSounds.end(); /* empty */) { playing = (*it); if (!playing) { it = m_playingSounds.erase(it); continue; } if (playing->m_status == PS_Stopped) { //m_stoppedAudio.push_back(playing); releasePlayingAudio( playing ); it = m_playingSounds.erase(it); } else { if (m_volumeHasChanged) { adjustPlayingVolume(playing); } ++it; } } for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) { playing = (*it); if (!playing) { it = m_playing3DSounds.erase(it); continue; } if (playing->m_status == PS_Stopped) { //m_stoppedAudio.push_back(playing); releasePlayingAudio( playing ); it = m_playing3DSounds.erase(it); } else { if (m_volumeHasChanged) { adjustPlayingVolume(playing); } const Coord3D *pos = getCurrentPositionFromEvent(playing->m_audioEventRTS); if (pos) { if( playing->m_audioEventRTS->isDead() ) { stopAudioEvent( playing->m_audioEventRTS->getPlayingHandle() ); it++; continue; } else { Real volForConsideration = getEffectiveVolume(playing->m_audioEventRTS); volForConsideration /= (m_sound3DVolume > 0.0f ? m_soundVolume : 1.0f); Bool playAnyways = BitTest( playing->m_audioEventRTS->getAudioEventInfo()->m_type, ST_GLOBAL) || playing->m_audioEventRTS->getAudioEventInfo()->m_priority == AP_CRITICAL; if( volForConsideration < m_audioSettings->m_minVolume && !playAnyways ) { // don't want to get an additional callback for this sample AIL_register_3D_EOS_callback(playing->m_3DSample, NULL); //m_stoppedAudio.push_back(playing); releasePlayingAudio( playing ); it = m_playing3DSounds.erase(it); continue; } else { Real x = pos->x; Real y = pos->y; Real z = pos->z; AIL_set_3D_position( playing->m_3DSample, x, y, z ); } } } else { AIL_register_3D_EOS_callback(playing->m_3DSample, NULL); //m_stoppedAudio.push_back(playing); releasePlayingAudio( playing ); it = m_playing3DSounds.erase(it); continue; } ++it; } } for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ) { playing = (*it); if (!playing) { it = m_playingStreams.erase(it); continue; } if (playing->m_status == PS_Stopped) { //m_stoppedAudio.push_back(playing); releasePlayingAudio( playing ); it = m_playingStreams.erase(it); } else { if (m_volumeHasChanged) { adjustPlayingVolume(playing); } ++it; } } if (m_volumeHasChanged) { m_volumeHasChanged = false; } } //Patch for a rare bug (only on about 5% of in-studio machines suffer, and not all the time) . //The actual mechanics of this problem are still elusive as of the date of this comment. 8/21/03 //but the cause is clear. Some cinematics do a radical change in the microphone position, which //calls for a radical 3DSoundVolume adjustment. If this happens while a stereo stream is *ENDING*, //low-level code gets caught in a tight loop. (Hangs) on some machines. //To prevent this condition, we just suppress the updating of 3DSoundVolume while one of these //is on the list. Since the music tracks play continuously, they never *END* during these cinematics. //so we filter them out as, *NOT SENSITIVE*... we do want to update 3DSoundVolume during music, //which is almost all of the time. Bool MilesAudioManager::has3DSensitiveStreamsPlaying( void ) const { if ( m_playingStreams.empty() ) return FALSE; for ( std::list< PlayingAudio* >::const_iterator it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it ) { const PlayingAudio *playing = (*it); if ( ! playing ) continue; if ( playing->m_audioEventRTS->getAudioEventInfo()->m_soundType != AT_Music ) { return TRUE; } if ( playing->m_audioEventRTS->getEventName().startsWith("Game_") == FALSE ) { return TRUE; } } return FALSE; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::processFadingList( void ) { std::list::iterator it; PlayingAudio *playing; for (it = m_fadingAudio.begin(); it != m_fadingAudio.end(); /* emtpy */) { playing = *it; if (!playing) { continue; } if (playing->m_framesFaded >= getAudioSettings()->m_fadeAudioFrames) { playing->m_status = PS_Stopped; playing->m_requestStop = true; //m_stoppedAudio.push_back(playing); releasePlayingAudio( playing ); it = m_fadingAudio.erase(it); continue; } ++playing->m_framesFaded; Real volume = getEffectiveVolume(playing->m_audioEventRTS); volume *= (1.0f - 1.0f * playing->m_framesFaded / getAudioSettings()->m_fadeAudioFrames); switch(playing->m_type) { case PAT_Sample: { AIL_set_sample_volume_pan(playing->m_sample, volume, 0.5f); break; } case PAT_3DSample: { AIL_set_3D_sample_volume(playing->m_3DSample, volume); break; } case PAT_Stream: { AIL_set_stream_volume_pan(playing->m_stream, volume, 0.5f); break; } } ++it; } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::processStoppedList( void ) { std::list::iterator it; PlayingAudio *playing; for (it = m_stoppedAudio.begin(); it != m_stoppedAudio.end(); /* emtpy */) { playing = *it; if (playing) { releasePlayingAudio(playing); } it = m_stoppedAudio.erase(it); } } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::shouldProcessRequestThisFrame( AudioRequest *req ) const { if (!req->m_usePendingEvent) { return true; } if (req->m_pendingEvent->getDelay() < MSEC_PER_LOGICFRAME_REAL) { return true; } return false; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::adjustRequest( AudioRequest *req ) { if (!req->m_usePendingEvent) { return; } req->m_pendingEvent->decrementDelay(MSEC_PER_LOGICFRAME_REAL); req->m_requiresCheckForSample = true; } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::checkForSample( AudioRequest *req ) { if (!req->m_usePendingEvent) { return true; } if ( req->m_pendingEvent->getAudioEventInfo() == NULL ) { // Fill in event info getInfoForAudioEvent( req->m_pendingEvent ); } if (req->m_pendingEvent->getAudioEventInfo()->m_type != AT_SoundEffect) { return true; } return m_sound->canPlayNow(req->m_pendingEvent); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::setHardwareAccelerated(Bool accel) { // Extends Bool retEarly = (accel == m_hardwareAccel); AudioManager::setHardwareAccelerated(accel); if (retEarly) { return; } if (m_hardwareAccel) { for (Int i = 0; i < MAX_HW_PROVIDERS; ++i) { UnsignedInt providerNdx = TheAudio->getProviderIndex(TheAudio->getAudioSettings()->m_preferred3DProvider[i]); TheAudio->selectProvider(providerNdx); if (getSelectedProvider() == providerNdx) { return; } } } // set it false AudioManager::setHardwareAccelerated(FALSE); UnsignedInt providerNdx = TheAudio->getProviderIndex(TheAudio->getAudioSettings()->m_preferred3DProvider[MAX_HW_PROVIDERS]); TheAudio->selectProvider(providerNdx); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::setSpeakerSurround(Bool surround) { // Extends Bool retEarly = (surround == m_surroundSpeakers); AudioManager::setSpeakerSurround(surround); if (retEarly) { return; } UnsignedInt speakerType; if (m_surroundSpeakers) { speakerType = TheAudio->getAudioSettings()->m_defaultSpeakerType3D; } else { speakerType = TheAudio->getAudioSettings()->m_defaultSpeakerType2D; } TheAudio->setSpeakerType(speakerType); } //------------------------------------------------------------------------------------------------- Real MilesAudioManager::getFileLengthMS( AsciiString strToLoad ) const { if (strToLoad.isEmpty()) { return 0.0f; } // Load it as a stream to get the file info without actually opening the file. HSTREAM stream = AIL_open_stream(m_digitalHandle, strToLoad.str(), 0); if (!stream) { return 0.0f; } long retVal; AIL_stream_ms_position(stream, &retVal, NULL); // Now close the stream AIL_close_stream(stream); return INT_TO_REAL(retVal); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::closeAnySamplesUsingFile( const void *fileToClose ) { std::list::iterator it; PlayingAudio *playing; for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ) { playing = *it; if (!playing) { continue; } if (playing->m_file == fileToClose) { releasePlayingAudio(playing); it = m_playingSounds.erase(it); } else { ++it; } } for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) { playing = *it; if (!playing) { continue; } if (playing->m_file == fileToClose) { releasePlayingAudio(playing); it = m_playing3DSounds.erase(it); } else { ++it; } } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::setDeviceListenerPosition( void ) { if (m_listener) { AIL_set_3D_orientation(m_listener, m_listenerOrientation.x, m_listenerOrientation.y, m_listenerOrientation.z, 0, 0, -1); Real x = m_listenerPosition.x; Real y = m_listenerPosition.y; Real z = m_listenerPosition.z; AIL_set_3D_position( m_listener, x, y, z ); } } //------------------------------------------------------------------------------------------------- const Coord3D *MilesAudioManager::getCurrentPositionFromEvent( AudioEventRTS *event ) { if (!event->isPositionalAudio()) { return NULL; } return event->getCurrentPosition(); } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::isOnScreen( const Coord3D *pos ) const { static ICoord2D dummy; // WorldToScreen will return True if the point is onscreen and false if it is offscreen. return TheTacticalView->worldToScreen(pos, &dummy); } //------------------------------------------------------------------------------------------------- Real MilesAudioManager::getEffectiveVolume(AudioEventRTS *event) const { Real volume = 1.0f; volume *= (event->getVolume() * event->getVolumeShift()); if (event->getAudioEventInfo()->m_soundType == AT_Music) { volume *= m_musicVolume; } else if (event->getAudioEventInfo()->m_soundType == AT_Streaming) { volume *= m_speechVolume; } else { if (event->isPositionalAudio()) { volume *= m_sound3DVolume; Coord3D distance = m_listenerPosition; const Coord3D *pos = event->getCurrentPosition(); if (pos) { distance.sub(pos); Real objMinDistance; Real objMaxDistance; if (event->getAudioEventInfo()->m_type & ST_GLOBAL) { objMinDistance = TheAudio->getAudioSettings()->m_globalMinRange; objMaxDistance = TheAudio->getAudioSettings()->m_globalMaxRange; } else { objMinDistance = event->getAudioEventInfo()->m_minDistance; objMaxDistance = event->getAudioEventInfo()->m_maxDistance; } Real objDistance = distance.length(); if( objDistance > objMinDistance ) { volume *= 1 / (objDistance / objMinDistance); } if( objDistance >= objMaxDistance ) { volume = 0.0f; } //else if( objDistance > objMinDistance ) //{ // volume *= 1.0f - (objDistance - objMinDistance) / (objMaxDistance - objMinDistance); //} } } else { volume *= m_soundVolume; } } return volume; } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::startNextLoop( PlayingAudio *looping ) { closeFile(looping->m_file); looping->m_file = NULL; if (looping->m_requestStop) { return false; } if (looping->m_audioEventRTS->hasMoreLoops()) { // generate a new filename, and test to see whether we can play with it now looping->m_audioEventRTS->generateFilename(); if (looping->m_audioEventRTS->getDelay() > MSEC_PER_LOGICFRAME_REAL) { // fake it out so that this sound appears done, but also so that it will not // delete the sound on completion (which would suck) looping->m_cleanupAudioEventRTS = false; looping->m_requestStop = true; looping->m_status = PS_Stopped; AudioRequest *req = allocateAudioRequest(true); req->m_pendingEvent = looping->m_audioEventRTS; req->m_requiresCheckForSample = true; appendAudioRequest(req); return true; } if (looping->m_type == PAT_3DSample) { looping->m_file = playSample3D(looping->m_audioEventRTS, looping->m_3DSample); } else { looping->m_file = playSample(looping->m_audioEventRTS, looping->m_sample); } return looping->m_file != NULL; } return false; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::playStream( AudioEventRTS *event, HSTREAM stream ) { // Force it to the beginning if (event->getAudioEventInfo()->m_soundType == AT_Music) { AIL_set_stream_loop_count(stream, INFINITE_LOOP_COUNT); } AIL_register_stream_callback(stream, setStreamCompleted); AIL_start_stream(stream); if (event->getAudioEventInfo()->m_soundType == AT_Music) { // Need to stop/fade out the old music here. } } //------------------------------------------------------------------------------------------------- void *MilesAudioManager::playSample( AudioEventRTS *event, HSAMPLE sample ) { AIL_init_sample(sample); // Prep any sort of filtering, etc, here AIL_register_EOS_callback(sample, setSampleCompleted); initFilters(sample, event); // Load the file in void *fileBuffer = NULL; fileBuffer = loadFileForRead(event); if (fileBuffer) { AIL_set_sample_file(sample, fileBuffer, 0); // Start playback AIL_start_sample(sample); } return fileBuffer; } //------------------------------------------------------------------------------------------------- void *MilesAudioManager::playSample3D( AudioEventRTS *event, H3DSAMPLE sample3D ) { const Coord3D *pos = getCurrentPositionFromEvent(event); if (pos) { // Load the file in void *fileBuffer = loadFileForRead(event); if (fileBuffer) { AIL_set_3D_sample_file(sample3D, fileBuffer); // Prep any sort of filtering, etc, here AIL_register_3D_EOS_callback(sample3D, set3DSampleCompleted); // Set the position values of the sample here if (event->getAudioEventInfo()->m_type & ST_GLOBAL) { AIL_set_3D_sample_distances(sample3D, TheAudio->getAudioSettings()->m_globalMinRange, TheAudio->getAudioSettings()->m_globalMaxRange ); } else { AIL_set_3D_sample_distances(sample3D, event->getAudioEventInfo()->m_minDistance, event->getAudioEventInfo()->m_maxDistance ); } // Set the position of the sample here Real x = pos->x; Real y = pos->y; Real z = pos->z; AIL_set_3D_position( sample3D, x, y, z ); initFilters3D(sample3D, event, pos); // Start playback AIL_start_3D_sample(sample3D); } return fileBuffer; } return NULL; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::buildProviderList( void ) { HPROENUM next = HPROENUM_FIRST; char *name; UnsignedInt index = 0; while (index < MAXPROVIDERS && AIL_enumerate_3D_providers(&next, &m_provider3D[index].id, &name)) { m_provider3D[index].name.set(name); // set it to the AsciiString ++index; } m_providerCount = index; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::createListener( void ) { if (!(isOn(AudioAffect_Sound3D) && isValidProvider())) { return; } m_listener = AIL_open_3D_listener(m_provider3D[m_selectedProvider].id); // initial listener position will be (0, 0, 0) } //------------------------------------------------------------------------------------------------- void MilesAudioManager::initDelayFilter( void ) { if (m_delayFilter != NULL) { return; } char* filterName; HPROENUM enumFLTs = HPROENUM_FIRST; HPROVIDER currentProvider; while (AIL_enumerate_filters(&enumFLTs, ¤tProvider, &filterName )) { if (strcmp(filterName,"Mono Delay Filter") == 0) { m_delayFilter = currentProvider; break; } } } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::isValidProvider( void ) { return (m_selectedProvider < m_providerCount); } //------------------------------------------------------------------------------------------------- void MilesAudioManager::initSamplePools( void ) { if (!(isOn(AudioAffect_Sound3D) && isValidProvider())) { return; } int i = 0; for (i = 0; i < getAudioSettings()->m_sampleCount2D; ++i) { HSAMPLE sample = AIL_allocate_sample_handle(m_digitalHandle); DEBUG_ASSERTCRASH(sample, ("Couldn't get %d 2D samples\n", i + 1)); if (sample) { AIL_init_sample(sample); AIL_set_sample_user_data(sample, 0, i + 1); m_availableSamples.push_back(sample); ++m_num2DSamples; } } for (i = 0; i < getAudioSettings()->m_sampleCount3D; ++i) { H3DSAMPLE sample = AIL_allocate_3D_sample_handle(m_provider3D[m_selectedProvider].id); DEBUG_ASSERTCRASH(sample, ("Couldn't get %d 3D samples\n", i + 1)); if (sample) { AIL_set_3D_user_data(sample, 0, i + 1); m_available3DSamples.push_back(sample); ++m_num3DSamples; } } // Streams are basically free, so we can just allocate the appropriate number m_numStreams = getAudioSettings()->m_streamCount; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::processRequest( AudioRequest *req ) { switch (req->m_request) { case AR_Play: { playAudioEvent(req->m_pendingEvent); break; } case AR_Pause: { pauseAudioEvent(req->m_handleToInteractOn); break; } case AR_Stop: { stopAudioEvent(req->m_handleToInteractOn); break; } } } //------------------------------------------------------------------------------------------------- void *MilesAudioManager::getHandleForBink( void ) { if (m_binkHandle == NULL) { PlayingAudio *aud = allocatePlayingAudio(); aud->m_audioEventRTS = NEW AudioEventRTS("BinkHandle"); // poolify getInfoForAudioEvent(aud->m_audioEventRTS); aud->m_sample = getFirst2DSample(aud->m_audioEventRTS); aud->m_type = PAT_Sample; if (!aud->m_sample) { releasePlayingAudio(aud); return NULL; } m_binkHandle = aud; } AILLPDIRECTSOUND lpDS; AIL_get_DirectSound_info(m_binkHandle->m_sample, &lpDS, NULL); return lpDS; } //------------------------------------------------------------------------------------------------- void MilesAudioManager::releaseHandleForBink( void ) { if (m_binkHandle) { releasePlayingAudio(m_binkHandle); m_binkHandle = NULL; } } //------------------------------------------------------------------------------------------------- void MilesAudioManager::friend_forcePlayAudioEventRTS(const AudioEventRTS* eventToPlay) { if (!eventToPlay->getAudioEventInfo()) { getInfoForAudioEvent(eventToPlay); if (!eventToPlay->getAudioEventInfo()) { DEBUG_CRASH(("No info for forced audio event '%s'\n", eventToPlay->getEventName().str())); return; } } switch (eventToPlay->getAudioEventInfo()->m_soundType) { case AT_Music: if (!isOn(AudioAffect_Music)) return; break; case AT_SoundEffect: if (!isOn(AudioAffect_Sound) || !isOn(AudioAffect_Sound3D)) return; break; case AT_Streaming: if (!isOn(AudioAffect_Speech)) return; break; } AudioEventRTS event = *eventToPlay; event.generateFilename(); event.generatePlayInfo(); std::list >::iterator it; for (it = m_adjustedVolumes.begin(); it != m_adjustedVolumes.end(); ++it) { if (it->first == event.getEventName()) { event.setVolume(it->second); break; } } AsciiString fileToPlay = event.getFilename(); HAUDIO haud = AIL_quick_load_and_play(fileToPlay.str(), 1, 0); // Even though the event type is not Speech, this is used only for mission briefings, so use the // speech slider to adjust the volume. // Get the volume from the event, and pass 0.5 to play the audio in the middle. (0.0 is full left, 1.0 is full right) AIL_quick_set_volume(haud, event.getVolume() * getVolume(AudioAffect_Speech), 0.5); m_audioForcePlayed.push_back(haud); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void AILCALLBACK setSampleCompleted( HSAMPLE sampleCompleted ) { TheAudio->notifyOfAudioCompletion((UnsignedInt) sampleCompleted, PAT_Sample); } //------------------------------------------------------------------------------------------------- void AILCALLBACK set3DSampleCompleted( H3DSAMPLE sample3DCompleted ) { TheAudio->notifyOfAudioCompletion((UnsignedInt) sample3DCompleted, PAT_3DSample); } //------------------------------------------------------------------------------------------------- void AILCALLBACK setStreamCompleted( HSTREAM streamCompleted ) { TheAudio->notifyOfAudioCompletion((UnsignedInt) streamCompleted, PAT_Stream); } //------------------------------------------------------------------------------------------------- U32 AILCALLBACK streamingFileOpen(char const *fileName, U32 *file_handle) { #if defined(_DEBUG) || defined(_INTERNAL) if (sizeof(U32) != sizeof(File*)) { RELEASE_CRASH(("streamingFileOpen - This function requires work in order to compile on non 32-bit platforms.\n")); } #endif (*file_handle) = (U32) TheFileSystem->openFile(fileName, File::READ | File::STREAMING); return ((*file_handle) != 0); } //------------------------------------------------------------------------------------------------- void AILCALLBACK streamingFileClose(U32 fileHandle) { ((File*) fileHandle)->close(); } //------------------------------------------------------------------------------------------------- S32 AILCALLBACK streamingFileSeek(U32 fileHandle, S32 offset, U32 type) { return ((File*) fileHandle)->seek(offset, (File::seekMode) type); } //------------------------------------------------------------------------------------------------- U32 AILCALLBACK streamingFileRead(U32 file_handle, void *buffer, U32 bytes) { return ((File*) file_handle)->read(buffer, bytes); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- AudioFileCache::AudioFileCache() : m_maxSize(0), m_currentlyUsedSize(0), m_mutexName("AudioFileCacheMutex") { m_mutex = CreateMutex(NULL, FALSE, m_mutexName); } //------------------------------------------------------------------------------------------------- AudioFileCache::~AudioFileCache() { { ScopedMutex mut(m_mutex); // Free all the samples that are open. OpenFilesHashIt it; for ( it = m_openFiles.begin(); it != m_openFiles.end(); ++it ) { if (it->second.m_openCount > 0) { DEBUG_CRASH(("Sample '%s' is still playing, and we're trying to quit.\n", it->second.m_eventInfo->m_audioName.str())); } releaseOpenAudioFile(&it->second); // Don't erase it from the map, cause it makes this whole process way more complicated, and // we're about to go away anyways. } } CloseHandle(m_mutex); } //------------------------------------------------------------------------------------------------- void *AudioFileCache::openFile( AudioEventRTS *eventToOpenFrom ) { // Protect the entire openFile function ScopedMutex mut(m_mutex); AsciiString strToFind; switch (eventToOpenFrom->getNextPlayPortion()) { case PP_Attack: strToFind = eventToOpenFrom->getAttackFilename(); break; case PP_Sound: strToFind = eventToOpenFrom->getFilename(); break; case PP_Decay: strToFind = eventToOpenFrom->getDecayFilename(); break; case PP_Done: return NULL; } OpenFilesHash::iterator it; it = m_openFiles.find(strToFind); if (it != m_openFiles.end()) { ++it->second.m_openCount; return it->second.m_file; } // Couldn't find the file, so actually open it. File *file = TheFileSystem->openFile(strToFind.str()); if (!file) { DEBUG_ASSERTLOG(strToFind.isEmpty(), ("Missing Audio File: '%s'\n", strToFind.str())); return NULL; } UnsignedInt fileSize = file->size(); char* buffer = file->readEntireAndClose(); OpenAudioFile openedAudioFile; openedAudioFile.m_eventInfo = eventToOpenFrom->getAudioEventInfo(); AILSOUNDINFO soundInfo; AIL_WAV_info(buffer, &soundInfo); if (eventToOpenFrom->isPositionalAudio()) { if (soundInfo.channels > 1) { DEBUG_CRASH(("Requested Positional Play of audio '%s', but it is in stereo.", strToFind.str())); delete [] buffer; return NULL; } } if (soundInfo.format == WAVE_FORMAT_IMA_ADPCM) { void *decompressFileBuffer; U32 newFileSize; AIL_decompress_ADPCM(&soundInfo, &decompressFileBuffer, &newFileSize); fileSize = newFileSize; openedAudioFile.m_compressed = TRUE; delete [] buffer; openedAudioFile.m_file = decompressFileBuffer; openedAudioFile.m_soundInfo = soundInfo; openedAudioFile.m_openCount = 1; } else if (soundInfo.format == WAVE_FORMAT_PCM) { openedAudioFile.m_compressed = FALSE; openedAudioFile.m_file = buffer; openedAudioFile.m_soundInfo = soundInfo; openedAudioFile.m_openCount = 1; } else { DEBUG_CRASH(("Unexpected compression type in '%s'\n", strToFind.str())); // prevent leaks delete [] buffer; return NULL; } openedAudioFile.m_fileSize = fileSize; m_currentlyUsedSize += openedAudioFile.m_fileSize; if (m_currentlyUsedSize > m_maxSize) { // We need to free some samples, or we're not going to be able to play this sound. if (!freeEnoughSpaceForSample(openedAudioFile)) { m_currentlyUsedSize -= openedAudioFile.m_fileSize; releaseOpenAudioFile(&openedAudioFile); return NULL; } } m_openFiles[strToFind] = openedAudioFile; return openedAudioFile.m_file; } //------------------------------------------------------------------------------------------------- void AudioFileCache::closeFile( void *fileToClose ) { if (!fileToClose) { return; } // Protect the entire closeFile function ScopedMutex mut(m_mutex); OpenFilesHash::iterator it; for ( it = m_openFiles.begin(); it != m_openFiles.end(); ++it ) { if ( it->second.m_file == fileToClose ) { --it->second.m_openCount; return; } } } //------------------------------------------------------------------------------------------------- void AudioFileCache::setMaxSize( UnsignedInt size ) { // Protect the function, in case we're trying to use this value elsewhere. ScopedMutex mut(m_mutex); m_maxSize = size; } //------------------------------------------------------------------------------------------------- void AudioFileCache::releaseOpenAudioFile( OpenAudioFile *fileToRelease ) { if (fileToRelease->m_openCount > 0) { // This thing needs to be terminated IMMEDIATELY. TheAudio->closeAnySamplesUsingFile(fileToRelease->m_file); } if (fileToRelease->m_file) { if (fileToRelease->m_compressed) { // Files read in via AIL_decompress_ADPCM must be freed with AIL_mem_free_lock. AIL_mem_free_lock(fileToRelease->m_file); } else { // Otherwise, we read it, we own it, blow it away. delete [] fileToRelease->m_file; } fileToRelease->m_file = NULL; fileToRelease->m_eventInfo = NULL; } } //------------------------------------------------------------------------------------------------- Bool AudioFileCache::freeEnoughSpaceForSample(const OpenAudioFile& sampleThatNeedsSpace) { Int spaceRequired = m_currentlyUsedSize - m_maxSize; Int runningTotal = 0; std::list filesToClose; // First, search for any samples that have ref counts of 0. They are low-hanging fruit, and // should be considered immediately. OpenFilesHashIt it; for (it = m_openFiles.begin(); it != m_openFiles.end(); ++it) { if (it->second.m_openCount == 0) { // This is said low-hanging fruit. filesToClose.push_back(it->first); runningTotal += it->second.m_fileSize; if (runningTotal >= spaceRequired) { break; } } } // If we don't have enough space yet, then search through the events who have a count of 1 or more // and who are lower priority than this sound. // Mical said that at this point, sounds shouldn't care if other sounds are interruptable or not. // Kill any files of lower priority necessary to clear our the buffer. if (runningTotal < spaceRequired) { for (it = m_openFiles.begin(); it != m_openFiles.end(); ++it) { if (it->second.m_openCount > 0) { if (it->second.m_eventInfo->m_priority < sampleThatNeedsSpace.m_eventInfo->m_priority) { filesToClose.push_back(it->first); runningTotal += it->second.m_fileSize; if (runningTotal >= spaceRequired) { break; } } } } } // We weren't able to find enough sounds to truncate. Therefore, this sound is not going to play. if (runningTotal < spaceRequired) { return FALSE; } std::list::iterator ait; for (ait = filesToClose.begin(); ait != filesToClose.end(); ++ait) { OpenFilesHashIt itToErase = m_openFiles.find(*ait); if (itToErase != m_openFiles.end()) { releaseOpenAudioFile(&itToErase->second); m_currentlyUsedSize -= itToErase->second.m_fileSize; m_openFiles.erase(itToErase); } } return TRUE; } #if defined(_DEBUG) || defined(_INTERNAL) //------------------------------------------------------------------------------------------------- void MilesAudioManager::dumpAllAssetsUsed() { if (!TheGlobalData->m_preloadReport) { return; } // Dump all the audio assets we've used. FILE *logfile=fopen("PreloadedAssets.txt","a+"); //append to log if (!logfile) return; std::list missingEvents; std::list usedFiles; std::list::iterator lit; fprintf(logfile, "\nAudio Asset Report - BEGIN\n"); { SetAsciiStringIt it; std::vector::iterator asIt; for (it = m_allEventsLoaded.begin(); it != m_allEventsLoaded.end(); ++it) { AsciiString astr = *it; AudioEventInfo *aei = findAudioEventInfo(astr); if (!aei) { missingEvents.push_back(astr); continue; } for (asIt = aei->m_attackSounds.begin(); asIt != aei->m_attackSounds.end(); ++asIt) { usedFiles.push_back(*asIt); } for (asIt = aei->m_sounds.begin(); asIt != aei->m_sounds.end(); ++asIt) { usedFiles.push_back(*asIt); } for (asIt = aei->m_decaySounds.begin(); asIt != aei->m_decaySounds.end(); ++asIt) { usedFiles.push_back(*asIt); } if (!aei->m_filename.isEmpty()) { usedFiles.push_back(aei->m_filename); } } fprintf(logfile, "\nEvents Requested that are missing information - BEGIN\n"); for (lit = missingEvents.begin(); lit != missingEvents.end(); ++lit) { fprintf(logfile, "%s\n", (*lit).str()); } fprintf(logfile, "\nEvents Requested that are missing information - END\n"); fprintf(logfile, "\nFiles Used - BEGIN\n"); for (lit = usedFiles.begin(); lit != usedFiles.end(); ++lit) { fprintf(logfile, "%s\n", (*lit).str()); } fprintf(logfile, "\nFiles Used - END\n"); } fprintf(logfile, "\nAudio Asset Report - END\n"); fclose(logfile); logfile = NULL; } #endif