From: <man...@us...> - 2013-04-03 10:06:46
|
Revision: 1714 http://sourceforge.net/p/modplug/code/1714 Author: manxorist Date: 2013-04-03 10:06:28 +0000 (Wed, 03 Apr 2013) Log Message: ----------- Merge branch snddev-fixes. Merged revision(s) 1606, 1609-1619, 1621, 1627-1628, 1633, 1635-1639, 1643-1648, 1651-1659, 1662-1664, 1670 from branches/manx/snddev-fixes: [Mod] Options Dialog: Changed "buffer length" value into "Latency" (real audio latency) and "Update Interval" (for GUI updates). [Fix] Pattern display no longer lags with high Wave Out latency values. [Ref] Remove SetLastMixActiveTime() which calculated stuff that was used nowhere. [Ref] Remove EnableLowLatencyMode() which did nothing for asio and was most of the time broken for dsound. [Ref] Remove the related nMaxLatency parameter in ISoundDevice::FillAudioBuffer. [Ref] Modify WaveOut code to refill the buffer more often than exactly 3 times. [Ref] Free WaveOut buffers as soon as they are not needed anymore. [Ref] Replace the old configuration variable BufferLength with 2 new ones: Latency=3*BufferLength (Latency=BufferLength for ASIO) and UpdaterInterval=BufferLength/8 which matches the old behaviour as close as possible. [Ref] Sanitize the wakeup intervals of the AudioThread. [Ref] Properly shut down the audio thread. [Ref] Add common/mutex.h with a general mutex implementation in the spirit of c++11. [Ref] Rewrite the NotificationBuffer to use proper syncronization primitives and a fixed size ring buffer to avoid a bunch of possible race conditions and other strange effects. [Ref] Count up the total rendered samples monotonically without ever resetting the counter. [Ref] Properly shut down the notification thread. [Ref] Remove a bunch of now-unused global variables. [Ref] Replace a bunch of m_dwStatus&MODSTATUS_PLAYING tests with IsPlaying(). [Mod] Show actual values used for the sound device in the options dialog. ........ [Ref] Add m_AudioThreadActive flag to signal when the audio thread should do rendering. [Ref] Remove redundant CMainFrame::m_dwStatus and associated MODSTATUS_PLAYING and MODSTATUS_RENDERING. Change the meaning of IsPlaying() while opening the sound device and adapt code to it. [Ref] Also remove redundant CMainFrame::m_pModPlaying. [Ref] Make a bunch of audio and notify thread related variables non-global. ........ [Fix] Fix crash in CMainFrame::PauseMod and CMainFrame::SetAudioThreadActive which was introduced by the CMainFrame::m_dwStatus removal commit. ........ [Ref] Remove CMainFrame::slSampleSize and pass around the amount of audio data in the unit of samples instead of bytes in the ISoundSource interface. [Ref] Change CSoundFile::Read to also take the buffer size in number of samples. The return value had already been in the unit of number of samples anyway. ........ [Ref] Remove pointless fallbacks in CMainFrame::audioOpenDevice when opening the audio device with the chosen settings did not work. ........ [Ref] Remove dwUser parameter in ISoundDevice::FillAudioBuffer. ........ [Ref] Move applying of TrackerSettings to m_pSndFile from function audioTryOpeningDevice, PlayMod and PlaySoundfile to a new function ApplyTrackerSettings. ........ [Fix] Oops, when introducing ApplyTrackerSettings in r1614, an unrelated change slipped in, revert it. ........ [Ref] Rewrite AudioThread to consume no CPU time at all when idle. Only increase the MMTIMER resolution when the thread is nott idling. Terminate it by using an event instead of WM_QUIT so this thread does not need a message queue at all. ........ [Ref] Remove unused m_RefCount in ISoundDevice. [Ref] Let CreateSoundDevice() return the interface pointer directly. ........ [Ref] Properly wait in SetAudioThreadActive(false) until the audio thread has really entered idle state and is not running any more. Stop the audio device right after that. This makes stopping audio output synchronous. [Ref] Fix a deadlock caused by this change in CModDoc::OnPlayerPlayFromStart() by moving AudioCriticalSection after the call to PauseMod() (every other PauseMod() call site does it this way anyway). ........ [Fix] Latency estimation for ASIO was wrong in the call to AudioDone. It used the wrong unit (in the old code it used Samples and the AudioDone() code expected bytes and now it used a meaningless Samples devided by samplesize. Pass on the correct value as samples now. Has been broken probably forever. Did probably not matter that much because ASIO latencies tend to be very small anyway. ........ [Ref] The audio thread should not, in any way, directly interact with the gui thread. When a audio stream reached its end, the audio thread did a PostMessage to the gui thread, simulating a click on the Stop button. Generate a notification instead and let the gui thread itself simulate the click. Refactor FillAudioBuffer and AudioDone to prapagate when end of stream is reached. [Ref] Wake up the audio thread only for non-ASIO devices. [Ref] Start() and Stop() non/ASIO devices directly from the audio thread. It knows best when it is ready to fill the buffer. ........ [Ref] Add ISoundDevice::HasGetStreamPosition() and ISoundDevice::GetStreamPositionSamples() to query the precise actual sample position that is currently being sent to the audio output. [Ref] Rework notification timestamp handling to optionally use this information for ISoundDevices that support it. This changes the meaning of MPTNOTIFICATION::TimestampSamples depending of whether the ISoundDevice implementation supports GetStreamPositionSamples() from latency compensated to not latency compensated timestamps. Also, clear the notification buffer and reset m_TotalSamplesRendered after the sound device has been closed. Notifications still pending at that point are completely meaningless anyway. [Ref] Implement GetStreamPositionSamples() for CWaveDevice. [Fix] Call AudioDone() more regularly when using Waveout and more than one buffer has to be filled. Prevents stuttering gui redraw in some situations (especially directly after stream start with high latencies). ........ [Fix] Actually starting waveout device in CWaveDevice::Start() makes sense now. ........ [Ref] Move old minimum and maximum values for buffer length into TrackerSettings.cpp. They are now only used for loading and sanitizing old configuration settings. ........ [Var] Add portaudio v19-20111121 source code. ........ [Var] Integrate portaudio into the VS2010 build process and document the related changes to portaudio in include/portaudio/OpenMPT.txt. ........ [Var] Use multiprocessor compilation for portaudio release builds with VS2010. ........ [Var] Add portaudio to VS2008 build process. ........ [New] Implement a new sound device class CPortaudioDevice. Use it to additionally provide WASAPI and WDM-KS audio output support. Add a NO_PORTAUDIO macro to optionally disable any portaudio code. Portaudio is linked as a delay loaded DLL and the availability of the DLL is checked before trying any portaudio functionality at all. This gives the user the ability to disable portaudio by just deleting or renaming the DLL in case something behaves strangely, e.g. during device enumeration. ........ [Fix] Initialize m_bMixRunning in CAsioDevice. [Var] Add debugging assertions to check for unbalanced Start() / Stop() in CAsioDevice. [Ref] Add CriticalSection::AssertUnlock() to assert that the current thread does not hold the audio ctricial section at a certain point. [Ref] Make audioOpenDevice() idempotent. Add audioReopenDevice(). [Ref] Rewrite and try to clean up the playing/not-playing state transitions in PlayMod(),PauseMod(),StopMod() and PlaySoundFile(). The basic idea is to pause the playback then fiddle around with m_pSndFile and then unpause the playback again. [Mod] In certain cases, when OpenMPT stops the sound device for presumably a short period of time, do not actually stop the asio device but switch it to render silence instead. Avoids anoying state changes with some asio drivers. [Ref] Document the MPTNOTIFY constants. ........ [Fix] Fix enumeration of portaudio device to actually really include only device supporting sound output. ........ [Mod] Got rid of pointless AGC reset after 15 seconds of silence. ........ [Reg] Do not pause playback while loading and/or generating previews. ........ [Ref] Remove redundant PausePlayback(). ........ [Ref] Add a SetSource() member do ISoundDevice to avoid needing a global function SoundDeviceCallback. [Ref] Add ISoundSource::FillAudioBufferLocked which should take all needed locks and then call back into CSoundDevice::FillAudioBuffer which in turn will call ISoundSource::AudioRead and ISoundSource::AudioDone. [Ref] Remove CMPTSoundSource and its global instance gMPTSoundSource and let CMainFrame directly implement ISoundSource ........ [Ref] Move audio thread implementation into its own class CAudioThread. ........ [Ref] Move CAudioThread to snddev.cpp ........ [Ref] Move CAudioThread handling completely out of CMainFrame and directly into the ISoundDevice implementations that need their own thread (Waveout and Directsound). This is implemented via CWaveDevice and CDSoundDevice deriving from a new CSoundDeviceWithThread. ........ [Ref] Remove unused ISoundDevice::DirectCallback() ........ [Ref] Remove unused DSoundDone() ........ [Ref] Pass a IFillAudioBuffer reference to CMainFrame::FillAudioBufferLocked so that it directly knows were to call back instead of relying on the state of a member variable. ........ [Ref] Remove unused BOOL audioFillBuffers(); ........ [Ref] Always generate mastervu, it's not that heavy anyway. Remove the coresponding MPTNOTIFY_MASTERVU and remove ApplyMixerHooks which did just enable and disable this one single hook. Now, a permanently visible VU meter could be added somewhere in the GUI. ........ [Mod] Disable WDM-KS support in portaudio again. The implementation in portaudio seems broken. ........ [Mod] Link portaudio statically and thereby disable the possibility to deactivate it by deleting the DLL. ........ [Var] Fix debug build of portaudio with VS2010. ........ [Fix] Improve handling of DSERR_BUFFERLOST when calling ->Lock() or ->Play(). ........ [Ref] More consistent usage of MemsetZero in Snddev.cpp ........ Revision Links: -------------- http://sourceforge.net/p/modplug/code/1614 Modified Paths: -------------- trunk/OpenMPT/common/AudioCriticalSection.cpp trunk/OpenMPT/common/AudioCriticalSection.h trunk/OpenMPT/common/misc_util.h trunk/OpenMPT/common/stdafx.h trunk/OpenMPT/mptrack/Ctrl_com.cpp trunk/OpenMPT/mptrack/Ctrl_gen.cpp trunk/OpenMPT/mptrack/InputHandler.h trunk/OpenMPT/mptrack/MPTRACK_08.sln trunk/OpenMPT/mptrack/MPTRACK_10.sln trunk/OpenMPT/mptrack/MainFrm.cpp trunk/OpenMPT/mptrack/Mainfrm.h trunk/OpenMPT/mptrack/Mod2wave.cpp trunk/OpenMPT/mptrack/Moddoc.cpp trunk/OpenMPT/mptrack/Mpdlgs.cpp trunk/OpenMPT/mptrack/Mpdlgs.h trunk/OpenMPT/mptrack/TrackerSettings.cpp trunk/OpenMPT/mptrack/TrackerSettings.h trunk/OpenMPT/mptrack/View_pat.cpp trunk/OpenMPT/mptrack/Vstplug.cpp trunk/OpenMPT/mptrack/mptrack.rc trunk/OpenMPT/mptrack/mptrack_08.vcproj trunk/OpenMPT/mptrack/mptrack_10.vcxproj trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters trunk/OpenMPT/mptrack/resource.h trunk/OpenMPT/soundlib/SNDDEV.H trunk/OpenMPT/soundlib/SNDDEVX.H trunk/OpenMPT/soundlib/Snddev.cpp trunk/OpenMPT/soundlib/Sndmix.cpp Added Paths: ----------- trunk/OpenMPT/common/mutex.h trunk/OpenMPT/include/portaudio/ Removed Paths: ------------- trunk/OpenMPT/common/mutex.h Property Changed: ---------------- trunk/OpenMPT/ trunk/OpenMPT/include/portaudio/bindings/ trunk/OpenMPT/include/portaudio/bindings/cpp/ trunk/OpenMPT/include/portaudio/bindings/cpp/bin/ trunk/OpenMPT/include/portaudio/bindings/cpp/build/ trunk/OpenMPT/include/portaudio/bindings/cpp/build/gnu/ trunk/OpenMPT/include/portaudio/bindings/cpp/build/vc6/ trunk/OpenMPT/include/portaudio/bindings/cpp/build/vc7/ trunk/OpenMPT/include/portaudio/bindings/cpp/build/vc7_1/ trunk/OpenMPT/include/portaudio/bindings/cpp/doc/ trunk/OpenMPT/include/portaudio/bindings/cpp/example/ trunk/OpenMPT/include/portaudio/bindings/cpp/include/ trunk/OpenMPT/include/portaudio/bindings/cpp/include/portaudiocpp/ trunk/OpenMPT/include/portaudio/bindings/cpp/lib/ trunk/OpenMPT/include/portaudio/bindings/cpp/source/ trunk/OpenMPT/include/portaudio/bindings/cpp/source/portaudiocpp/ trunk/OpenMPT/include/portaudio/build/ trunk/OpenMPT/include/portaudio/build/msvc/ trunk/OpenMPT/include/portaudio/build/scons/ trunk/OpenMPT/include/portaudio/cmake_support/ trunk/OpenMPT/include/portaudio/doc/ trunk/OpenMPT/include/portaudio/doc/html/ trunk/OpenMPT/include/portaudio/doc/src/ trunk/OpenMPT/include/portaudio/doc/src/images/ trunk/OpenMPT/include/portaudio/doc/utils/ trunk/OpenMPT/include/portaudio/examples/ trunk/OpenMPT/include/portaudio/include/ trunk/OpenMPT/include/portaudio/pablio/ trunk/OpenMPT/include/portaudio/qa/ trunk/OpenMPT/include/portaudio/qa/loopback/ trunk/OpenMPT/include/portaudio/qa/loopback/macosx/ trunk/OpenMPT/include/portaudio/qa/loopback/src/ trunk/OpenMPT/include/portaudio/src/ trunk/OpenMPT/include/portaudio/src/common/ trunk/OpenMPT/include/portaudio/src/hostapi/ trunk/OpenMPT/include/portaudio/src/hostapi/alsa/ trunk/OpenMPT/include/portaudio/src/hostapi/asihpi/ trunk/OpenMPT/include/portaudio/src/hostapi/asio/ trunk/OpenMPT/include/portaudio/src/hostapi/coreaudio/ trunk/OpenMPT/include/portaudio/src/hostapi/dsound/ trunk/OpenMPT/include/portaudio/src/hostapi/jack/ trunk/OpenMPT/include/portaudio/src/hostapi/oss/ trunk/OpenMPT/include/portaudio/src/hostapi/skeleton/ trunk/OpenMPT/include/portaudio/src/hostapi/wasapi/ trunk/OpenMPT/include/portaudio/src/hostapi/wasapi/mingw-include/ trunk/OpenMPT/include/portaudio/src/hostapi/wdmks/ trunk/OpenMPT/include/portaudio/src/hostapi/wmme/ trunk/OpenMPT/include/portaudio/src/os/ trunk/OpenMPT/include/portaudio/src/os/mac_osx/ trunk/OpenMPT/include/portaudio/src/os/unix/ trunk/OpenMPT/include/portaudio/src/os/win/ trunk/OpenMPT/include/portaudio/test/ trunk/OpenMPT/include/portaudio/testcvs/ Index: trunk/OpenMPT =================================================================== --- trunk/OpenMPT 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT 2013-04-03 10:06:28 UTC (rev 1714) Property changes on: trunk/OpenMPT ___________________________________________________________________ Modified: svn:mergeinfo ## -1,3 +1,4 ## /branches/manx/build-speedup:1586-1589 /branches/manx/header-dependencies-cleanups:1394-1397,1401-1402,1405-1406 /branches/manx/project-files-cleanups:1378-1382 +/branches/manx/snddev-fixes:1605-1713 \ No newline at end of property Modified: trunk/OpenMPT/common/AudioCriticalSection.cpp =================================================================== --- trunk/OpenMPT/common/AudioCriticalSection.cpp 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/common/AudioCriticalSection.cpp 2013-04-03 10:06:28 UTC (rev 1714) @@ -12,3 +12,7 @@ #include "../common/AudioCriticalSection.h" CRITICAL_SECTION g_csAudio; +#ifdef _DEBUG +int g_csAudioLockCount = 0; +#endif + Modified: trunk/OpenMPT/common/AudioCriticalSection.h =================================================================== --- trunk/OpenMPT/common/AudioCriticalSection.h 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/common/AudioCriticalSection.h 2013-04-03 10:06:28 UTC (rev 1714) @@ -12,6 +12,9 @@ #include <windows.h> extern CRITICAL_SECTION g_csAudio; +#ifdef _DEBUG +extern int g_csAudioLockCount; +#endif // Critical section handling done in (safe) RAII style. // Create a CriticalSection object whenever you need exclusive access to CSoundFile. @@ -23,8 +26,46 @@ protected: bool inSection; public: - CriticalSection() { inSection = false; Enter(); }; - ~CriticalSection() { Leave(); }; - void Enter() { if(!inSection) { inSection = true; EnterCriticalSection(&g_csAudio); } }; - void Leave() { if(inSection) { inSection = false; LeaveCriticalSection(&g_csAudio); } }; + CriticalSection() + { + inSection = false; + Enter(); + }; + void Enter() + { + if(!inSection) + { + inSection = true; + EnterCriticalSection(&g_csAudio); +#ifdef _DEBUG + g_csAudioLockCount++; +#endif + } + }; + void Leave() + { + if(inSection) + { + inSection = false; +#ifdef _DEBUG + g_csAudioLockCount--; +#endif + LeaveCriticalSection(&g_csAudio); + } + }; + ~CriticalSection() + { + Leave(); + }; + static void AssertUnlocked() + { + // asserts that the critical section is currently not hold by THIS thread +#ifdef _DEBUG + if(TryEnterCriticalSection(&g_csAudio)) + { + ASSERT(g_csAudioLockCount==0); + LeaveCriticalSection(&g_csAudio); + } +#endif + } }; Modified: trunk/OpenMPT/common/misc_util.h =================================================================== --- trunk/OpenMPT/common/misc_util.h 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/common/misc_util.h 2013-04-03 10:06:28 UTC (rev 1714) @@ -383,4 +383,71 @@ return static_cast<int32>( ( static_cast<int64>(a) * b + ( c / 2 ) ) / c ); } + template<typename T, std::size_t n> + class fixed_size_queue { + private: + T buffer[n+1]; + std::size_t read_position; + std::size_t write_position; + public: + fixed_size_queue() : read_position(0), write_position(0) { + return; + } + void clear() { + read_position = 0; + write_position = 0; + } + std::size_t read_size() const { + if ( write_position > read_position ) { + return write_position - read_position; + } else if ( write_position < read_position ) { + return write_position - read_position + n + 1; + } else { + return 0; + } + } + std::size_t write_size() const { + if ( write_position > read_position ) { + return read_position - write_position + n; + } else if ( write_position < read_position ) { + return read_position - write_position - 1; + } else { + return n; + } + } + bool push( const T & v ) { + if ( !write_size() ) { + return false; + } + buffer[write_position] = v; + write_position = ( write_position + 1 ) % ( n + 1 ); + return true; + } + bool pop() { + if ( !read_size() ) { + return false; + } + read_position = ( read_position + 1 ) % ( n + 1 ); + return true; + } + T peek() { + if ( !read_size() ) { + return T(); + } + return buffer[read_position]; + } + const T * peek_p() { + if ( !read_size() ) { + return nullptr; + } + return &(buffer[read_position]); + } + const T * peek_next_p() { + if ( read_size() < 2 ) { + return nullptr; + } + return &(buffer[(read_position+1)%(n+1)]); + } + }; + } // namespace Util Deleted: trunk/OpenMPT/common/mutex.h =================================================================== --- trunk/OpenMPT/common/mutex.h 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/common/mutex.h 2013-04-03 10:06:28 UTC (rev 1714) @@ -1,51 +0,0 @@ -/* - * mutex.h - * ------- - * Purpose: Partially implement c++ mutexes as far as openmpt needs them. Can eventually go away when we only support c++11 compilers some time. - * Notes : (currently none) - * Authors: OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - -#pragma once - -#define WIN32_LEAN_AND_MEAN -#define VC_EXTRALEAN -#define NOMINMAX -#include <windows.h> - -namespace Util { - -// compatible with c++11 std::mutex, can eventually be replaced without touching any usage site -class mutex { -private: - CRITICAL_SECTION impl; -public: - mutex() { InitializeCriticalSection(&impl); } - ~mutex() { DeleteCriticalSection(&impl); } - void lock() { EnterCriticalSection(&impl); } - void unlock() { LeaveCriticalSection(&impl); } -}; - -// compatible with c++11 std::recursive_mutex, can eventually be replaced without touching any usage site -class recursive_mutex { -private: - CRITICAL_SECTION impl; -public: - recursive_mutex() { InitializeCriticalSection(&impl); } - ~recursive_mutex() { DeleteCriticalSection(&impl); } - void lock() { EnterCriticalSection(&impl); } - void unlock() { LeaveCriticalSection(&impl); } -}; - -// compatible with c++11 std::lock_guard, can eventually be replaced without touching any usage site -template< typename mutex_type > -class lock_guard { -private: - mutex_type & mutex; -public: - lock_guard( mutex_type & m ) : mutex(m) { mutex.lock(); } - ~lock_guard() { mutex.unlock(); } -}; - -} // namespace Util Copied: trunk/OpenMPT/common/mutex.h (from rev 1713, branches/manx/snddev-fixes/common/mutex.h) =================================================================== --- trunk/OpenMPT/common/mutex.h (rev 0) +++ trunk/OpenMPT/common/mutex.h 2013-04-03 10:06:28 UTC (rev 1714) @@ -0,0 +1,51 @@ +/* + * mutex.h + * ------- + * Purpose: Partially implement c++ mutexes as far as openmpt needs them. Can eventually go away when we only support c++11 compilers some time. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define VC_EXTRALEAN +#define NOMINMAX +#include <windows.h> + +namespace Util { + +// compatible with c++11 std::mutex, can eventually be replaced without touching any usage site +class mutex { +private: + CRITICAL_SECTION impl; +public: + mutex() { InitializeCriticalSection(&impl); } + ~mutex() { DeleteCriticalSection(&impl); } + void lock() { EnterCriticalSection(&impl); } + void unlock() { LeaveCriticalSection(&impl); } +}; + +// compatible with c++11 std::recursive_mutex, can eventually be replaced without touching any usage site +class recursive_mutex { +private: + CRITICAL_SECTION impl; +public: + recursive_mutex() { InitializeCriticalSection(&impl); } + ~recursive_mutex() { DeleteCriticalSection(&impl); } + void lock() { EnterCriticalSection(&impl); } + void unlock() { LeaveCriticalSection(&impl); } +}; + +// compatible with c++11 std::lock_guard, can eventually be replaced without touching any usage site +template< typename mutex_type > +class lock_guard { +private: + mutex_type & mutex; +public: + lock_guard( mutex_type & m ) : mutex(m) { mutex.lock(); } + ~lock_guard() { mutex.unlock(); } +}; + +} // namespace Util Modified: trunk/OpenMPT/common/stdafx.h =================================================================== --- trunk/OpenMPT/common/stdafx.h 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/common/stdafx.h 2013-04-03 10:06:28 UTC (rev 1714) @@ -88,6 +88,9 @@ // (HACK) Define to build without VST support; makes build possible without VST SDK. //#define NO_VST +// Define to build without portaudio. +//#define NO_PORTAUDIO + // Define to build without MO3 support. //#define NO_MO3_SUPPORT Index: trunk/OpenMPT/include/portaudio =================================================================== --- branches/manx/snddev-fixes/include/portaudio 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/include/portaudio 2013-04-03 10:06:28 UTC (rev 1714) Property changes on: trunk/OpenMPT/include/portaudio ___________________________________________________________________ Added: tsvn:logminsize ## -0,0 +1 ## +10 \ No newline at end of property Modified: trunk/OpenMPT/mptrack/Ctrl_com.cpp =================================================================== --- trunk/OpenMPT/mptrack/Ctrl_com.cpp 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/mptrack/Ctrl_com.cpp 2013-04-03 10:06:28 UTC (rev 1714) @@ -52,7 +52,7 @@ void CCtrlComments::OnActivatePage(LPARAM) //---------------------------------------- { - CMainFrame::EnableLowLatencyMode(FALSE); + // nothing } Modified: trunk/OpenMPT/mptrack/Ctrl_gen.cpp =================================================================== --- trunk/OpenMPT/mptrack/Ctrl_gen.cpp 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/mptrack/Ctrl_gen.cpp 2013-04-03 10:06:28 UTC (rev 1714) @@ -145,9 +145,8 @@ //--------------------------------------- { CMainFrame *pMainFrm = CMainFrame::GetMainFrame(); - if (m_pModDoc) m_pModDoc->SetFollowWnd(m_hWnd, MPTNOTIFY_MASTERVU); - if (pMainFrm) pMainFrm->SetFollowSong(m_pModDoc, m_hWnd, TRUE, MPTNOTIFY_MASTERVU); - CMainFrame::EnableLowLatencyMode(FALSE); + if (m_pModDoc) m_pModDoc->SetFollowWnd(m_hWnd, MPTNOTIFY_DEFAULT); + if (pMainFrm) pMainFrm->SetFollowSong(m_pModDoc, m_hWnd, TRUE, MPTNOTIFY_DEFAULT); PostViewMessage(VIEWMSG_SETACTIVE, NULL); SetFocus(); @@ -528,15 +527,8 @@ MPTNOTIFICATION *pnotify = (MPTNOTIFICATION *)lParam; if (pnotify) { - if (pnotify->dwType & MPTNOTIFY_MASTERVU) - { - m_VuMeterLeft.SetVuMeter(pnotify->dwPos[0]); - m_VuMeterRight.SetVuMeter(pnotify->dwPos[1]); - } else - { - m_VuMeterLeft.SetVuMeter(0); - m_VuMeterRight.SetVuMeter(0); - } + m_VuMeterLeft.SetVuMeter(pnotify->MasterVuLeft); + m_VuMeterRight.SetVuMeter(pnotify->MasterVuRight); } return 0; } Modified: trunk/OpenMPT/mptrack/InputHandler.h =================================================================== --- trunk/OpenMPT/mptrack/InputHandler.h 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/mptrack/InputHandler.h 2013-04-03 10:06:28 UTC (rev 1714) @@ -14,6 +14,7 @@ enum { WM_MOD_UPDATEPOSITION = (WM_USER+1973), + WM_MOD_UPDATEPOSITIONTHREADED, WM_MOD_INVALIDATEPATTERNS, WM_MOD_ACTIVATEVIEW, WM_MOD_CHANGEVIEWCLASS, Modified: trunk/OpenMPT/mptrack/MPTRACK_08.sln =================================================================== --- trunk/OpenMPT/mptrack/MPTRACK_08.sln 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/mptrack/MPTRACK_08.sln 2013-04-03 10:06:28 UTC (rev 1714) @@ -7,6 +7,7 @@ {DCC2BB2F-6778-4FD3-9C00-D6CD8DC917B8} = {DCC2BB2F-6778-4FD3-9C00-D6CD8DC917B8} {FAE39936-1DC7-40BB-AD3F-3B5B9E9AB0E8} = {FAE39936-1DC7-40BB-AD3F-3B5B9E9AB0E8} {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8} = {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8} + {0A18A071-125E-442F-AFF7-A3F68ABECF99} = {0A18A071-125E-442F-AFF7-A3F68ABECF99} {4CEFBC84-C215-11DB-8314-0800200C9A66} = {4CEFBC84-C215-11DB-8314-0800200C9A66} {CF3C2CA5-5D45-4635-BBA4-C1F435E10896} = {CF3C2CA5-5D45-4635-BBA4-C1F435E10896} {FF541CE2-DAA1-4F84-9883-0A0F111BAA0B} = {FF541CE2-DAA1-4F84-9883-0A0F111BAA0B} @@ -34,6 +35,8 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libFLAC_static", "..\include\flac\src\libFLAC\libFLAC_static_08.vcproj", "{4CEFBC84-C215-11DB-8314-0800200C9A66}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "portaudio", "..\include\portaudio\build\msvc\portaudio_openmpt_vs2008.vcproj", "{0A18A071-125E-442F-AFF7-A3F68ABECF99}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -76,6 +79,10 @@ {4CEFBC84-C215-11DB-8314-0800200C9A66}.Debug|Win32.Build.0 = Debug|Win32 {4CEFBC84-C215-11DB-8314-0800200C9A66}.Release|Win32.ActiveCfg = Release|Win32 {4CEFBC84-C215-11DB-8314-0800200C9A66}.Release|Win32.Build.0 = Release|Win32 + {0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|Win32.ActiveCfg = Debug|Win32 + {0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|Win32.Build.0 = Debug|Win32 + {0A18A071-125E-442F-AFF7-A3F68ABECF99}.Release|Win32.ActiveCfg = Release|Win32 + {0A18A071-125E-442F-AFF7-A3F68ABECF99}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE Modified: trunk/OpenMPT/mptrack/MPTRACK_10.sln =================================================================== --- trunk/OpenMPT/mptrack/MPTRACK_10.sln 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/mptrack/MPTRACK_10.sln 2013-04-03 10:06:28 UTC (rev 1714) @@ -21,6 +21,8 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unzip", "..\unzip\unzip_10.vcxproj", "{F23CC68D-1D58-4EB1-9425-A28F5058EB31}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "portaudio", "..\include\portaudio\build\msvc\portaudio_openmpt_vs2010.vcxproj", "{0A18A071-125E-442F-AFF7-A3F68ABECF99}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -82,6 +84,12 @@ {F23CC68D-1D58-4EB1-9425-A28F5058EB31}.Release|Win32.Build.0 = Release|Win32 {F23CC68D-1D58-4EB1-9425-A28F5058EB31}.ReleaseNoLTCG|Win32.ActiveCfg = Release|Win32 {F23CC68D-1D58-4EB1-9425-A28F5058EB31}.ReleaseNoLTCG|Win32.Build.0 = Release|Win32 + {0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|Win32.ActiveCfg = Debug|Win32 + {0A18A071-125E-442F-AFF7-A3F68ABECF99}.Debug|Win32.Build.0 = Debug|Win32 + {0A18A071-125E-442F-AFF7-A3F68ABECF99}.Release|Win32.ActiveCfg = Release|Win32 + {0A18A071-125E-442F-AFF7-A3F68ABECF99}.Release|Win32.Build.0 = Release|Win32 + {0A18A071-125E-442F-AFF7-A3F68ABECF99}.ReleaseNoLTCG|Win32.ActiveCfg = Release|Win32 + {0A18A071-125E-442F-AFF7-A3F68ABECF99}.ReleaseNoLTCG|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE Modified: trunk/OpenMPT/mptrack/MainFrm.cpp =================================================================== --- trunk/OpenMPT/mptrack/MainFrm.cpp 2013-04-02 20:18:42 UTC (rev 1713) +++ trunk/OpenMPT/mptrack/MainFrm.cpp 2013-04-03 10:06:28 UTC (rev 1714) @@ -45,19 +45,6 @@ #define MPTTIMER_PERIOD 200 -//======================================== -class CMPTSoundSource: public ISoundSource -//======================================== -{ -public: - CMPTSoundSource() {} - ULONG AudioRead(PVOID pData, ULONG cbSize); - VOID AudioDone(ULONG dwSize, ULONG dwLatency); -}; - - -CMPTSoundSource gMPTSoundSource; - ///////////////////////////////////////////////////////////////////////////// // CMainFrame @@ -99,6 +86,7 @@ ON_UPDATE_COMMAND_UI(ID_INDICATOR_CPU, OnUpdateCPU) ON_UPDATE_COMMAND_UI(IDD_TREEVIEW, OnUpdateControlBarMenu) ON_MESSAGE(WM_MOD_UPDATEPOSITION, OnUpdatePosition) + ON_MESSAGE(WM_MOD_UPDATEPOSITIONTHREADED, OnUpdatePositionThreaded) ON_MESSAGE(WM_MOD_INVALIDATEPATTERNS, OnInvalidatePatterns) ON_MESSAGE(WM_MOD_SPECIALKEY, OnSpecialKey) ON_MESSAGE(WM_MOD_KEYCOMMAND, OnCustomKeyMsg) //rewbs.customKeys @@ -112,31 +100,13 @@ ON_WM_SHOWWINDOW() END_MESSAGE_MAP() -// Static -static int gdwLastLowLatencyTime = 0; -static int gdwLastMixActiveTime = 0; -static DWORD gsdwTotalSamples = 0; -static DWORD gdwPlayLatency = 0; - // Globals -DWORD CMainFrame::gdwNotificationType = MPTNOTIFY_DEFAULT; UINT CMainFrame::m_nLastOptionsPage = 0; HHOOK CMainFrame::ghKbdHook = NULL; std::vector<CString> CMainFrame::s_ExampleModulePaths; std::vector<CString> CMainFrame::s_TemplateModulePaths; -HANDLE CMainFrame::m_hPlayThread = NULL; -DWORD CMainFrame::m_dwPlayThreadId = 0; -HANDLE CMainFrame::m_hAudioWakeUp = NULL; -HANDLE CMainFrame::m_hNotifyThread = NULL; -DWORD CMainFrame::m_dwNotifyThreadId = 0; -HANDLE CMainFrame::m_hNotifyWakeUp = NULL; -ISoundDevice *CMainFrame::gpSoundDevice = NULL; -LONG CMainFrame::slSampleSize = 2; -LONG CMainFrame::sdwSamplesPerSec = 44100; -LONG CMainFrame::sdwAudioBufferSize = MAX_AUDIO_BUFFERSIZE; -UINT CMainFrame::gdwIdleTime = 0; LONG CMainFrame::gnLVuMeter = 0; LONG CMainFrame::gnRVuMeter = 0; @@ -200,23 +170,31 @@ ID_INDICATOR_CPU }; + ///////////////////////////////////////////////////////////////////////////// // CMainFrame construction/destruction //#include <direct.h> CMainFrame::CMainFrame() //---------------------- { + + m_hNotifyThread = NULL; + m_dwNotifyThreadId = 0; + m_hNotifyWakeUp = NULL; + gpSoundDevice = NULL; + m_IsPlaybackRunning = false; + m_bModTreeHasFocus = false; //rewbs.customKeys m_pNoteMapHasFocus = nullptr; //rewbs.customKeys m_pOrderlistHasFocus = nullptr; m_bOptionsLocked = false; //rewbs.customKeys + m_SoundCardOptionsDialog = nullptr; + m_pJustModifiedDoc = nullptr; - m_pModPlaying = nullptr; m_hFollowSong = NULL; m_hWndMidi = NULL; m_pSndFile = nullptr; - m_dwStatus = 0; m_dwTimeSec = 0; m_dwNotifyType = 0; m_nTimer = 0; @@ -225,6 +203,9 @@ m_szInfoText[0] = 0; m_szXInfoText[0]= 0; //rewbs.xinfo + m_TotalSamplesRendered = 0; + m_PendingNotificationSempahore = NULL; + MemsetZero(gpenVuMeter); // Create Audio Critical Section @@ -262,6 +243,8 @@ SetTitle(title); OnUpdateFrameTitle(false); + CSoundFile::gpSndMixHook = CalcStereoVuMeters; + // Check for valid sound device if (!EnumerateSoundDevices(SNDDEV_GET_TYPE(TrackerSettings::Instance().m_nWaveDevice), SNDDEV_GET_NUMBER(TrackerSettings::Instance().m_nWaveDevice), nullptr, 0)) { @@ -272,11 +255,10 @@ } } - // Create Audio Thread - m_hAudioWakeUp = CreateEvent(NULL, FALSE, FALSE, NULL); + // Create Notify Thread + m_PendingNotificationSempahore = CreateSemaphore(NULL, 0, 1, NULL); m_hNotifyWakeUp = CreateEvent(NULL, FALSE, FALSE, NULL); - m_hPlayThread = CreateThread(NULL, 0, AudioThread, NULL, 0, &m_dwPlayThreadId); - m_hNotifyThread = CreateThread(NULL, 0, NotifyThread, NULL, 0, &m_dwNotifyThreadId); + m_hNotifyThread = CreateThread(NULL, 0, NotifyThreadWrapper, NULL, 0, &m_dwNotifyThreadId); // Setup timer OnUpdateUser(NULL); m_nTimer = SetTimer(1, MPTTIMER_PERIOD, NULL); @@ -395,6 +377,8 @@ BOOL CMainFrame::DestroyWindow() //------------------------------ { + CSoundFile::gpSndMixHook = nullptr; + SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); // Uninstall Keyboard Hook if (ghKbdHook) @@ -409,14 +393,23 @@ m_nTimer = 0; } if (shMidiIn) midiCloseDevice(); - if (m_hPlayThread != NULL) + if(m_hNotifyThread != NULL) { - if(TerminateThread(m_hPlayThread, 0)) m_hPlayThread = NULL; + PostThreadMessage(m_dwNotifyThreadId, WM_QUIT, 0, 0); + WaitForSingleObject(m_hNotifyThread, INFINITE); + m_dwNotifyThreadId = 0; + m_hNotifyThread = NULL; } - if (m_hNotifyThread != NULL) + if(m_hNotifyWakeUp != NULL) { - if(TerminateThread(m_hNotifyThread, 0)) m_hNotifyThread = NULL; + CloseHandle(m_hNotifyWakeUp); + m_hNotifyWakeUp = NULL; } + if(m_PendingNotificationSempahore != NULL) + { + CloseHandle(m_PendingNotificationSempahore); + m_PendingNotificationSempahore = NULL; + } // Delete bitmaps if (bmpPatterns) { @@ -492,14 +485,14 @@ CChildFrame *pMDIActive = (CChildFrame *)MDIGetActive(); BeginWaitCursor(); - if (m_dwStatus & MODSTATUS_PLAYING) PauseMod(); + if (IsPlaying()) PauseMod(); if (pMDIActive) pMDIActive->SavePosition(TRUE); if (gpSoundDevice) { CriticalSection cs; //gpSoundDevice->Reset(); //audioCloseDevice(); - gpSoundDevice->Release(); + delete gpSoundDevice; gpSoundDevice = NULL; } // Save Settings @@ -667,214 +660,147 @@ ///////////////////////////////////////////////////////////////////////////// // CMainFrame Sound Library -static BOOL gbStopSent = FALSE; - -// Sound Device Callback -BOOL SoundDeviceCallback(DWORD dwUser) -//------------------------------------ +// Notify thread +DWORD WINAPI CMainFrame::NotifyThreadWrapper(LPVOID) { - BOOL bOk = FALSE; - CMainFrame *pMainFrm = (CMainFrame *)theApp.m_pMainWnd; - if (gbStopSent) return FALSE; - CriticalSection cs; - if ((pMainFrm) && (pMainFrm->IsPlaying()) && (CMainFrame::gpSoundDevice)) - { - bOk = CMainFrame::gpSoundDevice->FillAudioBuffer(&gMPTSoundSource, gdwPlayLatency, dwUser); - } - if (!bOk) - { - gbStopSent = TRUE; - pMainFrm->PostMessage(WM_COMMAND, ID_PLAYER_STOP); - } - return bOk; + return ((CMainFrame*)theApp.m_pMainWnd)->NotifyThread(); } - - -// Audio thread -DWORD WINAPI CMainFrame::AudioThread(LPVOID) -//------------------------------------------ +DWORD CMainFrame::NotifyThread() +//------------------------------------------- { - CMainFrame *pMainFrm; - BOOL bWait; - UINT nSleep; - -// -> CODE#0021 -// -> DESC="use multimedia timer instead of Sleep() in audio thread" - HANDLE sleepEvent = CreateEvent(NULL,TRUE,FALSE,NULL); -// -! BEHAVIOUR_CHANGE#0021 - - bWait = TRUE; - nSleep = 50; -// -> CODE#0021 -// -> DESC="use multimedia timer instead of Sleep() in audio thread" -//rewbs: reduce to normal priority during debug for easier hang debugging + // initialize thread message queue + MSG msg; + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); #ifdef NDEBUG - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL ); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL ); // we shall not stall the audio thread while holding m_NotificationBufferMutex #endif -#ifdef _DEBUG - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); -#endif -// SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); -// -! BEHAVIOUR_CHANGE#0021 - for (;;) + bool terminate = false; + bool cansend = true; + while(!terminate) { - if (bWait) + switch(MsgWaitForMultipleObjects(1, &m_hNotifyWakeUp, FALSE, 1000, QS_ALLEVENTS)) { - WaitForSingleObject(CMainFrame::m_hAudioWakeUp, 250); - } else - { -// -> CODE#0021 -// -> DESC="use multimedia timer instead of Sleep() in audio thread" -// Sleep(nSleep); - timeSetEvent(nSleep,1,(LPTIMECALLBACK)sleepEvent,NULL,TIME_ONESHOT | TIME_CALLBACK_EVENT_SET); - WaitForSingleObject(sleepEvent,nSleep); - ResetEvent(sleepEvent); -// -! BEHAVIOUR_CHANGE#0021 - } - bWait = TRUE; - pMainFrm = (CMainFrame *)theApp.m_pMainWnd; - CriticalSection cs; - if ((!gbStopSent) && (pMainFrm) && (pMainFrm->IsPlaying()) && (CMainFrame::gpSoundDevice)) - { - if (!CMainFrame::gpSoundDevice->Directcallback()) + case WAIT_OBJECT_0 + 1: { - if (CMainFrame::gpSoundDevice->FillAudioBuffer(&gMPTSoundSource, gdwPlayLatency)) + while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - ULONG nMaxSleep = CMainFrame::gpSoundDevice->GetMaxFillInterval(); - bWait = FALSE; - nSleep = TrackerSettings::Instance().m_nBufferLength / 8; - if (nSleep > nMaxSleep) nSleep = nMaxSleep; - if (nSleep < 10) nSleep = 10; - if (nSleep > 40) nSleep = 40; - } else - { - gbStopSent = TRUE; - pMainFrm->PostMessage(WM_COMMAND, ID_PLAYER_STOP); + if(msg.message == WM_QUIT) terminate = true; } - } else - { - nSleep = 50; } - } - } - -// -> CODE#0021 -// -> DESC="use multimedia timer instead of Sleep() in audio thread" - // Commented as this caused "warning C4702: unreachable code" - //CloseHandle(sleepEvent); -// -! BEHAVIOUR_CHANGE#0021 - - // Commented the two lines below as those caused "warning C4702: unreachable code" - //ExitThread(0); - //return 0; -} - - -// Notify thread -DWORD WINAPI CMainFrame::NotifyThread(LPVOID) -//------------------------------------------- -{ - CMainFrame *pMainFrm; - - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); - for (;;) - { - WaitForSingleObject(CMainFrame::m_hNotifyWakeUp, 1000); - pMainFrm = (CMainFrame *)theApp.m_pMainWnd; - if ((pMainFrm) && (pMainFrm->IsPlaying())) - { - MPTNOTIFICATION *pnotify = NULL; - DWORD dwLatency = 0; - - for (UINT i=0; i<MAX_UPDATE_HISTORY; i++) - { - MPTNOTIFICATION *p = &pMainFrm->NotifyBuffer[i]; - if ((p->dwType & MPTNOTIFY_PENDING) - && (!(pMainFrm->m_dwStatus & MODSTATUS_BUSY))) + break; + case WAIT_OBJECT_0: { - if (p->dwLatency >= dwLatency) + const MPTNOTIFICATION * pnotify = nullptr; { - if (pnotify) pnotify->dwType = 0; - pnotify = p; - } else + int64 currenttotalsamples = 0; + Util::lock_guard<Util::mutex> lock(m_NotificationBufferMutex); + if(gpSoundDevice && gpSoundDevice->HasGetStreamPosition()) + { + currenttotalsamples = gpSoundDevice->GetStreamPositionSamples(); + } else + { + currenttotalsamples = m_TotalSamplesRendered; + } + // advance to the newest notification, drop the obsolete ones + const MPTNOTIFICATION * p = m_NotifyBuffer.peek_p(); + if(p && currenttotalsamples >= p->TimestampSamples) + { + pnotify = p; + while(m_NotifyBuffer.peek_next_p() && currenttotalsamples >= m_NotifyBuffer.peek_next_p()->TimestampSamples) + { + m_NotifyBuffer.pop(); + p = m_NotifyBuffer.peek_p(); + pnotify = p; + } + } + } + if(pnotify) { - p->dwType = 0; + if(!cansend) + { + // poll the semaphore instead of waiting directly after sending the message to avoid deadlocks on termination of openmpt or when stopping audio rendering + if(WaitForSingleObject(m_PendingNotificationSempahore, 0) == WAIT_OBJECT_0) + { + // last notification has been handled by gui thread, so we can pop the notify buffer + cansend = true; + } + } + if(cansend) + { + m_PendingNotification = *pnotify; // copy notification so that we can free the buffer + { + Util::lock_guard<Util::mutex> lock(m_NotificationBufferMutex); + m_NotifyBuffer.pop(); + } + if(PostMessage(WM_MOD_UPDATEPOSITIONTHREADED, 0, 0)) + { + cansend = false; + } + } } } - } - if (pnotify) - { - pMainFrm->m_dwStatus |= MODSTATUS_BUSY; - pMainFrm->PostMessage(WM_MOD_UPDATEPOSITION, 0, (LPARAM)pnotify); - } + break; } } - // Commented the two lines below as those caused "warning C4702: unreachable code" - //ExitThread(0); - //return 0; + return 0; } - - -ULONG CMPTSoundSource::AudioRead(PVOID pData, ULONG cbSize) -//--------------------------------------------------------- +void CMainFrame::SetAudioThreadActive(bool active) +//------------------------------------------------ { - CMainFrame *pMainFrm = (CMainFrame *)theApp.m_pMainWnd; - if (pMainFrm) return pMainFrm->AudioRead(pData, cbSize); - return 0; + if(active) + { + if(m_IsPlaybackRunning) return; + m_IsPlaybackRunning = true; + gpSoundDevice->Start(); + } else + { + if(!m_IsPlaybackRunning) return; + gpSoundDevice->Stop(); + m_IsPlaybackRunning = false; + } } -VOID CMPTSoundSource::AudioDone(ULONG nBytesWritten, ULONG nLatency) -//------------------------------------------------------------------ +void CMainFrame::FillAudioBufferLocked(IFillAudioBuffer &callback) +//---------------------------------------------------------------- { - CMainFrame *pMainFrm = (CMainFrame *)theApp.m_pMainWnd; - if (pMainFrm) pMainFrm->AudioDone(nBytesWritten, nLatency); + CriticalSection cs; + callback.FillAudioBuffer(); } - -ULONG CMainFrame::AudioRead(PVOID pvData, ULONG ulSize) -//----------------------------------------------------- +ULONG CMainFrame::AudioRead(PVOID pvData, ULONG MaxSamples) +//--------------------------------------------------------- { - if ((IsPlaying()) && (m_pSndFile)) - { - DWORD dwSamplesRead = m_pSndFile->Read(pvData, ulSize); + DWORD dwSamplesRead = m_pSndFile->Read(pvData, MaxSamples); //m_dTotalCPU = m_pPerfCounter->StartStop()/(static_cast<double>(dwSamplesRead)/m_dwRate); - return dwSamplesRead * slSampleSize; - } - return 0; + return dwSamplesRead; } -VOID CMainFrame::AudioDone(ULONG nBytesWritten, ULONG nLatency) -//------------------------------------------------------------- +void CMainFrame::AudioDone(ULONG SamplesWritten, ULONG SamplesLatency, bool end_of_stream) +//---------------------------------------------------------------------------------------- { - if (nBytesWritten > (DWORD)slSampleSize) + if (SamplesWritten > 0) { - DoNotification(nBytesWritten/CMainFrame::slSampleSize, nLatency); + DoNotification(SamplesWritten, SamplesLatency, end_of_stream); } } -LONG CMainFrame::audioTryOpeningDevice(UINT channels, UINT bits, UINT samplespersec) +bool CMainFrame::audioTryOpeningDevice(UINT channels, UINT bits, UINT samplespersec) //---------------------------------------------------------------------------------- { WAVEFORMATEXTENSIBLE WaveFormat; - UINT buflen = TrackerSettings::Instance().m_nBufferLength; - if (!m_pSndFile) return -1; - slSampleSize = (bits/8) * channels; - sdwAudioBufferSize = ((samplespersec * buflen) / 1000) * slSampleSize; - sdwAudioBufferSize = (sdwAudioBufferSize + 0x0F) & ~0x0F; - if (sdwAudioBufferSize < MIN_AUDIO_BUFFERSIZE) sdwAudioBufferSize = MIN_AUDIO_BUFFERSIZE; - if (sdwAudioBufferSize > MAX_AUDIO_BUFFERSIZE) sdwAudioBufferSize = MAX_AUDIO_BUFFERSIZE; + UINT bytespersample = (bits/8) * channels; WaveFormat.Format.wFormatTag = WAVE_FORMAT_PCM; WaveFormat.Format.nChannels = (unsigned short) channels; WaveFormat.Format.nSamplesPerSec = samplespersec; - WaveFormat.Format.nAvgBytesPerSec = samplespersec * slSampleSize; - WaveFormat.Format.nBlockAlign = (unsigned short) slSampleSize; + WaveFormat.Format.nAvgBytesPerSec = samplespersec * bytespersample; + WaveFormat.Format.nBlockAlign = (unsigned short)bytespersample; WaveFormat.Format.wBitsPerSample = (unsigned short)bits; WaveFormat.Format.cbSize = 0; // MultiChannel configuration @@ -890,79 +816,74 @@ case 2: WaveFormat.dwChannelMask = 0x0003; break; // FRONT_LEFT | FRONT_RIGHT case 3: WaveFormat.dwChannelMask = 0x0103; break; // FRONT_LEFT|FRONT_RIGHT|BACK_CENTER case 4: WaveFormat.dwChannelMask = 0x0033; break; // FRONT_LEFT|FRONT_RIGHT|BACK_LEFT|BACK_RIGHT - default: WaveFormat.dwChannelMask = 0; break; + default: WaveFormat.dwChannelMask = 0; return false; break; } WaveFormat.SubFormat = guid_MEDIASUBTYPE_PCM; } - if (TrackerSettings::Instance().m_dwSoundSetup & SOUNDSETUP_STREVERSE) CSoundFile::gdwSoundSetup |= SNDMIX_REVERSESTEREO; - else CSoundFile::gdwSoundSetup &= ~SNDMIX_REVERSESTEREO; - m_pSndFile->SetWaveConfig(samplespersec, bits, channels, (TrackerSettings::Instance().m_dwSoundSetup & SOUNDSETUP_ENABLEMMX) ? TRUE : FALSE); - // Maybe we failed because someone is playing sound already. - // Shut any sound off, and try once more before giving up. UINT nDevType = SNDDEV_GET_TYPE(TrackerSettings::Instance().m_nWaveDevice); - UINT nDevNo = SNDDEV_GET_NUMBER(TrackerSettings::Instance().m_nWaveDevice); - UINT fulOptions = (TrackerSettings::Instance().m_dwSoundSetup & SOUNDSETUP_SECONDARY) ? SNDDEV_OPTIONS_SECONDARY : 0; if ((gpSoundDevice) && (gpSoundDevice->GetDeviceType() != nDevType)) { - gpSoundDevice->Release(); + delete gpSoundDevice; gpSoundDevice = NULL; } - if (!gpSoundDevice) - { - if (!CreateSoundDevice(nDevType, &gpSoundDevice)) return -1; - } - gpSoundDevice->Configure(m_hWnd, NUM_AUDIO_BUFFERS, TrackerSettings::Instance().m_nBufferLength, fulOptions); - gbStopSent = FALSE; - m_pSndFile->SetResamplingMode(TrackerSettings::Instance().m_nSrcMode); - UpdateDspEffects(); -#ifndef NO_AGC - m_pSndFile->SetAGC(TrackerSettings::Instance().m_dwQuality & QUALITY_AGC); -#endif - if (!gpSoundDevice->Open(nDevNo, &WaveFormat.Format)) return -1; - return 0; + if(!gpSoundDevice) gpSoundDevice = CreateSoundDevice(nDevType); + if(!gpSoundDevice) return false; + gpSoundDevice->SetSource(this); + gpSoundDevice->Configure(m_hWnd, TrackerSettings::Instance().m_LatencyMS, TrackerSettings::Instance().m_UpdateIntervalMS, (TrackerSettings::Instance().m_dwSoundSetup & SOUNDSETUP_SECONDARY) ? SNDDEV_OPTIONS_SECONDARY : 0); + if (!gpSoundDevice->Open(SNDDEV_GET_NUMBER(TrackerSettings::Instance().m_nWaveDevice), &WaveFormat.Format)) return false; + return true; } -BOOL CMainFrame::audioOpenDevice() +bool CMainFrame::IsAudioDeviceOpen() const +//---------------------------------------- +{ + return gpSoundDevice && gpSoundDevice->IsOpen(); +} + + +bool CMainFrame::audioOpenDevice() //-------------------------------- { + if(IsAudioDeviceOpen()) return true; UINT nFixedBitsPerSample; - LONG err; + bool err = false; - if ((!m_pSndFile) || (!m_pSndFile->GetType())) return FALSE; - if (m_dwStatus & MODSTATUS_PLAYING) return TRUE; - if (!TrackerSettings::Instance().m_dwRate) TrackerSettings::Instance().m_dwRate = 22050; - if ((TrackerSettings::Instance().m_nChannels != 1) && (TrackerSettings::Instance().m_nChannels != 2) && (TrackerSettings::Instance().m_nChannels != 4)) TrackerSettings::Instance().m_nChannels = 2; - err = audioTryOpeningDevice(TrackerSettings::Instance().m_nChannels, + if (!TrackerSettings::Instance().m_dwRate) err = true; + if ((TrackerSettings::Instance().m_nChannels != 1) && (TrackerSettings::Instance().m_nChannels != 2) && (TrackerSettings::Instance().m_nChannels != 4)) err = true; + if(!err) + { + err = !audioTryOpeningDevice(TrackerSettings::Instance().m_nChannels, TrackerSettings::Instance().m_nBitsPerSample, TrackerSettings::Instance().m_dwRate); - nFixedBitsPerSample = (gpSoundDevice) ? gpSoundDevice->HasFixedBitsPerSample() : 0; - if ((err) && ((TrackerSettings::Instance().m_dwRate > 44100) || (TrackerSettings::Instance().m_nChannels > 2) || (TrackerSettings::Instance().m_nBitsPerSample > 16) - || ((nFixedBitsPerSample) && (nFixedBitsPerSample != TrackerSettings::Instance().m_nBitsPerSample)))) - { - DWORD oldrate = TrackerSettings::Instance().m_dwRate; - - TrackerSettings::Instance().m_dwRate = 44100; - if (TrackerSettings::Instance().m_nChannels > 2) TrackerSettings::Instance().m_nChannels = 2; - if (nFixedBitsPerSample) TrackerSettings::Instance().m_nBitsPerSample = nFixedBitsPerSample; - else if (TrackerSettings::Instance().m_nBitsPerSample > 16) TrackerSettings::Instance().m_nBitsPerSample = 16; - err = audioTryOpeningDevice(TrackerSettings::Instance().m_nChannels, + nFixedBitsPerSample = (gpSoundDevice) ? gpSoundDevice->HasFixedBitsPerSample() : 0; + if(err && (nFixedBitsPerSample && (nFixedBitsPerSample != TrackerSettings::Instance().m_nBitsPerSample))) + { + if(nFixedBitsPerSample) TrackerSettings::Instance().m_nBitsPerSample = nFixedBitsPerSample; + err = !audioTryOpeningDevice(TrackerSettings::Instance().m_nChannels, TrackerSettings::Instance().m_nBitsPerSample, TrackerSettings::Instance().m_dwRate); - if (err) TrackerSettings::Instance().m_dwRate = oldrate; + } } // Display error message box - if (err != 0) + if(err) { Reporting::Error("Unable to open sound device!"); - return FALSE; + return false; } // Device is ready - gdwLastMixActiveTime = timeGetTime(); - return TRUE; + return true; } +bool CMainFrame::audioReopenDevice() +//---------------------------------- +{ + audioCloseDevice(); + return audioOpenDevice(); +} + + void CMainFrame::audioCloseDevice() //--------------------------------- { @@ -972,6 +893,13 @@ gpSoundDevice->Reset(); gpSoundDevice->Close(); + + // reset notify buffer as timestamps revert here + { + Util::lock_guard<Util::mutex> lock(m_NotificationBufferMutex); + m_NotifyBuffer.clear(); + m_TotalSamplesRendered = 0; + } } } @@ -1007,36 +935,38 @@ } -BOOL CMainFrame::DoNotification(DWORD dwSamplesRead, DWORD dwLatency) +BOOL CMainFrame::DoNotification(DWORD dwSamplesRead, DWORD SamplesLatency, bool end_of_stream) //------------------------------------------------------------------- { - gsdwTotalSamples += dwSamplesRead; + int64 notificationtimestamp = 0; + { + Util::lock_guard<Util::mutex> lock(m_NotificationBufferMutex); // protect m_TotalSamplesRendered + m_TotalSamplesRendered += dwSamplesRead; + if(gpSoundDevice->HasGetStreamPosition()) + { + notificationtimestamp = m_TotalSamplesRendered; + } else + { + notificationtimestamp = m_TotalSamplesRendered + SamplesLatency; + } + } if (!m_pSndFile) return FALSE; if (m_nMixChn < m_pSndFile->m_nMixStat) m_nMixChn++; if (m_nMixChn > m_pSndFile->m_nMixStat) m_nMixChn--; if (!(m_dwNotifyType & MPTNOTIFY_TYPEMASK)) return FALSE; // Notify Client - for (UINT i=0; i<MAX_UPDATE_HISTORY; i++) + //if(m_NotifyBuffer.read_size() > 0) { - MPTNOTIFICATION *p = &NotifyBuffer[i]; - if ((p->dwType & MPTNOTIFY_TYPEMASK) - && (!(p->dwType & MPTNOTIFY_PENDING)) - && (gsdwTotalSamples >= p->dwLatency)) - { - p->dwType |= MPTNOTIFY_PENDING; - SetEvent(m_hNotifyWakeUp); - } + SetEvent(m_hNotifyWakeUp); } - if (!m_pSndFile) return FALSE; // Add an entry to the notification history - for (UINT j=0; j<MAX_UPDATE_HISTORY; j++) - { - MPTNOTIFICATION *p = &NotifyBuffer[j]; - if (!(p->dwType & MPTNOTIFY_TYPEMASK)) - { - p->dwType = m_dwNotifyType; - DWORD d = dwLatency / slSampleSize; - p->dwLatency = gsdwTotalSamples + d; + + MPTNOTIFICATION notification; + MemsetZero(notification); + MPTNOTIFICATION *p = ¬ification; + + p->dwType = m_dwNotifyType | (end_of_stream ? MPTNOTIFY_EOS : 0); + p->TimestampSamples = notificationtimestamp; p->nOrder = m_pSndFile->m_nCurrentOrder; p->nRow = m_pSndFile->m_nRow; p->nPattern = m_pSndFile->m_nPattern; @@ -1099,23 +1029,29 @@ UINT vur = pChn->nRightVU; p->dwPos[k] = (vul << 8) | (vur); } - } else if (m_dwNotifyType & MPTNOTIFY_MASTERVU) + } { DWORD lVu = (gnLVuMeter >> 11); DWORD rVu = (gnRVuMeter >> 11); if (lVu > 0x10000) lVu = 0x10000; if (rVu > 0x10000) rVu = 0x10000; - p->dwPos[0] = lVu; - p->dwPos[1] = rVu; + p->MasterVuLeft = lVu; + p->MasterVuRight = rVu; DWORD dwVuDecay = Util::muldiv(dwSamplesRead, 120000, TrackerSettings::Instance().m_dwRate) + 1; if (lVu >= dwVuDecay) gnLVuMeter = (lVu - dwVuDecay) << 11; else gnLVuMeter = 0; if (rVu >= dwVuDecay) gnRVuMeter = (rVu - dwVuDecay) << 11; else gnRVuMeter = 0; } - return TRUE; - } + + { + Util::lock_guard<Util::mutex> lock(m_NotificationBufferMutex); + if(m_NotifyBuffer.write_size() == 0) return FALSE; // drop notification + m_NotifyBuffer.push(notification); } - return FALSE; + SetEvent(m_hNotifyWakeUp); + + return TRUE; + } @@ -1183,14 +1119,6 @@ } -void CMainFrame::EnableLowLatencyMode(BOOL bOn) -//--------------------------------------------- -{ - gdwPlayLatency = (bOn) ? sdwAudioBufferSize : 0; - gdwLastLowLatencyTime = timeGetTime(); -} - - ///////////////////////////////////////////////////////////////////////////// // CMainFrame diagnostics @@ -1316,115 +1244,95 @@ } -BOOL CMainFrame::ResetNotificationBuffer(HWND hwnd) -//------------------------------------------------- +void CMainFrame::ResetNotificationBuffer() +//---------------------------------------- { - if ((!hwnd) || (m_hFollowSong == hwnd)) - { - MemsetZero(NotifyBuffer); - gsdwTotalSamples = 0; - return TRUE; - } - return FALSE; + Util::lock_guard<Util::mutex> lock(m_NotificationBufferMutex); + m_NotifyBuffer.clear(); } -BOOL CMainFrame::PlayMod(CModDoc *pModDoc, HWND hPat, DWORD dwNotifyType) -//----------------------------------------------------------------------- +void CMainFrame::ApplyTrackerSettings(CSoundFile *pSndFile) +//---------------------------------------------------------- { - if (!pModDoc) return FALSE; - CSoundFile *pSndFile = pModDoc->GetSoundFile(); - if ((!pSndFile) || (!pSndFile->GetType())) return FALSE; - const bool bPaused = pSndFile->IsPaused(); - const bool bPatLoop = pSndFile->m_SongFlags[SONG_PATTERNLOOP]; - pSndFile->ResetChannels(); - // Select correct bidi loop mode when playing a module. - pSndFile->SetupITBidiMode(); - - if(m_pSndFile != nullptr || (m_dwStatus & MODSTATUS_PLAYING)) PauseMod(); - if(m_pSndFile != nullptr && (pSndFile != m_pSndFile || !m_pSndFile->GetTotalSampleCount())) - { + if(!pSndFile) return; + if (TrackerSettings::Instance().m_dwSoundSetup & SOUNDSETUP_STREVERSE) CSoundFile::gdwSoundSetup |= SNDMIX_REVERSESTEREO; + else CSoundFile::gdwSoundSetup &= ~SNDMIX_REVERSESTEREO; + pSndFile->SetWaveConfig(TrackerSettings::Instance().m_dwRate, TrackerSettings::Instance().m_nBitsPerSample, TrackerSettings::Instance().m_nChannels, (TrackerSettings::Instance().m_dwSoundSetup & SOUNDSETUP_ENABLEMMX) ? TRUE : FALSE); + pSndFile->SetResamplingMode(TrackerSettings::Instance().m_nSrcMode); + UpdateDspEffects(); #ifndef NO_AGC - CSoundFile::m_AGC.Reset(); + pSndFile->SetAGC(TrackerSettings::Instance().m_dwQuality & QUALITY_AGC); #endif - } - m_pSndFile = pSndFile; - m_pModPlaying = pModDoc; - m_hFollowSong = hPat; - m_dwNotifyType = dwNotifyType; - if (m_dwNotifyType & MPTNOTIFY_MASTERVU) - { - gnLVuMeter = gnRVuMeter = 0; - CSoundFile::gpSndMixHook = CalcStereoVuMeters; - } else - { - CSoundFile::gpSndMixHook = NULL; - } + pSndFile->SetMasterVolume(TrackerSettings::Instance().m_nPreAmp, true); + pSndFile->SetMixerSettings(TrackerSettings::Instance().m_MixerSettings); +#ifndef NO_AGC + CSoundFile::m_AGC.Reset(); +#endif +} - if (!audioOpenDevice()) - { - m_pSndFile = NULL; - m_pModPlaying = NULL; - m_hFollowSong = NULL; - return FALSE; - } - m_nMixChn = m_nAvgMixChn = 0; - gsdwTotalSamples = 0; - if (!bPatLoop) - { - if (bPaused) - { - pSndFile->m_SongFlags.set(SONG_PAUSED); - } - } - pSndFile->SetRepeatCount((TrackerSettings::Instance().gbLoopSong) ? -1 : 0); - m_pSndFile->SetMasterVolume(TrackerSettings::Instance().m_nPreAmp, true); - m_pSndFile->SetMixerSettings(TrackerSettings::Instance().m_MixerSettings); - m_pSndFile->InitPlayer(TRUE); - MemsetZero(NotifyBuffer); - m_dwStatus |= MODSTATUS_PLAYING; - m_wndToolBar.SetCurrentSong(m_pSndFile); - if (gpSoundDevice) gpSoundDevice->Start(); - SetEvent(m_hAudioWakeUp); - return TRUE; +bool CMainFrame::PreparePlayback() +//-------------------------------- +{ + // open the audio device to update needed TrackerSettings mixer parameters + if(!audioOpenDevice()) return false; + return true; } -BOOL CMainFrame::PauseMod(CModDoc *pModDoc) +bool CMainFrame::StartPlayback() +//------------------------------ +{ + if(!m_pSndFile) return false; // nothing to play + if(!IsAudioDeviceOpen()) return false; + SetAudioThreadActive(true); + return true; +} + + +void CMainFrame::StopPlayback() +//----------------------------- +{ + if(!IsAudioDeviceOpen()) return; + SetAudioThreadActive(false); + audioCloseDevice(); +} + + +bool CMainFrame::PausePlayback() +//------------------------------ +{ + if(!IsAudioDeviceOpen()) return false; + SetAudioThreadActive(false); + return true; +} + + +void CMainFrame::GenerateStopNotification() //----------------------------------------- { - if ((pModDoc) && (pModDoc != m_pModPlaying)) return FALSE; - if (m_dwStatus & MODSTATUS_PLAYING) - { - m_dwStatus &= ~MODSTATUS_PLAYING; + MPTNOTIFICATION mn; + MemsetZero(mn); + mn.dwType = MPTNOTIFY_STOP; + SendMessage(WM_MOD_UPDATEPOSITION, 0, (LPARAM)&mn); +} - if (gpSoundDevice) gpSoundDevice->Reset(); - audioCloseDevice(); +void CMainFrame::UnsetPlaybackSoundFile() +//--------------------------------------- +{ + if(m_pSndFile) + { + m_pSndFile->SuspendPlugins(); + m_nMixChn = 0; + m_nAvgMixChn = 0; + if(m_pSndFile->GetpModDoc()) { - CriticalSection cs; - m_pSndFile->SuspendPlugins(); + m_wndTree.UpdatePlayPos(m_pSndFile->GetpModDoc(), NULL); } - - m_nMixChn = m_nAvgMixChn = 0; - Sleep(1); - if (m_hFollowSong) - { - MPTNOTIFICATION mn; - MemsetZero(mn); - mn.dwType = MPTNOTIFY_STOP; - ::SendMessage(m_hFollowSong, WM_MOD_UPDATEPOSITION, 0, (LPARAM)&mn); - } - } - if (m_pModPlaying) - { - m_wndTree.UpdatePlayPos(m_pModPlaying, NULL); - } - if (m_pSndFile) - { m_pSndFile->m_SongFlags.reset(SONG_PAUSED); - if (m_pSndFile == &m_WaveFile) + if(m_pSndFile == &m_WaveFile) { // Unload previewed instrument m_WaveFile.Destroy(); @@ -1440,45 +1348,165 @@ } } } - - m_pModPlaying = nullptr; m_pSndFile = nullptr; - m_hFollowSong = nullptr; - m_wndToolBar.SetCurrentSong(NULL); - return TRUE; + m_hFollowSong = NULL; + m_wndToolBar.SetCurrentSong(nullptr); + { + Util::lock_guard<Util::mutex> lock(m_NotificationBufferMutex); + m_NotifyBuffer.clear(); + } } -BOOL CMainFrame::StopMod(CModDoc *pModDoc) +void CMainFrame::SetPlaybackSoundFile(CSoundFile *pSndFile, HWND hPat, DWORD dwNotifyType) +//---------------------------------------------------------------------------------------- +{ + m_pSndFile = pSndFile; + m_hFollowSong = hPat; + m_dwNotifyType = dwNotifyType; +} + + +bool CMainFrame::PlayMod(CModDoc *pModDoc, HWND hPat, DWORD dwNotifyType) +//----------------------------------------------------------------------- +{ + CriticalSection::AssertUnlocked(); + if(!pModDoc) return false; + CSoundFile *pSndFile = pModDoc->GetSoundFile(); + if(!IsValidSoundFile(pSndFile)) return false; + + // if something is playing, pause it + PausePlayback(); + GenerateStopNotification(); + + UnsetPlaybackSoundFile(); + + // open audio device if not already open + if (!PreparePlayback()) return false; + + // set mixing parameters in CSoundFile + ApplyTrackerSettings(pSndFile); + + SetPlaybackSoundFile(pSndFile, hPat, dwNotifyType); + + const bool bPaused = m_pSndFile->IsPaused(); + const bool bPatLoop = m_pSndFile->m_SongFlags[SONG_PATTERNLOOP]; + + m_pSndFile... [truncated message content] |