Merge pull request #219 from tidalcycles/webaudio-output

encapsulate webaudio output
This commit is contained in:
Felix Roos 2022-09-22 21:23:11 +02:00 committed by GitHub
commit cc54da5980
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -22,6 +22,21 @@ export const getAudioContext = () => {
return audioContext; return audioContext;
}; };
let destination;
const getDestination = () => {
const ctx = getAudioContext();
if (!destination) {
destination = ctx.createGain();
destination.connect(ctx.destination);
}
return destination;
};
export const panic = () => {
getDestination().gain.linearRampToValueAtTime(0, getAudioContext().currentTime + 0.01);
destination = null;
};
const getFilter = (type, frequency, Q) => { const getFilter = (type, frequency, Q) => {
const filter = getAudioContext().createBiquadFilter(); const filter = getAudioContext().createBiquadFilter();
filter.type = type; filter.type = type;
@ -87,7 +102,7 @@ const getSampleBufferSource = async (s, n, note) => {
} }
const bank = samples?.[s]; const bank = samples?.[s];
if (!bank) { if (!bank) {
throw new Error('sample not found:', s, 'try one of ' + Object.keys(samples)); throw new Error(`sample not found: "${s}", try one of ${Object.keys(samples).join(', ')}`);
} }
if (typeof bank !== 'object') { if (typeof bank !== 'object') {
throw new Error('wrong format for sample bank:', s); throw new Error('wrong format for sample bank:', s);
@ -147,21 +162,27 @@ function getWorklet(ac, processor, params) {
return node; return node;
} }
if (typeof window !== 'undefined') {
try { try {
loadWorklets(); loadWorklets();
} catch (err) { } catch (err) {
console.warn('could not load AudioWorklet effects coarse, crush and shape', err); console.warn('could not load AudioWorklet effects coarse, crush and shape', err);
} }
}
const cutGroups = []; const cutGroups = [];
Pattern.prototype.out = function () { // export const webaudioOutput = async (t, hap, ct, cps) => {
return this.onTrigger(async (t, hap, ct, cps) => { export const webaudioOutput = async (hap, deadline, hapDuration) => {
const hapDuration = hap.duration / cps;
try { try {
const ac = getAudioContext(); const ac = getAudioContext();
if (typeof hap.value !== 'object') {
throw new Error(
`hap.value ${hap.value} is not supported by webaudio output. Hint: append .note() or .s() to the end`,
);
}
// calculate correct time (tone.js workaround) // calculate correct time (tone.js workaround)
t = ac.currentTime + t - ct; let t = ac.currentTime + deadline;
// destructure value // destructure value
let { let {
freq, freq,
@ -308,17 +329,17 @@ Pattern.prototype.out = function () {
panner.pan.value = 2 * pan - 1; panner.pan.value = 2 * pan - 1;
chain.push(panner); chain.push(panner);
} }
// master out
/* const master = ac.createGain();
master.gain.value = 0.8 * gain;
chain.push(master); */
chain.push(ac.destination);
// connect chain elements together // connect chain elements together
chain.slice(1).reduce((last, current) => last.connect(current), chain[0]); chain.slice(1).reduce((last, current) => last.connect(current), chain[0]);
chain[chain.length - 1].connect(getDestination());
// disconnect all nodes when source node has ended: // disconnect all nodes when source node has ended:
chain[0].onended = () => chain.forEach((n) => n.disconnect()); chain[0].onended = () => chain.forEach((n) => n.disconnect());
} catch (e) { } catch (e) {
console.warn('.out error:', e); console.warn('.out error:', e);
} }
}); };
Pattern.prototype.out = function () {
// TODO: refactor (t, hap, ct, cps) to (hap, deadline, duration) ?
return this.onTrigger((t, hap, ct, cps) => webaudioOutput(hap, t - ct, hap.duration / cps));
}; };