This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

🚧 Work on vst beaconing

+476 -124
+42
Cargo.lock
··· 958 958 ] 959 959 960 960 [[package]] 961 + name = "data-encoding" 962 + version = "2.8.0" 963 + source = "registry+https://github.com/rust-lang/crates.io-index" 964 + checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" 965 + 966 + [[package]] 961 967 name = "data-url" 962 968 version = "0.3.1" 963 969 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2454 2460 ] 2455 2461 2456 2462 [[package]] 2463 + name = "http" 2464 + version = "1.3.1" 2465 + source = "registry+https://github.com/rust-lang/crates.io-index" 2466 + checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 2467 + dependencies = [ 2468 + "bytes 1.10.1", 2469 + "fnv", 2470 + "itoa", 2471 + ] 2472 + 2473 + [[package]] 2457 2474 name = "http-auth" 2458 2475 version = "0.1.10" 2459 2476 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4455 4472 "tiny-skia", 4456 4473 "tokio", 4457 4474 "toml 0.8.20", 4475 + "tungstenite", 4476 + "url", 4458 4477 "video-rs", 4459 4478 "wasm-bindgen", 4460 4479 "watchexec", ··· 5080 5099 ] 5081 5100 5082 5101 [[package]] 5102 + name = "tungstenite" 5103 + version = "0.26.2" 5104 + source = "registry+https://github.com/rust-lang/crates.io-index" 5105 + checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" 5106 + dependencies = [ 5107 + "bytes 1.10.1", 5108 + "data-encoding", 5109 + "http", 5110 + "httparse", 5111 + "log", 5112 + "rand 0.9.0", 5113 + "sha1", 5114 + "thiserror 2.0.12", 5115 + "utf-8", 5116 + ] 5117 + 5118 + [[package]] 5083 5119 name = "typeid" 5084 5120 version = "1.0.3" 5085 5121 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5209 5245 "unicode-vo", 5210 5246 "xmlwriter", 5211 5247 ] 5248 + 5249 + [[package]] 5250 + name = "utf-8" 5251 + version = "0.7.6" 5252 + source = "registry+https://github.com/rust-lang/crates.io-index" 5253 + checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 5212 5254 5213 5255 [[package]] 5214 5256 name = "utf16_iter"
+2
Cargo.toml
··· 91 91 tokio = { version = "1.44.1", optional = true } 92 92 watchexec-events = { version = "5.0.0", optional = true } 93 93 serde = { version = "1.0.219", features = ["derive"] } 94 + url = "2.5.4" 95 + tungstenite = "0.26.2" 94 96 95 97 96 98 [dev-dependencies]
+2
src/cli/mod.rs
··· 18 18 19 19 Usage: shapemaker test-video [options] [--color <mapping>...] <file> 20 20 shapemaker beacon start [options] [--color <mapping>...] <file> 21 + shapemaker beacon ping 21 22 shapemaker examples (dna-analysis-machine|shapeshed|colors-shed|grid) [options] <file> 22 23 shapemaker new <name> 23 24 shapemaker watch [<directory>] ··· 95 96 pub cmd_start: bool, 96 97 pub cmd_new: bool, 97 98 pub cmd_watch: bool, 99 + pub cmd_ping: bool, 98 100 pub arg_directory: String, 99 101 pub arg_name: String, 100 102 pub arg_file: String,
+19
src/main.rs
··· 51 51 run_video(args, canvas) 52 52 } else if args.cmd_beacon && args.cmd_start { 53 53 run_beacon_start(args, canvas) 54 + } else if args.cmd_beacon && args.cmd_ping { 55 + run_beacon_ping(args) 54 56 } else { 55 57 Ok(()) 56 58 } ··· 68 70 fn run_beacon_start(_args: cli::Args, _canvas: Canvas) -> Result<()> { 69 71 pub use vst::beacon::Beacon; 70 72 Beacon::start() 73 + } 74 + 75 + #[cfg(all(feature = "cli", not(feature = "vst")))] 76 + fn run_beacon_ping(_args: cli::Args) -> Result<()> { 77 + println!( 78 + "VST support is disabled. Enable the vst feature to use VST beaconing." 79 + ); 80 + Ok(()) 81 + } 82 + 83 + #[cfg(all(feature = "cli", feature = "vst"))] 84 + fn run_beacon_ping(_args: cli::Args) -> Result<()> { 85 + use rand; 86 + use vst::remote_probe::RemoteProbe; 87 + let mut probe = RemoteProbe::new(rand::random()); 88 + probe.say("ping hehe"); 89 + Ok(()) 71 90 } 72 91 73 92 #[cfg(all(feature = "cli", not(feature = "mp4")))]
+167 -78
src/vst/beacon.rs
··· 1 1 extern crate env_logger; 2 2 extern crate ws; 3 3 4 + use crate::vst::probe::Datapoint; 5 + 4 6 use super::Probe; 5 7 use anyhow::Result; 6 8 use once_cell::sync::Lazy; 7 9 use std::sync::{Mutex, MutexGuard}; 10 + use tungstenite; 8 11 9 12 const BEACON_PORT: u16 = 8080; 10 13 ··· 13 16 return format!("ws://localhost:{BEACON_PORT}"); 14 17 } 15 18 16 - pub fn connect_to_beacon<T: FnMut(&ws::Sender) -> ()>( 17 - mut action: T, 18 - ) -> Result<()> { 19 - ws::connect(beacon_url(), |out| { 20 - action(&out); 21 - move |_msg| out.close(ws::CloseCode::Normal) 22 - })?; 23 - Ok(()) 24 - } 25 - 26 - pub fn register_probe(probe: Probe) -> Result<()> { 27 - connect_to_beacon(|beacon| { 28 - beacon 29 - .send(format!( 30 - "+ probe {}", 31 - serde_json::to_string(&probe).expect("Failed to serialize probe") 32 - )) 33 - .expect("Failed to send register probe message"); 34 - })?; 35 - Ok(()) 36 - } 37 - 38 - pub fn unregister_probe(id: u32) -> Result<()> { 39 - connect_to_beacon(|beacon| { 40 - beacon 41 - .send(format!("- probe {}", id)) 42 - .expect("Failed to send unregister probe message"); 43 - })?; 44 - Ok(()) 19 + pub fn connect_to_beacon() -> Result< 20 + tungstenite::WebSocket< 21 + tungstenite::stream::MaybeTlsStream<std::net::TcpStream>, 22 + >, 23 + > { 24 + println!("Connecting to beacon at {}", beacon_url()); 25 + let (socket, _response) = tungstenite::connect(beacon_url())?; 26 + Ok(socket) 45 27 } 46 28 47 29 #[derive(Default)] ··· 63 45 pub fn start() -> Result<()> { 64 46 ws::listen(format!("127.0.0.1:{BEACON_PORT}"), |out| { 65 47 println!("Opening beacon connection with a probe..."); 66 - move |msg| match msg { 67 - ws::Message::Text(text) => match split3(&text) { 68 - ("+", "probe", probe_json) => { 69 - match serde_json::from_str::<Probe>(probe_json) { 70 - Ok(probe) => { 71 - let mut beacon = get_beacon(); 72 - beacon.probes.push(probe); 73 - out.send("^ probe added") 48 + move |msg| { 49 + println!("Received message: {:?}", msg); 50 + match msg { 51 + ws::Message::Text(text) => match text.split3() { 52 + ("?", "hi", probe_json) => { 53 + match serde_json::from_str::<Probe>(probe_json) { 54 + Ok(probe) => { 55 + let mut beacon = get_beacon(); 56 + beacon.probes.push(probe); 57 + out.send("{} probe added!") 58 + } 59 + Err(_) => out.send("? invalid JSON :/"), 60 + } 61 + } 62 + (id_str, "hi", probe_json) => { 63 + match serde_json::from_str::<Probe>(probe_json) { 64 + Ok(probe) => { 65 + let mut beacon = get_beacon(); 66 + let probe_index = 67 + beacon.probes.iter().position(|p| { 68 + p.id == id_str.parse::<u32>().unwrap() 69 + }); 70 + if let None = probe_index { 71 + return out.send(format!( 72 + "{} not found :/", 73 + probe.id 74 + )); 75 + } 76 + beacon.probes[probe_index.unwrap()] = probe; 77 + out.send("{} probe added!") 78 + } 79 + Err(_) => out.send("? invalid JSON :/"), 80 + } 81 + } 82 + (id_str, "byebye", "") => { 83 + let id = id_str.parse::<u32>().unwrap(); 84 + let mut beacon = get_beacon(); 85 + let probe_index = beacon 86 + .probes 87 + .iter() 88 + .position(|probe| probe.id == id); 89 + match probe_index { 90 + Some(probe_index) => { 91 + let removed_probe = 92 + beacon.probes.remove(probe_index); 93 + out.send(format!( 94 + "{} probe removed!", 95 + removed_probe.id 96 + )) 97 + } 98 + None => out.send(format!("{id} not found :/")), 99 + } 100 + } 101 + ("*", "wtf", "") => { 102 + let beacon = get_beacon(); 103 + let body = 104 + serde_json::to_string(&beacon.probes).unwrap(); 105 + out.send(body) 106 + } 107 + (id_str, "wtf", "") => { 108 + let id = id_str.parse::<u32>().unwrap(); 109 + let beacon = get_beacon(); 110 + let probe = 111 + beacon.probes.iter().find(|probe| probe.id == id); 112 + match probe { 113 + Some(probe) => { 114 + out.send(format!( 115 + "probe {} with {} datapoints stored", 116 + probe.id, 117 + probe.datapoints.len() 118 + ))?; 119 + out.send( 120 + serde_json::to_string(probe).unwrap(), 121 + ) 122 + } 123 + None => out.send(format!("{id} not found :/")), 74 124 } 75 - Err(_) => out.send("! probe invalid JSON"), 76 125 } 77 - } 78 - ("-", "probe", id_str) => { 79 - let id = id_str.parse::<u32>().unwrap(); 80 - let mut beacon = get_beacon(); 81 - let probe_index = 82 - beacon.probes.iter().position(|probe| probe.id == id); 83 - match probe_index { 84 - Some(probe_index) => { 85 - let removed_probe = 86 - beacon.probes.remove(probe_index); 87 - out.send(format!( 88 - "^ probe {} removed", 89 - removed_probe.id 90 - )) 126 + (id_str, "say", msg) => { 127 + let id = id_str.parse::<u32>().unwrap(); 128 + let beacon = get_beacon(); 129 + let probe = 130 + beacon.probes.iter().find(|probe| probe.id == id); 131 + match probe { 132 + Some(probe) => { 133 + println!("probe {}: {}", probe.id, msg); 134 + out.send("ok") 135 + } 136 + None => out.send(format!("{id} not found :/")), 91 137 } 92 - None => out.send(format!("! probe {id} not found")), 93 138 } 94 - } 95 - ("=", "probe", "*") => { 96 - let beacon = get_beacon(); 97 - let body = serde_json::to_string(&beacon.probes).unwrap(); 98 - out.send(body) 99 - } 100 - ("=", "probe", id_str) => { 101 - let id = id_str.parse::<u32>().unwrap(); 102 - let beacon = get_beacon(); 103 - let probe = 104 - beacon.probes.iter().find(|probe| probe.id == id); 105 - match probe { 106 - Some(probe) => { 107 - out.send(serde_json::to_string(probe).unwrap()) 139 + (probe_id, timestamp, msg) => { 140 + let id = probe_id.parse::<u32>().unwrap(); 141 + let mut beacon = get_beacon(); 142 + let probe = beacon 143 + .probes 144 + .iter_mut() 145 + .find(|probe| probe.id == id); 146 + 147 + if let None = probe { 148 + return out.send(format!("{id} not found :/")); 108 149 } 109 - None => out.send(format!("! probe {id} not found")), 150 + 151 + let probe = probe.unwrap(); 152 + let timestamp: usize = 153 + timestamp.parse().expect(&format!( 154 + "{timestamp} to be a number (timestamp)" 155 + )); 156 + 157 + match msg.split2() { 158 + ("%", data) => match data.split2() { 159 + (paramname, paramvalue) => { 160 + probe.store(Datapoint::Automation( 161 + timestamp, 162 + paramname.parse().expect(&format!( 163 + "{paramname} to be a number" 164 + )), 165 + paramvalue.parse().expect(&format!( 166 + "{paramvalue} to be a number" 167 + )), 168 + )) 169 + } 170 + }, 171 + ("#", data) => probe.store(Datapoint::Midi( 172 + timestamp, 173 + data.as_bytes().to_vec(), 174 + )), 175 + ("~", data) => probe.store(Datapoint::Audio( 176 + timestamp, 177 + data.split(' ') 178 + .map(|f| f.parse().unwrap()) 179 + .collect(), 180 + )), 181 + _ => { 182 + return out.send("invalid command :/"); 183 + } 184 + } 185 + 186 + out.send("gotchu!") 110 187 } 111 - } 112 - _ => out.send("! invalid command"), 113 - }, 114 - ws::Message::Binary(_) => todo!(), 188 + }, 189 + ws::Message::Binary(_) => todo!(), 190 + } 115 191 } 116 192 })?; 117 193 Ok(()) 118 194 } 119 195 } 120 196 121 - fn split3(subject: &str) -> (&str, &str, &str) { 122 - let mut parts = subject.splitn(3, ' '); 123 - let first = parts.next().unwrap_or_default(); 124 - let second = parts.next().unwrap_or_default(); 125 - let third = parts.next().unwrap_or_default(); 126 - return (first, second, third); 197 + trait FixedSplits { 198 + fn split3(&self) -> (&str, &str, &str); 199 + fn split2(&self) -> (&str, &str); 200 + } 201 + impl FixedSplits for str { 202 + fn split3(&self) -> (&str, &str, &str) { 203 + let mut parts = self.splitn(3, ' '); 204 + let first = parts.next().unwrap_or_default(); 205 + let second = parts.next().unwrap_or_default(); 206 + let third = parts.next().unwrap_or_default(); 207 + return (first, second, third); 208 + } 209 + 210 + fn split2(&self) -> (&str, &str) { 211 + let mut parts = self.splitn(2, ' '); 212 + let first = parts.next().unwrap_or_default(); 213 + let second = parts.next().unwrap_or_default(); 214 + return (first, second); 215 + } 127 216 }
+1
src/vst/mod.rs
··· 1 1 pub mod beacon; 2 + pub mod remote_probe; 2 3 pub mod probe; 3 4 pub mod vst; 4 5
+34 -4
src/vst/probe.rs
··· 1 1 use serde::{Deserialize, Serialize}; 2 - use std::fmt::Display; 2 + use std::{collections::HashMap, fmt::Display}; 3 3 4 4 #[derive(Serialize, Deserialize, Clone)] 5 5 pub struct Probe { 6 6 pub id: u32, 7 7 pub added_at: String, 8 - pub automation_name: String, 8 + /// Maps automation parameter indices to their names 9 + pub automation_names: HashMap<usize, String>, 10 + /// MIDI In signal name 9 11 pub midi_name: String, 12 + /// Audio In signal name 10 13 pub audio_name: String, 14 + 15 + #[serde(skip)] 16 + pub datapoints: Vec<Datapoint>, 17 + } 18 + 19 + #[derive(Clone)] 20 + pub enum Datapoint { 21 + Automation(usize, usize, f32), 22 + Midi(usize, Vec<u8>), 23 + Audio(usize, Vec<f32>), 11 24 } 12 25 13 26 impl Probe { ··· 18 31 ..self.clone() 19 32 }; 20 33 } 34 + 35 + pub fn store(&mut self, datapoint: Datapoint) { 36 + self.datapoints.push(datapoint); 37 + } 21 38 } 22 39 23 40 impl Display for Probe { 24 41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 25 42 write!(f, "probe {} [", self.id)?; 26 - if !self.automation_name.is_empty() { 27 - write!(f, "automation \"{}\"", self.automation_name)?; 43 + if !self.automation_names.is_empty() { 44 + write!(f, "automations {:?}", self.automation_names)?; 28 45 if !self.midi_name.is_empty() || !self.audio_name.is_empty() { 29 46 write!(f, " ")?; 30 47 } ··· 42 59 return Ok(()); 43 60 } 44 61 } 62 + 63 + impl Default for Probe { 64 + fn default() -> Self { 65 + Self { 66 + id: 0, 67 + audio_name: String::new(), 68 + midi_name: String::new(), 69 + added_at: chrono::Utc::now().to_rfc3339(), 70 + datapoints: Vec::new(), 71 + automation_names: HashMap::new(), 72 + } 73 + } 74 + }
+147
src/vst/remote_probe.rs
··· 1 + use super::{beacon::connect_to_beacon, probe::Datapoint, Probe}; 2 + use anyhow::Result; 3 + use nih_plug::params::FloatParam; 4 + use std::{fmt::Display, net::TcpStream}; 5 + use tungstenite::{stream::MaybeTlsStream, WebSocket}; 6 + 7 + pub struct RemoteProbe { 8 + pub id: u32, 9 + pub out: WebSocket<MaybeTlsStream<TcpStream>>, 10 + pub pointsbuffer: Vec<Datapoint>, 11 + } 12 + 13 + impl RemoteProbe { 14 + pub fn new(id: u32) -> Self { 15 + Self { 16 + id, 17 + out: connect_to_beacon().unwrap(), 18 + pointsbuffer: Vec::new(), 19 + } 20 + } 21 + 22 + pub fn register(&mut self) -> Result<()> { 23 + let probe = Probe { 24 + id: self.id, 25 + ..Default::default() 26 + }; 27 + 28 + self.out 29 + .send( 30 + format!( 31 + "? hi {}", 32 + serde_json::to_string(&probe) 33 + .expect("Failed to serialize probe") 34 + ) 35 + .into(), 36 + ) 37 + .expect("Failed to send register probe message"); 38 + 39 + Ok(()) 40 + } 41 + 42 + pub fn update(&mut self, probe: Probe) -> Result<()> { 43 + self.out 44 + .send( 45 + format!( 46 + "{} hi {}", 47 + self.id, 48 + serde_json::to_string(&probe) 49 + .expect("Failed to serialize probe") 50 + ) 51 + .into(), 52 + ) 53 + .expect("Failed to send update probe message"); 54 + Ok(()) 55 + } 56 + 57 + pub fn timestamp() -> usize { 58 + std::time::SystemTime::now() 59 + .duration_since(std::time::UNIX_EPOCH) 60 + .unwrap() 61 + .as_millis() as usize 62 + } 63 + 64 + /// Store a automation data point. Don't forget to call .out.flush(), this one does not flush on its own! 65 + pub fn store_automation( 66 + &mut self, 67 + timestamp: usize, 68 + param_id: usize, 69 + param: &FloatParam, 70 + ) -> Result<()> { 71 + self.store(Datapoint::Automation(timestamp, param_id, param.value())) 72 + } 73 + 74 + /// Store a audio data point. Don't forget to call .out.flush(), this one does not flush on its own! 75 + pub fn store_audio( 76 + &mut self, 77 + timestamp: usize, 78 + samples: Vec<f32>, 79 + ) -> Result<()> { 80 + self.store(Datapoint::Audio(timestamp, samples)) 81 + } 82 + 83 + /// Store a midi data point. Don't forget to call .out.flush(), this one does not flush on its own! 84 + pub fn store_midi(&mut self, timestamp: usize, data: &[u8]) -> Result<()> { 85 + self.store(Datapoint::Midi(timestamp, data.to_vec())) 86 + } 87 + 88 + /// Store a data point. Don't forget to call .out.flush(), this one does not flush on its own! 89 + pub fn say(&mut self, msg: impl Display) -> Result<()> { 90 + self.out 91 + .write(format!("{} say {}", self.id, msg).into()) 92 + .expect("Failed to send say message"); 93 + Ok(()) 94 + } 95 + 96 + pub fn store(&mut self, datapoint: Datapoint) -> Result<()> { 97 + self.pointsbuffer.push(datapoint); 98 + if self.pointsbuffer.len() >= 100 { 99 + self.say("flushing buffer of datapoints")?; 100 + for datapoint in self.pointsbuffer.drain(..) { 101 + self.out 102 + .write( 103 + format!( 104 + "{} {}", 105 + self.id, 106 + match &datapoint { 107 + Datapoint::Automation(ts, param_id, value) => { 108 + format!("{ts} % {} {}", param_id, value) 109 + } 110 + Datapoint::Midi(ts, data) => { 111 + format!( 112 + "{ts} # 0 {}", 113 + data.iter() 114 + .map(|b| b.to_string()) 115 + .collect::<Vec<String>>() 116 + .join(" ") 117 + ) 118 + } 119 + Datapoint::Audio(ts, data) => { 120 + format!( 121 + "{ts} ~ {}", 122 + data.iter() 123 + .map(|f| f.to_string()) 124 + .collect::<Vec<String>>() 125 + .join(" ") 126 + ) 127 + } 128 + } 129 + ) 130 + .into(), 131 + ) 132 + .expect("Failed to send store datapoint message"); 133 + } 134 + self.out.flush().expect("Failed to flush probe connection"); 135 + } 136 + 137 + Ok(()) 138 + } 139 + } 140 + 141 + impl Drop for RemoteProbe { 142 + fn drop(&mut self) { 143 + self.out 144 + .close(None) 145 + .expect("Failed to close probe connection"); 146 + } 147 + }
+62 -42
src/vst/vst.rs
··· 1 - use super::beacon; 2 - use super::probe::Probe; 1 + use super::{probe::Datapoint, remote_probe::RemoteProbe}; 3 2 use nih_plug::prelude::*; 4 3 use rand::Rng; 5 4 use std::sync::Arc; 6 5 7 6 pub struct ShapemakerVST { 8 7 params: Arc<ShapemakerVSTParams>, 9 - probe: Probe, 8 + probe: RemoteProbe, 10 9 } 11 10 12 11 #[derive(Params)] 13 12 struct ShapemakerVSTParams { 14 13 /// Used to send automation data to Shapemaker 15 - #[id = "automation"] 16 - pub automation: FloatParam, 14 + #[id = "param1"] 15 + pub param1: FloatParam, 16 + #[id = "param2"] 17 + pub param2: FloatParam, 18 + #[id = "param3"] 19 + pub param3: FloatParam, 20 + #[id = "param4"] 21 + pub param4: FloatParam, 22 + #[id = "param5"] 23 + pub param5: FloatParam, 24 + #[id = "param6"] 25 + pub param6: FloatParam, 26 + #[id = "param7"] 27 + pub param7: FloatParam, 28 + #[id = "param8"] 29 + pub param8: FloatParam, 30 + #[id = "param9"] 31 + pub param9: FloatParam, 17 32 } 18 33 19 34 impl Default for ShapemakerVST { ··· 21 36 let probe_id = rand::thread_rng().gen_range(1..=u32::MAX); 22 37 Self { 23 38 params: Arc::new(ShapemakerVSTParams::default()), 24 - probe: Probe { 25 - id: probe_id, 26 - added_at: chrono::Utc::now().to_rfc3339(), 27 - automation_name: format!("{probe_id}/automation"), 28 - midi_name: format!("{probe_id}/midi"), 29 - audio_name: format!("{probe_id}/audio"), 30 - }, 39 + probe: RemoteProbe::new(probe_id), 31 40 } 32 41 } 33 42 } 34 43 35 44 impl Default for ShapemakerVSTParams { 36 45 fn default() -> Self { 37 - Self { 38 - automation: FloatParam::new( 39 - "Send automation data", 40 - util::db_to_gain(0.0), 41 - FloatRange::Skewed { 42 - min: util::db_to_gain(-30.0), 43 - max: util::db_to_gain(30.0), 44 - // This makes the range appear as if it was linear when displaying the values as 45 - // decibels 46 - factor: FloatRange::gain_skew_factor(-30.0, 30.0), 47 - }, 46 + let paramdef = |id: usize| { 47 + FloatParam::new( 48 + format!("Param #{}", id), 49 + 0.0, 50 + FloatRange::Linear { min: 0.0, max: 1.1 }, 48 51 ) 49 - // Because the gain parameter is stored as linear gain instead of storing the value as 50 - // decibels, we need logarithmic smoothing 51 - .with_smoother(SmoothingStyle::Logarithmic(50.0)) 52 - .with_unit(" dB") 53 - // There are many predefined formatters we can use here. If the gain was stored as 54 - // decibels instead of as a linear gain value, we could have also used the 55 - // `.with_step_size(0.1)` function to get internal rounding. 56 - .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) 57 - .with_string_to_value(formatters::s2v_f32_gain_to_db()), 52 + }; 53 + Self { 54 + param1: paramdef(1), 55 + param2: paramdef(2), 56 + param3: paramdef(3), 57 + param4: paramdef(4), 58 + param5: paramdef(5), 59 + param6: paramdef(6), 60 + param7: paramdef(7), 61 + param8: paramdef(8), 62 + param9: paramdef(9), 58 63 } 59 64 } 60 65 } ··· 106 111 _buffer_config: &BufferConfig, 107 112 _context: &mut impl InitContext<Self>, 108 113 ) -> bool { 109 - let _ = beacon::register_probe(self.probe.with_added_at_now()); 114 + let _ = self.probe.register(); 110 115 true 111 116 } 112 117 ··· 116 121 } 117 122 118 123 fn deactivate(&mut self) { 119 - let _ = beacon::unregister_probe(self.probe.id); 124 + // probe should be removed from beacon thanks to the Drop impl 120 125 } 121 126 122 127 fn process( 123 128 &mut self, 124 - buffer: &mut Buffer, 129 + _buffer: &mut Buffer, 125 130 _aux: &mut AuxiliaryBuffers, 126 131 _context: &mut impl ProcessContext<Self>, 127 132 ) -> ProcessStatus { 128 - for channel_samples in buffer.iter_samples() { 129 - // Smoothing is optionally built into the parameters themselves 130 - let gain = self.params.automation.smoothed.next(); 133 + let ts = RemoteProbe::timestamp(); 134 + // self.probe.say(format!("{} sending data", ts)); 135 + self.probe.store_automation(ts, 1, &self.params.param1); 136 + self.probe.store_automation(ts, 2, &self.params.param2); 137 + self.probe.store_automation(ts, 3, &self.params.param3); 138 + self.probe.store_automation(ts, 4, &self.params.param4); 139 + self.probe.store_automation(ts, 4, &self.params.param4); 140 + self.probe.store_automation(ts, 5, &self.params.param5); 141 + self.probe.store_automation(ts, 6, &self.params.param6); 142 + self.probe.store_automation(ts, 7, &self.params.param7); 143 + self.probe.store_automation(ts, 8, &self.params.param8); 144 + self.probe.store_automation(ts, 9, &self.params.param9); 145 + // self.probe.say(format!("{} sent automation", ts)); 131 146 132 - for sample in channel_samples { 133 - *sample *= gain; 134 - } 135 - } 147 + // self.probe.store_audio( 148 + // ts, 149 + // buffer 150 + // .iter_samples() 151 + // .flatten() 152 + // .map(|f| *f) 153 + // .collect::<Vec<f32>>(), 154 + // ); 155 + // self.probe.say(format!("{} sent audio", ts)); 136 156 137 157 ProcessStatus::Normal 138 158 }