Exemplo n.º 1
0
	def __init__(self, channels, samplesPerSec, bitsPerSample, outputDevice=WAVE_MAPPER, closeWhenIdle=True,wantDucking=True):
		"""Constructor.
		@param channels: The number of channels of audio; e.g. 2 for stereo, 1 for mono.
		@type channels: int
		@param samplesPerSec: Samples per second (hz).
		@type samplesPerSec: int
		@param bitsPerSample: The number of bits per sample.
		@type bitsPerSample: int
		@param outputDevice: The device ID or name of the audio output device to use.
		@type outputDevice: int or basestring
		@param closeWhenIdle: If C{True}, close the output device when no audio is being played.
		@type closeWhenIdle: bool
		@param wantDucking: if true then background audio will be ducked on Windows 8 and higher
		@type wantDucking: bool
		@note: If C{outputDevice} is a name and no such device exists, the default device will be used.
		@raise WindowsError: If there was an error opening the audio output device.
		"""
		self.channels=channels
		self.samplesPerSec=samplesPerSec
		self.bitsPerSample=bitsPerSample
		if isinstance(outputDevice, basestring):
			outputDevice = outputDeviceNameToID(outputDevice, True)
		self.outputDeviceID = outputDevice
		if wantDucking:
			import audioDucking
			if audioDucking.isAudioDuckingSupported():
				self._audioDucker=audioDucking.AudioDucker()
		#: If C{True}, close the output device when no audio is being played.
		#: @type: bool
		self.closeWhenIdle = closeWhenIdle
		self._waveout = None
		self._waveout_event = winKernel.kernel32.CreateEventW(None, False, False, None)
		self._waveout_lock = threading.RLock()
		self._lock = threading.RLock()
		self.open()
Exemplo n.º 2
0
    def __init__(self,
                 channels,
                 samplesPerSec,
                 bitsPerSample,
                 outputDevice=WAVE_MAPPER,
                 closeWhenIdle=True,
                 wantDucking=True,
                 buffered=False):
        """Constructor.
		@param channels: The number of channels of audio; e.g. 2 for stereo, 1 for mono.
		@type channels: int
		@param samplesPerSec: Samples per second (hz).
		@type samplesPerSec: int
		@param bitsPerSample: The number of bits per sample.
		@type bitsPerSample: int
		@param outputDevice: The device ID or name of the audio output device to use.
		@type outputDevice: int or str
		@param closeWhenIdle: If C{True}, close the output device when no audio is being played.
		@type closeWhenIdle: bool
		@param wantDucking: if true then background audio will be ducked on Windows 8 and higher
		@type wantDucking: bool
		@param buffered: Whether to buffer small chunks of audio to prevent audio glitches.
		@type buffered: bool
		@note: If C{outputDevice} is a name and no such device exists, the default device will be used.
		@raise WindowsError: If there was an error opening the audio output device.
		"""
        self.channels = channels
        self.samplesPerSec = samplesPerSec
        self.bitsPerSample = bitsPerSample
        if isinstance(outputDevice, str):
            outputDevice = outputDeviceNameToID(outputDevice, True)
        self.outputDeviceID = outputDevice
        if wantDucking:
            import audioDucking
            if audioDucking.isAudioDuckingSupported():
                self._audioDucker = audioDucking.AudioDucker()
        #: If C{True}, close the output device when no audio is being played.
        #: @type: bool
        self.closeWhenIdle = closeWhenIdle
        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}.
            BITS_PER_BYTE = 8
            MS_PER_SEC = 1000
            self._minBufferSize = samplesPerSec * channels * (
                bitsPerSample /
                BITS_PER_BYTE) / MS_PER_SEC * self.MIN_BUFFER_MS
            self._buffer = b""
        else:
            self._minBufferSize = None
        #: Function to call when the previous chunk of audio has finished playing.
        self._prevOnDone = None
        self._waveout = None
        self._waveout_event = winKernel.kernel32.CreateEventW(
            None, False, False, None)
        self._waveout_lock = threading.RLock()
        self._lock = threading.RLock()
        self.open()
Exemplo n.º 3
0
	def __init__(self,_defaultVoiceToken=None):
		"""
		@param _defaultVoiceToken: an optional sapi voice token which should be used as the default voice (only useful for subclasses)
		@type _defaultVoiceToken: ISpeechObjectToken
		"""
		if audioDucking.isAudioDuckingSupported():
			self._audioDucker = audioDucking.AudioDucker()
		self._pitch=50
		self._initTts(_defaultVoiceToken)
Exemplo n.º 4
0
def waveOutOpen(pWaveOutHandle,deviceID,wfx,callback,callbackInstance,flags):
	try:
		res=windll.winmm.waveOutOpen(pWaveOutHandle,deviceID,wfx,callback,callbackInstance,flags) or 0
	except WindowsError as e:
		res=e.winerror
	if res==0 and pWaveOutHandle:
		h=pWaveOutHandle.contents.value
		d=audioDucking.AudioDucker()
		d.enable()
		_duckersByHandle[h]=d
	return res
