mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-11 13:48:34 +00:00
refactor onTrigger
This commit is contained in:
parent
5fc8f10602
commit
b668a2c0d2
@ -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);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -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,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -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);
|
||||
});
|
||||
};
|
||||
|
||||
@ -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 });
|
||||
});
|
||||
};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -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 });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user