Skip to content

Commit

Permalink
Set the source gain to 0 before stopping it
Browse files Browse the repository at this point in the history
Stopping a source in the middle of playback can causing undesirable "popping"
from a sudden change in amplitude. Setting the source gain to 0 gives OpenAL
the opportunity to ramp the source's output to 0, where it can be safely
stopped without causing a sudden change in amplitude.
  • Loading branch information
kcat authored and coelckers committed Apr 24, 2017
1 parent 78231d5 commit f97efef
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 20 deletions.
109 changes: 89 additions & 20 deletions src/sound/oalsound.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1315,14 +1323,28 @@ void OpenALSoundRenderer::UnloadSound(SoundHandle sfx)
if((ALuint)bufID == buffer)
{
FSoundChan *next = schan->NextChan;
StopChannel(schan);
ForceStopChannel(schan);
schan = next;
continue;
}
}
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<ALuint>(bufID) == buffer)
{
FreeSource(iter->first);
iter = FadingSources.erase(iter);
}
else
++iter;
}

alDeleteBuffers(1, &buffer);
getALError();
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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)
Expand Down Expand Up @@ -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<std::chrono::milliseconds>(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;
Expand Down Expand Up @@ -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<float>(refresh));
return out;
}

Expand Down Expand Up @@ -2106,7 +2175,7 @@ void OpenALSoundRenderer::PurgeStoppedSources()
{
if(schan->SysChannel != NULL && src == GET_PTRID(schan->SysChannel))
{
StopChannel(schan);
ForceStopChannel(schan);
break;
}
schan = schan->NextChan;
Expand Down
7 changes: 7 additions & 0 deletions src/sound/oalsound.h
Expand Up @@ -5,6 +5,7 @@
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <unordered_map>

#include "i_sound.h"
#include "s_sound.h"
Expand Down Expand Up @@ -200,8 +201,10 @@ class OpenALSoundRenderer : public SoundRenderer
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;
Expand All @@ -222,6 +225,10 @@ class OpenALSoundRenderer : public SoundRenderer
TArray<ALuint> ReverbSfx;
TArray<ALuint> SfxGroup;

int UpdateTimeMS;
using SourceTimeMap = std::unordered_map<ALuint,int64_t>;
SourceTimeMap FadingSources;

const ReverbContainer *PrevEnvironment;

typedef TMap<uint16_t,ALuint> EffectMap;
Expand Down

0 comments on commit f97efef

Please sign in to comment.