Repository: nvaccess/nvda

Issue:
When audio output device is changed beeps are still beeing played in the previous device
**Reported by ragb on 2012-03-15 00:48**
This was reported on the portuguese list. According to an user when the audio output device is changed on the synthercizer settings dialog, beeps and other sounds play on the default sound card. Is that the expected behaviour? At least beeps for progress bars and mouse tracking make sence to playalong with the synth's audio.

I can't reproduce it now because I don't have access to an external soundcard at the moment, but I recall seeing this behaviour at some point...



Discussion:
Comment 1 by jteh on 2012-03-15 00:55
It's definitely a bug. I knew about it already, but never got around to filing a ticket. :)
Changes:
Milestone changed from None to near-term

Comment 2 by zahari_bgr on 2012-03-15 01:17
I test it on Windows 7 32bit with external USB plug&play sound card.
After changing the device in the Synthesizer dialog all speech and sounds including beeps for capitals and progress bar updates was redirected to the new device as expected.
May be it has something about drivers or OS version?

Oops, when i change it back, the beeps and other non-speech sounds still were coming from the external card.
May be because it was set to default audio device by Windows.
Sorry for being too hasty.

Comment 3 by jteh on 2012-03-15 02:18
If the Windows default output device changes and NVDA is set to use Microsoft Sound Mapper, NVDA's output will change accordingly. However, if you set a specific output device in NVDA, tones stay on the output set when NVDA was started.

Comment 4 by ondrosik on 2012-09-30 06:04
I have also this issue, but comments 2 and 3 brings new point of wiev, I will experiment with it more deeply, if changing system default device really can do strange things. I am currently on xp.
So what I know at this time:
I am using USB dongle, because my laptop headphone output is broken. It behave as a standard audio device. When I set NVDA's output to this, voice goes from USB dongle, but sounds as beep on cap character, NVDA error, NVDA shutdown are going through the device which was set before, in my case soundmax driver. After restarting NVDA, all sounds are going from via USb, as set in previous step. This works also in reversed, E. G. now when i set it back to soundmax, speech goes from soundmax, but sounds from USB dongle. In documentation is said, that This option allows you to choose the sound card that NVDA should instruct the selected synthesizer to speak through. But why it change the sounds after restarting? I know that for this case NVDA should remember two settings, one for speech, one for sounds. or maybe it should affect all sounds and it does not work for some cases.
It should be interesting to see, how it behaves on other computers, can someone reproduce it? I testet it also with Zoom H1 output device with same result.

Comment 5 by nvdakor on 2014-07-25 16:04
Hi,
People on the NvDA lists report that this issue is gone in recent versions of NVDA. Please test with 2014.2 to see if the issue returns. Thanks.

Comment 6 by jteh on 2014-07-28 06:18
This bug still exists, though there are various edge cases in which the behaviour is different.


Resolving patch:
diff --git a/source/core.py b/source/core.py
--- a/source/core.py
+++ b/source/core.py
@@ -146,6 +146,7 @@ def resetConfiguration(factoryDefaults=False):
 	import vision
 	import languageHandler
 	import inputCore
+	import tones
 	log.debug("Terminating vision")
 	vision.terminate()
 	log.debug("Terminating braille")
@@ -154,6 +155,8 @@ def resetConfiguration(factoryDefaults=False):
 	brailleInput.terminate()
 	log.debug("terminating speech")
 	speech.terminate()
+	log.debug("terminating tones")
+	tones.terminate()
 	log.debug("terminating addonHandler")
 	addonHandler.terminate()
 	log.debug("Reloading config")
@@ -165,6 +168,8 @@ def resetConfiguration(factoryDefaults=False):
 	languageHandler.setLanguage(lang)
 	# Addons
 	addonHandler.initialize()
+	# Tones
+	tones.initialize()
 	#Speech
 	log.debug("initializing speech")
 	speech.initialize()
