Initial commit
This commit is contained in:
@@ -0,0 +1,858 @@
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::common::validator::{validate_ascii_padded, validate_range};
|
||||
use crate::dsp408::client::constants::{self as client_constants};
|
||||
use crate::dsp408::protocol::constants as protocol_constants;
|
||||
use crate::dsp408::protocol::validators;
|
||||
use crate::common::errors::{MapperError, DecodingError, ValidationError};
|
||||
use super::{encode_ascii, decode_ascii};
|
||||
|
||||
use crate::dsp408::client::types;
|
||||
|
||||
// ------ CHANNEL ------
|
||||
|
||||
fn channel_to_raw_value(channel: types::Channel) -> u8 {
|
||||
match channel {
|
||||
types::Channel::Input(types::InputChannel::InA) => 0x00,
|
||||
types::Channel::Input(types::InputChannel::InB) => 0x01,
|
||||
types::Channel::Input(types::InputChannel::InC) => 0x02,
|
||||
types::Channel::Input(types::InputChannel::InD) => 0x03,
|
||||
types::Channel::Output(types::OutputChannel::Out1) => 0x04,
|
||||
types::Channel::Output(types::OutputChannel::Out2) => 0x05,
|
||||
types::Channel::Output(types::OutputChannel::Out3) => 0x06,
|
||||
types::Channel::Output(types::OutputChannel::Out4) => 0x07,
|
||||
types::Channel::Output(types::OutputChannel::Out5) => 0x08,
|
||||
types::Channel::Output(types::OutputChannel::Out6) => 0x09,
|
||||
types::Channel::Output(types::OutputChannel::Out7) => 0x0A,
|
||||
types::Channel::Output(types::OutputChannel::Out8) => 0x0B,
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_to_channel_value(value: u8) -> Option<types::Channel> {
|
||||
match value {
|
||||
0x00 => Some(types::Channel::Input(types::InputChannel::InA)),
|
||||
0x01 => Some(types::Channel::Input(types::InputChannel::InB)),
|
||||
0x02 => Some(types::Channel::Input(types::InputChannel::InC)),
|
||||
0x03 => Some(types::Channel::Input(types::InputChannel::InD)),
|
||||
0x04 => Some(types::Channel::Output(types::OutputChannel::Out1)),
|
||||
0x05 => Some(types::Channel::Output(types::OutputChannel::Out2)),
|
||||
0x06 => Some(types::Channel::Output(types::OutputChannel::Out3)),
|
||||
0x07 => Some(types::Channel::Output(types::OutputChannel::Out4)),
|
||||
0x08 => Some(types::Channel::Output(types::OutputChannel::Out5)),
|
||||
0x09 => Some(types::Channel::Output(types::OutputChannel::Out6)),
|
||||
0x0A => Some(types::Channel::Output(types::OutputChannel::Out7)),
|
||||
0x0B => Some(types::Channel::Output(types::OutputChannel::Out8)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn channel_to_raw(channel: types::Channel) -> Result<u8, MapperError> {
|
||||
Ok(channel_to_raw_value(channel))
|
||||
}
|
||||
|
||||
pub fn raw_to_channel(value: u8) -> Result<types::Channel, MapperError> {
|
||||
validators::validate_raw_channel(value)?;
|
||||
|
||||
raw_to_channel_value(value)
|
||||
.ok_or_else(|| {
|
||||
MapperError::Decoding(
|
||||
DecodingError::new(
|
||||
format!("Unknown channel value: {:#04x}", value)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn input_channel_to_raw(channel: types::InputChannel) -> Result<u8, MapperError> {
|
||||
Ok(channel_to_raw_value(types::Channel::Input(channel)))
|
||||
}
|
||||
|
||||
pub fn output_channel_to_raw(channel: types::OutputChannel) -> Result<u8, MapperError> {
|
||||
Ok(channel_to_raw_value(types::Channel::Output(channel)))
|
||||
}
|
||||
|
||||
// ------ NAME ------
|
||||
|
||||
pub fn channel_name_to_raw(name: &str) -> Result<Vec<u8>, MapperError> {
|
||||
validate_ascii_padded(
|
||||
name.as_bytes(),
|
||||
client_constants::CHANNEL_NAME_LENGTH,
|
||||
client_constants::CHANNEL_NAME_PADDING,
|
||||
"Channel name",
|
||||
)?;
|
||||
|
||||
Ok(encode_ascii(name, client_constants::CHANNEL_NAME_LENGTH, client_constants::CHANNEL_NAME_PADDING)?)
|
||||
}
|
||||
|
||||
pub fn raw_to_channel_name(raw_name: &[u8]) -> Result<String, MapperError> {
|
||||
validators::validate_raw_channel_name(raw_name)?;
|
||||
|
||||
Ok(decode_ascii(raw_name, true)?)
|
||||
}
|
||||
|
||||
// ------ ATTACK ------
|
||||
|
||||
pub fn attack_ms_to_raw(value: u16) -> Result<u16, MapperError> {
|
||||
validate_range(
|
||||
value,
|
||||
client_constants::ATTACK_MS_RANGE,
|
||||
|| format!("Invalid attack: {:#06x}", value),
|
||||
)?;
|
||||
|
||||
Ok(value - 1)
|
||||
}
|
||||
|
||||
pub fn raw_to_attack_ms(value: u16) -> Result<u16, MapperError> {
|
||||
validators::validate_raw_attack(value)?;
|
||||
|
||||
Ok(value + 1)
|
||||
}
|
||||
|
||||
// ------ RELEASE ------
|
||||
|
||||
pub fn release_ms_to_raw(value: u16) -> Result<u16, MapperError> {
|
||||
validate_range(
|
||||
value,
|
||||
client_constants::RELEASE_MS_RANGE,
|
||||
|| format!("Invalid release: {:#06x}", value),
|
||||
)?;
|
||||
|
||||
Ok(value - 1)
|
||||
}
|
||||
|
||||
pub fn raw_to_release_ms(value: u16) -> Result<u16, MapperError> {
|
||||
validators::validate_raw_release(value)?;
|
||||
|
||||
Ok(value + 1)
|
||||
}
|
||||
|
||||
// ------ HOLD ------
|
||||
|
||||
pub fn hold_ms_to_raw(hold: u16) -> Result<u16, MapperError> {
|
||||
validate_range(
|
||||
hold,
|
||||
client_constants::HOLD_MS_RANGE,
|
||||
|| format!("Invalid hold: {:#06x}", hold),
|
||||
)?;
|
||||
|
||||
Ok(hold - 1)
|
||||
}
|
||||
|
||||
pub fn raw_to_hold_ms(hold: u16) -> Result<u16, MapperError> {
|
||||
validators::validate_raw_hold(hold)?;
|
||||
|
||||
Ok(hold + 1)
|
||||
}
|
||||
|
||||
// ------ GATE THRESHOLD ------
|
||||
|
||||
pub fn gate_threshold_db_to_raw(db: f32) -> Result<u16, MapperError> {
|
||||
validate_range(
|
||||
db,
|
||||
client_constants::GATE_THRESHOLD_DB_RANGE,
|
||||
|| format!("Invalid gate threshold: {db}"),
|
||||
)?;
|
||||
|
||||
let db = (db * 2.0).round() / 2.0;
|
||||
|
||||
Ok(((db - *client_constants::GATE_THRESHOLD_DB_RANGE.start()) * 2.0).round() as u16)
|
||||
}
|
||||
|
||||
pub fn raw_to_gate_threshold_db(value: u16) -> Result<f32, MapperError> {
|
||||
validators::validate_raw_gate_threshold(value)?;
|
||||
|
||||
Ok(*client_constants::GATE_THRESHOLD_DB_RANGE.start() + value as f32 / 2.0)
|
||||
}
|
||||
|
||||
// ------ EQ GAIN ------
|
||||
|
||||
fn eq_gain_raw_steps() -> f32 {
|
||||
(*protocol_constants::EQ_GAIN_RAW_RANGE.end()
|
||||
- *protocol_constants::EQ_GAIN_RAW_RANGE.start()) as f32
|
||||
}
|
||||
|
||||
fn eq_gain_step_db() -> f32 {
|
||||
(*client_constants::EQ_GAIN_DB_RANGE.end()
|
||||
- *client_constants::EQ_GAIN_DB_RANGE.start())
|
||||
/ eq_gain_raw_steps()
|
||||
}
|
||||
|
||||
fn eq_quantize(db: f32) -> f32 {
|
||||
(db / eq_gain_step_db()).round() * eq_gain_step_db()
|
||||
}
|
||||
|
||||
pub fn eq_gain_db_to_raw(gain: f32) -> Result<u16, MapperError> {
|
||||
validate_range(
|
||||
gain,
|
||||
client_constants::EQ_GAIN_DB_RANGE,
|
||||
|| format!("Invalid EQ gain: {gain}"),
|
||||
)?;
|
||||
|
||||
let gain = eq_quantize(gain);
|
||||
|
||||
Ok(
|
||||
*protocol_constants::EQ_GAIN_RAW_RANGE.start()
|
||||
+ ((gain - *client_constants::EQ_GAIN_DB_RANGE.start())
|
||||
/ eq_gain_step_db())
|
||||
.round() as u16
|
||||
)
|
||||
}
|
||||
|
||||
pub fn raw_to_eq_gain(value: u16) -> Result<f32, MapperError> {
|
||||
validators::validate_raw_eq_gain(value)?;
|
||||
|
||||
let gain =
|
||||
*client_constants::EQ_GAIN_DB_RANGE.start()
|
||||
+ (value - *protocol_constants::EQ_GAIN_RAW_RANGE.start()) as f32
|
||||
* eq_gain_step_db();
|
||||
|
||||
Ok(eq_quantize(gain))
|
||||
}
|
||||
|
||||
// ------ PEQ BAND ------
|
||||
|
||||
fn peq_band_to_raw_value(band: types::PEQBand) -> u8 {
|
||||
match band {
|
||||
types::PEQBand::B1 => 0,
|
||||
types::PEQBand::B2 => 1,
|
||||
types::PEQBand::B3 => 2,
|
||||
types::PEQBand::B4 => 3,
|
||||
types::PEQBand::B5 => 4,
|
||||
types::PEQBand::B6 => 5,
|
||||
types::PEQBand::B7 => 6,
|
||||
types::PEQBand::B8 => 7,
|
||||
types::PEQBand::B9 => 8,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peq_band_to_raw(band: types::PEQBand, channel: types::Channel) -> Result<u8, MapperError> {
|
||||
match channel {
|
||||
types::Channel::Input(_) => {
|
||||
if matches!(band, types::PEQBand::B9) {
|
||||
return Err(MapperError::Validation(ValidationError::Value(
|
||||
"PEQ Band B9 is not available on input channels".to_string()
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
types::Channel::Output(_) => {
|
||||
// All PEQ bands are allowed
|
||||
}
|
||||
}
|
||||
|
||||
Ok(peq_band_to_raw_value(band))
|
||||
}
|
||||
|
||||
// ------ GAIN ------
|
||||
|
||||
fn gain_split_raw() -> u16 {
|
||||
((client_constants::GAIN_SPLIT_DB - client_constants::GAIN_DB_RANGE.start()) / client_constants::GAIN_LOW_STEP_DB).round() as u16
|
||||
}
|
||||
|
||||
fn quantize_gain(db: f32) -> f32 {
|
||||
if db < client_constants::GAIN_SPLIT_DB {
|
||||
(db / client_constants::GAIN_LOW_STEP_DB).round() * client_constants::GAIN_LOW_STEP_DB
|
||||
} else {
|
||||
(db / client_constants::GAIN_HIGH_STEP_DB).round() * client_constants::GAIN_HIGH_STEP_DB
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gain_db_to_raw(gain: f32) -> Result<u16, MapperError> {
|
||||
validate_range(
|
||||
gain,
|
||||
client_constants::GAIN_DB_RANGE,
|
||||
|| format!("Invalid channel gain: {gain}"),
|
||||
)?;
|
||||
|
||||
let gain = quantize_gain(gain);
|
||||
|
||||
if gain < client_constants::GAIN_SPLIT_DB {
|
||||
Ok(((gain - client_constants::GAIN_DB_RANGE.start()) / client_constants::GAIN_LOW_STEP_DB).round() as u16)
|
||||
} else {
|
||||
Ok(gain_split_raw() + ((gain - client_constants::GAIN_SPLIT_DB) / client_constants::GAIN_HIGH_STEP_DB).round() as u16)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_to_gain(raw_gain: u16) -> Result<f32, MapperError> {
|
||||
validators::validate_raw_gain(raw_gain)?;
|
||||
|
||||
if raw_gain < gain_split_raw() {
|
||||
Ok(client_constants::GAIN_DB_RANGE.start() + raw_gain as f32 * client_constants::GAIN_LOW_STEP_DB)
|
||||
} else {
|
||||
Ok(client_constants::GAIN_SPLIT_DB + (raw_gain - gain_split_raw()) as f32 * client_constants::GAIN_HIGH_STEP_DB)
|
||||
}
|
||||
}
|
||||
|
||||
// ------ CONTIGUOUS FREQUENCY ------
|
||||
|
||||
pub fn frequency_hz_to_raw(hz: f32) -> Result<u16, MapperError> {
|
||||
|
||||
validate_range(
|
||||
hz,
|
||||
client_constants::FREQUENCY_HZ_RANGE,
|
||||
|| format!("Invalid frequency: {hz}"),
|
||||
)?;
|
||||
|
||||
let device_steps = (protocol_constants::FREQUENCY_RAW_RANGE.end() - protocol_constants::FREQUENCY_RAW_RANGE.start()) as f32;
|
||||
|
||||
let raw = ((hz / client_constants::FREQUENCY_HZ_RANGE.start()).ln() / (client_constants::FREQUENCY_HZ_RANGE.end() / client_constants::FREQUENCY_HZ_RANGE.start()).ln()
|
||||
* device_steps)
|
||||
.round();
|
||||
|
||||
Ok(protocol_constants::FREQUENCY_RAW_RANGE.start() + raw as u16)
|
||||
}
|
||||
|
||||
pub fn raw_to_frequency_hz(value: u16) -> Result<f32, MapperError> {
|
||||
validators::validate_raw_frequency(value)?;
|
||||
|
||||
let device_steps = (protocol_constants::FREQUENCY_RAW_RANGE.end() - protocol_constants::FREQUENCY_RAW_RANGE.start()) as f32;
|
||||
let normalized = (value - protocol_constants::FREQUENCY_RAW_RANGE.start()) as f32 / device_steps;
|
||||
|
||||
Ok(client_constants::FREQUENCY_HZ_RANGE.start() * (client_constants::FREQUENCY_HZ_RANGE.end() / client_constants::FREQUENCY_HZ_RANGE.start()).powf(normalized))
|
||||
}
|
||||
|
||||
// ------ DISCRETE FREQUENCY ------
|
||||
|
||||
fn discrete_frequency_to_raw_value(frequency: types::DiscreteFrequency) -> u8 {
|
||||
match frequency {
|
||||
types::DiscreteFrequency::F20 => 0,
|
||||
types::DiscreteFrequency::F25 => 1,
|
||||
types::DiscreteFrequency::F31_5 => 2,
|
||||
types::DiscreteFrequency::F40 => 3,
|
||||
types::DiscreteFrequency::F50 => 4,
|
||||
types::DiscreteFrequency::F63 => 5,
|
||||
types::DiscreteFrequency::F80 => 6,
|
||||
types::DiscreteFrequency::F100 => 7,
|
||||
types::DiscreteFrequency::F125 => 8,
|
||||
types::DiscreteFrequency::F160 => 9,
|
||||
types::DiscreteFrequency::F200 => 10,
|
||||
types::DiscreteFrequency::F250 => 11,
|
||||
types::DiscreteFrequency::F315 => 12,
|
||||
types::DiscreteFrequency::F400 => 13,
|
||||
types::DiscreteFrequency::F500 => 14,
|
||||
types::DiscreteFrequency::F630 => 15,
|
||||
types::DiscreteFrequency::F800 => 16,
|
||||
types::DiscreteFrequency::F1000 => 17,
|
||||
types::DiscreteFrequency::F1250 => 18,
|
||||
types::DiscreteFrequency::F1600 => 19,
|
||||
types::DiscreteFrequency::F2000 => 20,
|
||||
types::DiscreteFrequency::F2500 => 21,
|
||||
types::DiscreteFrequency::F3150 => 22,
|
||||
types::DiscreteFrequency::F4000 => 23,
|
||||
types::DiscreteFrequency::F5000 => 24,
|
||||
types::DiscreteFrequency::F6300 => 25,
|
||||
types::DiscreteFrequency::F8000 => 26,
|
||||
types::DiscreteFrequency::F10000 => 27,
|
||||
types::DiscreteFrequency::F12500 => 28,
|
||||
types::DiscreteFrequency::F16000 => 29,
|
||||
types::DiscreteFrequency::F20000 => 30,
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_to_discrete_frequency_value(raw: u8) -> Option<types::DiscreteFrequency> {
|
||||
Some(match raw {
|
||||
0 => types::DiscreteFrequency::F20,
|
||||
1 => types::DiscreteFrequency::F25,
|
||||
2 => types::DiscreteFrequency::F31_5,
|
||||
3 => types::DiscreteFrequency::F40,
|
||||
4 => types::DiscreteFrequency::F50,
|
||||
5 => types::DiscreteFrequency::F63,
|
||||
6 => types::DiscreteFrequency::F80,
|
||||
7 => types::DiscreteFrequency::F100,
|
||||
8 => types::DiscreteFrequency::F125,
|
||||
9 => types::DiscreteFrequency::F160,
|
||||
10 => types::DiscreteFrequency::F200,
|
||||
11 => types::DiscreteFrequency::F250,
|
||||
12 => types::DiscreteFrequency::F315,
|
||||
13 => types::DiscreteFrequency::F400,
|
||||
14 => types::DiscreteFrequency::F500,
|
||||
15 => types::DiscreteFrequency::F630,
|
||||
16 => types::DiscreteFrequency::F800,
|
||||
17 => types::DiscreteFrequency::F1000,
|
||||
18 => types::DiscreteFrequency::F1250,
|
||||
19 => types::DiscreteFrequency::F1600,
|
||||
20 => types::DiscreteFrequency::F2000,
|
||||
21 => types::DiscreteFrequency::F2500,
|
||||
22 => types::DiscreteFrequency::F3150,
|
||||
23 => types::DiscreteFrequency::F4000,
|
||||
24 => types::DiscreteFrequency::F5000,
|
||||
25 => types::DiscreteFrequency::F6300,
|
||||
26 => types::DiscreteFrequency::F8000,
|
||||
27 => types::DiscreteFrequency::F10000,
|
||||
28 => types::DiscreteFrequency::F12500,
|
||||
29 => types::DiscreteFrequency::F16000,
|
||||
30 => types::DiscreteFrequency::F20000,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn frequency_value_to_raw(frequency: types::DiscreteFrequency) -> Result<u8, MapperError> {
|
||||
|
||||
Ok(discrete_frequency_to_raw_value(frequency))
|
||||
}
|
||||
|
||||
pub fn raw_to_frequency_value(frequency: u8) -> Result<types::DiscreteFrequency, MapperError> {
|
||||
validators::validate_raw_discrete_frequency(frequency)?;
|
||||
|
||||
raw_to_discrete_frequency_value(frequency)
|
||||
.ok_or_else(|| {
|
||||
MapperError::Decoding(
|
||||
DecodingError::new(
|
||||
format!("Unknown frequency value: {:#04x}", frequency)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// ------ CROSSOVER ------
|
||||
|
||||
fn crossover_filter_to_raw_value(filter: types::CrossoverFilter) -> u8 {
|
||||
match filter {
|
||||
types::CrossoverFilter::Bypass => 0,
|
||||
types::CrossoverFilter::Bw6 => 1,
|
||||
types::CrossoverFilter::Bl6 => 2,
|
||||
types::CrossoverFilter::Bw12 => 3,
|
||||
types::CrossoverFilter::Bl12 => 4,
|
||||
types::CrossoverFilter::Lk12 => 5,
|
||||
types::CrossoverFilter::Bw18 => 6,
|
||||
types::CrossoverFilter::Bl18 => 7,
|
||||
types::CrossoverFilter::Bw24 => 8,
|
||||
types::CrossoverFilter::Bl24 => 9,
|
||||
types::CrossoverFilter::Lk24 => 10,
|
||||
types::CrossoverFilter::Bw30 => 11,
|
||||
types::CrossoverFilter::Bl30 => 12,
|
||||
types::CrossoverFilter::Bw36 => 13,
|
||||
types::CrossoverFilter::Bl36 => 14,
|
||||
types::CrossoverFilter::Lk36 => 15,
|
||||
types::CrossoverFilter::Bw42 => 16,
|
||||
types::CrossoverFilter::Bl42 => 17,
|
||||
types::CrossoverFilter::Bw48 => 18,
|
||||
types::CrossoverFilter::Bl48 => 19,
|
||||
types::CrossoverFilter::Lk48 => 20,
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_to_crossover_filter_value(raw: u8) -> Option<types::CrossoverFilter> {
|
||||
Some(match raw {
|
||||
0 => types::CrossoverFilter::Bypass,
|
||||
1 => types::CrossoverFilter::Bw6,
|
||||
2 => types::CrossoverFilter::Bl6,
|
||||
3 => types::CrossoverFilter::Bw12,
|
||||
4 => types::CrossoverFilter::Bl12,
|
||||
5 => types::CrossoverFilter::Lk12,
|
||||
6 => types::CrossoverFilter::Bw18,
|
||||
7 => types::CrossoverFilter::Bl18,
|
||||
8 => types::CrossoverFilter::Bw24,
|
||||
9 => types::CrossoverFilter::Bl24,
|
||||
10 => types::CrossoverFilter::Lk24,
|
||||
11 => types::CrossoverFilter::Bw30,
|
||||
12 => types::CrossoverFilter::Bl30,
|
||||
13 => types::CrossoverFilter::Bw36,
|
||||
14 => types::CrossoverFilter::Bl36,
|
||||
15 => types::CrossoverFilter::Lk36,
|
||||
16 => types::CrossoverFilter::Bw42,
|
||||
17 => types::CrossoverFilter::Bl42,
|
||||
18 => types::CrossoverFilter::Bw48,
|
||||
19 => types::CrossoverFilter::Bl48,
|
||||
20 => types::CrossoverFilter::Lk48,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn crossover_filter_to_raw(filter: types::CrossoverFilter) -> Result<u8, MapperError> {
|
||||
Ok(crossover_filter_to_raw_value(filter))
|
||||
}
|
||||
|
||||
pub fn raw_to_crossover_filter(raw: u8) -> Result<types::CrossoverFilter, MapperError> {
|
||||
validators::validate_raw_crossover_filter(raw)?;
|
||||
|
||||
raw_to_crossover_filter_value(raw)
|
||||
.ok_or_else(|| {
|
||||
MapperError::Decoding(
|
||||
DecodingError::new(
|
||||
format!("Unknown crossover filter value: {:#04x}", raw)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// ------ PEQ Q ------
|
||||
|
||||
pub fn peq_q_to_raw(q: f32, filter: types::PEQFilter) -> Result<u8, MapperError> {
|
||||
validate_range(
|
||||
q,
|
||||
client_constants::peq_q_range(filter),
|
||||
|| format!("Invalid PEQ Q: {q}"),
|
||||
)?;
|
||||
|
||||
let raw_range = (protocol_constants::PEQ_Q_RAW_RANGE.end() - protocol_constants::PEQ_Q_RAW_RANGE.start()) as f32;
|
||||
|
||||
Ok(((q / client_constants::PEQ_Q_RANGE.start()).ln() / (client_constants::PEQ_Q_RANGE.end() / client_constants::PEQ_Q_RANGE.start()).ln() * raw_range).round() as u8
|
||||
+ protocol_constants::PEQ_Q_RAW_RANGE.start())
|
||||
}
|
||||
|
||||
pub fn raw_to_peq_q(value: u8) -> Result<f32, MapperError> {
|
||||
validators::validate_raw_peq_q(value)?;
|
||||
|
||||
let raw_range = (protocol_constants::PEQ_Q_RAW_RANGE.end() - protocol_constants::PEQ_Q_RAW_RANGE.start()) as f32;
|
||||
let normalized = (value - protocol_constants::PEQ_Q_RAW_RANGE.start()) as f32 / raw_range;
|
||||
|
||||
Ok(client_constants::PEQ_Q_RANGE.start() * (client_constants::PEQ_Q_RANGE.end() / client_constants::PEQ_Q_RANGE.start()).powf(normalized))
|
||||
}
|
||||
|
||||
// ------ FILTER ------
|
||||
|
||||
fn peq_filter_to_raw_value(filter: types::PEQFilter) -> u8 {
|
||||
match filter {
|
||||
types::PEQFilter::Peak => 0,
|
||||
types::PEQFilter::LowShelf => 1,
|
||||
types::PEQFilter::HighShelf => 2,
|
||||
types::PEQFilter::Lp6Db => 3,
|
||||
types::PEQFilter::Lp12Db => 4,
|
||||
types::PEQFilter::Hp6Db => 5,
|
||||
types::PEQFilter::Hp12Db => 6,
|
||||
types::PEQFilter::AllPass1 => 7,
|
||||
types::PEQFilter::AllPass2 => 8,
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_to_peq_filter_value(value: u8) -> Option<types::PEQFilter> {
|
||||
Some(match value {
|
||||
0 => types::PEQFilter::Peak,
|
||||
1 => types::PEQFilter::LowShelf,
|
||||
2 => types::PEQFilter::HighShelf,
|
||||
3 => types::PEQFilter::Lp6Db,
|
||||
4 => types::PEQFilter::Lp12Db,
|
||||
5 => types::PEQFilter::Hp6Db,
|
||||
6 => types::PEQFilter::Hp12Db,
|
||||
7 => types::PEQFilter::AllPass1,
|
||||
8 => types::PEQFilter::AllPass2,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn peq_filter_to_raw(filter: types::PEQFilter) -> Result<u8, MapperError> {
|
||||
Ok(peq_filter_to_raw_value(filter))
|
||||
}
|
||||
|
||||
pub fn raw_to_peq_filter(value: u8) -> Result<types::PEQFilter, MapperError> {
|
||||
validators::validate_raw_peq_filter(value)?;
|
||||
|
||||
raw_to_peq_filter_value(value)
|
||||
.ok_or_else(|| {
|
||||
MapperError::Decoding(
|
||||
DecodingError::new(
|
||||
format!("Unknown PEQ filter value: {:#04x}", value)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// ------ RATIO ------
|
||||
|
||||
fn ratio_to_raw_value(ratio: types::Ratio) -> u16 {
|
||||
match ratio {
|
||||
types::Ratio::Ratio1_1 => 0,
|
||||
types::Ratio::Ratio1_1_1 => 1,
|
||||
types::Ratio::Ratio1_1_3 => 2,
|
||||
types::Ratio::Ratio1_1_5 => 3,
|
||||
types::Ratio::Ratio1_1_7 => 4,
|
||||
types::Ratio::Ratio1_2 => 5,
|
||||
types::Ratio::Ratio1_2_5 => 6,
|
||||
types::Ratio::Ratio1_3 => 7,
|
||||
types::Ratio::Ratio1_3_5 => 8,
|
||||
types::Ratio::Ratio1_4 => 9,
|
||||
types::Ratio::Ratio1_5 => 10,
|
||||
types::Ratio::Ratio1_6 => 11,
|
||||
types::Ratio::Ratio1_8 => 12,
|
||||
types::Ratio::Ratio1_10 => 13,
|
||||
types::Ratio::Ratio1_20 => 14,
|
||||
types::Ratio::Limit => 15,
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_to_ratio_value(value: u16) -> Option<types::Ratio> {
|
||||
Some(match value {
|
||||
0 => types::Ratio::Ratio1_1,
|
||||
1 => types::Ratio::Ratio1_1_1,
|
||||
2 => types::Ratio::Ratio1_1_3,
|
||||
3 => types::Ratio::Ratio1_1_5,
|
||||
4 => types::Ratio::Ratio1_1_7,
|
||||
5 => types::Ratio::Ratio1_2,
|
||||
6 => types::Ratio::Ratio1_2_5,
|
||||
7 => types::Ratio::Ratio1_3,
|
||||
8 => types::Ratio::Ratio1_3_5,
|
||||
9 => types::Ratio::Ratio1_4,
|
||||
10 => types::Ratio::Ratio1_5,
|
||||
11 => types::Ratio::Ratio1_6,
|
||||
12 => types::Ratio::Ratio1_8,
|
||||
13 => types::Ratio::Ratio1_10,
|
||||
14 => types::Ratio::Ratio1_20,
|
||||
15 => types::Ratio::Limit,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ratio_to_raw(ratio: types::Ratio) -> Result<u16, MapperError> {
|
||||
Ok(ratio_to_raw_value(ratio))
|
||||
}
|
||||
|
||||
pub fn raw_to_ratio(value: u16) -> Result<types::Ratio, MapperError> {
|
||||
validators::validate_raw_compressor_ratio(value)?;
|
||||
|
||||
raw_to_ratio_value(value)
|
||||
.ok_or_else(|| {
|
||||
MapperError::Decoding(
|
||||
DecodingError::new(
|
||||
format!("Unknown ratio value: {:#04x}", value)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// ------ KNEE ------
|
||||
|
||||
pub fn knee_db_to_raw(db: u16) -> Result<u16, MapperError> {
|
||||
validate_range(
|
||||
db,
|
||||
client_constants::COMPRESSOR_KNEE_DB_RANGE,
|
||||
|| format!("Invalid compressor knee: {db}"),
|
||||
)?;
|
||||
|
||||
Ok(db)
|
||||
}
|
||||
|
||||
pub fn raw_to_knee_db(value: u16) -> Result<u16, MapperError> {
|
||||
validators::validate_raw_compressor_knee(value)?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
// ------ THRESHOLD ------
|
||||
|
||||
pub fn threshold_db_to_raw(db: f32) -> Result<u16, MapperError> {
|
||||
validate_range(
|
||||
db,
|
||||
client_constants::THRESHOLD_DB_RANGE,
|
||||
|| format!("Invalid compressor threshold: {db}"),
|
||||
)?;
|
||||
|
||||
let db = (db * 2.0).round() / 2.0;
|
||||
|
||||
Ok(((db - client_constants::THRESHOLD_DB_RANGE.start()) * 2.0).round() as u16)
|
||||
}
|
||||
|
||||
pub fn raw_to_threshold_db(value: u16) -> Result<f32, MapperError> {
|
||||
validators::validate_raw_threshold(value)?;
|
||||
Ok(client_constants::THRESHOLD_DB_RANGE.start() + (value as f32 / 2.0))
|
||||
}
|
||||
|
||||
// ------ LINK ------
|
||||
|
||||
pub fn link_channels_to_raw(
|
||||
source: types::Channel,
|
||||
destinations: &[types::Channel],
|
||||
) -> Result<u8, MapperError> {
|
||||
|
||||
let mut mask = 0u8;
|
||||
|
||||
match source {
|
||||
types::Channel::Input(source_channel) => {
|
||||
for destination in destinations {
|
||||
let types::Channel::Input(destination_channel) = destination else {
|
||||
return Err(MapperError::Validation(
|
||||
ValidationError::value(
|
||||
"Input channel cannot link to output channel"
|
||||
),
|
||||
));
|
||||
};
|
||||
|
||||
if *destination_channel < source_channel {
|
||||
return Err(MapperError::Validation(
|
||||
ValidationError::value(format!(
|
||||
"{:?} cannot link to previous channel {:?}",
|
||||
source_channel,
|
||||
destination_channel
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
mask |= 1 << (*destination_channel as u8);
|
||||
}
|
||||
}
|
||||
|
||||
types::Channel::Output(source_channel) => {
|
||||
for destination in destinations {
|
||||
let types::Channel::Output(destination_channel) = destination else {
|
||||
return Err(MapperError::Validation(
|
||||
ValidationError::value(
|
||||
"Output channel cannot link to input channel"
|
||||
),
|
||||
));
|
||||
};
|
||||
|
||||
if *destination_channel < source_channel {
|
||||
return Err(MapperError::Validation(
|
||||
ValidationError::value(format!(
|
||||
"{:?} cannot link to previous channel {:?}",
|
||||
source_channel,
|
||||
destination_channel
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
mask |= 1 << (*destination_channel as u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mask)
|
||||
}
|
||||
|
||||
pub fn raw_to_link_channels(source_raw: u8, destination_mask: u8) -> Result<Vec<types::Channel>, MapperError> {
|
||||
validators::validate_raw_link_channel(source_raw, destination_mask)?;
|
||||
|
||||
let source = raw_to_channel(source_raw)?;
|
||||
|
||||
let mut destinations = Vec::new();
|
||||
|
||||
match source {
|
||||
types::Channel::Input(_) => {
|
||||
for (index, channel) in types::InputChannel::iter().enumerate() {
|
||||
if destination_mask & (1 << index) != 0 {
|
||||
destinations.push(types::Channel::Input(channel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
types::Channel::Output(_) => {
|
||||
for (index, channel) in types::OutputChannel::iter().enumerate() {
|
||||
if destination_mask & (1 << index) != 0 {
|
||||
destinations.push(types::Channel::Output(channel));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(destinations)
|
||||
}
|
||||
|
||||
// ------ MATRIX GAIN ------
|
||||
|
||||
fn matrix_gain_split_raw() -> u16 {
|
||||
((client_constants::GAIN_SPLIT_DB - client_constants::MATRIX_GAIN_DB_RANGE.start()) / client_constants::GAIN_LOW_STEP_DB).round() as u16
|
||||
}
|
||||
|
||||
fn quantize_matrix_gain(db: f32) -> f32 {
|
||||
if db < client_constants::GAIN_SPLIT_DB {
|
||||
(db / client_constants::GAIN_LOW_STEP_DB).round() * client_constants::GAIN_LOW_STEP_DB
|
||||
} else {
|
||||
(db / client_constants::GAIN_HIGH_STEP_DB).round() * client_constants::GAIN_HIGH_STEP_DB
|
||||
}
|
||||
}
|
||||
|
||||
pub fn matrix_gain_db_to_raw(gain: f32) -> Result<u16, MapperError> {
|
||||
validate_range(
|
||||
gain,
|
||||
client_constants::MATRIX_GAIN_DB_RANGE,
|
||||
|| format!("Invalid matrix gain: {gain}"),
|
||||
)?;
|
||||
|
||||
let gain = quantize_matrix_gain(gain);
|
||||
|
||||
if gain < client_constants::GAIN_SPLIT_DB {
|
||||
Ok(((gain - client_constants::MATRIX_GAIN_DB_RANGE.start()) / client_constants::GAIN_LOW_STEP_DB).round() as u16)
|
||||
} else {
|
||||
Ok(matrix_gain_split_raw()
|
||||
+ ((gain - client_constants::GAIN_SPLIT_DB) / client_constants::GAIN_HIGH_STEP_DB).round() as u16)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_to_matrix_gain(raw_gain: u16) -> Result<f32, MapperError> {
|
||||
validators::validate_raw_matrix_gain(raw_gain)?;
|
||||
|
||||
if raw_gain < matrix_gain_split_raw() {
|
||||
Ok(client_constants::MATRIX_GAIN_DB_RANGE.start() + raw_gain as f32 * client_constants::GAIN_LOW_STEP_DB)
|
||||
} else {
|
||||
Ok(client_constants::GAIN_SPLIT_DB + (raw_gain - matrix_gain_split_raw()) as f32 * client_constants::GAIN_HIGH_STEP_DB)
|
||||
}
|
||||
}
|
||||
|
||||
// ------ MATRIX ------
|
||||
|
||||
fn matrix_input_mask(channel: types::InputChannel) -> u8 {
|
||||
match channel {
|
||||
types::InputChannel::InA => protocol_constants::MATRIX_INPUT_A_MASK,
|
||||
types::InputChannel::InB => protocol_constants::MATRIX_INPUT_B_MASK,
|
||||
types::InputChannel::InC => protocol_constants::MATRIX_INPUT_C_MASK,
|
||||
types::InputChannel::InD => protocol_constants::MATRIX_INPUT_D_MASK,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn matrix_to_raw(input_channels: &[types::InputChannel]) -> Result<u8, MapperError> {
|
||||
let mut bitmask = 0x00;
|
||||
|
||||
for &channel in input_channels {
|
||||
bitmask |= matrix_input_mask(channel);
|
||||
}
|
||||
|
||||
Ok(bitmask)
|
||||
}
|
||||
|
||||
pub fn raw_to_matrix(raw: u8) -> Result<Vec<types::InputChannel>, MapperError> {
|
||||
validators::validate_raw_matrix_bitmask(raw)?;
|
||||
|
||||
Ok([types::InputChannel::InA, types::InputChannel::InB, types::InputChannel::InC, types::InputChannel::InD]
|
||||
.into_iter()
|
||||
.filter(|&c| raw & matrix_input_mask(c) != 0)
|
||||
.collect())
|
||||
}
|
||||
|
||||
// ------ DELAY ------
|
||||
|
||||
fn delay_raw_steps() -> f32 {
|
||||
(protocol_constants::DELAY_RAW_RANGE.end() - protocol_constants::DELAY_RAW_RANGE.start()) as f32
|
||||
}
|
||||
|
||||
pub fn delay_to_raw(delay: f32, unit: types::DelayUnit) -> Result<u8, MapperError> {
|
||||
|
||||
let (delay_min, delay_max) = client_constants::delay_range(unit);
|
||||
|
||||
validate_range(
|
||||
delay,
|
||||
delay_min..=delay_max,
|
||||
|| format!("Invalid channel delay: {delay}")
|
||||
)?;
|
||||
|
||||
let raw = *protocol_constants::DELAY_RAW_RANGE.start() as f32
|
||||
+ ((delay - delay_min) / (delay_max - delay_min) * delay_raw_steps()).round();
|
||||
|
||||
Ok(raw as u8)
|
||||
}
|
||||
|
||||
pub fn raw_to_delay(value: u8, unit: types::DelayUnit) -> Result<f32, MapperError> {
|
||||
validators::validate_raw_channel_delay(value)?;
|
||||
|
||||
let (delay_min, delay_max) = client_constants::delay_range(unit);
|
||||
|
||||
Ok(delay_min + ((value - protocol_constants::DELAY_RAW_RANGE.start()) as f32 / delay_raw_steps()) * (delay_max - delay_min))
|
||||
}
|
||||
|
||||
// ------ COPY CHANNEL ------
|
||||
|
||||
pub fn copy_channel_to_raw(source: types::Channel, destination: types::Channel) -> Result<(u8, u8), MapperError> {
|
||||
if source == destination {
|
||||
return Err(ValidationError::Value("Source channel and destination channel can't be the same".to_string()).into())
|
||||
}
|
||||
|
||||
match (&source, &destination) {
|
||||
(types::Channel::Input(_), types::Channel::Output(_))
|
||||
| (types::Channel::Output(_), types::Channel::Input(_)) => {
|
||||
return Err(ValidationError::Value(
|
||||
"Channels need to be both inputs or outputs".to_string()).into(),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok((channel_to_raw_value(source), channel_to_raw_value(destination)))
|
||||
}
|
||||
Reference in New Issue
Block a user