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::{
|
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::{
|
||||||
use crate::dsp408::client::types;
|
common::errors::{DSPError, ValidationError},
|
||||||
use crate::dsp408::client::constants;
|
dsp408::{
|
||||||
use crate::dsp408::protocol::frame::extract_frame;
|
mapper::{
|
||||||
use crate::dsp408::protocol::commands;
|
self,
|
||||||
use crate::dsp408::mapper::response::map_response;
|
constants::{PRESET_INDEX_MAX, CONFIG_CHUNK_INDEX_MAX},
|
||||||
use crate::dsp408::mapper;
|
response::map_response,
|
||||||
|
},
|
||||||
|
protocol::{
|
||||||
|
commands,
|
||||||
|
config::decode_config,
|
||||||
|
frame::extract_frame,
|
||||||
|
},
|
||||||
|
types,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub const SOCKET_TIMEOUT: Duration = Duration::from_secs(20);
|
pub const SOCKET_TIMEOUT: Duration = Duration::from_secs(20);
|
||||||
pub const RECV_BUFSIZE: usize = 4096;
|
pub const RECV_BUFSIZE: usize = 4096;
|
||||||
@@ -231,9 +245,9 @@ impl DSP408 {
|
|||||||
let flags = self.get_device_flags()?;
|
let flags = self.get_device_flags()?;
|
||||||
let current_preset = self.get_current_preset()?;
|
let current_preset = self.get_current_preset()?;
|
||||||
let modified = self.get_preset_modification_status()?;
|
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(
|
preset_names.push(
|
||||||
self.get_preset_name(i + 1)?
|
self.get_preset_name(i + 1)?
|
||||||
);
|
);
|
||||||
@@ -805,6 +819,7 @@ impl DSP408 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Command `0x33` (Set PEQ Band)
|
// Command `0x33` (Set PEQ Band)
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn set_peq_band(
|
pub fn set_peq_band(
|
||||||
&mut self,
|
&mut self,
|
||||||
channel: types::Channel,
|
channel: types::Channel,
|
||||||
@@ -847,7 +862,7 @@ impl DSP408 {
|
|||||||
gain: gain_db,
|
gain: gain_db,
|
||||||
frequency: freq_hz,
|
frequency: freq_hz,
|
||||||
q,
|
q,
|
||||||
filter_type: filter_type,
|
filter_type,
|
||||||
bypass,
|
bypass,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1607,12 +1622,12 @@ impl DSP408 {
|
|||||||
fn read_config_state(&mut self) -> Result<types::DSPConfigState, DSPError> {
|
fn read_config_state(&mut self) -> Result<types::DSPConfigState, DSPError> {
|
||||||
let mut raw = Vec::new();
|
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)?;
|
let chunk = self.get_config_chunk(i)?;
|
||||||
raw.extend_from_slice(&chunk.data);
|
raw.extend_from_slice(&chunk.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = build_config_from_chunk(&raw)?;
|
let config = decode_config(&raw)?;
|
||||||
|
|
||||||
if !self.request_acknowledgement()? {
|
if !self.request_acknowledgement()? {
|
||||||
return Err(DSPError::OperationFailed(
|
return Err(DSPError::OperationFailed(
|
||||||
@@ -1641,375 +1656,3 @@ impl fmt::Debug for DSP408 {
|
|||||||
.finish()
|
.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 mod dsp408;
|
||||||
|
|
||||||
pub use dsp408::DSP408;
|
pub use dsp408::DSP408;
|
||||||
@@ -1,13 +1,31 @@
|
|||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::common::validator::{validate_ascii_padded, validate_range};
|
use crate::{
|
||||||
use crate::dsp408::client::constants::{self as client_constants};
|
common::{
|
||||||
use crate::dsp408::protocol::constants as protocol_constants;
|
errors::{
|
||||||
use crate::dsp408::protocol::validators;
|
DecodingError,
|
||||||
use crate::common::errors::{MapperError, DecodingError, ValidationError};
|
MapperError,
|
||||||
use super::{encode_ascii, decode_ascii};
|
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 ------
|
// ------ 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> {
|
pub fn channel_name_to_raw(name: &str) -> Result<Vec<u8>, MapperError> {
|
||||||
validate_ascii_padded(
|
validate_ascii_padded(
|
||||||
name.as_bytes(),
|
name.as_bytes(),
|
||||||
client_constants::CHANNEL_NAME_LENGTH,
|
protocol_constants::CHANNEL_NAME_LENGTH,
|
||||||
client_constants::CHANNEL_NAME_PADDING,
|
protocol_constants::CHANNEL_NAME_PADDING,
|
||||||
"Channel name",
|
"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> {
|
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> {
|
pub fn attack_ms_to_raw(value: u16) -> Result<u16, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
value,
|
value,
|
||||||
client_constants::ATTACK_MS_RANGE,
|
mapper_constants::ATTACK_MS_RANGE,
|
||||||
|| format!("Invalid attack: {:#06x}", value),
|
|| 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> {
|
pub fn release_ms_to_raw(value: u16) -> Result<u16, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
value,
|
value,
|
||||||
client_constants::RELEASE_MS_RANGE,
|
mapper_constants::RELEASE_MS_RANGE,
|
||||||
|| format!("Invalid release: {:#06x}", value),
|
|| 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> {
|
pub fn hold_ms_to_raw(hold: u16) -> Result<u16, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
hold,
|
hold,
|
||||||
client_constants::HOLD_MS_RANGE,
|
mapper_constants::HOLD_MS_RANGE,
|
||||||
|| format!("Invalid hold: {:#06x}", hold),
|
|| 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> {
|
pub fn gate_threshold_db_to_raw(db: f32) -> Result<u16, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
db,
|
db,
|
||||||
client_constants::GATE_THRESHOLD_DB_RANGE,
|
mapper_constants::GATE_THRESHOLD_DB_RANGE,
|
||||||
|| format!("Invalid gate threshold: {db}"),
|
|| format!("Invalid gate threshold: {db}"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let db = (db * 2.0).round() / 2.0;
|
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> {
|
pub fn raw_to_gate_threshold_db(value: u16) -> Result<f32, MapperError> {
|
||||||
validators::validate_raw_gate_threshold(value)?;
|
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 ------
|
// ------ EQ GAIN ------
|
||||||
@@ -172,8 +190,8 @@ fn eq_gain_raw_steps() -> f32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn eq_gain_step_db() -> f32 {
|
fn eq_gain_step_db() -> f32 {
|
||||||
(*client_constants::EQ_GAIN_DB_RANGE.end()
|
(*mapper_constants::EQ_GAIN_DB_RANGE.end()
|
||||||
- *client_constants::EQ_GAIN_DB_RANGE.start())
|
- *mapper_constants::EQ_GAIN_DB_RANGE.start())
|
||||||
/ eq_gain_raw_steps()
|
/ 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> {
|
pub fn eq_gain_db_to_raw(gain: f32) -> Result<u16, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
gain,
|
gain,
|
||||||
client_constants::EQ_GAIN_DB_RANGE,
|
mapper_constants::EQ_GAIN_DB_RANGE,
|
||||||
|| format!("Invalid EQ gain: {gain}"),
|
|| format!("Invalid EQ gain: {gain}"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -192,7 +210,7 @@ pub fn eq_gain_db_to_raw(gain: f32) -> Result<u16, MapperError> {
|
|||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
*protocol_constants::EQ_GAIN_RAW_RANGE.start()
|
*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())
|
/ eq_gain_step_db())
|
||||||
.round() as u16
|
.round() as u16
|
||||||
)
|
)
|
||||||
@@ -202,7 +220,7 @@ pub fn raw_to_eq_gain(value: u16) -> Result<f32, MapperError> {
|
|||||||
validators::validate_raw_eq_gain(value)?;
|
validators::validate_raw_eq_gain(value)?;
|
||||||
|
|
||||||
let gain =
|
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
|
+ (value - *protocol_constants::EQ_GAIN_RAW_RANGE.start()) as f32
|
||||||
* eq_gain_step_db();
|
* eq_gain_step_db();
|
||||||
|
|
||||||
@@ -246,30 +264,30 @@ pub fn peq_band_to_raw(band: types::PEQBand, channel: types::Channel) -> Result<
|
|||||||
// ------ GAIN ------
|
// ------ GAIN ------
|
||||||
|
|
||||||
fn gain_split_raw() -> u16 {
|
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 {
|
fn quantize_gain(db: f32) -> f32 {
|
||||||
if db < client_constants::GAIN_SPLIT_DB {
|
if db < mapper_constants::GAIN_SPLIT_DB {
|
||||||
(db / client_constants::GAIN_LOW_STEP_DB).round() * client_constants::GAIN_LOW_STEP_DB
|
(db / mapper_constants::GAIN_LOW_STEP_DB).round() * mapper_constants::GAIN_LOW_STEP_DB
|
||||||
} else {
|
} 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> {
|
pub fn gain_db_to_raw(gain: f32) -> Result<u16, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
gain,
|
gain,
|
||||||
client_constants::GAIN_DB_RANGE,
|
mapper_constants::GAIN_DB_RANGE,
|
||||||
|| format!("Invalid channel gain: {gain}"),
|
|| format!("Invalid channel gain: {gain}"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let gain = quantize_gain(gain);
|
let gain = quantize_gain(gain);
|
||||||
|
|
||||||
if gain < client_constants::GAIN_SPLIT_DB {
|
if gain < mapper_constants::GAIN_SPLIT_DB {
|
||||||
Ok(((gain - client_constants::GAIN_DB_RANGE.start()) / client_constants::GAIN_LOW_STEP_DB).round() as u16)
|
Ok(((gain - mapper_constants::GAIN_DB_RANGE.start()) / mapper_constants::GAIN_LOW_STEP_DB).round() as u16)
|
||||||
} else {
|
} 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)?;
|
validators::validate_raw_gain(raw_gain)?;
|
||||||
|
|
||||||
if raw_gain < gain_split_raw() {
|
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 {
|
} 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(
|
validate_range(
|
||||||
hz,
|
hz,
|
||||||
client_constants::FREQUENCY_HZ_RANGE,
|
mapper_constants::FREQUENCY_HZ_RANGE,
|
||||||
|| format!("Invalid frequency: {hz}"),
|
|| format!("Invalid frequency: {hz}"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let device_steps = (protocol_constants::FREQUENCY_RAW_RANGE.end() - protocol_constants::FREQUENCY_RAW_RANGE.start()) as f32;
|
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)
|
* device_steps)
|
||||||
.round();
|
.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 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;
|
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 ------
|
// ------ 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> {
|
pub fn peq_q_to_raw(q: f32, filter: types::PEQFilter) -> Result<u8, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
q,
|
q,
|
||||||
client_constants::peq_q_range(filter),
|
mapper_constants::peq_q_range(filter),
|
||||||
|| format!("Invalid PEQ Q: {q}"),
|
|| format!("Invalid PEQ Q: {q}"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let raw_range = (protocol_constants::PEQ_Q_RAW_RANGE.end() - protocol_constants::PEQ_Q_RAW_RANGE.start()) as f32;
|
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())
|
+ 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 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;
|
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 ------
|
// ------ 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> {
|
pub fn knee_db_to_raw(db: u16) -> Result<u16, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
db,
|
db,
|
||||||
client_constants::KNEE_DB_RANGE,
|
mapper_constants::KNEE_DB_RANGE,
|
||||||
|| format!("Invalid compressor knee: {db}"),
|
|| 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> {
|
pub fn threshold_db_to_raw(db: f32) -> Result<u16, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
db,
|
db,
|
||||||
client_constants::THRESHOLD_DB_RANGE,
|
mapper_constants::THRESHOLD_DB_RANGE,
|
||||||
|| format!("Invalid compressor threshold: {db}"),
|
|| format!("Invalid compressor threshold: {db}"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let db = (db * 2.0).round() / 2.0;
|
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> {
|
pub fn raw_to_threshold_db(value: u16) -> Result<f32, MapperError> {
|
||||||
validators::validate_raw_threshold(value)?;
|
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 ------
|
// ------ LINK ------
|
||||||
@@ -739,31 +757,31 @@ pub fn raw_to_link_channels(source_raw: u8, destination_mask: u8) -> Result<Vec<
|
|||||||
// ------ MATRIX GAIN ------
|
// ------ MATRIX GAIN ------
|
||||||
|
|
||||||
fn matrix_gain_split_raw() -> u16 {
|
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 {
|
fn quantize_matrix_gain(db: f32) -> f32 {
|
||||||
if db < client_constants::GAIN_SPLIT_DB {
|
if db < mapper_constants::GAIN_SPLIT_DB {
|
||||||
(db / client_constants::GAIN_LOW_STEP_DB).round() * client_constants::GAIN_LOW_STEP_DB
|
(db / mapper_constants::GAIN_LOW_STEP_DB).round() * mapper_constants::GAIN_LOW_STEP_DB
|
||||||
} else {
|
} 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> {
|
pub fn matrix_gain_db_to_raw(gain: f32) -> Result<u16, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
gain,
|
gain,
|
||||||
client_constants::MATRIX_GAIN_DB_RANGE,
|
mapper_constants::MATRIX_GAIN_DB_RANGE,
|
||||||
|| format!("Invalid matrix gain: {gain}"),
|
|| format!("Invalid matrix gain: {gain}"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let gain = quantize_matrix_gain(gain);
|
let gain = quantize_matrix_gain(gain);
|
||||||
|
|
||||||
if gain < client_constants::GAIN_SPLIT_DB {
|
if gain < mapper_constants::GAIN_SPLIT_DB {
|
||||||
Ok(((gain - client_constants::MATRIX_GAIN_DB_RANGE.start()) / client_constants::GAIN_LOW_STEP_DB).round() as u16)
|
Ok(((gain - mapper_constants::MATRIX_GAIN_DB_RANGE.start()) / mapper_constants::GAIN_LOW_STEP_DB).round() as u16)
|
||||||
} else {
|
} else {
|
||||||
Ok(matrix_gain_split_raw()
|
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)?;
|
validators::validate_raw_matrix_gain(raw_gain)?;
|
||||||
|
|
||||||
if raw_gain < matrix_gain_split_raw() {
|
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 {
|
} 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> {
|
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(
|
validate_range(
|
||||||
delay,
|
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> {
|
pub fn raw_to_delay(value: u8, unit: types::DelayUnit) -> Result<f32, MapperError> {
|
||||||
validators::validate_raw_channel_delay(value)?;
|
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))
|
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() {
|
fn channel_name_round_trip() {
|
||||||
let name = "In ABCDE";
|
let name = "In ABCDE";
|
||||||
let raw = channel_name_to_raw(name).unwrap();
|
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();
|
let decoded = raw_to_channel_name(&raw).unwrap();
|
||||||
assert_eq!(decoded, name);
|
assert_eq!(decoded, name);
|
||||||
}
|
}
|
||||||
@@ -923,7 +941,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn channel_name_padding_is_applied() {
|
fn channel_name_padding_is_applied() {
|
||||||
let raw = channel_name_to_raw(" Kick ").unwrap();
|
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 std::ops::RangeInclusive;
|
||||||
|
|
||||||
use super::types::{DelayUnit, PEQFilter};
|
use crate::dsp408::types::{DelayUnit, PEQFilter};
|
||||||
|
|
||||||
pub const PRESET_INDEX_MAX: usize = 20;
|
pub const PRESET_INDEX_MAX: usize = 20;
|
||||||
|
|
||||||
pub const CONFIG_CHUNK_INDEX_MAX: usize = 28;
|
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 ATTACK_MS_RANGE: RangeInclusive<u16> = 1..=999;
|
||||||
pub const RELEASE_MS_RANGE: RangeInclusive<u16> = 10..=3000;
|
pub const RELEASE_MS_RANGE: RangeInclusive<u16> = 10..=3000;
|
||||||
pub const KNEE_DB_RANGE: RangeInclusive<u16> = 0..=12;
|
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 EQ_GAIN_DB_RANGE: RangeInclusive<f32> = -12.0..=12.0;
|
||||||
pub const PEQ_Q_RANGE: RangeInclusive<f32> = 0.40..=128.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> {
|
pub fn peq_q_range(filter: PEQFilter) -> RangeInclusive<f32> {
|
||||||
match filter {
|
match filter {
|
||||||
PEQFilter::Peak
|
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 GATE_THRESHOLD_DB_RANGE: RangeInclusive<f32> = -90.0..=0.0;
|
||||||
pub const HOLD_MS_RANGE: RangeInclusive<u16> = 10..=999;
|
pub const HOLD_MS_RANGE: RangeInclusive<u16> = 10..=999;
|
||||||
|
|
||||||
pub const MATRIX_GAIN_DB_RANGE: RangeInclusive<f32> = -60.0..=0.0;
|
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 ------
|
// ------ STRING ------
|
||||||
|
|
||||||
|
|||||||
+39
-24
@@ -1,10 +1,25 @@
|
|||||||
use crate::common::validator::{validate_ascii_padded, validate_range};
|
use crate::{
|
||||||
use crate::dsp408::client::constants as client_constants;
|
common::{
|
||||||
use crate::dsp408::client::types;
|
errors::{DecodingError, MapperError},
|
||||||
use crate::dsp408::protocol::validators;
|
validator::{
|
||||||
use crate::common::errors::{MapperError, DecodingError};
|
validate_ascii_padded,
|
||||||
use crate::dsp408::protocol::constants as protocol_constants;
|
validate_range,
|
||||||
use super::{encode_ascii, decode_ascii};
|
},
|
||||||
|
},
|
||||||
|
dsp408::{
|
||||||
|
protocol::{
|
||||||
|
constants as protocol_constants,
|
||||||
|
validators,
|
||||||
|
},
|
||||||
|
types,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
constants,
|
||||||
|
decode_ascii,
|
||||||
|
encode_ascii,
|
||||||
|
};
|
||||||
|
|
||||||
// ------ DELAY ------
|
// ------ DELAY ------
|
||||||
|
|
||||||
@@ -51,7 +66,7 @@ pub fn raw_to_delay_unit(
|
|||||||
pub fn preset_index_to_raw(index: usize) -> Result<u8, MapperError> {
|
pub fn preset_index_to_raw(index: usize) -> Result<u8, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
index,
|
index,
|
||||||
0..=client_constants::PRESET_INDEX_MAX,
|
0..=constants::PRESET_INDEX_MAX,
|
||||||
|| format!("Invalid preset index {}", index)
|
|| format!("Invalid preset index {}", index)
|
||||||
)?;
|
)?;
|
||||||
Ok(index as u8)
|
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> {
|
pub fn preset_index_without_factory_to_raw(index: usize) -> Result<u8, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
index,
|
index,
|
||||||
1..=client_constants::PRESET_INDEX_MAX,
|
1..=constants::PRESET_INDEX_MAX,
|
||||||
|| format!("Invalid preset index {}", index)
|
|| format!("Invalid preset index {}", index)
|
||||||
)?;
|
)?;
|
||||||
Ok(index as u8 - 1)
|
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> {
|
pub fn config_chunk_to_raw(index: usize) -> Result<u8, MapperError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
index,
|
index,
|
||||||
0..=client_constants::CONFIG_CHUNK_INDEX_MAX,
|
0..=constants::CONFIG_CHUNK_INDEX_MAX,
|
||||||
|| format!("Invalid config index: {:#04x}", index)
|
|| format!("Invalid config index: {:#04x}", index)
|
||||||
)?;
|
)?;
|
||||||
Ok(index as u8)
|
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> {
|
pub fn preset_name_to_raw(name: &str) -> Result<Vec<u8>, MapperError> {
|
||||||
validate_ascii_padded(
|
validate_ascii_padded(
|
||||||
name.as_bytes(),
|
name.as_bytes(),
|
||||||
client_constants::PRESET_NAME_LENGTH,
|
protocol_constants::PRESET_NAME_LENGTH,
|
||||||
client_constants::PRESET_NAME_PADDING,
|
protocol_constants::PRESET_NAME_PADDING,
|
||||||
"Preset name"
|
"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> {
|
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> {
|
pub fn password_to_raw(password: &str) -> Result<Vec<u8>, MapperError> {
|
||||||
validate_ascii_padded(
|
validate_ascii_padded(
|
||||||
password.as_bytes(),
|
password.as_bytes(),
|
||||||
client_constants::PASSWORD_LENGTH,
|
protocol_constants::PASSWORD_LENGTH,
|
||||||
client_constants::PASSWORD_PADDING,
|
protocol_constants::PASSWORD_PADDING,
|
||||||
"Password"
|
"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)]
|
#[cfg(test)]
|
||||||
@@ -176,8 +191,8 @@ mod tests {
|
|||||||
fn preset_index_to_raw_bounds() {
|
fn preset_index_to_raw_bounds() {
|
||||||
assert_eq!(preset_index_to_raw(0).unwrap(), 0);
|
assert_eq!(preset_index_to_raw(0).unwrap(), 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
preset_index_to_raw(client_constants::PRESET_INDEX_MAX).unwrap(),
|
preset_index_to_raw(constants::PRESET_INDEX_MAX).unwrap(),
|
||||||
client_constants::PRESET_INDEX_MAX as u8,
|
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(1).unwrap(), 0);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
preset_index_without_factory_to_raw(client_constants::PRESET_INDEX_MAX).unwrap(),
|
preset_index_without_factory_to_raw(constants::PRESET_INDEX_MAX).unwrap(),
|
||||||
client_constants::PRESET_INDEX_MAX as u8 - 1,
|
constants::PRESET_INDEX_MAX as u8 - 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn preset_index_rejects_out_of_range() {
|
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());
|
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(0).unwrap(), 0);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config_chunk_to_raw(client_constants::CONFIG_CHUNK_INDEX_MAX).unwrap(),
|
config_chunk_to_raw(constants::CONFIG_CHUNK_INDEX_MAX).unwrap(),
|
||||||
client_constants::CONFIG_CHUNK_INDEX_MAX as u8,
|
constants::CONFIG_CHUNK_INDEX_MAX as u8,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn config_chunk_rejects_invalid() {
|
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 --------
|
// -------- Preset Name --------
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ pub mod channel;
|
|||||||
pub mod encoding;
|
pub mod encoding;
|
||||||
pub mod global;
|
pub mod global;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
|
pub mod constants;
|
||||||
|
|
||||||
|
|
||||||
pub use channel::*;
|
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::{
|
||||||
use crate::dsp408::client::types::{
|
common::errors::ProtocolError,
|
||||||
Acknowledgement, AuthenticationResult, ConfigChunk, CurrentPreset, DeviceFlags, DeviceInfo,
|
dsp408::{
|
||||||
DeviceResponseType, HandshakeAck, Meters, ModifiedPreset, NotImplemented, PresetName,
|
protocol::{
|
||||||
UnknownResponse,
|
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};
|
use super::encoding::{decode_ascii, decode_float16};
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -1,5 +1,6 @@
|
|||||||
pub mod client;
|
mod client;
|
||||||
pub mod mapper;
|
mod mapper;
|
||||||
pub mod protocol;
|
pub(crate) mod protocol;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
pub use client::DSP408;
|
pub use client::DSP408;
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
use super::frame::{build_frame};
|
|
||||||
|
|
||||||
use crate::common::errors::ValidationError;
|
use crate::common::errors::ValidationError;
|
||||||
use super::validators::*;
|
|
||||||
|
use super::{
|
||||||
|
frame::build_frame,
|
||||||
|
validators::*,
|
||||||
|
};
|
||||||
|
|
||||||
fn split_u16(value: u16) -> [u8; 2] {
|
fn split_u16(value: u16) -> [u8; 2] {
|
||||||
[
|
[
|
||||||
@@ -150,7 +152,7 @@ pub fn build_unlock_device(
|
|||||||
|
|
||||||
Ok(build_frame(
|
Ok(build_frame(
|
||||||
0x2d,
|
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,
|
_ => 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 {
|
if data.len() < 2 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
for i in 2..data.len() - 1 {
|
|
||||||
if data[i] == 0x10 && data[i + 1] == 0x03 {
|
(2..data.len() - 1)
|
||||||
return Some(i);
|
.find(|&i| data[i] == 0x10 && data[i + 1] == 0x03)
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
pub mod frame;
|
pub(crate) mod frame;
|
||||||
pub mod commands;
|
pub(crate) mod commands;
|
||||||
pub mod constants;
|
pub(crate) mod constants;
|
||||||
pub mod validators;
|
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::{
|
||||||
use crate::common::validator::{validate_ascii_padded, validate_bitmask, validate_range};
|
common::{
|
||||||
use super::constants::*;
|
errors::ValidationError,
|
||||||
|
validator::{
|
||||||
|
validate_ascii_padded,
|
||||||
|
validate_bitmask,
|
||||||
|
validate_range,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dsp408::protocol::constants::*,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn validate_raw_delay_unit(raw_unit: u8) -> Result<(), ValidationError> {
|
pub fn validate_raw_delay_unit(raw_unit: u8) -> Result<(), ValidationError> {
|
||||||
validate_range(
|
validate_range(
|
||||||
|
|||||||
+2
-6
@@ -1,8 +1,4 @@
|
|||||||
mod common;
|
mod common;
|
||||||
mod dsp408;
|
pub mod dsp408;
|
||||||
|
|
||||||
pub use common::errors::DSPError;
|
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::net::IpAddr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use dsp_thomann::DSP408;
|
use dsp_thomann::dsp408::DSP408;
|
||||||
use dsp_thomann::types;
|
use dsp_thomann::dsp408::types;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user