Exemplo n.º 5
0
def waveOutOpen(pWaveOutHandle,deviceID,wfx,callback,callbackInstance,flags):
	if audioDucking._isDebug():
		log.debugWarning("Ducking audio requested for SAPI5 synthdriver")
	try:
		res=windll.winmm.waveOutOpen(pWaveOutHandle,deviceID,wfx,callback,callbackInstance,flags) or 0
	except WindowsError as e:
		res=e.winerror
	if res==0 and pWaveOutHandle:
		h=pWaveOutHandle.contents.value
		d=audioDucking.AudioDucker()
		if not d.enable():
			log.warning("Ducking audio failed for SAPI5 synthdriver")
		_duckersByHandle[h]=d
	else:
		log.warning("Opening wave out failed for SAPI5 synthdriver")
		log.debugWarning(f"Win Error: {res}\n WaveOutHandle: {pWaveOutHandle}")
	return res
Exemplo n.º 6
0
	def speak(self, speechSequence):
		textList = []

		# NVDA SpeechCommands are linear, but XML is hierarchical.
		# Therefore, we track values for non-empty tags.
		# When a tag changes, we close all previously opened tags and open new ones.
		tags = {}
		# We have to use something mutable here because it needs to be changed by the inner function.
		tagsChanged = [True]
		openedTags = []
		def outputTags():
			if not tagsChanged[0]:
				return
			for tag in reversed(openedTags):
				textList.append("</%s>" % tag)
			del openedTags[:]
			for tag, attrs in tags.items():
				textList.append("<%s" % tag)
				for attr, val in attrs.items():
					textList.append(' %s="%s"' % (attr, val))
				textList.append(">")
				openedTags.append(tag)
			tagsChanged[0] = False

		pitch = self._pitch
		# Pitch must always be specified in the markup.
		tags["pitch"] = {"absmiddle": self._percentToPitch(pitch)}
		rate = self.rate
		volume = self.volume

		for item in speechSequence:
			if isinstance(item, str):
				outputTags()
				textList.append(item.replace("<", "&lt;"))
			elif isinstance(item, IndexCommand):
				textList.append('<Bookmark Mark="%d" />' % item.index)
			elif isinstance(item, CharacterModeCommand):
				if item.state:
					tags["spell"] = {}
				else:
					try:
						del tags["spell"]
					except KeyError:
						pass
				tagsChanged[0] = True
			elif isinstance(item, BreakCommand):
				textList.append('<silence msec="%d" />' % item.time)
			elif isinstance(item, PitchCommand):
				tags["pitch"] = {"absmiddle": self._percentToPitch(int(pitch * item.multiplier))}
				tagsChanged[0] = True
			elif isinstance(item, VolumeCommand):
				if item.multiplier == 1:
					try:
						del tags["volume"]
					except KeyError:
						pass
				else:
					tags["volume"] = {"level": int(volume * item.multiplier)}
				tagsChanged[0] = True
			elif isinstance(item, RateCommand):
				if item.multiplier == 1:
					try:
						del tags["rate"]
					except KeyError:
						pass
				else:
					tags["rate"] = {"absspeed": self._percentToRate(int(rate * item.multiplier))}
				tagsChanged[0] = True
			elif isinstance(item, PhonemeCommand):
				try:
					textList.append(u'<pron sym="%s">%s</pron>'
						% (self._convertPhoneme(item.ipa), item.text or u""))
				except LookupError:
					log.debugWarning("Couldn't convert character in IPA string: %s" % item.ipa)
					if item.text:
						textList.append(item.text)
			elif isinstance(item, SpeechCommand):
				log.debugWarning("Unsupported speech command: %s" % item)
			else:
				log.error("Unknown speech: %s" % item)
		# Close any tags that are still open.
		tags.clear()
		tagsChanged[0] = True
		outputTags()

		text = "".join(textList)
		flags = SpeechVoiceSpeakFlags.IsXML | SpeechVoiceSpeakFlags.Async
		# Ducking should be complete before the synth starts producing audio.
		# For this to happen, the speech method must block until ducking is complete.
		# Ducking should be disabled when the synth is finished producing audio.
		# Note that there may be calls to speak with a string that results in no audio,
		# it is important that in this case the audio does not get stuck ducked.
		# When there is no audio produced the startStream and endStream handlers are not called.
		# To prevent audio getting stuck ducked, it is unducked at the end of speech.
		# There are some known issues:
		# - When there is no audio produced by the synth, a user may notice volume lowering (ducking) temporarily.
		# - If the call to startStream handler is delayed significantly, users may notice a variation in volume
		# (as ducking is disabled at the end of speak, and re-enabled when the startStream handler is called)
		
		# A note on the synchronicity of components of this approach:
		# SAPISink.StartStream event handler (callback):
		# the synth speech is not blocked by this event callback.
		# SAPISink.EndStream event handler (callback):
		# assumed also to be async but not confirmed. Synchronicity is irrelevant to the current approach.
		# AudioDucker.disable returns before the audio is completely unducked.
		# AudioDucker.enable() ducking will complete before the function returns.
		# It is not possible to "double duck the audio", calling twice yields the same result as calling once.
		# AudioDucker class instances count the number of enables/disables,
		# in order to unduck there must be no remaining enabled audio ducker instances.
		# Due to this a temporary audio ducker is used around the call to speak.
		# SAPISink.StartStream: Ducking here may allow the early speech to start before ducking is completed.
		if audioDucking.isAudioDuckingSupported():
			tempAudioDucker = audioDucking.AudioDucker()
		else:
			tempAudioDucker = None
		if tempAudioDucker:
			if audioDucking._isDebug():
				log.debug("Enabling audio ducking due to speak call")
			tempAudioDucker.enable()
		try:
			self.tts.Speak(text, flags)
		finally:
			if tempAudioDucker:
				if audioDucking._isDebug():
					log.debug("Disabling audio ducking  after speak call")
				tempAudioDucker.disable()