@@ -201,8 +206,11 @@ def _setInitialFocus():
 
 def main():
 	"""NVDA's core main loop.
-This initializes all modules such as audio, IAccessible, keyboard, mouse, and GUI. Then it initialises the wx application object and sets up the core pump, which checks the queues and executes functions when requested. Finally, it starts the wx main loop.
-"""
+	This initializes all modules such as audio, IAccessible, keyboard, mouse, and GUI.
+	Then it initialises the wx application object and sets up the core pump,
+	which checks the queues and executes functions when requested.
+	Finally, it starts the wx main loop.
+	"""
 	log.debug("Core starting")
 
 	ctypes.windll.user32.SetProcessDPIAware()
@@ -249,6 +257,9 @@ def main():
 	import NVDAHelper
 	log.debug("Initializing NVDAHelper")
 	NVDAHelper.initialize()
+	log.debug("Initializing tones")
+	import tones
+	tones.initialize()
 	import speechDictHandler
 	log.debug("Speech Dictionary processing")
 	speechDictHandler.initialize()
@@ -558,6 +569,7 @@ def run(self):
 	_terminate(winConsoleHandler, name="Legacy winConsole support")
 	_terminate(JABHandler, name="Java Access Bridge support")
 	_terminate(appModuleHandler, name="app module handler")
+	_terminate(tones)
 	_terminate(NVDAHelper)
 	_terminate(touchHandler)
 	_terminate(keyboardHandler, name="keyboard handler")
diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1000,6 +1000,11 @@ def onOk(self, evt):
 			config.conf['audio']['audioDuckingMode']=index
 			audioDucking.setAudioDuckingMode(index)
 
+		# Reinitialize the tones module to update the audio device
+		import tones
+		tones.terminate()
+		tones.initialize()
+
 		if self.IsModal():
 			# Hack: we need to update the synth in our parent window before closing.
 			# Otherwise, NVDA will report the old synth even though the new synth is reflected visually.
diff --git a/source/tones.py b/source/tones.py
--- a/source/tones.py
+++ b/source/tones.py
@@ -1,8 +1,7 @@
-#tones.py
-#A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2007-2017 NV Access Limited, Aleksey Sadovoy
-#This file is covered by the GNU General Public License.
-#See the file COPYING for more details.
+# A part of NonVisual Desktop Access (NVDA)
+# Copyright (C) 2007-2017 NV Access Limited, Aleksey Sadovoy, Leonard de Ruijter
+# This file is covered by the GNU General Public License.
+# See the file COPYING for more details.
 
 """Utilities to generate and play tones"""
 
@@ -15,16 +14,27 @@
 
 SAMPLE_RATE = 44100
 
-try:
-	player = nvwave.WavePlayer(channels=2, samplesPerSec=int(SAMPLE_RATE), bitsPerSample=16, outputDevice=config.conf["speech"]["outputDevice"],wantDucking=False)
-except:
-	log.warning("Failed to initialize audio for tones")
-	player = None
+player = None
+
+
+def initialize():
+	global player
+	try:
+		player = nvwave.WavePlayer(
+			channels=2,
+			samplesPerSec=int(SAMPLE_RATE),
+			bitsPerSample=16,
+			outputDevice=config.conf["speech"]["outputDevice"],
+			wantDucking=False
+		)
+	except Exception:
+		log.warning("Failed to initialize audio for tones", exc_info=True)
+		player = None
 
 # When exiting, ensure player is deleted before modules get cleaned up.
 # Otherwise, WavePlayer.__del__ will fail with an exception.
 @atexit.register
-def _cleanup():
+def terminate():
 	global player
 	player = None
 
@@ -38,7 +48,7 @@ def beep(hz,length,left=50,right=50):
 	@type left: integer
 	@param right: volume of the right channel (0 to 100)
 	@type right: integer
-	""" 
+	"""
 	log.io("Beep at pitch %s, for %s ms, left volume %s, right volume %s"%(hz,length,left,right))
 	if not player:
 		return


Test patch: