Fix audio drivers with a low sample count maximum value (#1697)
This commit is contained in:
+50
-30
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() );
|
||||
|
||||
Reference in New Issue
Block a user