From b668a2c0d2f33675f698f2e2fa6edd436e502b76 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 12 Nov 2022 20:17:57 +0100 Subject: [PATCH] refactor onTrigger --- packages/core/speak.mjs | 7 +--- packages/midi/midi.mjs | 78 +++++++++++++++++------------------- packages/osc/osc.mjs | 44 ++++++++++---------- packages/serial/serial.mjs | 30 ++++++-------- packages/tone/tone.mjs | 47 ++++++++++------------ packages/webdirt/webdirt.mjs | 57 +++++++++++++------------- 6 files changed, 122 insertions(+), 141 deletions(-) diff --git a/packages/core/speak.mjs b/packages/core/speak.mjs index caad36be..2e9ba80a 100644 --- a/packages/core/speak.mjs +++ b/packages/core/speak.mjs @@ -32,11 +32,8 @@ function speak(words, lang, voice) { } Pattern.prototype._speak = function (lang, voice) { - return this._withHap((hap) => { - const onTrigger = (time, hap) => { - speak(hap.value, lang, voice); - }; - return hap.setContext({ ...hap.context, onTrigger }); + return this.onTrigger((_, hap) => { + speak(hap.value, lang, voice); }); }; diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 216a112e..c59757fb 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -65,46 +65,42 @@ Pattern.prototype.midi = async function (output, channel = 1) { }')`, ); } - return this._withHap((hap) => { - // const onTrigger = (time: number, hap: any) => { - const onTrigger = (time, hap) => { - let note = getPlayableNoteValue(hap); - const velocity = hap.context?.velocity ?? 0.9; - if (!isNote(note)) { - throw new Error('not a note: ' + note); - } - if (!WebMidi.enabled) { - throw new Error(`🎹 WebMidi is not enabled. Supported Browsers: https://caniuse.com/?search=webmidi`); - } - if (!WebMidi.outputs.length) { - throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`); - } - let device; - if (typeof output === 'number') { - device = WebMidi.outputs[output]; - } else if (typeof output === 'string') { - device = outputByName(output); - } else { - device = WebMidi.outputs[0]; - } - if (!device) { - throw new Error( - `🔌 MIDI device '${output ? output : ''}' not found. Use one of ${WebMidi.outputs - .map((o) => `'${o.name}'`) - .join(' | ')}`, - ); - } - // console.log('midi', value, output); - const timingOffset = WebMidi.time - getAudioContext().currentTime * 1000; - time = time * 1000 + timingOffset; - // const inMs = '+' + (time - Tone.getContext().currentTime) * 1000; - // await enableWebMidi() - device.playNote(note, channel, { - time, - duration: hap.duration.valueOf() * 1000 - 5, - attack: velocity, - }); - }; - return hap.setContext({ ...hap.context, onTrigger }); + return this.onTrigger((time, hap) => { + let note = getPlayableNoteValue(hap); + const velocity = hap.context?.velocity ?? 0.9; + if (!isNote(note)) { + throw new Error('not a note: ' + note); + } + if (!WebMidi.enabled) { + throw new Error(`🎹 WebMidi is not enabled. Supported Browsers: https://caniuse.com/?search=webmidi`); + } + if (!WebMidi.outputs.length) { + throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`); + } + let device; + if (typeof output === 'number') { + device = WebMidi.outputs[output]; + } else if (typeof output === 'string') { + device = outputByName(output); + } else { + device = WebMidi.outputs[0]; + } + if (!device) { + throw new Error( + `🔌 MIDI device '${output ? output : ''}' not found. Use one of ${WebMidi.outputs + .map((o) => `'${o.name}'`) + .join(' | ')}`, + ); + } + // console.log('midi', value, output); + const timingOffset = WebMidi.time - getAudioContext().currentTime * 1000; + time = time * 1000 + timingOffset; + // const inMs = '+' + (time - Tone.getContext().currentTime) * 1000; + // await enableWebMidi() + device.playNote(note, channel, { + time, + duration: hap.duration.valueOf() * 1000 - 5, + attack: velocity, + }); }); }; diff --git a/packages/osc/osc.mjs b/packages/osc/osc.mjs index fd1b1c63..15909203 100644 --- a/packages/osc/osc.mjs +++ b/packages/osc/osc.mjs @@ -15,12 +15,13 @@ function connect() { const osc = new OSC(); osc.open(); osc.on('open', () => { - logger('OSC connected!'); + const url = osc.options?.plugin?.socket?.url; + logger(`[osc] connected${url ? ` to ${url}` : ''}`); resolve(osc); }); osc.on('close', () => { connection = undefined; // allows new connection afterwards - console.log('osc connection closed'); + console.log('[osc] disconnected'); reject('OSC connection closed'); }); osc.on('error', (err) => reject(err)); @@ -45,26 +46,23 @@ let startedAt = -1; */ Pattern.prototype.osc = async function () { const osc = await connect(); - return this._withHap((hap) => { - const onTrigger = (time, hap, currentTime, cps = 1) => { - const cycle = hap.wholeOrPart().begin.valueOf(); - const delta = hap.duration.valueOf(); - // time should be audio time of onset - // currentTime should be current time of audio context (slightly before time) - if (startedAt < 0) { - startedAt = Date.now() - currentTime * 1000; - } - 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 keyvals = Object.entries(controls).flat(); - const ts = Math.floor(startedAt + (time + latency) * 1000); - const message = new OSC.Message('/dirt/play', ...keyvals); - const bundle = new OSC.Bundle([message], ts); - bundle.timestamp(ts); // workaround for https://github.com/adzialocha/osc-js/issues/60 - osc.send(bundle); - }; - return hap.setContext({ ...hap.context, onTrigger }); + return this.onTrigger((time, hap, currentTime, cps = 1) => { + const cycle = hap.wholeOrPart().begin.valueOf(); + const delta = hap.duration.valueOf(); + // time should be audio time of onset + // currentTime should be current time of audio context (slightly before time) + if (startedAt < 0) { + startedAt = Date.now() - currentTime * 1000; + } + 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 keyvals = Object.entries(controls).flat(); + const ts = Math.floor(startedAt + (time + latency) * 1000); + const message = new OSC.Message('/dirt/play', ...keyvals); + const bundle = new OSC.Bundle([message], ts); + bundle.timestamp(ts); // workaround for https://github.com/adzialocha/osc-js/issues/60 + osc.send(bundle); }); }; diff --git a/packages/serial/serial.mjs b/packages/serial/serial.mjs index c2202b14..4452195d 100644 --- a/packages/serial/serial.mjs +++ b/packages/serial/serial.mjs @@ -9,7 +9,7 @@ import { Pattern, isPattern } from '@strudel.cycles/core'; var serialWriter; var choosing = false; -export async function getWriter(br=38400) { +export async function getWriter(br = 38400) { if (choosing) { return; } @@ -24,11 +24,10 @@ export async function getWriter(br=38400) { const writableStreamClosed = textEncoder.readable.pipeTo(port.writable); const writer = textEncoder.writable.getWriter(); serialWriter = function (message) { - writer.write(message) - } - } - else { - throw('Webserial is not available in this browser.') + writer.write(message); + }; + } else { + throw 'Webserial is not available in this browser.'; } } @@ -40,7 +39,7 @@ Pattern.prototype.serial = function (...args) { getWriter(...args); } const onTrigger = (time, hap, currentTime) => { - var message = ""; + var message = ''; if (typeof hap.value === 'object') { if ('action' in hap.value) { message += hap.value['action'] + '('; @@ -51,26 +50,23 @@ Pattern.prototype.serial = function (...args) { } if (first) { first = false; + } else { + message += ','; } - else { - message +=','; - } - message += `${key}:${val}` + message += `${key}:${val}`; } message += ')'; - } - else { + } else { for (const [key, val] of Object.entries(hap.value)) { - message += `${key}:${val};` + message += `${key}:${val};`; } } - } - else { + } else { message = hap.value; } const offset = (time - currentTime + latency) * 1000; window.setTimeout(serialWriter, offset, message); }; - return hap.setContext({ ...hap.context, onTrigger }); + return hap.setContext({ ...hap.context, onTrigger, dominantTrigger: true }); }); }; diff --git a/packages/tone/tone.mjs b/packages/tone/tone.mjs index 47ab0f7e..b5ea4b9e 100644 --- a/packages/tone/tone.mjs +++ b/packages/tone/tone.mjs @@ -50,32 +50,29 @@ export const getDefaultSynth = () => { // with this function, you can play the pattern with any tone synth Pattern.prototype.tone = function (instrument) { - return this._withHap((hap) => { - const onTrigger = (time, hap) => { - let note; - let velocity = hap.context?.velocity ?? 0.75; - if (instrument instanceof PluckSynth) { - note = getPlayableNoteValue(hap); - instrument.triggerAttack(note, time); - } else if (instrument instanceof NoiseSynth) { - instrument.triggerAttackRelease(hap.duration.valueOf(), time); // noise has no value - } else if (instrument instanceof Sampler) { - note = getPlayableNoteValue(hap); - instrument.triggerAttackRelease(note, hap.duration.valueOf(), time, velocity); - } else if (instrument instanceof Players) { - if (!instrument.has(hap.value)) { - throw new Error(`name "${hap.value}" not defined for players`); - } - const player = instrument.player(hap.value); - // velocity ? - player.start(time); - player.stop(time + hap.duration.valueOf()); - } else { - note = getPlayableNoteValue(hap); - instrument.triggerAttackRelease(note, hap.duration.valueOf(), time, velocity); + return this.onTrigger((time, hap) => { + let note; + let velocity = hap.context?.velocity ?? 0.75; + if (instrument instanceof PluckSynth) { + note = getPlayableNoteValue(hap); + instrument.triggerAttack(note, time); + } else if (instrument instanceof NoiseSynth) { + instrument.triggerAttackRelease(hap.duration.valueOf(), time); // noise has no value + } else if (instrument instanceof Sampler) { + note = getPlayableNoteValue(hap); + instrument.triggerAttackRelease(note, hap.duration.valueOf(), time, velocity); + } else if (instrument instanceof Players) { + if (!instrument.has(hap.value)) { + throw new Error(`name "${hap.value}" not defined for players`); } - }; - return hap.setContext({ ...hap.context, instrument, onTrigger }); + const player = instrument.player(hap.value); + // velocity ? + player.start(time); + player.stop(time + hap.duration.valueOf()); + } else { + note = getPlayableNoteValue(hap); + instrument.triggerAttackRelease(note, hap.duration.valueOf(), time, velocity); + } }); }; diff --git a/packages/webdirt/webdirt.mjs b/packages/webdirt/webdirt.mjs index 467ce416..500d7373 100644 --- a/packages/webdirt/webdirt.mjs +++ b/packages/webdirt/webdirt.mjs @@ -62,37 +62,34 @@ export function loadWebDirt(config) { */ Pattern.prototype.webdirt = function () { // create a WebDirt object and initialize Web Audio context - return this._withHap((hap) => { - const onTrigger = async (time, e, currentTime) => { - if (!webDirt) { - throw new Error('WebDirt not initialized!'); - } - const deadline = time - currentTime; - const { s, n = 0, ...rest } = e.value || {}; - if (!s) { - console.warn('Pattern.webdirt: no "s" was set!'); - } - const samples = getLoadedSamples(); - if (!samples?.[s]) { - // try default samples - webDirt.playSample({ s, n, ...rest }, deadline); - return; - } - if (!samples?.[s]) { - console.warn(`Pattern.webdirt: sample "${s}" not found in loaded samples`, samples); + return this.onTrigger(async (time, e, currentTime) => { + if (!webDirt) { + throw new Error('WebDirt not initialized!'); + } + const deadline = time - currentTime; + const { s, n = 0, ...rest } = e.value || {}; + if (!s) { + console.warn('Pattern.webdirt: no "s" was set!'); + } + const samples = getLoadedSamples(); + if (!samples?.[s]) { + // try default samples + webDirt.playSample({ s, n, ...rest }, deadline); + return; + } + if (!samples?.[s]) { + console.warn(`Pattern.webdirt: sample "${s}" not found in loaded samples`, samples); + } else { + const bank = samples[s]; + const sampleUrl = bank[n % bank.length]; + const buffer = getLoadedBuffer(sampleUrl); + if (!buffer) { + console.log(`Pattern.webdirt: load ${s}:${n} from ${sampleUrl}`); + loadBuffer(sampleUrl, webDirt.ac); } else { - const bank = samples[s]; - const sampleUrl = bank[n % bank.length]; - const buffer = getLoadedBuffer(sampleUrl); - if (!buffer) { - console.log(`Pattern.webdirt: load ${s}:${n} from ${sampleUrl}`); - loadBuffer(sampleUrl, webDirt.ac); - } else { - const msg = { buffer: { buffer }, ...rest }; - webDirt.playSample(msg, deadline); - } + const msg = { buffer: { buffer }, ...rest }; + webDirt.playSample(msg, deadline); } - }; - return hap.setContext({ ...hap.context, onTrigger }); + } }); };