+1
-1
@@ -1,2 +1,2 @@
|
||||
pub mod errors;
|
||||
pub(crate) mod validator;
|
||||
pub(crate) mod validator;
|
||||
|
||||
+9
-49
@@ -66,79 +66,39 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn validate_range_rejects_out_of_range() {
|
||||
let err = validate_range(11u8, 0..=10, || "bad value".into())
|
||||
.unwrap_err();
|
||||
let err = validate_range(11u8, 0..=10, || "bad value".into()).unwrap_err();
|
||||
|
||||
assert!(err.to_string().contains("bad value"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_ascii_padded_accepts_ascii_and_padding() {
|
||||
assert!(validate_ascii_padded(
|
||||
b"ABC ",
|
||||
4,
|
||||
b' ',
|
||||
"name",
|
||||
)
|
||||
.is_ok());
|
||||
assert!(validate_ascii_padded(b"ABC ", 4, b' ', "name",).is_ok());
|
||||
|
||||
assert!(validate_ascii_padded(
|
||||
&[b'A', 0, 0, 0],
|
||||
4,
|
||||
0,
|
||||
"password",
|
||||
)
|
||||
.is_ok());
|
||||
assert!(validate_ascii_padded(&[b'A', 0, 0, 0], 4, 0, "password",).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_ascii_padded_rejects_wrong_length() {
|
||||
assert!(validate_ascii_padded(
|
||||
b"ABC",
|
||||
4,
|
||||
b' ',
|
||||
"name",
|
||||
)
|
||||
.is_err());
|
||||
assert!(validate_ascii_padded(b"ABC", 4, b' ', "name",).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_ascii_padded_rejects_non_ascii() {
|
||||
assert!(validate_ascii_padded(
|
||||
&[0xFF, b'A', b'B', b'C'],
|
||||
4,
|
||||
b' ',
|
||||
"name",
|
||||
)
|
||||
.is_err());
|
||||
assert!(validate_ascii_padded(&[0xFF, b'A', b'B', b'C'], 4, b' ', "name",).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_bitmask_accepts_valid_bits() {
|
||||
assert!(validate_bitmask(
|
||||
0b0101,
|
||||
0b1111,
|
||||
|| "".into(),
|
||||
)
|
||||
.is_ok());
|
||||
assert!(validate_bitmask(0b0101, 0b1111, || "".into(),).is_ok());
|
||||
|
||||
assert!(validate_bitmask(
|
||||
0,
|
||||
0b1111,
|
||||
|| "".into(),
|
||||
)
|
||||
.is_ok());
|
||||
assert!(validate_bitmask(0, 0b1111, || "".into(),).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_bitmask_rejects_invalid_bits() {
|
||||
let err = validate_bitmask(
|
||||
0b10000,
|
||||
0b01111,
|
||||
|| "invalid mask".into(),
|
||||
)
|
||||
.unwrap_err();
|
||||
let err = validate_bitmask(0b10000, 0b01111, || "invalid mask".into()).unwrap_err();
|
||||
|
||||
assert!(err.to_string().contains("invalid mask"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+120
-349
@@ -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,2 +1,2 @@
|
||||
mod dsp408;
|
||||
pub use dsp408::DSP408;
|
||||
pub use dsp408::DSP408;
|
||||
|
||||
+384
-335
File diff suppressed because it is too large
Load Diff
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
mod common;
|
||||
pub mod dsp408;
|
||||
|
||||
pub use common::errors::DSPError;
|
||||
pub use common::errors::DSPError;
|
||||
|
||||
+2
-8
@@ -5,14 +5,9 @@ use dsp_thomann::dsp408::DSP408;
|
||||
use dsp_thomann::dsp408::types;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let host = IpAddr::from_str("192.168.49.30")?;
|
||||
|
||||
let mut dsp = DSP408::new(
|
||||
host,
|
||||
9761,
|
||||
1,
|
||||
)?;
|
||||
let mut dsp = DSP408::new(host, 9761, 1)?;
|
||||
|
||||
println!("Connecting to DSP...");
|
||||
match dsp.connect() {
|
||||
@@ -35,6 +30,5 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
dsp.disconnect();
|
||||
println!("Disconnected");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user