From e6610223293a2b221aa6b6d593ff8e69ba4d73d2 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 30 Sep 2026 16:09:18 +0200 Subject: [PATCH] feat: add random meters in demo --- Cargo.lock | 108 ++++++++++++++++++++++------- Cargo.toml | 49 ++++++------- dsp408-protocol.md | 79 +++++++++++++++++---- src/dsp408/{ => client}/default.rs | 32 ++++++++- src/dsp408/client/dsp408.rs | 19 ++--- src/dsp408/client/mod.rs | 2 + src/dsp408/mod.rs | 2 - 7 files changed, 217 insertions(+), 74 deletions(-) rename src/dsp408/{ => client}/default.rs (92%) diff --git a/Cargo.lock b/Cargo.lock index bb75e73..7e897c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base64" -version = "0.22.1" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] name = "bitflags" @@ -60,6 +60,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core", +] + [[package]] name = "chrono" version = "0.4.45" @@ -79,10 +90,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "darling" -version = "0.23.0" +name = "cpufeatures" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "darling" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" dependencies = [ "darling_core", "darling_macro", @@ -90,26 +110,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" dependencies = [ "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.119", + "syn 3.0.6", ] [[package]] name = "darling_macro" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" dependencies = [ "darling_core", "quote", - "syn 2.0.119", + "syn 3.0.6", ] [[package]] @@ -163,6 +183,7 @@ name = "dsp_thomann" version = "0.1.0" dependencies = [ "dotenvy", + "rand", "serde", "serde_json", "serde_with", @@ -213,6 +234,18 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "rand_core", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -445,6 +478,29 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" +dependencies = [ + "chacha20", + "getrandom", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "ref-cast" version = "1.0.27" @@ -462,7 +518,7 @@ checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.2", + "syn 3.0.6", ] [[package]] @@ -522,7 +578,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.2", + "syn 3.0.6", ] [[package]] @@ -540,9 +596,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.22.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" +checksum = "df9adc193c780ef8f159aee8b61e2d5801aaa555e6eb0947fe45530ec506296f" dependencies = [ "base64", "bs58", @@ -561,14 +617,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.22.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" +checksum = "3e17bbc68e28663bbbb90df47e058aa7eda4fb445b89fe70457bb94fbccf6e49" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.6", ] [[package]] @@ -620,9 +676,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.2" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a207d6d6a2b7fc470b80443726053f18a2481b7e1eee970597051596567987a3" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" dependencies = [ "proc-macro2", "quote", @@ -631,22 +687,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.20" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.20" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn 3.0.2", + "syn 3.0.6", ] [[package]] @@ -732,7 +788,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 3.0.2", + "syn 3.0.6", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index 12ccb37..09e4276 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,24 +1,25 @@ -[package] -name = "dsp_thomann" -version = "0.1.0" -edition = "2024" - -[dev-dependencies] -dotenvy = "0.15" - -[dependencies] -thiserror = "2.0.20" -strum = "0.28.0" -strum_macros = "0.28.0" -serde = { version = "1", features = ["derive"], optional = true } -serde_json = { version = "1", optional = true } -serde_with = "3" - -[lib] -name = "dsp_thomann" -path = "src/lib.rs" - -[features] -hardware-tests = [] -default = [] -serialization = ["dep:serde", "dep:serde_json"] +[package] +name = "dsp_thomann" +version = "0.1.0" +edition = "2024" + +[dev-dependencies] +dotenvy = "0.15" + +[dependencies] +thiserror = "2.0.21" +strum = "0.28.0" +strum_macros = "0.28.0" +serde = { version = "1", features = ["derive"], optional = true } +serde_json = { version = "1", optional = true } +serde_with = "3.24.0" +rand = "0.10.3" + +[lib] +name = "dsp_thomann" +path = "src/lib.rs" + +[features] +hardware-tests = [] +default = [] +serialization = ["dep:serde", "dep:serde_json"] diff --git a/dsp408-protocol.md b/dsp408-protocol.md index 38bbd88..7b5c582 100644 --- a/dsp408-protocol.md +++ b/dsp408-protocol.md @@ -617,26 +617,81 @@ delay = delay_min + (raw / 255) × (delay_max - delay_min) | Field | Value | |---|---| -| **Purpose** | Per-channel level + peak metering | -| **Size** | 3 bytes per channel | -| **Total** | 12 channels × 3 bytes = 36 bytes | +| **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 | |---|---|---| -| 0–1 | Level | IEEE754 float16, little-endian | -| 2 | Peak | 1-byte peak value (format not specified in source) | +| `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. -**Encoding (value → raw):** ```text -level_bytes = float16_encode(level) // 2 bytes, little-endian -peak_byte = peak_encode(peak) // format undefined — see note below -raw = level_bytes + peak_byte +rms_raw = float16_decode(lo = payload[i], hi = payload[i + 1]) +peak_raw = float16_decode(lo = payload[i + 2], hi = payload[i + 1]) ``` -**Decoding (raw → value):** +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 -level = float16_decode(raw[0:2]) // little-endian -peak = peak_decode(raw[2]) // format undefined — see note below +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 ``` --- diff --git a/src/dsp408/default.rs b/src/dsp408/client/default.rs similarity index 92% rename from src/dsp408/default.rs rename to src/dsp408/client/default.rs index 33e9c56..1477f26 100644 --- a/src/dsp408/default.rs +++ b/src/dsp408/client/default.rs @@ -1,8 +1,9 @@ use std::collections::HashMap; use strum::IntoEnumIterator; +use rand::RngExt; use crate::dsp408::types::ConnectionStatus; -use super::types::{ +use crate::dsp408::types::{ Compressor, Crossover, CrossoverFilter, CrossoverFilters, DSPConfigState, DSPState, DelayUnit, DeviceFlags, DiscreteFrequency, Gate, GraphicEQ, InputChannel, InputChannelState, InputMeters, InputSource, InputType, Limiter, MatrixRoutes, Meter, OutputChannel, OutputChannelState, @@ -301,3 +302,32 @@ fn default_meter() -> Meter { clip: false, } } + +fn random_meter() -> Meter { + let mut rng = rand::rng(); + + let rms = rng.random_range(-60.0..=0.0); + let peak = (rms + rng.random_range(0.0..=12.0) as f32).min(12.0); + + Meter { + rms, + peak, + clip: peak > 0.0, + } +} + +pub fn random_input_meters() -> InputMeters { + InputMeters { + meters: InputChannel::iter() + .map(|channel| (channel, random_meter())) + .collect(), + } +} + +pub fn random_output_meters() -> OutputMeters { + OutputMeters { + meters: OutputChannel::iter() + .map(|channel| (channel, random_meter())) + .collect(), + } +} diff --git a/src/dsp408/client/dsp408.rs b/src/dsp408/client/dsp408.rs index 531a60f..c675605 100644 --- a/src/dsp408/client/dsp408.rs +++ b/src/dsp408/client/dsp408.rs @@ -8,17 +8,12 @@ use std::{ }; use crate::{ - common::errors::DSPError, - dsp408::{ - default::{default_input_meters, default_output_meters}, - default_dsp_state, - mapper::{ + common::errors::DSPError, dsp408::{ + client::default::{default_dsp_state, default_input_meters, default_output_meters, random_input_meters, random_output_meters}, mapper::{ self, constants::{CONFIG_CHUNK_INDEX_MAX, PRESET_INDEX_MAX, delay_range}, response::map_response, - }, - protocol::{commands, config::decode_config, frame::extract_frame}, - types::{self, ConnectionStatus::Disconnected, DSPMode}, + }, protocol::{commands, config::decode_config, frame::extract_frame}, types::{self, ConnectionStatus::Disconnected, DSPMode}, }, }; @@ -1796,7 +1791,13 @@ impl DSP408 { match self.state.connection { types::ConnectionStatus::Connected => { if self.mode == types::DSPMode::Demo { - return Ok((self.input_meters.clone(), self.output_meters.clone())); + self.input_meters = random_input_meters(); + self.output_meters = random_output_meters(); + + return Ok(( + self.input_meters.clone(), + self.output_meters.clone(), + )); } let request = commands::build_get_meter_levels(); diff --git a/src/dsp408/client/mod.rs b/src/dsp408/client/mod.rs index 2115262..b3b4c5e 100644 --- a/src/dsp408/client/mod.rs +++ b/src/dsp408/client/mod.rs @@ -1,2 +1,4 @@ mod dsp408; +mod default; + pub use dsp408::DSP408; diff --git a/src/dsp408/mod.rs b/src/dsp408/mod.rs index 654c3c8..1c1f95f 100644 --- a/src/dsp408/mod.rs +++ b/src/dsp408/mod.rs @@ -1,9 +1,7 @@ mod client; -mod default; mod mapper; mod protocol; pub mod types; pub use client::DSP408; -pub(crate) use default::default_dsp_state;