fix: refactor to avoid circular dependencies

This commit is contained in:
2026-07-20 23:14:26 +02:00
parent 455585eeb6
commit 605d23f529
20 changed files with 603 additions and 522 deletions
+1
View File
@@ -0,0 +1 @@
# verify import order
+29 -386
View File
@@ -1,16 +1,30 @@
use std::{
collections::HashMap, io::{Read, Write}, net::{IpAddr, SocketAddr, TcpStream}, sync::{Mutex, OnceLock}, time::Duration, fmt,
collections::HashMap,
fmt,
io::{Read, Write},
net::{IpAddr, SocketAddr, TcpStream},
sync::{Mutex, OnceLock},
time::Duration,
};
use strum::{EnumCount, IntoEnumIterator};
use strum::IntoEnumIterator;
use crate::common::errors::{DSPError, ValidationError};
use crate::dsp408::client::types;
use crate::dsp408::client::constants;
use crate::dsp408::protocol::frame::extract_frame;
use crate::dsp408::protocol::commands;
use crate::dsp408::mapper::response::map_response;
use crate::dsp408::mapper;
use crate::{
common::errors::{DSPError, ValidationError},
dsp408::{
mapper::{
self,
constants::{PRESET_INDEX_MAX, CONFIG_CHUNK_INDEX_MAX},
response::map_response,
},
protocol::{
commands,
config::decode_config,
frame::extract_frame,
},
types,
},
};
pub const SOCKET_TIMEOUT: Duration = Duration::from_secs(20);
pub const RECV_BUFSIZE: usize = 4096;
@@ -231,9 +245,9 @@ impl DSP408 {
let flags = self.get_device_flags()?;
let current_preset = self.get_current_preset()?;
let modified = self.get_preset_modification_status()?;
let mut preset_names = Vec::with_capacity(constants::PRESET_INDEX_MAX);
let mut preset_names = Vec::with_capacity(PRESET_INDEX_MAX);
for i in 0..constants::PRESET_INDEX_MAX {
for i in 0..PRESET_INDEX_MAX {
preset_names.push(
self.get_preset_name(i + 1)?
);
@@ -805,6 +819,7 @@ impl DSP408 {
}
// Command `0x33` (Set PEQ Band)
#[allow(clippy::too_many_arguments)]
pub fn set_peq_band(
&mut self,
channel: types::Channel,
@@ -847,7 +862,7 @@ impl DSP408 {
gain: gain_db,
frequency: freq_hz,
q,
filter_type: filter_type,
filter_type,
bypass,
};
@@ -1607,12 +1622,12 @@ impl DSP408 {
fn read_config_state(&mut self) -> Result<types::DSPConfigState, DSPError> {
let mut raw = Vec::new();
for i in 0..=constants::CONFIG_CHUNK_INDEX_MAX {
for i in 0..=CONFIG_CHUNK_INDEX_MAX {
let chunk = self.get_config_chunk(i)?;
raw.extend_from_slice(&chunk.data);
}
let config = build_config_from_chunk(&raw)?;
let config = decode_config(&raw)?;
if !self.request_acknowledgement()? {
return Err(DSPError::OperationFailed(
@@ -1641,375 +1656,3 @@ impl fmt::Debug for DSP408 {
.finish()
}
}
struct BinaryReader<'a> {
data: &'a [u8],
}
impl<'a> BinaryReader<'a> {
fn new(data: &'a [u8]) -> Self {
Self { data }
}
fn pop(&mut self, n: usize) -> Result<&'a [u8], DSPError> {
if self.data.len() < n {
return Err(DSPError::InvalidRequest(
"buffer underflow".into()
));
}
let (value, rest) = self.data.split_at(n);
self.data = rest;
Ok(value)
}
fn read_u8(&mut self) -> Result<u8, DSPError> {
Ok(self.pop(1)?[0])
}
fn read_u16(&mut self) -> Result<u16, DSPError> {
Ok(u16::from_le_bytes(
self.pop(2)?.try_into().unwrap()
))
}
fn skip(&mut self, n: usize) -> Result<(), DSPError> {
self.pop(n)?;
Ok(())
}
}
fn apply_flag<T>(
r: &mut BinaryReader,
targets: &mut [T],
setter: impl Fn(&mut T, bool),
) -> Result<(), DSPError> {
let bitmap = r.read_u16()?;
for (index, target) in targets.iter_mut().enumerate() {
setter(target, bitmap & (1 << index) != 0);
}
Ok(())
}
fn read_gate(r: &mut BinaryReader) -> Result<types::Gate, DSPError> {
Ok(types::Gate {
attack: mapper::raw_to_attack_ms(r.read_u16()?)?,
release: mapper::raw_to_release_ms(r.read_u16()?)?,
hold: mapper::raw_to_hold_ms(r.read_u16()?)?,
threshold: mapper::raw_to_gate_threshold_db(r.read_u16()?)?,
})
}
fn read_geq(
r: &mut BinaryReader,
) -> Result<types::GraphicEQ, DSPError> {
let gains = types::DiscreteFrequency::iter()
.map(|_| {
let raw = r.read_u16()?;
Ok(mapper::raw_to_eq_gain(raw)?)
})
.collect::<Result<Vec<_>, DSPError>>()?;
Ok(types::GraphicEQ {
gains,
bypass: false,
})
}
fn read_peq_chain(
r: &mut BinaryReader,
band_count: usize,
) -> Result<types::PEQChain, DSPError> {
let bands = (0..band_count)
.map(|_| {
Ok(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,
})
})
.collect::<Result<Vec<_>, DSPError>>()?;
Ok(types::PEQChain {
bands,
bypass: false,
})
}
fn read_crossover(
r: &mut BinaryReader,
) -> Result<types::CrossoverFilters, DSPError> {
let hp_freq = r.read_u16()?;
let lp_freq = r.read_u16()?;
let hp_filter = r.read_u8()?;
let lp_filter = r.read_u8()?;
Ok(types::CrossoverFilters {
high_pass: types::Crossover {
frequency: mapper::raw_to_frequency_hz(hp_freq)?,
slope: mapper::raw_to_crossover_filter(hp_filter)?,
},
low_pass: types::Crossover {
frequency: mapper::raw_to_frequency_hz(lp_freq)?,
slope: mapper::raw_to_crossover_filter(lp_filter)?,
},
})
}
fn read_compressor(
r: &mut BinaryReader,
) -> Result<types::Compressor, DSPError> {
Ok(types::Compressor {
ratio: mapper::raw_to_ratio(r.read_u16()?)?,
attack: mapper::raw_to_attack_ms(r.read_u16()?)?,
release: mapper::raw_to_release_ms(r.read_u16()?)?,
knee: mapper::raw_to_knee_db(r.read_u16()?)?,
threshold: mapper::raw_to_threshold_db(r.read_u16()?)?,
})
}
fn read_limiter(
r: &mut BinaryReader,
) -> Result<types::Limiter, DSPError> {
let attack = mapper::raw_to_attack_ms(r.read_u16()?)?;
let release = mapper::raw_to_release_ms(r.read_u16()?)?;
r.skip(2)?;
let threshold = mapper::raw_to_threshold_db(r.read_u16()?)?;
Ok(types::Limiter {
attack,
release,
threshold,
})
}
fn build_config_from_chunk(payload: &[u8]) -> Result<types::DSPConfigState, DSPError> {
let mut r = BinaryReader::new(payload);
r.skip(2)?; // flags (unused)
let _preset_name = mapper::raw_to_preset_name(r.pop(constants::PRESET_NAME_LENGTH)?)?;
let mut input_channels = vec![types::InputChannelState::default(); types::InputChannel::COUNT];
let mut output_channels = vec![types::OutputChannelState::default(); types::OutputChannel::COUNT];
let mut input_delays_raw: Vec<u8> = Vec::new();
let mut output_delays_raw: Vec<u8> = Vec::new();
// ============================
// INPUT CHANNELS
// ============================
for (channel, inp) in types::InputChannel::iter().zip(input_channels.iter_mut()) {
inp.name = mapper::raw_to_channel_name(r.pop(constants::CHANNEL_NAME_LENGTH)?)?;
inp.gate = read_gate(&mut r)?;
inp.geq = read_geq(&mut r)?;
inp.peq_chain = read_peq_chain(&mut r, constants::PEQ_INPUT_BAND_COUNT)?;
inp.crossover = read_crossover(&mut r)?;
inp.gain = mapper::raw_to_gain(r.read_u16()?)?;
inp.phase_inverted = r.read_u8()? != 0;
r.skip(2)?; // reserved
input_delays_raw.push(r.read_u8()?);
r.skip(1)?;
inp.linked_channels = mapper::raw_to_link_channels(mapper::channel_to_raw(types::Channel::Input(channel))?, r.read_u8()?)?;
}
// ============================
// OUTPUT CHANNELS
// ============================
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(|_| {
let raw = r.read_u16()?;
Ok::<f32, DSPError>(mapper::raw_to_matrix_gain(raw)?)
})
.collect::<Result<Vec<_>, _>>()?,
};
out.crossover = read_crossover(&mut r)?;
out.peq_chain = read_peq_chain(&mut r, constants::PEQ_OUTPUT_BAND_COUNT)?;
out.compressor = read_compressor(&mut r)?;
out.limiter = read_limiter(&mut r)?;
out.gain = mapper::raw_to_gain(r.read_u16()?)?;
out.phase_inverted = r.read_u8()? != 0;
r.skip(2)?; // reserved
output_delays_raw.push(r.read_u8()?);
r.skip(1)?;
out.linked_channels = mapper::raw_to_link_channels(mapper::channel_to_raw(types::Channel::Output(channel))?, r.read_u8()?)?;
}
// ============================
// MUTE FLAGS
// ============================
apply_flag(
&mut r,
&mut input_channels,
|ch, value| ch.mute = value,
)?;
apply_flag(
&mut r,
&mut output_channels,
|ch, value| ch.mute = value,
)?;
// ============================
// PEQ BYPASS FLAGS
// ============================
for inp in &mut input_channels {
apply_flag(
&mut r,
inp.peq_chain.bands.as_mut_slice(),
|band, value| band.bypass = value,
)?;
}
for out in &mut output_channels {
apply_flag(
&mut r,
out.peq_chain.bands.as_mut_slice(),
|band, value| band.bypass = value,
)?;
}
// ============================
// INPUT SOURCE
// ============================
let input_source = types::InputSource {
r#type: mapper::raw_to_input_source(
r.read_u16()? as u8,
)?,
frequency: mapper::raw_to_frequency_value(
r.read_u16()? as u8,
)?,
};
// ============================
// DELAY UNIT
// ============================
let delay_unit = mapper::raw_to_delay_unit(
r.read_u16()? as u8,
)?;
// ============================
// APPLY DELAYS
// ============================
for (inp, raw_delay) in input_channels.iter_mut().zip(input_delays_raw)
{
inp.delay = mapper::raw_to_delay(
raw_delay,
delay_unit,
)?;
}
for (out, raw_delay) in output_channels.iter_mut().zip(output_delays_raw)
{
out.delay = mapper::raw_to_delay(
raw_delay,
delay_unit,
)?;
}
// ============================
// PEQ / GEQ BYPASS
// ============================
let input_peq_bitmap = r.read_u16()?;
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() {
peq.bypass = bypass;
}
}
let output_peq_bitmap = r.read_u16()?;
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() {
peq.bypass = bypass;
}
}
let bitmap = r.read_u16()?;
for (index, inp) in input_channels.iter_mut().enumerate() {
inp.geq.bypass = bitmap & (1 << index) != 0;
}
// ============================
// BUILD CHANNEL MAP
// ============================
let mut input_states = HashMap::new();
for (channel, state) in types::InputChannel::iter().zip(input_channels) {
input_states.insert(
channel,
state,
);
}
let mut output_states = HashMap::new();
for (channel, state) in types::OutputChannel::iter().zip(output_channels) {
output_states.insert(
channel,
state,
);
}
Ok(types::DSPConfigState {
input_states,
output_states,
delay_unit: delay_unit,
input_source: input_source,
})
}
-3
View File
@@ -1,5 +1,2 @@
pub mod constants;
pub mod types;
pub mod dsp408;
pub use dsp408::DSP408;
+73 -55
View File
@@ -1,13 +1,31 @@
use strum::IntoEnumIterator;
use crate::common::validator::{validate_ascii_padded, validate_range};
use crate::dsp408::client::constants::{self as client_constants};
use crate::dsp408::protocol::constants as protocol_constants;
use crate::dsp408::protocol::validators;
use crate::common::errors::{MapperError, DecodingError, ValidationError};
use super::{encode_ascii, decode_ascii};
use crate::{
common::{
errors::{
DecodingError,
MapperError,
ValidationError,
},
validator::{
validate_ascii_padded,
validate_range,
},
},
dsp408::{
protocol::{
constants as protocol_constants,
validators,
},
types,
},
};
use crate::dsp408::client::types;
use super::{
constants as mapper_constants,
decode_ascii,
encode_ascii,
};
// ------ CHANNEL ------
@@ -76,12 +94,12 @@ pub fn output_channel_to_raw(channel: types::OutputChannel) -> Result<u8, Mapper
pub fn channel_name_to_raw(name: &str) -> Result<Vec<u8>, MapperError> {
validate_ascii_padded(
name.as_bytes(),
client_constants::CHANNEL_NAME_LENGTH,
client_constants::CHANNEL_NAME_PADDING,
protocol_constants::CHANNEL_NAME_LENGTH,
protocol_constants::CHANNEL_NAME_PADDING,
"Channel name",
)?;
Ok(encode_ascii(name, client_constants::CHANNEL_NAME_LENGTH, client_constants::CHANNEL_NAME_PADDING)?)
Ok(encode_ascii(name, protocol_constants::CHANNEL_NAME_LENGTH, protocol_constants::CHANNEL_NAME_PADDING)?)
}
pub fn raw_to_channel_name(raw_name: &[u8]) -> Result<String, MapperError> {
@@ -95,7 +113,7 @@ pub fn raw_to_channel_name(raw_name: &[u8]) -> Result<String, MapperError> {
pub fn attack_ms_to_raw(value: u16) -> Result<u16, MapperError> {
validate_range(
value,
client_constants::ATTACK_MS_RANGE,
mapper_constants::ATTACK_MS_RANGE,
|| format!("Invalid attack: {:#06x}", value),
)?;
@@ -113,7 +131,7 @@ pub fn raw_to_attack_ms(value: u16) -> Result<u16, MapperError> {
pub fn release_ms_to_raw(value: u16) -> Result<u16, MapperError> {
validate_range(
value,
client_constants::RELEASE_MS_RANGE,
mapper_constants::RELEASE_MS_RANGE,
|| format!("Invalid release: {:#06x}", value),
)?;
@@ -131,7 +149,7 @@ pub fn raw_to_release_ms(value: u16) -> Result<u16, MapperError> {
pub fn hold_ms_to_raw(hold: u16) -> Result<u16, MapperError> {
validate_range(
hold,
client_constants::HOLD_MS_RANGE,
mapper_constants::HOLD_MS_RANGE,
|| format!("Invalid hold: {:#06x}", hold),
)?;
@@ -149,19 +167,19 @@ pub fn raw_to_hold_ms(hold: u16) -> Result<u16, MapperError> {
pub fn gate_threshold_db_to_raw(db: f32) -> Result<u16, MapperError> {
validate_range(
db,
client_constants::GATE_THRESHOLD_DB_RANGE,
mapper_constants::GATE_THRESHOLD_DB_RANGE,
|| format!("Invalid gate threshold: {db}"),
)?;
let db = (db * 2.0).round() / 2.0;
Ok(((db - *client_constants::GATE_THRESHOLD_DB_RANGE.start()) * 2.0).round() as u16)
Ok(((db - *mapper_constants::GATE_THRESHOLD_DB_RANGE.start()) * 2.0).round() as u16)
}
pub fn raw_to_gate_threshold_db(value: u16) -> Result<f32, MapperError> {
validators::validate_raw_gate_threshold(value)?;
Ok(*client_constants::GATE_THRESHOLD_DB_RANGE.start() + value as f32 / 2.0)
Ok(*mapper_constants::GATE_THRESHOLD_DB_RANGE.start() + value as f32 / 2.0)
}
// ------ EQ GAIN ------
@@ -172,8 +190,8 @@ fn eq_gain_raw_steps() -> f32 {
}
fn eq_gain_step_db() -> f32 {
(*client_constants::EQ_GAIN_DB_RANGE.end()
- *client_constants::EQ_GAIN_DB_RANGE.start())
(*mapper_constants::EQ_GAIN_DB_RANGE.end()
- *mapper_constants::EQ_GAIN_DB_RANGE.start())
/ eq_gain_raw_steps()
}
@@ -184,7 +202,7 @@ fn eq_quantize(db: f32) -> f32 {
pub fn eq_gain_db_to_raw(gain: f32) -> Result<u16, MapperError> {
validate_range(
gain,
client_constants::EQ_GAIN_DB_RANGE,
mapper_constants::EQ_GAIN_DB_RANGE,
|| format!("Invalid EQ gain: {gain}"),
)?;
@@ -192,7 +210,7 @@ pub fn eq_gain_db_to_raw(gain: f32) -> Result<u16, MapperError> {
Ok(
*protocol_constants::EQ_GAIN_RAW_RANGE.start()
+ ((gain - *client_constants::EQ_GAIN_DB_RANGE.start())
+ ((gain - *mapper_constants::EQ_GAIN_DB_RANGE.start())
/ eq_gain_step_db())
.round() as u16
)
@@ -202,7 +220,7 @@ pub fn raw_to_eq_gain(value: u16) -> Result<f32, MapperError> {
validators::validate_raw_eq_gain(value)?;
let gain =
*client_constants::EQ_GAIN_DB_RANGE.start()
*mapper_constants::EQ_GAIN_DB_RANGE.start()
+ (value - *protocol_constants::EQ_GAIN_RAW_RANGE.start()) as f32
* eq_gain_step_db();
@@ -246,30 +264,30 @@ pub fn peq_band_to_raw(band: types::PEQBand, channel: types::Channel) -> Result<
// ------ GAIN ------
fn gain_split_raw() -> u16 {
((client_constants::GAIN_SPLIT_DB - client_constants::GAIN_DB_RANGE.start()) / client_constants::GAIN_LOW_STEP_DB).round() as u16
((mapper_constants::GAIN_SPLIT_DB - mapper_constants::GAIN_DB_RANGE.start()) / mapper_constants::GAIN_LOW_STEP_DB).round() as u16
}
fn quantize_gain(db: f32) -> f32 {
if db < client_constants::GAIN_SPLIT_DB {
(db / client_constants::GAIN_LOW_STEP_DB).round() * client_constants::GAIN_LOW_STEP_DB
if db < mapper_constants::GAIN_SPLIT_DB {
(db / mapper_constants::GAIN_LOW_STEP_DB).round() * mapper_constants::GAIN_LOW_STEP_DB
} else {
(db / client_constants::GAIN_HIGH_STEP_DB).round() * client_constants::GAIN_HIGH_STEP_DB
(db / mapper_constants::GAIN_HIGH_STEP_DB).round() * mapper_constants::GAIN_HIGH_STEP_DB
}
}
pub fn gain_db_to_raw(gain: f32) -> Result<u16, MapperError> {
validate_range(
gain,
client_constants::GAIN_DB_RANGE,
mapper_constants::GAIN_DB_RANGE,
|| format!("Invalid channel gain: {gain}"),
)?;
let gain = quantize_gain(gain);
if gain < client_constants::GAIN_SPLIT_DB {
Ok(((gain - client_constants::GAIN_DB_RANGE.start()) / client_constants::GAIN_LOW_STEP_DB).round() as u16)
if gain < mapper_constants::GAIN_SPLIT_DB {
Ok(((gain - mapper_constants::GAIN_DB_RANGE.start()) / mapper_constants::GAIN_LOW_STEP_DB).round() as u16)
} else {
Ok(gain_split_raw() + ((gain - client_constants::GAIN_SPLIT_DB) / client_constants::GAIN_HIGH_STEP_DB).round() as u16)
Ok(gain_split_raw() + ((gain - mapper_constants::GAIN_SPLIT_DB) / mapper_constants::GAIN_HIGH_STEP_DB).round() as u16)
}
}
@@ -277,9 +295,9 @@ pub fn raw_to_gain(raw_gain: u16) -> Result<f32, MapperError> {
validators::validate_raw_gain(raw_gain)?;
if raw_gain < gain_split_raw() {
Ok(client_constants::GAIN_DB_RANGE.start() + raw_gain as f32 * client_constants::GAIN_LOW_STEP_DB)
Ok(mapper_constants::GAIN_DB_RANGE.start() + raw_gain as f32 * mapper_constants::GAIN_LOW_STEP_DB)
} else {
Ok(client_constants::GAIN_SPLIT_DB + (raw_gain - gain_split_raw()) as f32 * client_constants::GAIN_HIGH_STEP_DB)
Ok(mapper_constants::GAIN_SPLIT_DB + (raw_gain - gain_split_raw()) as f32 * mapper_constants::GAIN_HIGH_STEP_DB)
}
}
@@ -289,13 +307,13 @@ pub fn frequency_hz_to_raw(hz: f32) -> Result<u16, MapperError> {
validate_range(
hz,
client_constants::FREQUENCY_HZ_RANGE,
mapper_constants::FREQUENCY_HZ_RANGE,
|| format!("Invalid frequency: {hz}"),
)?;
let device_steps = (protocol_constants::FREQUENCY_RAW_RANGE.end() - protocol_constants::FREQUENCY_RAW_RANGE.start()) as f32;
let raw = ((hz / client_constants::FREQUENCY_HZ_RANGE.start()).ln() / (client_constants::FREQUENCY_HZ_RANGE.end() / client_constants::FREQUENCY_HZ_RANGE.start()).ln()
let raw = ((hz / mapper_constants::FREQUENCY_HZ_RANGE.start()).ln() / (mapper_constants::FREQUENCY_HZ_RANGE.end() / mapper_constants::FREQUENCY_HZ_RANGE.start()).ln()
* device_steps)
.round();
@@ -308,7 +326,7 @@ pub fn raw_to_frequency_hz(value: u16) -> Result<f32, MapperError> {
let device_steps = (protocol_constants::FREQUENCY_RAW_RANGE.end() - protocol_constants::FREQUENCY_RAW_RANGE.start()) as f32;
let normalized = (value - protocol_constants::FREQUENCY_RAW_RANGE.start()) as f32 / device_steps;
Ok(client_constants::FREQUENCY_HZ_RANGE.start() * (client_constants::FREQUENCY_HZ_RANGE.end() / client_constants::FREQUENCY_HZ_RANGE.start()).powf(normalized))
Ok(mapper_constants::FREQUENCY_HZ_RANGE.start() * (mapper_constants::FREQUENCY_HZ_RANGE.end() / mapper_constants::FREQUENCY_HZ_RANGE.start()).powf(normalized))
}
// ------ DISCRETE FREQUENCY ------
@@ -481,13 +499,13 @@ pub fn raw_to_crossover_filter(raw: u8) -> Result<types::CrossoverFilter, Mapper
pub fn peq_q_to_raw(q: f32, filter: types::PEQFilter) -> Result<u8, MapperError> {
validate_range(
q,
client_constants::peq_q_range(filter),
mapper_constants::peq_q_range(filter),
|| format!("Invalid PEQ Q: {q}"),
)?;
let raw_range = (protocol_constants::PEQ_Q_RAW_RANGE.end() - protocol_constants::PEQ_Q_RAW_RANGE.start()) as f32;
Ok(((q / client_constants::PEQ_Q_RANGE.start()).ln() / (client_constants::PEQ_Q_RANGE.end() / client_constants::PEQ_Q_RANGE.start()).ln() * raw_range).round() as u8
Ok(((q / mapper_constants::PEQ_Q_RANGE.start()).ln() / (mapper_constants::PEQ_Q_RANGE.end() / mapper_constants::PEQ_Q_RANGE.start()).ln() * raw_range).round() as u8
+ protocol_constants::PEQ_Q_RAW_RANGE.start())
}
@@ -497,7 +515,7 @@ pub fn raw_to_peq_q(value: u8) -> Result<f32, MapperError> {
let raw_range = (protocol_constants::PEQ_Q_RAW_RANGE.end() - protocol_constants::PEQ_Q_RAW_RANGE.start()) as f32;
let normalized = (value - protocol_constants::PEQ_Q_RAW_RANGE.start()) as f32 / raw_range;
Ok(client_constants::PEQ_Q_RANGE.start() * (client_constants::PEQ_Q_RANGE.end() / client_constants::PEQ_Q_RANGE.start()).powf(normalized))
Ok(mapper_constants::PEQ_Q_RANGE.start() * (mapper_constants::PEQ_Q_RANGE.end() / mapper_constants::PEQ_Q_RANGE.start()).powf(normalized))
}
// ------ FILTER ------
@@ -615,7 +633,7 @@ pub fn raw_to_ratio(value: u16) -> Result<types::Ratio, MapperError> {
pub fn knee_db_to_raw(db: u16) -> Result<u16, MapperError> {
validate_range(
db,
client_constants::KNEE_DB_RANGE,
mapper_constants::KNEE_DB_RANGE,
|| format!("Invalid compressor knee: {db}"),
)?;
@@ -632,18 +650,18 @@ pub fn raw_to_knee_db(value: u16) -> Result<u16, MapperError> {
pub fn threshold_db_to_raw(db: f32) -> Result<u16, MapperError> {
validate_range(
db,
client_constants::THRESHOLD_DB_RANGE,
mapper_constants::THRESHOLD_DB_RANGE,
|| format!("Invalid compressor threshold: {db}"),
)?;
let db = (db * 2.0).round() / 2.0;
Ok(((db - client_constants::THRESHOLD_DB_RANGE.start()) * 2.0).round() as u16)
Ok(((db - mapper_constants::THRESHOLD_DB_RANGE.start()) * 2.0).round() as u16)
}
pub fn raw_to_threshold_db(value: u16) -> Result<f32, MapperError> {
validators::validate_raw_threshold(value)?;
Ok(client_constants::THRESHOLD_DB_RANGE.start() + (value as f32 / 2.0))
Ok(mapper_constants::THRESHOLD_DB_RANGE.start() + (value as f32 / 2.0))
}
// ------ LINK ------
@@ -739,31 +757,31 @@ pub fn raw_to_link_channels(source_raw: u8, destination_mask: u8) -> Result<Vec<
// ------ MATRIX GAIN ------
fn matrix_gain_split_raw() -> u16 {
((client_constants::GAIN_SPLIT_DB - client_constants::MATRIX_GAIN_DB_RANGE.start()) / client_constants::GAIN_LOW_STEP_DB).round() as u16
((mapper_constants::GAIN_SPLIT_DB - mapper_constants::MATRIX_GAIN_DB_RANGE.start()) / mapper_constants::GAIN_LOW_STEP_DB).round() as u16
}
fn quantize_matrix_gain(db: f32) -> f32 {
if db < client_constants::GAIN_SPLIT_DB {
(db / client_constants::GAIN_LOW_STEP_DB).round() * client_constants::GAIN_LOW_STEP_DB
if db < mapper_constants::GAIN_SPLIT_DB {
(db / mapper_constants::GAIN_LOW_STEP_DB).round() * mapper_constants::GAIN_LOW_STEP_DB
} else {
(db / client_constants::GAIN_HIGH_STEP_DB).round() * client_constants::GAIN_HIGH_STEP_DB
(db / mapper_constants::GAIN_HIGH_STEP_DB).round() * mapper_constants::GAIN_HIGH_STEP_DB
}
}
pub fn matrix_gain_db_to_raw(gain: f32) -> Result<u16, MapperError> {
validate_range(
gain,
client_constants::MATRIX_GAIN_DB_RANGE,
mapper_constants::MATRIX_GAIN_DB_RANGE,
|| format!("Invalid matrix gain: {gain}"),
)?;
let gain = quantize_matrix_gain(gain);
if gain < client_constants::GAIN_SPLIT_DB {
Ok(((gain - client_constants::MATRIX_GAIN_DB_RANGE.start()) / client_constants::GAIN_LOW_STEP_DB).round() as u16)
if gain < mapper_constants::GAIN_SPLIT_DB {
Ok(((gain - mapper_constants::MATRIX_GAIN_DB_RANGE.start()) / mapper_constants::GAIN_LOW_STEP_DB).round() as u16)
} else {
Ok(matrix_gain_split_raw()
+ ((gain - client_constants::GAIN_SPLIT_DB) / client_constants::GAIN_HIGH_STEP_DB).round() as u16)
+ ((gain - mapper_constants::GAIN_SPLIT_DB) / mapper_constants::GAIN_HIGH_STEP_DB).round() as u16)
}
}
@@ -771,9 +789,9 @@ pub fn raw_to_matrix_gain(raw_gain: u16) -> Result<f32, MapperError> {
validators::validate_raw_matrix_gain(raw_gain)?;
if raw_gain < matrix_gain_split_raw() {
Ok(client_constants::MATRIX_GAIN_DB_RANGE.start() + raw_gain as f32 * client_constants::GAIN_LOW_STEP_DB)
Ok(mapper_constants::MATRIX_GAIN_DB_RANGE.start() + raw_gain as f32 * mapper_constants::GAIN_LOW_STEP_DB)
} else {
Ok(client_constants::GAIN_SPLIT_DB + (raw_gain - matrix_gain_split_raw()) as f32 * client_constants::GAIN_HIGH_STEP_DB)
Ok(mapper_constants::GAIN_SPLIT_DB + (raw_gain - matrix_gain_split_raw()) as f32 * mapper_constants::GAIN_HIGH_STEP_DB)
}
}
@@ -815,7 +833,7 @@ fn delay_raw_steps() -> f32 {
pub fn delay_to_raw(delay: f32, unit: types::DelayUnit) -> Result<u8, MapperError> {
let (delay_min, delay_max) = client_constants::delay_range(unit);
let (delay_min, delay_max) = mapper_constants::delay_range(unit);
validate_range(
delay,
@@ -832,7 +850,7 @@ pub fn delay_to_raw(delay: f32, unit: types::DelayUnit) -> Result<u8, MapperErro
pub fn raw_to_delay(value: u8, unit: types::DelayUnit) -> Result<f32, MapperError> {
validators::validate_raw_channel_delay(value)?;
let (delay_min, delay_max) = client_constants::delay_range(unit);
let (delay_min, delay_max) = mapper_constants::delay_range(unit);
Ok(delay_min + ((value - protocol_constants::DELAY_RAW_RANGE.start()) as f32 / delay_raw_steps()) * (delay_max - delay_min))
}
@@ -915,7 +933,7 @@ mod tests {
fn channel_name_round_trip() {
let name = "In ABCDE";
let raw = channel_name_to_raw(name).unwrap();
assert_eq!(raw.len(), client_constants::CHANNEL_NAME_LENGTH);
assert_eq!(raw.len(), protocol_constants::CHANNEL_NAME_LENGTH);
let decoded = raw_to_channel_name(&raw).unwrap();
assert_eq!(decoded, name);
}
@@ -923,7 +941,7 @@ mod tests {
#[test]
fn channel_name_padding_is_applied() {
let raw = channel_name_to_raw(" Kick ").unwrap();
assert_eq!(raw.len(), client_constants::CHANNEL_NAME_LENGTH);
assert_eq!(raw.len(), protocol_constants::CHANNEL_NAME_LENGTH);
}
// ------------------------------------------------------------------
@@ -1,17 +1,10 @@
use std::ops::RangeInclusive;
use super::types::{DelayUnit, PEQFilter};
use crate::dsp408::types::{DelayUnit, PEQFilter};
pub const PRESET_INDEX_MAX: usize = 20;
pub const CONFIG_CHUNK_INDEX_MAX: usize = 28;
pub const PRESET_NAME_LENGTH: usize = 14;
pub const PRESET_NAME_PADDING: u8 = 0x20;
pub const PASSWORD_LENGTH: usize = 4;
pub const PASSWORD_PADDING: u8 = 0x00;
pub const ATTACK_MS_RANGE: RangeInclusive<u16> = 1..=999;
pub const RELEASE_MS_RANGE: RangeInclusive<u16> = 10..=3000;
pub const KNEE_DB_RANGE: RangeInclusive<u16> = 0..=12;
@@ -21,8 +14,6 @@ pub const FREQUENCY_HZ_RANGE: RangeInclusive<f32> = 19.7..=20160.0;
pub const EQ_GAIN_DB_RANGE: RangeInclusive<f32> = -12.0..=12.0;
pub const PEQ_Q_RANGE: RangeInclusive<f32> = 0.40..=128.0;
pub const PEQ_INPUT_BAND_COUNT: usize = 8;
pub const PEQ_OUTPUT_BAND_COUNT:usize = 9;
pub fn peq_q_range(filter: PEQFilter) -> RangeInclusive<f32> {
match filter {
PEQFilter::Peak
@@ -55,15 +46,7 @@ pub fn delay_range(unit: DelayUnit) -> (f32, f32) {
}
}
pub const CHANNEL_NAME_LENGTH: usize = 8;
pub const CHANNEL_NAME_PADDING: u8 = 0x00;
pub const GATE_THRESHOLD_DB_RANGE: RangeInclusive<f32> = -90.0..=0.0;
pub const HOLD_MS_RANGE: RangeInclusive<u16> = 10..=999;
pub const MATRIX_GAIN_DB_RANGE: RangeInclusive<f32> = -60.0..=0.0;
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;
+4 -1
View File
@@ -1,4 +1,7 @@
use crate::common::errors::{EncodingError, DecodingError};
use crate::common::errors::{
DecodingError,
EncodingError,
};
// ------ STRING ------
+39 -24
View File
@@ -1,10 +1,25 @@
use crate::common::validator::{validate_ascii_padded, validate_range};
use crate::dsp408::client::constants as client_constants;
use crate::dsp408::client::types;
use crate::dsp408::protocol::validators;
use crate::common::errors::{MapperError, DecodingError};
use crate::dsp408::protocol::constants as protocol_constants;
use super::{encode_ascii, decode_ascii};
use crate::{
common::{
errors::{DecodingError, MapperError},
validator::{
validate_ascii_padded,
validate_range,
},
},
dsp408::{
protocol::{
constants as protocol_constants,
validators,
},
types,
},
};
use super::{
constants,
decode_ascii,
encode_ascii,
};
// ------ DELAY ------
@@ -51,7 +66,7 @@ pub fn raw_to_delay_unit(
pub fn preset_index_to_raw(index: usize) -> Result<u8, MapperError> {
validate_range(
index,
0..=client_constants::PRESET_INDEX_MAX,
0..=constants::PRESET_INDEX_MAX,
|| format!("Invalid preset index {}", index)
)?;
Ok(index as u8)
@@ -60,7 +75,7 @@ pub fn preset_index_to_raw(index: usize) -> Result<u8, MapperError> {
pub fn preset_index_without_factory_to_raw(index: usize) -> Result<u8, MapperError> {
validate_range(
index,
1..=client_constants::PRESET_INDEX_MAX,
1..=constants::PRESET_INDEX_MAX,
|| format!("Invalid preset index {}", index)
)?;
Ok(index as u8 - 1)
@@ -71,7 +86,7 @@ pub fn preset_index_without_factory_to_raw(index: usize) -> Result<u8, MapperErr
pub fn config_chunk_to_raw(index: usize) -> Result<u8, MapperError> {
validate_range(
index,
0..=client_constants::CONFIG_CHUNK_INDEX_MAX,
0..=constants::CONFIG_CHUNK_INDEX_MAX,
|| format!("Invalid config index: {:#04x}", index)
)?;
Ok(index as u8)
@@ -82,12 +97,12 @@ pub fn config_chunk_to_raw(index: usize) -> Result<u8, MapperError> {
pub fn preset_name_to_raw(name: &str) -> Result<Vec<u8>, MapperError> {
validate_ascii_padded(
name.as_bytes(),
client_constants::PRESET_NAME_LENGTH,
client_constants::PRESET_NAME_PADDING,
protocol_constants::PRESET_NAME_LENGTH,
protocol_constants::PRESET_NAME_PADDING,
"Preset name"
)?;
Ok(encode_ascii(name, client_constants::PRESET_NAME_LENGTH, client_constants::PRESET_NAME_PADDING)?)
Ok(encode_ascii(name, protocol_constants::PRESET_NAME_LENGTH, protocol_constants::PRESET_NAME_PADDING)?)
}
pub fn raw_to_preset_name(raw_name: &[u8]) -> Result<String, MapperError> {
@@ -139,12 +154,12 @@ pub fn raw_to_input_source(value: u8) -> Result<types::InputType, MapperError> {
pub fn password_to_raw(password: &str) -> Result<Vec<u8>, MapperError> {
validate_ascii_padded(
password.as_bytes(),
client_constants::PASSWORD_LENGTH,
client_constants::PASSWORD_PADDING,
protocol_constants::PASSWORD_LENGTH,
protocol_constants::PASSWORD_PADDING,
"Password"
)?;
Ok(encode_ascii(password, client_constants::PASSWORD_LENGTH, client_constants::PASSWORD_PADDING)?)
Ok(encode_ascii(password, protocol_constants::PASSWORD_LENGTH, protocol_constants::PASSWORD_PADDING)?)
}
#[cfg(test)]
@@ -176,8 +191,8 @@ mod tests {
fn preset_index_to_raw_bounds() {
assert_eq!(preset_index_to_raw(0).unwrap(), 0);
assert_eq!(
preset_index_to_raw(client_constants::PRESET_INDEX_MAX).unwrap(),
client_constants::PRESET_INDEX_MAX as u8,
preset_index_to_raw(constants::PRESET_INDEX_MAX).unwrap(),
constants::PRESET_INDEX_MAX as u8,
);
}
@@ -186,14 +201,14 @@ mod tests {
assert_eq!(preset_index_without_factory_to_raw(1).unwrap(), 0);
assert_eq!(
preset_index_without_factory_to_raw(client_constants::PRESET_INDEX_MAX).unwrap(),
client_constants::PRESET_INDEX_MAX as u8 - 1,
preset_index_without_factory_to_raw(constants::PRESET_INDEX_MAX).unwrap(),
constants::PRESET_INDEX_MAX as u8 - 1,
);
}
#[test]
fn preset_index_rejects_out_of_range() {
assert!(preset_index_to_raw(client_constants::PRESET_INDEX_MAX + 1).is_err());
assert!(preset_index_to_raw(constants::PRESET_INDEX_MAX + 1).is_err());
assert!(preset_index_without_factory_to_raw(0).is_err());
}
@@ -204,14 +219,14 @@ mod tests {
assert_eq!(config_chunk_to_raw(0).unwrap(), 0);
assert_eq!(
config_chunk_to_raw(client_constants::CONFIG_CHUNK_INDEX_MAX).unwrap(),
client_constants::CONFIG_CHUNK_INDEX_MAX as u8,
config_chunk_to_raw(constants::CONFIG_CHUNK_INDEX_MAX).unwrap(),
constants::CONFIG_CHUNK_INDEX_MAX as u8,
);
}
#[test]
fn config_chunk_rejects_invalid() {
assert!(config_chunk_to_raw(client_constants::CONFIG_CHUNK_INDEX_MAX + 1).is_err());
assert!(config_chunk_to_raw(constants::CONFIG_CHUNK_INDEX_MAX + 1).is_err());
}
// -------- Preset Name --------
+1
View File
@@ -2,6 +2,7 @@ pub mod channel;
pub mod encoding;
pub mod global;
pub mod response;
pub mod constants;
pub use channel::*;
+28 -7
View File
@@ -1,11 +1,32 @@
use crate::dsp408::client::constants::{METERS_HIGH_STEP, METERS_LOW_STEP, METERS_SPLIT, METERS_SPLIT_LEVEL_DB};
use crate::dsp408::client::types::{
Acknowledgement, AuthenticationResult, ConfigChunk, CurrentPreset, DeviceFlags, DeviceInfo,
DeviceResponseType, HandshakeAck, Meters, ModifiedPreset, NotImplemented, PresetName,
UnknownResponse,
use crate::{
common::errors::ProtocolError,
dsp408::{
protocol::{
constants::{
METERS_HIGH_STEP,
METERS_LOW_STEP,
METERS_SPLIT,
METERS_SPLIT_LEVEL_DB,
},
frame::Frame,
},
types::{
Acknowledgement,
AuthenticationResult,
ConfigChunk,
CurrentPreset,
DeviceFlags,
DeviceInfo,
DeviceResponseType,
HandshakeAck,
Meters,
ModifiedPreset,
NotImplemented,
PresetName,
UnknownResponse,
},
},
};
use crate::common::errors::ProtocolError;
use crate::dsp408::protocol::frame::Frame;
use super::encoding::{decode_ascii, decode_float16};
+4 -3
View File
@@ -1,5 +1,6 @@
pub mod client;
pub mod mapper;
pub mod protocol;
mod client;
mod mapper;
pub(crate) mod protocol;
pub mod types;
pub use client::DSP408;
+6 -4
View File
@@ -1,7 +1,9 @@
use super::frame::{build_frame};
use crate::common::errors::ValidationError;
use super::validators::*;
use super::{
frame::build_frame,
validators::*,
};
fn split_u16(value: u16) -> [u8; 2] {
[
@@ -150,7 +152,7 @@ pub fn build_unlock_device(
Ok(build_frame(
0x2d,
&password
password
))
}
+349
View File
@@ -0,0 +1,349 @@
use std::collections::HashMap;
use strum::{EnumCount, IntoEnumIterator};
use crate::{
common::errors::DSPError,
dsp408::{
mapper,
types,
protocol::{
constants,
reader::BinaryReader,
},
},
};
fn apply_flag<T>(
r: &mut BinaryReader,
targets: &mut [T],
setter: impl Fn(&mut T, bool),
) -> Result<(), DSPError> {
let bitmap = r.read_u16()?;
for (index, target) in targets.iter_mut().enumerate() {
setter(target, bitmap & (1 << index) != 0);
}
Ok(())
}
fn read_gate(r: &mut BinaryReader) -> Result<types::Gate, DSPError> {
Ok(types::Gate {
attack: mapper::raw_to_attack_ms(r.read_u16()?)?,
release: mapper::raw_to_release_ms(r.read_u16()?)?,
hold: mapper::raw_to_hold_ms(r.read_u16()?)?,
threshold: mapper::raw_to_gate_threshold_db(r.read_u16()?)?,
})
}
fn read_geq(
r: &mut BinaryReader,
) -> Result<types::GraphicEQ, DSPError> {
let gains = types::DiscreteFrequency::iter()
.map(|_| {
let raw = r.read_u16()?;
Ok(mapper::raw_to_eq_gain(raw)?)
})
.collect::<Result<Vec<_>, DSPError>>()?;
Ok(types::GraphicEQ {
gains,
bypass: false,
})
}
fn read_peq_chain(
r: &mut BinaryReader,
band_count: usize,
) -> Result<types::PEQChain, DSPError> {
let bands = (0..band_count)
.map(|_| {
Ok(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,
})
})
.collect::<Result<Vec<_>, DSPError>>()?;
Ok(types::PEQChain {
bands,
bypass: false,
})
}
fn read_crossover(
r: &mut BinaryReader,
) -> Result<types::CrossoverFilters, DSPError> {
let hp_freq = r.read_u16()?;
let lp_freq = r.read_u16()?;
let hp_filter = r.read_u8()?;
let lp_filter = r.read_u8()?;
Ok(types::CrossoverFilters {
high_pass: types::Crossover {
frequency: mapper::raw_to_frequency_hz(hp_freq)?,
slope: mapper::raw_to_crossover_filter(hp_filter)?,
},
low_pass: types::Crossover {
frequency: mapper::raw_to_frequency_hz(lp_freq)?,
slope: mapper::raw_to_crossover_filter(lp_filter)?,
},
})
}
fn read_compressor(
r: &mut BinaryReader,
) -> Result<types::Compressor, DSPError> {
Ok(types::Compressor {
ratio: mapper::raw_to_ratio(r.read_u16()?)?,
attack: mapper::raw_to_attack_ms(r.read_u16()?)?,
release: mapper::raw_to_release_ms(r.read_u16()?)?,
knee: mapper::raw_to_knee_db(r.read_u16()?)?,
threshold: mapper::raw_to_threshold_db(r.read_u16()?)?,
})
}
fn read_limiter(
r: &mut BinaryReader,
) -> Result<types::Limiter, DSPError> {
let attack = mapper::raw_to_attack_ms(r.read_u16()?)?;
let release = mapper::raw_to_release_ms(r.read_u16()?)?;
r.skip(2)?;
let threshold = mapper::raw_to_threshold_db(r.read_u16()?)?;
Ok(types::Limiter {
attack,
release,
threshold,
})
}
pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError> {
let mut r = BinaryReader::new(payload);
r.skip(2)?; // flags (unused)
let _preset_name = mapper::raw_to_preset_name(r.pop(constants::PRESET_NAME_LENGTH)?)?;
let mut input_channels = vec![types::InputChannelState::default(); types::InputChannel::COUNT];
let mut output_channels = vec![types::OutputChannelState::default(); types::OutputChannel::COUNT];
let mut input_delays_raw: Vec<u8> = Vec::new();
let mut output_delays_raw: Vec<u8> = Vec::new();
// ============================
// INPUT CHANNELS
// ============================
for (channel, inp) in types::InputChannel::iter().zip(input_channels.iter_mut()) {
inp.name = mapper::raw_to_channel_name(r.pop(constants::CHANNEL_NAME_LENGTH)?)?;
inp.gate = read_gate(&mut r)?;
inp.geq = read_geq(&mut r)?;
inp.peq_chain = read_peq_chain(&mut r, constants::PEQ_INPUT_BAND_RANGE.count())?;
inp.crossover = read_crossover(&mut r)?;
inp.gain = mapper::raw_to_gain(r.read_u16()?)?;
inp.phase_inverted = r.read_u8()? != 0;
r.skip(2)?; // reserved
input_delays_raw.push(r.read_u8()?);
r.skip(1)?;
inp.linked_channels = mapper::raw_to_link_channels(mapper::channel_to_raw(types::Channel::Input(channel))?, r.read_u8()?)?;
}
// ============================
// OUTPUT CHANNELS
// ============================
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(|_| {
let raw = r.read_u16()?;
Ok::<f32, DSPError>(mapper::raw_to_matrix_gain(raw)?)
})
.collect::<Result<Vec<_>, _>>()?,
};
out.crossover = read_crossover(&mut r)?;
out.peq_chain = read_peq_chain(&mut r, constants::PEQ_OUTPUT_BAND_RANGE.count())?;
out.compressor = read_compressor(&mut r)?;
out.limiter = read_limiter(&mut r)?;
out.gain = mapper::raw_to_gain(r.read_u16()?)?;
out.phase_inverted = r.read_u8()? != 0;
r.skip(2)?; // reserved
output_delays_raw.push(r.read_u8()?);
r.skip(1)?;
out.linked_channels = mapper::raw_to_link_channels(mapper::channel_to_raw(types::Channel::Output(channel))?, r.read_u8()?)?;
}
// ============================
// MUTE FLAGS
// ============================
apply_flag(
&mut r,
&mut input_channels,
|ch, value| ch.mute = value,
)?;
apply_flag(
&mut r,
&mut output_channels,
|ch, value| ch.mute = value,
)?;
// ============================
// PEQ BYPASS FLAGS
// ============================
for inp in &mut input_channels {
apply_flag(
&mut r,
inp.peq_chain.bands.as_mut_slice(),
|band, value| band.bypass = value,
)?;
}
for out in &mut output_channels {
apply_flag(
&mut r,
out.peq_chain.bands.as_mut_slice(),
|band, value| band.bypass = value,
)?;
}
// ============================
// INPUT SOURCE
// ============================
let input_source = types::InputSource {
r#type: mapper::raw_to_input_source(
r.read_u16()? as u8,
)?,
frequency: mapper::raw_to_frequency_value(
r.read_u16()? as u8,
)?,
};
// ============================
// DELAY UNIT
// ============================
let delay_unit = mapper::raw_to_delay_unit(
r.read_u16()? as u8,
)?;
// ============================
// APPLY DELAYS
// ============================
for (inp, raw_delay) in input_channels.iter_mut().zip(input_delays_raw)
{
inp.delay = mapper::raw_to_delay(
raw_delay,
delay_unit,
)?;
}
for (out, raw_delay) in output_channels.iter_mut().zip(output_delays_raw)
{
out.delay = mapper::raw_to_delay(
raw_delay,
delay_unit,
)?;
}
// ============================
// PEQ / GEQ BYPASS
// ============================
let input_peq_bitmap = r.read_u16()?;
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() {
peq.bypass = bypass;
}
}
let output_peq_bitmap = r.read_u16()?;
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() {
peq.bypass = bypass;
}
}
let bitmap = r.read_u16()?;
for (index, inp) in input_channels.iter_mut().enumerate() {
inp.geq.bypass = bitmap & (1 << index) != 0;
}
// ============================
// BUILD CHANNEL MAP
// ============================
let mut input_states = HashMap::new();
for (channel, state) in types::InputChannel::iter().zip(input_channels) {
input_states.insert(
channel,
state,
);
}
let mut output_states = HashMap::new();
for (channel, state) in types::OutputChannel::iter().zip(output_channels) {
output_states.insert(
channel,
state,
);
}
Ok(types::DSPConfigState {
input_states,
output_states,
delay_unit,
input_source,
})
}
+5
View File
@@ -77,3 +77,8 @@ 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;
+3 -6
View File
@@ -88,12 +88,9 @@ fn find_footer(data: &[u8]) -> Option<usize> {
if data.len() < 2 {
return None;
}
for i in 2..data.len() - 1 {
if data[i] == 0x10 && data[i + 1] == 0x03 {
return Some(i);
}
}
None
(2..data.len() - 1)
.find(|&i| data[i] == 0x10 && data[i + 1] == 0x03)
}
#[cfg(test)]
+6 -4
View File
@@ -1,4 +1,6 @@
pub mod frame;
pub mod commands;
pub mod constants;
pub mod validators;
pub(crate) mod frame;
pub(crate) mod commands;
pub(crate) mod constants;
pub(crate) mod validators;
mod reader;
pub(crate) mod config;
+39
View File
@@ -0,0 +1,39 @@
use crate::common::errors::DSPError;
pub struct BinaryReader<'a> {
data: &'a [u8],
}
impl<'a> BinaryReader<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data }
}
pub fn pop(&mut self, n: usize) -> Result<&'a [u8], DSPError> {
if self.data.len() < n {
return Err(DSPError::InvalidRequest(
"buffer underflow".into()
));
}
let (value, rest) = self.data.split_at(n);
self.data = rest;
Ok(value)
}
pub fn read_u8(&mut self) -> Result<u8, DSPError> {
Ok(self.pop(1)?[0])
}
pub fn read_u16(&mut self) -> Result<u16, DSPError> {
Ok(u16::from_le_bytes(
self.pop(2)?.try_into().unwrap()
))
}
pub fn skip(&mut self, n: usize) -> Result<(), DSPError> {
self.pop(n)?;
Ok(())
}
}
+11 -3
View File
@@ -1,6 +1,14 @@
use crate::common::errors::ValidationError;
use crate::common::validator::{validate_ascii_padded, validate_bitmask, validate_range};
use super::constants::*;
use crate::{
common::{
errors::ValidationError,
validator::{
validate_ascii_padded,
validate_bitmask,
validate_range,
},
},
dsp408::protocol::constants::*,
};
pub fn validate_raw_delay_unit(raw_unit: u8) -> Result<(), ValidationError> {
validate_range(
+2 -6
View File
@@ -1,8 +1,4 @@
mod common;
mod dsp408;
pub mod dsp408;
pub use common::errors::DSPError;
pub use dsp408::DSP408;
pub mod types {
pub use crate::dsp408::client::types::*;
}
pub use common::errors::DSPError;
+2 -2
View File
@@ -1,8 +1,8 @@
use std::net::IpAddr;
use std::str::FromStr;
use dsp_thomann::DSP408;
use dsp_thomann::types;
use dsp_thomann::dsp408::DSP408;
use dsp_thomann::dsp408::types;
fn main() -> Result<(), Box<dyn std::error::Error>> {