use crate::error::{Error, Result};
use crate::params::{
find_parameter, Access, ParamKind, ParamValue, Parameter, PARAMETERS,
};
use crate::{TUNING_TIMEOUT, VENDOR_IN, VENDOR_OUT};
use rusb::{DeviceHandle, UsbContext};
pub struct Tuning<'a, T: UsbContext> {
handle: &'a DeviceHandle<T>,
}
impl<'a, T: UsbContext> Tuning<'a, T> {
pub(crate) fn new(handle: &'a DeviceHandle<T>) -> Self {
Self { handle }
}
pub fn parameters(&self) -> &'static [Parameter] {
PARAMETERS
}
pub fn version(&self) -> Result<u8> {
let mut buf = [0u8; 1];
self.handle
.read_control(VENDOR_IN, 0, 0x80, 0, &mut buf, TUNING_TIMEOUT)?;
Ok(buf[0])
}
pub fn doa_angle(&self) -> Result<i32> {
match self.read("DOAANGLE")? {
ParamValue::Int(v) => Ok(v),
ParamValue::Float(v) => Ok(v as i32),
}
}
pub fn is_voice(&self) -> Result<bool> {
Ok(self.read("VOICEACTIVITY")?.as_i32().unwrap_or(0) != 0)
}
pub fn set_vad_threshold(&self, db: f64) -> Result<()> {
self.write("GAMMAVAD_SR", ParamValue::Float(db))
}
pub fn read(&self, name: &str) -> Result<ParamValue> {
let param = find_parameter(name)
.ok_or_else(|| Error::UnknownParameter(name.to_string()))?;
self.read_param(param)
}
pub fn write(&self, name: &str, value: ParamValue) -> Result<()> {
let param = find_parameter(name)
.ok_or_else(|| Error::UnknownParameter(name.to_string()))?;
if param.access == Access::ReadOnly {
return Err(Error::ReadOnly(param.name.to_string()));
}
self.write_param(param, value)
}
pub fn write_str(&self, name: &str, text: &str) -> Result<()> {
let param = find_parameter(name)
.ok_or_else(|| Error::UnknownParameter(name.to_string()))?;
let value = match param.kind {
ParamKind::Int => {
let v: i32 = text
.parse()
.map_err(|e| Error::InvalidValue(format!("expected int: {e}")))?;
ParamValue::Int(v)
}
ParamKind::Float => {
let v: f64 = text
.parse()
.map_err(|e| Error::InvalidValue(format!("expected float: {e}")))?;
ParamValue::Float(v)
}
};
self.write(param.name, value)
}
fn read_param(&self, param: &Parameter) -> Result<ParamValue> {
let mut cmd = 0x80u16 | u16::from(param.offset);
if matches!(param.kind, ParamKind::Int) {
cmd |= 0x40;
}
let mut response = [0u8; 8];
self.handle.read_control(
VENDOR_IN,
0,
cmd,
param.id,
&mut response,
TUNING_TIMEOUT,
)?;
let value0 = i32::from_le_bytes(response[0..4].try_into().unwrap());
let value1 = i32::from_le_bytes(response[4..8].try_into().unwrap());
Ok(match param.kind {
ParamKind::Int => ParamValue::Int(value0),
ParamKind::Float => ParamValue::Float((value0 as f64) * 2f64.powi(value1)),
})
}
fn write_param(&self, param: &Parameter, value: ParamValue) -> Result<()> {
let mut payload = [0u8; 12];
payload[0..4].copy_from_slice(&i32::from(param.offset).to_le_bytes());
match param.kind {
ParamKind::Int => {
let v = value
.as_i32()
.ok_or_else(|| Error::InvalidValue("expected int value".into()))?;
payload[4..8].copy_from_slice(&v.to_le_bytes());
payload[8..12].copy_from_slice(&1i32.to_le_bytes());
}
ParamKind::Float => {
let v = value.as_f64() as f32;
payload[4..8].copy_from_slice(&v.to_le_bytes());
payload[8..12].copy_from_slice(&0i32.to_le_bytes());
}
}
self.handle.write_control(
VENDOR_OUT,
0,
0,
param.id,
&payload,
TUNING_TIMEOUT,
)?;
Ok(())
}
}