Summary
On macOS, MacOSAudioDeviceMonitor.start() fails with:
TypeError: Callable argument is not a PyObjC closure
at the AudioObjectAddPropertyListener(...) call in src/audio_hotplug/_platform/macos.py (~line 46). Non-fatal in LedFx — startup continues — but audio-device hotplug detection is disabled, so newly-attached audio devices aren't picked up until LedFx is restarted.
Environment
- macOS 15 (Darwin 24.6.0), Apple Silicon (M1 Max)
- LedFx 2.1.9 (
LedFx-2.1.9-osx-arm64.tar.gz)
- Bundled Python 3.12, bundled audio-hotplug 0.1.0
Log excerpt
audio_hotplug.monitor INFO Starting macOS audio device monitor
audio_hotplug.monitor ERROR Failed to start macOS audio device monitor: Callable argument is not a PyObjC closure
Traceback (most recent call last):
File "audio_hotplug/_platform/macos.py", line 46, in start
TypeError: Callable argument is not a PyObjC closure
ledfx.core WARNING Failed to start audio device monitor: Callable argument is not a PyObjC closure. Device list will not update automatically when devices are added/removed.
Root cause
PyObjC won't auto-bridge a plain Python function to a CoreAudio C function pointer with the right signature. The nested def device_list_changed_callback(...) needs a @objc.callbackFor(...) decorator so PyObjC generates a properly-typed callback trampoline matching AudioObjectPropertyListenerProc.
Suggested fix
import objc
from CoreAudio import AudioObjectAddPropertyListener # must be imported before the decorator runs
@objc.callbackFor(AudioObjectAddPropertyListener)
def device_list_changed_callback(obj_id, num_addresses, addresses, client_data):
self._logger.debug("macOS audio device list changed")
self._debouncer.trigger()
return 0
The decorator has to be applied at the point of use (after the AudioObjectAddPropertyListener import) so PyObjC can look up the C signature.
Happy to send a PR if useful.
Summary
On macOS,
MacOSAudioDeviceMonitor.start()fails with:at the
AudioObjectAddPropertyListener(...)call insrc/audio_hotplug/_platform/macos.py(~line 46). Non-fatal in LedFx — startup continues — but audio-device hotplug detection is disabled, so newly-attached audio devices aren't picked up until LedFx is restarted.Environment
LedFx-2.1.9-osx-arm64.tar.gz)Log excerpt
Root cause
PyObjC won't auto-bridge a plain Python function to a CoreAudio C function pointer with the right signature. The nested
def device_list_changed_callback(...)needs a@objc.callbackFor(...)decorator so PyObjC generates a properly-typed callback trampoline matchingAudioObjectPropertyListenerProc.Suggested fix
The decorator has to be applied at the point of use (after the
AudioObjectAddPropertyListenerimport) so PyObjC can look up the C signature.Happy to send a PR if useful.