diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 59249601b..0ea9d75b7 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -22,7 +22,7 @@ import { import { noteToMidi, getControlName } from '@strudel/core'; import { Note } from 'webmidi'; import { getAudioContext } from '@strudel/webaudio'; -import { scheduleAtTime } from '../superdough/helpers.mjs'; +import { scheduleAtTime, ensureMinimalOutput } from '../superdough/helpers.mjs'; import { getMidiDeviceNamesString, getDevice } from './util.mjs'; import { MidiInput } from './input.mjs'; @@ -338,6 +338,8 @@ Pattern.prototype.midi = function (midiport, options = {}) { logger(`Midi device disconnected! Available: ${getMidiDeviceNamesString(outputs)}`), }); + ensureMinimalOutput(); + let p; // filtered clock offset let lastOffset; diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 07199aff6..bc39cc43f 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -12,6 +12,21 @@ export function gainNode(value) { return node; } +// this helper makes sure the audio context is "used", meaning it outputs something +// this prevents the browser from throttling timing accuracy +// it happened when only midi was running, the clock got more drifty without this +let constantNode, constantNodeAudioContext; +export function ensureMinimalOutput() { + if (constantNode && constantNodeAudioContext === getAudioContext()) { + return; + } + constantNodeAudioContext = getAudioContext(); + constantNode = new ConstantSourceNode(constantNodeAudioContext); + constantNode.offset.value = 1e-7; + constantNode.connect(constantNodeAudioContext.destination); + constantNode.start(); +} + export function effectSend(input, effect, wet) { const send = gainNode(wet); input.connect(send);