fix: refactor to avoid circular dependencies
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# verify import order
|
||||
+29
-386
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
pub mod constants;
|
||||
pub mod types;
|
||||
pub mod dsp408;
|
||||
|
||||
pub use dsp408::DSP408;
|
||||
@@ -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;
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::common::errors::{EncodingError, DecodingError};
|
||||
use crate::common::errors::{
|
||||
DecodingError,
|
||||
EncodingError,
|
||||
};
|
||||
|
||||
// ------ STRING ------
|
||||
|
||||
|
||||
+39
-24
@@ -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 --------
|
||||
|
||||
@@ -2,6 +2,7 @@ pub mod channel;
|
||||
pub mod encoding;
|
||||
pub mod global;
|
||||
pub mod response;
|
||||
pub mod constants;
|
||||
|
||||
|
||||
pub use channel::*;
|
||||
|
||||
@@ -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,
|
||||
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
@@ -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;
|
||||
@@ -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
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
+1
-5
@@ -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::*;
|
||||
}
|
||||
+2
-2
@@ -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>> {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user