use std::{
fmt,
io::{self, Read},
iter::repeat_with,
};
const SYNTH_DEF_2_TYPE_ID: i32 = 0x53436766;
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct SynthDefFile {
pub type_id: i32,
pub version: i32,
pub synth_defs: Vec<SynthDef>,
}
impl SynthDefFile {
pub fn decode<R: Read>(read: R) -> Result<SynthDefFile> {
let mut scanner = Scanner(read);
SynthDefFile::new(&mut scanner)
}
fn new<R: Read>(scanner: &mut Scanner<R>) -> Result<Self> {
let type_id = scanner.scan_i32()?;
if type_id != SYNTH_DEF_2_TYPE_ID {
return Err(Error::BadTypeID(type_id));
}
let version = scanner.scan_i32()?;
let num_synth_defs = scanner.scan_i16()? as usize;
let synth_defs = dotimes(num_synth_defs, || SynthDef::new(scanner))?;
Ok(SynthDefFile {
type_id,
version,
synth_defs,
})
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct SynthDef {
pub name: String,
pub constants: Vec<f32>,
pub initial_parameter_values: Vec<f32>,
pub parameter_names: Vec<ParameterName>,
pub ugens: Vec<UGenSpec>,
pub variants: Vec<VariantSpec>,
}
impl SynthDef {
fn new<R: Read>(scanner: &mut Scanner<R>) -> Result<Self> {
let name = scanner.scan_pstring()?;
let num_constants = scanner.scan_i32()? as usize;
let constants = dotimes(num_constants, || scanner.scan_f32())?;
let num_parameters = scanner.scan_i32()? as usize;
let initial_parameter_values = dotimes(num_parameters, || scanner.scan_f32())?;
let num_parameter_names = scanner.scan_i32()? as usize;
let parameter_names = dotimes(num_parameter_names, || {
Ok(ParameterName {
value: scanner.scan_pstring()?,
parameter_index: scanner.scan_i32()? as usize,
})
})?;
let num_ugens = scanner.scan_i32()? as usize;
let ugens = dotimes(num_ugens, || UGenSpec::new(scanner))?;
let num_variants = scanner.scan_i16()? as usize;
let variants = dotimes(num_variants, || {
Ok(VariantSpec {
name: scanner.scan_pstring()?,
parameters: dotimes(num_parameters, || scanner.scan_f32())?,
})
})?;
Ok(SynthDef {
name,
constants,
initial_parameter_values,
parameter_names,
ugens,
variants,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ParameterName {
pub value: String,
pub parameter_index: usize,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct UGenSpec {
pub class_name: String,
pub rate: i8,
pub special_index: i16,
pub inputs: Vec<Input>,
pub outputs: Vec<i8>,
}
impl UGenSpec {
fn new<R: Read>(scanner: &mut Scanner<R>) -> Result<Self> {
let class_name = scanner.scan_pstring()?;
let rate = scanner.scan_i8()?;
let num_inputs = scanner.scan_i32()? as usize;
let num_outputs = scanner.scan_i32()? as usize;
let special_index = scanner.scan_i16()?;
let inputs = dotimes(num_inputs, || {
let x = scanner.scan_i32()?;
let y = scanner.scan_i32()? as usize;
Ok(if x == -1 {
Input::Constant { index: y }
} else {
Input::UGen {
index: x as usize,
output_index: y,
}
})
})?;
let outputs = dotimes(num_outputs, || scanner.scan_i8())?;
Ok(UGenSpec {
class_name,
rate,
special_index,
inputs,
outputs,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Input {
UGen { index: usize, output_index: usize },
Constant { index: usize },
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct VariantSpec {
pub name: String,
pub parameters: Vec<f32>,
}
fn dotimes<T, F>(len: usize, func: F) -> Result<Vec<T>>
where
F: FnMut() -> Result<T>,
{
repeat_with(func).take(len).collect::<Result<_>>()
}
struct Scanner<R: Read>(R);
impl<R: Read> Scanner<R> {
fn scan_i8(&mut self) -> Result<i8> {
let mut buf: [u8; 1] = [0; 1];
self.0.read_exact(&mut buf)?;
Ok(i8::from_be_bytes(buf))
}
fn scan_i16(&mut self) -> Result<i16> {
let mut buf: [u8; 2] = [0; 2];
self.0.read_exact(&mut buf)?;
Ok(i16::from_be_bytes(buf))
}
fn scan_i32(&mut self) -> Result<i32> {
let mut buf: [u8; 4] = [0; 4];
self.0.read_exact(&mut buf)?;
Ok(i32::from_be_bytes(buf))
}
fn scan_f32(&mut self) -> Result<f32> {
let mut buf: [u8; 4] = [0; 4];
self.0.read_exact(&mut buf)?;
Ok(f32::from_be_bytes(buf))
}
fn scan_pstring(&mut self) -> Result<String> {
let mut len_buf: [u8; 1] = [0; 1];
self.0.read_exact(&mut len_buf)?;
let len = len_buf[0] as usize;
let mut buf = vec![0; len];
self.0.read_exact(&mut buf)?;
Ok(String::from_utf8(buf).unwrap())
}
}
type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
BadTypeID(i32),
IO(io::Error),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::IO(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::BadTypeID(type_id) => write!(
f,
"bad type ID in header: expected {:x}, got {:x}",
SYNTH_DEF_2_TYPE_ID, type_id
),
Error::IO(err) => write!(f, "i/o error: {}", err),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::BadTypeID(_) => None,
Error::IO(err) => Some(err),
}
}
}