Re: kAudioDevicePropertyHogMode and NSSpeechSynthesizer...
Re: kAudioDevicePropertyHogMode and NSSpeechSynthesizer...
- Subject: Re: kAudioDevicePropertyHogMode and NSSpeechSynthesizer...
- From: "charlie" <email@hidden>
- Date: Fri, 04 Jun 2010 09:15:46 +0000
NSSound has the method, -setPlaybackDeviceIdentifier: which allows
you to tell it what device to use. The argument is the value of
kAudioDevicePropertyDeviceUID for the device you want to use.
I had tried it earlier in the evening, but wasn't getting the unique
identifier correctly. I ended up having to use
AudioDeviceGetProperty/kAudioDevicePropertyDeviceUID to get the correct
value.
Now that I've got the correct uid, it's sort of working....
My little bit of test code is in applicationDidFinishLaunching:.
Here's the basic algorithm:
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
get uid
capture built-in audio output device
speak into a file
init an NSSound object with that file
set the NSSound's unique id
play the NSSound
}
I can hear the voice begin to speak for just a split second now, but
then it gets cut off in the middle of the first word of the sentence.
If I add a little NSThread sleep after I play the NSSound, I can hear a
little more. If I increase the sleep to a long enough interval, I can
hear the whole thing. My guess is that it has to do with returning to
the runloop. I think that whatever it is that's cutting the sound off
is queued up and ready to trigger on the next pass thru the runloop,
and sleeping the main thread inside applicationDidFinishLaunching: is
just delaying that.
The good news is I'm pretty close to something usable here -- not
ideal... but usable.
Any ideas on why the sound is getting clipped?
Thanks.
Chuck
On June 4, 2010 12:48:32 A.M. EDT, Jeff Moore <email@hidden> wrote:
On Jun 3, 2010, at 9:30 PM, charlie wrote:
Your app would need to hog all the devices on the system to do this.
The code is written to loop thru all of the output devices and hold
each of them exclusively until the app quits. For testing purposes,
I've temporarily set it to only grab the "Built-in Output" device.
But, once I've got all this figured out and working, I'll put the
loop back the way it was before.
Let me step back a bit. The purpose of exclusive access to an
output device logically has to be to not only disallow other apps
from using the device **but also allow our own process to use it**.
It's obviously not designed to completely shut off access to the
device. This begs the question: how is one expected to route audio
thru the device when operating in this mode?
Typically, the clients that use hog mode are already using the lower
level APIs in the stack that allow you to specify which audio device
to use as opposed to just using the default device.
WORKAROUNDS:
For the last hour, I've been playing with NSSpeechSynthesizer's
startSpeakingString:toURL: and then picking the file up again and
playing it thru NSSound. startSpeakingString:toURL: works whether
in exclusive mode or not. NSSound will only play the sound when
**not** in exclusive mode.
NSSound has the method, -setPlaybackDeviceIdentifier: which allows
you to tell it what device to use. The argument is the value of
kAudioDevicePropertyDeviceUID for the device you want to use.
But, if I could replace the NSSound code with something lower-level
that **can** be routed thru my exclusive device, then I'd have
something usable.
The AudioQueue API in the AudioToolbox framework would be a good
place to start. An AudioQueue provides a straight forward way to play
audio data to the device of your choice.
(The vast majority of the students using this software will **not**
have access to this text-to-speech feature, so exclusive mode will
work exactly as expected on those instances. Only a very, very
small fraction will even have this feature available for them to use
in the interface. And for those few, performance is not going to be
an issue. So, whereas normally I would hate the idea of dumping to a
file and then reading it back in to play agin thru another audio
framework, in this case it'll be ok if that's how it has to be.)
Here's the other workaround I've been thinking about: I'm not sure
how mixing works in a cross-app context, but if it's possible, maybe
I could turn everybody else down and myself up. Then I wouldn't
need exclusive access. Is that possible?
No. There is no facility for doing something like that.
Also, is it possible to create a duplicate device in code? And, if
so, would that be useful here?
I'm not sure I'm following you here.
Thanks.
Chuck
On June 3, 2010 11:40:03 P.M. EDT, Jeff Moore <email@hidden> wrote:
On Jun 3, 2010, at 6:22 PM, charlie wrote:
Thanks for the quick response, Jeff.
This is for a kiosk test-taking app. One of the many lock down
responsibilities of the app is to ensure that the test taker
cannot have a pre-recorded audio cheat sheet playing in QuickTime
Player or iTunes while he's taking the test. But at the same
time, some students will need audio assistance from within the app
to read questions aloud using text-to-speech.
Your app would need to hog all the devices on the system to do
this. As you've seen, the default device will switch to another
device when the current one becomes hogged.
I just noticed something interesting...
NSSpeechSynthesizer's documentation states that it outputs thru
the "default output device", which I'm assuming means the selected
output device in the Sound pref pane in System Preferences.
If I open the Sound preferences and watch what happens when my app
enters and exits exclusive output mode, I see that the selected
output device (in the Sound pref pane) changes away from "Built-in
Output" to something else when I enter exclusive mode, then
switches back to "Built-in Output" after I exit exclusive mode.
Logically, this makes sense I guess, since my app now owns that
hardware, no one else should be able to route thru it.
Indeed. One of the requirements of being the default device is to
not be hogged.
But... If NSSpeechSynthesizer is trying to output thru the
"default output device", then that might mean that it's picking up
whatever happens to be selected in the Sound pref pane at that
moment, which, in my case, would be "MacVide Audio Connector" and
would result in silence.
Yes. This sounds like what I would expect to happen. If the current
default device becomes hogged, a new default device is chosen.
What I was hoping though, is that NSSpeechSynthesizer would define
"default output device" from the app's perspective, not the System
Preferences' perspective.
Unfortunately for your case, the default device is a system-wide property.
Is there another list that would be better for asking questions
about NSSpeechSynthesizer?
I don't know if the Speech group has a public API list but you
might find some help on one Cocoa-oriented lists.
On June 3, 2010 09:02:51 P.M. EDT, Jeff Moore <email@hidden> wrote:
I don't know a whole lot about NSSpeechSynthesizer. If it has a
facility to tell it what audio device to use, then you might be
able to work around the issue that way.
But to turn things on their heads, why do you need hog mode for
your app? Hog mode is a really unfriendly thing to assert on the
system. As such, it should only be used in circumstances where it
is essential like sending non-mixable data (such as AC-3) to the
hardware. The vast majority of apps are best off pretending like
hog mode doesn't exist.
--
Jeff Moore
Core Audio
Apple
On Jun 3, 2010, at 5:56 PM, charlie wrote:
To be clear, when I say it's "working perfectly", I mean that
other apps stop emitting sound after I gain exclusive control of
the output device and then resume emitting sound again after I
relinquish exclusive control.
The only sound my app will be generating is NSSpeechSynthesizer
text-to-speech.
Do I need to tell NSSpeechSynthesizer that we're in exclusive
output mode somehow? Do I need to "select" the output device
somehow after entering exclusive mode for that device?
Thanks.
Chuck
On June 3, 2010 08:45:51 P.M. EDT, charlie <email@hidden> wrote:
I can obtain and relinquish exclusive control of the built-in
output device using kAudioDevicePropertyHogMode. This works
perfectly.
But, after obtaining exclusive control of the built-in output
device, NSSpeechSynthesizer does not work (from within the same
app/pid).
Anyone know how I can get NSSpeechSynthesizer to work while in
exclusive output mode?
Thanks.
Chuck
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden