diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index 6e34476792..946fba2b57 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -230,7 +230,7 @@ class OpenALSoundStream : public SoundStream if(Renderer->FreeSfx.Size() == 0) { FSoundChan *lowest = Renderer->FindLowestChannel(); - if(lowest) Renderer->StopChannel(lowest); + if(lowest) Renderer->ForceStopChannel(lowest); if(Renderer->FreeSfx.Size() == 0) return false; @@ -810,6 +810,14 @@ OpenALSoundRenderer::OpenALSoundRenderer() return; } + ALCint refresh=0; + alcGetIntegerv(Device, ALC_REFRESH, 1, &refresh); + if(refresh > 0) + { + // Round up instead of down + UpdateTimeMS = (1000+refresh-1) / refresh; + } + ALCint numMono=0, numStereo=0; alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &numMono); alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &numStereo); @@ -1315,7 +1323,7 @@ void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) if((ALuint)bufID == buffer) { FSoundChan *next = schan->NextChan; - StopChannel(schan); + ForceStopChannel(schan); schan = next; continue; } @@ -1323,6 +1331,20 @@ void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) schan = schan->NextChan; } + // Make sure to kill any currently fading sounds too + for(auto iter = FadingSources.begin();iter != FadingSources.end();) + { + ALint bufID = 0; + alGetSourcei(iter->first, AL_BUFFER, &bufID); + if(static_cast(bufID) == buffer) + { + FreeSource(iter->first); + iter = FadingSources.erase(iter); + } + else + ++iter; + } + alDeleteBuffers(1, &buffer); getALError(); } @@ -1359,7 +1381,7 @@ FISoundChannel *OpenALSoundRenderer::StartSound(SoundHandle sfx, float vol, int if(FreeSfx.Size() == 0) { FSoundChan *lowest = FindLowestChannel(); - if(lowest) StopChannel(lowest); + if(lowest) ForceStopChannel(lowest); if(FreeSfx.Size() == 0) return NULL; @@ -1462,7 +1484,7 @@ FISoundChannel *OpenALSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener { if(lowest->Priority < priority || (lowest->Priority == priority && lowest->DistanceSqr > dist_sqr)) - StopChannel(lowest); + ForceStopChannel(lowest); } if(FreeSfx.Size() == 0) return NULL; @@ -1666,16 +1688,8 @@ void OpenALSoundRenderer::ChannelVolume(FISoundChannel *chan, float volume) alSourcef(source, AL_GAIN, SfxVolume * volume); } -void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) +void OpenALSoundRenderer::FreeSource(ALuint source) { - if(chan == NULL || chan->SysChannel == NULL) - return; - - ALuint source = GET_PTRID(chan->SysChannel); - // Release first, so it can be properly marked as evicted if it's being - // forcefully killed - S_ChannelEnded(chan); - alSourceRewind(source); alSourcei(source, AL_BUFFER, 0); getALError(); @@ -1685,11 +1699,48 @@ void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) PausableSfx.Delete(i); if((i=ReverbSfx.Find(source)) < ReverbSfx.Size()) ReverbSfx.Delete(i); + if((i=SfxGroup.Find(source)) < SfxGroup.Size()) + SfxGroup.Delete(i); - SfxGroup.Delete(SfxGroup.Find(source)); FreeSfx.Push(source); } +void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + ALuint source = GET_PTRID(chan->SysChannel); + // Release first, so it can be properly marked as evicted if it's being killed + S_ChannelEnded(chan); + + ALint state = AL_INITIAL; + alGetSourcei(source, AL_SOURCE_STATE, &state); + if(state != AL_PLAYING) + FreeSource(source); + else + { + // The sound is being killed while playing, so set its gain to 0 and track it + // as it fades. + alSourcef(source, AL_GAIN, 0.f); + getALError(); + + FadingSources.insert(std::make_pair( + source, std::chrono::steady_clock::now().time_since_epoch().count() + )); + } +} + +void OpenALSoundRenderer::ForceStopChannel(FISoundChannel *chan) +{ + ALuint source = GET_PTRID(chan->SysChannel); + if(!source) return; + + S_ChannelEnded(chan); + FreeSource(source); +} + + unsigned int OpenALSoundRenderer::GetPosition(FISoundChannel *chan) { if(chan == NULL || chan->SysChannel == NULL) @@ -1946,6 +1997,23 @@ void OpenALSoundRenderer::UpdateSounds() { alProcessUpdatesSOFT(); + if(!FadingSources.empty()) + { + auto cur_time = std::chrono::steady_clock::now().time_since_epoch(); + for(auto iter = FadingSources.begin();iter != FadingSources.end();) + { + auto time_diff = std::chrono::duration_cast(cur_time - + std::chrono::steady_clock::time_point::duration(iter->second)); + if(time_diff.count() >= UpdateTimeMS) + { + FreeSource(iter->first); + iter = FadingSources.erase(iter); + } + else + ++iter; + } + } + if(ALC.EXT_disconnect) { ALCint connected = ALC_TRUE; @@ -2031,17 +2099,18 @@ void OpenALSoundRenderer::PrintStatus() FString OpenALSoundRenderer::GatherStats() { - ALCint updates = 1; - alcGetIntegerv(Device, ALC_REFRESH, 1, &updates); + FString out; + + ALCint refresh = 1; + alcGetIntegerv(Device, ALC_REFRESH, 1, &refresh); getALCError(Device); uint32_t total = Sources.Size(); uint32_t used = SfxGroup.Size()+Streams.Size(); uint32_t unused = FreeSfx.Size(); - FString out; - out.Format("%u sources (" TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" active, " TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" free), Update interval: " TEXTCOLOR_YELLOW"%d" TEXTCOLOR_NORMAL"ms", - total, used, unused, 1000/updates); + out.Format("%u sources (" TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" active, " TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" free), Update interval: " TEXTCOLOR_YELLOW"%.1f" TEXTCOLOR_NORMAL"ms", + total, used, unused, 1000.f/static_cast(refresh)); return out; } @@ -2106,7 +2175,7 @@ void OpenALSoundRenderer::PurgeStoppedSources() { if(schan->SysChannel != NULL && src == GET_PTRID(schan->SysChannel)) { - StopChannel(schan); + ForceStopChannel(schan); break; } schan = schan->NextChan; diff --git a/src/sound/oalsound.h b/src/sound/oalsound.h index f14d7dd61f..82cb4a736f 100644 --- a/src/sound/oalsound.h +++ b/src/sound/oalsound.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "i_sound.h" #include "s_sound.h" @@ -200,8 +201,10 @@ private: void RemoveStream(OpenALSoundStream *stream); void LoadReverb(const ReverbContainer *env); + void FreeSource(ALuint source); void PurgeStoppedSources(); static FSoundChan *FindLowestChannel(); + void ForceStopChannel(FISoundChannel *chan); std::thread StreamThread; std::mutex StreamLock; @@ -222,6 +225,10 @@ private: TArray ReverbSfx; TArray SfxGroup; + int UpdateTimeMS; + using SourceTimeMap = std::unordered_map; + SourceTimeMap FadingSources; + const ReverbContainer *PrevEnvironment; typedef TMap EffectMap;