From 94ccfa631e09146c0b1850dafda969e7afc34774 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 8 Aug 2024 17:25:45 -0400 Subject: [PATCH 1/5] offset calc --- packages/desktopbridge/oscbridge.mjs | 37 ++++++++++++++++++++-------- src-tauri/src/oscbridge.rs | 10 +++++--- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/packages/desktopbridge/oscbridge.mjs b/packages/desktopbridge/oscbridge.mjs index 81366367b..cfeaaf648 100644 --- a/packages/desktopbridge/oscbridge.mjs +++ b/packages/desktopbridge/oscbridge.mjs @@ -1,6 +1,7 @@ import { parseNumeral, Pattern, getEventOffsetMs } from '@strudel/core'; import { Invoke } from './utils.mjs'; +let offsetTime; Pattern.prototype.osc = function () { return this.onTrigger(async (time, hap, currentTime, cps = 1, targetTime) => { hap.ensureObjectValue(); @@ -12,9 +13,17 @@ Pattern.prototype.osc = function () { controls.note && (controls.note = parseNumeral(controls.note)); const params = []; + // console.log(time, currentTime) + const unixTimeSecs = Date.now() / 1000; - const timestamp = Math.round(Date.now() + getEventOffsetMs(targetTime, currentTime)); - + if (offsetTime == null) { + const unixTimeSecs = Date.now() / 1000; + offsetTime = unixTimeSecs - currentTime; + } + const timestamp = offsetTime + targetTime + // const timestamp = unixTimeSecs + (targetTime - currentTime) + + console.log(offsetTime) Object.keys(controls).forEach((key) => { const val = controls[key]; const value = typeof val === 'number' ? val.toString() : val; @@ -29,15 +38,23 @@ Pattern.prototype.osc = function () { }); }); - const messagesfromjs = []; - if (params.length) { - messagesfromjs.push({ target: '/dirt/play', timestamp, params }); + + if (params.length === 0) { + return } + const message = { target: '/dirt/play', timestamp, params }; + setTimeout(() => { + Invoke('sendosc', { messagesfromjs: [message] }); + }); + // const messagesfromjs = []; + // if (params.length) { + // messagesfromjs.push({ target: '/dirt/play', timestamp, params }); + // } - if (messagesfromjs.length) { - setTimeout(() => { - Invoke('sendosc', { messagesfromjs }); - }); - } + // if (messagesfromjs.length) { + // setTimeout(() => { + // Invoke('sendosc', { messagesfromjs }); + // }); + // } }); }; diff --git a/src-tauri/src/oscbridge.rs b/src-tauri/src/oscbridge.rs index c8dcf03af..912e4dbe5 100644 --- a/src-tauri/src/oscbridge.rs +++ b/src-tauri/src/oscbridge.rs @@ -6,13 +6,13 @@ use std::net::UdpSocket; use serde::Deserialize; use std::sync::Arc; use std::thread::sleep; -use std::time::Duration; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use tokio::sync::{mpsc, Mutex}; use crate::loggerbridge::Logger; pub struct OscMsg { pub msg_buf: Vec, - pub timestamp: u64, + pub timestamp: f64, } pub struct AsyncInputTransmit { @@ -106,7 +106,7 @@ pub struct Param { #[derive(Deserialize)] pub struct MessageFromJS { params: Vec, - timestamp: u64, + timestamp: f64, target: String, } // Called from JS @@ -127,9 +127,11 @@ pub async fn sendosc( args.push(OscType::String(p.value)); } } + // let start = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + let time_delay = Duration::from_secs_f64(m.timestamp); let duration_since_epoch = - Duration::from_millis(m.timestamp) + Duration::new(UNIX_OFFSET, 0); + time_delay + Duration::new(UNIX_OFFSET, 0); let seconds = u32::try_from(duration_since_epoch.as_secs()) .map_err(|_| "bit conversion failed for osc message timetag")?; From 7d2110d75fb991309add5f73cf36bcf31b110bd7 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 8 Aug 2024 20:35:12 -0400 Subject: [PATCH 2/5] clock fail strategy --- packages/desktopbridge/oscbridge.mjs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/desktopbridge/oscbridge.mjs b/packages/desktopbridge/oscbridge.mjs index cfeaaf648..91df30754 100644 --- a/packages/desktopbridge/oscbridge.mjs +++ b/packages/desktopbridge/oscbridge.mjs @@ -2,6 +2,12 @@ import { parseNumeral, Pattern, getEventOffsetMs } from '@strudel/core'; import { Invoke } from './utils.mjs'; let offsetTime; +let weight = 1; +let timeAtPrevOffsetSample; +let rollingOffsetTime; + + +// let prevTime = 0; Pattern.prototype.osc = function () { return this.onTrigger(async (time, hap, currentTime, cps = 1, targetTime) => { hap.ensureObjectValue(); @@ -14,12 +20,22 @@ Pattern.prototype.osc = function () { const params = []; // console.log(time, currentTime) + const unixTimeSecs = Date.now() / 1000; + const newOffsetTime = unixTimeSecs - currentTime; + + if (unixTimeSecs - timeAtPrevOffsetSample > 2) { + timeAtPrevOffsetSample = unixTimeSecs; + rollingOffsetTime = newOffsetTime; + } + - if (offsetTime == null) { - const unixTimeSecs = Date.now() / 1000; - offsetTime = unixTimeSecs - currentTime; + //account for the js clock freezing or resetting for some reason + if (offsetTime == null || Math.abs(rollingOffsetTime - offsetTime) > .1 + ) { + offsetTime = newOffsetTime; } + // prevTime = currentTime; const timestamp = offsetTime + targetTime // const timestamp = unixTimeSecs + (targetTime - currentTime) From 28334ec94da0fa302b6c2f9b1515acfd841f453e Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 8 Aug 2024 20:55:49 -0400 Subject: [PATCH 3/5] rolling clock average --- packages/desktopbridge/oscbridge.mjs | 37 +++++++++++----------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/packages/desktopbridge/oscbridge.mjs b/packages/desktopbridge/oscbridge.mjs index 91df30754..73a94a8c1 100644 --- a/packages/desktopbridge/oscbridge.mjs +++ b/packages/desktopbridge/oscbridge.mjs @@ -2,10 +2,10 @@ import { parseNumeral, Pattern, getEventOffsetMs } from '@strudel/core'; import { Invoke } from './utils.mjs'; let offsetTime; -let weight = 1; let timeAtPrevOffsetSample; let rollingOffsetTime; - +let prevOffsetTimes = [] +const averageArray = arr => arr.reduce((a, b) => a + b) / arr.length; // let prevTime = 0; Pattern.prototype.osc = function () { @@ -19,27 +19,27 @@ Pattern.prototype.osc = function () { controls.note && (controls.note = parseNumeral(controls.note)); const params = []; - // console.log(time, currentTime) - + + const unixTimeSecs = Date.now() / 1000; const newOffsetTime = unixTimeSecs - currentTime; - - if (unixTimeSecs - timeAtPrevOffsetSample > 2) { - timeAtPrevOffsetSample = unixTimeSecs; - rollingOffsetTime = newOffsetTime; + prevOffsetTimes.push(newOffsetTime) + if (prevOffsetTimes.length > 8) { + prevOffsetTimes.shift() + } + // every two seconds, the average of the previous 8 offset times is calculated + if ( unixTimeSecs - timeAtPrevOffsetSample > 2 || rollingOffsetTime == null) { + timeAtPrevOffsetSample = unixTimeSecs + rollingOffsetTime = averageArray(prevOffsetTimes); } - - //account for the js clock freezing or resetting for some reason + //account for the js clock freezing or resets set the new offset if (offsetTime == null || Math.abs(rollingOffsetTime - offsetTime) > .1 ) { - offsetTime = newOffsetTime; + offsetTime = rollingOffsetTime; } - // prevTime = currentTime; const timestamp = offsetTime + targetTime - // const timestamp = unixTimeSecs + (targetTime - currentTime) - console.log(offsetTime) Object.keys(controls).forEach((key) => { const val = controls[key]; const value = typeof val === 'number' ? val.toString() : val; @@ -62,15 +62,6 @@ Pattern.prototype.osc = function () { setTimeout(() => { Invoke('sendosc', { messagesfromjs: [message] }); }); - // const messagesfromjs = []; - // if (params.length) { - // messagesfromjs.push({ target: '/dirt/play', timestamp, params }); - // } - // if (messagesfromjs.length) { - // setTimeout(() => { - // Invoke('sendosc', { messagesfromjs }); - // }); - // } }); }; From 814babbf7a00c3dc5a7705c14330b501665e7d7b Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 8 Aug 2024 22:52:06 -0400 Subject: [PATCH 4/5] cleaning up --- packages/core/util.mjs | 3 ++ packages/desktopbridge/oscbridge.mjs | 41 +++++++++++++--------------- src-tauri/src/oscbridge.rs | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 03f8b4104..798870f03 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -91,6 +91,9 @@ export const midi2note = (n) => { // modulo that works with negative numbers e.g. _mod(-1, 3) = 2. Works on numbers (rather than patterns of numbers, as @mod@ from pattern.mjs does) export const _mod = (n, m) => ((n % m) + m) % m; +// average numbers in an array +export const averageArray = (arr) => arr.reduce((a, b) => a + b) / arr.length; + export function nanFallback(value, fallback = 0) { if (isNaN(Number(value))) { logger(`"${value}" is not a number, falling back to ${fallback}`, 'warning'); diff --git a/packages/desktopbridge/oscbridge.mjs b/packages/desktopbridge/oscbridge.mjs index 73a94a8c1..42a9a6d4a 100644 --- a/packages/desktopbridge/oscbridge.mjs +++ b/packages/desktopbridge/oscbridge.mjs @@ -1,13 +1,10 @@ -import { parseNumeral, Pattern, getEventOffsetMs } from '@strudel/core'; +import { parseNumeral, Pattern, averageArray } from '@strudel/core'; import { Invoke } from './utils.mjs'; let offsetTime; let timeAtPrevOffsetSample; -let rollingOffsetTime; -let prevOffsetTimes = [] -const averageArray = arr => arr.reduce((a, b) => a + b) / arr.length; +let prevOffsetTimes = []; -// let prevTime = 0; Pattern.prototype.osc = function () { return this.onTrigger(async (time, hap, currentTime, cps = 1, targetTime) => { hap.ensureObjectValue(); @@ -20,26 +17,28 @@ Pattern.prototype.osc = function () { const params = []; - const unixTimeSecs = Date.now() / 1000; const newOffsetTime = unixTimeSecs - currentTime; - prevOffsetTimes.push(newOffsetTime) + if (offsetTime == null) { + offsetTime = newOffsetTime; + } + prevOffsetTimes.push(newOffsetTime); if (prevOffsetTimes.length > 8) { - prevOffsetTimes.shift() + prevOffsetTimes.shift(); } - // every two seconds, the average of the previous 8 offset times is calculated - if ( unixTimeSecs - timeAtPrevOffsetSample > 2 || rollingOffsetTime == null) { - timeAtPrevOffsetSample = unixTimeSecs - rollingOffsetTime = averageArray(prevOffsetTimes); + // every two seconds, the average of the previous 8 offset times is calculated and used as a stable reference + // for calculating the timestamp that will be sent to the backend + if (timeAtPrevOffsetSample == null || unixTimeSecs - timeAtPrevOffsetSample > 2) { + timeAtPrevOffsetSample = unixTimeSecs; + const rollingOffsetTime = averageArray(prevOffsetTimes); + //account for the js clock freezing or resets set the new offset + if (Math.abs(rollingOffsetTime - offsetTime) > 0.01) { + offsetTime = rollingOffsetTime; + } } - //account for the js clock freezing or resets set the new offset - if (offsetTime == null || Math.abs(rollingOffsetTime - offsetTime) > .1 - ) { - offsetTime = rollingOffsetTime; - } - const timestamp = offsetTime + targetTime - + const timestamp = offsetTime + targetTime; + Object.keys(controls).forEach((key) => { const val = controls[key]; const value = typeof val === 'number' ? val.toString() : val; @@ -54,14 +53,12 @@ Pattern.prototype.osc = function () { }); }); - if (params.length === 0) { - return + return; } const message = { target: '/dirt/play', timestamp, params }; setTimeout(() => { Invoke('sendosc', { messagesfromjs: [message] }); }); - }); }; diff --git a/src-tauri/src/oscbridge.rs b/src-tauri/src/oscbridge.rs index 912e4dbe5..f6ac84a73 100644 --- a/src-tauri/src/oscbridge.rs +++ b/src-tauri/src/oscbridge.rs @@ -6,7 +6,7 @@ use std::net::UdpSocket; use serde::Deserialize; use std::sync::Arc; use std::thread::sleep; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::{Duration}; use tokio::sync::{mpsc, Mutex}; use crate::loggerbridge::Logger; From 1b01f41ffdf8ad1658316187ea93ab77decc2e80 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 8 Aug 2024 23:10:39 -0400 Subject: [PATCH 5/5] fix test --- test/__snapshots__/examples.test.mjs.snap | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 41aa8d84e..11b27c7a6 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5234,6 +5234,25 @@ exports[`runs examples > example "pickF" example index 1 1`] = ` ] `; +exports[`runs examples > example "pickmodRestart" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | note:C3 s:piano ]", + "[ 1/4 → 1/2 | note:D3 s:piano ]", + "[ 1/2 → 3/4 | note:E3 s:piano ]", + "[ 3/4 → 1/1 | note:C3 s:piano ]", + "[ 1/1 → 5/4 | note:C3 s:piano ]", + "[ 5/4 → 3/2 | note:D3 s:piano ]", + "[ 3/2 → 7/4 | note:E3 s:piano ]", + "[ 7/4 → 2/1 | note:C3 s:piano ]", + "[ 2/1 → 9/4 | note:E3 s:piano ]", + "[ 9/4 → 5/2 | note:F3 s:piano ]", + "[ 5/2 → 11/4 | note:G3 s:piano ]", + "[ 3/1 → 13/4 | note:E3 s:piano ]", + "[ 13/4 → 7/2 | note:F3 s:piano ]", + "[ 7/2 → 15/4 | note:G3 s:piano ]", +] +`; + exports[`runs examples > example "pitchwheel" example index 0 1`] = ` [ "[ 0/1 → 1/13 | note:C3 s:sawtooth cutoff:500 ]",