# DSP 408 Protocol Documentation
# TODO
- Existing but not implemented commands: 0x19, 0x25, 0x28, 0x2E, 0x50, 0x51, 0x52, 0x53
- Commands 0x2E, 0x50, 0x51, 0x52, 0x53 seem to be used for firmware update: DO NOT SEND THEM WITHOUT KNOWLEDGE - RISK OF DEVICE BRICK (I brick my device)
- Commands: 0x19, 0x24, 0x28 are triggered by upload preset
- Identify: 0x2C parameters
- Implement: if 2 DSP with same ip, they need to share same socket since dsp dont accept multiples connections
- In unlock check if first byte is device id
- Dsp and python code allow to send commands even if dsp is locked
- Convert back data as in 0x38 to display real value
# Tables of Content
## Encoding Index
| Encoding | Used by | Description |
|---|---|---|
| [Preset Name](#21-preset-name) | Preset save/load | 14-byte fixed string |
| [Channel Name](#22-channel-name) | Channel labeling | 8-byte fixed string |
| [Password](#23-password) | Login/lock | 4-byte fixed string |
| [Preset Index](#31-preset-index) | Preset select | 1-byte index (21 presets) |
| [Channel Index](#32-channel-index) | Gain, Mute, Delay, PEQ, GEQ | 1-byte index (12 channels) |
| [PEQ Band Index](#33-peq-band-index) | PEQ | 1-byte index (9 bands) |
| [Frequency Band Index](#34-frequency-band-index) | GEQ | 1-byte index (31 bands) |
| [Delay Unit](#35-delay-unit) | Channel Delay | 1-byte unit selector |
| [Compressor Ratio](#36-compressor-ratio) | Compressor | 1-byte index (16 ratios) |
| [Input Source Type](#37-input-source-type) | Input select | 1-byte index (4 sources) |
| [PEQ Filter Type](#38-peq-filter-type) | PEQ | 1-byte index (9 filter types) |
| [Crossover Slope](#39-crossover-slope) | Crossover | 1-byte index (21 slopes) |
| [Matrix Input Mask](#41-matrix-input-mask) | Input routing | 1-byte bitmask |
| [Channel Gain](#51-channel-gain) | Gain | 16-bit dB value (-60 to +12 dB) |
| [GEQ / PEQ Gain](#52-geq--peq-gain) | GEQ, PEQ | 8-bit ±12 dB value |
| [PEQ Q Factor](#53-peq-q-factor) | PEQ | 8-bit logarithmic Q value |
| [Hold](#61-hold-gate) | Gate | 16-bit ms value |
| [Attack](#62-attack-compressor) | Compressor | 16-bit ms value |
| [Release](#63-release-compressor) | Compressor | 16-bit ms value |
| [Knee](#64-knee-compressor) | Compressor | 8-bit dB value |
| [Sidechain Frequency](#65-sidechain-frequency-compressor) | Compressor | 16-bit logarithmic Hz value |
| [Gate Threshold](#66-gate-threshold) | Gate | 16-bit dB value |
| [Compressor Threshold](#67-compressor-threshold) | Compressor | 8-bit dB value |
| [Channel Delay](#71-channel-delay) | Delay | 8-bit linear value |
| [Meter Level](#81-meter-level) | Metering | Float16 level + peak byte |
---
## Commands Index
| Command | Category | Description |
|---|---|---|
| `0x01` — [Acknowledgement](#0x01--acknowledgement) | Connection & Session | Signals the device finished loading/processing and is ready |
| `0x02` — [Not Implemented](#0x02--not-implemented) | Connection & Session | Signals that the command just sent is invalid/unimplemented |
| `0x10` — [Start](#0x10--start) | Connection & Session | Initiates a session with the device |
| `0x11` — [Disconnect](#0x11--disconnect) | Connection & Session | Terminates the session |
| `0x12` — [Acknowledgement (Client → Device)](#0x12--acknowledgement-client--device) | Connection & Session | Client-side confirmation after loading parameters |
| `0x13` — [Device Name](#0x13--device-name) | Device Info | Reads the device's display name |
| `0x14` — [Current Preset Index](#0x14--current-preset-index) | Preset Management | Reads the currently active preset |
| `0x15` — [Set Delay Unit](#0x15--set-delay-unit) | Channel Processing | Changes the display unit used for delay values |
| `0x20` — [Recall Preset](#0x20--recall-preset) | Preset Management | Recalls (loads) a stored preset |
| `0x21` — [Store Preset](#0x21--store-preset) | Preset Management | Saves the current live settings into a preset slot |
| `0x22` — [Get Preset Modification Status](#0x22--get-preset-modification-status) | Preset Management | Reports which presets have unsaved/modified state |
| `0x23` — [Factory Reset](#0x23--factory-reset) | Preset Management | Restores factory defaults |
| `0x27 / 0x24` — [Config Chunk (Request / Response)](#0x27--0x24--config-chunk-request--response) | Preset Management | Streams the full device configuration in indexed chunks |
| `0x26` — [Store Preset Name](#0x26--store-preset-name) | Preset Management | Sets the working preset name prior to saving |
| `0x29` — [Get Preset Name](#0x29--get-preset-name) | Preset Management | Reads the name of a given preset |
| `0x2A` — [Copy Channel](#0x2a--copy-channel) | Channel Configuration | Copies one channel's settings onto another |
| `0x2C` — [Device Flags](#0x2c--device-flags) | Device Info | Reads device status flags, including lock state |
| `0x2D` — [Unlock Device](#0x2d--unlock-device) | Security | Authenticates with a password to unlock the device |
| `0x2F` — [Lock Device](#0x2f--lock-device) | Security | Locks the device behind a password |
| `0x30` — [Set Compressor](#0x30--set-compressor) | Dynamics | Sets compressor parameters for a channel |
| `0x31` — [Set Low-Pass Filter](#0x31--set-low-pass-filter) | Filters & EQ | Sets the low-pass filter for a channel |
| `0x32` — [Set High-Pass Filter](#0x32--set-high-pass-filter) | Filters & EQ | Sets the high-pass filter for a channel |
| `0x33` — [Set PEQ Band](#0x33--set-peq-band) | Filters & EQ | Sets one parametric EQ band (input channels only) |
| `0x34` — [Set Gain](#0x34--set-gain) | Channel Processing | Sets a channel's gain |
| `0x35` — [Set Mute](#0x35--set-mute) | Channel Processing | Mutes or unmutes a channel |
| `0x36` — [Invert Gain](#0x36--invert-gain) | Channel Processing | Inverts polarity on a channel |
| `0x38` — [Set Delay](#0x38--set-delay) | Channel Processing | Sets a channel's delay value |
| `0x39` — [Set Input Source](#0x39--set-input-source) | Input & Routing | Selects the analog/test signal source |
| `0x3A` — [Matrix Routing](#0x3a--matrix-routing) | Input & Routing | Sets which inputs feed a given output channel |
| `0x3B` — [Link Channel](#0x3b--link-channel) | Channel Configuration | Groups channels so they move together |
| `0x3C` — [Bypass EQ](#0x3c--bypass-eq) | Channel Processing | Enables or bypasses all EQ processing on a channel |
| `0x3D` — [Set Channel Name](#0x3d--set-channel-name) | Channel Configuration | Sets the display name of an output channel |
| `0x3E` — [Set Gate Parameters](#0x3e--set-gate-parameters) | Dynamics | Sets noise-gate parameters for a channel |
| `0x3F` — [Set Output Limiter](#0x3f--set-output-limiter) | Dynamics | Sets output-limiter parameters (output channels only) |
| `0x40` — [Get Level Meters](#0x40--get-level-meters) | Metering | Reads current level-meter and clip-indicator values for all channels |
| `0x41` — [Set Matrix Gain](#0x41--set-matrix-gain) | Input & Routing | Sets the send gain from one input to one output |
| `0x48` — [Set GEQ Band](#0x48--set-geq-band) | Filters & EQ | Sets one band of a 31-band graphic EQ (input channels only) |
| `0x49` — [Set GEQ Bypass](#0x49--set-geq-bypass) | Filters & EQ | Enables or bypasses the graphic EQ on an input channel (input channels only) |
---
# Encoding / Decoding Specification
## 1. Overview & Conventions
- All multi-byte numeric fields are **little-endian**: the low-order byte (`LO`) is transmitted/stored first, the high-order byte (`HI`) second.
```text
LO = raw & 0xFF
HI = (raw >> 8) & 0xFF
```
```text
raw = LO | (HI << 8)
```
- Quantized parameters (e.g. gain, threshold) are rounded to the nearest valid step **before** encoding. The decoding formula always reverses the encoding formula exactly.
- All lookup-table fields (enums, indices) are 1 byte unless otherwise noted.
---
## 2. Fixed-Length Strings
All string fields share one encoding model: write the text as ASCII bytes, then pad up to the fixed length with a pad byte. Decoding reverses this: read the fixed length, then strip trailing pad bytes.
### 2.1 Preset Name
| Field | Value |
|---|---|
| **Purpose** | Human-readable preset name |
| **Size** | 14 bytes |
| **Pad byte** | `0x20` (space) |
**Encoding (value → raw):**
```text
bytes = ASCII(name)[0:14]
bytes = bytes + 0x20 × (14 - len(bytes))
```
**Decoding (raw → value):**
```text
name = ASCII(bytes).rstrip(0x20)
```
### 2.2 Channel Name
| Field | Value |
|---|---|
| **Purpose** | Human-readable channel name |
| **Size** | 8 bytes |
| **Pad byte** | `0x00` |
**Encoding (value → raw):**
```text
bytes = ASCII(name)[0:8]
bytes = bytes + 0x00 × (8 - len(bytes))
```
**Decoding (raw → value):**
```text
name = ASCII(bytes).rstrip(0x00)
```
### 2.3 Password
| Field | Value |
|---|---|
| **Purpose** | Password value |
| **Size** | 4 bytes |
| **Pad byte** | `0x00` |
**Encoding (value → raw):**
```text
bytes = ASCII(password)[0:4]
bytes = bytes + 0x00 × (4 - len(bytes))
```
**Decoding (raw → value):**
```text
password = ASCII(bytes).rstrip(0x00)
```
---
## 3. Enumerated & Index Values
All fields in this section are 1 byte and work as a direct lookup table.
**Encoding (value → raw):** find the desired option in the table and use its byte value.
**Decoding (raw → value):** look up the byte value in the table to get the option.
### 3.1 Preset Index
**Range:** `0x00`–`0x14` (21 presets)
| Index | Preset |
|---|---|
| `0x00` | Preset 0 |
| `0x01` | Preset 1 |
| … | … |
| `0x14` | Preset 20 |
### 3.2 Channel Index
**Range:** `0x00`–`0x0B` (12 channels)
| Index | Channel | Index | Channel |
|---|---|---|---|
| `0x00` | IN_A | `0x06` | OUT_3 |
| `0x01` | IN_B | `0x07` | OUT_4 |
| `0x02` | IN_C | `0x08` | OUT_5 |
| `0x03` | IN_D | `0x09` | OUT_6 |
| `0x04` | OUT_1 | `0x0A` | OUT_7 |
| `0x05` | OUT_2 | `0x0B` | OUT_8 |
### 3.3 PEQ Band Index
**Range:** `0x00`–`0x08` (9 bands)
**Band limit by channel type:**
| Channel Type | Maximum Band |
|---|---|
| Input | B7 (`0x07`) |
| Output | B8 (`0x08`) |
| Index | Band | Index | Band |
|---|---|---|---|
| `0x00` | B0 | `0x05` | B5 |
| `0x01` | B1 | `0x06` | B6 |
| `0x02` | B2 | `0x07` | B7 |
| `0x03` | B3 | `0x08` | B8 |
| `0x04` | B4 | | |
### 3.4 Frequency Band Index
**Range:** `0x00`–`0x1E` (31 bands, ISO 1/3-octave centers)
| Index | Frequency | Index | Frequency | Index | Frequency | Index | Frequency |
|---|---|---|---|---|---|---|---|
| `0x00` | 20 Hz | `0x08` | 125 Hz | `0x10` | 800 Hz | `0x18` | 5 kHz |
| `0x01` | 25 Hz | `0x09` | 160 Hz | `0x11` | 1 kHz | `0x19` | 6.3 kHz |
| `0x02` | 31.5 Hz | `0x0A` | 200 Hz | `0x12` | 1.25 kHz | `0x1A` | 8 kHz |
| `0x03` | 40 Hz | `0x0B` | 250 Hz | `0x13` | 1.6 kHz | `0x1B` | 10 kHz |
| `0x04` | 50 Hz | `0x0C` | 315 Hz | `0x14` | 2 kHz | `0x1C` | 12.5 kHz |
| `0x05` | 63 Hz | `0x0D` | 400 Hz | `0x15` | 2.5 kHz | `0x1D` | 16 kHz |
| `0x06` | 80 Hz | `0x0E` | 500 Hz | `0x16` | 3.15 kHz | `0x1E` | 20 kHz |
| `0x07` | 100 Hz | `0x0F` | 630 Hz | `0x17` | 4 kHz | | |
### 3.6 Compressor Ratio
**Range:** `0x0000`–`0x0F00` (16 values)
| Value | Ratio | Value | Ratio |
|---|---|---|---|
| `0x0000` | 1:1 | `0x0800` | 3.5:1 |
| `0x0100` | 1.1:1 | `0x0900` | 4:1 |
| `0x0200` | 1.3:1 | `0x0A00` | 5:1 |
| `0x0300` | 1.5:1 | `0x0B00` | 6:1 |
| `0x0400` | 1.7:1 | `0x000C` | 8:1 |
| `0x0500` | 2:1 | `0x0D00` | 10:1 |
| `0x0600` | 2.5:1 | `0x0E00` | 20:1 |
| `0x0700` | 3:1 | `0x0F00` | LIMIT |
### 3.7 Input Source Type
**Range:** `0x00`–`0x03` (4 types)
| Value | Input Source |
|---|---|
| `0x00` | Analog |
| `0x01` | Pink Noise |
| `0x02` | White Noise |
| `0x03` | Sine Wave |
### 3.8 PEQ Filter Type
**Range:** `0x00`–`0x08` (9 types)
| Value | Filter | Value | Filter |
|---|---|---|---|
| `0x00` | Peak | `0x05` | HP 6dB |
| `0x01` | Low Shelf | `0x06` | HP 12dB |
| `0x02` | High Shelf | `0x07` | All Pass 1 |
| `0x03` | LP 6dB | `0x08` | All Pass 2 |
| `0x04` | LP 12dB | | |
### 3.9 Crossover Slope
**Range:** `0x00`–`0x14` (21 values)
| Value | Slope | Value | Slope |
|---|---|---|---|
| `0x00` | Bypass | `0x0B` | BW 30 |
| `0x01` | BW 6 | `0x0C` | BL 30 |
| `0x02` | BL 6 | `0x0D` | BW 36 |
| `0x03` | BW 12 | `0x0E` | BL 36 |
| `0x04` | BL 12 | `0x0F` | LR 36 |
| `0x05` | LR 12 | `0x10` | BW 42 |
| `0x06` | BW 18 | `0x11` | BL 42 |
| `0x07` | BL 18 | `0x12` | BW 48 |
| `0x08` | BW 24 | `0x13` | BL 48 |
| `0x09` | BL 24 | `0x14` | LR 48 |
| `0x0A` | LR 24 | | |
---
## 4. Bit Mask Values
### 4.1 Matrix Input Mask
| Field | Value |
|---|---|
| **Purpose** | Which inputs are active/routed |
| **Size** | 1 byte |
| **Range** | `0x00`–`0x0F` |
| Input | Bit mask |
|---|---|
| IN_A | `0x01` |
| IN_B | `0x02` |
| IN_C | `0x04` |
| IN_D | `0x08` |
**Encoding (value → raw):** OR together the bit mask of every active input.
```text
raw = (IN_A ? 0x01 : 0) | (IN_B ? 0x02 : 0) | (IN_C ? 0x04 : 0) | (IN_D ? 0x08 : 0)
```
**Decoding (raw → value):** AND the raw byte with each bit mask to test whether that input is active.
```text
IN_A_active = (raw & 0x01) != 0
IN_B_active = (raw & 0x02) != 0
IN_C_active = (raw & 0x04) != 0
IN_D_active = (raw & 0x08) != 0
```
---
## 5. Gain & Equalizer Parameters
### 5.1 Channel Gain
| Field | Value |
|---|---|
| **Purpose** | Per-channel input/output gain |
| **Size** | 2 bytes, little-endian |
| **Range** | -60 dB to +12 dB |
**Quantization:**
| dB Range | Resolution |
|---|---|
| `[-60, -20)` | 0.5 dB |
| `[-20, +12]` | 0.1 dB |
**Encoding (value → raw):**
```text
if -60 <= dB < -20:
raw = round((dB + 60) / 0.5)
if -20 <= dB <= 12:
raw = round(80 + (dB + 20) / 0.1)
LO = raw & 0xFF
HI = (raw >> 8) & 0xFF
```
**Decoding (raw → value):**
```text
raw = LO | (HI << 8)
if 0 <= raw <= 79:
dB = raw × 0.5 - 60
if 80 <= raw <= 320:
dB = (raw - 80) × 0.1 - 20
```
### 5.2 GEQ / PEQ Gain
| Field | Value |
|---|---|
| **Purpose** | Graphic/parametric EQ band gain |
| **Size** | 2 bytes |
| **Range** | -12 dB to +12 dB |
| **Resolution** | 0.1 dB |
**Encoding (value → raw):**
```text
raw = round(dB × 10 + 120)
LO = raw & 0xFF
HI = (raw >> 8) & 0xFF
```
**Decoding (raw → value):**
```text
dB = (raw - 120) / 10
```
| Raw | Gain |
|---|---|
| `0x00` | -12 dB |
| `0x78` | 0 dB |
| `0xF0` | +12 dB |
### 5.3 PEQ Q Factor
| Field | Value |
|---|---|
| **Purpose** | Parametric EQ bandwidth (Q) |
| **Size** | 1 byte, logarithmic index |
| **Range** | 0.40 to 128.00 |
| **Raw range** | `0x00`–`0x64` (0–100) |
**Encoding (value → raw):**
```text
raw = round(log(Q / 0.40) / log(128 / 0.40) × 100)
```
**Decoding (raw → value):**
```text
Q = 0.40 × (128 / 0.40) ^ (raw / 100)
```
---
## 6. Dynamics Processing Parameters
### 6.1 Hold (Gate)
| Field | Value |
|---|---|
| **Purpose** | Gate hold time |
| **Size** | 2 bytes, little-endian |
| **Range** | 10 ms to 999 ms |
**Encoding (value → raw):**
```text
raw = hold_ms - 1
LO = raw & 0xFF
HI = (raw >> 8) & 0xFF
```
**Decoding (raw → value):**
```text
raw = LO | (HI << 8)
hold_ms = raw + 1
```
### 6.2 Attack (Compressor)
| Field | Value |
|---|---|
| **Purpose** | Compressor attack time |
| **Size** | 2 bytes, little-endian |
| **Range** | 1 ms to 999 ms |
**Encoding (value → raw):**
```text
raw = attack_ms - 1
LO = raw & 0xFF
HI = (raw >> 8) & 0xFF
```
**Decoding (raw → value):**
```text
raw = LO | (HI << 8)
attack_ms = raw + 1
```
### 6.3 Release
| Field | Value |
|---|---|
| **Purpose** | Release time |
| **Size** | 2 bytes, little-endian |
| **Range** | 10 ms to 3000 ms |
**Encoding (value → raw):**
```text
raw = release_ms - 1
LO = raw & 0xFF
HI = (raw >> 8) & 0xFF
```
**Decoding (raw → value):**
```text
raw = LO | (HI << 8)
release_ms = raw + 1
```
### 6.4 Knee (Compressor)
| Field | Value |
|---|---|
| **Purpose** | Compressor knee width |
| **Size** | 2 bytes |
| **Range** | 0 dB to 12 dB |
| **Resolution** | 1 dB |
**Encoding (value → raw):**
```text
raw = round(knee_db)
LO = raw & 0xFF
HI = (raw >> 8) & 0xFF
```
**Decoding (raw → value):**
```text
knee_db = LO | (HI << 8)
```
### 6.5 Sidechain Frequency (Compressor)
| Field | Value |
|---|---|
| **Purpose** | Compressor sidechain filter frequency |
| **Size** | 2 bytes, little-endian |
| **Range** | 19.70 Hz to 20160 Hz |
| **Raw range** | `0x0000`–`0x012C` |
**Encoding (value → raw):**
```text
raw = round(log(Hz / 19.70) / log(20160 / 19.70) × 300)
LO = raw & 0xFF
HI = (raw >> 8) & 0xFF
```
**Decoding (raw → value):**
```text
raw = LO | (HI << 8)
Hz = 19.70 × (20160 / 19.70) ^ (raw / 300)
```
### 6.6 Gate Threshold Encoding
Gate threshold uses a **16-bit little-endian** value.
| Field | Value |
|---|---|
| **Purpose** | Gate open/close threshold |
| **Size** | 2 bytes, little-endian |
| **Range** | `-90.0 dB` to `0.0 dB` |
| **Raw range** | `0x0000`–`0x00B4` (0–180) |
| **Resolution** | `0.5 dB` |
**Encoding (dB → raw):**
```text
raw = round((dB - (-90.0)) × 2)
LO = raw & 0xFF
HI = (raw >> 8) & 0xFF
```
**Decoding (raw → dB):**
```text
raw = LO | (HI << 8)
dB = -90.0 + (raw / 2)
```
### 6.7 Compressor Threshold
| Field | Value |
|---|---|
| **Purpose** | Compressor threshold |
| **Size** | 2 bytes |
| **Range** | -110 dB to 0 dB |
| **Resolution** | 0.5 dB |
**Encoding (value → raw):**
```text
raw = round((dB - (-110)) × 2)
LO = raw & 0xFF
HI = (raw >> 8) & 0xFF
```
**Decoding (raw → value):**
```text
raw = LO | (HI << 8)
dB = (raw / 2) - 110
```
---
## 7. Delay
### 7.1 Channel Delay
| Field | Value |
|---|---|
| **Purpose** | Per-channel delay |
| **Size** | 1 byte |
| **Raw range** | `0x00`–`0xFF` (0–255) |
**Range by selected unit** (see [Delay Unit](#35-delay-unit)):
| Unit | `delay_min` | `delay_max` |
|---|---|---|
| Millisecond | 0.000 ms | 680.000 ms |
| Meter | 0.000 m | 233.580 m |
| Feet | 0.000 ft | 766.329 ft |
**Encoding (value → raw):**
```text
raw = round((delay - delay_min) / (delay_max - delay_min) × 255)
```
**Decoding (raw → value):**
```text
delay = delay_min + (raw / 255) × (delay_max - delay_min)
```
### 7.2 Delay Unit
**Range:** `0x00`–`0x02` (3 units)
| Value | Unit |
|---|---|
| `0x00` | Millisecond |
| `0x01` | Meter |
| `0x02` | Feet |
---
## 8. Metering
### 8.1 Meter Level
| Field | Value |
|---|---|
| **Purpose** | Per-channel RMS + peak metering, with clip flags |
| **Payload size** | 38 bytes (exactly; any other length is an error) |
| **Layout** | 12 channels × 3 bytes = 36 bytes, followed by 2 clip-flag bytes |
| **Channel order** | Channels 0–3 are inputs, channels 4–11 are outputs, each mapped in enum order |
#### Channel record (3 bytes, at offset `i = channel × 3`)
| Byte offset | Field | Format |
|---|---|---|
| `i + 0` | RMS low byte | Low byte of the RMS float16 |
| `i + 1` | Shared high byte | High byte of **both** the RMS and Peak float16s |
| `i + 2` | Peak low byte | Low byte of the Peak float16 |
Both values are IEEE 754 half-precision floats (little-endian pairs). The peak is not a separate 1-byte value: it reuses byte `i + 1` as its high byte, so its byte order is reversed relative to the offset order.
```text
rms_raw = float16_decode(lo = payload[i], hi = payload[i + 1])
peak_raw = float16_decode(lo = payload[i + 2], hi = payload[i + 1])
```
If a float16 fails to decode, the raw value defaults to `0.0`, which converts to `MIN_DB` (-60 dB).
#### Clip flags (bytes 36–37)
| Channel index | Flag location |
|---|---|
| 0–3 | `payload[36]`, bit `index` |
| 4–11 | `payload[37]`, bit `index - 4` |
```text
clip = (flags_byte & (1 << bit)) != 0
```
#### Raw → dB conversion
Raw values are converted with a piecewise-linear curve, then clamped.
| Constant | Value | Meaning |
|---|---|---|
| `X0` | -25.0 dB | Breakpoint |
| `Y0` | 1.0 | Raw value at the breakpoint |
| `K1` | 0.0104 | Raw units per dB below the breakpoint (`raw < Y0`) |
| `K2` | 0.0208 | Raw units per dB above the breakpoint (`raw >= Y0`) |
| `MIN_DB` | -60.0 dB | Lower clamp |
| `MAX_DB` | 12.0 dB | Upper clamp |
**Decoding (raw → dB):**
```text
if raw < Y0: db = X0 + (raw - Y0) / K1
else: db = X0 + (raw - Y0) / K2
db = clamp(db, MIN_DB, MAX_DB)
```
**Encoding (dB → raw):** the inverse of the above. Values outside the clamp range are not recoverable.
```text
db = clamp(db, MIN_DB, MAX_DB)
if db < X0: raw = Y0 + (db - X0) * K1
else: raw = Y0 + (db - X0) * K2
```
#### Full decode
```text
require len(payload) == 38
for index in 0..11:
i = index * 3
rms_db = raw_to_db(float16_decode(payload[i], payload[i + 1]))
peak_db = raw_to_db(float16_decode(payload[i + 2], payload[i + 1]))
clip = index < 4 ? payload[36] bit index
: payload[37] bit (index - 4)
meter[index] = { rms: rms_db, peak: peak_db, clip }
inputs = meter[0..3] → InputChannel enum order
outputs = meter[4..11] → OutputChannel enum order
```
---
# Command Specification
## 1. Frame Structure
Every command — query or response — shares one frame layout. Colors match the highlighted examples throughout this document:
| Field | Size | Color | Description |
|---|---|---|---|
| `STX` | 2 bytes | ■ | Fixed start marker: `10 02` |
| `DIR` | 2 bytes | ■ | Direction (see below) |
| `LEN` | 1 byte | ■ | Byte count of `CMD + DATA` |
| `CMD` | 1 byte | ■ | Command code |
| `DATA` | `LEN - 1` bytes | ■ | Command payload (may be 0 bytes) |
| `ETX` | 2 bytes | ■ | Fixed end marker: `10 03` |
| `CS` | 1 byte | ■ | Checksum (see below) |
Example skeleton:
10 02 DIR DIR LEN CMD DATA... 10 03 CS
### Direction (`DIR`)
| Value | Direction |
|---|---|
| `00 XX` | Host → Device (Query) |
| `XX 00` | Device → Host (Response) |
XX represents the device Id
### Length (`LEN`)
```text
LEN = size(CMD) + size(DATA) = 1 + size(DATA)
```
### Checksum (`CS`)
The checksum is the **XOR of every byte in the frame from `DIR` through `DATA`, inclusive, with an initial seed of `0x01`**.
**Encoding (value → raw):**
```text
CS = DIR[0] ^ DIR[1] ^ LEN ^ CMD ^ DATA[0] ^ ... ^ DATA[N-1] ^ 0x01
```
**Decoding / verification (raw → check):**
```text
computed_CS = DIR[0] ^ DIR[1] ^ LEN ^ CMD ^ DATA[0] ^ ... ^ DATA[N-1] ^ 0x01
valid = (computed_CS == received_CS)
```
---
## 2. Command Template
Every command in this document uses the same shape:
**`0xXX` — Name**
**Direction:** Host→Device / Device→Host / Both
**Category:** ...
**Purpose:** one-line description
**Sequencing:** what must precede/follow this command, if anything
**Payload Fields** — table of `Offset | Field | Size | Encoding Reference | Value/Range`
**Example** — annotated, color-coded hex frame
---
## 3. Connection & Session
### `0x01` — Acknowledgement
**Direction:** Device → Host
**Category:** Connection & Session
**Purpose:** Signals the device finished loading/processing and is ready.
**Sequencing:** Sent after any state-changing command.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| — | (none) | 0 bytes | Command has no payload |
**Example**
10 02
01 00
01
01
10 03
00
---
### `0x02` — Not Implemented
**Direction:** Device → Host
**Category:** Connection & Session
**Purpose:** Signals that the command just sent is invalid/unimplemented.
**Sequencing:** Sent in place of `0x01` when a command fails to parse or is unsupported.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| — | (none) | 0 bytes | Command has no payload |
**Example**
10 02
01 00
01
02
10 03
00
---
### `0x10` — Start
**Direction:** Host → Device (Query), Device → Host (Response)
**Category:** Connection & Session
**Purpose:** Initiates a session with the device.
**Sequencing:** First command sent on connect.
**Payload Fields (Response only)**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Status | 1 byte | `0x13` = OK |
**Example — Query**
10 02
00 01
01
10
10 03
11
**Example — Response**
10 02
01 00
02
10
13
10 03
01
---
### `0x11` — Disconnect
**Direction:** Host → Device
**Category:** Connection & Session
**Purpose:** Terminates the session.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| — | (none) | 0 bytes | Command has no payload |
**Example**
10 02
00 01
01
11
10 03
10
---
### `0x12` — Acknowledgement (Client → Device)
**Direction:** Host → Device
**Category:** Connection & Session
**Purpose:** Client-side confirmation after loading parameters (mirrors `0x01` but in the opposite direction).
**Sequencing:** Followed by `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| — | (none) | 0 bytes | Command has no payload |
**Example**
10 02
00 01
01
12
10 03
13
---
## 4. Device Info
### `0x13` — Device Name
**Direction:** Host → Device (Query), Device → Host (Response)
**Category:** Device Info
**Purpose:** Reads the device's display name.
**Payload Fields (Response only)**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` – `Byte N-1` | Device name | 13 bytes | [Enc §2.2 Channel Name](#22-channel-name)|
**Example — Query**
10 02
00 01
01
13
10 03
12
**Example — Response**
10 02
01 00
0E
13
44 53 50 34 30 38 20 56 30 31 30 34 50
10 03
45
---
### `0x2C` — Device Flags
**Direction:** Host → Device (Query), Device → Host (Response)
**Category:** Device Info
**Purpose:** Reads device status flags, including lock state. Not fully reverse-engineered.
**Payload Fields (Response only)**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Unknown | 1 byte | `00` observed |
| `Byte 2-3` | Unknown | 2 bytes | `27 0F` observed |
| `Byte 4-5` | Unknown | 2 bytes | `00 00` observed |
| `Byte 6` | Lock state | 1 byte | `01` = Locked, `00` = Unlocked |
**Example — Query**
10 02
00 01
01
2C
10 03
2D
**Example — Response**
10 02
01 00
07
2C
00 27 0F 00 00 00
10 03
03
---
## 5. Preset Management
### `0x14` — Current Preset Index
**Direction:** Host → Device (Query), Device → Host (Response)
**Category:** Preset Management
**Purpose:** Reads the currently active preset.
**Payload Fields (Response only)**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Preset index | 1 byte | [Enc §3.1 Preset Index](#31-preset-index)|
**Example — Query**
10 02
00 01
01
14
10 03
15
**Example — Response**
10 02
01 00
02
14
06
10 03
10
---
### `0x20` — Recall Preset
**Direction:** Host → Device
**Category:** Preset Management
**Purpose:** Recalls (loads) a stored preset.
**Sequencing:** Followed by `0x01` (Acknowledgement) once loading finishes.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Preset index | 1 byte | [Enc §3.1 Preset Index](#31-preset-index) |
**Example**
10 02
00 01
02
20
10
10 03
32
---
### `0x21` — Store Preset
**Direction:** Host → Device
**Category:** Preset Management
**Purpose:** Saves the current live settings into a preset slot.
**Sequencing:** Call `0x26` (Store Preset Name) first to set the name being saved. Must be followed by `0x12`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Preset slot | 1 byte | [Enc §3.1 Preset Index](#31-preset-index), no 0x00|
**Example**
10 02
00 01
02
21
01
10 03
CS
---
### `0x22` — Get Preset Modification Status
**Direction:** Host → Device (Query), Device → Host (Response)
**Category:** Preset Management
**Purpose:** Reports which presets have unsaved/modified state.
**Payload Fields (Response only)**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1`–`Byte 20` | Per-preset modified flag | 1 byte each | `0xFF` = modified, `0x00` = unmodified |
**Example — Query**
10 02
00 01
01
22
10 03
23
**Example — Response**
10 02
01 00
15
22
FF 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00
10 03
37
---
### `0x23` — Factory Reset
**Direction:** Host → Device
**Category:** Preset Management
**Purpose:** Restores factory defaults.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| — | (none) | 0 bytes | Command has no payload |
**Example**
10 02
00 01
01
23
10 03
CS
---
### `0x27` / `0x24` — Config Chunk (Request / Response)
**Direction:** `0x27` Host→Device (Request), `0x24` Device→Host (Response)
**Category:** Preset Management
**Purpose:** Streams the full device configuration in indexed chunks.
**Sequencing:** Request `0x27` is followed by response `0x24`. Must be followed by `0x12`, after full batch request.
**Payload Fields — Request (`0x27`)**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Chunk index | 1 byte | `0x00`–`0x1C` |
**Payload Fields — Response (`0x24`)**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Sub-index | 1 bytes | `0x00`–`0x1C` |
| `Byte 2`–`Byte 51` | Configuration data | 50 bytes | binary/ASCII mixed |
**Example — Request**
10 02
00 01
03
27
INDEX
10 03
CS
**Example — Response**
10 02
01 00
34
24
00 FF 00 44 65 66 61 75 6C 74 20 50 72 65 73 65 74 49 6E 41 00 00 00 00 00 00 00 63 00 63 00 00 00 78 00 78 00 78 00 78 00 78 00 78 00 78 00 78 00
10 03
BF
---
### Config Data — Reassembled Payload Layout
The 50-byte pages from chunks `0x00`–`0x1C` are concatenated in index order into a single buffer before parsing. Below is that buffer's field layout, byte-for-byte, in the order fields are consumed.
**Legend (protocol constants used below)**
| Symbol | Meaning |
|---|---|
| `PNL` | `PRESET_NAME_LENGTH` |
| `CNL` | `CHANNEL_NAME_LENGTH` |
| `N_IN` | number of input channels (`len(INPUT_ORDER)`) |
| `N_OUT` | number of output channels (`len(OUTPUT_ORDER)`) |
| `N_FREQ` | number of GEQ bands (`len(list(FrequencyValue))`) |
| `N_PEQ_IN` | number of input PEQ bands (`len(PEQ_INPUT_BANDS)`) |
| `N_PEQ_OUT` | number of output PEQ bands (`len(PEQ_OUTPUT_BANDS)`) |
**Header**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `0x00` | Flags (unused) | 2 bytes | reserved |
| `0x02` | Preset name | `PNL` bytes | ASCII, raw → `raw_to_preset_name` |
**Per Input Channel — repeats `N_IN` times**
| Offset (within block) | Field | Size | Encoding |
|---|---|---|---|
| `+0x00` | Channel name | `CNL` bytes | ASCII, raw → `raw_to_channel_name` |
| `+CNL` | Gate: attack | 2 bytes | `u16` → `raw_to_attack_ms` |
| `+CNL+2` | Gate: release | 2 bytes | `u16` → `raw_to_release_ms` |
| `+CNL+4` | Gate: hold | 2 bytes | `u16` → `raw_to_hold_ms` |
| `+CNL+6` | Gate: threshold | 2 bytes | `u16` → `raw_to_threshold_db` |
| `+CNL+8` | GEQ gains | `2 × N_FREQ` bytes | `N_FREQ` × `u16` → `raw_to_eq_gain` |
| `+CNL+8+2·N_FREQ` | PEQ chain | `6 × N_PEQ_IN` bytes | `N_PEQ_IN` bands, each: `u16` gain, `u16` freq, `u8` Q, `u8` filter type |
| … | Crossover: HP freq | 2 bytes | `u16` → `raw_to_frequency_hz` |
| … | Crossover: LP freq | 2 bytes | `u16` → `raw_to_frequency_hz` |
| … | Crossover: HP slope | 1 byte | `u8` → `raw_to_crossover_filter` |
| … | Crossover: LP slope | 1 byte | `u8` → `raw_to_crossover_filter` |
| … | Channel gain | 2 bytes | `u16` → `raw_to_channel_gain` |
| … | Phase inverted | 1 byte | `u8` bool |
| … | Reserved | 2 bytes | skipped |
| … | Delay (raw) | 1 byte | `u8`, resolved via `raw_to_delay(value, delay_unit)` once the header-level delay unit is parsed |
| … | Linked channels | 2 bytes | `u16` bitmap → `raw_to_link_channels` |
Per-channel block size: `CNL + 2·N_FREQ + 6·N_PEQ_IN + 22` bytes.
**Per Output Channel — repeats `N_OUT` times** (immediately follows the last input channel block)
| Offset (within block) | Field | Size | Encoding |
|---|---|---|---|
| `+0x00` | Channel name | `CNL` bytes | ASCII, raw → `raw_to_channel_name` |
| … | Matrix: connected | 2 bytes | `u16` bitmap → `raw_to_matrix` |
| … | Matrix: gains | 8 bytes | 4 × `u16` → `raw_to_matrix_gain` |
| … | Crossover (HP/LP freq + slope) | 6 bytes | same layout as input crossover |
| … | PEQ chain | `6 × N_PEQ_OUT` bytes | `N_PEQ_OUT` bands, same per-band layout as input |
| … | Compressor: ratio | 2 bytes | `u16` → `raw_to_ratio` |
| … | Compressor: attack | 2 bytes | `u16` → `raw_to_attack_ms` |
| … | Compressor: release | 2 bytes | `u16` → `raw_to_release_ms` |
| … | Compressor: knee | 2 bytes | `u16` → `raw_to_knee_db` |
| … | Compressor: threshold | 2 bytes | `u16` → `raw_to_threshold_db` |
| … | Limiter: attack | 2 bytes | `u16` → `raw_to_attack_ms` |
| … | Limiter: release | 2 bytes | `u16` → `raw_to_release_ms` |
| … | Reserved | 2 bytes | skipped |
| … | Limiter: threshold | 2 bytes | `u16` → `raw_to_threshold_db` |
| … | Channel gain | 2 bytes | `u16` → `raw_to_channel_gain` |
| … | Phase inverted | 1 byte | `u8` bool |
| … | Reserved | 2 bytes | skipped |
| … | Delay (raw) | 1 byte | `u8`, resolved via `raw_to_delay(value, delay_unit)` |
| … | Linked channels | 2 bytes | `u16` bitmap → `raw_to_link_channels` |
Per-channel block size: `CNL + 6·N_PEQ_OUT + 42` bytes.
**Global Flags** (immediately follows the last output channel block)
| Offset (within section) | Field | Size | Encoding |
|---|---|---|---|
| `+0x00` | Input mute bitmap | 2 bytes | `u16`, bit *i* = `INPUT_ORDER[i]` muted |
| `+0x02` | Output mute bitmap | 2 bytes | `u16`, bit *i* = `OUTPUT_ORDER[i]` muted |
| `+0x04` | Input PEQ band mute | `2 × N_IN` bytes | one `u16` per input channel, bit *j* = `PEQ_INPUT_BANDS[j]` bypassed |
| `+0x04+2·N_IN` | Output PEQ band mute | `2 × N_OUT` bytes | one `u16` per output channel, bit *j* = `PEQ_OUTPUT_BANDS[j]` bypassed |
**Input Source & Delay Unit** (immediately follows Global Flags)
| Offset (within section) | Field | Size | Encoding |
|---|---|---|---|
| `+0x00` | Input source type | 2 bytes | `u16` → `raw_to_input_source` |
| `+0x02` | Input source frequency | 2 bytes | `u16` → `raw_to_frequency_value` |
| `+0x04` | Delay unit | 2 bytes | `u16` → `raw_to_delay_unit` (applies to all previously-read raw delay bytes) |
**Trailing Bypass Flags** (immediately follows Input Source & Delay Unit)
| Offset (within section) | Field | Size | Encoding |
|---|---|---|---|
| `+0x00` | Input PEQ chain bypass bitmap | 2 bytes | `u16`, bit *i* = `INPUT_ORDER[i]`'s PEQ chain bypassed |
| `+0x02` | Output PEQ chain bypass bitmap | 2 bytes | `u16`, bit *i* = `OUTPUT_ORDER[i]`'s PEQ chain bypassed |
| `+0x04` | Input GEQ bypass bitmap | 2 bytes | `u16`, bit *i* = `INPUT_ORDER[i]`'s GEQ bypassed |
**Total reassembled payload size:**
`2 + PNL + N_IN·(CNL + 2·N_FREQ + 6·N_PEQ_IN + 22) + N_OUT·(CNL + 6·N_PEQ_OUT + 42) + 4 + 2·N_IN + 2·N_OUT + 4 + 2 + 6` bytes.
---
### `0x26` — Store Preset Name
**Direction:** Host → Device
**Category:** Preset Management
**Purpose:** Sets the working preset name prior to saving.
**Sequencing:** Call before `0x21` (Store Preset).
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1`–`Byte 14` | Preset name | 14 bytes | [Enc §2.1 Preset Name](#21-preset-name) |
**Example**
10 02
00 01
0F
26
50 72 65 73 65 74 20 4E 61 6D 65 20 20 20
10 03
CS
---
### `0x29` — Get Preset Name
**Direction:** Host → Device (Query), Device → Host (Response)
**Category:** Preset Management
**Purpose:** Reads the name of a given preset.
**Payload Fields — Query**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Preset index | 1 byte | [Enc §3.1 Preset Index](#31-preset-index) |
**Payload Fields — Response**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1`–`Byte N` | Preset name | 14 bytes | [Enc §2.1 Preset Name](#21-preset-name) |
**Example — Query**
10 02
00 01
02
29
00
10 03
2B
**Example — Response**
10 02
01 00
10
29
4D 61 69 6E 20 70 72 65 73 65 74 20 20 20
10 03
17
---
## 6. Security
### `0x2D` — Unlock Device
**Direction:** Host → Device (Query), Device → Host (Response)
**Category:** Security
**Purpose:** Authenticates with a password to unlock the device.
**Payload Fields — Query**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Unknown | 1 byte | `00` observed |
| `Byte 2`–`Byte N` | Password | 4 bytes | [Enc §2.3 Password](#23-password) |
**Payload Fields — Response**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Status category | 1 byte | `00` observed |
| `Byte 2` | Authentication result | 1 byte | `00` = Failed, `01` = Success |
**Example — Query (password `1234`)**
10 02
00 01
06
2D
00 31 32 33 34
10 03
2F
**Example — Response**
10 02
01 00
03
2D
00 01
10 03
2F
---
### `0x2F` — Lock Device
**Direction:** Host → Device
**Category:** Security
**Purpose:** Locks the device behind a password.
**Sequencing:** Followed by `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1`–`Byte N` | Password | 4 bytes | [Enc §2.3 Password](#23-password) |
**Example (password `1234`)**
10 02
00 01
05
2F
31 32 33 34
10 03
2E
---
## 7. Channel Configuration
### `0x2A` — Copy Channel
**Direction:** Host → Device
**Category:** Channel Configuration
**Purpose:** Copies one channel's settings onto another.
**Sequencing:** Followed by `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Source channel | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2` | Destination channel | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
**Example**
10 02
00 01
03
2A
00 01
10 03
28
---
### `0x3B` — Link Channel
**Direction:** Host → Device
**Category:** Channel Configuration
**Purpose:** Groups channels so they move together.
**Sequencing:** Followed by `0x01`. Use `0x2A` (Copy Channel) before linking channels with `0x3B`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2` | Link mask | 1 byte | Bitmask — same OR pattern as [Enc §4.1 Matrix Input Mask](#41-matrix-input-mask), generalized to the channel's own bit position |
**Link mask rule:** bit `N` set → channel `N` is linked to this channel.
| Value | Meaning |
|---|---|
| `0x00` | No linked channels |
| `0x01`–`0x0F` | Valid range for input channels (bits 0–3 only) |
| `0x01`–`0xFF` | Valid range for output channels (bits 0–7) |
**Example**
10 02
00 01
03
3B
03 08
10 03
33
(Channel `0x03`, link mask `0x08` → linked to channel 3)
---
### `0x3D` — Set Channel Name
**Direction:** Host → Device
**Category:** Channel Configuration
**Purpose:** Sets the display name of an output channel.
**Sequencing:** Followed by `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Output channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2`–`Byte 9` | Channel name | 8 bytes | [Enc §2.2 Channel Name](#22-channel-name) |
**Example**
10 02
00 01
0A
3D
CH NAME[8]
10 03
CS
---
## 8. Channel Processing
### `0x15` — Set Delay Unit
**Direction:** Host → Device
**Category:** Channel Processing
**Purpose:** Changes the display unit used for delay values.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Delay unit | 1 byte | [Enc §3.5 Delay Unit](#72-delay-unit) |
**Example**
10 02
00 01
02
15
01
10 03
CS
---
### `0x34` — Set Gain
**Direction:** Host → Device
**Category:** Channel Processing
**Purpose:** Sets a channel's gain.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2-3` | Gain | 2 bytes, little-endian | [Enc §5.1 Channel Gain](#51-channel-gain) |
**Example**
10 02
00 01
04
34
CH LO HI
10 03
CS
---
### `0x35` — Set Mute
**Direction:** Host → Device
**Category:** Channel Processing
**Purpose:** Mutes or unmutes a channel.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2` | Mute state | 1 byte | `0x00` = Unmuted, `0x01` = Muted |
**Example**
10 02
00 01
03
35
CH MUTE
10 03
CS
---
### `0x36` — Invert Gain
**Direction:** Host → Device
**Category:** Channel Processing
**Purpose:** Inverts polarity on a channel.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2` | Invert state | 1 byte | `0x00` = Normal, `0x01` = Inverted |
**Example**
10 02
00 01
03
36
CH INVERT
10 03
CS
---
### `0x38` — Set Delay
**Direction:** Host → Device
**Category:** Channel Processing
**Purpose:** Sets a channel's delay value.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2` | Reserved | 1 byte | Always `0x00` |
| `Byte 3` | Delay | 1 byte | [Enc §7.1 Channel Delay](#71-channel-delay) |
**Example**
10 02
00 01
04
38
CH 00 DELAY
10 03
CS
---
### `0x3C` — Bypass EQ
**Direction:** Host → Device
**Category:** Channel Processing
**Purpose:** Enables or bypasses all EQ processing on a channel.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2` | EQ bypass state | 1 byte | `0x00` = Active, `0x01` = Bypassed |
**Example**
10 02
00 01
03
3C
CH BYPASS
10 03
CS
---
## 9. Dynamics
### `0x30` — Set Compressor
**Direction:** Host → Device
**Category:** Dynamics
**Purpose:** Sets compressor parameters for a channel.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2-3` | Ratio | 2 bytes, little-endian | [Enc §3.6 Compressor Ratio](#36-compressor-ratio) |
| `Byte 4-5` | Attack | 2 bytes, little-endian | [Enc §6.2 Attack](#62-attack-compressor) |
| `Byte 6-7` | Release | 2 bytes, little-endian | [Enc §6.3 Release](#63-release-compressor) |
| `Byte 8-9` | Knee | 2 bytes, little-endian | [Enc §6.4 Knee](#64-knee-compressor) |
| `Byte 10-11` | Threshold | 2 bytes, little-endian | [Enc §6.7 Compressor Threshold](#67-compressor-threshold) |
**Example**
10 02
00 01
0C
30
CH RA 00 AT_LO AT_HI RE_LO RE_HI KN 00 TH 00
10 03
CS
---
### `0x3E` — Set Gate Parameters
**Direction:** Host → Device
**Category:** Dynamics
**Purpose:** Sets noise-gate parameters for a channel.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2-3` | Attack | 2 bytes, little-endian | [Enc §6.2 Attack](#62-attack-compressor) |
| `Byte 4-5` | Release | 2 bytes, little-endian | [Enc §6.3 Release](#63-release-compressor) |
| `Byte 6-7` | Hold | 2 bytes, little-endian | [Enc §6.1 Hold](#61-hold-gate) |
| `Byte 8-9` | Threshold | 2 bytes, little-endian | [Enc §6.6 Gate Threshold](#66-gate-threshold) |
**Example**
10 02
00 01
0A
3E
CH AT 00 RE_LO RE_HI HO_LO HO_HI GA 00
10 03
82
---
### `0x3F` — Set Output Limiter
**Direction:** Host → Device
**Category:** Dynamics
**Purpose:** Sets output-limiter parameters. Output channels only.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Output channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2-3` | Attack | 2 bytes, little-endian | [Enc §6.2 Attack](#62-attack-compressor) |
| `Byte 4-5` | Release | 2 bytes, little-endian | [Enc §6.3 Release](#63-release-compressor) |
| `Byte 6-7` | Reserved | 2 bytes | Always `0000` |
| `Byte 8-9` | Threshold | 2 bytes, little-endian | [Enc §6.7 Compressor Threshold](#67-compressor-threshold) |
**Example**
10 02
00 01
0A
3F
CH AT_LO AT_HI RE_LO RE_HI 00 00 TH 00
10 03
CS
---
## 10. Filters & EQ
### `0x31` — Set Low-Pass Filter
**Direction:** Host → Device
**Category:** Filters & EQ
**Purpose:** Sets the low-pass filter for a channel.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2-3` | Cutoff frequency | 2 bytes, little-endian | [Enc §6.5 Sidechain Frequency](#65-sidechain-frequency-compressor) |
| `Byte 4` | Filter type | 1 byte | [Enc §3.9 Crossover Slope](#39-crossover-slope) |
**Example**
10 02
00 01
04
31
CH FREQ_LO FREQ_HI TYPE
10 03
CS
---
### `0x32` — Set High-Pass Filter
**Direction:** Host → Device
**Category:** Filters & EQ
**Purpose:** Sets the high-pass filter for a channel.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2-3` | Cutoff frequency | 2 bytes, little-endian | [Enc §6.5 Sidechain Frequency](#65-sidechain-frequency-compressor) |
| `Byte 4` | Filter type | 1 byte | [Enc §3.9 Crossover Slope](#39-crossover-slope) |
**Example**
10 02
00 01
04
32
CH FREQ_LO FREQ_HI TYPE
10 03
CS
---
### `0x33` — Set PEQ Band
**Direction:** Host → Device
**Category:** Filters & EQ
**Purpose:** Sets one parametric EQ band. Input channels only.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2` | PEQ band index | 1 byte | [Enc §3.3 PEQ Band Index](#33-peq-band-index) |
| `Byte 3-4` | Gain | 2 bytes, little-endian | [Enc §5.2 GEQ/PEQ Gain](#52-geq--peq-gain) |
| `Byte 5-6` | Frequency | 2 bytes, little-endian | [Enc §6.5 Sidechain Frequency](#65-sidechain-frequency-compressor)
| `Byte 7` | Q factor | 1 byte | [Enc §5.3 PEQ Q Factor](#53-peq-q-factor) | `0x00`–`0x64` |
| `Byte 8` | Filter type | 1 byte | [Enc §3.8 PEQ Filter Type](#38-peq-filter-type) | `0x00`–`0x08` |
| `Byte 9` | Bypass | 1 byte | `0x00` = Enabled, `0x01` = Bypassed |
**Example**
10 02
00 01
0A
33
CH BAND GAIN 00 FREQ_LO FREQ_HI Q TYPE BYPASS
10 03
CS
---
### `0x48` — Set GEQ Band
**Direction:** Host → Device
**Category:** Filters & EQ
**Purpose:** Sets one band of a 31-band graphic EQ. Input channels only.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2` | GEQ band index | 1 byte | [Enc §3.4 Frequency Band Index](#34-frequency-band-index) |
| `Byte 3-4` | Gain | 2 bytes, little-endian | [Enc §5.2 GEQ/PEQ Gain](#52-geq--peq-gain) |
**Example**
10 02
00 01
05
48
CH BAND DB 00
10 03
CS
---
### `0x49` — Set GEQ Bypass
**Direction:** Host → Device
**Category:** Filters & EQ
**Purpose:** Enables or bypasses the graphic EQ on an input channel.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2` | Bypass | 1 byte | `0x00` = GEQ enabled, `0x01` = GEQ bypassed |
**Example**
10 02
00 01
02
49
CH BP
10 03
CS
---
## 11. Input & Routing
### `0x39` — Set Input Source
**Direction:** Host → Device
**Category:** Input & Routing
**Purpose:** Selects the analog/test signal source.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Source type | 1 byte | [Enc §3.7 Input Source Type](#37-input-source-type) |
| `Byte 2` | Sine wave frequency (only meaningful when `Byte 1 = 0x03`) | 1 byte | [Enc §3.4 Frequency Band Index](#34-frequency-band-index) |
**Examples**
Analog:
10 02 00 01 03 39 00 00 10 03 3A
Pink Noise:
10 02 00 01 03 39 01 00 10 03 3B
White Noise:
10 02 00 01 03 39 02 00 10 03 38
Sine Wave:
10 02 00 01 03 39 03 XX 10 03 YY
---
### `0x3A` — Matrix Routing
**Direction:** Host → Device
**Category:** Input & Routing
**Purpose:** Sets which inputs feed a given output channel.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Output channel | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2` | Input routing mask | 1 byte | [Enc §4.1 Matrix Input Mask](#41-matrix-input-mask) |
**Example**
10 02
00 01
03
3A
OUT INPUT_MASK
10 03
CS
---
### `0x41` — Set Matrix Gain
**Direction:** Host → Device
**Category:** Input & Routing
**Purpose:** Sets the send gain from one input to one output.
**Sequencing:** Device acknowledges with `0x01`.
**Payload Fields**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1` | Output channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 2` | Input channel index | 1 byte | [Enc §3.2 Channel Index](#32-channel-index) |
| `Byte 3-4` | Gain | 2 bytes, little-endian | [Enc §5.1 Channel Gain](#51-channel-gain) |
**Example**
10 02
00 01
05
41
CH_OUT CH_IN GAIN_LO GAIN_HI
10 03
CS
---
## 12. Metering
### `0x40` — Get Level Meters
**Direction:** Host → Device (Query), Device → Host (Response)
**Category:** Metering
**Purpose:** Reads current level-meter and clip-indicator values for all channels.
**Payload Fields (Response only)**
| Offset | Field | Size | Encoding |
|---|---|---|---|
| `Byte 1-36` | 12 meter channels | 3 bytes × 12 | [Enc §8.1 Meter Level](#81-meter-level) |
| `Byte 37` | Input clip indicator | 1 byte bitmask | Same OR bitmask pattern as [Enc §4.1](#41-matrix-input-mask) |
| `Byte 38` | Output limit indicator | 1 byte bitmask | Same OR bitmask pattern as [Enc §4.1](#41-matrix-input-mask) |
**Example — Query**
10 02
00 01
01
40
10 03
41
**Example — Response**
10 02
01 00
27
40
A2 36 A5 A0 36 A0 ... 39 00 00
10 03
75
---