Skip to content

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

@Robots247

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions