#include "Python.h" #include static PyObject * AudioDevice_default_output_device_is_airplay(PyObject *self, PyObject *args) { AudioObjectPropertyAddress propertyAddress; propertyAddress.mScope = kAudioObjectPropertyScopeGlobal; propertyAddress.mElement = kAudioObjectPropertyElementMaster; propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; AudioDeviceID deviceID = kAudioDeviceUnknown; UInt32 size = sizeof(deviceID); OSStatus err; err = AudioHardwareServiceGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &deviceID); if (err != noErr) return PyErr_Format(PyExc_OSError, "AudioHardwareServiceGetPropertyData failed (%ld)", (long)err); if (deviceID == kAudioDeviceUnknown) Py_RETURN_NONE; UInt32 transportType; propertyAddress.mSelector = kAudioDevicePropertyTransportType, err = AudioObjectGetPropertyData(deviceID, &propertyAddress, 0, NULL, &size, &transportType); if (err != noErr) return PyErr_Format(PyExc_OSError, "AudioObjectGetPropertyData failed (%ld)", (long)err); if (transportType == kAudioDeviceTransportTypeAirPlay) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static PyObject *default_output_device_changed_callback = NULL; OSStatus output_device_changed_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void *inClientData) { PyGILState_STATE gstate = PyGILState_Ensure(); PyObject_CallObject(default_output_device_changed_callback, NULL); PyGILState_Release(gstate); return noErr; } static PyObject * AudioDevice_set_default_output_device_changed_callback(PyObject *self, PyObject *args) { PyObject *new_callback; if (!PyArg_ParseTuple(args, "O", &new_callback)) return NULL; if (!PyCallable_Check(new_callback)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } Py_INCREF(new_callback); if (default_output_device_changed_callback == NULL) { AudioObjectPropertyAddress propertyAddress; propertyAddress.mScope = kAudioObjectPropertyScopeGlobal; propertyAddress.mElement = kAudioObjectPropertyElementMaster; propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; OSStatus err; err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, &output_device_changed_listener, NULL); if (err != noErr) return PyErr_Format(PyExc_OSError, "AudioObjectAddPropertyListener failed (%ld)", (long)err); } else { Py_DECREF(default_output_device_changed_callback); } default_output_device_changed_callback = new_callback; Py_RETURN_NONE; } static PyMethodDef AudioDevicemodule_methods[] = { {"default_output_device_is_airplay", AudioDevice_default_output_device_is_airplay, METH_NOARGS, "default_output_device() -> bool or None\n\n" "Return whether the default CoreAudio output device is an AirPlay device."}, {"set_default_output_device_changed_callback", AudioDevice_set_default_output_device_changed_callback, METH_VARARGS, "set_default_output_device_changed_callback(callable)\n\n" "Set a callback invoked when the default CoreAudio output device changes."}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC initAudioDevice(void) { (void)Py_InitModule("AudioDevice", AudioDevicemodule_methods); }