Compare commits

...
5 Commits
Author SHA1 Message Date
LucasDLTG 2bf07d0fba fix: bypass calculation 2026-10-02 22:50:39 +02:00
Lucas e661022329 feat: add random meters in demo 2026-09-30 16:09:18 +02:00
Lucas f4650ece00 fix: import 2026-09-30 12:41:32 +02:00
Lucas 01cc0feda2 fix: errors 2026-09-29 17:01:12 +02:00
LucasDLTG 234dcd08d0 unstable modification 2026-09-28 23:21:21 +02:00
10 changed files with 787 additions and 670 deletions
Generated
+82 -26
View File
@@ -19,9 +19,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
@@ -60,6 +60,17 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 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]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.45" version = "0.4.45"
@@ -79,10 +90,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]] [[package]]
name = "darling" name = "cpufeatures"
version = "0.23.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"darling_core", "darling_core",
"darling_macro", "darling_macro",
@@ -90,26 +110,26 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.23.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff"
dependencies = [ dependencies = [
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn 2.0.119", "syn 3.0.6",
] ]
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.23.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.119", "syn 3.0.6",
] ]
[[package]] [[package]]
@@ -163,6 +183,7 @@ name = "dsp_thomann"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"dotenvy", "dotenvy",
"rand",
"serde", "serde",
"serde_json", "serde_json",
"serde_with", "serde_with",
@@ -213,6 +234,18 @@ dependencies = [
"slab", "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]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
@@ -445,6 +478,29 @@ dependencies = [
"proc-macro2", "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]] [[package]]
name = "ref-cast" name = "ref-cast"
version = "1.0.27" version = "1.0.27"
@@ -462,7 +518,7 @@ checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.2", "syn 3.0.6",
] ]
[[package]] [[package]]
@@ -522,7 +578,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.2", "syn 3.0.6",
] ]
[[package]] [[package]]
@@ -540,9 +596,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "3.22.0" version = "3.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" checksum = "df9adc193c780ef8f159aee8b61e2d5801aaa555e6eb0947fe45530ec506296f"
dependencies = [ dependencies = [
"base64", "base64",
"bs58", "bs58",
@@ -561,14 +617,14 @@ dependencies = [
[[package]] [[package]]
name = "serde_with_macros" name = "serde_with_macros"
version = "3.22.0" version = "3.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" checksum = "3e17bbc68e28663bbbb90df47e058aa7eda4fb445b89fe70457bb94fbccf6e49"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.119", "syn 3.0.6",
] ]
[[package]] [[package]]
@@ -620,9 +676,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "3.0.2" version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a207d6d6a2b7fc470b80443726053f18a2481b7e1eee970597051596567987a3" checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -631,22 +687,22 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.20" version = "2.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.20" version = "2.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.2", "syn 3.0.6",
] ]
[[package]] [[package]]
@@ -732,7 +788,7 @@ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.2", "syn 3.0.6",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
+25 -24
View File
@@ -1,24 +1,25 @@
[package] [package]
name = "dsp_thomann" name = "dsp_thomann"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[dev-dependencies] [dev-dependencies]
dotenvy = "0.15" dotenvy = "0.15"
[dependencies] [dependencies]
thiserror = "2.0.20" thiserror = "2.0.21"
strum = "0.28.0" strum = "0.28.0"
strum_macros = "0.28.0" strum_macros = "0.28.0"
serde = { version = "1", features = ["derive"], optional = true } serde = { version = "1", features = ["derive"], optional = true }
serde_json = { version = "1", optional = true } serde_json = { version = "1", optional = true }
serde_with = "3" serde_with = "3.24.0"
rand = "0.10.3"
[lib]
name = "dsp_thomann" [lib]
path = "src/lib.rs" name = "dsp_thomann"
path = "src/lib.rs"
[features]
hardware-tests = [] [features]
default = [] hardware-tests = []
serialization = ["dep:serde", "dep:serde_json"] default = []
serialization = ["dep:serde", "dep:serde_json"]
+67 -12
View File
@@ -617,26 +617,81 @@ delay = delay_min + (raw / 255) × (delay_max - delay_min)
| Field | Value | | Field | Value |
|---|---| |---|---|
| **Purpose** | Per-channel level + peak metering | | **Purpose** | Per-channel RMS + peak metering, with clip flags |
| **Size** | 3 bytes per channel | | **Payload size** | 38 bytes (exactly; any other length is an error) |
| **Total** | 12 channels × 3 bytes = 36 bytes | | **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 | | Byte offset | Field | Format |
|---|---|---| |---|---|---|
| 0–1 | Level | IEEE754 float16, little-endian | | `i + 0` | RMS low byte | Low byte of the RMS float16 |
| 2 | Peak | 1-byte peak value (format not specified in source) | | `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 ```text
level_bytes = float16_encode(level) // 2 bytes, little-endian rms_raw = float16_decode(lo = payload[i], hi = payload[i + 1])
peak_byte = peak_encode(peak) // format undefined — see note below peak_raw = float16_decode(lo = payload[i + 2], hi = payload[i + 1])
raw = level_bytes + peak_byte
``` ```
**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 ```text
level = float16_decode(raw[0:2]) // little-endian clip = (flags_byte & (1 << bit)) != 0
peak = peak_decode(raw[2]) // format undefined — see note below ```
#### 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
``` ```
--- ---
@@ -1,7 +1,9 @@
use rand::RngExt;
use std::collections::HashMap; use std::collections::HashMap;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use super::types::{ use crate::dsp408::types::ConnectionStatus;
use crate::dsp408::types::{
Compressor, Crossover, CrossoverFilter, CrossoverFilters, DSPConfigState, DSPState, DelayUnit, Compressor, Crossover, CrossoverFilter, CrossoverFilters, DSPConfigState, DSPState, DelayUnit,
DeviceFlags, DiscreteFrequency, Gate, GraphicEQ, InputChannel, InputChannelState, InputMeters, DeviceFlags, DiscreteFrequency, Gate, GraphicEQ, InputChannel, InputChannelState, InputMeters,
InputSource, InputType, Limiter, MatrixRoutes, Meter, OutputChannel, OutputChannelState, InputSource, InputType, Limiter, MatrixRoutes, Meter, OutputChannel, OutputChannelState,
@@ -17,11 +19,13 @@ pub fn default_dsp_state() -> DSPState {
DSPState { DSPState {
name: "Default".to_string(), name: "Default".to_string(),
flags: DeviceFlags { is_locked: false }, flags: DeviceFlags { is_locked: true },
presets: default_preset_bank(), presets: default_preset_bank(),
current_config: default_config(), current_config: default_config(),
connection: ConnectionStatus::Disconnected,
} }
} }
@@ -298,3 +302,32 @@ fn default_meter() -> Meter {
clip: false, 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(),
}
}
File diff suppressed because it is too large Load Diff
+2
View File
@@ -1,2 +1,4 @@
mod default;
mod dsp408; mod dsp408;
pub use dsp408::DSP408; pub use dsp408::DSP408;
-2
View File
@@ -1,9 +1,7 @@
mod client; mod client;
mod default;
mod mapper; mod mapper;
mod protocol; mod protocol;
pub mod types; pub mod types;
pub use client::DSP408; pub use client::DSP408;
pub(crate) use default::default_dsp_state;
+2 -6
View File
@@ -297,9 +297,7 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
for (index, inp) in input_channels.iter_mut().enumerate() { for (index, inp) in input_channels.iter_mut().enumerate() {
let bypass = input_peq_bitmap & (1 << index) != 0; let bypass = input_peq_bitmap & (1 << index) != 0;
for peq in inp.peq_chain.bands.values_mut() { inp.peq_chain.bypass = bypass;
peq.bypass = bypass;
}
} }
let output_peq_bitmap = r.read_u16()?; let output_peq_bitmap = r.read_u16()?;
@@ -307,9 +305,7 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
for (index, out) in output_channels.iter_mut().enumerate() { for (index, out) in output_channels.iter_mut().enumerate() {
let bypass = output_peq_bitmap & (1 << index) != 0; let bypass = output_peq_bitmap & (1 << index) != 0;
for peq in out.peq_chain.bands.values_mut() { out.peq_chain.bypass = bypass;
peq.bypass = bypass;
}
} }
let bitmap = r.read_u16()?; let bitmap = r.read_u16()?;
+15 -8
View File
@@ -398,6 +398,20 @@ impl DSPConfigState {
} }
} }
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DSPMode {
Demo,
Normal,
}
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionStatus {
Connected,
Disconnected,
}
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct DSPState { pub struct DSPState {
@@ -405,14 +419,7 @@ pub struct DSPState {
pub(crate) flags: DeviceFlags, pub(crate) flags: DeviceFlags,
pub(crate) presets: PresetBank, pub(crate) presets: PresetBank,
pub(crate) current_config: DSPConfigState, pub(crate) current_config: DSPConfigState,
} pub(crate) connection: ConnectionStatus,
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DSPMode {
Demo,
Disconnected,
Connected,
} }
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
+11 -13
View File
@@ -2,8 +2,6 @@ use std::net::IpAddr;
use std::str::FromStr; use std::str::FromStr;
use dsp_thomann::dsp408::DSP408; use dsp_thomann::dsp408::DSP408;
use dsp_thomann::dsp408::types;
use dsp_thomann::dsp408::types::InputChannel::InA;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let host = IpAddr::from_str("192.168.49.30")?; let host = IpAddr::from_str("192.168.49.30")?;
@@ -25,18 +23,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// dsp.set_mute(types::Channel::Input(types::InputChannel::InA), true)?; // dsp.set_mute(types::Channel::Input(types::InputChannel::InA), true)?;
let r = dsp.set_peq_band( // let r = dsp.set_peq_band(
types::Channel::Input(InA), // types::Channel::Input(InA),
types::PEQBand::B1, // types::PEQBand::B1,
3.0, // 3.0,
1400.0, // 1400.0,
2.1, // 2.1,
types::PEQFilter::LowShelf, // types::PEQFilter::LowShelf,
false, // false,
)?; // )?;
dbg!(r); // dbg!(r);
// dbg!(dsp.state()?); dbg!(&dsp.state());
// let s = &dsp.state()?.current_config.input_states[&InputChannel::InA].peq_chain; // let s = &dsp.state()?.current_config.input_states[&InputChannel::InA].peq_chain;
// dbg!(s) // dbg!(s)