Help us improve
Share bugs, ideas, or general feedback.
From media-os
Manipulates macOS audio via Core Audio HAL: list/change devices, convert files with afconvert, TTS with say, install BlackHole, and build aggregate devices.
npx claudepluginhub damionrashford/media-os --plugin media-osHow this skill is triggered — by the user, by Claude, or both
Slash command
/media-os:audio-coreaudio [action][action]The summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Context:** $ARGUMENTS
Guides technical evaluation of code review feedback: read fully, restate for understanding, verify against codebase, respond with reasoning or pushback before implementing.
Share bugs, ideas, or general feedback.
Context: $ARGUMENTS
macaudio.py list-devices)macaudio.py set-default)macaudio.py info)macaudio.py play)macaudio.py convert)macaudio.py tts)macaudio.py aggregate-create)macaudio.py hal-plugins)macaudio.py blackhole-install)afconvert (more format
coverage than SoX for Apple formats; handles CAF, M4A, ALAC natively).Not for: Linux (use audio-pipewire), Windows (use audio-wasapi).
The script exits 2 with a helpful message if run on non-macOS.
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py list-devices
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py list-devices --format json
SwitchAudioSource -a underneath. Each line is <name> (<type>) where type is
input / output. If SwitchAudioSource isn't installed the script tells you
to run brew install switchaudio-osx.
macOS distinguishes three defaults: output (music/TV audio), input (mic), system (alert sounds, which can be a different device).
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py set-default "MacBook Pro Speakers"
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py set-default "Scarlett Solo" --type input
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py set-default "MacBook Pro Speakers" --type system
Device name must match exactly (spaces matter, quoting required). Use
list-devices first.
afinfo + afplay ship with macOS. No install needed.
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py info track.caf
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py play track.caf
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py play track.caf --volume 128 # 0-255
afinfo reads every Apple format (CAF, M4A/AAC/ALAC, AIFF, WAV, MP3) plus
anything the system's registered AudioToolbox components understand.
afconvert is Apple's native format converter. Preferred over SoX for
Apple-specific flows (CAF, ALAC, AAC for M4A/M4R, lossless 32-bit float).
# WAV -> Apple Lossless in an M4A
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py convert \
--input in.wav --output out.m4a --format m4af
# 24-bit 96k WAV -> 16-bit 48k WAV
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py convert \
--input hi.wav --output lo.wav --format WAVE \
--rate 48000 --bit-depth 16
# Stereo -> 5.1 upmix (container must support it — CAF does)
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py convert \
--input stereo.wav --output surround.caf --format caff \
--channel-layout 5.1
FourCC codes the --format flag accepts: caff (CAF), m4af (MPEG-4 audio),
WAVE, AIFF, AIFC. Full list: afconvert -hf.
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py tts "Build complete"
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py tts "hola" --voice Monica
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py tts "saved" --output voicemail.aiff
say -v '?' lists installed voices. Voices beyond the defaults (Alex, Samantha,
Daniel) require a one-time download via System Settings → Accessibility →
Spoken Content → System Voice → Customize.
macOS lets you combine physical devices two ways:
This wrapper opens Audio MIDI Setup and prints the clickable steps, because
programmatic creation needs AudioHardwareCreateAggregateDevice from
CoreAudio/AudioHardware.h (Swift / Objective-C). Use when wiring for
automation:
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py aggregate-create \
--name "Studio Aggregate" \
--devices "AppleHDAEngineOutput:1B,0,0,1:0" "BlackHole2ch_UID" \
--drift-correct "BlackHole2ch_UID"
Member device UIDs come from list-devices (pass --format json for the full
UID strings).
For a truly automatable path, write the Swift snippet that calls
AudioHardwareCreateAggregateDevice — see references/virtual-drivers.md.
HAL plug-ins are .driver bundles that register virtual audio devices:
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py hal-plugins
Typical output includes BlackHole2ch.driver, BackgroundMusic.driver,
Loopback.driver, Aggregate/Multi-Output synthesizer drivers from Apple.
Global path: /Library/Audio/Plug-Ins/HAL/*.driver.
User path: ~/Library/Audio/Plug-Ins/HAL/*.driver (rare; per-user installs).
After installing or removing a HAL plug-in, coreaudiod has to rescan:
sudo pkill coreaudiod # wrapper intentionally does NOT run this
BlackHole is the go-to free open-source virtual driver — works on Apple Silicon (DriverKit / AudioDriverKit) and Intel, 2ch / 16ch / 64ch variants.
The wrapper prints install instructions rather than installing automatically:
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py blackhole-install --variant 2ch
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py blackhole-install --variant 16ch
Runs the install only if the user copies the printed brew install --cask line.
See references/virtual-drivers.md for the
full BlackHole vs Loopback vs Background Music comparison.
SwitchAudioSource device names must match exactly. Trailing spaces,
capitalization, and the full name (including brand prefix) all matter.
Quote-wrap everything. list-devices --format json gives you a copy-pasteable
UID.node-audiodevice) and a Go binary. Both predate SwitchAudioSource and have
different flags. Always specify the package/binary when pointing at them;
default to SwitchAudioSource in docs.list-devices
doesn't show it, SIP or kernel-extension approval is likely blocking it
(System Settings → Privacy & Security → Allow).afconvert fourCC codes are case-sensitive. WAVE with -f, not wave.
Likewise AIFF/AIFC/caff/m4af.AudioDeviceID values will miss it until restart. System
Settings → Sound usually re-enumerates immediately.say runs through the system output by default, not whatever
set-default --type input was last set to. If you need say → file, pipe
through --output (AIFF) or afconvert afterward for M4A/MP3.SwitchAudioSource for scripted default changes.AudioDeviceID when persisting
references in scripts / plist / DAW configs.blackhole-2ch before installing blackhole-16ch, or both drivers show up
simultaneously (which is fine — they are different devices, separately named).uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py list-devices # find exact name
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py set-default "Sony WH-1000XM5"
Install BlackHole 2ch:
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py blackhole-install --variant 2ch
# copy the printed brew command and run it
In Audio MIDI Setup build a Multi-Output Device combining your speakers + BlackHole; set it as system output.
Point OBS's "Audio Input Capture" at BlackHole.
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py convert \
--input master.wav --output master.m4a --format m4af
afinfo covers it:
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py info recording.aiff
uv run ${CLAUDE_SKILL_DIR}/scripts/macaudio.py tts "Tests passed, zero failures" --voice Samantha
SwitchAudioSource: command not foundCause: Not installed.
Fix: brew install switchaudio-osx. The wrapper points at this in its error
output.
Cause: macOS default-reassign behavior when "When playing sound through" is
set to "whatever device is connected". Overrides your scripted default.
Fix: System Settings → Sound → Output → uncheck "Automatically switch to
newly connected devices" (macOS 14+), or persist the default in a LaunchAgent
that re-runs set-default on device change.
afconvert: Error -50 (invalid parameter)Cause: Requested an output format/rate/bit-depth combo that the codec
rejects (e.g. ALAC in WAV container, 32-bit float into M4A).
Fix: Drop mismatched flags. ALAC belongs in m4af only; bit-depth is
AAC-ignored.
Cause: System Extension blocked by macOS. Fix: System Settings → Privacy & Security → scroll to the "System software was blocked" banner → Allow. Reboot. Retry.
afplay works but no soundCause: afplay uses the current system default output. If you set a weird
output recently (aggregate without speakers as a member), nothing audible is
reached.
Fix: Re-run set-default with a known-good physical output and retry.
Cause: No drift correction, or drift master isn't the most-stable clock. Fix: In Audio MIDI Setup, check "Drift Correction" on every non-master member. Put the interface with the best clock (Thunderbolt → PCIe card → USB 2) as master.
references/virtual-drivers.md.