From 60e600c3810415368ac2d06503291c480f159f3e Mon Sep 17 00:00:00 2001 From: Jade Rowland Date: Sun, 3 Sep 2023 13:23:13 -0400 Subject: [PATCH 1/9] setting up --- src-tauri/Cargo.lock | 27 +++++++ src-tauri/Cargo.toml | 1 + src-tauri/src/main.rs | 17 +++-- src-tauri/src/oscbridge.rs | 143 +++++++++++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 5 deletions(-) create mode 100644 src-tauri/src/oscbridge.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 90767fec..38fce20b 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -98,6 +98,7 @@ name = "app" version = "0.1.0" dependencies = [ "midir", + "rosc", "serde", "serde_json", "tauri", @@ -1557,6 +1558,12 @@ dependencies = [ "windows 0.43.0", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1629,6 +1636,16 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2173,6 +2190,16 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +[[package]] +name = "rosc" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e63d9e6b0d090be1485cf159b1e04c3973d2d3e1614963544ea2ff47a4a981" +dependencies = [ + "byteorder", + "nom", +] + [[package]] name = "rustc-demangle" version = "0.1.23" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 22170cac..35aab489 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -20,6 +20,7 @@ serde = { version = "1.0", features = ["derive"] } tauri = { version = "1.4.0", features = ["fs-all"] } midir = "0.9.1" tokio = { version = "1.29.0", features = ["full"] } +rosc = "0.10.1" [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 0643cefd..c91e37df 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -2,20 +2,27 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] mod midibridge; +mod oscbridge; use tokio::sync::mpsc; use tokio::sync::Mutex; fn main() { - let (async_input_transmitter, async_input_receiver) = mpsc::channel(1); - let (async_output_transmitter, async_output_receiver) = mpsc::channel(1); + let (async_input_transmitter_midi, async_input_receiver_midi) = mpsc::channel(1); + let (async_output_transmitter_midi, async_output_receiver_midi) = mpsc::channel(1); + let (async_input_transmitter_osc, async_input_receiver_osc) = mpsc::channel(1); + let (async_output_transmitter_osc, async_output_receiver_osc) = mpsc::channel(1); tauri::Builder ::default() .manage(midibridge::AsyncInputTransmit { - inner: Mutex::new(async_input_transmitter), + inner: Mutex::new(async_input_transmitter_midi), }) - .invoke_handler(tauri::generate_handler![midibridge::sendmidi]) + .manage(oscbridge::AsyncInputTransmit { + inner: Mutex::new(async_input_transmitter_osc), + }) + .invoke_handler(tauri::generate_handler![midibridge::sendmidi, oscbridge::sendosc]) .setup(|_app| { - midibridge::init(async_input_receiver, async_output_receiver, async_output_transmitter); + midibridge::init(async_input_receiver_midi, async_output_receiver_midi, async_output_transmitter_midi); + oscbridge::init(async_input_receiver_osc, async_output_receiver_osc, async_output_transmitter_osc); Ok(()) }) .run(tauri::generate_context!()) diff --git a/src-tauri/src/oscbridge.rs b/src-tauri/src/oscbridge.rs new file mode 100644 index 00000000..0ea7e6c5 --- /dev/null +++ b/src-tauri/src/oscbridge.rs @@ -0,0 +1,143 @@ +use rosc::encoder; +use rosc::{ OscMessage, OscPacket, OscType }; +use std::net::{ SocketAddrV4, UdpSocket }; +use std::str::FromStr; +use std::time::Duration; +use std::{ env, f32 }; +use std::sync::Arc; +use tokio::sync::{ mpsc, Mutex }; +use tokio::time::Instant; +use serde::Deserialize; +use std::thread::sleep; +pub struct OscMsg { + pub msg_buf: Vec, + pub instant: Instant, + pub offset: u64, +} + +pub struct AsyncInputTransmit { + pub inner: Mutex>>, +} +fn get_addr_from_arg(arg: &str) -> SocketAddrV4 { + SocketAddrV4::from_str(arg).unwrap() +} +pub fn init( + async_input_receiver: mpsc::Receiver>, + mut async_output_receiver: mpsc::Receiver>, + async_output_transmitter: mpsc::Sender> +) { + tauri::async_runtime::spawn(async move { async_process_model(async_input_receiver, async_output_transmitter).await }); + let message_queue: Arc>> = Arc::new(Mutex::new(Vec::new())); + /* ........................................................... + Listen For incoming messages and add to queue + ............................................................*/ + let message_queue_clone = Arc::clone(&message_queue); + tauri::async_runtime::spawn(async move { + loop { + if let Some(package) = async_output_receiver.recv().await { + let mut message_queue = message_queue_clone.lock().await; + let messages = package; + //println!("received message"); + for message in messages { + (*message_queue).push(message); + } + } + } + }); + println!("cloning message queue"); + + let message_queue_clone = Arc::clone(&message_queue); + tauri::async_runtime::spawn(async move { + println!("opening osc port"); + /* ........................................................... + Open OSC Ports + ............................................................*/ + + let sock = UdpSocket::bind("localhost:57121").unwrap(); + let to_addr = String::from("localhost:57120"); + + /* ........................................................... + Process queued messages + ............................................................*/ + + loop { + let mut message_queue = message_queue_clone.lock().await; + println!("num messages {}", message_queue.len()); + + //iterate over each message, play and remove messages when they are ready + message_queue.retain(|message| { + if message.instant.elapsed().as_millis() < message.offset.into() { + return true; + } + sock.send_to(&message.msg_buf, to_addr.clone()).unwrap(); + return false; + }); + + sleep(Duration::from_millis(1)); + } + }); +} + +pub async fn async_process_model( + mut input_reciever: mpsc::Receiver>, + output_transmitter: mpsc::Sender> +) -> Result<(), Box> { + while let Some(input) = input_reciever.recv().await { + let output = input; + output_transmitter.send(output).await?; + } + Ok(()) +} + +#[derive(Deserialize)] +pub struct Param { + name: String, + value: String, + valueisnumber: bool, +} +#[derive(Deserialize)] +pub struct MessageFromJS { + params: Vec, + offset: u64, + target: String, +} +// Called from JS +#[tauri::command] +pub async fn sendosc( + messagesfromjs: Vec, + state: tauri::State<'_, AsyncInputTransmit> +) -> Result<(), String> { + let async_proc_input_tx = state.inner.lock().await; + let mut messages_to_process: Vec = Vec::new(); + + for m in messagesfromjs { + let mut args = Vec::new(); + for p in m.params { + args.push(OscType::String(p.name)); + + if p.valueisnumber { + args.push(OscType::Float(p.value.parse().unwrap())); + } else { + args.push(OscType::String(p.value)); + } + } + + let msg_buf = encoder + ::encode( + &OscPacket::Message(OscMessage { + addr: m.target, + args, + }) + ) + .unwrap(); + + let message_to_process = OscMsg { + instant: Instant::now(), + msg_buf, + offset: m.offset, + }; + messages_to_process.push(message_to_process); + } + + async_proc_input_tx.send(messages_to_process).await.map_err(|e| e.to_string()) +} From 62e3774ff7a68a14aff7375bc1e957c25af4cf30 Mon Sep 17 00:00:00 2001 From: Jade Rowland Date: Sun, 3 Sep 2023 16:28:52 -0400 Subject: [PATCH 2/9] its working --- packages/desktopbridge/oscbridge.mjs | 43 ++++++++++++++++++++++++++++ src-tauri/src/oscbridge.rs | 19 +++--------- website/src/repl/Repl.jsx | 4 +-- 3 files changed, 49 insertions(+), 17 deletions(-) create mode 100644 packages/desktopbridge/oscbridge.mjs diff --git a/packages/desktopbridge/oscbridge.mjs b/packages/desktopbridge/oscbridge.mjs new file mode 100644 index 00000000..d6c13d45 --- /dev/null +++ b/packages/desktopbridge/oscbridge.mjs @@ -0,0 +1,43 @@ +import { logger, parseNumeral, Pattern } from '@strudel.cycles/core'; +import { Invoke } from './utils.mjs'; + +Pattern.prototype.osc = function () { + return this.onTrigger(async (time, hap, currentTime, cps = 1) => { + hap.ensureObjectValue(); + const cycle = hap.wholeOrPart().begin.valueOf(); + const delta = hap.duration.valueOf(); + const controls = Object.assign({}, { cps, cycle, delta }, hap.value); + // make sure n and note are numbers + controls.n && (controls.n = parseNumeral(controls.n)); + controls.note && (controls.note = parseNumeral(controls.note)); + + const messagesfromjs = []; + const params = []; + const offset = Math.round((time - currentTime) * 1000 - 48); + + Object.keys(controls).forEach((key) => { + const val = controls[key]; + const value = typeof val === 'number' ? val.toString() : val; + + if (value == null) { + return; + } + params.push({ + name: key, + value, + valueisnumber: typeof val === 'number', + }); + }); + + if (params.length) { + messagesfromjs.push({ target: '/dirt/play', offset, params }); + } + console.log(messagesfromjs); + + if (messagesfromjs.length) { + setTimeout(() => { + Invoke('sendosc', { messagesfromjs }); + }); + } + }); +}; diff --git a/src-tauri/src/oscbridge.rs b/src-tauri/src/oscbridge.rs index 0ea7e6c5..bf329ab9 100644 --- a/src-tauri/src/oscbridge.rs +++ b/src-tauri/src/oscbridge.rs @@ -1,9 +1,7 @@ use rosc::encoder; use rosc::{ OscMessage, OscPacket, OscType }; -use std::net::{ SocketAddrV4, UdpSocket }; -use std::str::FromStr; +use std::net::UdpSocket; use std::time::Duration; -use std::{ env, f32 }; use std::sync::Arc; use tokio::sync::{ mpsc, Mutex }; use tokio::time::Instant; @@ -18,9 +16,7 @@ pub struct OscMsg { pub struct AsyncInputTransmit { pub inner: Mutex>>, } -fn get_addr_from_arg(arg: &str) -> SocketAddrV4 { - SocketAddrV4::from_str(arg).unwrap() -} + pub fn init( async_input_receiver: mpsc::Receiver>, mut async_output_receiver: mpsc::Receiver>, @@ -44,22 +40,17 @@ pub fn init( } } }); - println!("cloning message queue"); let message_queue_clone = Arc::clone(&message_queue); tauri::async_runtime::spawn(async move { - println!("opening osc port"); /* ........................................................... Open OSC Ports ............................................................*/ - - let sock = UdpSocket::bind("localhost:57121").unwrap(); - let to_addr = String::from("localhost:57120"); - + let sock = UdpSocket::bind("127.0.0.1:57121").unwrap(); + let to_addr = String::from("127.0.0.1:57120"); /* ........................................................... Process queued messages ............................................................*/ - loop { let mut message_queue = message_queue_clone.lock().await; println!("num messages {}", message_queue.len()); @@ -109,12 +100,10 @@ pub async fn sendosc( ) -> Result<(), String> { let async_proc_input_tx = state.inner.lock().await; let mut messages_to_process: Vec = Vec::new(); - for m in messagesfromjs { let mut args = Vec::new(); for p in m.params { args.push(OscType::String(p.name)); - if p.valueisnumber { args.push(OscType::Float(p.value.parse().unwrap())); } else { diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 4434ed7a..6cb4eb7d 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -37,10 +37,10 @@ const modules = [ import('@strudel.cycles/core'), import('@strudel.cycles/tonal'), import('@strudel.cycles/mini'), - isTauri() ? import('@strudel/desktopbridge') : import('@strudel.cycles/midi'), + isTauri() ? import('@strudel/desktopbridge/midibridge.mjs') : import('@strudel.cycles/midi'), import('@strudel.cycles/xen'), import('@strudel.cycles/webaudio'), - import('@strudel.cycles/osc'), + isTauri() ? import('@strudel/desktopbridge/oscbridge.mjs') : import('@strudel.cycles/osc'), import('@strudel.cycles/serial'), import('@strudel.cycles/soundfonts'), import('@strudel.cycles/csound'), From 349d11e76d0e43e29ad6e616eed8c5eb61d9fb0a Mon Sep 17 00:00:00 2001 From: Jade Rowland Date: Sun, 3 Sep 2023 17:36:21 -0400 Subject: [PATCH 3/9] different time strats --- src-tauri/src/oscbridge.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/oscbridge.rs b/src-tauri/src/oscbridge.rs index bf329ab9..6fc6718d 100644 --- a/src-tauri/src/oscbridge.rs +++ b/src-tauri/src/oscbridge.rs @@ -1,7 +1,8 @@ use rosc::encoder; use rosc::{ OscMessage, OscPacket, OscType }; use std::net::UdpSocket; -use std::time::Duration; + +use std::time::{ Duration, SystemTime, UNIX_EPOCH }; use std::sync::Arc; use tokio::sync::{ mpsc, Mutex }; use tokio::time::Instant; @@ -9,8 +10,7 @@ use serde::Deserialize; use std::thread::sleep; pub struct OscMsg { pub msg_buf: Vec, - pub instant: Instant, - pub offset: u64, + pub timestamp: u128, } pub struct AsyncInputTransmit { @@ -51,13 +51,15 @@ pub fn init( /* ........................................................... Process queued messages ............................................................*/ + loop { let mut message_queue = message_queue_clone.lock().await; - println!("num messages {}", message_queue.len()); + let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(); //iterate over each message, play and remove messages when they are ready message_queue.retain(|message| { - if message.instant.elapsed().as_millis() < message.offset.into() { + println!("current_time {} message time {}", current_time, message.timestamp); + if current_time < message.timestamp { return true; } sock.send_to(&message.msg_buf, to_addr.clone()).unwrap(); @@ -89,7 +91,7 @@ pub struct Param { #[derive(Deserialize)] pub struct MessageFromJS { params: Vec, - offset: u64, + timestamp: u128, target: String, } // Called from JS @@ -121,9 +123,8 @@ pub async fn sendosc( .unwrap(); let message_to_process = OscMsg { - instant: Instant::now(), msg_buf, - offset: m.offset, + timestamp: m.timestamp, }; messages_to_process.push(message_to_process); } From 77349526e406699088e2faad3b6b5ca3b7b3ecc3 Mon Sep 17 00:00:00 2001 From: Jade Rowland Date: Mon, 4 Sep 2023 14:21:30 -0400 Subject: [PATCH 4/9] timetag mishaps --- src-tauri/src/oscbridge.rs | 71 ++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/src-tauri/src/oscbridge.rs b/src-tauri/src/oscbridge.rs index 6fc6718d..51e3da73 100644 --- a/src-tauri/src/oscbridge.rs +++ b/src-tauri/src/oscbridge.rs @@ -1,5 +1,5 @@ -use rosc::encoder; -use rosc::{ OscMessage, OscPacket, OscType }; +use rosc::{ encoder, OscTime }; +use rosc::{ OscMessage, OscPacket, OscType, OscBundle }; use std::net::UdpSocket; use std::time::{ Duration, SystemTime, UNIX_EPOCH }; @@ -10,13 +10,15 @@ use serde::Deserialize; use std::thread::sleep; pub struct OscMsg { pub msg_buf: Vec, - pub timestamp: u128, + pub timestamp: u64, } pub struct AsyncInputTransmit { pub inner: Mutex>>, } +const SECONDS_70_YEARS: u64 = 2208988800; + pub fn init( async_input_receiver: mpsc::Receiver>, mut async_output_receiver: mpsc::Receiver>, @@ -46,11 +48,15 @@ pub fn init( /* ........................................................... Open OSC Ports ............................................................*/ - let sock = UdpSocket::bind("127.0.0.1:57121").unwrap(); + let sock = UdpSocket::bind("127.0.0.1:57122").unwrap(); let to_addr = String::from("127.0.0.1:57120"); + sock.set_nonblocking(true).unwrap(); + sock.connect(to_addr).expect("could not connect to OSC address"); + /* ........................................................... Process queued messages ............................................................*/ + let mut prev_time = Instant::now(); loop { let mut message_queue = message_queue_clone.lock().await; @@ -58,11 +64,11 @@ pub fn init( //iterate over each message, play and remove messages when they are ready message_queue.retain(|message| { - println!("current_time {} message time {}", current_time, message.timestamp); - if current_time < message.timestamp { - return true; - } - sock.send_to(&message.msg_buf, to_addr.clone()).unwrap(); + // println!("current_time {} message time {}", current_time, message.timestamp); + + sock.send(&message.msg_buf).unwrap(); + println!("func delay {}", prev_time.elapsed().as_millis()); + prev_time = Instant::now(); return false; }); @@ -91,8 +97,11 @@ pub struct Param { #[derive(Deserialize)] pub struct MessageFromJS { params: Vec, - timestamp: u128, + timestamp: u64, target: String, + seconds: u64, + fractional: u64, + offset: u64, } // Called from JS #[tauri::command] @@ -112,16 +121,42 @@ pub async fn sendosc( args.push(OscType::String(p.value)); } } + // let o = Duration::from_millis(m.offset); + // let t: Duration = Duration::from_millis(m.timestamp); - let msg_buf = encoder - ::encode( - &OscPacket::Message(OscMessage { - addr: m.target, - args, - }) - ) - .unwrap(); + let t = Duration::from_secs(SECONDS_70_YEARS) + Duration::from_millis(m.timestamp); + //let offset = SystemTime::now().duration_since(UNIX_EPOCH).unwrap() + Duration::from_secs(SECONDS_70_YEARS); + // Duration::from_millis(m.offset); + // let offsetsecs = offset.as_secs() as u32; + let timetag = OscTime::from((t.as_secs() as u32, t.subsec_millis())); + // let timetag = OscTime::from((offset.as_secs() as u32, offset.subsec_nanos())); + // let timetag = OscTime::from((m.seconds as u32, m.fractional as u32)); + + //let timetag = OscTime::from((0, 0)); + + //timetag.seconds; + println!("secs timetag {} ns {}", timetag.seconds, timetag.fractional); + + // args.push(OscType::String("timetag".to_string())); + // args.push(OscType::Long(m.timestamp as i64)); + let packet = OscPacket::Message(OscMessage { + addr: m.target, + args, + }); + + let x = OscBundle { + content: vec![packet], + timetag, + }; + + let msg_buf = encoder::encode(&OscPacket::Bundle(x)).unwrap(); + // let msg_buf = encoder::encode(&packet).unwrap(); + println!("message"); + // for p in msg_buf { + // print!("{}, ", p); + // } + //let msg_buf = encoder::encode(&packet).unwrap(); let message_to_process = OscMsg { msg_buf, timestamp: m.timestamp, From 009760c31a04fd67c3c4e7861d33f4142bbab1a9 Mon Sep 17 00:00:00 2001 From: Jade Rowland Date: Mon, 4 Sep 2023 15:00:43 -0400 Subject: [PATCH 5/9] correct bit calcuations --- src-tauri/src/oscbridge.rs | 50 ++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/src-tauri/src/oscbridge.rs b/src-tauri/src/oscbridge.rs index 51e3da73..34983f2a 100644 --- a/src-tauri/src/oscbridge.rs +++ b/src-tauri/src/oscbridge.rs @@ -1,4 +1,4 @@ -use rosc::{ encoder, OscTime }; +use rosc::{ encoder, OscTime, OscTimeError }; use rosc::{ OscMessage, OscPacket, OscType, OscBundle }; use std::net::UdpSocket; @@ -17,7 +17,13 @@ pub struct AsyncInputTransmit { pub inner: Mutex>>, } -const SECONDS_70_YEARS: u64 = 2208988800; +// const SECONDS_70_YEARS: u64 = 2208988800; +// 70 years in seconds +const UNIX_OFFSET: u64 = 2_208_988_800; // From RFC 5905 +const TWO_POW_32: f64 = (u32::MAX as f64) + 1.0; // Number of bits in a `u32` +const ONE_OVER_TWO_POW_32: f64 = 1.0 / TWO_POW_32; +const NANOS_PER_SECOND: f64 = 1.0e9; +const SECONDS_PER_NANO: f64 = 1.0 / NANOS_PER_SECOND; pub fn init( async_input_receiver: mpsc::Receiver>, @@ -60,14 +66,9 @@ pub fn init( loop { let mut message_queue = message_queue_clone.lock().await; - let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(); - //iterate over each message, play and remove messages when they are ready message_queue.retain(|message| { - // println!("current_time {} message time {}", current_time, message.timestamp); - sock.send(&message.msg_buf).unwrap(); - println!("func delay {}", prev_time.elapsed().as_millis()); prev_time = Instant::now(); return false; }); @@ -99,8 +100,7 @@ pub struct MessageFromJS { params: Vec, timestamp: u64, target: String, - seconds: u64, - fractional: u64, + offset: u64, } // Called from JS @@ -121,25 +121,22 @@ pub async fn sendosc( args.push(OscType::String(p.value)); } } - // let o = Duration::from_millis(m.offset); - // let t: Duration = Duration::from_millis(m.timestamp); - let t = Duration::from_secs(SECONDS_70_YEARS) + Duration::from_millis(m.timestamp); - //let offset = SystemTime::now().duration_since(UNIX_EPOCH).unwrap() + Duration::from_secs(SECONDS_70_YEARS); - // Duration::from_millis(m.offset); - // let offsetsecs = offset.as_secs() as u32; - let timetag = OscTime::from((t.as_secs() as u32, t.subsec_millis())); - // let timetag = OscTime::from((offset.as_secs() as u32, offset.subsec_nanos())); + // let duration_since_epoch = + // SystemTime::now() + // .duration_since(UNIX_EPOCH) + // .map_err(|_| "incorrect calc")? + + // Duration::new(UNIX_OFFSET, 0) + + // Duration::from_millis(m.offset); - // let timetag = OscTime::from((m.seconds as u32, m.fractional as u32)); + let duration_since_epoch = Duration::from_millis(m.timestamp) + Duration::new(UNIX_OFFSET, 0); - //let timetag = OscTime::from((0, 0)); + let seconds = u32::try_from(duration_since_epoch.as_secs()).map_err(|_| "process sec error")?; + let nanos = duration_since_epoch.subsec_nanos() as f64; + let fractional = (nanos * SECONDS_PER_NANO * TWO_POW_32).round() as u32; - //timetag.seconds; - println!("secs timetag {} ns {}", timetag.seconds, timetag.fractional); + let timetag = OscTime::from((seconds, fractional)); - // args.push(OscType::String("timetag".to_string())); - // args.push(OscType::Long(m.timestamp as i64)); let packet = OscPacket::Message(OscMessage { addr: m.target, args, @@ -151,12 +148,7 @@ pub async fn sendosc( }; let msg_buf = encoder::encode(&OscPacket::Bundle(x)).unwrap(); - // let msg_buf = encoder::encode(&packet).unwrap(); - println!("message"); - // for p in msg_buf { - // print!("{}, ", p); - // } - //let msg_buf = encoder::encode(&packet).unwrap(); + let message_to_process = OscMsg { msg_buf, timestamp: m.timestamp, From 4001cc953131e2a3919892092e78e9c6379fb686 Mon Sep 17 00:00:00 2001 From: Jade Rowland Date: Mon, 4 Sep 2023 15:33:28 -0400 Subject: [PATCH 6/9] cleaning up --- src-tauri/src/oscbridge.rs | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src-tauri/src/oscbridge.rs b/src-tauri/src/oscbridge.rs index 34983f2a..8630d7be 100644 --- a/src-tauri/src/oscbridge.rs +++ b/src-tauri/src/oscbridge.rs @@ -1,8 +1,8 @@ -use rosc::{ encoder, OscTime, OscTimeError }; +use rosc::{ encoder, OscTime }; use rosc::{ OscMessage, OscPacket, OscType, OscBundle }; use std::net::UdpSocket; -use std::time::{ Duration, SystemTime, UNIX_EPOCH }; +use std::time::Duration; use std::sync::Arc; use tokio::sync::{ mpsc, Mutex }; use tokio::time::Instant; @@ -17,11 +17,8 @@ pub struct AsyncInputTransmit { pub inner: Mutex>>, } -// const SECONDS_70_YEARS: u64 = 2208988800; -// 70 years in seconds -const UNIX_OFFSET: u64 = 2_208_988_800; // From RFC 5905 +const UNIX_OFFSET: u64 = 2_208_988_800; // 70 years in seconds const TWO_POW_32: f64 = (u32::MAX as f64) + 1.0; // Number of bits in a `u32` -const ONE_OVER_TWO_POW_32: f64 = 1.0 / TWO_POW_32; const NANOS_PER_SECOND: f64 = 1.0e9; const SECONDS_PER_NANO: f64 = 1.0 / NANOS_PER_SECOND; @@ -41,7 +38,6 @@ pub fn init( if let Some(package) = async_output_receiver.recv().await { let mut message_queue = message_queue_clone.lock().await; let messages = package; - //println!("received message"); for message in messages { (*message_queue).push(message); } @@ -100,8 +96,6 @@ pub struct MessageFromJS { params: Vec, timestamp: u64, target: String, - - offset: u64, } // Called from JS #[tauri::command] @@ -122,16 +116,12 @@ pub async fn sendosc( } } - // let duration_since_epoch = - // SystemTime::now() - // .duration_since(UNIX_EPOCH) - // .map_err(|_| "incorrect calc")? + - // Duration::new(UNIX_OFFSET, 0) + - // Duration::from_millis(m.offset); - let duration_since_epoch = Duration::from_millis(m.timestamp) + Duration::new(UNIX_OFFSET, 0); - let seconds = u32::try_from(duration_since_epoch.as_secs()).map_err(|_| "process sec error")?; + let seconds = u32 + ::try_from(duration_since_epoch.as_secs()) + .map_err(|_| "bit conversion failed for osc message timetag")?; + let nanos = duration_since_epoch.subsec_nanos() as f64; let fractional = (nanos * SECONDS_PER_NANO * TWO_POW_32).round() as u32; @@ -142,12 +132,12 @@ pub async fn sendosc( args, }); - let x = OscBundle { + let bundle = OscBundle { content: vec![packet], timetag, }; - let msg_buf = encoder::encode(&OscPacket::Bundle(x)).unwrap(); + let msg_buf = encoder::encode(&OscPacket::Bundle(bundle)).unwrap(); let message_to_process = OscMsg { msg_buf, From a9d8e7ca82ca19006f1e16cdee4802257e85d39a Mon Sep 17 00:00:00 2001 From: Jade Rowland Date: Mon, 4 Sep 2023 15:33:44 -0400 Subject: [PATCH 7/9] cleaning up --- packages/desktopbridge/index.mjs | 1 + packages/desktopbridge/oscbridge.mjs | 10 +++++----- packages/desktopbridge/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/desktopbridge/index.mjs b/packages/desktopbridge/index.mjs index 2cc9db3d..591bbe34 100644 --- a/packages/desktopbridge/index.mjs +++ b/packages/desktopbridge/index.mjs @@ -6,3 +6,4 @@ This program is free software: you can redistribute it and/or modify it under th export * from './midibridge.mjs'; export * from './utils.mjs'; +export * from './oscbridge.mjs'; diff --git a/packages/desktopbridge/oscbridge.mjs b/packages/desktopbridge/oscbridge.mjs index d6c13d45..135a2b02 100644 --- a/packages/desktopbridge/oscbridge.mjs +++ b/packages/desktopbridge/oscbridge.mjs @@ -1,4 +1,4 @@ -import { logger, parseNumeral, Pattern } from '@strudel.cycles/core'; +import { parseNumeral, Pattern } from '@strudel.cycles/core'; import { Invoke } from './utils.mjs'; Pattern.prototype.osc = function () { @@ -11,9 +11,9 @@ Pattern.prototype.osc = function () { controls.n && (controls.n = parseNumeral(controls.n)); controls.note && (controls.note = parseNumeral(controls.note)); - const messagesfromjs = []; const params = []; - const offset = Math.round((time - currentTime) * 1000 - 48); + + const timestamp = Math.round(Date.now() + (time - currentTime) * 1000); Object.keys(controls).forEach((key) => { const val = controls[key]; @@ -29,10 +29,10 @@ Pattern.prototype.osc = function () { }); }); + const messagesfromjs = []; if (params.length) { - messagesfromjs.push({ target: '/dirt/play', offset, params }); + messagesfromjs.push({ target: '/dirt/play', timestamp, params }); } - console.log(messagesfromjs); if (messagesfromjs.length) { setTimeout(() => { diff --git a/packages/desktopbridge/package.json b/packages/desktopbridge/package.json index bdeda2d9..c2acba9e 100644 --- a/packages/desktopbridge/package.json +++ b/packages/desktopbridge/package.json @@ -1,7 +1,7 @@ { "name": "@strudel/desktopbridge", "version": "0.1.0", - "description": "send midi messages between the JS and Tauri (Rust) sides of the Studel desktop app", + "description": "tools/shims for communicating between the JS and Tauri (Rust) sides of the Studel desktop app", "main": "index.mjs", "type": "module", "repository": { From d233d50de33caffa2e549e0ab65384afbbca8f4d Mon Sep 17 00:00:00 2001 From: Jade Rowland Date: Mon, 4 Sep 2023 17:07:50 -0400 Subject: [PATCH 8/9] dont panic if message fails to send --- src-tauri/src/oscbridge.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/oscbridge.rs b/src-tauri/src/oscbridge.rs index 8630d7be..d3a19439 100644 --- a/src-tauri/src/oscbridge.rs +++ b/src-tauri/src/oscbridge.rs @@ -58,14 +58,15 @@ pub fn init( /* ........................................................... Process queued messages ............................................................*/ - let mut prev_time = Instant::now(); loop { let mut message_queue = message_queue_clone.lock().await; message_queue.retain(|message| { - sock.send(&message.msg_buf).unwrap(); - prev_time = Instant::now(); + let result = sock.send(&message.msg_buf); + if result.is_err() { + println!("OSC Message failed to send, the server might no longer be available"); + } return false; }); From 15c15a9c36ff513c622aff655e0011c250d49b51 Mon Sep 17 00:00:00 2001 From: Jade Rowland Date: Mon, 4 Sep 2023 17:09:54 -0400 Subject: [PATCH 9/9] remove unused import --- src-tauri/src/oscbridge.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src-tauri/src/oscbridge.rs b/src-tauri/src/oscbridge.rs index d3a19439..c8a1ee09 100644 --- a/src-tauri/src/oscbridge.rs +++ b/src-tauri/src/oscbridge.rs @@ -5,7 +5,6 @@ use std::net::UdpSocket; use std::time::Duration; use std::sync::Arc; use tokio::sync::{ mpsc, Mutex }; -use tokio::time::Instant; use serde::Deserialize; use std::thread::sleep; pub struct OscMsg {