def _processQueue(self): if not self._queuedSpeech and self._player is None: # If oneCore speech has not been successful yet the player will not have initialised. (#11544) # We can't sync the player in this instance. log.debugWarning( "Cannot process speech queue as player not set and no speech queued" ) return if not self._queuedSpeech: # There are no more queued utterances at this point, so call sync. # This blocks while waiting for the final chunk to play, # so by the time this is done, there might be something queued. # #10721: We use sync instead of idle because idle closes the audio # device. If there's something in the queue after playing the final chunk, # that will result in WaveOutOpen being called in the callback when we # push the next chunk of audio. We *really* don't want this because calling # WaveOutOpen blocks for ~100 ms if called from the callback when the SSML # includes marks, resulting in lag between utterances. if isDebugForSynthDriver(): log.debug("Calling sync on audio player") self._player.sync() if not self._queuedSpeech: # There's still nothing in the queue, so it's okay to call idle now. if isDebugForSynthDriver(): log.debug("Calling idle on audio player") self._player.idle() synthDoneSpeaking.notify(synth=self) while self._queuedSpeech: item = self._queuedSpeech.pop(0) if isinstance(item, tuple): # Parameter change. # Note that, if prosody otions aren't supported, this code will never be executed. func, value = item value = ctypes.c_double(value) func(self._ocSpeechToken, value) continue self._wasCancelled = False if isDebugForSynthDriver(): log.debug("Begin processing speech") self._isProcessing = True # ocSpeech_speak is async. # It will call _callback in a background thread once done, # which will eventually process the queue again. self._dll.ocSpeech_speak(self._ocSpeechToken, item) return if isDebugForSynthDriver(): log.debug("Queue empty, done processing") self._isProcessing = False
def _callback(self, bytes, len, markers): if self._earlyExitCB: # prevent any pending callbacks from interacting further with the synth. # used during termination. return if len == 0: # Speech failed self._handleSpeechFailure() return else: self._consecutiveSpeechFailures = 0 # This gets called in a background thread. stream = io.BytesIO(ctypes.string_at(bytes, len)) wav = wave.open(stream, "r") self._maybeInitPlayer(wav) data = wav.readframes(wav.getnframes()) if markers: markers = markers.split('|') else: markers = [] prevPos = 0 # Push audio up to each marker so we can sync the audio with the markers. for marker in markers: if self._wasCancelled: break name, pos = marker.split(':') index = int(name) pos = int(pos) # pos is a time offset in 100-nanosecond units. # Convert this to a byte offset. # Order the equation so we don't have to do floating point. pos = pos * self._bytesPerSec // HUNDRED_NS_PER_SEC # Push audio up to this marker. self._player.feed(data[prevPos:pos], onDone=lambda index=index: synthIndexReached. notify(synth=self, index=index)) prevPos = pos if self._wasCancelled: if isDebugForSynthDriver(): log.debug("Cancelled, stopped pushing audio") else: self._player.feed(data[prevPos:]) if isDebugForSynthDriver(): log.debug("Done pushing audio") self._processQueue()
def _callback(self, bytes, len, markers): if len == 0: # The C++ code will log an error with details. log.debugWarning("ocSpeech_speak failed!") self._processQueue() return # This gets called in a background thread. stream = io.BytesIO(ctypes.string_at(bytes, len)) wav = wave.open(stream, "r") self._maybeInitPlayer(wav) data = wav.readframes(wav.getnframes()) if markers: markers = markers.split('|') else: markers = [] prevPos = 0 # Push audio up to each marker so we can sync the audio with the markers. for marker in markers: if self._wasCancelled: break name, pos = marker.split(':') index = int(name) pos = int(pos) # pos is a time offset in 100-nanosecond units. # Convert this to a byte offset. # Order the equation so we don't have to do floating point. pos = pos * self._bytesPerSec // HUNDRED_NS_PER_SEC # Push audio up to this marker. self._player.feed(data[prevPos:pos], onDone=lambda index=index: synthIndexReached. notify(synth=self, index=index)) prevPos = pos if self._wasCancelled: if isDebugForSynthDriver(): log.debug("Cancelled, stopped pushing audio") else: self._player.feed(data[prevPos:]) if isDebugForSynthDriver(): log.debug("Done pushing audio") self._processQueue()
def cancel(self): # Set a flag to tell the callback not to push more audio. self._wasCancelled = True if isDebugForSynthDriver(): log.debug("Cancelling") # There might be more text pending. Throw it away. if self.supportsProsodyOptions: # In this case however, we must keep any parameter changes. self._queuedSpeech = [item for item in self._queuedSpeech if not isinstance(item, str)] else: self._queuedSpeech = [] if self._player: self._player.stop()