Merge pull request 'ontrigger-refactoring' (#1442) from ontrigger-refactoring into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1442
This commit is contained in:
commit
d0a544d7a7
15 changed files with 17 additions and 42 deletions
|
|
@ -67,6 +67,7 @@ export class Cyclist {
|
||||||
// the following line is dumb and only here for backwards compatibility
|
// the following line is dumb and only here for backwards compatibility
|
||||||
// see https://codeberg.org/uzu/strudel/pulls/1004
|
// see https://codeberg.org/uzu/strudel/pulls/1004
|
||||||
const deadline = targetTime - phase;
|
const deadline = targetTime - phase;
|
||||||
|
// this onTrigger has another signature
|
||||||
onTrigger?.(hap, deadline, duration, this.cps, targetTime);
|
onTrigger?.(hap, deadline, duration, this.cps, targetTime);
|
||||||
if (hap.value.cps !== undefined && this.cps != hap.value.cps) {
|
if (hap.value.cps !== undefined && this.cps != hap.value.cps) {
|
||||||
this.cps = hap.value.cps;
|
this.cps = hap.value.cps;
|
||||||
|
|
|
||||||
|
|
@ -3263,7 +3263,7 @@ export const slice = register(
|
||||||
* s("bd!8").onTriggerTime((hap) => {console.info(hap)})
|
* s("bd!8").onTriggerTime((hap) => {console.info(hap)})
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.onTriggerTime = function (func) {
|
Pattern.prototype.onTriggerTime = function (func) {
|
||||||
return this.onTrigger((t_deprecate, hap, currentTime, cps = 1, targetTime) => {
|
return this.onTrigger((hap, currentTime, _cps, targetTime) => {
|
||||||
const diff = targetTime - currentTime;
|
const diff = targetTime - currentTime;
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
func(hap);
|
func(hap);
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,7 @@ export function repl({
|
||||||
export const getTrigger =
|
export const getTrigger =
|
||||||
({ getTime, defaultOutput }) =>
|
({ getTime, defaultOutput }) =>
|
||||||
async (hap, deadline, duration, cps, t) => {
|
async (hap, deadline, duration, cps, t) => {
|
||||||
|
// ^ this signature is different from hap.context.onTrigger, as set by Pattern.onTrigger(onTrigger)
|
||||||
// TODO: get rid of deadline after https://codeberg.org/uzu/strudel/pulls/1004
|
// TODO: get rid of deadline after https://codeberg.org/uzu/strudel/pulls/1004
|
||||||
try {
|
try {
|
||||||
if (!hap.context.onTrigger || !hap.context.dominantTrigger) {
|
if (!hap.context.onTrigger || !hap.context.dominantTrigger) {
|
||||||
|
|
@ -252,7 +253,7 @@ export const getTrigger =
|
||||||
}
|
}
|
||||||
if (hap.context.onTrigger) {
|
if (hap.context.onTrigger) {
|
||||||
// call signature of output / onTrigger is different...
|
// call signature of output / onTrigger is different...
|
||||||
await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps, t);
|
await hap.context.onTrigger(hap, getTime(), cps, t);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger(`[cyclist] error: ${err.message}`, 'error');
|
logger(`[cyclist] error: ${err.message}`, 'error');
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ function triggerSpeech(words, lang, voice) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const speak = register('speak', function (lang, voice, pat) {
|
export const speak = register('speak', function (lang, voice, pat) {
|
||||||
return pat.onTrigger((_, hap) => {
|
return pat.onTrigger((hap) => {
|
||||||
triggerSpeech(hap.value, lang, voice);
|
triggerSpeech(hap.value, lang, voice);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export const csound = register('csound', (instrument, pat) => {
|
||||||
instrument = instrument || 'triangle';
|
instrument = instrument || 'triangle';
|
||||||
init(); // not async to support csound inside other patterns + to be able to call pattern methods after it
|
init(); // not async to support csound inside other patterns + to be able to call pattern methods after it
|
||||||
// TODO: find a alternative way to wait for csound to load (to wait with first time playback)
|
// TODO: find a alternative way to wait for csound to load (to wait with first time playback)
|
||||||
return pat.onTrigger((time_deprecate, hap, currentTime, _cps, targetTime) => {
|
return pat.onTrigger((hap, currentTime, _cps, targetTime) => {
|
||||||
if (!_csound) {
|
if (!_csound) {
|
||||||
logger('[csound] not loaded yet', 'warning');
|
logger('[csound] not loaded yet', 'warning');
|
||||||
return;
|
return;
|
||||||
|
|
@ -142,7 +142,7 @@ export const csoundm = register('csoundm', (instrument, pat) => {
|
||||||
p1 = `"${instrument}"`;
|
p1 = `"${instrument}"`;
|
||||||
}
|
}
|
||||||
init(); // not async to support csound inside other patterns + to be able to call pattern methods after it
|
init(); // not async to support csound inside other patterns + to be able to call pattern methods after it
|
||||||
return pat.onTrigger((tidal_time, hap) => {
|
return pat.onTrigger((hap, currentTime, _cps, targetTime) => {
|
||||||
if (!_csound) {
|
if (!_csound) {
|
||||||
logger('[csound] not loaded yet', 'warning');
|
logger('[csound] not loaded yet', 'warning');
|
||||||
return;
|
return;
|
||||||
|
|
@ -151,7 +151,7 @@ export const csoundm = register('csoundm', (instrument, pat) => {
|
||||||
throw new Error('csound only support objects as hap values');
|
throw new Error('csound only support objects as hap values');
|
||||||
}
|
}
|
||||||
// Time in seconds counting from now.
|
// Time in seconds counting from now.
|
||||||
const p2 = tidal_time - getAudioContext().currentTime;
|
const p2 = targetTime - currentTime;
|
||||||
const p3 = hap.duration.valueOf() + 0;
|
const p3 = hap.duration.valueOf() + 0;
|
||||||
const frequency = getFrequency(hap);
|
const frequency = getFrequency(hap);
|
||||||
let { gain = 1, velocity = 0.9 } = hap.value;
|
let { gain = 1, velocity = 0.9 } = hap.value;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ const OFF_MESSAGE = 0x80;
|
||||||
const CC_MESSAGE = 0xb0;
|
const CC_MESSAGE = 0xb0;
|
||||||
|
|
||||||
Pattern.prototype.midi = function (output) {
|
Pattern.prototype.midi = function (output) {
|
||||||
return this.onTrigger((time_deprecate, hap, currentTime, cps, targetTime) => {
|
return this.onTrigger((hap, currentTime, cps, targetTime) => {
|
||||||
let { note, nrpnn, nrpv, ccn, ccv, velocity = 0.9, gain = 1 } = hap.value;
|
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
|
//magic number to get audio engine to line up, can probably be calculated somehow
|
||||||
const latencyMs = 34;
|
const latencyMs = 34;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { Invoke } from './utils.mjs';
|
||||||
|
|
||||||
const collator = new ClockCollator({});
|
const collator = new ClockCollator({});
|
||||||
|
|
||||||
export async function oscTriggerTauri(t_deprecate, hap, currentTime, cps = 1, targetTime) {
|
export async function oscTriggerTauri(hap, currentTime, cps = 1, targetTime) {
|
||||||
const controls = parseControlsFromHap(hap, cps);
|
const controls = parseControlsFromHap(hap, cps);
|
||||||
const params = [];
|
const params = [];
|
||||||
const timestamp = collator.calculateTimestamp(currentTime, targetTime);
|
const timestamp = collator.calculateTimestamp(currentTime, targetTime);
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,7 @@ Pattern.prototype.midi = function (midiport, options = {}) {
|
||||||
logger(`Midi device disconnected! Available: ${getMidiDeviceNamesString(outputs)}`),
|
logger(`Midi device disconnected! Available: ${getMidiDeviceNamesString(outputs)}`),
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.onTrigger((time_deprecate, hap, currentTime, cps, targetTime) => {
|
return this.onTrigger((hap, currentTime, cps, targetTime) => {
|
||||||
if (!WebMidi.enabled) {
|
if (!WebMidi.enabled) {
|
||||||
logger('Midi not enabled');
|
logger('Midi not enabled');
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export function parseControlsFromHap(hap, cps) {
|
||||||
|
|
||||||
const collator = new ClockCollator({});
|
const collator = new ClockCollator({});
|
||||||
|
|
||||||
export async function oscTrigger(t_deprecate, hap, currentTime, cps = 1, targetTime) {
|
export async function oscTrigger(hap, currentTime, cps = 1, targetTime) {
|
||||||
const osc = await connect();
|
const osc = await connect();
|
||||||
const controls = parseControlsFromHap(hap, cps);
|
const controls = parseControlsFromHap(hap, cps);
|
||||||
const keyvals = Object.entries(controls).flat();
|
const keyvals = Object.entries(controls).flat();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { getAudioContext, registerSound } from '@strudel/webaudio';
|
||||||
import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato';
|
import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato';
|
||||||
|
|
||||||
Pattern.prototype.soundfont = function (sf, n = 0) {
|
Pattern.prototype.soundfont = function (sf, n = 0) {
|
||||||
return this.onTrigger((time_deprecate, h, ct, cps, targetTime) => {
|
return this.onTrigger((h, ct, cps, targetTime) => {
|
||||||
const ctx = getAudioContext();
|
const ctx = getAudioContext();
|
||||||
const note = getPlayableNoteValue(h);
|
const note = getPlayableNoteValue(h);
|
||||||
const preset = sf.presets[n % sf.presets.length];
|
const preset = sf.presets[n % sf.presets.length];
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,6 @@ export const dough = async (code) => {
|
||||||
worklet.node.connect(ac.destination);
|
worklet.node.connect(ac.destination);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function doughTrigger(time_deprecate, hap, currentTime, cps, targetTime) {
|
export function doughTrigger(hap, currentTime, cps, targetTime) {
|
||||||
window.postMessage({ time: targetTime, dough: hap.value, currentTime, duration: hap.duration, cps });
|
window.postMessage({ time: targetTime, dough: hap.value, currentTime, duration: hap.duration, cps });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -462,8 +462,8 @@ function mapChannelNumbers(channels) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const superdough = async (value, t, hapDuration, cps = 0.5) => {
|
export const superdough = async (value, t, hapDuration, cps = 0.5) => {
|
||||||
|
// new: t is always expected to be the absolute target onset time
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
t = typeof t === 'string' && t.startsWith('=') ? Number(t.slice(1)) : ac.currentTime + t;
|
|
||||||
let { stretch } = value;
|
let { stretch } = value;
|
||||||
if (stretch != null) {
|
if (stretch != null) {
|
||||||
//account for phase vocoder latency
|
//account for phase vocoder latency
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,10 @@ const hap2value = (hap) => {
|
||||||
return hap.value;
|
return hap.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
// uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004
|
||||||
|
// TODO: refactor output callbacks to eliminate deadline
|
||||||
export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => {
|
export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => {
|
||||||
return superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration, cps);
|
return superdough(hap2value(hap), t, hapDuration, cps);
|
||||||
};
|
|
||||||
|
|
||||||
Pattern.prototype.webaudio = function () {
|
|
||||||
return this.onTrigger(webaudioOutputTrigger);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function webaudioRepl(options = {}) {
|
export function webaudioRepl(options = {}) {
|
||||||
|
|
|
||||||
|
|
@ -359,28 +359,6 @@ stack(
|
||||||
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
|
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
|
||||||
).note().piano().slow(2)`;
|
).note().piano().slow(2)`;
|
||||||
|
|
||||||
/*
|
|
||||||
export const customTrigger = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
|
||||||
// by Felix Roos
|
|
||||||
stack(
|
|
||||||
freq("55 [110,165] 110 [220,275]".mul("<1 <3/4 2/3>>").struct("x(3,8)").layer(x=>x.mul("1.006,.995"))),
|
|
||||||
freq("440(5,8)".clip(.18).mul("<1 3/4 2 2/3>")).gain(perlin.range(.2,.8))
|
|
||||||
).s("<sawtooth square>/2")
|
|
||||||
.onTrigger((t,hap,ct)=>{
|
|
||||||
const ac = Tone.getContext().rawContext;
|
|
||||||
t = ac.currentTime + t - ct;
|
|
||||||
const { freq, s, gain = 1 } = hap.value;
|
|
||||||
const master = ac.createGain();
|
|
||||||
master.gain.value = 0.1 * gain;
|
|
||||||
master.connect(ac.destination);
|
|
||||||
const o = ac.createOscillator();
|
|
||||||
o.type = s || 'triangle';
|
|
||||||
o.frequency.value = Number(freq);
|
|
||||||
o.connect(master);
|
|
||||||
o.start(t);
|
|
||||||
o.stop(t + hap.duration);
|
|
||||||
}).stack(s("bd(3,8),hh*4,~ sd").webdirt())`; */
|
|
||||||
|
|
||||||
export const swimmingWithSoundfonts = `// Koji Kondo - Swimming (Super Mario World)
|
export const swimmingWithSoundfonts = `// Koji Kondo - Swimming (Super Mario World)
|
||||||
stack(
|
stack(
|
||||||
n(
|
n(
|
||||||
|
|
|
||||||
|
|
@ -647,7 +647,6 @@
|
||||||
"evaluate"
|
"evaluate"
|
||||||
],
|
],
|
||||||
"/packages/webaudio/webaudio.mjs": [
|
"/packages/webaudio/webaudio.mjs": [
|
||||||
"webaudioOutputTrigger",
|
|
||||||
"webaudioOutput",
|
"webaudioOutput",
|
||||||
"webaudioRepl"
|
"webaudioRepl"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue