From 8e4549d6d39d9d47a055ce86a4766e11780f752e Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 6 Apr 2024 17:07:54 -0400 Subject: [PATCH 1/4] mfix --- packages/core/clockworker.js | 1 + packages/core/evaluate.mjs | 2 ++ packages/core/neocyclist.mjs | 8 +++++--- packages/midi/midi.mjs | 10 +++++----- packages/webaudio/webaudio.mjs | 3 ++- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/core/clockworker.js b/packages/core/clockworker.js index 4c33de29..b10db186 100644 --- a/packages/core/clockworker.js +++ b/packages/core/clockworker.js @@ -46,6 +46,7 @@ const sendTick = (phase, duration, tick, time) => { num_seconds_at_cps_change, num_seconds_since_cps_change, cycle, + phase, }); num_ticks_since_cps_change++; }; diff --git a/packages/core/evaluate.mjs b/packages/core/evaluate.mjs index ea63d8df..93a3a4f5 100644 --- a/packages/core/evaluate.mjs +++ b/packages/core/evaluate.mjs @@ -34,6 +34,7 @@ function safeEval(str, options = {}) { str = `(async ()=>${str})()`; } const body = `"use strict";return (${str})`; + console.log(body); return Function(body)(); } @@ -48,5 +49,6 @@ export const evaluate = async (code, transpiler) => { // if no transpiler is given, we expect a single instruction (!wrapExpression) const options = { wrapExpression: !!transpiler }; let evaluated = await safeEval(code, options); + console.log(evaluated); return { mode: 'javascript', pattern: evaluated, meta }; }; diff --git a/packages/core/neocyclist.mjs b/packages/core/neocyclist.mjs index 278ddc8a..dab83f38 100644 --- a/packages/core/neocyclist.mjs +++ b/packages/core/neocyclist.mjs @@ -59,18 +59,19 @@ export class NeoCyclist { end, tickdeadline, cycle, + phase, } = payload; this.cps = cps; this.cycle = cycle; setTimeReference(num_seconds_at_cps_change, num_seconds_since_cps_change, tickdeadline); - processHaps(begin, end, num_cycles_at_cps_change, num_seconds_at_cps_change); + processHaps(begin, end, num_cycles_at_cps_change, num_seconds_at_cps_change, phase); this.time_at_last_tick_message = this.getTime(); }; - const processHaps = (begin, end, num_cycles_at_cps_change, seconds_at_cps_change) => { + const processHaps = (begin, end, num_cycles_at_cps_change, seconds_at_cps_change, phase) => { if (this.started === false) { return; } @@ -85,7 +86,8 @@ export class NeoCyclist { this.latency + this.worker_time_dif; const duration = hap.duration / this.cps; - onTrigger?.(hap, 0, duration, this.cps, targetTime); + const deadline = targetTime - phase; + onTrigger?.(hap, deadline, duration, this.cps, targetTime); } }); }; diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 54787f97..68c65fe2 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -112,17 +112,17 @@ Pattern.prototype.midi = function (output) { logger(`Midi device disconnected! Available: ${getMidiDeviceNamesString(outputs)}`), }); - return this.onTrigger((time, hap, currentTime, cps) => { + return this.onTrigger((time, hap, currentTime, cps, targetTime) => { if (!WebMidi.enabled) { console.log('not enabled'); return; } const device = getDevice(output, WebMidi.outputs); hap.ensureObjectValue(); - - const offset = (time - currentTime) * 1000; + //magic number to get audio engine to line up, can probably be calculated somehow + const latency = 0.03; // passing a string with a +num into the webmidi api adds an offset to the current time https://webmidijs.org/api/classes/Output - const timeOffsetString = `+${offset}`; + const timeOffsetString = `+${(targetTime - currentTime + latency) * 1000}`; // destructure value let { note, nrpnn, nrpv, ccn, ccv, midichan = 1, midicmd, gain = 1, velocity = 0.9 } = hap.value; @@ -130,7 +130,7 @@ Pattern.prototype.midi = function (output) { velocity = gain * velocity; // note off messages will often a few ms arrive late, try to prevent glitching by subtracting from the duration length - const duration = Math.floor((hap.duration.valueOf() / cps) * 1000 - 10); + const duration = (hap.duration.valueOf() / cps) * 1000 - 10; if (note != null) { const midiNumber = typeof note === 'number' ? note : noteToMidi(note); const midiNote = new Note(midiNumber, { attack: velocity, duration }); diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index f82a436a..dcb94347 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -17,8 +17,9 @@ const hap2value = (hap) => { export const webaudioOutputTrigger = (t, hap, ct, cps) => superdough(hap2value(hap), t - ct, hap.duration / cps, cps); // uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004 -export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => +export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => { superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration); +}; Pattern.prototype.webaudio = function () { return this.onTrigger(webaudioOutputTrigger); From 183d3ea4a19b4cb4ffad581d1ae0cc012643316c Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 6 Apr 2024 17:28:30 -0400 Subject: [PATCH 2/4] fix desktop midi --- packages/desktopbridge/midibridge.mjs | 6 ++++-- packages/midi/midi.mjs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/desktopbridge/midibridge.mjs b/packages/desktopbridge/midibridge.mjs index 065f9839..4bb9da29 100644 --- a/packages/desktopbridge/midibridge.mjs +++ b/packages/desktopbridge/midibridge.mjs @@ -6,9 +6,11 @@ const OFF_MESSAGE = 0x80; const CC_MESSAGE = 0xb0; Pattern.prototype.midi = function (output) { - return this.onTrigger((time, hap, currentTime, cps) => { + return this.onTrigger((time, hap, currentTime, cps, targetTime) => { let { note, nrpnn, nrpv, ccn, ccv, velocity = 0.9, gain = 1 } = hap.value; - const offset = (time - currentTime) * 1000; + //magic number to get audio engine to line up, can probably be calculated somehow + const latency = 0.03; + const offset = (targetTime - currentTime + latency) * 1000; velocity = Math.floor(gain * velocity * 100); const duration = Math.floor((hap.duration.valueOf() / cps) * 1000 - 10); const roundedOffset = Math.round(offset); diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 68c65fe2..ca1cc9b5 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -112,7 +112,7 @@ Pattern.prototype.midi = function (output) { logger(`Midi device disconnected! Available: ${getMidiDeviceNamesString(outputs)}`), }); - return this.onTrigger((time, hap, currentTime, cps, targetTime) => { + return this.onTrigger((time_deprecate, hap, currentTime, cps, targetTime) => { if (!WebMidi.enabled) { console.log('not enabled'); return; From cbfa09a2daa11a46485f61aa9d097f1dc9d6bdfa Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 6 Apr 2024 17:31:56 -0400 Subject: [PATCH 3/4] remove unessecary changes --- packages/core/clockworker.js | 1 - packages/core/neocyclist.mjs | 9 ++++----- packages/desktopbridge/midibridge.mjs | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/core/clockworker.js b/packages/core/clockworker.js index b10db186..4c33de29 100644 --- a/packages/core/clockworker.js +++ b/packages/core/clockworker.js @@ -46,7 +46,6 @@ const sendTick = (phase, duration, tick, time) => { num_seconds_at_cps_change, num_seconds_since_cps_change, cycle, - phase, }); num_ticks_since_cps_change++; }; diff --git a/packages/core/neocyclist.mjs b/packages/core/neocyclist.mjs index dab83f38..4600d0ef 100644 --- a/packages/core/neocyclist.mjs +++ b/packages/core/neocyclist.mjs @@ -12,6 +12,7 @@ export class NeoCyclist { this.cps = 0.5; this.lastTick = 0; // absolute time when last tick (clock callback) happened this.getTime = getTime; // get absolute time + this.time_at_last_tick_message = 0; this.num_cycles_at_cps_change = 0; this.onToggle = onToggle; @@ -59,19 +60,18 @@ export class NeoCyclist { end, tickdeadline, cycle, - phase, } = payload; this.cps = cps; this.cycle = cycle; setTimeReference(num_seconds_at_cps_change, num_seconds_since_cps_change, tickdeadline); - processHaps(begin, end, num_cycles_at_cps_change, num_seconds_at_cps_change, phase); + processHaps(begin, end, num_cycles_at_cps_change, num_seconds_at_cps_change); this.time_at_last_tick_message = this.getTime(); }; - const processHaps = (begin, end, num_cycles_at_cps_change, seconds_at_cps_change, phase) => { + const processHaps = (begin, end, num_cycles_at_cps_change, seconds_at_cps_change) => { if (this.started === false) { return; } @@ -86,8 +86,7 @@ export class NeoCyclist { this.latency + this.worker_time_dif; const duration = hap.duration / this.cps; - const deadline = targetTime - phase; - onTrigger?.(hap, deadline, duration, this.cps, targetTime); + onTrigger?.(hap, 0, duration, this.cps, targetTime); } }); }; diff --git a/packages/desktopbridge/midibridge.mjs b/packages/desktopbridge/midibridge.mjs index 4bb9da29..87f94750 100644 --- a/packages/desktopbridge/midibridge.mjs +++ b/packages/desktopbridge/midibridge.mjs @@ -6,7 +6,7 @@ const OFF_MESSAGE = 0x80; const CC_MESSAGE = 0xb0; Pattern.prototype.midi = function (output) { - return this.onTrigger((time, hap, currentTime, cps, targetTime) => { + return this.onTrigger((time_deprecate, hap, currentTime, cps, targetTime) => { let { note, nrpnn, nrpv, ccn, ccv, velocity = 0.9, gain = 1 } = hap.value; //magic number to get audio engine to line up, can probably be calculated somehow const latency = 0.03; From 0a6373f64c63417228f8745886ecb8e787e05fd9 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 6 Apr 2024 17:40:08 -0400 Subject: [PATCH 4/4] adjust latency --- packages/core/evaluate.mjs | 2 -- packages/desktopbridge/midibridge.mjs | 2 +- packages/midi/midi.mjs | 2 +- packages/webaudio/webaudio.mjs | 3 +-- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/core/evaluate.mjs b/packages/core/evaluate.mjs index 93a3a4f5..ea63d8df 100644 --- a/packages/core/evaluate.mjs +++ b/packages/core/evaluate.mjs @@ -34,7 +34,6 @@ function safeEval(str, options = {}) { str = `(async ()=>${str})()`; } const body = `"use strict";return (${str})`; - console.log(body); return Function(body)(); } @@ -49,6 +48,5 @@ export const evaluate = async (code, transpiler) => { // if no transpiler is given, we expect a single instruction (!wrapExpression) const options = { wrapExpression: !!transpiler }; let evaluated = await safeEval(code, options); - console.log(evaluated); return { mode: 'javascript', pattern: evaluated, meta }; }; diff --git a/packages/desktopbridge/midibridge.mjs b/packages/desktopbridge/midibridge.mjs index 87f94750..07a5f963 100644 --- a/packages/desktopbridge/midibridge.mjs +++ b/packages/desktopbridge/midibridge.mjs @@ -9,7 +9,7 @@ Pattern.prototype.midi = function (output) { return this.onTrigger((time_deprecate, hap, currentTime, cps, targetTime) => { let { note, nrpnn, nrpv, ccn, ccv, velocity = 0.9, gain = 1 } = hap.value; //magic number to get audio engine to line up, can probably be calculated somehow - const latency = 0.03; + const latency = 0.034; const offset = (targetTime - currentTime + latency) * 1000; velocity = Math.floor(gain * velocity * 100); const duration = Math.floor((hap.duration.valueOf() / cps) * 1000 - 10); diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index ca1cc9b5..93c7fd8f 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -120,7 +120,7 @@ Pattern.prototype.midi = function (output) { const device = getDevice(output, WebMidi.outputs); hap.ensureObjectValue(); //magic number to get audio engine to line up, can probably be calculated somehow - const latency = 0.03; + const latency = 0.034; // passing a string with a +num into the webmidi api adds an offset to the current time https://webmidijs.org/api/classes/Output const timeOffsetString = `+${(targetTime - currentTime + latency) * 1000}`; diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index dcb94347..f82a436a 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -17,9 +17,8 @@ const hap2value = (hap) => { export const webaudioOutputTrigger = (t, hap, ct, cps) => superdough(hap2value(hap), t - ct, hap.duration / cps, cps); // uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004 -export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => { +export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration); -}; Pattern.prototype.webaudio = function () { return this.onTrigger(webaudioOutputTrigger);