Lyra · User Guide
Lyra maps F10 / F11 / F12 to the monitor volume of a Universal Audio Apollo. This page is the canonical user reference. Skim the table of contents to jump to a feature, or read top-to-bottom for the full tour. The full URL scheme reference lives on a separate page.
That's the whole app. Everything below is detail.
Lyra intercepts F10 / F11 / F12 globally using a CGEventTap. This is the same mechanism macOS itself uses to handle media keys, and it requires Accessibility permission.
On first launch:
/Applications.Lyra polls the permission state in the background, so it will start the key handler the moment you grant access - no second relaunch required after the first.
If the system gets into a weird state (rare, but possible after reinstalls), clear the TCC entry:
tccutil reset Accessibility com.headroom.Lyra
Then relaunch Lyra to re-trigger the prompt.
Lyra only reads three keys (NX_KEYTYPE_MUTE, NX_KEYTYPE_SOUND_DOWN, NX_KEYTYPE_SOUND_UP) - the system-defined media-key events. It does not log, store, or forward anything. It also stops intercepting whenever your Apollo isn't reachable, so when the interface is unplugged your F-keys behave like normal volume keys again.
All shortcuts work globally and are intercepted before any other app sees them - but only when Mixer Engine is reachable. When it isn't, the F-keys fall through to whatever the system would otherwise do (usually nothing on a UA-only setup).
| Shortcut | Action |
|---|---|
| F12 | Volume up - one step |
| F11 | Volume down - one step |
| F10 | Mute / Unmute |
| ⇧F12 | Volume up - fine step (½) |
| ⇧F11 | Volume down - fine step (½) |
| ⇧F10 | DIM - toggle Apollo hardware DIM |
| ⌥F10 | Mono - toggle L+R sum to mono |
| ⌃F10 | Jump to Reference Level slot 1 |
| ⌃F11 | Jump to Reference Level slot 2 |
| ⌃F12 | Jump to Reference Level slot 3 |
Lyra uses a default step of 1/16 of full range (~6.25%). Shift halves it to ~3.125% for fine adjustments - the same pattern macOS uses for built-in volume keys.
Depending on your macOS keyboard settings, you may need to hold Fn to send F10/F11/F12 as F-keys (rather than the secondary brightness/expose actions printed on the keys). Change this in System Settings → Keyboard → Function Keys, or use Fn as a permanent modifier.
Ctrl+F-keys always do a Reference Level jump, even if Shift or Option are also held. This lets you hold Ctrl from a prior chord and the jump still works.
Whenever the monitor level changes - from F-keys, the menu slider, the Apollo's hardware knob, UA Console, or a lyra:// URL - Lyra shows a small horizontal pill just below its menu bar icon.
Two modes:
On macOS 26+ the pill uses Liquid Glass - the system's translucent material - and inherits the colour of whatever is behind the menu bar.
As of v1.6, the HUD has an in-app toggle in Settings → General → Show volume HUD overlay. Flip it off and the pill stops appearing immediately - no relaunch needed.
On v1.5 and earlier, set the showHUD UserDefault and restart Lyra:
defaults write com.headroom.Lyra showHUD -bool false
Set it back to true (or delete the key with defaults delete com.headroom.Lyra showHUD) to re-enable.
Lyra has three monitor presets, each stored as a tapered position (0.0-1.0) plus a user-renamable name. Defaults are Quiet (25%), Mix (50%), Loud (75%).
Three ways:
open lyra://reference/2 (slots are 1-indexed in URLs).Jumping is a one-shot - Lyra writes the stored tapered value straight to the Apollo's CRMonitorLevelTapered, the same parameter the hardware knob and Console's monitor section read. No ramping, no faders, no smoothing. The HUD pops up with the slot's name (or calibrated label) for ~2.5 seconds.
A checkmark appears on the jump item whenever the current live volume matches the slot's stored value (within ~0.5%). So if you turn the knob to exactly Mix, the menu shows Mix as checked.
Adjust the monitor to where you want it (knob, F-keys, slider - anything works), then Reference Levels → Save current as "<slot name>". The slot now stores that exact tapered position; the next Ctrl+F-key jump will land there.
Calibration data is cleared on overwrite. If the slot previously had a measured dB SPL value attached (from Audita - see below), manually saving over it clears the SPL value, because the prior measurement was tied to the old level and no longer applies. The menu and HUD revert to showing a percentage until you re-calibrate.
Reference Levels → Rename "<slot name>"… opens a small modal with a text field. Keep it short - names appear in the menu and the HUD pill. Common names: K-14, K-20, Mix, Quiet, Cinema, Late night, 79 dB.
lyra://reference/cycle advances to the next slot in order (1 → 2 → 3 → 1). Useful for a single Stream Deck button or a Shortcut that toggles between presets.
Audita is the Headroom SPL meter for macOS. After running Audita's target-level calibration - speaker at known distance, pink noise at known level, measure with the meter - Audita knows what dB SPL your monitor system produces at any given monitor knob position.
Click "Send to Lyra…" in Audita to write that calibrated level into one of Lyra's three slots. Audita opens a sheet:
When you confirm, Audita fires lyra://reference/{n}/save?name=…&dBSPL=… and Lyra:
Mix - 85 dB SPL.From that point on, the slot displays as Mix - 85 dB SPL in:
Mix - 85 dB SPL ⌃F11)The dB SPL value is metadata - Lyra doesn't re-measure or verify it. It's the number you actually think in once your room is calibrated. If you change anything in the signal chain (different speaker, different distance, different room treatment), re-run Audita and re-send the slot.
Don't have Audita? The slot still works - it just shows a percentage instead of a measured dB SPL value. Audita is the calibration layer; Lyra is the recall layer.
lyra://) #Lyra registers a custom lyra:// URL scheme so any tool that can fire a URL can drive it - Stream Deck, Keyboard Maestro, Apple Shortcuts, BetterTouchTool, AppleScript, open from the shell, other apps (Audita).
Try it from Terminal:
open "lyra://reference/2"
open "lyra://volume/set?tapered=0.4"
open "lyra://mute/toggle"
Unknown URLs are logged to the console and silently ignored - they won't crash or interrupt anything.
| URL | What it does |
|---|---|
lyra://reference/1 | Jump to slot 1 |
lyra://reference/2 | Jump to slot 2 |
lyra://reference/3 | Jump to slot 3 |
lyra://reference/cycle | Advance to the next slot |
lyra://reference/{1|2|3}/save?name=Mix&dBSPL=85 | Write a slot. Lyra reads the live Apollo level for the tapered value. dBSPL optional. |
lyra://reference/{n}/save?name=Mix&dBSPL=85&tapered=0.5 | Same, but with an explicit tapered override. |
save is the URL Audita uses; you can use it from your own scripts too. If you omit name, the slot's existing name is kept. If you omit dBSPL, any existing calibration value on the slot is preserved (only an explicit value will replace it - URLs can't clear calibration).
| URL | What it does |
|---|---|
lyra://volume/set?tapered=0.5 | Set absolute level. tapered is 0.0-1.0 - same scale as CRMonitorLevelTapered. |
lyra://volume/up | Step up one full step (default 1/16). |
lyra://volume/up?fine=1 | Step up by half - Shift equivalent. |
lyra://volume/down | Step down one full step. |
lyra://volume/down?fine=1 | Step down by half. |
| URL | What it does |
|---|---|
lyra://mute/toggle | Toggle mute (same as F10) |
lyra://dim/toggle | Toggle DIM (same as Shift+F10) |
lyra://mono/toggle | Toggle Mono (same as Option+F10) |
All action URLs require Lyra to be running and your Apollo to be reachable (Mixer Engine connected). If the Mixer Engine isn't up, volume / toggle URLs are silently dropped - Lyra never queues or replays them. Pass x-error=… (see below) if you want a structured failure callback instead of a silent drop.
Three endpoints return live state via the x-success callback. They have no effect on their own - they only fire when the caller supplies an x-success URL.
| URL | Returns (appended to x-success) |
|---|---|
lyra://volume/get?x-success=… | tapered, db |
lyra://reference/current?x-success=… | slot (1-3), slot_name, slot_tapered, slot_dbspl if calibrated. Slot keys are absent when no slot matches the live volume. |
lyra://state?x-success=… | All of the above, plus muted, dim, mono, mixer_connected, device_connected, device_name, volume_step. |
db may be absent for a fraction of a second on first launch - Lyra waits for the first CRMonitorLevel push from the Apollo before it can answer. After that it stays in sync with the hardware knob.
Any endpoint - read or action - accepts two optional callback URLs so you can chain Lyra into a wider workflow (Shortcuts, Drafts, Stream Deck multi-actions, …):
| Param | Fires when |
|---|---|
x-success | The endpoint completed. Returned values are appended to this URL as query items. Action endpoints also append result=ok plus a small status object (e.g. muted=true). |
x-error | The endpoint failed. Lyra appends error_code and error_message. Common codes: mixer_disconnected, invalid_slot, invalid_argument, device_not_found, unknown_endpoint. |
Example - chain "set volume to 0.4 and then run a Raycast script":
lyra://volume/set?tapered=0.4&x-success=raycast://script/run?id=announce-mix
After Lyra finishes the set, it opens:
raycast://script/run?id=announce-mix&db=-12.34&result=ok&tapered=0.4000
(Lyra appends its return params to whatever was already in your x-success URL.)
Example - read the current state and feed it into a Shortcut:
lyra://state?x-success=shortcuts://run-shortcut?name=LogMonitor
The Shortcut receives the full state dictionary as URL query items - use Get value from URL for each key.
The full URL reference - every endpoint, every return field, worked examples for Shortcuts, Stream Deck, Keyboard Maestro and AppleScript - lives on the API reference page.
On macOS 13 (Ventura) and later, Lyra exposes an App Intents bundle: eleven intents covering every action you can drive from the URL scheme, plus structured reads. They show up in the macOS Shortcuts app, in Spotlight (pre-wired App Shortcuts for the most-used ones), in Siri (via a named Shortcut), and in Focus Filters (e.g. switch to reference slot 2 when Mixing Focus engages).
On macOS 12 (Monterey), Lyra runs identically except this section is hidden. The Shortcuts integration requires macOS 13+ because App Intents itself does.
The full reference - all eleven intents with parameters and return values, the App Shortcuts list, the Siri-via-named-Shortcut workflow, and the Focus Filter setup - lives in the Automation API reference.
Lyra ships four interactive widgets in the macOS widget gallery (v1.6+). Open Notification Centre (click the date/time, or swipe right with two fingers from the right edge of the trackpad), scroll to the bottom, click Edit Widgets, and search for Lyra.
Lyra's widgets on the desktop - the Control Panel (left), plus the Monitor Toggles and Volume widgets.
| Widget | Buttons | Sizes |
|---|---|---|
| Lyra - Control Panel | Volume, Mute / DIM / Mono, Slots 1-3 (all in one) | Large |
| Lyra - Monitor Toggles | Mute, DIM, Mono | Medium |
| Lyra - Volume | Volume down, Volume up | Small or Medium |
| Lyra - Reference Levels | Slot 1, Slot 2, Slot 3 | Medium |
Every widget carries a small Lyra header - the menu-bar knob mark, the "Lyra" wordmark, and a live connection dot (green when the Mixer Engine is reachable, red when it isn't). The small Volume widget shows the dot alone to save space; the larger widgets add a "Connected" / "Offline" label.
Each tap fires the matching lyra:// URL, so the actual action runs in Lyra's main process. Lyra has to be running - it's a menu bar app, so just having it launched is enough, no window needed.
The widgets reflect live state: the Mute and Mono buttons fill with the accent colour when active, the Reference Levels widget highlights the slot you're currently on, and every button dims when Lyra isn't connected to the Mixer Engine. State updates a moment after each tap, once the Apollo echoes the change back to Lyra.
Volume buttons step by Lyra's Volume step preference (set in Settings), the same as F11 / F12. Reference slot buttons honour your stored slot names and dB SPL calibrations exactly like the menu's Reference Levels submenu.
macOS 14+ required. The widgets use interactive buttons that aren't available on earlier macOS. On macOS 12 and 13, Lyra runs identically - the widget gallery entries are simply absent.
Lyra talks to UA Mixer Engine - the local daemon that backs UA Console. It connects over TCP to localhost:4710 and uses UA's NULL-terminated text protocol (get /path\0, set /path value\0, subscribe /path\0 → JSON responses).
The monitor parameters Lyra reads/writes:
| Parameter | Path | What it controls |
|---|---|---|
| Volume (tapered) | /devices/0/outputs/4/CRMonitorLevelTapered | Float 0.0-1.0 - the Monitor knob |
| Mute | /devices/0/outputs/4/Mute | Bool |
| DIM | /devices/0/outputs/4/DimOn | Bool - Apollo applies the attenuation set in Console |
| Mix-to-Mono | /devices/0/outputs/4/MixToMono | Bool |
The paths above show outputs/4 because that's the Monitor output on Apollo Twin X. As of v1.6, the output index is discovered at connect, not pinned - Lyra asks the Mixer Engine which output is the Monitor and uses whichever index your hardware reports. Twin X reports 4; other Apollo models report different indices. If discovery doesn't return a usable answer, Lyra falls back to 4 so existing Twin X setups are unaffected.
Lyra subscribes to all four properties, so any change made elsewhere - turning the hardware knob, clicking Console's mute, automating Mixer parameters from a script - is reflected in Lyra's icon, menu, and HUD within ~100 ms. This works both ways: Lyra is a peer to Console, not a layer on top of it.
If the menu shows "<device> - UA Mixer not running", click Start UA Mixer Engine. Lyra prefers the headless engine:
UA Mixer Engine.app at /Library/Application Support/Universal Audio/Apollo/UA Mixer Engine.app - installed by recent UA drivers, runs the TCP daemon with no GUI window.Once the daemon is up, Lyra's TCP client auto-connects and the menu flips to "Connected: <device>".
If the daemon drops (Console crashes, you quit it, the machine sleeps) Lyra retries the connection every 5 seconds in the background. You don't need to relaunch - leave Lyra running and it will pick the connection back up when the daemon returns. Same on Apollo unplug/replug: when CoreAudio reports the device gone, Lyra stops trying and waits; when it's back, the connection resumes.
Two opt-in toggles in Settings tie Lyra to the Apollo's presence:
Each automation has a paired Show a notification when this happens sub-toggle (on by default). The banner explains what Lyra just did. Once you trust the automation you can turn the notification off and let it run silently. macOS asks for notification permission the first time Lyra tries to post.
Both parent toggles default to off. Lyra has no opinion about your UA workflow until you ask for one.
Open with Cmd+, or Settings… in the menu. New in v1.6. Two sections.
Settings persist across launches in the standard macOS preferences. Changing the volume step takes effect immediately - no relaunch needed.
Lyra is $9.99, one-time purchase, via Lemon Squeezy.
The first launch starts a 7-day free trial with full functionality - every feature, no nags, no watermarks. The trial start date persists across reinstalls. After 7 days Lyra shows a hard-lock window on launch and refuses to run until you activate a license.
The hard lock only fires at launch - if you're already running Lyra when the trial expires, nothing happens until your next restart.
As of v1.6, if your trial has ended and you're testing a build before committing to buy (a fix you reported, a feature against your gear), the hard-lock window offers a Test for 5 minutes button. Click it and Lyra runs normally for five minutes, then locks again. You get two sessions per day - the counter resets at local midnight. The limit is intentionally narrow: it's a verification tool, not a workaround.
Click Buy Lyra - $9.99 in the menu (or the Buy button on the website). Lemon Squeezy emails you the license key after checkout.
Menu → Enter License Key… opens the activation window. Paste your key and click Activate. Activation requires an internet connection. Once activated, Lyra works fully offline.
Each license can be active on up to two Macs. To free a slot, open Lyra on the machine you want to release, Menu → Manage License… → Deactivate, then activate on the new Mac.
Check the Lemon Squeezy confirmation email. If you can't find it, email hello@headroomstudio.dev with the address you used at checkout.
Lyra silently re-validates your license on each launch. If activation has been revoked (refund, deactivation, abuse), the local credentials are cleared and the app drops to trial / expired state. Network failures are silently ignored - the offline case is benefit-of-the-doubt.
Lyra ships with Sparkle (the standard macOS auto-update framework). The app checks headroomstudio.dev/lyra/appcast.xml every 24 hours; when a higher build number is found you'll see an update prompt at the next launch (or sooner if a check is in flight).
To check on demand: Menu → Check for Updates…
Updates are signed with EdDSA - Sparkle verifies the signature against the public key embedded in Lyra before installing. If the signature doesn't match the download is rejected.
Release notes: headroomstudio.dev/lyra/releases.html.
Menu → Launch at Login registers Lyra with SMAppService (macOS 13+). The checkmark always reflects the actual system state - if you flip the toggle in System Settings → General → Login Items, Lyra's menu follows.
On macOS 12 the menu item is hidden (SMAppService is 13+ only). Use the legacy Login Items pane in System Settings if you're on Monterey.
Quit and reopen System Settings. This is a known macOS bug - the new entry sometimes doesn't show until the prefpane is reloaded. If Lyra still doesn't appear, use the + button at the bottom to add it manually from /Applications/Lyra.app.
Lyra looks for any output device whose name contains "Universal Audio", "Apollo", or "UA ". Open Audio MIDI Setup and check that your Apollo appears in the device list. If it does but Lyra still doesn't see it, check the device name - older driver versions use slightly different strings. Lyra logs all detected devices to the system console on launch ([Lyra] === AUDIO DEVICES ===) - that's the fastest way to see exactly what names CoreAudio is reporting.
Click Start UA Mixer Engine in the menu. This launches the headless UA Mixer Engine.app if it's installed, falling back to UA Console. Either should make Lyra connect within a few seconds. If neither path works, opening UA Console manually always starts the daemon as a side-effect.
The default step is 1/16 of full range (~6.25%). Hold ⇧ with F11/F12 for half steps. As of v1.6 the step size is configurable in Settings → General → Volume step (range ~1.6% to 12.5%) - drag the slider to a step that suits your gain staging. Power users can still drive lyra://volume/set?tapered=… for any precision they want.
Make sure you haven't disabled it: defaults read com.headroom.Lyra showHUD should return 1 (or no value, which defaults to enabled). If you previously set it to false:
defaults delete com.headroom.Lyra showHUD
Then relaunch Lyra.
Reset Sparkle's "I already checked / I already skipped that version" state:
defaults delete com.headroom.Lyra
Relaunch, then Menu → Check for Updates… to force a fresh check. This also resets other preferences (HUD setting, saved target device, etc.) to factory defaults.
Wait 30 seconds and try again - Lemon Squeezy occasionally needs a moment to propagate. If it still fails, double-check that there's no leading/trailing whitespace in the key you pasted (the activation field trims, but it's worth a look). Otherwise email hello@headroomstudio.dev with the key and order number.
The trial start date persists across reinstalls, so reinstalling won't reset it. If you genuinely just installed Lyra for the first time and it's reading as expired, email hello@headroomstudio.dev.
Out of scope, in case any of these are dealbreakers:
Monitor output (outputs/4) is exposed. Cue outputs and aux faders have their own paths in the mixer.localhost:4710 only. It does not work with RME TotalMix FX, MOTU CueMix, Focusrite Control, etc.lyra://volume/set?tapered=… - your launcher can hold any number of presets.lyra:// URLs from any tool of your choice.Email hello@headroomstudio.dev with what happened, what you expected, and ideally what's in the Console.app logs filtered by Lyra (Lyra prefixes every log line with [Lyra] or [AudioManager] so it's easy to grab the relevant slice).