Skip to content

macOS: "Callable argument is not a PyObjC closure" when starting MacOSAudioDeviceMonitor #9

Description

@Robots247

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions