No description
Find a file
Tyler B. c7b4f18882 vibed
2026-05-31 15:17:26 -04:00
.DS_Store vibed 2026-05-31 15:17:26 -04:00
.gitignore vibed 2026-05-31 15:17:26 -04:00
calibrate.py vibed 2026-05-31 15:17:26 -04:00
common.py vibed 2026-05-31 15:17:26 -04:00
debug_osm_connection.py vibed 2026-05-31 15:17:26 -04:00
osmdca_config.json vibed 2026-05-31 15:17:26 -04:00
README.md vibed 2026-05-31 15:17:26 -04:00
requirements.txt vibed 2026-05-31 15:17:26 -04:00
spl_dca_rider.py vibed 2026-05-31 15:17:26 -04:00
test_mixingstation_dca.py vibed 2026-05-31 15:17:26 -04:00
test_osm_reader.py vibed 2026-05-31 15:17:26 -04:00

SPL DCA Rider

Slow DCA fader rider for dance recitals. Reads SPL from Open Sound Meter, calculates smooth DCA reduction using dual fast/slow control, and sends MIDI CC to Mixing Station (PreSonus StudioLive 64S) via macOS IAC virtual MIDI.

This is a slow SPL rider, not a hard limiter. Use a real limiter on your PA / system processor for safety.


Dual Control Concept

The rider uses two independent SPL measurements:

Control Metric Behavior
Fast Short-term (LCeq1s, LCFast, CFast) Accepts SPL within a target ± range. Above the high bound, applies proportional reduction.
Slow Long-term (LCslow, CSlow, LCeq) Acts as a ceiling. Above the max threshold, applies proportional reduction.

Both control outputs are combined (max of the two) and smoothed before being sent as MIDI CC. This prevents short peaks from triggering large moves while still catching sustained loudness.

Defaults

  • Fast target: 95 dB
  • Fast range: ±10 dB (effective bounds: 85105 dB)
  • Slow max: 85 dB
  • Smoothing: 4 seconds (first-order low-pass)

The DCA only moves downward (never boosts above base CC). Recovery happens at the smoothing rate when SPL drops below limits.


Install

python3 -m pip install -r requirements.txt

Requires Python 3.11+.


macOS IAC Driver Setup

  1. Open Audio MIDI Setup (Applications/Utilities).
  2. Go to Window → Show MIDI Studio.
  3. Double-click the IAC Driver icon.
  4. Check "Device is online".
  5. Add at least one bus (e.g. "Bus 1").
  6. Click Apply.

The default port name is "IAC Driver Bus 1".


Mixing Station MIDI Mapping

  1. Open Mixing Station in offline mode (no console connected) for testing, or connect to your StudioLive 64S.
  2. Go to Settings → MIDI → Learn Mode.
  3. Select the DCA fader you want to control.
  4. Send the MIDI CC (use test_mixingstation_dca.py sweep mode).
  5. Mixing Station will learn the mapping.
  6. Save the profile.

Quick Start

OSM v1.5.2 listens on TCP port 49007 for remote control. Your OSM source UUID is auto-read from autosave.osm.

Step 1: Verify OSM SPL Reading

python3 test_osm_reader.py --transport tcp-request --target -40

Output (4 updates/sec):

    TIME      SPL     PEAK   REDUCTION    CC  PATH
13:30:50    -36.5    -20.3    0.00 dB    100 *  spl=level  peak=measurementPeak

The SPL value is in dBFS (relative to digital full scale). To work in dBC, provide a calibration offset.

Step 2: Calibrate (optional)

If you know the relationship between dBFS and dBC (e.g. 0 dBFS = 95 dBC):

python3 test_osm_reader.py --transport tcp-request --target 95 --cal-offset 95

This adds +95 dB to every SPL reading: -40 dBFS → +55 dBC, 0 dBFS → 95 dBC.

Step 3: Verify MIDI DCA Movement

python3 test_mixingstation_dca.py \
    --midi-port "IAC Driver Bus 1" \
    --cc 20 \
    --channel 1 \
    --sweep \
    --min-cc 80 \
    --max-cc 100

Step 4: Run the Rider

python3 spl_dca_rider.py \
    --fast-target 95 \
    --fast-range 10 \
    --slow-max 85 \
    --smoothing-time 4 \
    --cal-offset 95 \
    --midi-port "IAC Driver Bus 1" \
    --cc 20 \
    --channel 1

Console output:

12:41:03  fast 96.2 path meters.LCeq1s | slow 84.7 path meters.LCslow | peak N/A | fastReq 0.0 | slowReq 0.0 | req 0.0 | applied 0.00 | CC 100 sent no
12:41:04  fast 106.0 path meters.LCeq1s | slow 86.2 path meters.LCslow | peak N/A | fastReq 1.0 | slowReq 1.2 | req 1.2 | applied 0.30 | CC 99 sent yes

Other Transports

WebSocket:

python3 spl_dca_rider.py \
    --transport websocket \
    --osm-url ws://127.0.0.1:8080 \
    --fast-target 95 --fast-range 10 --slow-max 85 \
    --midi-port "IAC Driver Bus 1" --cc 20 --channel 1

HTTP polling:

python3 spl_dca_rider.py \
    --transport http-poll \
    --osm-url http://127.0.0.1:8080/api \
    --fast-target 95 --fast-range 10 --slow-max 85 \
    --midi-port "IAC Driver Bus 1" --cc 20 --channel 1

SPL Path Discovery

OSM sends data in different JSON formats depending on transport. The script uses priority-based hint matching for both fast and slow SPL:

Fast SPL hints (in priority order): LCeq1s, LCFast, CFast, fast, LCeq, splC, spl

Slow SPL hints (in priority order): LCslow, CSlow, slow, LCeq, splC, spl

If the detected paths don't match your OSM data, use explicit overrides:

python3 spl_dca_rider.py \
    --fast-path "meters.LCeq1s" \
    --slow-path "meters.LCslow" \
    --peak-path "meters.LCpeak"

Exact paths use dot-separated JSON key traversal (e.g. data.meters.C.Fast).


How It Works

  1. OSM provides SPL data via the selected transport (TCP, WebSocket, HTTP, or OSC).
  2. The script extracts fast and slow SPL values using hint-based or explicit path matching.
  3. Fast control: if SPL exceeds fast_target + fast_range, reduction = (SPL - high) * fast_ratio. Within the range, no additional reduction. Below the low bound, slow recovery.
  4. Slow control: if SPL exceeds slow_max, reduction = (SPL - slow_max) * slow_ratio. Below the max, slow recovery.
  5. Combined: requested = max(fast_requested, slow_requested), clamped to max_reduction.
  6. Smoothing: first-order low-pass filter: applied += (requested - applied) * (dt / tau). Attack and release can use different time constants.
  7. MIDI: cc_value = base_cc - round(applied * cc_per_db), clamped 0127, never above base CC. Sent only on change.

Command Reference

spl_dca_rider.py

Argument Default Description
OSM connection
--transport tcp-request Transport: tcp-request, websocket, http-poll, osc-udp
--osm-url ws://127.0.0.1:8080 WebSocket/HTTP URL
--osm-host 127.0.0.1 TCP host
--osm-port 49007 TCP port
--source-uuid Source UUID (auto-read from autosave.osm)
--poll-interval 0.25 Poll/sample interval (s)
--osc-host 127.0.0.1 OSC listen host
--osc-port 9000 OSC listen port
Calibration
--cal-offset 0 dBFS→dBC offset
SPL path overrides
--fast-path Exact JSON path for fast SPL
--slow-path Exact JSON path for slow SPL
--peak-path Exact JSON path for peak
Control
--fast-target 95 Fast SPL target center
--fast-range 10 Fast SPL range (± around target)
--fast-low target-range Fast low bound override
--fast-high target+range Fast high bound override
--slow-max 85 Slow SPL maximum
--fast-ratio 1.0 Proportional gain for fast
--slow-ratio 1.0 Proportional gain for slow
--max-reduction 12 Maximum total reduction (dB)
Smoothing
--smoothing-time 4.0 Time constant (s), used for both attack & release
--attack-time Attack time constant (overrides smoothing-time)
--release-time Release time constant (overrides smoothing-time)
MIDI
--midi-port IAC Driver Bus 1 MIDI port name substring
--cc 20 MIDI CC number
--channel 1 MIDI channel (116)
--base-cc 100 MIDI CC at 0 dB reduction
--cc-per-db 2.0 MIDI CC steps per dB
Startup / Exit
--no-initial-send off Skip sending base CC on startup
--return-to-base-on-exit off Send base CC on Ctrl+C

test_osm_reader.py

Argument Default Description
--target 95 Target SPL
--transport tcp-request tcp-request, multicast, websocket, http-poll, osc-udp
--cal-offset 0 Calibration offset dBFS→dBC
--osm-host 127.0.0.1 OSM TCP host
--osm-port 49007 OSM TCP port
--source-uuid OSM source UUID (auto-read)
--osm-url ws://127.0.0.1:8080 OSM URL (websocket/http-poll)
--poll-interval 0.5 Poll interval (s)
--osc-host 127.0.0.1 OSC listen host
--osc-port 9000 OSC listen port
--deadband 0.7 dB deadband around target
--down-rate 0.75 Reduction increase rate (dB/s)
--up-rate 0.25 Reduction decrease rate (dB/s)
--max-reduction 6 Maximum reduction (dB)
--base-cc 100 MIDI CC at 0 dB reduction
--cc-per-db 2.0 MIDI CC steps per dB

debug_osm_connection.py

Argument Default Description
--host 127.0.0.1 Host to scan
--ports 3000,5000,8000,8080,8081,9000,49007 Comma-separated ports
--paths (empty),/,/api,/status,/metrics,/ws,/socket Comma-separated paths
--timeout 3 Connection timeout (s)
--url Test a single URL (auto-detects protocol)
--listen Listen for OSM UDP multicast

test_mixingstation_dca.py

Argument Default Description
--midi-port IAC Driver Bus 1 MIDI port name substring
--cc 20 MIDI CC number
--channel 1 MIDI channel (116)
--value Send single CC value and exit
--sweep off Sweep from min to max and back
--min-cc 0 Sweep minimum
--max-cc 127 Sweep maximum
--step 1 Sweep step
--delay 0.2 Sweep step delay (s)

Troubleshooting OSM Connection

  1. Ensure OSM Server mode is enabled. Check the Server checkbox in OSM's settings (enables TCP on port 49007).

  2. Source UUID discovery. If autosave.osm isn't found, provide the UUID manually: --source-uuid "{d762b054-...}". Find it in ~/Library/Application Support/OpenSoundMeter/OpenSoundMeter/autosave.osm.

  3. macOS network permissions. Allow Python in System Settings → Privacy & Security → Local Network if prompted.

  4. Test localhost first. Always confirm connectivity on 127.0.0.1 before trying a LAN IP.


OSM Protocol Details

OSM v1.5.2 uses the following protocol on port 49007:

  • TCP (command/response): 4-byte little-endian length prefix + raw JSON request. Response is 4-byte LE length + qCompress-ed JSON.
  • UDP multicast (live data): Plain JSON broadcasts to 239.255.42.42:49007. Not usable from the same machine (port conflict).

TCP request types:

  • requestChanged with source UUID → source settings (including level)
  • requestData with source UUID → FFT / time domain data

Safety

  • Always use a real limiter on your PA/system processor.
  • This tool is designed for slow musical riding, not peak limiting.
  • Test thoroughly during rehearsal before any performance.
  • Have a bypass plan (e.g. Mixing Station scene recall).