From af853ea2d520b7eb6edcca729d0045b28b482ee8 Mon Sep 17 00:00:00 2001 From: James Teh Date: Fri, 17 Apr 2020 21:49:37 +1000 Subject: [PATCH] nvwave: Don't close the audio device unless there hasn't been any audio played for 10 seconds. --- source/nvwave.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/source/nvwave.py b/source/nvwave.py index 56d88bfdf65..c1330b101a3 100644 --- a/source/nvwave.py +++ b/source/nvwave.py @@ -18,6 +18,7 @@ import config from logHandler import log import os.path +import core __all__ = ( "WavePlayer", "getOutputDeviceNames", "outputDeviceIDToName", "outputDeviceNameToID", @@ -100,6 +101,9 @@ class WavePlayer(object): """ #: Minimum length of buffer (in ms) before audio is played. MIN_BUFFER_MS = 300 + #: The time (in ms) to wait after L{idle} is called before closing the audio + #: device. This is only applicable if L{closeWhenIdle} is C{True}. + IDLE_CLOSE_DELAY_MS = 10000 #: Flag used to signal that L{stop} has been called. STOPPING = "stopping" #: A lock to prevent WaveOut* functions from being called simultaneously, as this can cause problems even if they are for different HWAVEOUTs. @@ -141,6 +145,7 @@ def __init__(self, channels, samplesPerSec, bitsPerSample, #: If C{True}, close the output device when no audio is being played. #: @type: bool self.closeWhenIdle = closeWhenIdle + self._closeTimer = None if buffered: #: Minimum size of the buffer before audio is played. #: However, this is ignored if an C{onDone} callback is provided to L{feed}. @@ -158,6 +163,12 @@ def __init__(self, channels, samplesPerSec, bitsPerSample, self._lock = threading.RLock() self.open() + def _callOnMainThread(self, func, *args): + if threading.get_ident() == core.mainThreadId: + func(*args) + else: + wx.CallAfter(func, *args) + def open(self): """Open the output device. This will be called automatically when required. @@ -165,6 +176,8 @@ def open(self): """ with self._waveout_lock: if self._waveout: + if self._closeTimer and self._closeTimer.IsRunning(): + self._callOnMainThread(self._closeTimer.Stop) return wfx = WAVEFORMATEX() wfx.wFormatTag = WAVE_FORMAT_PCM @@ -269,7 +282,8 @@ def pause(self, switch): def idle(self): """Indicate that this player is now idle; i.e. the current continuous segment of audio is complete. This will first call L{sync} to synchronise with playback. - If L{closeWhenIdle} is C{True}, the output device will be closed. + If L{closeWhenIdle} is C{True}, the output device will be closed if there + is no further audio within L{IDLE_CLOSE_DELAY_MS}. A subsequent call to L{feed} will reopen it. """ if not self._minBufferSize: @@ -286,7 +300,13 @@ def _idleUnbuffered(self): if not self._waveout: return if self.closeWhenIdle: - self._close() + if not self._closeTimer: + # We need a cancellable timer, so we can't use core.callLater. + self._closeTimer = wx.PyTimer(self._close) + self._callOnMainThread( + self._closeTimer.Start, self.IDLE_CLOSE_DELAY_MS, + wx.TIMER_ONE_SHOT + ) if self._audioDucker: self._audioDucker.disable() def stop(self):