From 504b572ae4013e823aeef0c105706d38f1b69afe Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Mon, 1 Jun 2020 20:17:19 +0200 Subject: [PATCH] Update sounds at ~60Hz instead of ~10Hz, fixes #141 For some reason sounds were only updated/started about every 100ms, which could lead to delays of up to around 100ms after a sound gets started (with idSoundEmitterLocal::StartSound()) until OpenAL is finally told to play the sound. Also, the actual delay drifted over time between 1ms and 100ms, as the sound ticks weren't a fixed multiple of the (16ms) gameticks - and the sound updates didn't even happen at the regular 92-94ms intervals they should because they run in the async thread which only updates every 16ms... Because of this, the machine gun and other rapid firing weapons sounded like they shot in bursts or left out shots occasionally, even though they don't. Anyway, now sound is updated every 16ms in the async thread so delays are <= 16ms and hopefully less noticeable. You can still get the old behavior with com_asyncSound 2 if you want. --- neo/framework/Common.cpp | 18 ++++++++++-------- neo/framework/Session.cpp | 2 +- neo/sound/snd_system.cpp | 32 +++++++++++--------------------- neo/sound/snd_world.cpp | 7 ++++++- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/neo/framework/Common.cpp b/neo/framework/Common.cpp index 37648c907..7b172bc5f 100644 --- a/neo/framework/Common.cpp +++ b/neo/framework/Common.cpp @@ -83,12 +83,8 @@ idCVar com_purgeAll( "com_purgeAll", "0", CVAR_BOOL | CVAR_ARCHIVE | CVAR_SYSTEM idCVar com_memoryMarker( "com_memoryMarker", "-1", CVAR_INTEGER | CVAR_SYSTEM | CVAR_INIT, "used as a marker for memory stats" ); idCVar com_preciseTic( "com_preciseTic", "1", CVAR_BOOL|CVAR_SYSTEM, "run one game tick every async thread update" ); idCVar com_asyncInput( "com_asyncInput", "0", CVAR_BOOL|CVAR_SYSTEM, "sample input from the async thread" ); -#define ASYNCSOUND_INFO "0: mix sound inline, 1: memory mapped async mix, 2: callback mixing, 3: write async mix" -#if defined( __unix__ ) && !defined( MACOS_X ) -idCVar com_asyncSound( "com_asyncSound", "3", CVAR_INTEGER|CVAR_SYSTEM|CVAR_ROM, ASYNCSOUND_INFO ); -#else -idCVar com_asyncSound( "com_asyncSound", "1", CVAR_INTEGER|CVAR_SYSTEM, ASYNCSOUND_INFO, 0, 1 ); -#endif +#define ASYNCSOUND_INFO "0: mix sound inline, 1 or 3: async update every 16ms 2: async update about every 100ms (original behavior)" +idCVar com_asyncSound( "com_asyncSound", "1", CVAR_INTEGER|CVAR_SYSTEM, ASYNCSOUND_INFO, 0, 3 ); idCVar com_forceGenericSIMD( "com_forceGenericSIMD", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "force generic platform independent SIMD" ); idCVar com_developer( "developer", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "developer mode" ); idCVar com_allowConsole( "com_allowConsole", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "allow toggling console with the tilde key" ); @@ -2506,11 +2502,14 @@ void idCommonLocal::SingleAsyncTic( void ) { switch ( com_asyncSound.GetInteger() ) { case 1: - soundSystem->AsyncUpdate( stat->milliseconds ); - break; case 3: + // DG: these are now used for the new default behavior of "update every async tic (every 16ms)" soundSystem->AsyncUpdateWrite( stat->milliseconds ); break; + case 2: + // DG: use 2 for the old "update only 10x/second" behavior in case anyone likes that.. + soundSystem->AsyncUpdate( stat->milliseconds ); + break; } // we update com_ticNumber after all the background tasks @@ -2754,6 +2753,9 @@ static unsigned int AsyncTimer(unsigned int interval, void *) { // calculate the next interval to get as close to 60fps as possible unsigned int now = SDL_GetTicks(); unsigned int tick = com_ticNumber * USERCMD_MSEC; + // FIXME: this is pretty broken and basically always returns 1 because now now is much bigger than tic + // (probably com_tickNumber only starts incrementing a second after engine starts?) + // only reason this works is common->Async() checking again before calling SingleAsyncTic() if (now >= tick) return 1; diff --git a/neo/framework/Session.cpp b/neo/framework/Session.cpp index 46c3fe36a..2902c48c9 100644 --- a/neo/framework/Session.cpp +++ b/neo/framework/Session.cpp @@ -2524,7 +2524,7 @@ extern bool CheckOpenALDeviceAndRecoverIfNeeded(); void idSessionLocal::Frame() { if ( com_asyncSound.GetInteger() == 0 ) { - soundSystem->AsyncUpdate( Sys_Milliseconds() ); + soundSystem->AsyncUpdateWrite( Sys_Milliseconds() ); } // DG: periodically check if sound device is still there and try to reset it if not diff --git a/neo/sound/snd_system.cpp b/neo/sound/snd_system.cpp index 792b03d52..d9a2848c4 100644 --- a/neo/sound/snd_system.cpp +++ b/neo/sound/snd_system.cpp @@ -695,7 +695,10 @@ int idSoundSystemLocal::AsyncMix( int soundTime, float *mixBuffer ) { /* =================== idSoundSystemLocal::AsyncUpdate -called from async sound thread when com_asyncSound == 1 ( Windows ) +called from async sound thread when com_asyncSound == 2 +DG: using this for the "traditional" sound updates that + only happen about every 100ms (and lead to delays between 1 and 110ms between + starting a sound in gamecode and it being played), for people who like that.. =================== */ int idSoundSystemLocal::AsyncUpdate( int inTime ) { @@ -772,9 +775,12 @@ int idSoundSystemLocal::AsyncUpdate( int inTime ) { /* =================== idSoundSystemLocal::AsyncUpdateWrite -sound output using a write API. all the scheduling based on time -we mix MIXBUFFER_SAMPLES at a time, but we feed the audio device with smaller chunks (and more often) -called by the sound thread when com_asyncSound is 3 ( Linux ) +DG: using this now for 60Hz sound updates +called from async sound thread when com_asyncSound is 3 or 1 +also called from main thread if com_asyncSound == 0 +(those were the default values used in dhewm3 on unix-likes (except mac) or rest) +with this, once every async tic new sounds are started and existing ones updated, +instead of once every ~100ms. =================== */ int idSoundSystemLocal::AsyncUpdateWrite( int inTime ) { @@ -783,21 +789,7 @@ int idSoundSystemLocal::AsyncUpdateWrite( int inTime ) { return 0; } - unsigned int dwCurrentBlock = (unsigned int)( inTime * 44.1f / MIXBUFFER_SAMPLES ); - - if ( nextWriteBlock == 0xffffffff ) { - nextWriteBlock = dwCurrentBlock; - } - - if ( dwCurrentBlock < nextWriteBlock ) { - return 0; - } - - if ( nextWriteBlock != dwCurrentBlock ) { - Sys_Printf( "missed %d sound updates\n", dwCurrentBlock - nextWriteBlock ); - } - - int sampleTime = dwCurrentBlock * MIXBUFFER_SAMPLES; + int sampleTime = inTime * 44.1f; int numSpeakers = s_numberOfSpeakers.GetInteger(); // enable audio hardware caching @@ -811,8 +803,6 @@ int idSoundSystemLocal::AsyncUpdateWrite( int inTime ) { // disable audio hardware caching (this updates ALL settings since last alcSuspendContext) alcProcessContext( openalContext ); - // only move to the next block if the write was successful - nextWriteBlock = dwCurrentBlock + 1; CurrentSoundTime = sampleTime; return Sys_Milliseconds() - inTime; diff --git a/neo/sound/snd_world.cpp b/neo/sound/snd_world.cpp index 079dd845d..b79317a69 100644 --- a/neo/sound/snd_world.cpp +++ b/neo/sound/snd_world.cpp @@ -1857,7 +1857,11 @@ void idSoundWorldLocal::AddChannelContribution( idSoundEmitterLocal *sound, idSo chan->triggered = false; } } - } else { + } +#if 1 // DG: I /think/ this was only relevant for the old sound backends? + // FIXME: completely remove else branch, but for testing leave it in under com_asyncSound 2 + // (which also does the old 92-100ms updates) + else if( com_asyncSound.GetInteger() == 2 ) { if ( slowmoActive && !chan->disallowSlow ) { idSlowChannel slow = sound->GetSlowChannel( chan ); @@ -1952,6 +1956,7 @@ void idSoundWorldLocal::AddChannelContribution( idSoundEmitterLocal *sound, idSo } } +#endif // 1/0 soundSystemLocal.soundStats.activeSounds++;