use nanostore for soundmap

+ rename tab samples to sounds
+ listed sounds are now reactive
This commit is contained in:
Felix Roos 2023-03-06 23:11:09 +01:00
parent 6059c69995
commit b08a0b8102
8 changed files with 34 additions and 31 deletions

View File

@ -34,7 +34,8 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@strudel.cycles/core": "workspace:*" "@strudel.cycles/core": "workspace:*",
"nanostores": "^0.7.4"
}, },
"devDependencies": { "devDependencies": {
"vite": "^3.2.2" "vite": "^3.2.2"

View File

@ -179,11 +179,6 @@ export async function onTriggerSample(options, bank) {
// no playback // no playback
return; return;
} }
if (!s) {
// is this check really needed?
console.warn('no sample specified');
return;
}
//const soundfont = getSoundfontKey(s); //const soundfont = getSoundfontKey(s);
let bufferSource; let bufferSource;

View File

@ -2,7 +2,7 @@ import { fromMidi, toMidi } from '@strudel.cycles/core';
import { setSound } from './webaudio.mjs'; import { setSound } from './webaudio.mjs';
import { getOscillator, gainNode, getADSR } from './helpers.mjs'; import { getOscillator, gainNode, getADSR } from './helpers.mjs';
export function loadSynthSounds() { export function registerSynthSounds() {
['sine', 'square', 'triangle', 'sawtooth'].forEach((wave) => { ['sine', 'square', 'triangle', 'sawtooth'].forEach((wave) => {
setSound(wave, ({ hap, duration, t }) => { setSound(wave, ({ hap, duration, t }) => {
// destructure adsr here, because the default should be different for synths and samples // destructure adsr here, because the default should be different for synths and samples

View File

@ -11,15 +11,14 @@ const { Pattern } = strudel;
import './vowel.mjs'; import './vowel.mjs';
import workletsUrl from './worklets.mjs?url'; import workletsUrl from './worklets.mjs?url';
import { getFilter, gainNode } from './helpers.mjs'; import { getFilter, gainNode } from './helpers.mjs';
import { map } from 'nanostores';
// export const getAudioContext = () => Tone.getContext().rawContext; export const soundMap = map();
export const soundMap = new Map();
// onTrigger = ({ hap: Hap, t: number, deadline: number, duration: number, cps: number }) => AudioNode // onTrigger = ({ hap: Hap, t: number, deadline: number, duration: number, cps: number }) => AudioNode
export function setSound(key, onTrigger) { 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; let audioContext;
export const getAudioContext = () => { export const getAudioContext = () => {
@ -161,14 +160,17 @@ export const webaudioOutput = async (hap, deadline, hapDuration, cps) => {
if (bank && s) { if (bank && s) {
s = `${bank}_${s}`; s = `${bank}_${s}`;
} }
if (soundMap.has(s)) { // get source AudioNode
const node = await soundMap.get(s)({ hap, t, deadline, duration: hapDuration, cps }); let node;
chain.push(node); const options = { hap, t, deadline, duration: hapDuration, cps };
} else if (source) { if (source) {
chain.push(source({ hap, t, deadline, duration: hapDuration, cps })); node = source(options);
} else if (soundMap.get()[s]) {
node = await soundMap.get()[s](options);
} else { } else {
throw new Error(`sound ${s} not found! Is it loaded?`); throw new Error(`sound ${s} not found! Is it loaded?`);
} }
chain.push(node);
// gain stage // gain stage
chain.push(gainNode(gain)); chain.push(gainNode(gain));

2
pnpm-lock.yaml generated
View File

@ -321,9 +321,11 @@ importers:
packages/webaudio: packages/webaudio:
specifiers: specifiers:
'@strudel.cycles/core': workspace:* '@strudel.cycles/core': workspace:*
nanostores: ^0.7.4
vite: ^3.2.2 vite: ^3.2.2
dependencies: dependencies:
'@strudel.cycles/core': link:../core '@strudel.cycles/core': link:../core
nanostores: 0.7.4
devDependencies: devDependencies:
vite: 3.2.5 vite: 3.2.5

View File

@ -7,6 +7,8 @@ import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
import { Reference } from './Reference'; import { Reference } from './Reference';
import { themes } from './themes.mjs'; import { themes } from './themes.mjs';
import { useSettings, settingsMap, setActiveFooter, defaultSettings } from '../settings.mjs'; import { useSettings, settingsMap, setActiveFooter, defaultSettings } from '../settings.mjs';
import { soundMap } from '@strudel.cycles/webaudio';
import { useStore } from '@nanostores/react';
export function Footer({ context }) { export function Footer({ context }) {
const footerContent = useRef(); const footerContent = useRef();
@ -71,7 +73,7 @@ export function Footer({ context }) {
<div className="flex justify-between px-2"> <div className="flex justify-between px-2">
<div className={cx('flex select-none max-w-full overflow-auto', activeFooter && 'pb-2')}> <div className={cx('flex select-none max-w-full overflow-auto', activeFooter && 'pb-2')}>
<FooterTab name="intro" label="welcome" /> <FooterTab name="intro" label="welcome" />
<FooterTab name="samples" /> <FooterTab name="sounds" />
<FooterTab name="console" /> <FooterTab name="console" />
<FooterTab name="reference" /> <FooterTab name="reference" />
<FooterTab name="settings" /> <FooterTab name="settings" />
@ -89,7 +91,7 @@ export function Footer({ context }) {
> >
{activeFooter === 'intro' && <WelcomeTab />} {activeFooter === 'intro' && <WelcomeTab />}
{activeFooter === 'console' && <ConsoleTab log={log} />} {activeFooter === 'console' && <ConsoleTab log={log} />}
{activeFooter === 'samples' && <SamplesTab />} {activeFooter === 'sounds' && <SoundsTab />}
{activeFooter === 'reference' && <Reference />} {activeFooter === 'reference' && <Reference />}
{activeFooter === 'settings' && <SettingsTab scheduler={context.scheduler} />} {activeFooter === 'settings' && <SettingsTab scheduler={context.scheduler} />}
</div> </div>
@ -192,18 +194,19 @@ function ConsoleTab({ log }) {
); );
} }
function SamplesTab() { function SoundsTab() {
const sounds = useStore(soundMap);
return ( return (
<div id="samples-tab" className="break-normal w-full px-4 dark:text-white text-stone-900"> <div id="sounds-tab" className="break-normal w-full px-4 dark:text-white text-stone-900">
TODO: use nanostore with sampleMap {/* <span>{loadedSamples.length} banks loaded:</span> */}
{/* <span>{loadedSamples.length} banks loaded:</span> {Object.entries(sounds).map(([name, samples]) => (
{loadedSamples.map(([name, samples]) => (
<span key={name} className="cursor-pointer hover:opacity-50" onClick={() => {}}> <span key={name} className="cursor-pointer hover:opacity-50" onClick={() => {}}>
{' '} {' '}
{name}( {name}
{Array.isArray(samples) ? samples.length : typeof samples === 'object' ? Object.values(samples).length : 1}){' '} {/* (
{Array.isArray(samples) ? samples.length : typeof samples === 'object' ? Object.values(samples).length : 1}) */}
</span> </span>
))} */} ))}
</div> </div>
); );
} }

View File

@ -1,11 +1,10 @@
import { Pattern, toMidi, valueToMidi } from '@strudel.cycles/core'; 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() { export async function prebake() {
// https://archive.org/details/SalamanderGrandPianoV3 // https://archive.org/details/SalamanderGrandPianoV3
// License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm // License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm
loadSynthSounds(); await Promise.all([
return await Promise.all([
samples(`./piano.json`, `./piano/`), samples(`./piano.json`, `./piano/`),
// https://github.com/sgossner/VCSL/ // https://github.com/sgossner/VCSL/
// https://api.github.com/repositories/126427031/contents/ // https://api.github.com/repositories/126427031/contents/
@ -15,6 +14,7 @@ export async function prebake() {
samples(`./EmuSP12.json`, `./EmuSP12/`), samples(`./EmuSP12.json`, `./EmuSP12/`),
// samples('github:tidalcycles/Dirt-Samples/master'), // samples('github:tidalcycles/Dirt-Samples/master'),
]); ]);
registerSynthSounds();
} }
const maxPan = toMidi('C8'); const maxPan = toMidi('C8');

View File

@ -23,6 +23,6 @@
} }
#console-tab, #console-tab,
#samples-tab { #sounds-tab {
font-family: BigBlueTerminal, monospace; font-family: BigBlueTerminal, monospace;
} }