feat: add gitea related files
CI / Rust CI (push) Successful in 3m38s

This commit is contained in:
2026-07-21 00:24:40 +02:00
parent 6ffe5589e6
commit a3c17186be
27 changed files with 3015 additions and 1746 deletions
+120 -349
View File
@@ -14,14 +14,10 @@ use crate::{
dsp408::{
mapper::{
self,
constants::{PRESET_INDEX_MAX, CONFIG_CHUNK_INDEX_MAX},
constants::{CONFIG_CHUNK_INDEX_MAX, PRESET_INDEX_MAX},
response::map_response,
},
protocol::{
commands,
config::decode_config,
frame::extract_frame,
},
protocol::{commands, config::decode_config, frame::extract_frame},
types,
},
};
@@ -44,33 +40,21 @@ pub struct DSP408 {
}
impl DSP408 {
pub fn new(
host: IpAddr,
port: u16,
device_id: u8,
) -> Result<Self, DSPError> {
pub fn new(host: IpAddr, port: u16, device_id: u8) -> Result<Self, DSPError> {
if matches!(host, IpAddr::V6(_)) {
return Err(DSPError::InvalidHost(
"DSP doesn't support IPv6"
));
return Err(DSPError::InvalidHost("DSP doesn't support IPv6"));
}
if port == 0 {
return Err(DSPError::InvalidPort(
"Port must be between 1 and 65535"
));
return Err(DSPError::InvalidPort("Port must be between 1 and 65535"));
}
if device_id == 0 || device_id > 254 {
return Err(DSPError::InvalidDeviceId(
"Device ID must be between 1 and 254"
"Device ID must be between 1 and 254",
));
}
Ok(Self {
connection: types::DeviceConnection {
host,
@@ -88,83 +72,43 @@ impl DSP408 {
})
}
fn send_raw(
&mut self,
data: &[u8],
) -> Result<(), DSPError> {
let _guard =
self.lock.lock().unwrap();
let socket =
self.socket
.as_mut()
.ok_or(
DSPError::NotConnected
)?;
fn send_raw(&mut self, data: &[u8]) -> Result<(), DSPError> {
let _guard = self.lock.lock().unwrap();
let socket = self.socket.as_mut().ok_or(DSPError::NotConnected)?;
socket.write_all(data)?;
Ok(())
}
fn recv_raw(
&mut self
) -> Result<Vec<u8>, DSPError> {
let socket =
self.socket
.as_mut()
.ok_or(
DSPError::NotConnected
)?;
let mut buffer =
vec![0u8; RECV_BUFSIZE];
fn recv_raw(&mut self) -> Result<Vec<u8>, DSPError> {
let socket = self.socket.as_mut().ok_or(DSPError::NotConnected)?;
let mut buffer = vec![0u8; RECV_BUFSIZE];
match socket.read(&mut buffer) {
Ok(0) =>
Err(DSPError::ConnectionClosed),
Ok(0) => Err(DSPError::ConnectionClosed),
Ok(size) => {
buffer.truncate(size);
Ok(buffer)
},
}
Err(e)
if e.kind()
==
std::io::ErrorKind::TimedOut =>
{
Err(DSPError::Timeout)
},
Err(e) if e.kind() == std::io::ErrorKind::TimedOut => Err(DSPError::Timeout),
Err(e) =>
Err(DSPError::Io(e)),
Err(e) => Err(DSPError::Io(e)),
}
}
pub fn send_recv(
&mut self,
command: &[u8],
) -> Result<types::DeviceResponseType, DSPError> {
pub fn send_recv(&mut self, command: &[u8]) -> Result<types::DeviceResponseType, DSPError> {
self.send_raw(command)?;
let raw =
self.recv_raw()?;
let raw = self.recv_raw()?;
let frame =
extract_frame(&raw)?;
let frame = extract_frame(&raw)?;
let response =
map_response(&frame)?;
let response = map_response(&frame)?;
Ok(response)
}
@@ -174,46 +118,29 @@ impl DSP408 {
}
pub fn state(&self) -> Result<&types::DSPState, DSPError> {
self.state
.as_ref()
.ok_or(DSPError::NotConnected)
self.state.as_ref().ok_or(DSPError::NotConnected)
}
fn state_mut(&mut self) -> Result<&mut types::DSPState, DSPError> {
self.state
.as_mut()
.ok_or(DSPError::NotConnected)
self.state.as_mut().ok_or(DSPError::NotConnected)
}
// ---------- Commands ----------
// Command `0x10`
pub fn connect(&mut self) -> Result<(), DSPError> {
let key = (
self.connection.host,
self.connection.device_id
);
let key = (self.connection.host, self.connection.device_id);
{
let map = instances()
.lock()
.unwrap();
let map = instances().lock().unwrap();
if map.contains_key(&key) {
return Err(DSPError::AlreadyConnected);
}
}
let addr = SocketAddr::new(
self.connection.host,
self.connection.port
);
let addr = SocketAddr::new(self.connection.host, self.connection.port);
let stream =
TcpStream::connect_timeout(
&addr,
SOCKET_TIMEOUT
)?;
let stream = TcpStream::connect_timeout(&addr, SOCKET_TIMEOUT)?;
stream.set_read_timeout(Some(SOCKET_TIMEOUT))?;
@@ -226,18 +153,12 @@ impl DSP408 {
let start = std::time::Instant::now();
while start.elapsed() < SOCKET_TIMEOUT {
let raw = self.recv_raw()?;
let frame = extract_frame(&raw)?;
let response = map_response(&frame)?;
if matches!(response, types::DeviceResponseType::HandshakeAck(_)) {
instances()
.lock()
.unwrap()
.insert(key, ());
instances().lock().unwrap().insert(key, ());
self.connected = true;
@@ -248,9 +169,7 @@ impl DSP408 {
let mut preset_names = Vec::with_capacity(PRESET_INDEX_MAX);
for i in 0..PRESET_INDEX_MAX {
preset_names.push(
self.get_preset_name(i + 1)?
);
preset_names.push(self.get_preset_name(i + 1)?);
}
let state = self.read_config_state()?;
@@ -277,31 +196,15 @@ impl DSP408 {
// Command `0x11`
pub fn disconnect(&mut self) {
let key = (
self.connection.host,
self.connection.device_id
);
let key = (self.connection.host, self.connection.device_id);
if let Some(mut socket) = self.socket.take() {
let _ = socket.write_all(&commands::build_disconnect());
let _ =
socket.write_all(
&commands::build_disconnect()
);
let _ =
socket.shutdown(
std::net::Shutdown::Both
);
let _ = socket.shutdown(std::net::Shutdown::Both);
}
instances()
.lock()
.unwrap()
.remove(&key);
instances().lock().unwrap().remove(&key);
self.connected = false;
}
@@ -309,9 +212,9 @@ impl DSP408 {
// Command `0x12`
fn request_acknowledgement(&mut self) -> Result<bool, DSPError> {
let request = commands::build_acknowledgement();
let resp = self.send_recv(&request)?;
match resp {
types::DeviceResponseType::Acknowledgement(ack) => Ok(ack.success),
other => Err(DSPError::UnexpectedResponse(format!(
@@ -428,7 +331,7 @@ impl DSP408 {
if !self.request_acknowledgement()? {
return Err(DSPError::OperationFailed(
"Store Configuration fails".into()
"Store Configuration fails".into(),
));
}
@@ -470,9 +373,7 @@ impl DSP408 {
let resp = self.send_recv(&request)?;
match resp {
types::DeviceResponseType::Acknowledgement(ack) => {
Ok(ack.success)
}
types::DeviceResponseType::Acknowledgement(ack) => Ok(ack.success),
other => Err(DSPError::UnexpectedResponse(format!(
"Expected Acknowledgement, got {:?}",
other
@@ -516,9 +417,7 @@ impl DSP408 {
let resp = self.send_recv(&request)?;
match resp {
types::DeviceResponseType::ConfigChunk(config) => {
Ok(config)
}
types::DeviceResponseType::ConfigChunk(config) => Ok(config),
other => Err(DSPError::UnexpectedResponse(format!(
"Expected ConfigChunk, got {:?}",
other
@@ -549,20 +448,14 @@ impl DSP408 {
channel_1: types::Channel,
channel_2: types::Channel,
) -> Result<bool, DSPError> {
let (raw_channel_1, raw_channel_2) = mapper::copy_channel_to_raw(channel_1, channel_2)?;
let request = commands::build_copy_channel(
raw_channel_1,
raw_channel_2,
)?;
let request = commands::build_copy_channel(raw_channel_1, raw_channel_2)?;
let resp = self.send_recv(&request)?;
match resp {
types::DeviceResponseType::Acknowledgement(ack) => {
Ok(ack.success)
}
types::DeviceResponseType::Acknowledgement(ack) => Ok(ack.success),
other => Err(DSPError::UnexpectedResponse(format!(
"Expected Acknowledgement, got {:?}",
other
@@ -647,7 +540,6 @@ impl DSP408 {
knee: u16,
threshold: f32,
) -> Result<bool, DSPError> {
let raw_channel = mapper::output_channel_to_raw(channel)?;
let raw_ratio = mapper::ratio_to_raw(ratio)?;
let raw_attack = mapper::attack_ms_to_raw(attack)?;
@@ -674,16 +566,13 @@ impl DSP408 {
let state = self.state_mut()?;
state
.current_config
.output_state_mut(channel)
.compressor = types::Compressor {
threshold,
ratio,
attack,
release,
knee,
};
state.current_config.output_state_mut(channel).compressor = types::Compressor {
threshold,
ratio,
attack,
release,
knee,
};
Ok(true)
}
@@ -701,16 +590,11 @@ impl DSP408 {
freq: f32,
filter_type: types::CrossoverFilter,
) -> Result<bool, DSPError> {
let raw_channel = mapper::channel_to_raw(channel)?;
let raw_freq = mapper::frequency_hz_to_raw(freq)?;
let raw_filter = mapper::crossover_filter_to_raw(filter_type)?;
let request = commands::build_low_pass(
raw_channel,
raw_freq,
raw_filter,
)?;
let request = commands::build_low_pass(raw_channel, raw_freq, raw_filter)?;
let resp = self.send_recv(&request)?;
@@ -729,9 +613,9 @@ impl DSP408 {
.input_state_mut(input)
.crossover
.low_pass = types::Crossover {
frequency: freq,
slope: filter_type,
};
frequency: freq,
slope: filter_type,
};
}
types::Channel::Output(output) => {
@@ -740,9 +624,9 @@ impl DSP408 {
.output_state_mut(output)
.crossover
.low_pass = types::Crossover {
frequency: freq,
slope: filter_type,
};
frequency: freq,
slope: filter_type,
};
}
}
@@ -763,16 +647,11 @@ impl DSP408 {
freq: f32,
filter_type: types::CrossoverFilter,
) -> Result<bool, DSPError> {
let raw_channel = mapper::channel_to_raw(channel)?;
let raw_freq = mapper::frequency_hz_to_raw(freq)?;
let raw_filter = mapper::crossover_filter_to_raw(filter_type)?;
let request = commands::build_high_pass(
raw_channel,
raw_freq,
raw_filter,
)?;
let request = commands::build_high_pass(raw_channel, raw_freq, raw_filter)?;
let resp = self.send_recv(&request)?;
@@ -791,9 +670,9 @@ impl DSP408 {
.input_state_mut(input)
.crossover
.high_pass = types::Crossover {
frequency: freq,
slope: filter_type,
};
frequency: freq,
slope: filter_type,
};
}
types::Channel::Output(output) => {
@@ -802,9 +681,9 @@ impl DSP408 {
.output_state_mut(output)
.crossover
.high_pass = types::Crossover {
frequency: freq,
slope: filter_type,
};
frequency: freq,
slope: filter_type,
};
}
}
@@ -830,7 +709,6 @@ impl DSP408 {
filter_type: types::PEQFilter,
bypass: bool,
) -> Result<bool, DSPError> {
let raw_channel = mapper::channel_to_raw(channel)?;
let raw_band = mapper::peq_band_to_raw(band, channel)?;
let raw_gain = mapper::eq_gain_db_to_raw(gain_db)?;
@@ -868,11 +746,8 @@ impl DSP408 {
match channel {
types::Channel::Input(input) => {
state
.current_config
.input_state_mut(input)
.peq_chain
.bands[band as usize] = peq;
state.current_config.input_state_mut(input).peq_chain.bands
[band as usize] = peq;
}
types::Channel::Output(output) => {
@@ -895,19 +770,11 @@ impl DSP408 {
}
// Command `0x34` (Set Gain)
pub fn set_channel_gain(
&mut self,
channel: types::Channel,
db: f32,
) -> Result<bool, DSPError> {
pub fn set_channel_gain(&mut self, channel: types::Channel, db: f32) -> Result<bool, DSPError> {
let raw_channel = mapper::channel_to_raw(channel)?;
let raw_db = mapper::gain_db_to_raw(db)?;
let request = commands::build_gain(
raw_channel,
raw_db,
)?;
let request = commands::build_gain(raw_channel, raw_db)?;
let resp = self.send_recv(&request)?;
@@ -921,17 +788,11 @@ impl DSP408 {
match channel {
types::Channel::Input(input) => {
state
.current_config
.input_state_mut(input)
.gain = db;
state.current_config.input_state_mut(input).gain = db;
}
types::Channel::Output(output) => {
state
.current_config
.output_state_mut(output)
.gain = db;
state.current_config.output_state_mut(output).gain = db;
}
}
@@ -946,18 +807,10 @@ impl DSP408 {
}
// Command `0x35` (Set Mute)
pub fn set_mute(
&mut self,
channel: types::Channel,
muted: bool,
) -> Result<bool, DSPError> {
pub fn set_mute(&mut self, channel: types::Channel, muted: bool) -> Result<bool, DSPError> {
let raw_channel = mapper::channel_to_raw(channel)?;
let request = commands::build_mute(
raw_channel,
muted as u8,
)?;
let request = commands::build_mute(raw_channel, muted as u8)?;
let resp = self.send_recv(&request)?;
@@ -971,17 +824,11 @@ impl DSP408 {
match channel {
types::Channel::Input(input) => {
state
.current_config
.input_state_mut(input)
.mute = muted;
state.current_config.input_state_mut(input).mute = muted;
}
types::Channel::Output(output) => {
state
.current_config
.output_state_mut(output)
.mute = muted;
state.current_config.output_state_mut(output).mute = muted;
}
}
@@ -1001,13 +848,9 @@ impl DSP408 {
channel: types::Channel,
inverted: bool,
) -> Result<bool, DSPError> {
let raw_channel = mapper::channel_to_raw(channel)?;
let request = commands::build_inverted_gain(
raw_channel,
inverted as u8,
)?;
let request = commands::build_inverted_gain(raw_channel, inverted as u8)?;
let resp = self.send_recv(&request)?;
@@ -1021,17 +864,11 @@ impl DSP408 {
match channel {
types::Channel::Input(input) => {
state
.current_config
.input_state_mut(input)
.phase_inverted = inverted;
state.current_config.input_state_mut(input).phase_inverted = inverted;
}
types::Channel::Output(output) => {
state
.current_config
.output_state_mut(output)
.phase_inverted = inverted;
state.current_config.output_state_mut(output).phase_inverted = inverted;
}
}
@@ -1052,14 +889,10 @@ impl DSP408 {
delay: f32,
unit: types::DelayUnit,
) -> Result<bool, DSPError> {
let raw_channel = mapper::channel_to_raw(channel)?;
let raw_delay = mapper::delay_to_raw(delay, unit)?;
let request = commands::build_set_delay(
raw_channel,
raw_delay,
)?;
let request = commands::build_set_delay(raw_channel, raw_delay)?;
let resp = self.send_recv(&request)?;
@@ -1075,17 +908,11 @@ impl DSP408 {
match channel {
types::Channel::Input(input) => {
state
.current_config
.input_state_mut(input)
.delay = delay;
state.current_config.input_state_mut(input).delay = delay;
}
types::Channel::Output(output) => {
state
.current_config
.output_state_mut(output)
.delay = delay;
state.current_config.output_state_mut(output).delay = delay;
}
}
@@ -1100,18 +927,11 @@ impl DSP408 {
}
// Command `0x39` (Set Input Source)
pub fn set_input_source(
&mut self,
input_source: types::InputSource,
) -> Result<bool, DSPError> {
pub fn set_input_source(&mut self, input_source: types::InputSource) -> Result<bool, DSPError> {
let raw_source = mapper::input_source_to_raw(input_source.r#type)?;
let raw_frequency = mapper::frequency_value_to_raw(input_source.frequency)?;
let request = commands::build_set_input_source(
raw_source,
raw_frequency,
)?;
let request = commands::build_set_input_source(raw_source, raw_frequency)?;
let resp = self.send_recv(&request)?;
@@ -1140,14 +960,10 @@ impl DSP408 {
output: types::OutputChannel,
inputs: Vec<types::InputChannel>,
) -> Result<bool, DSPError> {
let raw_output = mapper::output_channel_to_raw(output)?;
let raw_inputs = mapper::matrix_to_raw(&inputs)?;
let request = commands::build_matrix_routing(
raw_output,
raw_inputs,
)?;
let request = commands::build_matrix_routing(raw_output, raw_inputs)?;
let resp = self.send_recv(&request)?;
@@ -1181,7 +997,6 @@ impl DSP408 {
source: types::Channel,
destinations: Vec<types::Channel>,
) -> Result<bool, DSPError> {
let raw_source = mapper::channel_to_raw(source)?;
let bitmask = mapper::link_channels_to_raw(source, &destinations)?;
@@ -1197,10 +1012,7 @@ impl DSP408 {
}
// Link source
let request = commands::build_link_channel(
raw_source,
bitmask,
)?;
let request = commands::build_link_channel(raw_source, bitmask)?;
let resp = self.send_recv(&request)?;
@@ -1219,7 +1031,6 @@ impl DSP408 {
}
}
// Clear destination links
for destination in &destinations {
if *destination == source {
@@ -1228,10 +1039,7 @@ impl DSP408 {
let raw_destination = mapper::channel_to_raw(*destination)?;
let request = commands::build_link_channel(
raw_destination,
0x00,
)?;
let request = commands::build_link_channel(raw_destination, 0x00)?;
let resp = self.send_recv(&request)?;
@@ -1268,13 +1076,9 @@ impl DSP408 {
channel: types::Channel,
bypass: bool,
) -> Result<bool, DSPError> {
let raw_channel = mapper::channel_to_raw(channel)?;
let request = commands::build_bypass_peq(
raw_channel,
bypass as u8,
)?;
let request = commands::build_bypass_peq(raw_channel, bypass as u8)?;
let resp = self.send_recv(&request)?;
@@ -1288,13 +1092,13 @@ impl DSP408 {
match channel {
types::Channel::Input(input) => {
state.current_config.input_state_mut(input)
.peq_chain
.bypass = bypass;
state.current_config.input_state_mut(input).peq_chain.bypass = bypass;
}
types::Channel::Output(output) => {
state.current_config.output_state_mut(output)
state
.current_config
.output_state_mut(output)
.peq_chain
.bypass = bypass;
}
@@ -1316,14 +1120,10 @@ impl DSP408 {
channel: types::Channel,
name: &str,
) -> Result<bool, DSPError> {
let raw_channel = mapper::channel_to_raw(channel)?;
let raw_name = mapper::channel_name_to_raw(name)?;
let request = commands::build_store_channel_name(
raw_channel,
&raw_name,
)?;
let request = commands::build_store_channel_name(raw_channel, &raw_name)?;
let resp = self.send_recv(&request)?;
@@ -1337,13 +1137,11 @@ impl DSP408 {
match channel {
types::Channel::Input(input) => {
state.current_config.input_state_mut(input)
.name = name.to_string();
state.current_config.input_state_mut(input).name = name.to_string();
}
types::Channel::Output(output) => {
state.current_config.output_state_mut(output)
.name = name.to_string();
state.current_config.output_state_mut(output).name = name.to_string();
}
}
@@ -1366,7 +1164,6 @@ impl DSP408 {
hold: u16,
release: u16,
) -> Result<bool, DSPError> {
let raw_channel = mapper::input_channel_to_raw(channel)?;
let raw_threshold = mapper::gate_threshold_db_to_raw(threshold)?;
let raw_attack = mapper::attack_ms_to_raw(attack)?;
@@ -1391,15 +1188,12 @@ impl DSP408 {
let state = self.state_mut()?;
state
.current_config
.input_state_mut(channel)
.gate = types::Gate {
attack,
release,
hold,
threshold,
};
state.current_config.input_state_mut(channel).gate = types::Gate {
attack,
release,
hold,
threshold,
};
Ok(true)
}
@@ -1419,18 +1213,13 @@ impl DSP408 {
attack: u16,
release: u16,
) -> Result<bool, DSPError> {
let raw_channel = mapper::output_channel_to_raw(channel)?;
let raw_threshold = mapper::threshold_db_to_raw(threshold)?;
let raw_attack = mapper::attack_ms_to_raw(attack)?;
let raw_release = mapper::release_ms_to_raw(release)?;
let request = commands::build_set_limiter(
raw_channel,
raw_threshold,
raw_attack,
raw_release,
)?;
let request =
commands::build_set_limiter(raw_channel, raw_threshold, raw_attack, raw_release)?;
let resp = self.send_recv(&request)?;
@@ -1442,14 +1231,11 @@ impl DSP408 {
let state = self.state_mut()?;
state
.current_config
.output_state_mut(channel)
.limiter = types::Limiter {
threshold,
attack,
release,
};
state.current_config.output_state_mut(channel).limiter = types::Limiter {
threshold,
attack,
release,
};
Ok(true)
}
@@ -1462,10 +1248,7 @@ impl DSP408 {
}
// Command `0x40` (Get Level Meters)
pub fn get_meter_levels(
&mut self,
) -> Result<types::Meters, DSPError> {
pub fn get_meter_levels(&mut self) -> Result<types::Meters, DSPError> {
let request = commands::build_get_meter_levels();
let resp = self.send_recv(&request)?;
@@ -1487,16 +1270,11 @@ impl DSP408 {
output_channel: types::OutputChannel,
gain: f32,
) -> Result<bool, DSPError> {
let raw_input_channel = mapper::input_channel_to_raw(input_channel)?;
let raw_output_channel = mapper::output_channel_to_raw(output_channel)?;
let raw_gain = mapper::matrix_gain_db_to_raw(gain)?;
let request = commands::build_matrix_gain(
raw_input_channel,
raw_output_channel,
raw_gain,
)?;
let request = commands::build_matrix_gain(raw_input_channel, raw_output_channel, raw_gain)?;
let resp = self.send_recv(&request)?;
@@ -1510,9 +1288,11 @@ impl DSP408 {
let index = types::InputChannel::iter()
.position(|x| x == input_channel)
.ok_or_else(|| DSPError::Validation(ValidationError::Value(
"Invalid input channel".to_string()
)))?;
.ok_or_else(|| {
DSPError::Validation(ValidationError::Value(
"Invalid input channel".to_string(),
))
})?;
state
.current_config
@@ -1537,16 +1317,12 @@ impl DSP408 {
frequency_band: types::DiscreteFrequency,
gain: f32,
) -> Result<bool, DSPError> {
let raw_input_channel = mapper::input_channel_to_raw(input_channel)?;
let raw_frequency_band = mapper::frequency_value_to_raw(frequency_band)?;
let raw_gain = mapper::eq_gain_db_to_raw(gain)?;
let request = commands::build_set_geq_band(
raw_input_channel,
raw_frequency_band,
raw_gain,
)?;
let request =
commands::build_set_geq_band(raw_input_channel, raw_frequency_band, raw_gain)?;
let resp = self.send_recv(&request)?;
@@ -1560,17 +1336,19 @@ impl DSP408 {
let index = types::DiscreteFrequency::iter()
.position(|x| x == frequency_band)
.ok_or_else(|| DSPError::Validation(ValidationError::Value(
"Invalid GEQ frequency band".to_string()
)))?;
.ok_or_else(|| {
DSPError::Validation(ValidationError::Value(
"Invalid GEQ frequency band".to_string(),
))
})?;
state
.current_config
.input_state_mut(input_channel)
.geq
.gains[index] = gain;
Ok(true)
}
Ok(true)
}
other => Err(DSPError::UnexpectedResponse(format!(
"Expected Acknowledgement, got {:?}",
@@ -1585,13 +1363,9 @@ impl DSP408 {
input_channel: types::InputChannel,
bypass: bool,
) -> Result<bool, DSPError> {
let raw_input_channel = mapper::input_channel_to_raw(input_channel)?;
let request = commands::build_set_geq_bypass(
raw_input_channel,
bypass as u8,
)?;
let request = commands::build_set_geq_bypass(raw_input_channel, bypass as u8)?;
let resp = self.send_recv(&request)?;
@@ -1630,14 +1404,11 @@ impl DSP408 {
let config = decode_config(&raw)?;
if !self.request_acknowledgement()? {
return Err(DSPError::OperationFailed(
"DSP initialization".into()
));
return Err(DSPError::OperationFailed("DSP initialization".into()));
}
Ok(config)
}
}
impl Drop for DSP408 {
+1 -1
View File
@@ -1,2 +1,2 @@
mod dsp408;
pub use dsp408::DSP408;
pub use dsp408::DSP408;
File diff suppressed because it is too large Load Diff
+5 -11
View File
@@ -10,26 +10,20 @@ pub const RELEASE_MS_RANGE: RangeInclusive<u16> = 10..=3000;
pub const KNEE_DB_RANGE: RangeInclusive<u16> = 0..=12;
pub const THRESHOLD_DB_RANGE: RangeInclusive<f32> = -90.0..=20.0;
pub const FREQUENCY_HZ_RANGE: RangeInclusive<f32> = 19.7..=20160.0;
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 fn peq_q_range(filter: PEQFilter) -> RangeInclusive<f32> {
match filter {
PEQFilter::Peak
| PEQFilter::AllPass1
| PEQFilter::AllPass2 => {
0.40..=128.0
}
PEQFilter::Peak | PEQFilter::AllPass1 | PEQFilter::AllPass2 => 0.40..=128.0,
PEQFilter::LowShelf
| PEQFilter::HighShelf
| PEQFilter::Lp6Db
| PEQFilter::Lp12Db
| PEQFilter::Hp6Db
| PEQFilter::Hp12Db => {
0.40..=3.0
}
| PEQFilter::Hp12Db => 0.40..=3.0,
}
}
@@ -41,8 +35,8 @@ pub const GAIN_HIGH_STEP_DB: f32 = 0.1;
pub fn delay_range(unit: DelayUnit) -> (f32, f32) {
match unit {
DelayUnit::Millisecond => (0.000, 680.000),
DelayUnit::Meter => (0.000, 233.580),
DelayUnit::Feet => (0.000, 766.329),
DelayUnit::Meter => (0.000, 233.580),
DelayUnit::Feet => (0.000, 766.329),
}
}
+17 -28
View File
@@ -1,17 +1,19 @@
use crate::common::errors::{
DecodingError,
EncodingError,
};
use crate::common::errors::{DecodingError, EncodingError};
// ------ STRING ------
pub fn encode_ascii(value: &str, length: usize, padding: u8) -> Result<Vec<u8>, EncodingError> {
if length == 0 {
return Err(EncodingError::new(format!("Invalid string length: {}", length)));
return Err(EncodingError::new(format!(
"Invalid string length: {}",
length
)));
}
if !value.is_ascii() {
return Err(EncodingError::new("String must contain ASCII characters only"));
return Err(EncodingError::new(
"String must contain ASCII characters only",
));
}
let raw = value.as_bytes();
@@ -38,7 +40,9 @@ pub fn decode_ascii(raw: &[u8], strip: bool) -> Result<String, DecodingError> {
let result = String::from_utf8(raw.to_vec()).expect("raw is ASCII, so always valid UTF-8");
Ok(if strip {
result.trim_end_matches(['\u{0020}', '\u{0000}']).to_string()
result
.trim_end_matches(['\u{0020}', '\u{0000}'])
.to_string()
} else {
result
})
@@ -65,15 +69,10 @@ pub fn decode_float16(lo: u8, hi: u8) -> Result<f32, DecodingError> {
return Ok(f32::NAN);
}
} else {
(1.0 + mantissa as f32 / 1024.0)
* 2f32.powi(exponent as i32 - 15)
(1.0 + mantissa as f32 / 1024.0) * 2f32.powi(exponent as i32 - 15)
};
if sign == 1 {
Ok(-result)
} else {
Ok(result)
}
if sign == 1 { Ok(-result) } else { Ok(result) }
}
#[cfg(test)]
@@ -92,10 +91,7 @@ mod tests {
fn test_encode_ascii_pads_short_string() {
let result = encode_ascii("hi", 5, b'\0');
assert_eq!(
result.unwrap(),
vec![b'h', b'i', 0, 0, 0]
);
assert_eq!(result.unwrap(), vec![b'h', b'i', 0, 0, 0]);
}
#[test]
@@ -103,10 +99,7 @@ mod tests {
let result = encode_ascii("hello", 0, b' ');
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Invalid string length: 0"
);
assert_eq!(result.unwrap_err().to_string(), "Invalid string length: 0");
}
#[test]
@@ -165,13 +158,9 @@ mod tests {
let result = decode_ascii(&raw, false);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Invalid ASCII data"
);
assert_eq!(result.unwrap_err().to_string(), "Invalid ASCII data");
}
// ------ FLOAT TESTS ------
#[test]
@@ -256,4 +245,4 @@ mod tests {
assert_eq!(result.unwrap(), 65504.0);
}
}
}
+49 -66
View File
@@ -1,25 +1,15 @@
use crate::{
common::{
errors::{DecodingError, MapperError},
validator::{
validate_ascii_padded,
validate_range,
},
validator::{validate_ascii_padded, validate_range},
},
dsp408::{
protocol::{
constants as protocol_constants,
validators,
},
protocol::{constants as protocol_constants, validators},
types,
},
};
use super::{
constants,
decode_ascii,
encode_ascii,
};
use super::{constants, decode_ascii, encode_ascii};
// ------ DELAY ------
@@ -40,55 +30,43 @@ fn raw_to_delay_unit_value(value: u8) -> Option<types::DelayUnit> {
}
}
pub fn delay_unit_to_raw(
unit: types::DelayUnit,
) -> Result<u8, MapperError> {
pub fn delay_unit_to_raw(unit: types::DelayUnit) -> Result<u8, MapperError> {
Ok(delay_unit_to_raw_value(unit))
}
pub fn raw_to_delay_unit(
value: u8,
) -> Result<types::DelayUnit, MapperError> {
pub fn raw_to_delay_unit(value: u8) -> Result<types::DelayUnit, MapperError> {
validators::validate_raw_delay_unit(value)?;
raw_to_delay_unit_value(value)
.ok_or_else(|| {
MapperError::Decoding(
DecodingError::new(
format!("Unknown delay unit value: {:#04x}", value),
),
)
})
raw_to_delay_unit_value(value).ok_or_else(|| {
MapperError::Decoding(DecodingError::new(format!(
"Unknown delay unit value: {:#04x}",
value
)))
})
}
// ------ PRESET INDEX ------
pub fn preset_index_to_raw(index: usize) -> Result<u8, MapperError> {
validate_range(
index,
0..=constants::PRESET_INDEX_MAX,
|| format!("Invalid preset index {}", index)
)?;
pub fn preset_index_to_raw(index: usize) -> Result<u8, MapperError> {
validate_range(index, 0..=constants::PRESET_INDEX_MAX, || {
format!("Invalid preset index {}", index)
})?;
Ok(index as u8)
}
pub fn preset_index_without_factory_to_raw(index: usize) -> Result<u8, MapperError> {
validate_range(
index,
1..=constants::PRESET_INDEX_MAX,
|| format!("Invalid preset index {}", index)
)?;
pub fn preset_index_without_factory_to_raw(index: usize) -> Result<u8, MapperError> {
validate_range(index, 1..=constants::PRESET_INDEX_MAX, || {
format!("Invalid preset index {}", index)
})?;
Ok(index as u8 - 1)
}
// ------ CONFIG CHUNK ------
pub fn config_chunk_to_raw(index: usize) -> Result<u8, MapperError> {
validate_range(
index,
0..=constants::CONFIG_CHUNK_INDEX_MAX,
|| format!("Invalid config index: {:#04x}", index)
)?;
validate_range(index, 0..=constants::CONFIG_CHUNK_INDEX_MAX, || {
format!("Invalid config index: {:#04x}", index)
})?;
Ok(index as u8)
}
@@ -99,10 +77,14 @@ pub fn preset_name_to_raw(name: &str) -> Result<Vec<u8>, MapperError> {
name.as_bytes(),
protocol_constants::PRESET_NAME_LENGTH,
protocol_constants::PRESET_NAME_PADDING,
"Preset name"
"Preset name",
)?;
Ok(encode_ascii(name, protocol_constants::PRESET_NAME_LENGTH, protocol_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> {
@@ -115,10 +97,12 @@ pub fn raw_to_preset_name(raw_name: &[u8]) -> Result<String, MapperError> {
fn input_source_to_raw_value(source: types::InputType) -> u8 {
match source {
types::InputType::Analog => *protocol_constants::INPUT_SOURCE_TYPE_RAW_RANGE.start(),
types::InputType::PinkNoise => *protocol_constants::INPUT_SOURCE_TYPE_RAW_RANGE.start() + 1,
types::InputType::WhiteNoise => *protocol_constants::INPUT_SOURCE_TYPE_RAW_RANGE.start() + 2,
types::InputType::SineWave => *protocol_constants::INPUT_SOURCE_TYPE_RAW_RANGE.start() + 3,
types::InputType::Analog => *protocol_constants::INPUT_SOURCE_TYPE_RAW_RANGE.start(),
types::InputType::PinkNoise => *protocol_constants::INPUT_SOURCE_TYPE_RAW_RANGE.start() + 1,
types::InputType::WhiteNoise => {
*protocol_constants::INPUT_SOURCE_TYPE_RAW_RANGE.start() + 2
}
types::InputType::SineWave => *protocol_constants::INPUT_SOURCE_TYPE_RAW_RANGE.start() + 3,
}
}
@@ -138,15 +122,13 @@ pub fn input_source_to_raw(source: types::InputType) -> Result<u8, MapperError>
pub fn raw_to_input_source(value: u8) -> Result<types::InputType, MapperError> {
validators::validate_raw_input_source(value)?;
raw_to_input_source_value(value)
.ok_or_else(|| {
MapperError::Decoding(
DecodingError::new(
format!("Unknown input value: {:#04x}", value)
)
)
})
raw_to_input_source_value(value).ok_or_else(|| {
MapperError::Decoding(DecodingError::new(format!(
"Unknown input value: {:#04x}",
value
)))
})
}
// ------ PASSWORD ------
@@ -156,10 +138,14 @@ pub fn password_to_raw(password: &str) -> Result<Vec<u8>, MapperError> {
password.as_bytes(),
protocol_constants::PASSWORD_LENGTH,
protocol_constants::PASSWORD_PADDING,
"Password"
"Password",
)?;
Ok(encode_ascii(password, protocol_constants::PASSWORD_LENGTH, protocol_constants::PASSWORD_PADDING)?)
Ok(encode_ascii(
password,
protocol_constants::PASSWORD_LENGTH,
protocol_constants::PASSWORD_PADDING,
)?)
}
#[cfg(test)]
@@ -237,10 +223,7 @@ mod tests {
let raw = preset_name_to_raw(name).unwrap();
assert_eq!(
raw_to_preset_name(&raw).unwrap(),
"DSP408",
);
assert_eq!(raw_to_preset_name(&raw).unwrap(), "DSP408",);
}
#[test]
@@ -282,4 +265,4 @@ mod tests {
assert!(password_to_raw("123").is_err());
assert!(password_to_raw("12345").is_err());
}
}
}
+2 -2
View File
@@ -1,9 +1,9 @@
mod channel;
pub(crate) mod constants;
mod encoding;
mod global;
pub(crate) mod response;
pub(crate) mod constants;
pub use channel::*;
pub use encoding::*;
pub use global::*;
pub use global::*;
+40 -131
View File
@@ -2,28 +2,13 @@ use crate::{
common::errors::ProtocolError,
dsp408::{
protocol::{
constants::{
METERS_HIGH_STEP,
METERS_LOW_STEP,
METERS_SPLIT,
METERS_SPLIT_LEVEL_DB,
},
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,
Acknowledgement, AuthenticationResult, ConfigChunk, CurrentPreset, DeviceFlags,
DeviceInfo, DeviceResponseType, HandshakeAck, Meters, ModifiedPreset, NotImplemented,
PresetName, UnknownResponse,
},
},
};
@@ -66,8 +51,7 @@ pub fn map_response(frame: &Frame) -> Result<DeviceResponseType, ProtocolError>
return Err(ProtocolError::new("Missing device name payload"));
}
let name = decode_ascii(payload, false)
.map_err(|e| ProtocolError::new(e.0))?;
let name = decode_ascii(payload, false).map_err(|e| ProtocolError::new(e.0))?;
return Ok(DeviceResponseType::DeviceInfo(DeviceInfo { name }));
}
@@ -131,8 +115,7 @@ pub fn map_response(frame: &Frame) -> Result<DeviceResponseType, ProtocolError>
return Err(ProtocolError::new("Missing preset index"));
}
let name = decode_ascii(&payload[1..], false)
.map_err(|e| ProtocolError::new(e.0))?;
let name = decode_ascii(&payload[1..], false).map_err(|e| ProtocolError::new(e.0))?;
return Ok(DeviceResponseType::PresetName(PresetName {
index: payload[0],
@@ -174,9 +157,11 @@ pub fn map_response(frame: &Frame) -> Result<DeviceResponseType, ProtocolError>
)));
}
return Ok(DeviceResponseType::AuthenticationResult(AuthenticationResult {
success: payload[1] == 0x01,
}));
return Ok(DeviceResponseType::AuthenticationResult(
AuthenticationResult {
success: payload[1] == 0x01,
},
));
}
// Command `0x40` (Get Level Meters)
@@ -224,9 +209,7 @@ mod tests {
assert_eq!(
result,
DeviceResponseType::Acknowledgement(Acknowledgement {
success: true
})
DeviceResponseType::Acknowledgement(Acknowledgement { success: true })
);
}
@@ -234,10 +217,7 @@ mod tests {
fn not_implemented() {
let result = map_response(&frame(0x02, vec![])).unwrap();
assert_eq!(
result,
DeviceResponseType::NotImplemented(NotImplemented)
);
assert_eq!(result, DeviceResponseType::NotImplemented(NotImplemented));
}
#[test]
@@ -246,9 +226,7 @@ mod tests {
assert_eq!(
result,
DeviceResponseType::HandshakeAck(HandshakeAck {
ack: Some(1)
})
DeviceResponseType::HandshakeAck(HandshakeAck { ack: Some(1) })
);
}
@@ -261,8 +239,7 @@ mod tests {
#[test]
fn device_name() {
let result = map_response(&frame(0x13, b"TestDevice".to_vec()))
.unwrap();
let result = map_response(&frame(0x13, b"TestDevice".to_vec())).unwrap();
assert_eq!(
result,
@@ -274,47 +251,34 @@ mod tests {
#[test]
fn device_name_missing_payload() {
assert!(
map_response(&frame(0x13, vec![]))
.is_err()
);
assert!(map_response(&frame(0x13, vec![])).is_err());
}
#[test]
fn device_name_invalid_ascii() {
assert!(
map_response(&frame(0x13, vec![0xff]))
.is_err()
);
assert!(map_response(&frame(0x13, vec![0xff])).is_err());
}
#[test]
fn current_preset() {
let result = map_response(&frame(0x14, vec![5]))
.unwrap();
let result = map_response(&frame(0x14, vec![5])).unwrap();
assert_eq!(
result,
DeviceResponseType::CurrentPreset(CurrentPreset {
index: 5
})
DeviceResponseType::CurrentPreset(CurrentPreset { index: 5 })
);
}
#[test]
fn current_preset_wrong_length_errors() {
assert!(
map_response(&frame(0x14, vec![1, 2]))
.is_err()
);
assert!(map_response(&frame(0x14, vec![1, 2])).is_err());
}
#[test]
fn modified_preset() {
let payload = vec![0xff; 20];
let result = map_response(&frame(0x22, payload))
.unwrap();
let result = map_response(&frame(0x22, payload)).unwrap();
match result {
DeviceResponseType::ModifiedPreset(p) => {
@@ -327,19 +291,12 @@ mod tests {
#[test]
fn modified_preset_invalid_length() {
assert!(
map_response(&frame(0x22, vec![0; 19]))
.is_err()
);
assert!(map_response(&frame(0x22, vec![0; 19])).is_err());
}
#[test]
fn config_chunk() {
let result = map_response(&frame(
0x24,
vec![3, 0xaa, 0xbb],
))
.unwrap();
let result = map_response(&frame(0x24, vec![3, 0xaa, 0xbb])).unwrap();
assert_eq!(
result,
@@ -352,8 +309,7 @@ mod tests {
#[test]
fn config_chunk_empty_payload_defaults_to_zero() {
let result = map_response(&frame(0x24, vec![]))
.unwrap();
let result = map_response(&frame(0x24, vec![])).unwrap();
assert_eq!(
result,
@@ -366,19 +322,12 @@ mod tests {
#[test]
fn config_chunk_invalid_index() {
assert!(
map_response(&frame(0x24, vec![0x1d]))
.is_err()
);
assert!(map_response(&frame(0x24, vec![0x1d])).is_err());
}
#[test]
fn preset_name() {
let result = map_response(&frame(
0x29,
vec![2, b'A', b'm', b'p'],
))
.unwrap();
let result = map_response(&frame(0x29, vec![2, b'A', b'm', b'p'])).unwrap();
assert_eq!(
result,
@@ -391,18 +340,12 @@ mod tests {
#[test]
fn preset_name_missing_index() {
assert!(
map_response(&frame(0x29, vec![]))
.is_err()
);
assert!(map_response(&frame(0x29, vec![])).is_err());
}
#[test]
fn preset_name_invalid_ascii() {
assert!(
map_response(&frame(0x29, vec![1, 0xff]))
.is_err()
);
assert!(map_response(&frame(0x29, vec![1, 0xff])).is_err());
}
#[test]
@@ -410,23 +353,17 @@ mod tests {
let mut payload = vec![0; 6];
payload[5] = 1;
let result = map_response(&frame(0x2c, payload))
.unwrap();
let result = map_response(&frame(0x2c, payload)).unwrap();
assert_eq!(
result,
DeviceResponseType::DeviceFlags(DeviceFlags {
is_locked: true
})
DeviceResponseType::DeviceFlags(DeviceFlags { is_locked: true })
);
}
#[test]
fn device_flags_invalid_length() {
assert!(
map_response(&frame(0x2c, vec![0; 5]))
.is_err()
);
assert!(map_response(&frame(0x2c, vec![0; 5])).is_err());
}
#[test]
@@ -434,52 +371,32 @@ mod tests {
let mut payload = vec![0; 6];
payload[5] = 2;
assert!(
map_response(&frame(0x2c, payload))
.is_err()
);
assert!(map_response(&frame(0x2c, payload)).is_err());
}
#[test]
fn authentication_success() {
let result = map_response(&frame(
0x2d,
vec![0, 1],
))
.unwrap();
let result = map_response(&frame(0x2d, vec![0, 1])).unwrap();
assert_eq!(
result,
DeviceResponseType::AuthenticationResult(
AuthenticationResult {
success: true
}
)
DeviceResponseType::AuthenticationResult(AuthenticationResult { success: true })
);
}
#[test]
fn authentication_missing_result() {
assert!(
map_response(&frame(0x2d, vec![0]))
.is_err()
);
assert!(map_response(&frame(0x2d, vec![0])).is_err());
}
#[test]
fn authentication_invalid_flag() {
assert!(
map_response(&frame(0x2d, vec![0, 2]))
.is_err()
);
assert!(map_response(&frame(0x2d, vec![0, 2])).is_err());
}
#[test]
fn meters_invalid_length() {
assert!(
map_response(&frame(0x40, vec![0; 10]))
.is_err()
);
assert!(map_response(&frame(0x40, vec![0; 10])).is_err());
}
#[test]
@@ -494,8 +411,7 @@ mod tests {
payload[i + 1] = 0x3c;
}
let result = map_response(&frame(0x40, payload))
.unwrap();
let result = map_response(&frame(0x40, payload)).unwrap();
match result {
DeviceResponseType::Meters(m) => {
@@ -511,21 +427,14 @@ mod tests {
#[test]
fn unknown_command_falls_through() {
let result = map_response(&frame(
0x99,
vec![0xaa],
))
.unwrap();
let result = map_response(&frame(0x99, vec![0xaa])).unwrap();
match result {
DeviceResponseType::UnknownResponse(r) => {
assert_eq!(r.cmd, "0x99");
assert_eq!(r.raw, vec![0xaa]);
}
other => panic!(
"expected UnknownResponse, got {:?}",
other
),
other => panic!("expected UnknownResponse, got {:?}", other),
}
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
mod protocol;
mod client;
mod mapper;
mod protocol;
pub mod types;
pub use client::DSP408;
pub use client::DSP408;
+77 -384
View File
@@ -1,15 +1,9 @@
use crate::common::errors::ValidationError;
use super::{
frame::build_frame,
validators::*,
};
use super::{frame::build_frame, validators::*};
fn split_u16(value: u16) -> [u8; 2] {
[
(value & 0xff) as u8,
(value >> 8) as u8,
]
[(value & 0xff) as u8, (value >> 8) as u8]
}
// Command 0x10 Start
@@ -38,135 +32,81 @@ pub fn build_get_current_preset() -> Vec<u8> {
}
// Command 0x15 Delay Unit
pub fn build_set_delay_unit(
raw_unit: u8
) -> Result<Vec<u8>, ValidationError> {
pub fn build_set_delay_unit(raw_unit: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_delay_unit(raw_unit)?;
Ok(build_frame(
0x15,
&[raw_unit]
))
Ok(build_frame(0x15, &[raw_unit]))
}
// Command 0x20 Recall
pub fn build_recall(
raw_index:u8
)->Result<Vec<u8>,ValidationError>{
pub fn build_recall(raw_index: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_preset_index(raw_index)?;
Ok(build_frame(
0x20,
&[raw_index]
))
Ok(build_frame(0x20, &[raw_index]))
}
// Command 0x21 Store preset
pub fn build_store_current_configuration(
raw_index:u8
)->Result<Vec<u8>,ValidationError>{
pub fn build_store_current_configuration(raw_index: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_store_preset_index(raw_index)?;
Ok(build_frame(
0x21,
&[raw_index]
))
Ok(build_frame(0x21, &[raw_index]))
}
// Command 0x22
pub fn build_preset_modification_status() -> Vec<u8> {
build_frame(0x22,&[])
build_frame(0x22, &[])
}
// Command 0x23
pub fn build_factory_reset() -> Vec<u8> {
build_frame(0x23,&[])
build_frame(0x23, &[])
}
// Command 0x26
pub fn build_store_current_preset_name(
raw_name:&[u8]
)->Result<Vec<u8>,ValidationError>{
pub fn build_store_current_preset_name(raw_name: &[u8]) -> Result<Vec<u8>, ValidationError> {
validate_raw_preset_name(raw_name)?;
Ok(build_frame(
0x26,
raw_name
))
Ok(build_frame(0x26, raw_name))
}
// Command 0x27
pub fn build_config_chunk(
raw_index:u8
)->Result<Vec<u8>,ValidationError>{
pub fn build_config_chunk(raw_index: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_config_chunk_index(raw_index)?;
Ok(build_frame(
0x27,
&[raw_index]
))
Ok(build_frame(0x27, &[raw_index]))
}
// Command 0x29
pub fn build_get_preset_name(
raw_index:u8
)->Result<Vec<u8>,ValidationError>{
pub fn build_get_preset_name(raw_index: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_get_preset_index(raw_index)?;
Ok(build_frame(
0x29,
&[raw_index]
))
Ok(build_frame(0x29, &[raw_index]))
}
// Command 0x2A
pub fn build_copy_channel(
source: u8,
destination: u8,
) -> Result<Vec<u8>, ValidationError> {
pub fn build_copy_channel(source: u8, destination: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_copy_channels(source, destination)?;
Ok(build_frame(
0x2A,
&[source, destination],
))
Ok(build_frame(0x2A, &[source, destination]))
}
// Command 0x2C
pub fn build_get_device_flags() -> Vec<u8> {
build_frame(0x2c,&[])
build_frame(0x2c, &[])
}
// Command 0x2D
pub fn build_unlock_device(
password:&[u8]
)->Result<Vec<u8>,ValidationError>{
pub fn build_unlock_device(password: &[u8]) -> Result<Vec<u8>, ValidationError> {
validate_raw_password(password)?;
Ok(build_frame(
0x2d,
password
))
Ok(build_frame(0x2d, password))
}
// Command 0x2F
pub fn build_lock_device(
password:&[u8]
)->Result<Vec<u8>,ValidationError>{
pub fn build_lock_device(password: &[u8]) -> Result<Vec<u8>, ValidationError> {
validate_raw_password(password)?;
Ok(build_frame(
0x2f,
password
))
Ok(build_frame(0x2f, password))
}
// Command 0x30 (Set Compressor)
@@ -178,7 +118,6 @@ pub fn build_set_compressor(
raw_knee: u16,
raw_threshold: u16,
) -> Result<Vec<u8>, ValidationError> {
validate_raw_output_channel(raw_channel)?;
validate_raw_compressor_ratio(raw_ratio)?;
validate_raw_attack(raw_attack)?;
@@ -216,7 +155,6 @@ pub fn build_low_pass(
raw_freq: u16,
raw_filter_type: u8,
) -> Result<Vec<u8>, ValidationError> {
validate_raw_channel(raw_channel)?;
validate_raw_frequency(raw_freq)?;
validate_raw_crossover_filter(raw_filter_type)?;
@@ -225,12 +163,7 @@ pub fn build_low_pass(
Ok(build_frame(
0x31,
&[
raw_channel,
freq[0],
freq[1],
raw_filter_type,
],
&[raw_channel, freq[0], freq[1], raw_filter_type],
))
}
@@ -240,7 +173,6 @@ pub fn build_high_pass(
raw_freq: u16,
raw_filter_type: u8,
) -> Result<Vec<u8>, ValidationError> {
validate_raw_channel(raw_channel)?;
validate_raw_frequency(raw_freq)?;
validate_raw_crossover_filter(raw_filter_type)?;
@@ -249,12 +181,7 @@ pub fn build_high_pass(
Ok(build_frame(
0x32,
&[
raw_channel,
freq[0],
freq[1],
raw_filter_type,
],
&[raw_channel, freq[0], freq[1], raw_filter_type],
))
}
@@ -268,7 +195,6 @@ pub fn build_peq_band(
raw_filter_type: u8,
bypass: u8,
) -> Result<Vec<u8>, ValidationError> {
validate_raw_channel(raw_channel)?;
validate_raw_peq_band(raw_channel, raw_band)?;
validate_raw_eq_gain(raw_gain)?;
@@ -297,115 +223,53 @@ pub fn build_peq_band(
}
// Command 0x34 (Set Gain)
pub fn build_gain(
channel: u8,
raw_gain: u16,
) -> Result<Vec<u8>, ValidationError> {
pub fn build_gain(channel: u8, raw_gain: u16) -> Result<Vec<u8>, ValidationError> {
validate_raw_channel(channel)?;
validate_raw_gain(raw_gain)?;
let gain = split_u16(raw_gain);
Ok(build_frame(
0x34,
&[
channel,
gain[0],
gain[1],
],
))
Ok(build_frame(0x34, &[channel, gain[0], gain[1]]))
}
// Command 0x35 (Set Mute)
pub fn build_mute(
channel:u8,
muted:u8,
)->Result<Vec<u8>,ValidationError>{
pub fn build_mute(channel: u8, muted: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_channel(channel)?;
validate_raw_bool(muted)?;
Ok(build_frame(
0x35,
&[
channel,
muted,
]
))
Ok(build_frame(0x35, &[channel, muted]))
}
// Command 0x36 (Invert Gain)
pub fn build_inverted_gain(
channel:u8,
inverted:u8,
)->Result<Vec<u8>,ValidationError>{
pub fn build_inverted_gain(channel: u8, inverted: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_channel(channel)?;
validate_raw_bool(inverted)?;
Ok(build_frame(
0x36,
&[
channel,
inverted,
]
))
Ok(build_frame(0x36, &[channel, inverted]))
}
// Command 0x38 (Set Delay)
pub fn build_set_delay(
channel:u8,
delay:u8,
)->Result<Vec<u8>,ValidationError>{
pub fn build_set_delay(channel: u8, delay: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_channel(channel)?;
validate_raw_channel_delay(delay)?;
Ok(build_frame(
0x38,
&[
channel,
0x00,
delay
]
))
Ok(build_frame(0x38, &[channel, 0x00, delay]))
}
// Command 0x39 (Set Input Source)
pub fn build_set_input_source(
input_type: u8,
frequency: u8,
) -> Result<Vec<u8>, ValidationError> {
pub fn build_set_input_source(input_type: u8, frequency: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_input_source(input_type)?;
validate_raw_discrete_frequency(frequency)?;
Ok(build_frame(
0x39,
&[
input_type,
frequency,
],
))
Ok(build_frame(0x39, &[input_type, frequency]))
}
// Command 0x3A (Matrix Routing)
pub fn build_matrix_routing(
output: u8,
input_bitmask: u8,
) -> Result<Vec<u8>, ValidationError> {
pub fn build_matrix_routing(output: u8, input_bitmask: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_output_channel(output)?;
validate_raw_matrix_bitmask(input_bitmask)?;
Ok(build_frame(
0x3A,
&[
output,
input_bitmask,
],
))
Ok(build_frame(0x3A, &[output, input_bitmask]))
}
// Command 0x3B (Link Channel)
@@ -413,52 +277,31 @@ pub fn build_link_channel(
source_channel: u8,
destination_channel_bitmask: u8,
) -> Result<Vec<u8>, ValidationError> {
validate_raw_link_channel(source_channel, destination_channel_bitmask)?;
Ok(build_frame(
0x3B,
&[
source_channel,
destination_channel_bitmask,
],
&[source_channel, destination_channel_bitmask],
))
}
// Command 0x3C (Bypass EQ)
pub fn build_bypass_peq(
channel: u8,
bypass: u8,
) -> Result<Vec<u8>, ValidationError> {
pub fn build_bypass_peq(channel: u8, bypass: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_channel(channel)?;
validate_raw_bool(bypass)?;
Ok(build_frame(
0x3C,
&[
channel,
bypass,
],
))
Ok(build_frame(0x3C, &[channel, bypass]))
}
// Command 0x3D (Store Channel Name)
pub fn build_store_channel_name(
channel: u8,
raw_name: &[u8],
) -> Result<Vec<u8>, ValidationError> {
pub fn build_store_channel_name(channel: u8, raw_name: &[u8]) -> Result<Vec<u8>, ValidationError> {
validate_raw_channel(channel)?;
validate_raw_channel_name(raw_name)?;
let mut payload = vec![channel];
payload.extend_from_slice(raw_name);
Ok(build_frame(
0x3D,
&payload,
))
Ok(build_frame(0x3D, &payload))
}
// Command 0x3E (Set Gate Parameters)
@@ -469,7 +312,6 @@ pub fn build_set_gate(
raw_hold: u16,
raw_release: u16,
) -> Result<Vec<u8>, ValidationError> {
validate_raw_input_channel(raw_channel)?;
validate_raw_gate_threshold(raw_threshold)?;
validate_raw_attack(raw_attack)?;
@@ -504,7 +346,6 @@ pub fn build_set_limiter(
raw_attack: u16,
raw_release: u16,
) -> Result<Vec<u8>, ValidationError> {
validate_raw_output_channel(raw_channel)?;
validate_raw_threshold(raw_threshold)?;
validate_raw_attack(raw_attack)?;
@@ -532,10 +373,7 @@ pub fn build_set_limiter(
// Command 0x40 (Get Level Meters)
pub fn build_get_meter_levels() -> Vec<u8> {
build_frame(
0x40,
&[],
)
build_frame(0x40, &[])
}
// Command 0x41 (Set Matrix Gain)
@@ -544,7 +382,6 @@ pub fn build_matrix_gain(
raw_output_channel: u8,
raw_gain: u16,
) -> Result<Vec<u8>, ValidationError> {
validate_raw_input_channel(raw_input_channel)?;
validate_raw_output_channel(raw_output_channel)?;
validate_raw_matrix_gain(raw_gain)?;
@@ -553,12 +390,7 @@ pub fn build_matrix_gain(
Ok(build_frame(
0x41,
&[
raw_output_channel,
raw_input_channel,
gain[0],
gain[1],
],
&[raw_output_channel, raw_input_channel, gain[0], gain[1]],
))
}
@@ -568,7 +400,6 @@ pub fn build_set_geq_band(
raw_frequency_band: u8,
raw_gain: u16,
) -> Result<Vec<u8>, ValidationError> {
validate_raw_input_channel(raw_channel)?;
validate_raw_discrete_frequency(raw_frequency_band)?;
validate_raw_eq_gain(raw_gain)?;
@@ -577,43 +408,24 @@ pub fn build_set_geq_band(
Ok(build_frame(
0x48,
&[
raw_channel,
raw_frequency_band,
gain[0],
gain[1],
],
&[raw_channel, raw_frequency_band, gain[0], gain[1]],
))
}
// Command 0x49 (Set GEQ Bypass)
pub fn build_set_geq_bypass(
raw_channel: u8,
bypass: u8,
) -> Result<Vec<u8>, ValidationError> {
pub fn build_set_geq_bypass(raw_channel: u8, bypass: u8) -> Result<Vec<u8>, ValidationError> {
validate_raw_input_channel(raw_channel)?;
validate_raw_bool(bypass)?;
Ok(build_frame(
0x49,
&[
raw_channel,
bypass,
],
))
Ok(build_frame(0x49, &[raw_channel, bypass]))
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::constants::*;
use super::*;
fn assert_frame(
frame: Vec<u8>,
expected_command: u8,
expected_payload: &[u8],
) {
fn assert_frame(frame: Vec<u8>, expected_command: u8, expected_payload: &[u8]) {
assert_eq!(frame[5], expected_command);
assert_eq!(&frame[6..frame.len() - 3], expected_payload);
}
@@ -627,86 +439,34 @@ mod tests {
#[test]
fn test_simple_commands() {
assert_frame(
build_handshake(),
0x10,
&[],
);
assert_frame(build_handshake(), 0x10, &[]);
assert_frame(
build_disconnect(),
0x11,
&[],
);
assert_frame(build_disconnect(), 0x11, &[]);
assert_frame(
build_acknowledgement(),
0x12,
&[],
);
assert_frame(build_acknowledgement(), 0x12, &[]);
assert_frame(
build_request_device_name(),
0x13,
&[],
);
assert_frame(build_request_device_name(), 0x13, &[]);
assert_frame(
build_get_current_preset(),
0x14,
&[],
);
assert_frame(build_get_current_preset(), 0x14, &[]);
assert_frame(
build_preset_modification_status(),
0x22,
&[],
);
assert_frame(build_preset_modification_status(), 0x22, &[]);
assert_frame(
build_factory_reset(),
0x23,
&[],
);
assert_frame(build_factory_reset(), 0x23, &[]);
assert_frame(
build_get_device_flags(),
0x2c,
&[],
);
assert_frame(build_get_device_flags(), 0x2c, &[]);
assert_frame(
build_get_meter_levels(),
0x40,
&[],
);
assert_frame(build_get_meter_levels(), 0x40, &[]);
}
#[test]
fn test_preset_commands() {
assert_frame(
build_recall(5).unwrap(),
0x20,
&[5],
);
assert_frame(build_recall(5).unwrap(), 0x20, &[5]);
assert_frame(
build_store_current_configuration(3).unwrap(),
0x21,
&[3],
);
assert_frame(build_store_current_configuration(3).unwrap(), 0x21, &[3]);
assert_frame(
build_get_preset_name(2).unwrap(),
0x29,
&[2],
);
assert_frame(build_get_preset_name(2).unwrap(), 0x29, &[2]);
assert_frame(
build_config_chunk(1).unwrap(),
0x27,
&[1],
);
assert_frame(build_config_chunk(1).unwrap(), 0x27, &[1]);
assert_frame(
build_store_current_preset_name(b"TEST ").unwrap(),
@@ -717,57 +477,27 @@ mod tests {
#[test]
fn test_device_lock_commands() {
assert_frame(
build_unlock_device(b"1234").unwrap(),
0x2d,
b"1234",
);
assert_frame(build_unlock_device(b"1234").unwrap(), 0x2d, b"1234");
assert_frame(
build_lock_device(b"1234").unwrap(),
0x2f,
b"1234",
);
assert_frame(build_lock_device(b"1234").unwrap(), 0x2f, b"1234");
}
#[test]
fn test_set_delay_unit() {
assert_frame(
build_set_delay_unit(1).unwrap(),
0x15,
&[1],
);
assert_frame(build_set_delay_unit(1).unwrap(), 0x15, &[1]);
}
#[test]
fn test_copy_channel() {
assert_frame(
build_copy_channel(1, 3).unwrap(),
0x2a,
&[1, 3],
);
assert_frame(build_copy_channel(1, 3).unwrap(), 0x2a, &[1, 3]);
}
#[test]
fn test_compressor_frame() {
assert_frame(
build_set_compressor(
5,
1,
300,
400,
2,
11,
).unwrap(),
build_set_compressor(5, 1, 300, 400, 2, 11).unwrap(),
0x30,
&[
5,
1, 0,
44, 1,
144, 1,
2, 0,
11, 0,
],
&[5, 1, 0, 44, 1, 144, 1, 2, 0, 11, 0],
);
}
@@ -869,17 +599,9 @@ mod tests {
#[test]
fn test_delay_and_input() {
assert_frame(
build_set_delay(
*CHANNELS_RANGE.end(),
*DELAY_RAW_RANGE.end(),
)
.unwrap(),
build_set_delay(*CHANNELS_RANGE.end(), *DELAY_RAW_RANGE.end()).unwrap(),
0x38,
&[
*CHANNELS_RANGE.end(),
0x00,
*DELAY_RAW_RANGE.end(),
],
&[*CHANNELS_RANGE.end(), 0x00, *DELAY_RAW_RANGE.end()],
);
assert_frame(
@@ -935,11 +657,7 @@ mod tests {
expected.extend_from_slice(&name);
assert_frame(
build_store_channel_name(
*CHANNELS_RANGE.start(),
&name,
)
.unwrap(),
build_store_channel_name(*CHANNELS_RANGE.start(), &name).unwrap(),
0x3D,
&expected,
);
@@ -953,14 +671,7 @@ mod tests {
let release = *RELEASE_RAW_RANGE.end();
assert_frame(
build_set_gate(
*INPUT_CHANNELS.end(),
threshold,
attack,
hold,
release,
)
.unwrap(),
build_set_gate(*INPUT_CHANNELS.end(), threshold, attack, hold, release).unwrap(),
0x3E,
&[
*INPUT_CHANNELS.end(),
@@ -978,13 +689,7 @@ mod tests {
let threshold = *THRESHOLD_RAW_RANGE.end();
assert_frame(
build_set_limiter(
*OUTPUT_CHANNELS.end(),
threshold,
attack,
release,
)
.unwrap(),
build_set_limiter(*OUTPUT_CHANNELS.end(), threshold, attack, release).unwrap(),
0x3F,
&[
*OUTPUT_CHANNELS.end(),
@@ -1005,12 +710,7 @@ mod tests {
let gain = *MATRIX_GAIN_RAW_RANGE.end();
assert_frame(
build_matrix_gain(
*INPUT_CHANNELS.end(),
*OUTPUT_CHANNELS.end(),
gain,
)
.unwrap(),
build_matrix_gain(*INPUT_CHANNELS.end(), *OUTPUT_CHANNELS.end(), gain).unwrap(),
0x41,
&[
*OUTPUT_CHANNELS.end(),
@@ -1042,16 +742,9 @@ mod tests {
);
assert_frame(
build_set_geq_bypass(
*INPUT_CHANNELS.end(),
1,
)
.unwrap(),
build_set_geq_bypass(*INPUT_CHANNELS.end(), 1).unwrap(),
0x49,
&[
*INPUT_CHANNELS.end(),
1,
],
&[*INPUT_CHANNELS.end(), 1],
);
}
}
}
+34 -77
View File
@@ -6,11 +6,8 @@ use crate::{
common::errors::DSPError,
dsp408::{
mapper,
protocol::{constants, reader::BinaryReader},
types,
protocol::{
constants,
reader::BinaryReader,
},
},
};
@@ -37,9 +34,7 @@ fn read_gate(r: &mut BinaryReader) -> Result<types::Gate, DSPError> {
})
}
fn read_geq(
r: &mut BinaryReader,
) -> Result<types::GraphicEQ, DSPError> {
fn read_geq(r: &mut BinaryReader) -> Result<types::GraphicEQ, DSPError> {
let gains = types::DiscreteFrequency::iter()
.map(|_| {
let raw = r.read_u16()?;
@@ -53,10 +48,7 @@ fn read_geq(
})
}
fn read_peq_chain(
r: &mut BinaryReader,
band_count: usize,
) -> Result<types::PEQChain, DSPError> {
fn read_peq_chain(r: &mut BinaryReader, band_count: usize) -> Result<types::PEQChain, DSPError> {
let bands = (0..band_count)
.map(|_| {
Ok(types::PEQ {
@@ -75,9 +67,7 @@ fn read_peq_chain(
})
}
fn read_crossover(
r: &mut BinaryReader,
) -> Result<types::CrossoverFilters, DSPError> {
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()?;
@@ -94,9 +84,7 @@ fn read_crossover(
})
}
fn read_compressor(
r: &mut BinaryReader,
) -> Result<types::Compressor, DSPError> {
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()?)?,
@@ -106,9 +94,7 @@ fn read_compressor(
})
}
fn read_limiter(
r: &mut BinaryReader,
) -> Result<types::Limiter, DSPError> {
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()?)?;
@@ -132,7 +118,8 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
let mut input_channels = vec![types::InputChannelState::default(); types::InputChannel::COUNT];
let mut output_channels = vec![types::OutputChannelState::default(); types::OutputChannel::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();
@@ -162,7 +149,10 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
r.skip(1)?;
inp.linked_channels = mapper::raw_to_link_channels(mapper::channel_to_raw(types::Channel::Input(channel))?, r.read_u8()?)?;
inp.linked_channels = mapper::raw_to_link_channels(
mapper::channel_to_raw(types::Channel::Input(channel))?,
r.read_u8()?,
)?;
}
// ============================
@@ -175,10 +165,7 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
r.skip(1)?;
out.matrix_routes = types::MatrixRoutes {
connected:
mapper::raw_to_matrix(
r.read_u8()?
)?,
connected: mapper::raw_to_matrix(r.read_u8()?)?,
gains: (0..4)
.map(|_| {
@@ -206,43 +193,34 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
r.skip(1)?;
out.linked_channels = mapper::raw_to_link_channels(mapper::channel_to_raw(types::Channel::Output(channel))?, r.read_u8()?)?;
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 input_channels, |ch, value| ch.mute = value)?;
apply_flag(
&mut r,
&mut output_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,
)?;
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,
)?;
apply_flag(&mut r, out.peq_chain.bands.as_mut_slice(), |band, value| {
band.bypass = value
})?;
}
// ============================
@@ -250,41 +228,27 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
// ============================
let input_source = types::InputSource {
r#type: mapper::raw_to_input_source(
r.read_u16()? as u8,
)?,
r#type: mapper::raw_to_input_source(r.read_u16()? as u8)?,
frequency: mapper::raw_to_frequency_value(
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,
)?;
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 (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,
)?;
for (out, raw_delay) in output_channels.iter_mut().zip(output_delays_raw) {
out.delay = mapper::raw_to_delay(raw_delay, delay_unit)?;
}
// ============================
@@ -301,7 +265,6 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
}
}
let output_peq_bitmap = r.read_u16()?;
for (index, out) in output_channels.iter_mut().enumerate() {
@@ -325,19 +288,13 @@ pub fn decode_config(payload: &[u8]) -> Result<types::DSPConfigState, DSPError>
let mut input_states = HashMap::new();
for (channel, state) in types::InputChannel::iter().zip(input_channels) {
input_states.insert(
channel,
state,
);
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,
);
output_states.insert(channel, state);
}
Ok(types::DSPConfigState {
+17 -17
View File
@@ -16,31 +16,31 @@ pub const CHANNELS_RANGE: RangeInclusive<u8> = 0x00..=0x0B;
pub const INPUT_CHANNELS: RangeInclusive<u8> = 0x00..=0x03;
pub const OUTPUT_CHANNELS: RangeInclusive<u8> = 0x04..=0x0B;
pub const RATIO_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x000F;
pub const ATTACK_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x03E6;
pub const RELEASE_RAW_RANGE: RangeInclusive<u16> = 0x0009..=0x0BB7;
pub const KNEE_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x000C;
pub const THRESHOLD_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x00DC;
pub const RATIO_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x000F;
pub const ATTACK_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x03E6;
pub const RELEASE_RAW_RANGE: RangeInclusive<u16> = 0x0009..=0x0BB7;
pub const KNEE_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x000C;
pub const THRESHOLD_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x00DC;
pub const FREQUENCY_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x012C;
pub const FREQUENCY_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x012C;
pub const CROSSOVER_FILTER_RAW_RANGE: RangeInclusive<u8> = 0x00..=0x14;
pub const EQ_GAIN_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x00F0;
pub const PEQ_Q_RAW_RANGE: RangeInclusive<u8> = 0x00..=0x64;
pub const PEQ_INPUT_BAND_RANGE: RangeInclusive<u8> = 0x00..=0x07;
pub const PEQ_OUTPUT_BAND_RANGE:RangeInclusive<u8> = 0x00..=0x08;
pub const PEQ_FILTER_RAW_RANGE :RangeInclusive<u8> = 0x00..=0x08;
pub const EQ_GAIN_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x00F0;
pub const PEQ_Q_RAW_RANGE: RangeInclusive<u8> = 0x00..=0x64;
pub const PEQ_INPUT_BAND_RANGE: RangeInclusive<u8> = 0x00..=0x07;
pub const PEQ_OUTPUT_BAND_RANGE: RangeInclusive<u8> = 0x00..=0x08;
pub const PEQ_FILTER_RAW_RANGE: RangeInclusive<u8> = 0x00..=0x08;
pub const CHANNEL_GAIN_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x0190;
pub const CHANNEL_GAIN_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x0190;
pub const DELAY_RAW_RANGE: RangeInclusive<u8> = 0x00..=0xFF;
pub const DELAY_RAW_RANGE: RangeInclusive<u8> = 0x00..=0xFF;
pub const INPUT_SOURCE_TYPE_RAW_RANGE: RangeInclusive<u8> = 0x00..=0x03;
pub const DISCRETE_FREQUENCY_RAW_RANGE: RangeInclusive<u8> = 0x00..=0x1E;
pub const MATRIX_INPUT_MASK_RANGE: RangeInclusive<u8> = 0x00..=0x0F;
pub const MATRIX_INPUT_MASK_RANGE: RangeInclusive<u8> = 0x00..=0x0F;
pub const MATRIX_INPUT_A_MASK: u8 = 0x01;
pub const MATRIX_INPUT_B_MASK: u8 = 0x02;
pub const MATRIX_INPUT_C_MASK: u8 = 0x04;
@@ -50,9 +50,9 @@ pub const CHANNEL_NAME_LENGTH: usize = 8;
pub const CHANNEL_NAME_PADDING: u8 = 0x00;
pub const GATE_THRESHOLD_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x00B4;
pub const HOLD_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x03E6;
pub const HOLD_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x03E6;
pub const MATRIX_GAIN_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x0118;
pub const MATRIX_GAIN_RAW_RANGE: RangeInclusive<u16> = 0x0000..=0x0118;
pub fn input_link_mask(channel: u8) -> Option<u8> {
match channel {
@@ -81,4 +81,4 @@ pub fn output_link_mask(channel: u8) -> Option<u8> {
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;
pub const METERS_HIGH_STEP: f32 = 0.0208;
+2 -4
View File
@@ -65,8 +65,7 @@ pub fn extract_frame(data: &[u8]) -> Result<Frame, FrameError> {
)));
}
let footer = find_footer(data)
.ok_or_else(|| FrameError::new("Missing frame footer"))?;
let footer = find_footer(data).ok_or_else(|| FrameError::new("Missing frame footer"))?;
let expected = checksum(&data[2..footer]);
let received = data[data.len() - 1];
@@ -89,8 +88,7 @@ fn find_footer(data: &[u8]) -> Option<usize> {
return None;
}
(2..data.len() - 1)
.find(|&i| data[i] == 0x10 && data[i + 1] == 0x03)
(2..data.len() - 1).find(|&i| data[i] == 0x10 && data[i + 1] == 0x03)
}
#[cfg(test)]
+3 -3
View File
@@ -1,8 +1,8 @@
mod reader;
pub(crate) mod frame;
pub(crate) mod commands;
pub(crate) mod validators;
pub(crate) mod config;
pub(crate) mod frame;
pub(crate) mod validators;
pub mod constants;
pub mod constants;
+3 -7
View File
@@ -11,9 +11,7 @@ impl<'a> BinaryReader<'a> {
pub fn pop(&mut self, n: usize) -> Result<&'a [u8], DSPError> {
if self.data.len() < n {
return Err(DSPError::InvalidRequest(
"buffer underflow".into()
));
return Err(DSPError::InvalidRequest("buffer underflow".into()));
}
let (value, rest) = self.data.split_at(n);
@@ -27,13 +25,11 @@ impl<'a> BinaryReader<'a> {
}
pub fn read_u16(&mut self) -> Result<u16, DSPError> {
Ok(u16::from_le_bytes(
self.pop(2)?.try_into().unwrap()
))
Ok(u16::from_le_bytes(self.pop(2)?.try_into().unwrap()))
}
pub fn skip(&mut self, n: usize) -> Result<(), DSPError> {
self.pop(n)?;
Ok(())
}
}
}
+186 -259
View File
@@ -1,45 +1,33 @@
use crate::{
common::{
errors::ValidationError,
validator::{
validate_ascii_padded,
validate_bitmask,
validate_range,
},
validator::{validate_ascii_padded, validate_bitmask, validate_range},
},
dsp408::protocol::constants::*,
};
pub fn validate_raw_delay_unit(raw_unit: u8) -> Result<(), ValidationError> {
validate_range(
raw_unit,
DELAY_UNIT_RAW_RANGE,
|| format!("Invalid delay unit: {:#04x}", raw_unit)
)
validate_range(raw_unit, DELAY_UNIT_RAW_RANGE, || {
format!("Invalid delay unit: {:#04x}", raw_unit)
})
}
pub fn validate_raw_preset_index(raw_index: u8) -> Result<(), ValidationError> {
validate_range(
raw_index,
0..=PRESET_INDEX_MAX,
|| format!("Invalid preset index {}", raw_index)
)
validate_range(raw_index, 0..=PRESET_INDEX_MAX, || {
format!("Invalid preset index {}", raw_index)
})
}
pub fn validate_raw_store_preset_index(raw_index: u8) -> Result<(), ValidationError> {
validate_range(
raw_index,
1..=PRESET_INDEX_MAX,
|| format!("Invalid preset index {}", raw_index)
)
validate_range(raw_index, 1..=PRESET_INDEX_MAX, || {
format!("Invalid preset index {}", raw_index)
})
}
pub fn validate_raw_get_preset_index(raw_index: u8) -> Result<(), ValidationError> {
validate_range(
raw_index,
0..PRESET_INDEX_MAX,
|| format!("Invalid preset index {}", raw_index)
)
validate_range(raw_index, 0..PRESET_INDEX_MAX, || {
format!("Invalid preset index {}", raw_index)
})
}
pub fn validate_raw_preset_name(raw_name: &[u8]) -> Result<(), ValidationError> {
@@ -47,58 +35,46 @@ pub fn validate_raw_preset_name(raw_name: &[u8]) -> Result<(), ValidationError>
raw_name,
PRESET_NAME_LENGTH,
PRESET_NAME_PADDING,
"Preset name"
"Preset name",
)
}
pub fn validate_raw_config_chunk_index(raw_index: u8) -> Result<(), ValidationError> {
validate_range(
raw_index,
0..=CONFIG_CHUNK_INDEX_MAX,
|| format!("Invalid config index: {:#04x}", raw_index)
)
validate_range(raw_index, 0..=CONFIG_CHUNK_INDEX_MAX, || {
format!("Invalid config index: {:#04x}", raw_index)
})
}
pub fn validate_raw_channel(channel: u8) -> Result<(), ValidationError> {
validate_range(
channel,
CHANNELS_RANGE,
|| format!("Invalid Channel: {:#04x}", channel),
)
validate_range(channel, CHANNELS_RANGE, || {
format!("Invalid Channel: {:#04x}", channel)
})
}
pub fn validate_raw_input_channel(channel: u8) -> Result<(), ValidationError> {
validate_range(
channel,
INPUT_CHANNELS,
|| format!("Invalid Channel: {:#04x}", channel),
)
validate_range(channel, INPUT_CHANNELS, || {
format!("Invalid Channel: {:#04x}", channel)
})
}
pub fn validate_raw_output_channel(channel: u8) -> Result<(), ValidationError> {
validate_range(
channel,
OUTPUT_CHANNELS,
|| format!("Invalid Channel: {:#04x}", channel),
)
validate_range(channel, OUTPUT_CHANNELS, || {
format!("Invalid Channel: {:#04x}", channel)
})
}
pub fn validate_raw_copy_channels(source: u8, destination: u8) -> Result<(), ValidationError> {
validate_range(
source,
CHANNELS_RANGE,
|| format!("Invalid source channel: {:#04x}", source),
)?;
validate_range(source, CHANNELS_RANGE, || {
format!("Invalid source channel: {:#04x}", source)
})?;
validate_range(
destination,
CHANNELS_RANGE,
|| format!("Invalid destination channel: {:#04x}", destination),
)?;
validate_range(destination, CHANNELS_RANGE, || {
format!("Invalid destination channel: {:#04x}", destination)
})?;
if source == destination {
return Err(ValidationError::value(
"Source channel and destination channel can't be the same"
"Source channel and destination channel can't be the same",
));
}
@@ -107,7 +83,7 @@ pub fn validate_raw_copy_channels(source: u8, destination: u8) -> Result<(), Val
if source_is_input != destination_is_input {
return Err(ValidationError::value(
"Channels need to be both inputs or outputs"
"Channels need to be both inputs or outputs",
));
}
@@ -115,150 +91,111 @@ pub fn validate_raw_copy_channels(source: u8, destination: u8) -> Result<(), Val
}
pub fn validate_raw_password(password: &[u8]) -> Result<(), ValidationError> {
validate_ascii_padded(
password,
PASSWORD_LENGTH,
PASSWORD_PADDING,
"Password"
)
validate_ascii_padded(password, PASSWORD_LENGTH, PASSWORD_PADDING, "Password")
}
pub fn validate_raw_compressor_ratio(raw_ratio: u16) -> Result<(), ValidationError> {
validate_range(
raw_ratio,
RATIO_RAW_RANGE,
|| format!("Invalid compressor ratio: {:#06x}", raw_ratio),
)
validate_range(raw_ratio, RATIO_RAW_RANGE, || {
format!("Invalid compressor ratio: {:#06x}", raw_ratio)
})
}
pub fn validate_raw_attack(raw_attack: u16) -> Result<(), ValidationError> {
validate_range(
raw_attack,
ATTACK_RAW_RANGE,
|| format!("Invalid attack: {:#06x}", raw_attack),
)
validate_range(raw_attack, ATTACK_RAW_RANGE, || {
format!("Invalid attack: {:#06x}", raw_attack)
})
}
pub fn validate_raw_release(raw_release: u16) -> Result<(), ValidationError> {
validate_range(
raw_release,
RELEASE_RAW_RANGE,
|| format!("Invalid release: {:#06x}", raw_release),
)
validate_range(raw_release, RELEASE_RAW_RANGE, || {
format!("Invalid release: {:#06x}", raw_release)
})
}
pub fn validate_raw_compressor_knee(raw_knee: u16) -> Result<(), ValidationError> {
validate_range(
raw_knee,
KNEE_RAW_RANGE,
|| format!("Invalid compressor knee: {:#06x}", raw_knee),
)
validate_range(raw_knee, KNEE_RAW_RANGE, || {
format!("Invalid compressor knee: {:#06x}", raw_knee)
})
}
pub fn validate_raw_threshold(raw_threshold: u16) -> Result<(), ValidationError> {
validate_range(
raw_threshold,
THRESHOLD_RAW_RANGE,
|| format!("Invalid compressor threshold: {:#06x}", raw_threshold),
)
validate_range(raw_threshold, THRESHOLD_RAW_RANGE, || {
format!("Invalid compressor threshold: {:#06x}", raw_threshold)
})
}
pub fn validate_raw_frequency(raw_freq: u16) -> Result<(), ValidationError> {
validate_range(
raw_freq,
FREQUENCY_RAW_RANGE,
|| format!("Invalid frequency: {:#06x}", raw_freq),
)
validate_range(raw_freq, FREQUENCY_RAW_RANGE, || {
format!("Invalid frequency: {:#06x}", raw_freq)
})
}
pub fn validate_raw_crossover_filter(raw_filter_type: u8) -> Result<(), ValidationError> {
validate_range(
raw_filter_type,
CROSSOVER_FILTER_RAW_RANGE,
|| format!("Invalid filter: {:#04x}", raw_filter_type),
)
validate_range(raw_filter_type, CROSSOVER_FILTER_RAW_RANGE, || {
format!("Invalid filter: {:#04x}", raw_filter_type)
})
}
pub fn validate_raw_peq_band(raw_channel: u8, raw_band: u8) -> Result<(), ValidationError> {
if INPUT_CHANNELS.contains(&raw_channel) {
validate_range(
raw_band,
PEQ_INPUT_BAND_RANGE,
|| format!("Invalid input PEQ band: {:#04x}", raw_band),
)?;
validate_range(raw_band, PEQ_INPUT_BAND_RANGE, || {
format!("Invalid input PEQ band: {:#04x}", raw_band)
})?;
} else if OUTPUT_CHANNELS.contains(&raw_channel) {
validate_range(
raw_band,
PEQ_OUTPUT_BAND_RANGE,
|| format!("Invalid output PEQ band: {:#04x}", raw_band),
)?;
validate_range(raw_band, PEQ_OUTPUT_BAND_RANGE, || {
format!("Invalid output PEQ band: {:#04x}", raw_band)
})?;
}
Ok(())
}
pub fn validate_raw_peq_q(raw_q: u8) -> Result<(), ValidationError> {
validate_range(
raw_q,
PEQ_Q_RAW_RANGE,
|| format!("Invalid PEQ Q: {:#04x}", raw_q),
)
validate_range(raw_q, PEQ_Q_RAW_RANGE, || {
format!("Invalid PEQ Q: {:#04x}", raw_q)
})
}
pub fn validate_raw_peq_filter(raw_filter_type: u8) -> Result<(), ValidationError> {
validate_range(
raw_filter_type,
PEQ_FILTER_RAW_RANGE,
|| format!("Invalid PEQ filter: {:#04x}", raw_filter_type),
)
validate_range(raw_filter_type, PEQ_FILTER_RAW_RANGE, || {
format!("Invalid PEQ filter: {:#04x}", raw_filter_type)
})
}
pub fn validate_raw_eq_gain(raw_gain: u16) -> Result<(), ValidationError> {
validate_range(
raw_gain,
EQ_GAIN_RAW_RANGE,
|| format!("Invalid EQ gain: {:#06x}", raw_gain),
)
validate_range(raw_gain, EQ_GAIN_RAW_RANGE, || {
format!("Invalid EQ gain: {:#06x}", raw_gain)
})
}
pub fn validate_raw_gain(raw_gain: u16) -> Result<(), ValidationError> {
validate_range(
raw_gain,
CHANNEL_GAIN_RAW_RANGE,
|| format!("Invalid channel gain: {:#06x}", raw_gain),
)
validate_range(raw_gain, CHANNEL_GAIN_RAW_RANGE, || {
format!("Invalid channel gain: {:#06x}", raw_gain)
})
}
pub fn validate_raw_channel_delay(delay: u8) -> Result<(), ValidationError> {
validate_range(
delay,
DELAY_RAW_RANGE,
|| format!("Invalid channel delay: {:#04x}", delay)
)
validate_range(delay, DELAY_RAW_RANGE, || {
format!("Invalid channel delay: {:#04x}", delay)
})
}
pub fn validate_raw_input_source(input_type: u8) -> Result<(), ValidationError> {
validate_range(
input_type,
INPUT_SOURCE_TYPE_RAW_RANGE,
|| format!("Invalid source type: {:#04x}", input_type),
)
validate_range(input_type, INPUT_SOURCE_TYPE_RAW_RANGE, || {
format!("Invalid source type: {:#04x}", input_type)
})
}
pub fn validate_raw_discrete_frequency(frequency: u8) -> Result<(), ValidationError> {
validate_range(
frequency,
DISCRETE_FREQUENCY_RAW_RANGE,
|| format!("Invalid frequency: {:#04x}", frequency),
)
validate_range(frequency, DISCRETE_FREQUENCY_RAW_RANGE, || {
format!("Invalid frequency: {:#04x}", frequency)
})
}
pub fn validate_raw_matrix_bitmask(input_bitmask: u8) -> Result<(), ValidationError> {
validate_range(
input_bitmask,
MATRIX_INPUT_MASK_RANGE,
|| format!("Invalid matrix bitmask: {:#04x}", input_bitmask),
)
validate_range(input_bitmask, MATRIX_INPUT_MASK_RANGE, || {
format!("Invalid matrix bitmask: {:#04x}", input_bitmask)
})
}
pub fn validate_raw_link_channel(
@@ -273,20 +210,18 @@ pub fn validate_raw_link_channel(
output_link_mask(source_channel)
}
.ok_or_else(|| {
ValidationError::value(
format!("Invalid link source channel: {:#04x}", source_channel)
)
ValidationError::value(format!(
"Invalid link source channel: {:#04x}",
source_channel
))
})?;
validate_bitmask(
destination_channel_bitmask,
allowed_mask,
|| format!(
validate_bitmask(destination_channel_bitmask, allowed_mask, || {
format!(
"Invalid link bitmask {:#04x} for source channel {:#04x}",
destination_channel_bitmask,
source_channel
),
)
destination_channel_bitmask, source_channel
)
})
}
pub fn validate_raw_channel_name(raw_name: &[u8]) -> Result<(), ValidationError> {
@@ -299,95 +234,87 @@ pub fn validate_raw_channel_name(raw_name: &[u8]) -> Result<(), ValidationError>
}
pub fn validate_raw_gate_threshold(raw_threshold: u16) -> Result<(), ValidationError> {
validate_range(
raw_threshold,
GATE_THRESHOLD_RAW_RANGE,
|| format!("Invalid gate threshold: {:#06x}", raw_threshold),
)
validate_range(raw_threshold, GATE_THRESHOLD_RAW_RANGE, || {
format!("Invalid gate threshold: {:#06x}", raw_threshold)
})
}
pub fn validate_raw_hold(raw_hold: u16) -> Result<(), ValidationError> {
validate_range(
raw_hold,
HOLD_RAW_RANGE,
|| format!("Invalid hold: {:#06x}", raw_hold),
)
validate_range(raw_hold, HOLD_RAW_RANGE, || {
format!("Invalid hold: {:#06x}", raw_hold)
})
}
pub fn validate_raw_matrix_gain(raw_gain: u16) -> Result<(), ValidationError> {
validate_range(
raw_gain,
MATRIX_GAIN_RAW_RANGE,
|| format!("Invalid matrix gain: {:#06x}", raw_gain),
)
validate_range(raw_gain, MATRIX_GAIN_RAW_RANGE, || {
format!("Invalid matrix gain: {:#06x}", raw_gain)
})
}
pub fn validate_raw_bool(value: u8) -> Result<(), ValidationError> {
validate_range(
value,
0..=1,
|| format!("Invalid bool value: {:#04x}", value),
)
validate_range(value, 0..=1, || {
format!("Invalid bool value: {:#04x}", value)
})
}
#[cfg(test)]
mod tests {
use super::*;
// ---- validate_raw_delay_unit: 0x00..=0x03 ----
#[test]
fn delay_unit_accepts_low_and_high_bounds() {
assert!(validate_raw_delay_unit(0x00).is_ok());
assert!(validate_raw_delay_unit(0x03).is_ok());
}
#[test]
fn delay_unit_rejects_just_above_max() {
assert!(validate_raw_delay_unit(0x04).is_err());
}
// ---- validate_raw_preset_index: 0..=20 ----
#[test]
fn preset_index_accepts_bounds() {
assert!(validate_raw_preset_index(0).is_ok());
assert!(validate_raw_preset_index(PRESET_INDEX_MAX).is_ok());
}
#[test]
fn preset_index_rejects_above_max() {
assert!(validate_raw_preset_index(PRESET_INDEX_MAX + 1).is_err());
}
// ---- validate_raw_store_preset_index: 1..=20 (0 reserved, unlike get/plain) ----
#[test]
fn store_preset_index_rejects_zero() {
assert!(validate_raw_store_preset_index(0).is_err());
}
#[test]
fn store_preset_index_accepts_one_through_max() {
assert!(validate_raw_store_preset_index(1).is_ok());
assert!(validate_raw_store_preset_index(PRESET_INDEX_MAX).is_ok());
}
#[test]
fn store_preset_index_rejects_above_max() {
assert!(validate_raw_store_preset_index(PRESET_INDEX_MAX + 1).is_err());
}
// ---- validate_raw_get_preset_index: 0..20 (max itself excluded) ----
#[test]
fn get_preset_index_accepts_zero_and_just_below_max() {
assert!(validate_raw_get_preset_index(0).is_ok());
assert!(validate_raw_get_preset_index(PRESET_INDEX_MAX - 1).is_ok());
}
#[test]
fn get_preset_index_rejects_max_itself() {
// Unlike validate_raw_preset_index, this range is exclusive of PRESET_INDEX_MAX.
assert!(validate_raw_get_preset_index(PRESET_INDEX_MAX).is_err());
}
// ---- validate_raw_preset_name: 14 ascii bytes, space-padded ----
#[test]
fn preset_name_accepts_correctly_padded_ascii() {
@@ -395,13 +322,13 @@ mod tests {
name.resize(PRESET_NAME_LENGTH, PRESET_NAME_PADDING);
assert!(validate_raw_preset_name(&name).is_ok());
}
#[test]
fn preset_name_rejects_wrong_length() {
let name = b"TooShort".to_vec();
assert!(validate_raw_preset_name(&name).is_err());
}
#[test]
fn preset_name_rejects_non_ascii_byte() {
let mut name = b"Bad".to_vec();
@@ -409,86 +336,86 @@ mod tests {
name.resize(PRESET_NAME_LENGTH, PRESET_NAME_PADDING);
assert!(validate_raw_preset_name(&name).is_err());
}
// ---- validate_raw_config_chunk_index: 0..=0x1C ----
#[test]
fn config_chunk_index_accepts_bounds() {
assert!(validate_raw_config_chunk_index(0x00).is_ok());
assert!(validate_raw_config_chunk_index(CONFIG_CHUNK_INDEX_MAX).is_ok());
}
#[test]
fn config_chunk_index_rejects_above_max() {
assert!(validate_raw_config_chunk_index(CONFIG_CHUNK_INDEX_MAX + 1).is_err());
}
// ---- validate_raw_channel: 0..=0x0B ----
#[test]
fn channel_accepts_full_range() {
assert!(validate_raw_channel(0x00).is_ok());
assert!(validate_raw_channel(0x0B).is_ok());
}
#[test]
fn channel_rejects_above_max() {
assert!(validate_raw_channel(0x0C).is_err());
}
// ---- validate_raw_input_channel: 0..=0x03 ----
#[test]
fn input_channel_accepts_its_own_range() {
assert!(validate_raw_input_channel(0x00).is_ok());
assert!(validate_raw_input_channel(0x03).is_ok());
}
#[test]
fn input_channel_rejects_an_output_channel() {
assert!(validate_raw_input_channel(0x04).is_err());
}
// ---- validate_raw_output_channel: 0x04..=0x0B ----
#[test]
fn output_channel_accepts_its_own_range() {
assert!(validate_raw_output_channel(0x04).is_ok());
assert!(validate_raw_output_channel(0x0B).is_ok());
}
#[test]
fn output_channel_rejects_an_input_channel() {
assert!(validate_raw_output_channel(0x03).is_err());
}
// ---- validate_raw_copy_channels ----
#[test]
fn copy_channels_allows_input_to_input() {
assert!(validate_raw_copy_channels(0x00, 0x01).is_ok());
}
#[test]
fn copy_channels_allows_output_to_output() {
assert!(validate_raw_copy_channels(0x04, 0x05).is_ok());
}
#[test]
fn copy_channels_rejects_same_source_and_destination() {
assert!(validate_raw_copy_channels(0x01, 0x01).is_err());
}
#[test]
fn copy_channels_rejects_mixed_input_and_output() {
assert!(validate_raw_copy_channels(0x01, 0x05).is_err());
}
#[test]
fn copy_channels_rejects_out_of_range_source() {
assert!(validate_raw_copy_channels(0x0C, 0x01).is_err());
}
#[test]
fn copy_channels_rejects_out_of_range_destination() {
assert!(validate_raw_copy_channels(0x01, 0x0C).is_err());
}
// ---- validate_raw_password: 4 bytes, null-padded ----
#[test]
fn password_accepts_null_padded_ascii() {
@@ -496,128 +423,128 @@ mod tests {
pw.resize(PASSWORD_LENGTH, PASSWORD_PADDING);
assert!(validate_raw_password(&pw).is_ok());
}
#[test]
fn password_rejects_wrong_length() {
assert!(validate_raw_password(b"abcde").is_err());
}
#[test]
fn password_rejects_non_ascii_non_padding_byte() {
let pw = vec![b'a', b'b', 0x01, 0x00];
assert!(validate_raw_password(&pw).is_err());
}
// ---- validate_raw_compressor_ratio: 0..=0x000F ----
#[test]
fn compressor_ratio_accepts_bounds() {
assert!(validate_raw_compressor_ratio(0x0000).is_ok());
assert!(validate_raw_compressor_ratio(0x000F).is_ok());
}
#[test]
fn compressor_ratio_rejects_above_max() {
assert!(validate_raw_compressor_ratio(0x0010).is_err());
}
// ---- validate_raw_attack: 0..=0x03E6 ----
#[test]
fn attack_accepts_bounds() {
assert!(validate_raw_attack(0x0000).is_ok());
assert!(validate_raw_attack(0x03E6).is_ok());
}
#[test]
fn attack_rejects_above_max() {
assert!(validate_raw_attack(0x03E7).is_err());
}
// ---- validate_raw_release: 0x0009..=0x0BB7 ----
#[test]
fn release_rejects_below_min() {
assert!(validate_raw_release(0x0008).is_err());
}
#[test]
fn release_accepts_bounds() {
assert!(validate_raw_release(0x0009).is_ok());
assert!(validate_raw_release(0x0BB7).is_ok());
}
#[test]
fn release_rejects_above_max() {
assert!(validate_raw_release(0x0BB8).is_err());
}
// ---- validate_raw_compressor_knee: 0..=0x000C ----
#[test]
fn compressor_knee_accepts_bounds() {
assert!(validate_raw_compressor_knee(0x0000).is_ok());
assert!(validate_raw_compressor_knee(0x000C).is_ok());
}
#[test]
fn compressor_knee_rejects_above_max() {
assert!(validate_raw_compressor_knee(0x000D).is_err());
}
// ---- validate_raw_threshold: 0..=0x00DC ----
#[test]
fn threshold_accepts_bounds() {
assert!(validate_raw_threshold(0x0000).is_ok());
assert!(validate_raw_threshold(0x00DC).is_ok());
}
#[test]
fn threshold_rejects_above_max() {
assert!(validate_raw_threshold(0x00DD).is_err());
}
// ---- validate_raw_frequency: 0..=0x012C ----
#[test]
fn frequency_accepts_bounds() {
assert!(validate_raw_frequency(0x0000).is_ok());
assert!(validate_raw_frequency(0x012C).is_ok());
}
#[test]
fn frequency_rejects_above_max() {
assert!(validate_raw_frequency(0x012D).is_err());
}
// ---- validate_raw_crossover_filter: 0..=0x14 ----
#[test]
fn crossover_filter_accepts_bounds() {
assert!(validate_raw_crossover_filter(0x00).is_ok());
assert!(validate_raw_crossover_filter(0x14).is_ok());
}
#[test]
fn crossover_filter_rejects_above_max() {
assert!(validate_raw_crossover_filter(0x15).is_err());
}
// ---- validate_raw_peq_band: depends on whether channel is input or output ----
#[test]
fn peq_band_accepts_max_band_for_input_channel() {
assert!(validate_raw_peq_band(0x00, 0x07).is_ok()); // input max band
}
#[test]
fn peq_band_rejects_above_max_for_input_channel() {
assert!(validate_raw_peq_band(0x00, 0x08).is_err());
}
#[test]
fn peq_band_accepts_max_band_for_output_channel() {
assert!(validate_raw_peq_band(0x04, 0x08).is_ok()); // output max band (one higher than input)
}
#[test]
fn peq_band_rejects_above_max_for_output_channel() {
assert!(validate_raw_peq_band(0x04, 0x09).is_err());
}
#[test]
fn peq_band_is_unchecked_for_out_of_range_channel() {
// Neither branch fires when the channel is outside both INPUT_CHANNELS and
@@ -625,55 +552,55 @@ mod tests {
// This documents that behavior rather than asserting it's necessarily "correct".
assert!(validate_raw_peq_band(0x0C, 0xFF).is_ok());
}
// ---- validate_raw_peq_q: 0..=0x64 ----
#[test]
fn peq_q_accepts_bounds() {
assert!(validate_raw_peq_q(0x00).is_ok());
assert!(validate_raw_peq_q(0x64).is_ok());
}
#[test]
fn peq_q_rejects_above_max() {
assert!(validate_raw_peq_q(0x65).is_err());
}
// ---- validate_raw_peq_filter: 0..=0x08 ----
#[test]
fn peq_filter_accepts_bounds() {
assert!(validate_raw_peq_filter(0x00).is_ok());
assert!(validate_raw_peq_filter(0x08).is_ok());
}
#[test]
fn peq_filter_rejects_above_max() {
assert!(validate_raw_peq_filter(0x09).is_err());
}
// ---- validate_raw_eq_gain: 0..=0x00F0 ----
#[test]
fn eq_gain_accepts_bounds() {
assert!(validate_raw_eq_gain(0x0000).is_ok());
assert!(validate_raw_eq_gain(0x00F0).is_ok());
}
#[test]
fn eq_gain_rejects_above_max() {
assert!(validate_raw_eq_gain(0x00F1).is_err());
}
// ---- validate_raw_gain: 0..=0x0190 ----
#[test]
fn gain_accepts_bounds() {
assert!(validate_raw_gain(0x0000).is_ok());
assert!(validate_raw_gain(0x0190).is_ok());
}
#[test]
fn gain_rejects_above_max() {
assert!(validate_raw_gain(0x0191).is_err());
}
// ---- validate_raw_channel_delay: 0..=0xFF (the full u8 range) ----
#[test]
fn channel_delay_accepts_the_entire_u8_range() {
@@ -682,80 +609,80 @@ mod tests {
assert!(validate_raw_channel_delay(0x00).is_ok());
assert!(validate_raw_channel_delay(0xFF).is_ok());
}
// ---- validate_raw_input_source: 0..=0x03 ----
#[test]
fn input_source_accepts_bounds() {
assert!(validate_raw_input_source(0x00).is_ok());
assert!(validate_raw_input_source(0x03).is_ok());
}
#[test]
fn input_source_rejects_above_max() {
assert!(validate_raw_input_source(0x04).is_err());
}
// ---- validate_raw_discrete_frequency: 0..=0x1E ----
#[test]
fn discrete_frequency_accepts_bounds() {
assert!(validate_raw_discrete_frequency(0x00).is_ok());
assert!(validate_raw_discrete_frequency(0x1E).is_ok());
}
#[test]
fn discrete_frequency_rejects_above_max() {
assert!(validate_raw_discrete_frequency(0x1F).is_err());
}
// ---- validate_raw_matrix_bitmask: 0..=0x0F ----
#[test]
fn matrix_bitmask_accepts_bounds() {
assert!(validate_raw_matrix_bitmask(0x00).is_ok());
assert!(validate_raw_matrix_bitmask(0x0F).is_ok());
}
#[test]
fn matrix_bitmask_rejects_above_max() {
assert!(validate_raw_matrix_bitmask(0x10).is_err());
}
// ---- validate_raw_link_channel ----
#[test]
fn link_channel_input_a_accepts_full_allowed_mask() {
// Channel 0x00 (A) allows linking to A|B|C|D = 0x0F per input_link_mask.
assert!(validate_raw_link_channel(0x00, 0x0F).is_ok());
}
#[test]
fn link_channel_input_a_rejects_bit_outside_allowed_mask() {
assert!(validate_raw_link_channel(0x00, 0x10).is_err());
}
#[test]
fn link_channel_input_d_only_allows_its_own_bit() {
// Channel 0x03 (D) only allows mask 0x08 per input_link_mask.
assert!(validate_raw_link_channel(0x03, 0x08).is_ok());
assert!(validate_raw_link_channel(0x03, 0x04).is_err());
}
#[test]
fn link_channel_output_first_accepts_full_mask() {
// Channel 0x04 allows mask 0xFF per output_link_mask.
assert!(validate_raw_link_channel(0x04, 0xFF).is_ok());
}
#[test]
fn link_channel_output_last_only_allows_its_own_bit() {
// Channel 0x0B allows only mask 0x80 per output_link_mask.
assert!(validate_raw_link_channel(0x0B, 0x80).is_ok());
assert!(validate_raw_link_channel(0x0B, 0x01).is_err());
}
#[test]
fn link_channel_rejects_out_of_range_source_channel() {
assert!(validate_raw_link_channel(0x0C, 0x01).is_err());
}
// ---- validate_raw_channel_name: 8 ascii bytes, null-padded ----
#[test]
fn channel_name_accepts_correctly_padded_ascii() {
@@ -763,12 +690,12 @@ mod tests {
name.resize(CHANNEL_NAME_LENGTH, CHANNEL_NAME_PADDING);
assert!(validate_raw_channel_name(&name).is_ok());
}
#[test]
fn channel_name_rejects_wrong_length() {
assert!(validate_raw_channel_name(b"TooLongName").is_err());
}
#[test]
fn channel_name_rejects_non_ascii_byte() {
let mut name = b"Kick".to_vec();
@@ -776,38 +703,38 @@ mod tests {
name.resize(CHANNEL_NAME_LENGTH, CHANNEL_NAME_PADDING);
assert!(validate_raw_channel_name(&name).is_err());
}
// ---- validate_raw_gate_threshold: 0..=0x00B4 ----
#[test]
fn gate_threshold_accepts_bounds() {
assert!(validate_raw_gate_threshold(0x0000).is_ok());
assert!(validate_raw_gate_threshold(0x00B4).is_ok());
}
#[test]
fn gate_threshold_rejects_above_max() {
assert!(validate_raw_gate_threshold(0x00B5).is_err());
}
// ---- validate_raw_hold: 0..=0x03E6 ----
#[test]
fn hold_accepts_bounds() {
assert!(validate_raw_hold(0x0000).is_ok());
assert!(validate_raw_hold(0x03E6).is_ok());
}
#[test]
fn hold_rejects_above_max() {
assert!(validate_raw_hold(0x03E7).is_err());
}
// ---- validate_raw_matrix_gain: 0..=0x0118 ----
#[test]
fn matrix_gain_accepts_bounds() {
assert!(validate_raw_matrix_gain(0x0000).is_ok());
assert!(validate_raw_matrix_gain(0x0118).is_ok());
}
#[test]
fn matrix_gain_rejects_above_max() {
assert!(validate_raw_matrix_gain(0x0119).is_err());
+4 -10
View File
@@ -1,5 +1,5 @@
use std::{collections::HashMap, net::IpAddr};
use strum_macros::{EnumIter, EnumCount};
use strum_macros::{EnumCount, EnumIter};
// ------ Enums ------
@@ -354,19 +354,13 @@ pub struct DSPConfigState {
}
impl DSPConfigState {
pub fn input_state_mut(
&mut self,
channel: InputChannel,
) -> &mut InputChannelState {
pub fn input_state_mut(&mut self, channel: InputChannel) -> &mut InputChannelState {
self.input_states
.get_mut(&channel)
.expect("Invalid input channel state")
}
pub fn output_state_mut(
&mut self,
channel: OutputChannel,
) -> &mut OutputChannelState {
pub fn output_state_mut(&mut self, channel: OutputChannel) -> &mut OutputChannelState {
self.output_states
.get_mut(&channel)
.expect("Invalid output channel state")