From b08a0b8102611f62ea60a75df5da5047698693a2 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 6 Mar 2023 23:11:09 +0100 Subject: [PATCH] use nanostore for soundmap + rename tab samples to sounds + listed sounds are now reactive --- packages/webaudio/package.json | 3 ++- packages/webaudio/sampler.mjs | 5 ----- packages/webaudio/synth.mjs | 2 +- packages/webaudio/webaudio.mjs | 22 ++++++++++++---------- pnpm-lock.yaml | 2 ++ website/src/repl/Footer.jsx | 23 +++++++++++++---------- website/src/repl/prebake.mjs | 6 +++--- website/src/styles/index.css | 2 +- 8 files changed, 34 insertions(+), 31 deletions(-) diff --git a/packages/webaudio/package.json b/packages/webaudio/package.json index 3bc7de38..74b155da 100644 --- a/packages/webaudio/package.json +++ b/packages/webaudio/package.json @@ -34,7 +34,8 @@ }, "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { - "@strudel.cycles/core": "workspace:*" + "@strudel.cycles/core": "workspace:*", + "nanostores": "^0.7.4" }, "devDependencies": { "vite": "^3.2.2" diff --git a/packages/webaudio/sampler.mjs b/packages/webaudio/sampler.mjs index 219b0581..44fbef0b 100644 --- a/packages/webaudio/sampler.mjs +++ b/packages/webaudio/sampler.mjs @@ -179,11 +179,6 @@ export async function onTriggerSample(options, bank) { // no playback return; } - if (!s) { - // is this check really needed? - console.warn('no sample specified'); - return; - } //const soundfont = getSoundfontKey(s); let bufferSource; diff --git a/packages/webaudio/synth.mjs b/packages/webaudio/synth.mjs index abc05b63..e43ed7a0 100644 --- a/packages/webaudio/synth.mjs +++ b/packages/webaudio/synth.mjs @@ -2,7 +2,7 @@ import { fromMidi, toMidi } from '@strudel.cycles/core'; import { setSound } from './webaudio.mjs'; import { getOscillator, gainNode, getADSR } from './helpers.mjs'; -export function loadSynthSounds() { +export function registerSynthSounds() { ['sine', 'square', 'triangle', 'sawtooth'].forEach((wave) => { setSound(wave, ({ hap, duration, t }) => { // destructure adsr here, because the default should be different for synths and samples diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index f515763a..0b83dd24 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -11,15 +11,14 @@ const { Pattern } = strudel; import './vowel.mjs'; import workletsUrl from './worklets.mjs?url'; import { getFilter, gainNode } from './helpers.mjs'; +import { map } from 'nanostores'; -// export const getAudioContext = () => Tone.getContext().rawContext; - -export const soundMap = new Map(); +export const soundMap = map(); // onTrigger = ({ hap: Hap, t: number, deadline: number, duration: number, cps: number }) => AudioNode export function setSound(key, onTrigger) { - soundMap.set(key, onTrigger); + soundMap.setKey(key, onTrigger); } -export const resetLoadedSounds = () => soundMap.clear(); +export const resetLoadedSounds = () => soundMap.set({}); let audioContext; export const getAudioContext = () => { @@ -161,14 +160,17 @@ export const webaudioOutput = async (hap, deadline, hapDuration, cps) => { if (bank && s) { s = `${bank}_${s}`; } - if (soundMap.has(s)) { - const node = await soundMap.get(s)({ hap, t, deadline, duration: hapDuration, cps }); - chain.push(node); - } else if (source) { - chain.push(source({ hap, t, deadline, duration: hapDuration, cps })); + // get source AudioNode + let node; + const options = { hap, t, deadline, duration: hapDuration, cps }; + if (source) { + node = source(options); + } else if (soundMap.get()[s]) { + node = await soundMap.get()[s](options); } else { throw new Error(`sound ${s} not found! Is it loaded?`); } + chain.push(node); // gain stage chain.push(gainNode(gain)); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 473c8f3b..b325c96c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -321,9 +321,11 @@ importers: packages/webaudio: specifiers: '@strudel.cycles/core': workspace:* + nanostores: ^0.7.4 vite: ^3.2.2 dependencies: '@strudel.cycles/core': link:../core + nanostores: 0.7.4 devDependencies: vite: 3.2.5 diff --git a/website/src/repl/Footer.jsx b/website/src/repl/Footer.jsx index fe2eddc9..9ce1d859 100644 --- a/website/src/repl/Footer.jsx +++ b/website/src/repl/Footer.jsx @@ -7,6 +7,8 @@ import React, { useCallback, useLayoutEffect, useRef, useState } from 'react'; import { Reference } from './Reference'; import { themes } from './themes.mjs'; import { useSettings, settingsMap, setActiveFooter, defaultSettings } from '../settings.mjs'; +import { soundMap } from '@strudel.cycles/webaudio'; +import { useStore } from '@nanostores/react'; export function Footer({ context }) { const footerContent = useRef(); @@ -71,7 +73,7 @@ export function Footer({ context }) {
- + @@ -89,7 +91,7 @@ export function Footer({ context }) { > {activeFooter === 'intro' && } {activeFooter === 'console' && } - {activeFooter === 'samples' && } + {activeFooter === 'sounds' && } {activeFooter === 'reference' && } {activeFooter === 'settings' && }
@@ -192,18 +194,19 @@ function ConsoleTab({ log }) { ); } -function SamplesTab() { +function SoundsTab() { + const sounds = useStore(soundMap); return ( -
- TODO: use nanostore with sampleMap - {/* {loadedSamples.length} banks loaded: - {loadedSamples.map(([name, samples]) => ( +
+ {/* {loadedSamples.length} banks loaded: */} + {Object.entries(sounds).map(([name, samples]) => ( {}}> {' '} - {name}( - {Array.isArray(samples) ? samples.length : typeof samples === 'object' ? Object.values(samples).length : 1}){' '} + {name} + {/* ( + {Array.isArray(samples) ? samples.length : typeof samples === 'object' ? Object.values(samples).length : 1}) */} - ))} */} + ))}
); } diff --git a/website/src/repl/prebake.mjs b/website/src/repl/prebake.mjs index e1a8ac9a..c9f92249 100644 --- a/website/src/repl/prebake.mjs +++ b/website/src/repl/prebake.mjs @@ -1,11 +1,10 @@ import { Pattern, toMidi, valueToMidi } from '@strudel.cycles/core'; -import { loadSynthSounds, samples } from '@strudel.cycles/webaudio'; +import { registerSynthSounds, samples } from '@strudel.cycles/webaudio'; export async function prebake() { // https://archive.org/details/SalamanderGrandPianoV3 // License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm - loadSynthSounds(); - return await Promise.all([ + await Promise.all([ samples(`./piano.json`, `./piano/`), // https://github.com/sgossner/VCSL/ // https://api.github.com/repositories/126427031/contents/ @@ -15,6 +14,7 @@ export async function prebake() { samples(`./EmuSP12.json`, `./EmuSP12/`), // samples('github:tidalcycles/Dirt-Samples/master'), ]); + registerSynthSounds(); } const maxPan = toMidi('C8'); diff --git a/website/src/styles/index.css b/website/src/styles/index.css index 2657bab7..61940e02 100644 --- a/website/src/styles/index.css +++ b/website/src/styles/index.css @@ -23,6 +23,6 @@ } #console-tab, -#samples-tab { +#sounds-tab { font-family: BigBlueTerminal, monospace; }