1/* 2dough.mjs 3Copyright (C) 2022 Strudel contributors - see <https://codeberg.org/uzu/strudel/src/branch/main/packages/osc/osc.mjs> 4This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. 5*/ 6 7import { register, noteToMidi } from '@strudel/core'; 8import { Dough, doughsamples } from 'dough-synth'; 9import { getAudioContext, ensureMinimalOutput } from '@strudel/webaudio'; 10import wasm from 'dough-synth/dough.wasm?url'; 11import workletCode from 'dough-synth/dough.js?raw'; 12 13Object.assign(globalThis, { doughsamples }); 14 15let D; 16/** 17 * 18 * initializes dough ahead of time. you don't need to use this, but if you do, you can await it, so dough is ready before the pattern starts. 19 * 20 * @name initDough 21 * @tags external_io 22 * @memberof Pattern 23 * @example 24 * await initDough() 25 * $: chord("<Dm9 Dm11 Dm7>").offset("<-1 0 1 0>").voicing() 26 * .phaser(3).fm(1.4).fmh(1.01) 27 * .dough() 28 */ 29export function initDough() { 30 if (!D || D.audioContext !== getAudioContext()) { 31 D = new Dough({ 32 wasm, 33 workletCode, 34 audioContext: getAudioContext(), 35 }); 36 } 37 return D.ready; 38} 39 40export async function doughTrigger(hap, _currentTime, cps = 1, targetTime) { 41 const offset = D.context_offset?.[0]; 42 if (!offset) { 43 return; // not ready 44 } 45 hap.ensureObjectValue(); 46 const event = { 47 dough: 'play', 48 ...hap.value, 49 time: targetTime - offset, 50 duration: hap.duration / cps, 51 }; 52 if (typeof event.note === 'string') { 53 event.note = noteToMidi(event.note); 54 } 55 D.evaluate(event); 56} 57 58/** 59 * 60 * Uses dough as the audio engine. more info at https://dough.strudel.cc 61 * 62 * @name dough 63 * @tags external_io 64 * @memberof Pattern 65 * @returns Pattern 66 * @example 67 * $: chord("<Dm9 Dm11 Dm7>").offset("<-1 0 1 0>").voicing() 68 * .phaser(3).fm(1.4).fmh(1.01) 69 * .dough() 70 */ 71export const dough = register('dough', (pat) => { 72 initDough(); 73 ensureMinimalOutput(); 74 return pat.onTrigger(doughTrigger); 75});