Touch phone -> Studio turns on and starts recording
One-Click Mobile Studio Automation & Ardour API Engine
This page outlines the end-to-end architecture, API integration layers, OSC/Lua communication protocols, and execution plan for triggering studio power, booting the DAW host, instantiating timestamped Ardour sessions from templates, arming target tracks, and rolling transport from a single mobile button.
1. End-to-End Workflow & Pipeline Architecture
The objective is a zero-touch "Quick Record" pipeline: pressing a single button on a mobile phone (iOS Shortcut, Android Widget, Telegram Bot, or PWA) triggers an automated multi-stage turn-up sequence across the studio network.
+---------------------------------------------------------------------------------------------------+
| Mobile Phone (iOS / Android / PWA) |
| [ 🔴 ONE-TOUCH QUICK RECORD ] |
+---------------------------------------------------------------------------------------------------+
| POST https://studio.drheck.dev/api/quick-record
v
+---------------------------------------------------------------------------------------------------+
| Host: ul (Gateway Orchestrator & Fastify/Python API Service) |
| 1. Dispatches RF power turn-on command to pikvm-studio |
| 2. Triggers ATX power / Wake-on-LAN for DAW host (bmo95) |
| 3. Polls bmo95 SSH & RT audio stack until ready |
| 4. Dispatches Ardour project instantiation & OSC track arming commands |
| 5. Pushes confirmation webhook / notification to phone |
+-----------------------------------|---------------------------------------|-----------------------+
| |
SSH Command | | SSH / OSC (Port 3819)
v v
+-----------------------------------------------+ +-----------------------------------------------+
| Host: pikvm-studio (Sub-GHz Transceiver) | | Host: bmo95 (DAW Host) |
| - Runs: sudo studio-power all on | | - Boots PREEMPT_RT kernel |
| - Engages YARD Stick One at 433.92 MHz | | - Auto-starts JACK + zita duplex bridges |
| - Powers: Rack, Monitors, Moroccan Lamp, LEDs | | - Launches Ardour 9 with Session Template |
| - Pulses ATX power header via GPIO / Relay | | - Arms selected tracks via OSC UDP 3819 |
+-----------------------------------------------+ | - Rolls global recording transport |
+-----------------------------------------------+
2. Technical Integration Layers
Layer A: Studio Power & DAW Wakeup (pikvm-studio)
- RF Outlet Switching:
- API calls
pikvm-studio:sudo /usr/local/bin/studio-power all on(switches the Moroccan Lamp, Recording Rack Gear, the Subwoofer, PA Monitors, the TV, and Neewer LEDs).
- API calls
- DAW Host ATX Wake:
pikvm-studiotriggers the ATX power button onbmo95via PiKVM ATX header.
Layer B: Audio Stack & Real-Time Engine Initialization (bmo95)
When bmo95 boots, its systemd user units initialize audio backend:
# Auto-launched on bmo95 user session login:
$ systemctl --user start jack.service
$ systemctl --user start zita-a2j-blue.service zita-j2a-blue.service
$ systemctl --user start zita-a2j-green.service zita-j2a-green.service
$ systemctl --user start zita-a2j-yellow.service zita-j2a-yellow.service
$ ~/git/audio_config/profiles/jack-alsa/set-port-aliases.sh
Layer C: Ardour 9 Headless/GUI Instantiation (bmo95)
Ardour 9 natively supports automated session creation from templates via CLI:
$ DISPLAY=:0 ardour9 \
--no-splash \
--no-announcements \
--template "48ch_Studio_Master" \
--new "/data/sessions/Session_$(date +%Y-%m-%d_%H%M%S)"
--template <name>: Clones track structures, input/output bus routing, plugin chains, and monitor matrices from a predefined master studio template.--new <path>: Automatically creates and locks a timestamped project folder without interactive GUI dialogs.--no-splash&--no-announcements: Suppresses startup modal windows that block automation.
Layer D: Ardour Remote API Control (OSC & Lua)
Ardour provides two high-performance remote control APIs:
1. Open Sound Control (OSC) — UDP Port 3819
Ardour includes a comprehensive built-in OSC server that can be queried and triggered over UDP.
| OSC Address | Arguments | Action |
|---|---|---|
/strip/recenable |
i: strip_num, i: state (1/0) |
Arms or disarms a specific audio track |
/track/recenable |
s: track_name, i: state (1/0) |
Arms or disarms track by name (e.g. Red_01) |
/rec_enable_toggle |
(none) | Toggles global master record arm status |
/transport_record |
i: 1 |
Sets global transport record arm to ON |
/transport_play |
i: 1 |
Rolls transport (starts playback/recording) |
/transport_stop |
i: 1 |
Stops transport |
/save_state |
(none) | Flushes and saves session XML to disk |
/access_action |
s: "Action/Name" |
Executes any internal Ardour GUI menu action |
2. Native Lua Scripting (ardour-exec / OSC /eval)
Ardour includes an embedded Lua execution engine. Complex automation scripts (e.g., auto-detecting connected microphones, verifying input levels, and arming only active channels) can be evaluated dynamically:
-- Lua script to arm all capture tracks matching prefix
for r in Session:get_routes():iter() do
local track = r:to_track()
if not track:is_null() and track:name():match("^Red_") then
track:rec_enable_control():set_value(1, PBD.GroupControlDisposition.NoGroup)
end
end
Session:maybe_enable_record()
Session:request_transport_speed(1.0, true, ARDOUR.TransportRequestSource.TRS_UI)
3. Implementation Plan & Execution Phases
The automation will be rolled out across 4 distinct phases:
Phase 1: Ardour Template & OSC Server Configuration (bmo95)
- Create master 48-channel template (
48ch_Studio_Master) with calibrated input routing (Red_01..12,Blue_01..12,Green_01..12,Yellow_01..12). - Enable and persist Ardour OSC server on UDP port 3819 (
Preferences -> Control Surfaces -> Open Sound Control). - Build a Python helper script
/usr/local/bin/ardour-controlonbmo95usingpython-oscto test track arming, session saving, and transport control via CLI.
Phase 2: Host Wakeup & Readiness Polling (ul & pikvm-studio)
- Configure Wake-on-LAN and PiKVM ATX power button trigger for
bmo95. - Write an orchestration wrapper script
/home/luke/bin/studio-quick-recordonulthat:- Sends RF power command
sudo studio-power all ontopikvm-studio. - Sends ATX boot command to
bmo95. - Polls
ssh -q bmo95until the PREEMPT_RT kernel is responsive (timeout: 45s). - Confirms
jack.serviceand all 3zita-a2j-*duplex bridges are active.
- Sends RF power command
Phase 3: Automated Session Launch & Transport Trigger
- Extend
/home/luke/bin/studio-quick-recordto invoke Ardour session creation via SSH. - Send OSC burst to arm user-selected tracks (e.g. Drums, Vocals, Master Stereo Bus).
- Roll recording transport and log initial take time.
Phase 4: Mobile Webhook & UI Control Endpoint
- Deploy a lightweight authenticated REST service on
ul(Fastify / Python Flask on dedicated ULA IP). - Expose two endpoints behind Authentik OIDC single sign-on / API token:
POST /api/studio/record/start: Powers studio, boots host, opens session, arms tracks, rolls recording.POST /api/studio/record/stop: Stops transport, saves session, optionally powers down.
- Configure iOS Shortcuts / Android Widget / Telegram Bot webhook for single-tap activation from any smartphone.
4. Python Ardour Remote Controller Reference
#!/usr/bin/env python3
"""Ardour Remote Control Client via OSC (UDP 3819)"""
import sys, time
from pythonosc import udp_client
ARDOUR_IP = "127.0.0.1"
ARDOUR_PORT = 3819
def get_client(ip=ARDOUR_IP, port=ARDOUR_PORT):
return udp_client.SimpleUDPClient(ip, port)
def start_recording(client, tracks_to_arm=[1, 2, 3, 4]):
print("-> Arming tracks:", tracks_to_arm)
for track_num in tracks_to_arm:
client.send_message("/strip/recenable", [track_num, 1])
time.sleep(0.05)
print("-> Enabling global record arm...")
client.send_message("/transport_record", 1)
time.sleep(0.1)
print("-> Rolling transport...")
client.send_message("/transport_play", 1)
print("-> Recording active.")
def stop_and_save(client):
print("-> Stopping transport...")
client.send_message("/transport_stop", 1)
time.sleep(0.2)
print("-> Saving session...")
client.send_message("/save_state", [])
print("-> Session saved successfully.")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: ardour-control <start|stop|save>")
sys.exit(1)
c = get_client()
cmd = sys.argv[1].lower()
if cmd == "start":
start_recording(c)
elif cmd == "stop":
stop_and_save(c)
elif cmd == "save":
c.send_message("/save_state", [])