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();
|
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()
|
void GameLoop::RunGameLoop()
|
||||||
{
|
{
|
||||||
@@ -268,47 +316,19 @@ void GameLoop::RunGameLoop()
|
|||||||
DoChangeTheme();
|
DoChangeTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update
|
|
||||||
float fDeltaTime = g_GameplayTimer.GetDeltaTime();
|
|
||||||
|
|
||||||
if( g_fConstantUpdateDeltaSeconds > 0 )
|
|
||||||
fDeltaTime = g_fConstantUpdateDeltaSeconds;
|
|
||||||
|
|
||||||
CheckGameLoopTimerSkips( fDeltaTime );
|
|
||||||
|
|
||||||
fDeltaTime *= g_fUpdateRate;
|
|
||||||
|
|
||||||
CheckFocus();
|
CheckFocus();
|
||||||
|
|
||||||
// Update SOUNDMAN early (before any RageSound::GetPosition calls), to flush position data.
|
UpdateAllButDraw(false);
|
||||||
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 );
|
|
||||||
|
|
||||||
if( INPUTMAN->DevicesChanged() )
|
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();
|
INPUTMAN->LoadDrivers();
|
||||||
RString sMessage;
|
RString sMessage;
|
||||||
if( INPUTMAPPER->CheckForChangedInputDevicesAndRemap(sMessage) )
|
if( INPUTMAPPER->CheckForChangedInputDevicesAndRemap(sMessage) )
|
||||||
SCREENMAN->SystemMessage( sMessage );
|
SCREENMAN->SystemMessage( sMessage );
|
||||||
}
|
}
|
||||||
|
|
||||||
LIGHTSMAN->Update( fDeltaTime );
|
|
||||||
|
|
||||||
// Render
|
|
||||||
SCREENMAN->Draw();
|
SCREENMAN->Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
namespace GameLoop
|
namespace GameLoop
|
||||||
{
|
{
|
||||||
void RunGameLoop();
|
void RunGameLoop();
|
||||||
|
void UpdateAllButDraw( bool bRunningFromVBLANK);
|
||||||
void SetUpdateRate( float fUpdateRate );
|
void SetUpdateRate( float fUpdateRate );
|
||||||
void ChangeTheme(const RString &sNewTheme);
|
void ChangeTheme(const RString &sNewTheme);
|
||||||
void ChangeGame(const RString& new_game, const RString& new_theme= "");
|
void ChangeGame(const RString& new_game, const RString& new_theme= "");
|
||||||
void StartConcurrentRendering();
|
void StartConcurrentRendering();
|
||||||
void FinishConcurrentRendering();
|
void FinishConcurrentRendering();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ PrefsManager::PrefsManager() :
|
|||||||
m_bSmoothLines ( "SmoothLines", false ),
|
m_bSmoothLines ( "SmoothLines", false ),
|
||||||
m_iSoundWriteAhead ( "SoundWriteAhead", 0 ),
|
m_iSoundWriteAhead ( "SoundWriteAhead", 0 ),
|
||||||
m_iSoundDevice ( "SoundDevice", "" ),
|
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_iSoundPreferredSampleRate ( "SoundPreferredSampleRate", 0 ),
|
||||||
m_sLightsStepsDifficulty ( "LightsStepsDifficulty", "medium" ),
|
m_sLightsStepsDifficulty ( "LightsStepsDifficulty", "medium" ),
|
||||||
m_bAllowUnacceleratedRenderer ( "AllowUnacceleratedRenderer", false ),
|
m_bAllowUnacceleratedRenderer ( "AllowUnacceleratedRenderer", false ),
|
||||||
|
|||||||
@@ -282,6 +282,7 @@ public:
|
|||||||
Preference<bool> m_bSmoothLines;
|
Preference<bool> m_bSmoothLines;
|
||||||
Preference<int> m_iSoundWriteAhead;
|
Preference<int> m_iSoundWriteAhead;
|
||||||
Preference<RString> m_iSoundDevice;
|
Preference<RString> m_iSoundDevice;
|
||||||
|
Preference<int> m_iRageSoundSampleCountClamp;
|
||||||
Preference<int> m_iSoundPreferredSampleRate;
|
Preference<int> m_iSoundPreferredSampleRate;
|
||||||
Preference<RString> m_sLightsStepsDifficulty;
|
Preference<RString> m_sLightsStepsDifficulty;
|
||||||
Preference<bool> m_bAllowUnacceleratedRenderer;
|
Preference<bool> m_bAllowUnacceleratedRenderer;
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ RageSoundManager::~RageSoundManager()
|
|||||||
m_mapPreloadedSounds.clear();
|
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)
|
void RageSoundManager::fix_bogus_sound_driver_pref(RString const& valid_setting)
|
||||||
{
|
{
|
||||||
g_sSoundDrivers.Set(valid_setting);
|
g_sSoundDrivers.Set(valid_setting);
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ public:
|
|||||||
void AddLoadedSound( const RString &sPath, RageSoundReader_Preload *pSound );
|
void AddLoadedSound( const RString &sPath, RageSoundReader_Preload *pSound );
|
||||||
|
|
||||||
void fix_bogus_sound_driver_pref(RString const& valid_setting);
|
void fix_bogus_sound_driver_pref(RString const& valid_setting);
|
||||||
|
void low_sample_count_workaround();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
map<RString, RageSoundReader_Preload *> m_mapPreloadedSounds;
|
map<RString, RageSoundReader_Preload *> m_mapPreloadedSounds;
|
||||||
|
|||||||
@@ -50,11 +50,14 @@ public:
|
|||||||
* RageSound::CommitPlayingPosition. */
|
* RageSound::CommitPlayingPosition. */
|
||||||
int64_t GetHardwareFrame( RageTimer *pTimer ) const;
|
int64_t GetHardwareFrame( RageTimer *pTimer ) const;
|
||||||
virtual int64_t GetPosition() const = 0;
|
virtual int64_t GetPosition() const = 0;
|
||||||
|
void low_sample_count_workaround();
|
||||||
|
|
||||||
/* When a sound is finished playing (GetDataToPlay returns 0) and the sound has
|
/* When a sound is finished playing (GetDataToPlay returns 0) and the sound has
|
||||||
* been completely flushed (so GetPosition is no longer meaningful), call
|
* been completely flushed (so GetPosition is no longer meaningful), call
|
||||||
* RageSoundBase::SoundIsFinishedPlaying(). */
|
* RageSoundBase::SoundIsFinishedPlaying(). */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Optional, if needed: */
|
/* Optional, if needed: */
|
||||||
virtual void Update();
|
virtual void Update();
|
||||||
|
|
||||||
@@ -199,6 +202,8 @@ private:
|
|||||||
|
|
||||||
int64_t ClampHardwareFrame( int64_t iHardwareFrame ) const;
|
int64_t ClampHardwareFrame( int64_t iHardwareFrame ) const;
|
||||||
mutable int64_t m_iMaxHardwareFrame;
|
mutable int64_t m_iMaxHardwareFrame;
|
||||||
|
mutable int64_t m_iVMaxHardwareFrame;
|
||||||
|
mutable int32_t soundDriverMaxSamples = 0;
|
||||||
|
|
||||||
bool m_bShutdownDecodeThread;
|
bool m_bShutdownDecodeThread;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "RageSoundDriver.h"
|
#include "RageSoundDriver.h"
|
||||||
|
#include "PrefsManager.h"
|
||||||
#include "RageLog.h"
|
#include "RageLog.h"
|
||||||
#include "RageSound.h"
|
#include "RageSound.h"
|
||||||
#include "RageUtil.h"
|
#include "RageUtil.h"
|
||||||
@@ -437,14 +437,20 @@ void RageSoundDriver::SetDecodeBufferSize( int iFrames )
|
|||||||
frames_to_buffer = iFrames;
|
frames_to_buffer = iFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RageSoundDriver::low_sample_count_workaround()
|
||||||
|
{
|
||||||
|
if (soundDriverMaxSamples != 0) GetHardwareFrame(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
RageSoundDriver::RageSoundDriver():
|
RageSoundDriver::RageSoundDriver():
|
||||||
m_Mutex("RageSoundDriver"),
|
m_Mutex("RageSoundDriver"),
|
||||||
m_SoundListMutex("SoundListMutex")
|
m_SoundListMutex("SoundListMutex")
|
||||||
{
|
{
|
||||||
m_bShutdownDecodeThread = false;
|
m_bShutdownDecodeThread = false;
|
||||||
m_iMaxHardwareFrame = 0;
|
m_iMaxHardwareFrame = 0;
|
||||||
|
m_iVMaxHardwareFrame = 0;
|
||||||
SetDecodeBufferSize( 4096 );
|
SetDecodeBufferSize( 4096 );
|
||||||
|
soundDriverMaxSamples = PREFSMAN->m_iRageSoundSampleCountClamp;
|
||||||
m_DecodeThread.SetName("Decode thread");
|
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
|
/* 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
|
* 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. */
|
* 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
|
diff = 0;
|
||||||
* output spam. */
|
int iMinuteSampleRate = GetSampleRate()*60; //get one minute worth of grace -- if you need more, there is very likely some other problem going on
|
||||||
static RageTimer last(RageZeroTimer);
|
//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( last.IsZero() || last.Ago() > 1.0f )
|
if ((soundDriverMaxSamples>0) && (iHardwareFrame<iMinuteSampleRate) && iHardwareFrame >= 0)
|
||||||
{
|
{
|
||||||
LOG->Trace( "RageSoundDriver: driver returned a lesser position (%d < %d)",
|
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 );
|
(int)iHardwareFrame, (int)m_iMaxHardwareFrame, soundDriverMaxSamples, iMinuteSampleRate, (int) m_iVMaxHardwareFrame);
|
||||||
last.Touch();
|
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 );
|
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 )
|
if( pTimestamp == NULL )
|
||||||
return ClampHardwareFrame( GetPosition() );
|
return ClampHardwareFrame( GetPosition() );
|
||||||
|
|||||||
Reference in New Issue
Block a user