From f4032dad22a5a8af595aabd796032c734dc321e5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 16 Mar 2024 02:14:24 +0100 Subject: [PATCH] inline multichannel scopes --- packages/superdough/superdough.mjs | 35 ++++++++++++++------------- packages/webaudio/scope.mjs | 38 ++++++++++++++++++------------ packages/widgets/canvas.mjs | 6 +++++ 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 3e6448d9..9aa8ee3c 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -215,35 +215,34 @@ function getReverb(orbit, duration, fade, lp, dim, ir) { return reverbs[orbit]; } -export let analyser, analyserData /* s = {} */; +export let analysers = {}, + analysersData = {}; -export function getAnalyser(/* orbit, */ fftSize = 2048) { - if (!analyser /*s [orbit] */) { +export function getAnalyserById(id, fftSize = 1024) { + if (!analysers[id]) { const analyserNode = getAudioContext().createAnalyser(); analyserNode.fftSize = fftSize; // getDestination().connect(analyserNode); - analyser /* s[orbit] */ = analyserNode; - //analyserData = new Uint8Array(analyser.frequencyBinCount); - analyserData = new Float32Array(analyser.frequencyBinCount); + analysers[id] = analyserNode; + analysersData[id] = new Float32Array(analysers[id].frequencyBinCount); } - if (analyser /* s[orbit] */.fftSize !== fftSize) { - analyser /* s[orbit] */.fftSize = fftSize; - //analyserData = new Uint8Array(analyser.frequencyBinCount); - analyserData = new Float32Array(analyser.frequencyBinCount); + if (analysers[id].fftSize !== fftSize) { + analysers[id].fftSize = fftSize; + analysersData[id] = new Float32Array(analysers[id].frequencyBinCount); } - return analyser /* s[orbit] */; + return analysers[id]; } -export function getAnalyzerData(type = 'time') { +export function getAnalyzerData(type = 'time', id = 1) { const getter = { - time: () => analyser?.getFloatTimeDomainData(analyserData), - frequency: () => analyser?.getFloatFrequencyData(analyserData), + time: () => analysers[id]?.getFloatTimeDomainData(analysersData[id]), + frequency: () => analyser[id]?.getFloatFrequencyData(analysersData[id]), }[type]; if (!getter) { throw new Error(`getAnalyzerData: ${type} not supported. use one of ${Object.keys(getter).join(', ')}`); } getter(); - return analyserData; + return analysersData[id]; } function effectSend(input, effect, wet) { @@ -256,6 +255,8 @@ function effectSend(input, effect, wet) { export function resetGlobalEffects() { delays = {}; reverbs = {}; + analysers = {}; + analysersData = {}; } export const superdough = async (value, deadline, hapDuration) => { @@ -512,8 +513,8 @@ export const superdough = async (value, deadline, hapDuration) => { // analyser let analyserSend; if (analyze) { - const analyserNode = getAnalyser(/* orbit, */ 2 ** (fft + 5)); - analyserSend = effectSend(post, analyserNode, analyze); + const analyserNode = getAnalyserById(analyze, 2 ** (fft + 5)); + analyserSend = effectSend(post, analyserNode, 1); } // connect chain elements together diff --git a/packages/webaudio/scope.mjs b/packages/webaudio/scope.mjs index 0371366c..4337ec72 100644 --- a/packages/webaudio/scope.mjs +++ b/packages/webaudio/scope.mjs @@ -1,13 +1,21 @@ import { Pattern, clamp } from '@strudel/core'; import { getDrawContext } from '../draw/index.mjs'; -import { analyser, getAnalyzerData } from 'superdough'; +import { getAnalyzerData } from 'superdough'; export function drawTimeScope( analyser, - { align = true, color = 'white', thickness = 3, scale = 0.25, pos = 0.75, trigger = 0 } = {}, + { + align = true, + color = 'white', + thickness = 3, + scale = 0.25, + pos = 0.75, + trigger = 0, + ctx = getDrawContext(), + id = 1, + } = {}, ) { - const ctx = getDrawContext(); - const dataArray = getAnalyzerData('time'); + const dataArray = getAnalyzerData('time', id); ctx.lineWidth = thickness; ctx.strokeStyle = color; @@ -39,10 +47,9 @@ export function drawTimeScope( export function drawFrequencyScope( analyser, - { color = 'white', scale = 0.25, pos = 0.75, lean = 0.5, min = -150, max = 0 } = {}, + { color = 'white', scale = 0.25, pos = 0.75, lean = 0.5, min = -150, max = 0, ctx = getDrawContext(), id = 1 } = {}, ) { - const dataArray = getAnalyzerData('frequency'); - const ctx = getDrawContext(); + const dataArray = getAnalyzerData('frequency', id); const canvas = ctx.canvas; ctx.fillStyle = color; @@ -61,8 +68,7 @@ export function drawFrequencyScope( } } -function clearScreen(smear = 0, smearRGB = `0,0,0`) { - const ctx = getDrawContext(); +function clearScreen(smear = 0, smearRGB = `0,0,0`, ctx = getDrawContext()) { if (!smear) { ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); } else { @@ -84,9 +90,10 @@ function clearScreen(smear = 0, smearRGB = `0,0,0`) { * s("sawtooth").fscope() */ Pattern.prototype.fscope = function (config = {}) { - return this.analyze(1).draw(() => { - clearScreen(config.smear); - analyser && drawFrequencyScope(analyser, config); + let id = config.id ?? 1; + return this.analyze(id).draw(() => { + clearScreen(config.smear, '0,0,0', config.ctx); + analysers[id] && drawFrequencyScope(analyser, config); }); }; @@ -105,9 +112,10 @@ Pattern.prototype.fscope = function (config = {}) { * s("sawtooth").scope() */ Pattern.prototype.tscope = function (config = {}) { - return this.analyze(1).draw(() => { - clearScreen(config.smear); - analyser && drawTimeScope(analyser, config); + let id = config.id ?? 1; + return this.analyze(id).draw(() => { + clearScreen(config.smear, '0,0,0', config.ctx); + analysers[id] && drawTimeScope(analysers[id], config); }); }; diff --git a/packages/widgets/canvas.mjs b/packages/widgets/canvas.mjs index 5fd15356..14aa8911 100644 --- a/packages/widgets/canvas.mjs +++ b/packages/widgets/canvas.mjs @@ -21,3 +21,9 @@ registerWidget('twist', (id, options = {}, pat) => { const ctx = getCanvasWidget(id, options).getContext('2d'); return pat.spiral({ ...options, ctx, id }); }); + +registerWidget('osci', (id, options = {}, pat) => { + options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options }; + const ctx = getCanvasWidget(id, options).getContext('2d'); + return pat.scope({ ...options, ctx, id }); +});