1818import config
1919from logHandler import log
2020import os .path
21+ import core
2122
2223__all__ = (
2324 "WavePlayer" , "getOutputDeviceNames" , "outputDeviceIDToName" , "outputDeviceNameToID" ,
@@ -100,6 +101,9 @@ class WavePlayer(object):
100101 """
101102 #: Minimum length of buffer (in ms) before audio is played.
102103 MIN_BUFFER_MS = 300
104+ #: The time (in ms) to wait after L{idle} is called before closing the audio
105+ #: device. This is only applicable if L{closeWhenIdle} is C{True}.
106+ IDLE_CLOSE_DELAY_MS = 10000
103107 #: Flag used to signal that L{stop} has been called.
104108 STOPPING = "stopping"
105109 #: 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,
141145 #: If C{True}, close the output device when no audio is being played.
142146 #: @type: bool
143147 self .closeWhenIdle = closeWhenIdle
148+ self ._closeTimer = None
144149 if buffered :
145150 #: Minimum size of the buffer before audio is played.
146151 #: However, this is ignored if an C{onDone} callback is provided to L{feed}.
@@ -158,13 +163,21 @@ def __init__(self, channels, samplesPerSec, bitsPerSample,
158163 self ._lock = threading .RLock ()
159164 self .open ()
160165
166+ def _callOnMainThread (self , func , * args ):
167+ if threading .get_ident () == core .mainThreadId :
168+ func (* args )
169+ else :
170+ wx .CallAfter (func , * args )
171+
161172 def open (self ):
162173 """Open the output device.
163174 This will be called automatically when required.
164175 It is not an error if the output device is already open.
165176 """
166177 with self ._waveout_lock :
167178 if self ._waveout :
179+ if self ._closeTimer and self ._closeTimer .IsRunning ():
180+ self ._callOnMainThread (self ._closeTimer .Stop )
168181 return
169182 wfx = WAVEFORMATEX ()
170183 wfx .wFormatTag = WAVE_FORMAT_PCM
@@ -269,7 +282,8 @@ def pause(self, switch):
269282 def idle (self ):
270283 """Indicate that this player is now idle; i.e. the current continuous segment of audio is complete.
271284 This will first call L{sync} to synchronise with playback.
272- If L{closeWhenIdle} is C{True}, the output device will be closed.
285+ If L{closeWhenIdle} is C{True}, the output device will be closed if there
286+ is no further audio within L{IDLE_CLOSE_DELAY_MS}.
273287 A subsequent call to L{feed} will reopen it.
274288 """
275289 if not self ._minBufferSize :
@@ -286,7 +300,13 @@ def _idleUnbuffered(self):
286300 if not self ._waveout :
287301 return
288302 if self .closeWhenIdle :
289- self ._close ()
303+ if not self ._closeTimer :
304+ # We need a cancellable timer, so we can't use core.callLater.
305+ self ._closeTimer = wx .PyTimer (self ._close )
306+ self ._callOnMainThread (
307+ self ._closeTimer .Start , self .IDLE_CLOSE_DELAY_MS ,
308+ wx .TIMER_ONE_SHOT
309+ )
290310 if self ._audioDucker : self ._audioDucker .disable ()
291311
292312 def stop (self ):
0 commit comments