source: trunk/StreamVision/AudioDevicemodule.c@ 654

Last change on this file since 654 was 654, checked in by Nicholas Riley, 11 years ago

AudioDevicemodule.c: Register callback for when default output device
changes.

StreamVision.py: Turn on/off the stereo when the current output device
changes to/from AirPlay.

File size: 3.9 KB
Line 
1#include "Python.h"
2#include <AudioToolbox/AudioServices.h>
3
4static PyObject *
5AudioDevice_default_output_device_is_airplay(PyObject *self, PyObject *args) {
6 AudioObjectPropertyAddress propertyAddress;
7 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
8 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
9 propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
10
11 AudioDeviceID deviceID = kAudioDeviceUnknown;
12 UInt32 size = sizeof(deviceID);
13 OSStatus err;
14
15 err = AudioHardwareServiceGetPropertyData(kAudioObjectSystemObject,
16 &propertyAddress, 0, NULL,
17 &size, &deviceID);
18 if (err != noErr)
19 return PyErr_Format(PyExc_OSError,
20 "AudioHardwareServiceGetPropertyData failed (%ld)",
21 (long)err);
22
23 if (deviceID == kAudioDeviceUnknown)
24 Py_RETURN_NONE;
25
26 UInt32 transportType;
27 propertyAddress.mSelector = kAudioDevicePropertyTransportType,
28 err = AudioObjectGetPropertyData(deviceID,
29 &propertyAddress, 0, NULL,
30 &size, &transportType);
31 if (err != noErr)
32 return PyErr_Format(PyExc_OSError,
33 "AudioObjectGetPropertyData failed (%ld)",
34 (long)err);
35
36 if (transportType == kAudioDeviceTransportTypeAirPlay)
37 Py_RETURN_TRUE;
38 else
39 Py_RETURN_FALSE;
40}
41
42static PyObject *default_output_device_changed_callback = NULL;
43
44OSStatus
45output_device_changed_listener(AudioObjectID inObjectID,
46 UInt32 inNumberAddresses,
47 const AudioObjectPropertyAddress inAddresses[],
48 void *inClientData) {
49 PyGILState_STATE gstate = PyGILState_Ensure();
50 PyObject_CallObject(default_output_device_changed_callback, NULL);
51 PyGILState_Release(gstate);
52
53 return noErr;
54}
55
56static PyObject *
57AudioDevice_set_default_output_device_changed_callback(PyObject *self,
58 PyObject *args) {
59 PyObject *new_callback;
60 if (!PyArg_ParseTuple(args, "O", &new_callback))
61 return NULL;
62
63 if (!PyCallable_Check(new_callback)) {
64 PyErr_SetString(PyExc_TypeError, "parameter must be callable");
65 return NULL;
66 }
67 Py_INCREF(new_callback);
68 if (default_output_device_changed_callback == NULL) {
69 AudioObjectPropertyAddress propertyAddress;
70 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
71 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
72 propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
73
74 OSStatus err;
75 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
76 &propertyAddress,
77 &output_device_changed_listener,
78 NULL);
79 if (err != noErr)
80 return PyErr_Format(PyExc_OSError,
81 "AudioObjectAddPropertyListener failed (%ld)",
82 (long)err);
83 } else {
84 Py_DECREF(default_output_device_changed_callback);
85 }
86 default_output_device_changed_callback = new_callback;
87
88 Py_RETURN_NONE;
89}
90
91static PyMethodDef AudioDevicemodule_methods[] = {
92 {"default_output_device_is_airplay",
93 AudioDevice_default_output_device_is_airplay, METH_NOARGS,
94 "default_output_device() -> bool or None\n\n"
95 "Return whether the default CoreAudio output device is an AirPlay device."},
96 {"set_default_output_device_changed_callback",
97 AudioDevice_set_default_output_device_changed_callback, METH_VARARGS,
98 "set_default_output_device_changed_callback(callable)\n\n"
99 "Set a callback invoked when the default CoreAudio output device changes."},
100 {NULL, NULL, 0, NULL}
101};
102
103PyMODINIT_FUNC
104initAudioDevice(void) {
105 (void)Py_InitModule("AudioDevice", AudioDevicemodule_methods);
106}
Note: See TracBrowser for help on using the repository browser.