diff --git a/Core/GameEngine/Include/Common/GameAudio.h b/Core/GameEngine/Include/Common/GameAudio.h index dae9d7cd132..60cb8e214ae 100644 --- a/Core/GameEngine/Include/Common/GameAudio.h +++ b/Core/GameEngine/Include/Common/GameAudio.h @@ -177,11 +177,10 @@ class AudioManager : public SubsystemInterface AsciiString prevTrackName(const AsciiString& currentTrack ); // changing music tracks - virtual void nextMusicTrack() = 0; - virtual void prevMusicTrack() = 0; + virtual AsciiString nextMusicTrack() = 0; + virtual AsciiString prevMusicTrack() = 0; virtual Bool isMusicPlaying() const = 0; virtual Bool hasMusicTrackCompleted( const AsciiString& trackName, Int numberOfTimes ) const = 0; - virtual AsciiString getMusicTrackName() const = 0; virtual void setAudioEventEnabled( AsciiString eventToAffect, Bool enable ); virtual void setAudioEventVolumeOverride( AsciiString eventToAffect, Real newVolume ); diff --git a/Core/GameEngineDevice/Include/MilesAudioDevice/MilesAudioManager.h b/Core/GameEngineDevice/Include/MilesAudioDevice/MilesAudioManager.h index 71707bd3054..3b80d972f96 100644 --- a/Core/GameEngineDevice/Include/MilesAudioDevice/MilesAudioManager.h +++ b/Core/GameEngineDevice/Include/MilesAudioDevice/MilesAudioManager.h @@ -23,6 +23,7 @@ #include "Common/AsciiString.h" #include "Common/GameAudio.h" #include "mss/mss.h" +#include "mutex.h" class AudioEventRTS; @@ -39,8 +40,8 @@ enum PlayingAudioType CPP_11(: Int) enum PlayingStatus CPP_11(: Int) { PS_Playing, - PS_Stopped, - PS_Paused + PS_Stopping, ///< Is about to be stopped + PS_Stopped, ///< Is about to be released }; enum PlayingWhich CPP_11(: Int) @@ -60,22 +61,26 @@ struct PlayingAudio HSTREAM m_stream; }; - PlayingAudioType m_type; - volatile PlayingStatus m_status; // This member is adjusted by another running thread. AudioEventRTS *m_audioEventRTS; - void *m_file; // The file that was opened to play this - Bool m_requestStop; + void *m_file; // The file that was opened to play this + PlayingAudioType m_type; + volatile PlayingStatus m_status; // This member is adjusted by another running thread. + Short m_framesFaded; + Bool m_fade; Bool m_cleanupAudioEventRTS; - Int m_framesFaded; - - PlayingAudio() : - m_type(PAT_INVALID), - m_audioEventRTS(nullptr), - m_requestStop(false), - m_cleanupAudioEventRTS(true), - m_sample(nullptr), - m_framesFaded(0) - { } + + PlayingAudio() + : m_sample(nullptr) + , m_audioEventRTS(nullptr) + , m_file(nullptr) + , m_type(PAT_INVALID) + , m_status(PS_Playing) + , m_framesFaded(0) + , m_fade(false) + , m_cleanupAudioEventRTS(true) + {} + + static_assert(sizeof(m_status) == sizeof(long), "Must be size of long, because it is used with Interlocked functions"); }; struct ProviderInfo @@ -114,7 +119,7 @@ class AudioFileCache // End Protected by mutex // Note: These functions should be used for informational purposes only. For speed reasons, - // they are not protected by the mutex, so they are not guarenteed to be valid if called from + // they are not protected by the mutex, so they are not guaranteed to be valid if called from // outside the audio cache. They should be used as a rough estimate only. UnsignedInt getCurrentlyUsedSize() const { return m_currentlyUsedSize; } UnsignedInt getMaxSize() const { return m_maxSize; } @@ -150,12 +155,10 @@ class MilesAudioManager : public AudioManager MilesAudioManager(); virtual ~MilesAudioManager() override; - - virtual void nextMusicTrack() override; - virtual void prevMusicTrack() override; + virtual AsciiString nextMusicTrack() override; + virtual AsciiString prevMusicTrack() override; virtual Bool isMusicPlaying() const override; virtual Bool hasMusicTrackCompleted( const AsciiString& trackName, Int numberOfTimes ) const override; - virtual AsciiString getMusicTrackName() const override; virtual void openDevice() override; virtual void closeDevice() override; @@ -172,8 +175,8 @@ class MilesAudioManager : public AudioManager ///< NOTE NOTE NOTE !!DO NOT USE THIS IN FOR GAMELOGIC PURPOSES!! NOTE NOTE NOTE virtual Bool isCurrentlyPlaying( AudioHandle handle ) override; - virtual void notifyOfAudioCompletion( UnsignedInt audioCompleted, UnsignedInt flags ) override; - virtual PlayingAudio *findPlayingAudioFrom( UnsignedInt audioCompleted, UnsignedInt flags ); + virtual void notifyOfAudioCompletion( UnsignedInt handle, UnsignedInt flags ) override; + virtual PlayingAudio *findPlayingAudioFrom( UnsignedInt handle, UnsignedInt flags ); virtual UnsignedInt getProviderCount() const override; virtual AsciiString getProviderName( UnsignedInt providerNum ) const override; @@ -208,7 +211,6 @@ class MilesAudioManager : public AudioManager virtual void processRequestList() override; virtual void processPlayingList(); virtual void processFadingList(); - virtual void processStoppedList(); Bool shouldProcessRequestThisFrame( AudioRequest *req ) const; void adjustRequest( AudioRequest *req ); @@ -243,7 +245,6 @@ class MilesAudioManager : public AudioManager void *playSample( AudioEventRTS *event, HSAMPLE sample ); void *playSample3D( AudioEventRTS *event, H3DSAMPLE sample3D ); - protected: void buildProviderList(); void createListener(); void initDelayFilter(); @@ -261,6 +262,11 @@ class MilesAudioManager : public AudioManager PlayingAudio *allocatePlayingAudio(); void releaseMilesHandles( PlayingAudio *release ); void releasePlayingAudio( PlayingAudio *release ); + void stopPlayingAudio( PlayingAudio *release ); + void fadePlayingAudio( PlayingAudio *playing ); + + PlayingAudio *findActiveMusic( const AsciiString *trackName = nullptr ); + const PlayingAudio *findActiveMusic( const AsciiString* trackName = nullptr ) const; void stopAllAudioImmediately(); void freeAllMilesHandles(); @@ -272,7 +278,6 @@ class MilesAudioManager : public AudioManager void stopAllSpeech(); - protected: void initFilters( HSAMPLE sample, AudioEventRTS *eventInfo ); void initFilters3D( H3DSAMPLE sample, AudioEventRTS *eventInfo, const Coord3D *pos ); @@ -298,22 +303,19 @@ class MilesAudioManager : public AudioManager std::list m_availableSamples; std::list m_available3DSamples; - // Currently Playing stuff. Useful if we have to preempt it. + // Currently Playing audio. Useful if we have to preempt it. // This should rarely if ever happen, as we mirror this in Sounds, and attempt to // keep preemption from taking place here. std::list m_playingSounds; std::list m_playing3DSounds; std::list m_playingStreams; - // Currently fading stuff. At this point, we just want to let it finish fading, when it is - // done it should be added to the completed list, then "freed" and the counts should be updated - // on the next update + // Currently fading music. We just let it finish fading, then release it. std::list m_fadingAudio; - // Stuff that is done playing (either because it has finished or because it was killed) - // This stuff should be cleaned up during the next update cycle. This includes updating counts - // in the sound engine - std::list m_stoppedAudio; + // TheSuperHackers @fix Erasing from PlayingAudio lists on main thread is not safe when when the MSS Timer thread + // needs to iterate the same lists. This critical section is used to guard writes that will invalidate iterators. + CriticalSectionClass m_playingCs; AudioFileCache *m_audioCache; PlayingAudio *m_binkHandle; @@ -343,17 +345,16 @@ class MilesAudioManagerDummy : public MilesAudioManager virtual void resumeAudio(AudioAffect which) override {} virtual void pauseAmbient(Bool shouldPause) override {} virtual void killAudioEventImmediately(AudioHandle audioEvent) override {} - virtual void nextMusicTrack() override {} - virtual void prevMusicTrack() override {} + virtual AsciiString nextMusicTrack() override { return AsciiString::TheEmptyString; } + virtual AsciiString prevMusicTrack() override { return AsciiString::TheEmptyString; } virtual Bool isMusicPlaying() const override { return false; } virtual Bool hasMusicTrackCompleted(const AsciiString& trackName, Int numberOfTimes) const override { return false; } - virtual AsciiString getMusicTrackName() const override { return ""; } //virtual void openDevice() override {} //virtual void closeDevice() override {} //virtual void* getDevice() override { return nullptr; } virtual void notifyOfAudioCompletion(UnsignedInt audioCompleted, UnsignedInt flags) override {} virtual UnsignedInt getProviderCount() const override { return 0; }; - virtual AsciiString getProviderName(UnsignedInt providerNum) const override { return ""; } + virtual AsciiString getProviderName(UnsignedInt providerNum) const override { return AsciiString::TheEmptyString; } virtual UnsignedInt getProviderIndex(AsciiString providerName) const override { return 0; } virtual void selectProvider(UnsignedInt providerNdx) override {} virtual void unselectProvider() override {} diff --git a/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp b/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp index 3a541494959..c2ebf508759 100644 --- a/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp +++ b/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp @@ -66,6 +66,8 @@ #include "Common/file.h" +#include + enum { INFINITE_LOOP_COUNT = 1000000 }; @@ -229,10 +231,6 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL channelCount = TheAudio->getNum2DSamples(); for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; - if (!playing) { - continue; - } - playingArray[(int)AIL_sample_user_data(playing->m_sample, 0)] = playing; } @@ -262,10 +260,6 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL for( it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { playing = *it; - if( !playing ) - { - continue; - } filenameNoSlashes = playing->m_audioEventRTS->getFilename(); filenameNoSlashes = filenameNoSlashes.reverseFind( '\\' ) + 1; @@ -290,10 +284,6 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL channelCount = TheAudio->getNum3DSamples(); for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; - if (!playing) { - continue; - } - playingArray[(int)AIL_3D_user_data(playing->m_3DSample, 0)] = playing; } @@ -357,10 +347,6 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL for( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { playing = *it; - if( !playing ) - { - continue; - } filenameNoSlashes = playing->m_audioEventRTS->getFilename(); filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; @@ -384,13 +370,9 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL 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); @@ -411,10 +393,6 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL for( it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it ) { playing = *it; - if( !playing ) - { - continue; - } filenameNoSlashes = playing->m_audioEventRTS->getFilename(); filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; @@ -482,7 +460,6 @@ void MilesAudioManager::update() processRequestList(); processPlayingList(); processFadingList(); - processStoppedList(); } //------------------------------------------------------------------------------------------------- @@ -494,49 +471,36 @@ void MilesAudioManager::stopAudio( AudioAffect which ) // 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 = nullptr; if (BitIsSet(which, AudioAffect_Sound)) { for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; - if (playing) { - AIL_register_EOS_callback(playing->m_sample, nullptr); - AIL_stop_sample(playing->m_sample); - playing->m_status = PS_Stopped; - } + stopPlayingAudio(playing); } } if (BitIsSet(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, nullptr); - AIL_stop_3D_sample(playing->m_3DSample); - playing->m_status = PS_Stopped; - } + stopPlayingAudio(playing); } } if (BitIsSet(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 (!BitIsSet(which, AudioAffect_Music)) { - continue; - } - } else { - if (!BitIsSet(which, AudioAffect_Speech)) { - continue; - } + if (playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { + if (!BitIsSet(which, AudioAffect_Music)) { + continue; + } + } else { + if (!BitIsSet(which, AudioAffect_Speech)) { + continue; } - AIL_register_stream_callback(playing->m_stream, nullptr); - AIL_pause_stream(playing->m_stream, 1); - playing->m_status = PS_Stopped; } + stopPlayingAudio(playing); } } } @@ -550,46 +514,40 @@ void MilesAudioManager::pauseAudio( AudioAffect which ) if (BitIsSet(which, AudioAffect_Sound)) { for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; - if (playing) { - AIL_stop_sample(playing->m_sample); - } + AIL_stop_sample(playing->m_sample); } } if (BitIsSet(which, AudioAffect_Sound3D)) { for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; - if (playing) { - AIL_stop_3D_sample(playing->m_3DSample); - } + AIL_stop_3D_sample(playing->m_3DSample); } } if (BitIsSet(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 (!BitIsSet(which, AudioAffect_Music)) { - continue; - } - } else { - if (!BitIsSet(which, AudioAffect_Speech)) { - continue; - } + if (playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { + if (!BitIsSet(which, AudioAffect_Music)) { + continue; + } + } else { + if (!BitIsSet(which, AudioAffect_Speech)) { + continue; } - - AIL_pause_stream(playing->m_stream, 1); } + + 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 */) + for (ait = m_audioRequests.begin(); ait != m_audioRequests.end(); ) { AudioRequest *req = (*ait); - if( req && req->m_request == AR_Play ) + if( req->m_request == AR_Play ) { deleteInstance(req); ait = m_audioRequests.erase(ait); @@ -611,36 +569,30 @@ void MilesAudioManager::resumeAudio( AudioAffect which ) if (BitIsSet(which, AudioAffect_Sound)) { for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; - if (playing) { - AIL_resume_sample(playing->m_sample); - } + AIL_resume_sample(playing->m_sample); } } if (BitIsSet(which, AudioAffect_Sound3D)) { for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; - if (playing) { - AIL_resume_3D_sample(playing->m_3DSample); - } + AIL_resume_3D_sample(playing->m_3DSample); } } if (BitIsSet(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 (!BitIsSet(which, AudioAffect_Music)) { - continue; - } - } else { - if (!BitIsSet(which, AudioAffect_Speech)) { - continue; - } + if (playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Music) { + if (!BitIsSet(which, AudioAffect_Music)) { + continue; + } + } else { + if (!BitIsSet(which, AudioAffect_Speech)) { + continue; } - AIL_pause_stream(playing->m_stream, 0); } + AIL_pause_stream(playing->m_stream, 0); } } } @@ -686,15 +638,11 @@ void MilesAudioManager::playAudioEvent( AudioEventRTS *event ) 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); + stopPlayingAudio(playing); foundSoundToReplace = true; break; } @@ -742,15 +690,11 @@ void MilesAudioManager::playAudioEvent( AudioEventRTS *event ) { 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); + stopPlayingAudio(playing); foundSoundToReplace = true; break; } @@ -812,15 +756,11 @@ void MilesAudioManager::playAudioEvent( AudioEventRTS *event ) 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); + stopPlayingAudio(playing); foundSoundToReplace = true; break; } @@ -893,67 +833,51 @@ void MilesAudioManager::stopAudioEvent( AudioHandle handle ) std::list::iterator it; if ( handle == AHSV_StopTheMusic || handle == AHSV_StopTheMusicFade ) { - // for music, just find the currently playing music stream and kill it. + // for music, find and stop the currently playing music stream(s). 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); + fadePlayingAudio(audio); } else { - //m_stoppedAudio.push_back(audio); - releasePlayingAudio( audio ); + stopPlayingAudio(audio); } - m_playingStreams.erase(it); - break; } } + return; } 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); + stopPlayingAudio(audio); 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; + stopPlayingAudio(audio); 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)", audio->m_audioEventRTS->getEventName())); #endif - audio->m_requestStop = true; + stopPlayingAudio(audio); break; } } @@ -967,7 +891,7 @@ void MilesAudioManager::killAudioEventImmediately( AudioHandle audioEvent ) for( ait = m_audioRequests.begin(); ait != m_audioRequests.end(); ait++ ) { AudioRequest *req = (*ait); - if( req && req->m_request == AR_Play && req->m_handleToInteractOn == audioEvent ) + if( req->m_request == AR_Play && req->m_handleToInteractOn == audioEvent ) { deleteInstance(req); ait = m_audioRequests.erase(ait); @@ -980,15 +904,10 @@ void MilesAudioManager::killAudioEventImmediately( AudioHandle audioEvent ) 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 ); + stopPlayingAudio(audio); return; } } @@ -997,15 +916,10 @@ void MilesAudioManager::killAudioEventImmediately( AudioHandle audioEvent ) 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 ); + stopPlayingAudio(audio); return; } } @@ -1014,15 +928,10 @@ void MilesAudioManager::killAudioEventImmediately( AudioHandle audioEvent ) 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 ); + stopPlayingAudio(audio); return; } } @@ -1052,9 +961,7 @@ void MilesAudioManager::closeFile( void *fileRead ) //------------------------------------------------------------------------------------------------- PlayingAudio *MilesAudioManager::allocatePlayingAudio() { - PlayingAudio *aud = NEW PlayingAudio; // poolify - aud->m_status = PS_Playing; - return aud; + return NEW PlayingAudio; // poolify } @@ -1069,6 +976,7 @@ void MilesAudioManager::releaseMilesHandles( PlayingAudio *release ) AIL_register_EOS_callback(release->m_sample, nullptr); AIL_stop_sample(release->m_sample); m_availableSamples.push_back(release->m_sample); + release->m_sample = nullptr; } break; } @@ -1078,6 +986,7 @@ void MilesAudioManager::releaseMilesHandles( PlayingAudio *release ) AIL_register_3D_EOS_callback(release->m_3DSample, nullptr); AIL_stop_3D_sample(release->m_3DSample); m_available3DSamples.push_back(release->m_3DSample); + release->m_3DSample = nullptr; } break; } @@ -1086,6 +995,7 @@ void MilesAudioManager::releaseMilesHandles( PlayingAudio *release ) if (release->m_stream) { AIL_register_stream_callback(release->m_stream, nullptr); AIL_close_stream(release->m_stream); + release->m_stream = nullptr; } break; } @@ -1096,6 +1006,26 @@ void MilesAudioManager::releaseMilesHandles( PlayingAudio *release ) //------------------------------------------------------------------------------------------------- void MilesAudioManager::releasePlayingAudio( PlayingAudio *release ) { + stopPlayingAudio(release); + + if (release->m_file) { + closeFile(release->m_file); + release->m_file = nullptr; + } + + if (release->m_cleanupAudioEventRTS) { + releaseAudioEventRTS(release->m_audioEventRTS); + } + + delete release; +} + +//------------------------------------------------------------------------------------------------- +void MilesAudioManager::stopPlayingAudio( PlayingAudio *release ) +{ + if (release->m_status == PS_Stopped) { + return; + } if (release->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_SoundEffect) { if (release->m_type == PAT_Sample) { if (release->m_sample) { @@ -1107,60 +1037,123 @@ void MilesAudioManager::releasePlayingAudio( PlayingAudio *release ) } } } - releaseMilesHandles(release); // forces stop of this audio - closeFile( release->m_file ); - if (release->m_cleanupAudioEventRTS) { - releaseAudioEventRTS(release->m_audioEventRTS); - } - delete release; - release = nullptr; + releaseMilesHandles(release); + // Mark as stopped. + InterlockedCompareExchange(reinterpret_cast(&release->m_status), PS_Stopping, PS_Playing); + InterlockedCompareExchange(reinterpret_cast(&release->m_status), PS_Stopped, PS_Stopping); } //------------------------------------------------------------------------------------------------- -void MilesAudioManager::stopAllAudioImmediately() +void MilesAudioManager::fadePlayingAudio( PlayingAudio *playing ) { - std::list::iterator it; - PlayingAudio *playing; + playing->m_fade = true; +} - for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ) { +//------------------------------------------------------------------------------------------------- +PlayingAudio *MilesAudioManager::findActiveMusic( const AsciiString* trackName ) +{ + std::list::const_iterator it; + PlayingAudio *playing; + for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; - if (!playing) { + if (playing->m_status != PS_Playing) { + continue; + } + if (playing->m_fade) { + continue; + } + if (playing->m_audioEventRTS->getAudioEventInfo()->m_soundType != AT_Music) { continue; } + if (trackName && *trackName != playing->m_audioEventRTS->getEventName()) { + continue; + } + return playing; + } + return nullptr; +} + +//------------------------------------------------------------------------------------------------- +const PlayingAudio *MilesAudioManager::findActiveMusic( const AsciiString* trackName ) const +{ + return const_cast(this)->findActiveMusic(trackName); +} + +//------------------------------------------------------------------------------------------------- +void MilesAudioManager::stopAllAudioImmediately() +{ + std::list::iterator it; + PlayingAudio *playing; - releasePlayingAudio(playing); - it = m_playingSounds.erase(it); + // + // Stop audio without locking. Calls into Miles. + // + for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { + playing = (*it); + stopPlayingAudio(playing); } - for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) { - playing = *it; - if (!playing) { - continue; - } + for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { + playing = (*it); + stopPlayingAudio(playing); + } - releasePlayingAudio(playing); - it = m_playing3DSounds.erase(it); + for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { + playing = (*it); + stopPlayingAudio(playing); } - for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ) { + for (it = m_fadingAudio.begin(); it != m_fadingAudio.end(); ++it) { playing = (*it); - if (!playing) { - continue; + stopPlayingAudio(playing); + } + + { + // + // Release audio with locking. Must not call into Miles! + // + CriticalSectionClass::LockClass lock(m_playingCs); + + for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ) { + playing = *it; + + DEBUG_ASSERTCRASH(playing->m_status == PS_Stopped, + ("PlayingAudio must be Stopped by now, otherwise this can deadlock with MSS Timer thread")); + + releasePlayingAudio(playing); + it = m_playingSounds.erase(it); } - releasePlayingAudio(playing); - it = m_playingStreams.erase(it); - } + for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) { + playing = *it; - for (it = m_fadingAudio.begin(); it != m_fadingAudio.end(); ) { - playing = (*it); - if (!playing) { - continue; - } + DEBUG_ASSERTCRASH(playing->m_status == PS_Stopped, + ("PlayingAudio must be Stopped by now, otherwise this can deadlock with MSS Timer thread")); - releasePlayingAudio(playing); - it = m_fadingAudio.erase(it); - } + releasePlayingAudio(playing); + it = m_playing3DSounds.erase(it); + } + + for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ) { + playing = (*it); + + DEBUG_ASSERTCRASH(playing->m_status == PS_Stopped, + ("PlayingAudio must be Stopped by now, otherwise this can deadlock with MSS Timer thread")); + + releasePlayingAudio(playing); + it = m_playingStreams.erase(it); + } + + for (it = m_fadingAudio.begin(); it != m_fadingAudio.end(); ) { + playing = (*it); + + DEBUG_ASSERTCRASH(playing->m_status == PS_Stopped, + ("PlayingAudio must be Stopped by now, otherwise this can deadlock with MSS Timer thread")); + + releasePlayingAudio(playing); + it = m_fadingAudio.erase(it); + } + } std::list::iterator hit; for (hit = m_audioForcePlayed.begin(); hit != m_audioForcePlayed.end(); ++hit) { @@ -1181,7 +1174,7 @@ void MilesAudioManager::freeAllMilesHandles() // 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 */ ) { + for ( it = m_availableSamples.begin(); it != m_availableSamples.end(); ) { HSAMPLE sample = *it; AIL_release_sample_handle(sample); it = m_availableSamples.erase(it); @@ -1189,7 +1182,7 @@ void MilesAudioManager::freeAllMilesHandles() m_num2DSamples = 0; std::list::iterator it3D; - for ( it3D = m_available3DSamples.begin(); it3D != m_available3DSamples.end(); /* empty */ ) { + for ( it3D = m_available3DSamples.begin(); it3D != m_available3DSamples.end(); ) { H3DSAMPLE sample3D = *it3D; AIL_release_3D_sample_handle(sample3D); it3D = m_available3DSamples.erase(it3D); @@ -1253,17 +1246,10 @@ void MilesAudioManager::stopAllSpeech() { std::list::iterator it; PlayingAudio *playing; - for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ) { + for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = (*it); - if (!playing) { - continue; - } - if (playing->m_audioEventRTS->getAudioEventInfo()->m_soundType == AT_Streaming) { - releasePlayingAudio(playing); - it = m_playingStreams.erase(it); - } else { - ++it; + stopPlayingAudio(playing); } } @@ -1317,16 +1303,12 @@ void MilesAudioManager::initFilters3D( H3DSAMPLE sample, AudioEventRTS *event, c } //------------------------------------------------------------------------------------------------- -void MilesAudioManager::nextMusicTrack() +AsciiString MilesAudioManager::nextMusicTrack() { 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(); - } + + if (PlayingAudio *playing = findActiveMusic()) { + trackName = playing->m_audioEventRTS->getEventName(); } // Stop currently playing music @@ -1335,19 +1317,17 @@ void MilesAudioManager::nextMusicTrack() trackName = nextTrackName(trackName); AudioEventRTS newTrack(trackName); TheAudio->addAudioEvent(&newTrack); + + return trackName; } //------------------------------------------------------------------------------------------------- -void MilesAudioManager::prevMusicTrack() +AsciiString MilesAudioManager::prevMusicTrack() { 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(); - } + + if (PlayingAudio *playing = findActiveMusic()) { + trackName = playing->m_audioEventRTS->getEventName(); } // Stop currently playing music @@ -1356,73 +1336,27 @@ void MilesAudioManager::prevMusicTrack() trackName = prevTrackName(trackName); AudioEventRTS newTrack(trackName); TheAudio->addAudioEvent(&newTrack); + + return trackName; } //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::isMusicPlaying() 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; + return findActiveMusic() != nullptr; } //------------------------------------------------------------------------------------------------- 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; - } - } + if (const PlayingAudio *playing = findActiveMusic(&trackName)) { + if (INFINITE_LOOP_COUNT - AIL_stream_loop_count(playing->m_stream) >= numberOfTimes) { + return TRUE; } } - return FALSE; } -//------------------------------------------------------------------------------------------------- -AsciiString MilesAudioManager::getMusicTrackName() 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() { @@ -1478,21 +1412,21 @@ Bool MilesAudioManager::isCurrentlyPlaying( AudioHandle handle ) for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; - if (playing && playing->m_audioEventRTS->getPlayingHandle() == handle) { + if (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) { + if (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) { + if (playing->m_audioEventRTS->getPlayingHandle() == handle) { return true; } } @@ -1502,7 +1436,7 @@ Bool MilesAudioManager::isCurrentlyPlaying( AudioHandle handle ) AudioRequest *req = nullptr; for (ait = m_audioRequests.begin(); ait != m_audioRequests.end(); ++ait) { req = *ait; - if (req && req->m_usePendingEvent && req->m_pendingEvent->getPlayingHandle() == handle) { + if (req->m_usePendingEvent && req->m_pendingEvent->getPlayingHandle() == handle) { return true; } } @@ -1511,9 +1445,9 @@ Bool MilesAudioManager::isCurrentlyPlaying( AudioHandle handle ) } //------------------------------------------------------------------------------------------------- -void MilesAudioManager::notifyOfAudioCompletion( UnsignedInt audioCompleted, UnsignedInt flags ) +void MilesAudioManager::notifyOfAudioCompletion( UnsignedInt handle, UnsignedInt flags ) { - PlayingAudio *playing = findPlayingAudioFrom(audioCompleted, flags); + PlayingAudio *playing = findPlayingAudioFrom(handle, flags); if (!playing) { DEBUG_CRASH(("Audio has completed playing, but we can't seem to find it. - jkmcd")); return; @@ -1564,45 +1498,54 @@ void MilesAudioManager::notifyOfAudioCompletion( UnsignedInt audioCompleted, Uns 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 + // it will be cleaned up on the next frame update + InterlockedCompareExchange(reinterpret_cast(&playing->m_status), PS_Stopping, PS_Playing); } //------------------------------------------------------------------------------------------------- -PlayingAudio *MilesAudioManager::findPlayingAudioFrom( UnsignedInt audioCompleted, UnsignedInt flags ) +PlayingAudio *MilesAudioManager::findPlayingAudioFrom( UnsignedInt handle, UnsignedInt flags ) { std::list::iterator it; PlayingAudio *playing; if (flags == PAT_Sample) { - HSAMPLE sample = (HSAMPLE) audioCompleted; + HSAMPLE sample = (HSAMPLE) handle; + CriticalSectionClass::LockClass lock(m_playingCs); for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; - if (playing && playing->m_sample == sample) { + if (playing->m_sample == sample) { return playing; } } } if (flags == PAT_3DSample) { - H3DSAMPLE sample3D = (H3DSAMPLE) audioCompleted; + H3DSAMPLE sample3D = (H3DSAMPLE) handle; + CriticalSectionClass::LockClass lock(m_playingCs); for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; - if (playing && playing->m_3DSample == sample3D) { + if (playing->m_3DSample == sample3D) { return playing; } } } if (flags == PAT_Stream) { - HSTREAM stream = (HSTREAM) audioCompleted; + HSTREAM stream = (HSTREAM) handle; + CriticalSectionClass::LockClass lock(m_playingCs); for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; - if (playing && playing->m_stream == stream) { + if (playing->m_stream == stream) { + return playing; + } + } + for (it = m_fadingAudio.begin(); it != m_fadingAudio.end(); ++it) { + playing = *it; + if (playing->m_stream == stream) { return playing; } } @@ -1845,9 +1788,6 @@ Bool MilesAudioManager::doesViolateLimit( AudioEventRTS *event ) const std::list::const_iterator arIt; for (arIt = m_audioRequests.begin(); arIt != m_audioRequests.end(); ++arIt) { AudioRequest *req = (*arIt); - if (req == nullptr) { - continue; - } if( req->m_usePendingEvent ) { if( req->m_pendingEvent->getEventName() == event->getEventName() ) @@ -2044,16 +1984,11 @@ Bool MilesAudioManager::killLowestPrioritySoundImmediately( AudioEventRTS *event 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 ); + stopPlayingAudio(playing); return TRUE; } } @@ -2063,16 +1998,11 @@ Bool MilesAudioManager::killLowestPrioritySoundImmediately( AudioEventRTS *event 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 sound channel immediately because we are going to play another sound in it's place. - releasePlayingAudio( playing ); - m_playingSounds.erase( it ); + stopPlayingAudio(playing); return TRUE; } } @@ -2091,7 +2021,7 @@ void MilesAudioManager::adjustVolumeOfPlayingAudio(AsciiString eventName, Real n PlayingAudio *playing = nullptr; for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = *it; - if (playing && playing->m_audioEventRTS->getEventName() == eventName) { + if (playing->m_audioEventRTS->getEventName() == eventName) { // Adjust it playing->m_audioEventRTS->setVolume(newVolume); AIL_sample_volume_pan(playing->m_sample, nullptr, &pan); @@ -2101,7 +2031,7 @@ void MilesAudioManager::adjustVolumeOfPlayingAudio(AsciiString eventName, Real n for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = *it; - if (playing && playing->m_audioEventRTS->getEventName() == eventName) { + if (playing->m_audioEventRTS->getEventName() == eventName) { // Adjust it playing->m_audioEventRTS->setVolume(newVolume); AIL_set_3D_sample_volume(playing->m_3DSample, getEffectiveVolume(playing->m_audioEventRTS)); @@ -2110,7 +2040,7 @@ void MilesAudioManager::adjustVolumeOfPlayingAudio(AsciiString eventName, Real n for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { playing = *it; - if (playing && playing->m_audioEventRTS->getEventName() == eventName) { + if (playing->m_audioEventRTS->getEventName() == eventName) { // Adjust it playing->m_audioEventRTS->setVolume(newVolume); AIL_stream_volume_pan(playing->m_stream, nullptr, &pan); @@ -2126,45 +2056,30 @@ void MilesAudioManager::removePlayingAudio( AsciiString eventName ) std::list::iterator it; PlayingAudio *playing = nullptr; - for( it = m_playingSounds.begin(); it != m_playingSounds.end(); ) + for( it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { playing = *it; - if( playing && playing->m_audioEventRTS->getEventName() == eventName ) - { - releasePlayingAudio( playing ); - it = m_playingSounds.erase(it); - } - else + if( playing->m_audioEventRTS->getEventName() == eventName ) { - it++; + stopPlayingAudio(playing); } } - for( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) + for( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { playing = *it; - if( playing && playing->m_audioEventRTS->getEventName() == eventName ) + if( playing->m_audioEventRTS->getEventName() == eventName ) { - releasePlayingAudio( playing ); - it = m_playing3DSounds.erase(it); - } - else - { - it++; + stopPlayingAudio(playing); } } - for( it = m_playingStreams.begin(); it != m_playingStreams.end(); ) + for( it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it ) { playing = *it; - if( playing && playing->m_audioEventRTS->getEventName() == eventName ) - { - releasePlayingAudio( playing ); - it = m_playingStreams.erase(it); - } - else + if( playing->m_audioEventRTS->getEventName() == eventName ) { - it++; + stopPlayingAudio(playing); } } } @@ -2175,45 +2090,30 @@ void MilesAudioManager::removeAllDisabledAudio() std::list::iterator it; PlayingAudio *playing = nullptr; - for( it = m_playingSounds.begin(); it != m_playingSounds.end(); ) + for( it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { playing = *it; - if( playing && playing->m_audioEventRTS->getVolume() == 0.0f ) - { - releasePlayingAudio( playing ); - it = m_playingSounds.erase(it); - } - else + if( playing->m_audioEventRTS->getVolume() == 0.0f ) { - it++; + stopPlayingAudio(playing); } } - for( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) + for( it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { playing = *it; - if( playing && playing->m_audioEventRTS->getVolume() == 0.0f ) + if( playing->m_audioEventRTS->getVolume() == 0.0f ) { - releasePlayingAudio( playing ); - it = m_playing3DSounds.erase(it); - } - else - { - it++; + stopPlayingAudio(playing); } } - for( it = m_playingStreams.begin(); it != m_playingStreams.end(); ) + for( it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it ) { playing = *it; - if( playing && playing->m_audioEventRTS->getVolume() == 0.0f ) - { - releasePlayingAudio( playing ); - it = m_playingStreams.erase(it); - } - else + if( playing->m_audioEventRTS->getVolume() == 0.0f ) { - it++; + stopPlayingAudio(playing); } } } @@ -2222,12 +2122,8 @@ void MilesAudioManager::removeAllDisabledAudio() void MilesAudioManager::processRequestList() { std::list::iterator it; - for (it = m_audioRequests.begin(); it != m_audioRequests.end(); /* empty */) { + for (it = m_audioRequests.begin(); it != m_audioRequests.end(); ) { AudioRequest *req = (*it); - if (req == nullptr) { - continue; - } - if (!shouldProcessRequestThisFrame(req)) { adjustRequest(req); ++it; @@ -2245,126 +2141,136 @@ void MilesAudioManager::processRequestList() //------------------------------------------------------------------------------------------------- void MilesAudioManager::processPlayingList() { - // 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 */) { + // + // Stop audio without locking. Calls into Miles. + // Update the position of the audio if it is positional. + // + for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { playing = (*it); - if (!playing) - { - it = m_playingSounds.erase(it); + + if (playing->m_status == PS_Stopping) { + stopPlayingAudio(playing); continue; } - if (playing->m_status == PS_Stopped) - { - //m_stoppedAudio.push_back(playing); - releasePlayingAudio( playing ); - it = m_playingSounds.erase(it); - } - else + if (m_volumeHasChanged) { - if (m_volumeHasChanged) - { - adjustPlayingVolume(playing); - } - ++it; + adjustPlayingVolume(playing); } } - for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) - { + for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it) { playing = (*it); - if (!playing) - { - it = m_playing3DSounds.erase(it); + + if (playing->m_status == PS_Stopping) { + stopPlayingAudio(playing); continue; } - if (playing->m_status == PS_Stopped) + if (m_volumeHasChanged) { - //m_stoppedAudio.push_back(playing); - releasePlayingAudio( playing ); - it = m_playing3DSounds.erase(it); + adjustPlayingVolume(playing); } - else + + const Coord3D *pos = getCurrentPositionFromEvent(playing->m_audioEventRTS); + if (pos) { - if (m_volumeHasChanged) + if( playing->m_audioEventRTS->isDead() ) { - adjustPlayingVolume(playing); + stopAudioEvent( playing->m_audioEventRTS->getPlayingHandle() ); } - - const Coord3D *pos = getCurrentPositionFromEvent(playing->m_audioEventRTS); - if (pos) + else { - if( playing->m_audioEventRTS->isDead() ) + Real volForConsideration = getEffectiveVolume(playing->m_audioEventRTS); + volForConsideration /= (m_sound3DVolume > 0.0f ? m_soundVolume : 1.0f); + Bool playAnyways = BitIsSet( playing->m_audioEventRTS->getAudioEventInfo()->m_type, ST_GLOBAL) + || playing->m_audioEventRTS->getAudioEventInfo()->m_priority == AP_CRITICAL; + if( volForConsideration < m_audioSettings->m_minVolume && !playAnyways ) { - stopAudioEvent( playing->m_audioEventRTS->getPlayingHandle() ); - it++; - continue; + stopPlayingAudio(playing); } else { - Real volForConsideration = getEffectiveVolume(playing->m_audioEventRTS); - volForConsideration /= (m_sound3DVolume > 0.0f ? m_soundVolume : 1.0f); - Bool playAnyways = BitIsSet( 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, nullptr); - //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 ); - } + 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, nullptr); - //m_stoppedAudio.push_back(playing); - releasePlayingAudio( playing ); - it = m_playing3DSounds.erase(it); - continue; - } + } + else + { + stopPlayingAudio(playing); + } + } - ++it; + for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ++it) { + playing = (*it); + + if (playing->m_status == PS_Stopping) { + stopPlayingAudio(playing); + continue; + } + + if (m_volumeHasChanged) + { + adjustPlayingVolume(playing); } } - for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ) { + // + // Release audio with locking. Must not call into Miles! + // + CriticalSectionClass::LockClass lock(m_playingCs); + + for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ) { playing = (*it); - if (!playing) + + if (playing->m_status == PS_Stopped) { - it = m_playingStreams.erase(it); + releasePlayingAudio(playing); + it = m_playingSounds.erase(it); continue; } + ++it; + } + + for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) + { + playing = (*it); + if (playing->m_status == PS_Stopped) { - //m_stoppedAudio.push_back(playing); - releasePlayingAudio( playing ); - it = m_playingStreams.erase(it); + releasePlayingAudio(playing); + it = m_playing3DSounds.erase(it); + continue; } - else + + ++it; + } + + for (it = m_playingStreams.begin(); it != m_playingStreams.end(); ) { + playing = (*it); + + if (playing->m_status == PS_Stopped) { - if (m_volumeHasChanged) - { - adjustPlayingVolume(playing); - } + releasePlayingAudio(playing); + it = m_playingStreams.erase(it); + continue; + } - ++it; + if (playing->m_fade) + { + m_fadingAudio.push_back(playing); + it = m_playingStreams.erase(it); + continue; } + + ++it; } if (m_volumeHasChanged) { @@ -2391,9 +2297,6 @@ Bool MilesAudioManager::has3DSensitiveStreamsPlaying() const { const PlayingAudio *playing = (*it); - if ( ! playing ) - continue; - if ( playing->m_audioEventRTS->getAudioEventInfo()->m_soundType != AT_Music ) { return TRUE; @@ -2416,18 +2319,14 @@ void MilesAudioManager::processFadingList() std::list::iterator it; PlayingAudio *playing; - for (it = m_fadingAudio.begin(); it != m_fadingAudio.end(); /* emtpy */) { - playing = *it; - if (!playing) { - continue; - } + // + // Stop audio without locking. Calls into Miles. + // + for (it = m_fadingAudio.begin(); it != m_fadingAudio.end(); ++it) { + playing = (*it); 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); + stopPlayingAudio(playing); continue; } @@ -2454,29 +2353,27 @@ void MilesAudioManager::processFadingList() AIL_set_stream_volume_pan(playing->m_stream, volume, 0.5f); break; } - } - - ++it; } -} -//------------------------------------------------------------------------------------------------- -void MilesAudioManager::processStoppedList() -{ - std::list::iterator it; - PlayingAudio *playing; + // + // Release audio with locking. Must not call into Miles! + // + CriticalSectionClass::LockClass lock(m_playingCs); - for (it = m_stoppedAudio.begin(); it != m_stoppedAudio.end(); /* emtpy */) { + for (it = m_fadingAudio.begin(); it != m_fadingAudio.end(); ) { playing = *it; - if (playing) { + + if (playing->m_status == PS_Stopped) { releasePlayingAudio(playing); + it = m_fadingAudio.erase(it); + continue; } - it = m_stoppedAudio.erase(it); + + ++it; } } - //------------------------------------------------------------------------------------------------- Bool MilesAudioManager::shouldProcessRequestThisFrame( AudioRequest *req ) const { @@ -2598,31 +2495,19 @@ void MilesAudioManager::closeAnySamplesUsingFile( const void *fileToClose ) std::list::iterator it; PlayingAudio *playing; - for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ) { + for (it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it ) { playing = *it; - if (!playing) { - continue; - } if (playing->m_file == fileToClose) { - releasePlayingAudio(playing); - it = m_playingSounds.erase(it); - } else { - ++it; + stopPlayingAudio(playing); } } - for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ) { + for (it = m_playing3DSounds.begin(); it != m_playing3DSounds.end(); ++it ) { playing = *it; - if (!playing) { - continue; - } if (playing->m_file == fileToClose) { - releasePlayingAudio(playing); - it = m_playing3DSounds.erase(it); - } else { - ++it; + stopPlayingAudio(playing); } } } @@ -2732,7 +2617,7 @@ Bool MilesAudioManager::startNextLoop( PlayingAudio *looping ) closeFile(looping->m_file); looping->m_file = nullptr; - if (looping->m_requestStop) { + if (looping->m_status != PS_Playing) { return false; } @@ -2744,9 +2629,7 @@ Bool MilesAudioManager::startNextLoop( PlayingAudio *looping ) // 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; - + InterlockedCompareExchange(reinterpret_cast(&looping->m_status), PS_Stopping, PS_Playing); AudioRequest *req = allocateAudioRequest(true); req->m_pendingEvent = looping->m_audioEventRTS; diff --git a/Dependencies/Utility/CMakeLists.txt b/Dependencies/Utility/CMakeLists.txt index a810813bd62..90acedcaec3 100644 --- a/Dependencies/Utility/CMakeLists.txt +++ b/Dependencies/Utility/CMakeLists.txt @@ -4,6 +4,7 @@ set(UTILITY_SRC Utility/endian_compat.h Utility/fstream_adapter.h Utility/hash_map_adapter.h + Utility/interlocked_adapter.h Utility/intrin_compat.h Utility/iostream_adapter.h Utility/mem_compat.h diff --git a/Dependencies/Utility/Utility/interlocked_adapter.h b/Dependencies/Utility/Utility/interlocked_adapter.h new file mode 100644 index 00000000000..87cbdedcff4 --- /dev/null +++ b/Dependencies/Utility/Utility/interlocked_adapter.h @@ -0,0 +1,28 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2025 TheSuperHackers +** +** 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 . +*/ + +#pragma once + +#if defined(_MSC_VER) && _MSC_VER < 1300 + +inline long InterlockedCompareExchange(long volatile *Destination, long Exchange, long Comparand) +{ + return (long)InterlockedCompareExchange((PVOID*)Destination, (PVOID)Exchange, (PVOID)Comparand); +} + +#endif diff --git a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index b657b769b4d..ae4b136dbb1 100644 --- a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4521,8 +4521,8 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_DEMO_MUSIC_NEXT_TRACK: { - TheAudio->nextMusicTrack(); - TheInGameUI->message( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugMusicTrack", L"Playing Track: %hs", TheAudio->getMusicTrackName().str()) ); + AsciiString trackName = TheAudio->nextMusicTrack(); + TheInGameUI->message( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugMusicTrack", L"Playing Track: %hs", trackName.str()) ); disp = DESTROY_MESSAGE; break; } @@ -4531,8 +4531,8 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_DEMO_MUSIC_PREV_TRACK: { - TheAudio->prevMusicTrack(); - TheInGameUI->message( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugMusicTrack", L"Playing Track: %hs", TheAudio->getMusicTrackName().str()) ); + AsciiString trackName = TheAudio->prevMusicTrack(); + TheInGameUI->message( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugMusicTrack", L"Playing Track: %hs", trackName.str()) ); disp = DESTROY_MESSAGE; break; } diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index 1f15291995a..3ffdc71758e 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4904,8 +4904,8 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_DEMO_MUSIC_NEXT_TRACK: { - TheAudio->nextMusicTrack(); - TheInGameUI->message( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugMusicTrack", L"Playing Track: %hs", TheAudio->getMusicTrackName().str()) ); + AsciiString trackName = TheAudio->nextMusicTrack(); + TheInGameUI->message( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugMusicTrack", L"Playing Track: %hs", trackName.str()) ); disp = DESTROY_MESSAGE; break; } @@ -4914,8 +4914,8 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage //----------------------------------------------------------------------------------------- case GameMessage::MSG_META_DEMO_MUSIC_PREV_TRACK: { - TheAudio->prevMusicTrack(); - TheInGameUI->message( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugMusicTrack", L"Playing Track: %hs", TheAudio->getMusicTrackName().str()) ); + AsciiString trackName = TheAudio->prevMusicTrack(); + TheInGameUI->message( TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:DebugMusicTrack", L"Playing Track: %hs", trackName.str()) ); disp = DESTROY_MESSAGE; break; }