Fix audio drivers with a low sample count maximum value (#1697)

This commit is contained in:
Chris Pable
2018-08-02 08:28:24 -07:00
committed by Colby Klein
parent b0ef8d9f00
commit c200600c65
8 changed files with 123 additions and 43 deletions
+50 -30
View File
@@ -249,6 +249,54 @@ namespace
g_NewTheme= RString();
}
}
static bool m_bUpdatedDuringVBLANK = false;
void GameLoop::UpdateAllButDraw(bool bRunningFromVBLANK)
{
//if we are running our once per frame routine and we were already run from VBLANK, we did the work already
if (!bRunningFromVBLANK && m_bUpdatedDuringVBLANK)
{
m_bUpdatedDuringVBLANK = false;
return; //would it kill us to run it again or do we want to draw asap?
}
//if vblank called us, we will tell the game loop we received an update for the frame it wants to process
if (bRunningFromVBLANK) m_bUpdatedDuringVBLANK = true;
else m_bUpdatedDuringVBLANK = false;
// Update our stuff
float fDeltaTime = g_GameplayTimer.GetDeltaTime();
if (g_fConstantUpdateDeltaSeconds > 0)
fDeltaTime = g_fConstantUpdateDeltaSeconds;
CheckGameLoopTimerSkips(fDeltaTime);
fDeltaTime *= g_fUpdateRate;
// Update SOUNDMAN early (before any RageSound::GetPosition calls), to flush position data.
SOUNDMAN->Update();
/* Update song beat information -before- calling update on all the classes that
* depend on it. If you don't do this first, the classes are all acting on old
* information and will lag. (but no longer fatally, due to timestamping -glenn) */
SOUND->Update(fDeltaTime);
TEXTUREMAN->Update(fDeltaTime);
GAMESTATE->Update(fDeltaTime);
SCREENMAN->Update(fDeltaTime);
MEMCARDMAN->Update();
NSMAN->Update(fDeltaTime);
/* Important: Process input AFTER updating game logic, or input will be
* acting on song beat from last frame */
HandleInputEvents(fDeltaTime);
//bandaid for low max audio sample counter
SOUNDMAN->low_sample_count_workaround();
LIGHTSMAN->Update(fDeltaTime);
}
void GameLoop::RunGameLoop()
{
@@ -268,47 +316,19 @@ void GameLoop::RunGameLoop()
DoChangeTheme();
}
// Update
float fDeltaTime = g_GameplayTimer.GetDeltaTime();
if( g_fConstantUpdateDeltaSeconds > 0 )
fDeltaTime = g_fConstantUpdateDeltaSeconds;
CheckGameLoopTimerSkips( fDeltaTime );
fDeltaTime *= g_fUpdateRate;
CheckFocus();
// Update SOUNDMAN early (before any RageSound::GetPosition calls), to flush position data.
SOUNDMAN->Update();
/* Update song beat information -before- calling update on all the classes that
* depend on it. If you don't do this first, the classes are all acting on old
* information and will lag. (but no longer fatally, due to timestamping -glenn) */
SOUND->Update( fDeltaTime );
TEXTUREMAN->Update( fDeltaTime );
GAMESTATE->Update( fDeltaTime );
SCREENMAN->Update( fDeltaTime );
MEMCARDMAN->Update();
NSMAN->Update( fDeltaTime );
/* Important: Process input AFTER updating game logic, or input will be
* acting on song beat from last frame */
HandleInputEvents( fDeltaTime );
UpdateAllButDraw(false);
if( INPUTMAN->DevicesChanged() )
{
INPUTFILTER->Reset(); // fix "buttons stuck" if button held while unplugged
INPUTFILTER->Reset(); // fix "buttons stuck" once per frame if button held while unplugged
INPUTMAN->LoadDrivers();
RString sMessage;
if( INPUTMAPPER->CheckForChangedInputDevicesAndRemap(sMessage) )
SCREENMAN->SystemMessage( sMessage );
}
LIGHTSMAN->Update( fDeltaTime );
// Render
SCREENMAN->Draw();
}
+2
View File
@@ -4,11 +4,13 @@
namespace GameLoop
{
void RunGameLoop();
void UpdateAllButDraw( bool bRunningFromVBLANK);
void SetUpdateRate( float fUpdateRate );
void ChangeTheme(const RString &sNewTheme);
void ChangeGame(const RString& new_game, const RString& new_theme= "");
void StartConcurrentRendering();
void FinishConcurrentRendering();
};
#endif
+1
View File
@@ -274,6 +274,7 @@ PrefsManager::PrefsManager() :
m_bSmoothLines ( "SmoothLines", false ),
m_iSoundWriteAhead ( "SoundWriteAhead", 0 ),
m_iSoundDevice ( "SoundDevice", "" ),
m_iRageSoundSampleCountClamp ("RageSoundSampleCountClamp", 0), //some sound drivers mask the sample location number, the most popular number for this is 2^27, this causes lockup after ~50 minutes at 44.1khz sample rate
m_iSoundPreferredSampleRate ( "SoundPreferredSampleRate", 0 ),
m_sLightsStepsDifficulty ( "LightsStepsDifficulty", "medium" ),
m_bAllowUnacceleratedRenderer ( "AllowUnacceleratedRenderer", false ),
+1
View File
@@ -282,6 +282,7 @@ public:
Preference<bool> m_bSmoothLines;
Preference<int> m_iSoundWriteAhead;
Preference<RString> m_iSoundDevice;
Preference<int> m_iRageSoundSampleCountClamp;
Preference<int> m_iSoundPreferredSampleRate;
Preference<RString> m_sLightsStepsDifficulty;
Preference<bool> m_bAllowUnacceleratedRenderer;
+6
View File
@@ -55,6 +55,12 @@ RageSoundManager::~RageSoundManager()
m_mapPreloadedSounds.clear();
}
void RageSoundManager::low_sample_count_workaround()
{
m_pDriver->low_sample_count_workaround();
}
void RageSoundManager::fix_bogus_sound_driver_pref(RString const& valid_setting)
{
g_sSoundDrivers.Set(valid_setting);
+1
View File
@@ -46,6 +46,7 @@ public:
void AddLoadedSound( const RString &sPath, RageSoundReader_Preload *pSound );
void fix_bogus_sound_driver_pref(RString const& valid_setting);
void low_sample_count_workaround();
private:
map<RString, RageSoundReader_Preload *> m_mapPreloadedSounds;
+5
View File
@@ -50,11 +50,14 @@ public:
* RageSound::CommitPlayingPosition. */
int64_t GetHardwareFrame( RageTimer *pTimer ) const;
virtual int64_t GetPosition() const = 0;
void low_sample_count_workaround();
/* When a sound is finished playing (GetDataToPlay returns 0) and the sound has
* been completely flushed (so GetPosition is no longer meaningful), call
* RageSoundBase::SoundIsFinishedPlaying(). */
/* Optional, if needed: */
virtual void Update();
@@ -199,6 +202,8 @@ private:
int64_t ClampHardwareFrame( int64_t iHardwareFrame ) const;
mutable int64_t m_iMaxHardwareFrame;
mutable int64_t m_iVMaxHardwareFrame;
mutable int32_t soundDriverMaxSamples = 0;
bool m_bShutdownDecodeThread;
@@ -1,6 +1,6 @@
#include "global.h"
#include "RageSoundDriver.h"
#include "PrefsManager.h"
#include "RageLog.h"
#include "RageSound.h"
#include "RageUtil.h"
@@ -437,14 +437,20 @@ void RageSoundDriver::SetDecodeBufferSize( int iFrames )
frames_to_buffer = iFrames;
}
void RageSoundDriver::low_sample_count_workaround()
{
if (soundDriverMaxSamples != 0) GetHardwareFrame(NULL);
}
RageSoundDriver::RageSoundDriver():
m_Mutex("RageSoundDriver"),
m_SoundListMutex("SoundListMutex")
{
m_bShutdownDecodeThread = false;
m_iMaxHardwareFrame = 0;
m_iVMaxHardwareFrame = 0;
SetDecodeBufferSize( 4096 );
soundDriverMaxSamples = PREFSMAN->m_iRageSoundSampleCountClamp;
m_DecodeThread.SetName("Decode thread");
}
@@ -470,24 +476,62 @@ int64_t RageSoundDriver::ClampHardwareFrame( int64_t iHardwareFrame ) const
/* It's sometimes possible for the hardware position to move backwards, usually
* on underrun. We can try to prevent this in each driver, but it's an obscure
* error, so let's clamp the result here instead. */
if( iHardwareFrame < m_iMaxHardwareFrame )
/* New extra logic for devices and drivers that cant return large numbers in their sample position
* calculate a diff and if the current sample # is >= 0 and less than a minute based on sample rate
* check if the user set soundDriverMaxSamples to a value, if they did
* (this is usually 134217728 aka 2^27 for some reason) hndle the wrap around to a rolling counter
* otherwise do the old logic for underrun
*/
//iHardwareFrame %= 0x800000; // debug test a sample value max of about 3 minutes so I dont have to spend an hour per test
int64_t diff = iHardwareFrame - m_iMaxHardwareFrame;
if( diff < 0 )
{
/* Clamp the output to one per second, so one underruns don't cascade due to
* output spam. */
static RageTimer last(RageZeroTimer);
if( last.IsZero() || last.Ago() > 1.0f )
diff = 0;
int iMinuteSampleRate = GetSampleRate()*60; //get one minute worth of grace -- if you need more, there is very likely some other problem going on
//if we have a sample clamp and the new hardware frame is within a fresh minute of the sample rate max and have 'underrun'
if ((soundDriverMaxSamples>0) && (iHardwareFrame<iMinuteSampleRate) && iHardwareFrame >= 0)
{
LOG->Trace( "RageSoundDriver: driver returned a lesser position (%d < %d)",
(int)iHardwareFrame, (int)m_iMaxHardwareFrame );
last.Touch();
LOG->Trace("RageSoundDriver: driver position mask adjustment hardware frame number: %d, last max frame number: %d, soundDriverMaxSamples: %d, iMinuteSampleRate: %d, m_iVMaxHardwareFrame: %d",
(int)iHardwareFrame, (int)m_iMaxHardwareFrame, soundDriverMaxSamples, iMinuteSampleRate, (int) m_iVMaxHardwareFrame);
diff = (soundDriverMaxSamples - m_iMaxHardwareFrame) + iHardwareFrame;
m_iMaxHardwareFrame = 0;
}
else
{
/* Clamp the output to one per second, so one underruns don't cascade due to
* output spam. */
static RageTimer last(RageZeroTimer);
if (last.IsZero() || last.Ago() > 1.0f)
{
//try to hand hold the user if their audio driver is possibly bad
int p = 21; // save some time, assume the buffer has at least a minute of cd quality audio -- 2^21
while (pow(2,p) < m_iMaxHardwareFrame)
{
if (p == 31) break; //do not want to go beyond signed DWORD size
p++;
}
LOG->Trace("RageSoundDriver: driver returned a lesser position (%d < %d). If this is a recurrent driver problem with your sound card and not an underrun, try setting the preference RageSoundSampleCountClamp to %d",
(int)iHardwareFrame, (int)m_iMaxHardwareFrame, (int)floor(pow(2.0, p)));
last.Touch();
}
//return m_iMaxHardwareFrame;
}
return m_iMaxHardwareFrame;
}
m_iMaxHardwareFrame = iHardwareFrame = max( iHardwareFrame, m_iMaxHardwareFrame );
return iHardwareFrame;
//return iHardwareFrame;
m_iVMaxHardwareFrame += diff;
return m_iVMaxHardwareFrame;
}
int64_t RageSoundDriver::GetHardwareFrame( RageTimer *pTimestamp ) const
int64_t RageSoundDriver::GetHardwareFrame( RageTimer *pTimestamp=NULL ) const
{
if( pTimestamp == NULL )
return ClampHardwareFrame( GetPosition() );