#![allow(dead_code)]
use std::f32::consts::PI;
use rosc::{OscMessage, OscPacket, OscType};
use scsynth::World;
pub fn reply_addr(bytes: &[u8]) -> Option<String> {
match rosc::decoder::decode_udp(bytes) {
Ok((_, OscPacket::Message(m))) => Some(m.addr),
_ => None,
}
}
pub fn pump_for_reply(
world: &mut World,
addr: &str,
channels: usize,
max_blocks: usize,
) -> Vec<u8> {
let mut scratch = vec![0f32; 64 * channels];
for _ in 0..max_blocks {
while let Some(bytes) = world.poll_reply() {
if reply_addr(&bytes).as_deref() == Some(addr) {
return bytes;
}
}
world.process(&[], 0, &mut scratch, channels);
#[cfg(not(target_arch = "wasm32"))]
std::thread::sleep(std::time::Duration::from_millis(1));
}
panic!("never received a `{addr}` reply within {max_blocks} blocks");
}
pub const RATE_AUDIO: u8 = 2;
pub fn synthdef_sine(freq: f32, channels: usize) -> Vec<u8> {
let mut b = Vec::new();
b.extend_from_slice(b"SCgf");
b.extend_from_slice(&2i32.to_be_bytes()); b.extend_from_slice(&1i16.to_be_bytes());
write_pstring(&mut b, "sine");
b.extend_from_slice(&2i32.to_be_bytes());
b.extend_from_slice(&freq.to_be_bytes());
b.extend_from_slice(&0.0f32.to_be_bytes());
b.extend_from_slice(&0i32.to_be_bytes());
b.extend_from_slice(&0i32.to_be_bytes());
b.extend_from_slice(&2i32.to_be_bytes());
write_ugen(&mut b, "SinOsc", &[(-1, 0), (-1, 1)], &[RATE_AUDIO]);
let mut out_inputs = vec![(-1, 1)];
out_inputs.extend(std::iter::repeat_n((0, 0), channels));
write_ugen(&mut b, "Out", &out_inputs, &[]);
b.extend_from_slice(&0i16.to_be_bytes());
b
}
pub fn write_ugen(b: &mut Vec<u8>, name: &str, inputs: &[(i32, i32)], output_rates: &[u8]) {
write_pstring(b, name);
b.push(RATE_AUDIO);
b.extend_from_slice(&(inputs.len() as i32).to_be_bytes());
b.extend_from_slice(&(output_rates.len() as i32).to_be_bytes());
b.extend_from_slice(&0i16.to_be_bytes()); for &(from_unit, from_output) in inputs {
b.extend_from_slice(&from_unit.to_be_bytes());
b.extend_from_slice(&from_output.to_be_bytes());
}
b.extend_from_slice(output_rates);
}
pub fn write_pstring(b: &mut Vec<u8>, s: &str) {
b.push(u8::try_from(s.len()).expect("name too long"));
b.extend_from_slice(s.as_bytes());
}
pub fn d_recv(synthdef: Vec<u8>) -> OscMessage {
OscMessage {
addr: "/d_recv".into(),
args: vec![OscType::Blob(synthdef)],
}
}
pub fn s_new(name: &str, node_id: i32, add_action: i32, target: i32) -> OscMessage {
OscMessage {
addr: "/s_new".into(),
args: vec![
OscType::String(name.into()),
OscType::Int(node_id),
OscType::Int(add_action),
OscType::Int(target),
],
}
}
pub fn n_free(node_id: i32) -> OscMessage {
OscMessage {
addr: "/n_free".into(),
args: vec![OscType::Int(node_id)],
}
}
pub fn notify(on: bool) -> OscMessage {
OscMessage {
addr: "/notify".into(),
args: vec![OscType::Int(i32::from(on))],
}
}
pub fn b_alloc(bufnum: i32, frames: i32, channels: i32) -> OscMessage {
OscMessage {
addr: "/b_alloc".into(),
args: vec![
OscType::Int(bufnum),
OscType::Int(frames),
OscType::Int(channels),
],
}
}
pub fn b_setn(bufnum: i32, start: i32, values: &[f32]) -> OscMessage {
let mut args = vec![
OscType::Int(bufnum),
OscType::Int(start),
OscType::Int(values.len() as i32),
];
args.extend(values.iter().map(|&v| OscType::Float(v)));
OscMessage {
addr: "/b_setn".into(),
args,
}
}
pub fn encode(message: OscMessage) -> Vec<u8> {
rosc::encoder::encode(&OscPacket::Message(message)).expect("failed to encode OSC message")
}
pub fn goertzel(samples: &[f32], sample_rate: f32, freq: f32) -> f32 {
let w = 2.0 * PI * freq / sample_rate;
let coeff = 2.0 * w.cos();
let (mut s1, mut s2) = (0.0f32, 0.0f32);
for &x in samples {
let s0 = x + coeff * s1 - s2;
s2 = s1;
s1 = s0;
}
(s1 * s1 + s2 * s2 - coeff * s1 * s2).sqrt()
}
pub fn rms(samples: &[f32]) -> f32 {
if samples.is_empty() {
return 0.0;
}
(samples.iter().map(|s| s * s).sum::<f32>() / samples.len() as f32).sqrt()
}