From: <man...@us...> - 2014-02-14 21:04:32
|
Revision: 3714 http://sourceforge.net/p/modplug/code/3714 Author: manxorist Date: 2014-02-14 21:04:24 +0000 (Fri, 14 Feb 2014) Log Message: ----------- [Fix] ASIO: Implement all driver to host messages that are required to be handled by the ASIO 2.1 specification. [Ref] sounddev: Add an idle time callback into the sound drivers. ASIO needs this to requery the latencies. [Ref] sounddev: Add stop, restart and reset requests from the driver to OpenMPT. This is needed to implement ASIO. [Ref] sounddev: Poll these driver requests in idle processing and in the 200ms GUI timer and handle them appropriately. ASIO requires this to be done at idle time and not from within a driver callback. Modified Paths: -------------- trunk/OpenMPT/mptrack/MainFrm.cpp trunk/OpenMPT/mptrack/Mainfrm.h trunk/OpenMPT/mptrack/Mptrack.cpp trunk/OpenMPT/sounddev/SoundDevice.cpp trunk/OpenMPT/sounddev/SoundDevice.h trunk/OpenMPT/sounddev/SoundDeviceASIO.cpp trunk/OpenMPT/sounddev/SoundDeviceASIO.h Modified: trunk/OpenMPT/mptrack/MainFrm.cpp =================================================================== --- trunk/OpenMPT/mptrack/MainFrm.cpp 2014-02-14 20:06:12 UTC (rev 3713) +++ trunk/OpenMPT/mptrack/MainFrm.cpp 2014-02-14 21:04:24 UTC (rev 3714) @@ -1213,6 +1213,23 @@ } +bool CMainFrame::RestartPlayback() +//-------------------------------- +{ + if(!m_pSndFile) return false; // nothing to play + if(!IsAudioDeviceOpen()) return false; + if(!gpSoundDevice->IsPlaying()) return false; + gpSoundDevice->Stop(true); + if(m_NotifyTimer) + { + KillTimer(m_NotifyTimer); + m_NotifyTimer = 0; + } + ResetNotificationBuffer(); + return StartPlayback(); +} + + bool CMainFrame::PausePlayback() //------------------------------ { @@ -1594,11 +1611,42 @@ } -BOOL CMainFrame::SetupSoundCard(const SoundDeviceSettings &deviceSettings, SoundDeviceID deviceID) -//------------------------------------------------------------------------------------------------ +void CMainFrame::IdleHandlerSounddevice() +//--------------------------------------- { - if((TrackerSettings::Instance().GetSoundDeviceID() != deviceID) || (TrackerSettings::Instance().GetSoundDeviceSettings(deviceID) != deviceSettings)) + if(gpSoundDevice) { + const LONG requestFlags = gpSoundDevice->GetRequestFlags(); + if(requestFlags & ISoundDevice::RequestFlagClose) + { + StopPlayback(); + audioCloseDevice(); + } else if(requestFlags & ISoundDevice::RequestFlagReset) + { + ResetSoundCard(); + } else if(requestFlags & ISoundDevice::RequestFlagRestart) + { + RestartPlayback(); + } else + { + gpSoundDevice->OnIdle(); + } + } +} + + +BOOL CMainFrame::ResetSoundCard() +//------------------------------- +{ + return CMainFrame::SetupSoundCard(TrackerSettings::Instance().GetSoundDeviceSettings(TrackerSettings::Instance().GetSoundDeviceID()), TrackerSettings::Instance().GetSoundDeviceID(), true); +} + + +BOOL CMainFrame::SetupSoundCard(const SoundDeviceSettings &deviceSettings, SoundDeviceID deviceID, bool forceReset) +//----------------------------------------------------------------------------------------------------------------- +{ + if(forceReset || (TrackerSettings::Instance().GetSoundDeviceID() != deviceID) || (TrackerSettings::Instance().GetSoundDeviceSettings(deviceID) != deviceSettings)) + { CModDoc *pActiveMod = nullptr; if(IsPlaying()) { @@ -1944,6 +1992,9 @@ void CMainFrame::OnTimerGUI() //--------------------------- { + + IdleHandlerSounddevice(); + // Display Time in status bar CSoundFile::samplecount_t time = 0; if(m_pSndFile != nullptr && m_pSndFile->GetSampleRate() != 0) Modified: trunk/OpenMPT/mptrack/Mainfrm.h =================================================================== --- trunk/OpenMPT/mptrack/Mainfrm.h 2014-02-14 20:06:12 UTC (rev 3713) +++ trunk/OpenMPT/mptrack/Mainfrm.h 2014-02-14 21:04:24 UTC (rev 3714) @@ -435,6 +435,7 @@ bool PreparePlayback(); bool StartPlayback(); void StopPlayback(); + bool RestartPlayback(); bool PausePlayback(); static bool IsValidSoundFile(CSoundFile &sndFile) { return sndFile.GetType() ? true : false; } static bool IsValidSoundFile(CSoundFile *pSndFile) { return pSndFile && pSndFile->GetType(); } @@ -464,7 +465,10 @@ BOOL StopRenderer(CSoundFile*); void SwitchToActiveView(); - BOOL SetupSoundCard(const SoundDeviceSettings &deviceSettings, SoundDeviceID deviceID); + void IdleHandlerSounddevice(); + + BOOL ResetSoundCard(); + BOOL SetupSoundCard(const SoundDeviceSettings &deviceSettings, SoundDeviceID deviceID, bool forceReset = false); BOOL SetupMiscOptions(); BOOL SetupPlayer(); Modified: trunk/OpenMPT/mptrack/Mptrack.cpp =================================================================== --- trunk/OpenMPT/mptrack/Mptrack.cpp 2014-02-14 20:06:12 UTC (rev 3713) +++ trunk/OpenMPT/mptrack/Mptrack.cpp 2014-02-14 21:04:24 UTC (rev 3714) @@ -1684,6 +1684,12 @@ //--------------------------------- { BOOL b = CWinApp::OnIdle(lCount); + + if(CMainFrame::GetMainFrame()) + { + CMainFrame::GetMainFrame()->IdleHandlerSounddevice(); + } + if ((gpSplashScreen) && (m_bInitialized)) { if (timeGetTime() - m_dwTimeStarted > 1000) //Set splash screen duration here -rewbs Modified: trunk/OpenMPT/sounddev/SoundDevice.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDevice.cpp 2014-02-14 20:06:12 UTC (rev 3713) +++ trunk/OpenMPT/sounddev/SoundDevice.cpp 2014-02-14 21:04:24 UTC (rev 3714) @@ -136,6 +136,8 @@ m_CurrentUpdateInterval = 0.0; m_StreamPositionRenderFrames = 0; m_StreamPositionOutputFrames = 0; + + InterlockedExchange(&m_RequestFlags, 0); } @@ -221,6 +223,7 @@ m_BufferAttributes.Latency = m_Settings.LatencyMS / 1000.0; m_BufferAttributes.UpdateInterval = m_Settings.UpdateIntervalMS / 1000.0; m_BufferAttributes.NumBuffers = 0; + InterlockedExchange(&m_RequestFlags, 0); return InternalOpen(); } @@ -230,7 +233,9 @@ { if(!IsOpen()) return true; Stop(); - return InternalClose(); + bool result = InternalClose(); + InterlockedExchange(&m_RequestFlags, 0); + return result; } @@ -331,6 +336,7 @@ m_StreamPositionOutputFrames = 0; } m_Clock.SetResolution(1); + _InterlockedAnd(&m_RequestFlags, ~RequestFlagRestart); if(!InternalStart()) { m_Clock.SetResolution(0); @@ -342,13 +348,20 @@ } -void ISoundDevice::Stop() -//----------------------- +void ISoundDevice::Stop(bool force) +//--------------------------------- { if(!IsOpen()) return; if(IsPlaying()) { - InternalStop(); + if(force) + { + InternalStopForce(); + } else + { + InternalStop(); + } + _InterlockedAnd(&m_RequestFlags, ~RequestFlagRestart); m_Clock.SetResolution(0); m_IsPlaying = false; { Modified: trunk/OpenMPT/sounddev/SoundDevice.h =================================================================== --- trunk/OpenMPT/sounddev/SoundDevice.h 2014-02-14 20:06:12 UTC (rev 3713) +++ trunk/OpenMPT/sounddev/SoundDevice.h 2014-02-14 21:04:24 UTC (rev 3714) @@ -376,6 +376,12 @@ int64 m_StreamPositionRenderFrames; int64 m_StreamPositionOutputFrames; + mutable LONG m_RequestFlags; +public: + static const LONG RequestFlagClose = 1<<0; + static const LONG RequestFlagReset = 1<<1; + static const LONG RequestFlagRestart = 1<<2; + protected: virtual void InternalFillAudioBuffer() = 0; @@ -387,6 +393,10 @@ void SourceAudioRead(void *buffer, std::size_t numFrames); void SourceAudioDone(std::size_t numFrames, int32 framesLatency); + void RequestClose() { _InterlockedOr(&m_RequestFlags, RequestFlagClose); } + void RequestReset() { _InterlockedOr(&m_RequestFlags, RequestFlagReset); } + void RequestRestart() { _InterlockedOr(&m_RequestFlags, RequestFlagRestart); } + void AudioSendMessage(const std::string &str); protected: @@ -408,6 +418,7 @@ virtual bool InternalOpen() = 0; virtual bool InternalStart() = 0; virtual void InternalStop() = 0; + virtual void InternalStopForce() { InternalStop(); } virtual bool InternalClose() = 0; public: @@ -430,11 +441,15 @@ bool Open(const SoundDeviceSettings &settings); bool Close(); bool Start(); - void Stop(); + void Stop(bool force = false); + LONG GetRequestFlags() const { return InterlockedExchangeAdd(&m_RequestFlags, 0); /* read */ } + bool IsOpen() const { return InternalIsOpen(); } bool IsPlaying() const { return m_IsPlaying; } + virtual bool OnIdle() { return false; } // return true if any work has been done + SoundDeviceSettings GetSettings() const { return m_Settings; } SampleFormat GetActualSampleFormat() const { return IsOpen() ? m_Settings.sampleFormat : SampleFormatInvalid; } Modified: trunk/OpenMPT/sounddev/SoundDeviceASIO.cpp =================================================================== --- trunk/OpenMPT/sounddev/SoundDeviceASIO.cpp 2014-02-14 20:06:12 UTC (rev 3713) +++ trunk/OpenMPT/sounddev/SoundDeviceASIO.cpp 2014-02-14 21:04:24 UTC (rev 3714) @@ -29,6 +29,9 @@ #ifndef NO_ASIO +static const double AsioSampleRateTolerance = 0.05; + + // Helper class to temporarily open a driver for a query. class TemporaryASIODriverOpener { @@ -177,6 +180,8 @@ : ISoundDevice(id, internalID) { Init(); + m_QueriedFeatures.reset(); + m_UsedFeatures.reset(); } @@ -202,11 +207,24 @@ InterlockedExchange(&m_RenderSilence, 0); InterlockedExchange(&m_RenderingSilence, 0); - m_QueriedFeatures.reset(); - m_UsedFeatures.reset(); + InterlockedExchange(&m_AsioRequestFlags, 0); } +bool CASIODevice::HandleRequests() +//-------------------------------- +{ + bool result = false; + LONG flags = InterlockedExchange(&m_AsioRequestFlags, 0); + if(flags & AsioRequestFlagLatenciesChanged) + { + UpdateLatency(); + result = true; + } + return result; +} + + CASIODevice::~CASIODevice() //------------------------- { @@ -421,29 +439,7 @@ m_StreamPositionOffset = m_nAsioBufferLen; - SoundBufferAttributes bufferAttributes; - long inputLatency = 0; - long outputLatency = 0; - try - { - asioCall(getLatencies(&inputLatency, &outputLatency)); - } catch(...) - { - // continue, failure is not fatal here - inputLatency = 0; - outputLatency = 0; - } - if(outputLatency >= (long)m_nAsioBufferLen) - { - bufferAttributes.Latency = (double)(outputLatency + m_nAsioBufferLen) / (double)m_Settings.Samplerate; // ASIO and OpenMPT semantics of 'latency' differ by one chunk/buffer - } else - { - // pointless value returned from asio driver, use a sane estimate - bufferAttributes.Latency = 2.0 * (double)m_nAsioBufferLen / (double)m_Settings.Samplerate; - } - bufferAttributes.UpdateInterval = (double)m_nAsioBufferLen / (double)m_Settings.Samplerate; - bufferAttributes.NumBuffers = 2; - UpdateBufferAttributes(bufferAttributes); + UpdateLatency(); return true; @@ -459,6 +455,35 @@ } +void CASIODevice::UpdateLatency() +//------------------------------- +{ + SoundBufferAttributes bufferAttributes; + long inputLatency = 0; + long outputLatency = 0; + try + { + asioCall(getLatencies(&inputLatency, &outputLatency)); + } catch(...) + { + // continue, failure is not fatal here + inputLatency = 0; + outputLatency = 0; + } + if(outputLatency >= (long)m_nAsioBufferLen) + { + bufferAttributes.Latency = (double)(outputLatency + m_nAsioBufferLen) / (double)m_Settings.Samplerate; // ASIO and OpenMPT semantics of 'latency' differ by one chunk/buffer + } else + { + // pointless value returned from asio driver, use a sane estimate + bufferAttributes.Latency = 2.0 * (double)m_nAsioBufferLen / (double)m_Settings.Samplerate; + } + bufferAttributes.UpdateInterval = (double)m_nAsioBufferLen / (double)m_Settings.Samplerate; + bufferAttributes.NumBuffers = 2; + UpdateBufferAttributes(bufferAttributes); +} + + void CASIODevice::SetRenderSilence(bool silence, bool wait) //--------------------------------------------------------- { @@ -527,12 +552,24 @@ } +void CASIODevice::InternalStopForce() +//----------------------------------- +{ + InternalStopImpl(true); +} + void CASIODevice::InternalStop() //------------------------------ { + InternalStopImpl(false); +} + +void CASIODevice::InternalStopImpl(bool force) +//-------------------------------------------- +{ ALWAYS_ASSERT_WARN_MESSAGE(!CriticalSection::IsLocked(), "AudioCriticalSection locked while stopping ASIO"); - if(m_Settings.KeepDeviceRunning) + if(m_Settings.KeepDeviceRunning && !force) { SetRenderSilence(true, true); return; @@ -1089,7 +1126,19 @@ void CASIODevice::SampleRateDidChange(ASIOSampleRate sRate) //--------------------------------------------------------- { - MPT_UNREFERENCED_PARAMETER(sRate); + if(Util::Round<uint32>(sRate) == m_Settings.Samplerate) + { + // not different, ignore it + return; + } + m_UsedFeatures.set(AsioFeatureSampleRateChange); + if((double)m_Settings.Samplerate * (1.0 - AsioSampleRateTolerance) <= sRate && sRate <= (double)m_Settings.Samplerate * (1.0 + AsioSampleRateTolerance)) + { + // ignore slight differences which might me due to a unstable external ASIO clock source + return; + } + // play safe and close the device + RequestClose(); } @@ -1104,6 +1153,7 @@ if(features[AsioFeatureBufferSizeChange]) { if(!first) { result += ","; } first = false; result += "buffer"; } if(features[AsioFeatureOverload]) { if(!first) { result += ","; } first = false; result += "load"; } if(features[AsioFeatureNoDirectProcess]) { if(!first) { result += ","; } first = false; result += "nodirect"; } + if(features[AsioFeatureSampleRateChange]) { if(!first) { result += ","; } first = false; result += "srate"; } return result; } @@ -1111,12 +1161,18 @@ std::string CASIODevice::GetStatistics() const //-------------------------------------------- { - if(m_UsedFeatures.any()) + const FlagSet<AsioFeatures> unsupported((AsioFeatures)(AsioFeatureNoDirectProcess | AsioFeatureOverload | AsioFeatureBufferSizeChange | AsioFeatureSampleRateChange)); + FlagSet<AsioFeatures> unsupportedFeatues = m_UsedFeatures; + unsupportedFeatues &= unsupported; + if(unsupportedFeatues.any()) { - return mpt::String::Print("WARNING: unsupported features used: %1", AsioFeaturesToString(m_UsedFeatures)); + return mpt::String::Print("WARNING: unsupported features: %1", AsioFeaturesToString(unsupportedFeatues)); + } else if(m_UsedFeatures.any()) + { + return mpt::String::Print("OK, features used: %1", AsioFeaturesToString(m_UsedFeatures)); } else if(m_QueriedFeatures.any()) { - return mpt::String::Print("features queried: %1", AsioFeaturesToString(m_QueriedFeatures)); + return mpt::String::Print("OK, features queried: %1", AsioFeaturesToString(m_QueriedFeatures)); } return std::string("OK."); } @@ -1140,23 +1196,19 @@ break; case kAsioResetRequest: m_QueriedFeatures.set(AsioFeatureResetRequest); - // unsupported - result = 0; + result = 1; break; case kAsioBufferSizeChange: m_QueriedFeatures.set(AsioFeatureBufferSizeChange); - // unsupported result = 0; break; case kAsioResyncRequest: m_QueriedFeatures.set(AsioFeatureResyncRequest); - // unsupported - result = 0; + result = 1; break; case kAsioLatenciesChanged: m_QueriedFeatures.set(AsioFeatureLatenciesChanged); - // unsupported - result = 0; + result = 1; break; case kAsioOverload: m_QueriedFeatures.set(AsioFeatureOverload); @@ -1177,23 +1229,23 @@ break; case kAsioResetRequest: m_UsedFeatures.set(AsioFeatureResetRequest); - // unsupported - result = 0; + RequestReset(); + result = 1; break; case kAsioBufferSizeChange: m_UsedFeatures.set(AsioFeatureBufferSizeChange); - // unsupported + // We do not support kAsioBufferSizeChange. + // This should cause a driver to send a kAsioResetRequest. result = 0; break; case kAsioResyncRequest: m_UsedFeatures.set(AsioFeatureResyncRequest); - // unsupported - result = 0; + RequestRestart(); + result = 1; break; case kAsioLatenciesChanged: - m_UsedFeatures.set(AsioFeatureLatenciesChanged); - // unsupported - result = 0; + _InterlockedOr(&m_AsioRequestFlags, AsioRequestFlagLatenciesChanged); + result = 1; break; case kAsioOverload: m_UsedFeatures.set(AsioFeatureOverload); Modified: trunk/OpenMPT/sounddev/SoundDeviceASIO.h =================================================================== --- trunk/OpenMPT/sounddev/SoundDeviceASIO.h 2014-02-14 20:06:12 UTC (rev 3713) +++ trunk/OpenMPT/sounddev/SoundDeviceASIO.h 2014-02-14 21:04:24 UTC (rev 3714) @@ -34,6 +34,7 @@ AsioFeatureBufferSizeChange = 1<<3, AsioFeatureOverload = 1<<4, AsioFeatureNoDirectProcess = 1<<5, + AsioFeatureSampleRateChange = 1<<6, AsioFeatureNone = 0 }; DECLARE_FLAGSET(AsioFeatures) @@ -68,6 +69,9 @@ int64 m_StreamPositionOffset; + static const LONG AsioRequestFlagLatenciesChanged = 1<<0; + LONG m_AsioRequestFlags; + FlagSet<AsioFeatures> m_QueriedFeatures; FlagSet<AsioFeatures> m_UsedFeatures; @@ -88,16 +92,24 @@ private: void Init(); + bool HandleRequests(); // return true if any work has been done + void UpdateLatency(); + void InternalStopImpl(bool force); + public: bool InternalOpen(); bool InternalClose(); void InternalFillAudioBuffer(); bool InternalStart(); void InternalStop(); + void InternalStopForce(); bool InternalIsOpen() const { return m_BuffersCreated; } + bool OnIdle() { return HandleRequests(); } + SoundDeviceCaps GetDeviceCaps(const std::vector<uint32> &baseSampleRates); + bool OpenDriverSettings(); std::string GetStatistics() const; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |