source: trunk/StreamVision/AudioDevicemodule.c@ 660

Last change on this file since 660 was 657, checked in by Nicholas Riley, 12 years ago

AudioDevicemodule.c: Make error handling more useful and consistent.
Make spacing consistent. Ignore sporadic kAudioHardwareBadObjectError.

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