Compare commits
6
Commits
bd5eaa3446
...
af2e7dc602
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af2e7dc602 | ||
|
|
e2fee36cdb | ||
|
|
27821c1866 | ||
|
|
3d94df0b7a | ||
|
|
f6b6beda5e | ||
|
|
a9a49bef89 |
Generated
+63
@@ -13,6 +13,8 @@ name = "dsp_thomann"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dotenvy",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"thiserror",
|
||||
@@ -24,6 +26,18 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
@@ -42,6 +56,49 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.28.0"
|
||||
@@ -107,3 +164,9 @@ name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
||||
|
||||
@@ -10,6 +10,8 @@ dotenvy = "0.15"
|
||||
thiserror = "2.0.19"
|
||||
strum = "0.28.0"
|
||||
strum_macros = "0.28.0"
|
||||
serde = { version = "1", features = ["derive"], optional = true }
|
||||
serde_json = { version = "1", optional = true }
|
||||
|
||||
[lib]
|
||||
name = "dsp_thomann"
|
||||
@@ -17,3 +19,5 @@ path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
hardware-tests = []
|
||||
default = []
|
||||
serialization = ["dep:serde", "dep:serde_json"]
|
||||
|
||||
+1
-1
@@ -1868,7 +1868,7 @@ Sine Wave:
|
||||
|---|---|---|---|
|
||||
| `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 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**
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
- Figure out Meter level encoding
|
||||
- Full test of client (Improve existing one)
|
||||
- Write correct readme.md
|
||||
- Make client dsp408 thread safe
|
||||
- Absolutely, send command to avoid tcp keep alive, that make the dsp crash
|
||||
|
||||
Rust library for remote LAN control of Thomann DSP408 digital signal processors.
|
||||
|
||||
|
||||
@@ -122,6 +122,12 @@ pub enum DSPError {
|
||||
#[error("operation failed: {0}")]
|
||||
OperationFailed(String),
|
||||
|
||||
#[error("internal error: {0}")]
|
||||
InternalError(String),
|
||||
|
||||
#[error("mutex poisoned: {0}")]
|
||||
MutexPoisoned(String),
|
||||
|
||||
#[error("I/O error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
@@ -134,3 +140,9 @@ pub enum DSPError {
|
||||
#[error("frame error: {0}")]
|
||||
Frame(#[from] FrameError),
|
||||
}
|
||||
|
||||
impl<T> From<std::sync::PoisonError<T>> for DSPError {
|
||||
fn from(err: std::sync::PoisonError<T>) -> Self {
|
||||
DSPError::MutexPoisoned(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
+172
-96
@@ -3,22 +3,20 @@ use std::{
|
||||
fmt,
|
||||
io::{Read, Write},
|
||||
net::{IpAddr, SocketAddr, TcpStream},
|
||||
sync::{Mutex, OnceLock},
|
||||
sync::{Arc, Mutex, OnceLock},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::{
|
||||
common::errors::{DSPError, ValidationError},
|
||||
common::errors::DSPError,
|
||||
dsp408::{
|
||||
mapper::{
|
||||
self,
|
||||
constants::{CONFIG_CHUNK_INDEX_MAX, PRESET_INDEX_MAX},
|
||||
constants::{CONFIG_CHUNK_INDEX_MAX, PRESET_INDEX_MAX, delay_range},
|
||||
response::map_response,
|
||||
},
|
||||
protocol::{commands, config::decode_config, frame::extract_frame},
|
||||
types,
|
||||
types::{self},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -32,9 +30,8 @@ fn instances() -> &'static Mutex<HashMap<(IpAddr, u8), ()>> {
|
||||
}
|
||||
|
||||
pub struct DSP408 {
|
||||
connection: types::DeviceConnection,
|
||||
socket: Option<TcpStream>,
|
||||
lock: Mutex<()>,
|
||||
connection: types::DeviceConnection, // read-only so thread protected
|
||||
socket: Arc<Mutex<Option<TcpStream>>>, // thread protected
|
||||
connected: bool,
|
||||
state: Option<types::DSPState>,
|
||||
}
|
||||
@@ -62,9 +59,7 @@ impl DSP408 {
|
||||
device_id,
|
||||
},
|
||||
|
||||
socket: None,
|
||||
|
||||
lock: Mutex::new(()),
|
||||
socket: Arc::new(Mutex::new(None)),
|
||||
|
||||
connected: false,
|
||||
|
||||
@@ -72,19 +67,13 @@ impl DSP408 {
|
||||
})
|
||||
}
|
||||
|
||||
fn send_raw(&mut self, data: &[u8]) -> Result<(), DSPError> {
|
||||
let _guard = self.lock.lock().unwrap();
|
||||
|
||||
let socket = self.socket.as_mut().ok_or(DSPError::NotConnected)?;
|
||||
|
||||
fn send_raw(socket: &mut TcpStream, data: &[u8]) -> Result<(), DSPError> {
|
||||
socket.write_all(data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv_raw(&mut self) -> Result<Vec<u8>, DSPError> {
|
||||
let socket = self.socket.as_mut().ok_or(DSPError::NotConnected)?;
|
||||
|
||||
fn recv_raw(socket: &mut TcpStream) -> Result<Vec<u8>, DSPError> {
|
||||
let mut buffer = vec![0u8; RECV_BUFSIZE];
|
||||
|
||||
match socket.read(&mut buffer) {
|
||||
@@ -101,10 +90,17 @@ impl DSP408 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_recv(&mut self, command: &[u8]) -> Result<types::DeviceResponseType, DSPError> {
|
||||
self.send_raw(command)?;
|
||||
pub fn send_recv(&self, command: &[u8]) -> Result<types::DeviceResponseType, DSPError> {
|
||||
let mut socket_guard = self
|
||||
.socket
|
||||
.lock()
|
||||
.map_err(|e| DSPError::MutexPoisoned(e.to_string()))?;
|
||||
|
||||
let raw = self.recv_raw()?;
|
||||
let socket = socket_guard.as_mut().ok_or(DSPError::NotConnected)?;
|
||||
|
||||
Self::send_raw(socket, command)?;
|
||||
|
||||
let raw = Self::recv_raw(socket)?;
|
||||
|
||||
let frame = extract_frame(&raw)?;
|
||||
|
||||
@@ -117,6 +113,10 @@ impl DSP408 {
|
||||
self.connected
|
||||
}
|
||||
|
||||
pub fn connection(&self) -> &types::DeviceConnection {
|
||||
&self.connection
|
||||
}
|
||||
|
||||
pub fn state(&self) -> Result<&types::DSPState, DSPError> {
|
||||
self.state.as_ref().ok_or(DSPError::NotConnected)
|
||||
}
|
||||
@@ -125,43 +125,7 @@ impl DSP408 {
|
||||
self.state.as_mut().ok_or(DSPError::NotConnected)
|
||||
}
|
||||
|
||||
// ---------- Commands ----------
|
||||
// Command `0x10`
|
||||
pub fn connect(&mut self) -> Result<(), DSPError> {
|
||||
let key = (self.connection.host, self.connection.device_id);
|
||||
|
||||
{
|
||||
let map = instances().lock().unwrap();
|
||||
|
||||
if map.contains_key(&key) {
|
||||
return Err(DSPError::AlreadyConnected);
|
||||
}
|
||||
}
|
||||
|
||||
let addr = SocketAddr::new(self.connection.host, self.connection.port);
|
||||
|
||||
let stream = TcpStream::connect_timeout(&addr, SOCKET_TIMEOUT)?;
|
||||
|
||||
stream.set_read_timeout(Some(SOCKET_TIMEOUT))?;
|
||||
|
||||
self.socket = Some(stream);
|
||||
|
||||
let handshake = commands::build_handshake();
|
||||
|
||||
self.send_raw(&handshake)?;
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
while start.elapsed() < SOCKET_TIMEOUT {
|
||||
let raw = self.recv_raw()?;
|
||||
let frame = extract_frame(&raw)?;
|
||||
let response = map_response(&frame)?;
|
||||
|
||||
if matches!(response, types::DeviceResponseType::HandshakeAck(_)) {
|
||||
instances().lock().unwrap().insert(key, ());
|
||||
|
||||
self.connected = true;
|
||||
|
||||
fn initialize_state(&mut self) -> Result<(), DSPError> {
|
||||
let name = self.get_device_name()?;
|
||||
let flags = self.get_device_flags()?;
|
||||
let current_preset = self.get_current_preset()?;
|
||||
@@ -185,28 +149,121 @@ impl DSP408 {
|
||||
current_config: state,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn convert_delay_unit(value: f32, from: types::DelayUnit, to: types::DelayUnit) -> f32 {
|
||||
let (_, from_max) = delay_range(from);
|
||||
let (_, to_max) = delay_range(to);
|
||||
|
||||
(value / from_max) * to_max
|
||||
}
|
||||
|
||||
// ---------- Commands ----------
|
||||
// Command `0x10`
|
||||
pub fn connect(&mut self) -> Result<(), DSPError> {
|
||||
let key = (self.connection.host, self.connection.device_id);
|
||||
|
||||
{
|
||||
let map = instances()
|
||||
.lock()
|
||||
.map_err(|e| DSPError::InternalError(e.to_string()))?;
|
||||
|
||||
if map.contains_key(&key) {
|
||||
return Err(DSPError::AlreadyConnected);
|
||||
}
|
||||
}
|
||||
|
||||
let addr = SocketAddr::new(self.connection.host, self.connection.port);
|
||||
|
||||
let stream = TcpStream::connect_timeout(&addr, SOCKET_TIMEOUT)?;
|
||||
|
||||
stream.set_read_timeout(Some(SOCKET_TIMEOUT))?;
|
||||
|
||||
{
|
||||
let mut socket = self
|
||||
.socket
|
||||
.lock()
|
||||
.map_err(|e| DSPError::InternalError(e.to_string()))?;
|
||||
|
||||
*socket = Some(stream);
|
||||
}
|
||||
|
||||
let handshake = commands::build_handshake();
|
||||
|
||||
{
|
||||
let mut socket = self
|
||||
.socket
|
||||
.lock()
|
||||
.map_err(|e| DSPError::InternalError(e.to_string()))?;
|
||||
|
||||
let socket = socket.as_mut().ok_or(DSPError::NotConnected)?;
|
||||
|
||||
Self::send_raw(socket, &handshake)?;
|
||||
}
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
while start.elapsed() < SOCKET_TIMEOUT {
|
||||
let raw = {
|
||||
let mut socket = self
|
||||
.socket
|
||||
.lock()
|
||||
.map_err(|e| DSPError::InternalError(e.to_string()))?;
|
||||
|
||||
let socket = socket.as_mut().ok_or(DSPError::NotConnected)?;
|
||||
|
||||
Self::recv_raw(socket)?
|
||||
};
|
||||
|
||||
let frame = extract_frame(&raw)?;
|
||||
let response = map_response(&frame)?;
|
||||
|
||||
if matches!(response, types::DeviceResponseType::HandshakeAck(_)) {
|
||||
let result = self.initialize_state();
|
||||
|
||||
if result.is_err() {
|
||||
self.disconnect()?;
|
||||
return result;
|
||||
}
|
||||
|
||||
instances()
|
||||
.lock()
|
||||
.map_err(|e| DSPError::InternalError(e.to_string()))?
|
||||
.insert(key, ());
|
||||
|
||||
self.connected = true;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
self.disconnect();
|
||||
self.disconnect()?;
|
||||
|
||||
Err(DSPError::HandshakeFailed)
|
||||
}
|
||||
|
||||
// Command `0x11`
|
||||
pub fn disconnect(&mut self) {
|
||||
pub fn disconnect(&mut self) -> Result<(), DSPError> {
|
||||
let key = (self.connection.host, self.connection.device_id);
|
||||
|
||||
if let Some(mut socket) = self.socket.take() {
|
||||
let _ = socket.write_all(&commands::build_disconnect());
|
||||
let mut socket_guard = self.socket.lock()?;
|
||||
|
||||
let _ = socket.shutdown(std::net::Shutdown::Both);
|
||||
if let Some(mut socket) = socket_guard.take() {
|
||||
socket
|
||||
.write_all(&commands::build_disconnect())
|
||||
.map_err(DSPError::Io)?;
|
||||
|
||||
socket
|
||||
.shutdown(std::net::Shutdown::Both)
|
||||
.map_err(DSPError::Io)?;
|
||||
}
|
||||
|
||||
instances().lock().unwrap().remove(&key);
|
||||
instances().lock()?.remove(&key);
|
||||
|
||||
self.connected = false;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Command `0x12`
|
||||
@@ -225,7 +282,7 @@ impl DSP408 {
|
||||
}
|
||||
|
||||
// Command `0x13` (Device Name)
|
||||
pub fn get_device_name(&mut self) -> Result<String, DSPError> {
|
||||
fn get_device_name(&mut self) -> Result<String, DSPError> {
|
||||
let request = commands::build_request_device_name();
|
||||
|
||||
let resp = self.send_recv(&request)?;
|
||||
@@ -240,7 +297,7 @@ impl DSP408 {
|
||||
}
|
||||
|
||||
// Command `0x14` (Current Preset Index)
|
||||
pub fn get_current_preset(&mut self) -> Result<u8, DSPError> {
|
||||
fn get_current_preset(&mut self) -> Result<u8, DSPError> {
|
||||
let request = commands::build_get_current_preset();
|
||||
|
||||
let resp = self.send_recv(&request)?;
|
||||
@@ -268,7 +325,20 @@ impl DSP408 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
self.state_mut()?.current_config.delay_unit = unit;
|
||||
let state = self.state_mut()?;
|
||||
let old_unit = state.current_config.delay_unit;
|
||||
|
||||
if old_unit != unit {
|
||||
for input in state.current_config.input_states.values_mut() {
|
||||
input.delay = Self::convert_delay_unit(input.delay, old_unit, unit);
|
||||
}
|
||||
|
||||
for output in state.current_config.output_states.values_mut() {
|
||||
output.delay = Self::convert_delay_unit(output.delay, old_unit, unit);
|
||||
}
|
||||
}
|
||||
|
||||
state.current_config.delay_unit = unit;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
@@ -348,7 +418,7 @@ impl DSP408 {
|
||||
}
|
||||
|
||||
// Command `0x22` (Get Preset Modification Status)
|
||||
pub fn get_preset_modification_status(&mut self) -> Result<types::ModifiedPreset, DSPError> {
|
||||
fn get_preset_modification_status(&mut self) -> Result<types::ModifiedPreset, DSPError> {
|
||||
let request = commands::build_preset_modification_status();
|
||||
|
||||
let resp = self.send_recv(&request)?;
|
||||
@@ -395,8 +465,20 @@ impl DSP408 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let state = self.state_mut()?;
|
||||
let current_index = {
|
||||
let state = self.state()?;
|
||||
state.presets.current_index
|
||||
};
|
||||
|
||||
if !self.store_current_configuration(current_index, true)? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !self.request_acknowledgement()? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let state = self.state_mut()?;
|
||||
state.presets.names[state.presets.current_index] = name.to_string();
|
||||
|
||||
Ok(true)
|
||||
@@ -426,7 +508,7 @@ impl DSP408 {
|
||||
}
|
||||
|
||||
// Command `0x29` (Preset Name)
|
||||
pub fn get_preset_name(&mut self, preset_index: usize) -> Result<String, DSPError> {
|
||||
fn get_preset_name(&mut self, preset_index: usize) -> Result<String, DSPError> {
|
||||
let raw_index = mapper::preset_index_without_factory_to_raw(preset_index)?;
|
||||
|
||||
let request = commands::build_get_preset_name(raw_index)?;
|
||||
@@ -464,7 +546,7 @@ impl DSP408 {
|
||||
}
|
||||
|
||||
// Command `0x2C` (Device Flags)
|
||||
pub fn get_device_flags(&mut self) -> Result<types::DeviceFlags, DSPError> {
|
||||
fn get_device_flags(&mut self) -> Result<types::DeviceFlags, DSPError> {
|
||||
let request = commands::build_get_device_flags();
|
||||
|
||||
let resp = self.send_recv(&request)?;
|
||||
@@ -746,8 +828,12 @@ impl DSP408 {
|
||||
|
||||
match channel {
|
||||
types::Channel::Input(input) => {
|
||||
state.current_config.input_state_mut(input).peq_chain.bands
|
||||
[band as usize] = peq;
|
||||
state
|
||||
.current_config
|
||||
.input_state_mut(input)
|
||||
.peq_chain
|
||||
.bands
|
||||
.insert(band, peq);
|
||||
}
|
||||
|
||||
types::Channel::Output(output) => {
|
||||
@@ -755,7 +841,8 @@ impl DSP408 {
|
||||
.current_config
|
||||
.output_state_mut(output)
|
||||
.peq_chain
|
||||
.bands[band as usize] = peq;
|
||||
.bands
|
||||
.insert(band, peq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,7 +970,7 @@ impl DSP408 {
|
||||
}
|
||||
|
||||
// Command `0x38` (Set Delay)
|
||||
pub fn set_channel_delay(
|
||||
pub fn set_delay(
|
||||
&mut self,
|
||||
channel: types::Channel,
|
||||
delay: f32,
|
||||
@@ -1286,19 +1373,12 @@ impl DSP408 {
|
||||
|
||||
let state = self.state_mut()?;
|
||||
|
||||
let index = types::InputChannel::iter()
|
||||
.position(|x| x == input_channel)
|
||||
.ok_or_else(|| {
|
||||
DSPError::Validation(ValidationError::Value(
|
||||
"Invalid input channel".to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
state
|
||||
.current_config
|
||||
.output_state_mut(output_channel)
|
||||
.matrix_routes
|
||||
.gains[index] = gain;
|
||||
.gains
|
||||
.insert(input_channel, gain);
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
@@ -1334,19 +1414,13 @@ impl DSP408 {
|
||||
|
||||
let state = self.state_mut()?;
|
||||
|
||||
let index = types::DiscreteFrequency::iter()
|
||||
.position(|x| x == frequency_band)
|
||||
.ok_or_else(|| {
|
||||
DSPError::Validation(ValidationError::Value(
|
||||
"Invalid GEQ frequency band".to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
state
|
||||
.current_config
|
||||
.input_state_mut(input_channel)
|
||||
.geq
|
||||
.gains[index] = gain;
|
||||
.gains
|
||||
.insert(frequency_band, gain);
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@@ -1413,7 +1487,9 @@ impl DSP408 {
|
||||
|
||||
impl Drop for DSP408 {
|
||||
fn drop(&mut self) {
|
||||
self.disconnect();
|
||||
if self.connected {
|
||||
let _ = self.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
use crate::{
|
||||
common::errors::ProtocolError,
|
||||
dsp408::{
|
||||
protocol::{
|
||||
constants::{METERS_HIGH_STEP, METERS_LOW_STEP, METERS_SPLIT, METERS_SPLIT_LEVEL_DB},
|
||||
frame::Frame,
|
||||
},
|
||||
protocol::frame::Frame,
|
||||
types::{
|
||||
Acknowledgement, AuthenticationResult, ConfigChunk, CurrentPreset, DeviceFlags,
|
||||
DeviceInfo, DeviceResponseType, HandshakeAck, Meters, ModifiedPreset, NotImplemented,
|
||||
PresetName, UnknownResponse,
|
||||
DeviceInfo, DeviceResponseType, HandshakeAck, Meter, Meters, ModifiedPreset,
|
||||
NotImplemented, PresetName, UnknownResponse,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -88,10 +85,6 @@ pub fn map_response(frame: &Frame) -> Result<DeviceResponseType, ProtocolError>
|
||||
|
||||
// Command `0x24` (Get Config Chunk)
|
||||
if cmd == 0x24 {
|
||||
// Mirrors Python's `int.from_bytes(payload[:1], "little")`, which
|
||||
// is `0` for an empty payload rather than an error — Python slices
|
||||
// never panic. `payload.get(1..)` below mirrors the same
|
||||
// forgiving-slice behavior for the remainder.
|
||||
let index = payload.first().copied().unwrap_or(0);
|
||||
|
||||
if index > 0x1c {
|
||||
@@ -166,27 +159,54 @@ pub fn map_response(frame: &Frame) -> Result<DeviceResponseType, ProtocolError>
|
||||
|
||||
// Command `0x40` (Get Level Meters)
|
||||
if cmd == 0x40 {
|
||||
const X0: f32 = -25.0; // breakpoint, in dB
|
||||
const Y0: f32 = 1.0; // raw value at the breakpoint
|
||||
const K1: f32 = 0.0104; // slope below breakpoint (raw < Y0)
|
||||
const K2: f32 = 0.0208; // slope above breakpoint (raw >= Y0)
|
||||
|
||||
const MIN_DB: f32 = -60.0;
|
||||
const MAX_DB: f32 = 12.0;
|
||||
|
||||
fn float_to_db(raw: f32) -> f32 {
|
||||
let db = if raw < Y0 {
|
||||
X0 + (raw - Y0) / K1
|
||||
} else {
|
||||
X0 + (raw - Y0) / K2
|
||||
};
|
||||
// Clamping naturally absorbs noise-floor outliers (e.g. raw ≈ 0.385
|
||||
// resolves to well below -60 dB here, so it correctly floors to -60).
|
||||
db.clamp(MIN_DB, MAX_DB)
|
||||
}
|
||||
|
||||
if payload.len() != 38 {
|
||||
return Err(ProtocolError::new("Not enough bytes for meter levels"));
|
||||
}
|
||||
|
||||
let floats: Vec<f32> = (0..36)
|
||||
let channels: Vec<Meter> = (0..36)
|
||||
.step_by(3)
|
||||
.map(|i| decode_float16(payload[i], payload[i + 1]).unwrap_or_default())
|
||||
.collect();
|
||||
.enumerate()
|
||||
.map(|(index, i)| {
|
||||
let rms = decode_float16(payload[i], payload[i + 1]).unwrap_or_default();
|
||||
let peak = decode_float16(payload[i + 2], payload[i + 1]).unwrap_or_default();
|
||||
|
||||
let meters: Vec<f32> = floats
|
||||
.into_iter()
|
||||
.map(|f| {
|
||||
if f < METERS_SPLIT {
|
||||
METERS_SPLIT_LEVEL_DB + (f - METERS_SPLIT) / METERS_LOW_STEP
|
||||
let rms_db = float_to_db(rms);
|
||||
let peak_db = float_to_db(peak);
|
||||
|
||||
let clip = if index < 4 {
|
||||
payload[36] & (1 << index) != 0
|
||||
} else {
|
||||
METERS_SPLIT_LEVEL_DB + (f - METERS_SPLIT) / METERS_HIGH_STEP
|
||||
payload[37] & (1 << (index - 4)) != 0
|
||||
};
|
||||
|
||||
Meter {
|
||||
rms: rms_db,
|
||||
peak: peak_db,
|
||||
clip,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
return Ok(DeviceResponseType::Meters(Meters { levels: meters }));
|
||||
return Ok(DeviceResponseType::Meters(Meters { channels }));
|
||||
}
|
||||
|
||||
Ok(DeviceResponseType::UnknownResponse(UnknownResponse {
|
||||
@@ -415,10 +435,10 @@ mod tests {
|
||||
|
||||
match result {
|
||||
DeviceResponseType::Meters(m) => {
|
||||
assert_eq!(m.levels.len(), 12);
|
||||
assert_eq!(m.channels.len(), 12);
|
||||
|
||||
for level in m.levels {
|
||||
assert!(level.is_finite());
|
||||
for channel in m.channels {
|
||||
assert!(channel.rms.is_finite());
|
||||
}
|
||||
}
|
||||
other => panic!("unexpected {:?}", other),
|
||||
|
||||
@@ -35,12 +35,14 @@ fn read_gate(r: &mut BinaryReader) -> Result<types::Gate, DSPError> {
|
||||
}
|
||||
|
||||
fn read_geq(r: &mut BinaryReader) -> Result<types::GraphicEQ, DSPError> {
|
||||
let gains = types::DiscreteFrequency::iter()
|
||||
.map(|_| {
|
||||
let mut gains = std::collections::HashMap::new();
|
||||
|
||||
for frequency in types::DiscreteFrequency::iter() {
|
||||
let raw = r.read_u16()?;
|
||||
Ok(mapper::raw_to_eq_gain(raw)?)
|
||||
})
|
||||
.collect::<Result<Vec<_>, DSPError>>()?;
|
||||
let gain = mapper::raw_to_eq_gain(raw)?;
|
||||
|
||||
gains.insert(frequency, gain);
|
||||
}
|
||||
|
||||
Ok(types::GraphicEQ {
|
||||
gains,
|
||||
@@ -49,17 +51,20 @@ fn read_geq(r: &mut BinaryReader) -> Result<types::GraphicEQ, DSPError> {
|
||||
}
|
||||
|
||||
fn read_peq_chain(r: &mut BinaryReader, band_count: usize) -> Result<types::PEQChain, DSPError> {
|
||||
let bands = (0..band_count)
|
||||
.map(|_| {
|
||||
Ok(types::PEQ {
|
||||
let bands = types::PEQBand::iter()
|
||||
.take(band_count)
|
||||
.map(|band| {
|
||||
let peq = types::PEQ {
|
||||
gain: mapper::raw_to_eq_gain(r.read_u16()?)?,
|
||||
frequency: mapper::raw_to_frequency_hz(r.read_u16()?)?,
|
||||
q: mapper::raw_to_peq_q(r.read_u8()?)?,
|
||||
filter_type: mapper::raw_to_peq_filter(r.read_u8()?)?,
|
||||
bypass: false,
|
||||
};
|
||||
|
||||
Ok((band, peq))
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, DSPError>>()?;
|
||||
.collect::<Result<HashMap<_, _>, DSPError>>()?;
|
||||
|
||||
Ok(types::PEQChain {
|
||||
bands,
|
||||
@@ -67,6 +72,21 @@ fn read_peq_chain(r: &mut BinaryReader, band_count: usize) -> Result<types::PEQC
|
||||
})
|
||||
}
|
||||
|
||||
fn apply_peq_bypass(
|
||||
r: &mut BinaryReader,
|
||||
bands: &mut HashMap<types::PEQBand, types::PEQ>,
|
||||
) -> Result<(), DSPError> {
|
||||
let bitmap = r.read_u16()?;
|
||||
|
||||
for (index, band) in types::PEQBand::iter().enumerate() {
|
||||
if let Some(peq) = bands.get_mut(&band) {
|
||||
peq.bypass = bitmap & (1 << index) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_crossover(r: &mut BinaryReader) -> Result<types::CrossoverFilters, DSPError> {
|
||||
let hp_freq = r.read_u16()?;
|
||||
let lp_freq = r.read_u16()?;
|
||||
@@ -152,7 +172,13 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
|
||||
inp.linked_channels = mapper::raw_to_link_channels(
|
||||
mapper::channel_to_raw(types::Channel::Input(channel))?,
|
||||
r.read_u8()?,
|
||||
)?;
|
||||
)?
|
||||
.into_iter()
|
||||
.filter_map(|link| match link {
|
||||
types::Channel::Input(input) => Some(input),
|
||||
types::Channel::Output(_) => None,
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
// ============================
|
||||
@@ -162,17 +188,26 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
|
||||
for (channel, out) in types::OutputChannel::iter().zip(output_channels.iter_mut()) {
|
||||
out.name = mapper::raw_to_channel_name(r.pop(constants::CHANNEL_NAME_LENGTH)?)?;
|
||||
|
||||
r.skip(1)?;
|
||||
|
||||
out.matrix_routes = types::MatrixRoutes {
|
||||
connected: mapper::raw_to_matrix(r.read_u8()?)?,
|
||||
|
||||
gains: (0..4)
|
||||
.map(|_| {
|
||||
gains: {
|
||||
r.skip(1)?;
|
||||
|
||||
[
|
||||
types::InputChannel::InA,
|
||||
types::InputChannel::InB,
|
||||
types::InputChannel::InC,
|
||||
types::InputChannel::InD,
|
||||
]
|
||||
.into_iter()
|
||||
.map(|input| {
|
||||
let raw = r.read_u16()?;
|
||||
Ok::<f32, DSPError>(mapper::raw_to_matrix_gain(raw)?)
|
||||
let gain = mapper::raw_to_matrix_gain(raw)?;
|
||||
Ok::<(types::InputChannel, f32), DSPError>((input, gain))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
.collect::<Result<HashMap<_, _>, _>>()?
|
||||
},
|
||||
};
|
||||
|
||||
out.crossover = read_crossover(&mut r)?;
|
||||
@@ -196,7 +231,13 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
|
||||
out.linked_channels = mapper::raw_to_link_channels(
|
||||
mapper::channel_to_raw(types::Channel::Output(channel))?,
|
||||
r.read_u8()?,
|
||||
)?;
|
||||
)?
|
||||
.into_iter()
|
||||
.filter_map(|link| match link {
|
||||
types::Channel::Output(output) => Some(output),
|
||||
types::Channel::Input(_) => None,
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
// ============================
|
||||
@@ -212,15 +253,11 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
|
||||
// ============================
|
||||
|
||||
for inp in &mut input_channels {
|
||||
apply_flag(&mut r, inp.peq_chain.bands.as_mut_slice(), |band, value| {
|
||||
band.bypass = value
|
||||
})?;
|
||||
apply_peq_bypass(&mut r, &mut inp.peq_chain.bands)?;
|
||||
}
|
||||
|
||||
for out in &mut output_channels {
|
||||
apply_flag(&mut r, out.peq_chain.bands.as_mut_slice(), |band, value| {
|
||||
band.bypass = value
|
||||
})?;
|
||||
apply_peq_bypass(&mut r, &mut out.peq_chain.bands)?;
|
||||
}
|
||||
|
||||
// ============================
|
||||
@@ -260,7 +297,7 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
|
||||
for (index, inp) in input_channels.iter_mut().enumerate() {
|
||||
let bypass = input_peq_bitmap & (1 << index) != 0;
|
||||
|
||||
for peq in inp.peq_chain.bands.iter_mut() {
|
||||
for peq in inp.peq_chain.bands.values_mut() {
|
||||
peq.bypass = bypass;
|
||||
}
|
||||
}
|
||||
@@ -270,7 +307,7 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
|
||||
for (index, out) in output_channels.iter_mut().enumerate() {
|
||||
let bypass = output_peq_bitmap & (1 << index) != 0;
|
||||
|
||||
for peq in out.peq_chain.bands.iter_mut() {
|
||||
for peq in out.peq_chain.bands.values_mut() {
|
||||
peq.bypass = bypass;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +77,3 @@ pub fn output_link_mask(channel: u8) -> Option<u8> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub const METERS_SPLIT: f32 = 1.0;
|
||||
pub const METERS_SPLIT_LEVEL_DB: f32 = -18.0;
|
||||
pub const METERS_LOW_STEP: f32 = 0.0104;
|
||||
pub const METERS_HIGH_STEP: f32 = 0.0208;
|
||||
|
||||
+45
-8
@@ -1,8 +1,12 @@
|
||||
use std::{collections::HashMap, net::IpAddr};
|
||||
use strum_macros::{EnumCount, EnumIter};
|
||||
|
||||
#[cfg(feature = "serialization")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ------ Enums ------
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum DelayUnit {
|
||||
Millisecond,
|
||||
@@ -10,6 +14,7 @@ pub enum DelayUnit {
|
||||
Feet,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter, EnumCount, PartialOrd, Ord)]
|
||||
pub enum InputChannel {
|
||||
InA,
|
||||
@@ -18,6 +23,7 @@ pub enum InputChannel {
|
||||
InD,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter, EnumCount, PartialOrd, Ord)]
|
||||
pub enum OutputChannel {
|
||||
Out1,
|
||||
@@ -30,12 +36,14 @@ pub enum OutputChannel {
|
||||
Out8,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum Channel {
|
||||
Input(InputChannel),
|
||||
Output(OutputChannel),
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub enum Ratio {
|
||||
#[default]
|
||||
@@ -57,6 +65,7 @@ pub enum Ratio {
|
||||
Limit,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub enum CrossoverFilter {
|
||||
#[default]
|
||||
@@ -83,6 +92,7 @@ pub enum CrossoverFilter {
|
||||
Bl48,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)]
|
||||
pub enum PEQBand {
|
||||
B1 = 1,
|
||||
@@ -96,6 +106,7 @@ pub enum PEQBand {
|
||||
B9,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum PEQFilter {
|
||||
Peak,
|
||||
@@ -109,6 +120,7 @@ pub enum PEQFilter {
|
||||
AllPass2,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)]
|
||||
pub enum DiscreteFrequency {
|
||||
F20,
|
||||
@@ -144,6 +156,7 @@ pub enum DiscreteFrequency {
|
||||
F20000,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum InputType {
|
||||
Analog,
|
||||
@@ -152,6 +165,7 @@ pub enum InputType {
|
||||
SineWave,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct InputSource {
|
||||
pub r#type: InputType,
|
||||
@@ -194,6 +208,7 @@ pub struct PresetName {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct DeviceFlags {
|
||||
pub is_locked: bool,
|
||||
@@ -204,9 +219,18 @@ pub struct AuthenticationResult {
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Meter {
|
||||
pub rms: f32,
|
||||
pub peak: f32,
|
||||
pub clip: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Meters {
|
||||
pub levels: Vec<f32>,
|
||||
pub channels: Vec<Meter>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -238,7 +262,7 @@ pub enum DeviceResponseType {
|
||||
}
|
||||
|
||||
// ------ State ------
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct Gate {
|
||||
pub attack: u16,
|
||||
@@ -247,6 +271,7 @@ pub struct Gate {
|
||||
pub threshold: f32,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct Compressor {
|
||||
pub threshold: f32,
|
||||
@@ -256,6 +281,7 @@ pub struct Compressor {
|
||||
pub knee: u16,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct Limiter {
|
||||
pub threshold: f32,
|
||||
@@ -263,12 +289,14 @@ pub struct Limiter {
|
||||
pub release: u16,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct GraphicEQ {
|
||||
pub gains: Vec<f32>,
|
||||
pub gains: HashMap<DiscreteFrequency, f32>,
|
||||
pub bypass: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct PEQ {
|
||||
pub gain: f32,
|
||||
@@ -278,30 +306,35 @@ pub struct PEQ {
|
||||
pub bypass: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct CrossoverFilters {
|
||||
pub high_pass: Crossover,
|
||||
pub low_pass: Crossover,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct Crossover {
|
||||
pub frequency: f32,
|
||||
pub slope: CrossoverFilter,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct MatrixRoutes {
|
||||
pub connected: Vec<InputChannel>,
|
||||
pub gains: Vec<f32>,
|
||||
pub gains: HashMap<InputChannel, f32>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct PEQChain {
|
||||
pub bands: Vec<PEQ>,
|
||||
pub bands: HashMap<PEQBand, PEQ>,
|
||||
pub bypass: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct PresetBank {
|
||||
pub current_index: usize,
|
||||
@@ -309,6 +342,7 @@ pub struct PresetBank {
|
||||
pub modified: Vec<bool>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct DeviceConnection {
|
||||
pub host: IpAddr,
|
||||
@@ -316,6 +350,7 @@ pub struct DeviceConnection {
|
||||
pub device_id: u8,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct InputChannelState {
|
||||
pub name: String,
|
||||
@@ -326,10 +361,11 @@ pub struct InputChannelState {
|
||||
pub geq: GraphicEQ,
|
||||
pub peq_chain: PEQChain,
|
||||
pub crossover: CrossoverFilters,
|
||||
pub linked_channels: Vec<Channel>,
|
||||
pub linked_channels: Vec<InputChannel>,
|
||||
pub mute: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct OutputChannelState {
|
||||
pub name: String,
|
||||
@@ -341,10 +377,11 @@ pub struct OutputChannelState {
|
||||
pub gain: f32,
|
||||
pub phase_inverted: bool,
|
||||
pub delay: f32,
|
||||
pub linked_channels: Vec<Channel>,
|
||||
pub linked_channels: Vec<OutputChannel>,
|
||||
pub mute: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct DSPConfigState {
|
||||
pub(crate) input_states: HashMap<InputChannel, InputChannelState>,
|
||||
@@ -367,11 +404,11 @@ impl DSPConfigState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct DSPState {
|
||||
pub(crate) name: String,
|
||||
pub(crate) flags: DeviceFlags,
|
||||
pub(crate) presets: PresetBank,
|
||||
|
||||
pub(crate) current_config: DSPConfigState,
|
||||
}
|
||||
|
||||
+12
-3
@@ -3,6 +3,7 @@ use std::str::FromStr;
|
||||
|
||||
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>> {
|
||||
let host = IpAddr::from_str("192.168.49.30")?;
|
||||
@@ -22,13 +23,21 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
println!("DSP connected.");
|
||||
|
||||
dsp.set_mute(types::Channel::Input(types::InputChannel::InA), true)?;
|
||||
// dsp.set_mute(types::Channel::Input(types::InputChannel::InA), true)?;
|
||||
|
||||
dbg!(dsp.state()?);
|
||||
let r = dsp.set_peq_band(types::Channel::Input(InA), types::PEQBand::B1, 3.0, 1400.0, 2.1, types::PEQFilter::LowShelf, false)?;
|
||||
dbg!(r);
|
||||
|
||||
// dbg!(dsp.state()?);
|
||||
|
||||
// let s = &dsp.state()?.current_config.input_states[&InputChannel::InA].peq_chain;
|
||||
// dbg!(s)
|
||||
|
||||
println!("Disconnecting...");
|
||||
dsp.disconnect();
|
||||
dsp.disconnect()?;
|
||||
println!("Disconnected");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 10020100 27 40 263df7 263dfd 263dfd 263dfd 063d8d 063d63 263dcf 263dfd 263df6 263dcf 263dcf 263dcf 0300 10038b
|
||||
|
||||
Reference in New Issue
Block a user