Help us improve
Share bugs, ideas, or general feedback.
From media-os
Routes professional low-latency audio with JACK Audio Connection Kit: start jackd server, list/connect ports, measure round-trip latency, record multi-channel WAV, and bridge audio over the internet with JackTrip.
npx claudepluginhub damionrashford/media-os --plugin media-osHow this skill is triggered — by the user, by Claude, or both
Slash command
/media-os:audio-jack [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
jackctl.py start)jackctl.py ports)jackctl.py link)jackctl.py status)jackctl.py latency)jackctl.py record)jackctl.py jacktrip)Not for: generic Linux desktop routing (use audio-pipewire), macOS default-device
changes (use audio-coreaudio), Windows default-device changes (use audio-wasapi).
jackd accepts a single backend driver after -d. This wrapper picks a per-OS
default (alsa on Linux, coreaudio on macOS, portaudio on Windows).
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py start \
--rate 48000 --period 256 --nperiods 2
Force backend + device:
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py start \
--backend alsa --device hw:0 --rate 48000 --period 128
Typical period / nperiods:
| Scenario | --period | --nperiods |
|---|---|---|
| Live performance, PCIe / Thunderbolt card | 64–128 | 2 |
| Tracking / monitoring, USB class-compliant | 128–256 | 3 |
| Mixing, offline | 512–1024 | 2 |
See references/backends.md for per-backend flag quirks.
On JACK2 systems with D-Bus, jack_control can drive the server instead — this
wrapper does not (jack_control is interactive by design; scripts should call
jackd directly as shown).
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py ports # just port names
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py ports --connections # -c: who connects to what
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py ports --types # -t: audio vs midi
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py ports --input # -i: input ports only
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py ports --output # -o: output ports only
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py ports --latency # -l: port latency info
Flags combine.
JACK ports use the client:port naming convention. The physical device typically
lives under the system client:
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py link 'Ardour:MixL' 'system:playback_1'
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py link 'Ardour:MixR' 'system:playback_2'
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py unlink 'Ardour:MixL' 'system:playback_1'
Quote names with shell-special chars (spaces, colons, parens).
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py status
Prints jack_samplerate, jack_bufsize, and jack_cpu_load back-to-back.
High CPU load combined with xruns = lower --period isn't achievable on this
host/backend; raise it.
Change buffer size at runtime (JACK2 only):
jack_bufsize 512 # bare jack tool — wrapper intentionally doesn't wrap this
jack_iodelay creates a client named jack_delay with one input + one output.
To measure a loopback you have to wire it:
# Start the tool
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py latency \
--capture-port system:capture_1 --playback-port system:playback_1
With the suggestion printed, in another shell connect the loopback cable path (physical or virtual):
jack_connect jack_delay:out system:playback_1
jack_connect system:capture_1 jack_delay:in
jack_iodelay reports round-trip delay in frames + milliseconds. Divide by 2 to
get one-way.
jack_rec writes a multichannel interleaved WAV from a list of ports:
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py record out.wav \
--channels system:capture_1 system:capture_2 --duration 30
The wrapper uses timeout for --duration. Without --duration, record until
SIGINT.
JackTrip sends uncompressed multichannel audio over UDP for remote ensembles. Requires one peer in server mode and one in client mode:
# On the host with a static / known IP:
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py jacktrip server --channels 2
# On the other side:
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py jacktrip client \
--host studio.example.com --channels 2
See references/jacktrip.md for hub mode, FEC, and
port/firewall details.
jackd is almost always PipeWire's libjack shim. Running
jackctl.py start under PipeWire starts a second jackd that PipeWire's shim
already emulates — usually a no-op / error. Check with ldd $(which jackd)
or pw-cli info 0. For PipeWire environments, use audio-pipewire instead;
existing JACK client apps already work against PipeWire's shim.jack_control. JACK2 is multi-process C++ with D-Bus. Only JACK2 can
dynamically change buffer size via jack_bufsize at runtime. If you need the
D-Bus API, require JACK2.jackd stop. Server exits when its controlling process does,
or on SIGTERM / SIGKILL. This wrapper's stop sends SIGTERM after trying
jack_control exit for JACK2.(period * nperiods) / samplerate. At 48 kHz, 256 × 2 ≈ 10.7 ms.
USB class-compliant interfaces often need nperiods=3.jack_connect just returns non-zero.system:capture_* and system:playback_* are from the server's POV.
capture_1 is an output port (audio captured from the device, flowing into
the graph). playback_1 is an input port (audio leaving the graph into
the device). This trips everyone at least once.: in them.
Always quote when passing to a shell.dmix/multi
plugins or zita-ajbridge; on macOS build an Aggregate Device first
(see audio-coreaudio).4464 open by default plus the per-channel data
ports. If you're behind NAT with no port forwarding, use hub mode through a
public relay.jack_rec on some distros is missing or replaced by jack_capture.
Check which jack_rec. If absent, install the jack-example-tools / jack-tools
package.jack_iodelay doesn't know the actual latency path. It just reports what
a round-trip through its own ports measured. You have to wire that round-trip.# 128 frames / 48000 Hz = 2.67 ms per period. 2 periods = 5.33 ms output latency.
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py start --rate 48000 --period 128 --nperiods 2
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py ports --output # find Ardour's ports
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py link 'Ardour:Master/out-L' 'system:playback_1'
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py link 'Ardour:Master/out-R' 'system:playback_2'
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py record session.wav \
--channels system:capture_1 system:capture_2 system:capture_3 system:capture_4 \
system:capture_5 system:capture_6 system:capture_7 system:capture_8 \
--duration 300
On the rehearsal space with the static IP:
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py jacktrip server --channels 4
On the guest player's machine:
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py jacktrip client --host rehearsal.example.com --channels 4
Both sides then wire JackTrip:receive_* and JackTrip:send_* to their monitors
and interfaces with jack_connect.
# Physical loopback: patch playback_1 back into capture_1 with a TRS cable.
uv run ${CLAUDE_SKILL_DIR}/scripts/jackctl.py latency \
--capture-port system:capture_1 --playback-port system:playback_1
# Then in another shell run the two jack_connect commands it prints.
Cannot lock down memory area / Cannot use real-time schedulingCause: Running jackd without RT privileges.
Fix: Add the user to the audio group (Linux) and ensure
/etc/security/limits.d/95-jack.conf has @audio - rtprio 95 and
@audio - memlock unlimited. Log out / in to pick up. On macOS jackd
requests RT automatically if entitled.
JACK server not running or cannot be startedCause: Trying to connect a client before jackd has started, or the backend
device is busy (e.g. ALSA device grabbed by PulseAudio/PipeWire).
Fix: On PipeWire-based distros, don't run jackd — use PipeWire's shim.
On PulseAudio, suspend it: pactl suspend-sink 1 && pactl suspend-source 1.
Cause: Period too small for the host or the backend, or IRQ contention.
Fix: Raise --period (256 → 512 → 1024). Check jack_cpu_load — above
~60% on long stretches is risky. On Linux, confirm a low-latency / RT kernel.
Cause: Direction mismatch — JACK rejects silently (return code only). Fix: Source must be an output port (flows out of a client) and destination must be an input port.
jack_iodelay prints "no port found"Cause: jack_iodelay starts a client named jack_delay — if a previous run
didn't clean up, the name is taken.
Fix: Kill any stale jack_iodelay, then retry.
Cause: Only the server's firewall is open. UDP requires both sides to accept
the return path.
Fix: Ensure both peers allow inbound UDP on the jacktrip port
(default 4464) plus the negotiated per-channel data ports.
references/backends.md.references/jacktrip.md.