How to detect when an Audio Device is disconnected in CoreAudio? - macos

Is there a way to set up a listener for any Audio Device to detect if it's been removed or unplugged? I found this post which helps if you only care about built-in audio related devices:
How to get notifications when the headphones are plugged in/out? Mac
But I'm looking for a more universal solution (i.e. all devices, USB, HDMI, etc.). And it's OSX specific.
Any ideas on how to do this?

I figured it out! One just needs to add a listener to the appropriate AudioDeviceID (the device you wish to monitor):
// add listener for detecting when a device is removed
const AudioObjectPropertyAddress alive_address =
{
kAudioDevicePropertyDeviceIsAlive,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
AudioObjectAddPropertyListener(current_device_id_, &alive_address, deviceIsAliveCallback, &player_);
And then write the corresponding callback:
OSStatus deviceIsAliveCallback(AudioObjectID inObjectID,
UInt32 inNumberAddresses,
const AudioObjectPropertyAddress inAddresses[],
void* inClientData)
{
// your code here
}
Hope this helps someone!

Related

Apple Mac disable screen dimming and/or lock

Is there a way to programatically (e.g. from Python code) to prevent my Mac from dimming and subsequently locking the screen? Of course, after my application is done, I would like to enable normal operation again. I know about caffiniate, but that applies to the whole application...that is not what I want. At some point in my code I want to disable dimming and then at some other point I want to enable it again.. Any tips, hints, suggestions?
You can try IOKit. Use IOPMAssertionDeclareUserActivity will wake the screen if necessary and keep it awake until the user's display sleep Energy Saver settings:
IOReturn ret;
CGError err;
IOPMAssertionID assertId;
ret = IOPMAssertionDeclareUserActivity(CFSTR("Stay awake!"), kIOPMUserActiveLocal, &assertId);
if (ret == kIOReturnSuccess)
{
// The screen is on
}
However, from the documentation for that method:
"If you prefer to hold the display awake for a longer period and you know how long you'd like to hold it, consider taking assertion kIOPMAssertionTypePreventUserIdleDisplaySleep using IOPMAssertionCreateWithDescription API instead."
Sounds closer to what you want. But I haven't tried it so I don't have a sample.

How to ignore changing audio-output from System Preference? (macOS)

I made my app can select audio-output. (like 'system default' or 'user's DAC')
but when user choose a output from system preferences panel - sound, my app's output follows the output user seleced.
I searched a lot and add some listener so I can change immediatly my app's output to previously user selected if system output has been changed.
BUT it makes very anonying few milliseconds swiching delay.
I guess it is because I switch my app's output after it's already changed to system default.
So I wonder If I can know BEFORE system default output's changing.
(Like viewWillAppear api from cocoa)
Thank you.
listener that I used for knowing chaninging of system default audio out is from the article below.
How to get notification if System Preferences Default Sound changed
thanks
more details
I used AudioUnitSetProperty(audioOut, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Output, 0, &deviceID, (UInt32)sizeof(deviceID)) for selecting output device. apple document
and add this listener
func addListenerBlock(listenerBlock: #escaping AudioObjectPropertyListenerBlock, onAudioObjectID: AudioObjectID, forPropertyAddress: inout AudioObjectPropertyAddress) {
if (kAudioHardwareNoError != AudioObjectAddPropertyListenerBlock(onAudioObjectID, &forPropertyAddress, nil, listenerBlock)) {
LOG("Error calling: AudioObjectAddPropertyListenerBlock") }
}
func add() {
var propertyAddress = AudioObjectPropertyAddress(mSelector: kAudioHardwarePropertyDefaultOutputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster)
self.addListenerBlock(listenerBlock: audioObjectPropertyListenerBlock,
onAudioObjectID: AudioObjectID(bitPattern: kAudioObjectSystemObject),
forPropertyAddress: &propertyAddress)
}
kAudioUnitSubType_DefaultOutput tracks the current output device selected by the user in the Sound Preferences. To play to a specific device use kAudioUnitSubType_HALOutput. The comments in AUComponent.h are helpful:
#enum Apple input/output audio unit sub types (OS X)
#constant kAudioUnitSubType_HALOutput
- desktop only
The audio unit that interfaces to any audio device. The user specifies which
audio device to track. The audio unit can do input from the device as well as
output to the device. Bus 0 is used for the output side, bus 1 is used
to get audio input from the device.
#constant kAudioUnitSubType_DefaultOutput
- desktop only
A specialisation of AUHAL that is used to track the user's selection of the
default device as set in the Sound Prefs
#constant kAudioUnitSubType_SystemOutput
- desktop only
A specialisation of AUHAL that is used to track the user's selection of the
device to use for sound effects, alerts
and other UI sounds.
You didn't specify how you're setting up your output (AUGraph?) so the way to use kAudioUnitSubType_HALOutput varies.

Change the output device for AVAudioPlayer on OSX

I need to play audio from AVAudioPlayer to a particular audio device such as a USB headset. By default the audio will get played out to the system default device setting. With QTKit, I was able to use SetMovieAudioContext() to do such a task but do not see equivalent in AVFoundation. How is it possible to change this on OSX?
Use NSSound instead:
Get list of audio devices with CoreAudio functions. Then get the devices' unique identifiers:
//...
address.mSelector = kAudioDevicePropertyDeviceUID;
CFStringRef uid = NULL;
UInt32 size = sizeof(uid);
AudioObjectGetPropertyData(deviceId, &address, 0, NULL, &size, &uid);
//...
Initialize sound item, set playbackDeviceIdentifier property value.
Opened a DTS case with Apple and they said it was not possible (as i need to support 10.8). If you only need to support 10.9 and above AVPlayerAudioDeviceSupport should work.

Programmatically switch audio devices on Windows 7

On my Windows 7 PC, I've got a set of speakers, some wireless headphones and a USB web cam. This means that I have two possible audio output devices and 2 possible audio input devices.
I find myself having to switch between them fairly frequently. At the moment this is a manual process: right-click on the speaker icon, choose one of "Playback devices" or "Recording devices", choose the correct device in the list (and there's some "dead" ones in there, too) and then hit "Set Default".
I've looked around, and all I can find are people scripting SendKeys to automate this.
That sucks.
Is there anyway to programmatically switch audio input/output devices, so that I can write a simple tray app/hotkey app to make this easier?
Allegedly undocumented COM-interface IPolicyConfig (kudos to #author EreTIk) allows to do that.
This is a sample implementation.
HRESULT SetDefaultAudioPlaybackDevice(LPCWSTR devID)
{
IPolicyConfigVista *pPolicyConfig;
ERole reserved = eConsole;
HRESULT hr = CoCreateInstance(
__uuidof(CPolicyConfigVistaClient),
NULL,
CLSCTX_ALL,
__uuidof(IPolicyConfigVista),
(LPVOID *)&pPolicyConfig);
if (SUCCEEDED(hr))
{
hr = pPolicyConfig->SetDefaultEndpoint(devID, reserved);
pPolicyConfig->Release();
}
return hr;
}
A string of Device ID needs to be passed to this function. An example of a device id
{0.0.1.00000000}.{d915c7bb-d5d7-4c92-80d9-1a0ee5d954f1}
This device id can be obtained through audio device enumeration.
If you are looking into changing default devices programmatically, then this is impossible by design.
Programatically setting the default playback device (and recording device)
How to change default sound playback device programatically?

Why doesn't narrator report itself as a screen reader to windows?

I'm trying to detect if a screen reader is attached to my application so that I can improve the experience for blind and low vision users. I'm using this win32 api (http://msdn.microsoft.com/en-us/library/ms724947%28VS.85%29.aspx), and specifying SPI_GETSCREENREADER as the uiAction. Call looks something like this:
int iAction = 70; // SPI_GETSCREENREADER constant;
int iParam = 0;
int iUpdate = 0;
bool result = false;
bool bReturn = SystemParametersInfo(iAction, iParam, &result, iUpdate);
If JAWS is running, or for that matter the magnification utility, this API reports that a screen reader is attached. However, if I only have the built in screen reader running (MS Narrator), this API reports that no screen reader is attached.
Is this really happening? Did the folks over at Microsoft really decide not to report the built in screen reader as a screen reader?
I can’t test the code out but sadly you are probably correct. Narrator is a very basic screen reader that provides almost no useful functionality other than allowing you to see if your main screen reader has crashed. Rumor has it that Microsoft wanted to make it a full featured screen reader when it was originally released but backed off do to possible antitrust issues from companies that were already producing Screen Readers. Note this is what I’ve heard on some of the blindness email lists I used to be on but cannot verify whether there’s any truth to it. If it is true it would explain why Narrator has limped along with no real improvements for as long as I can remember. I wouldn’t worry about Narrator, if a user is using your application they will be using a decent screen reader such as Jaws. I’ve been using screen reading software all my life and have never known anyone to use Narrator as a primary screen reader. If you wish to test with a free screen reader I would recommend NVDA In my experience it isn’t quite as good as jaws but is a quite usable screen reader without the high price tag.
If anyone falls into this horrible trap. Narrator sets a mutex when it starts running (this is totally not documented, but it appears to work if you need to detect ms narrator)
wstring m_wstrMutexKey = L"NarratorRunning";
// security attributes are part of windows API for CreateMutex
LPSECURITY_ATTRIBUTES securityAttributes = new _SECURITY_ATTRIBUTES();
securityAttributes->bInheritHandle = false;
securityAttributes->lpSecurityDescriptor = NULL;
securityAttributes->nLength = sizeof(LPSECURITY_ATTRIBUTES);
// initialize values
bool isRunning = false;
// CreateMutex returns a windows application HANDLE
HANDLE m_applicationHandle = CreateMutex(securityAttributes, false, m_wstrMutexKey.c_str());
// This should never happen
if (m_applicationHandle == NULL) {
isRunning = false;
}
// This condition indicates that narrator is running.
if (GetLastError() == ERROR_ALREADY_EXISTS) {
isRunning = true;
}
if (isRunning)
{
cout<<"Narrator is running.";
} else {
cout<<"No Mutex found. Narrator is not running.";
}
delete(securityAttributes);

Resources