diff --git a/packages/webaudio/sampler.mjs b/packages/webaudio/sampler.mjs index eaaaf8dd..cff090d7 100644 --- a/packages/webaudio/sampler.mjs +++ b/packages/webaudio/sampler.mjs @@ -105,7 +105,7 @@ export const getLoadedBuffer = (url) => { * */ -export const samples = async (sampleMap, baseUrl = sampleMap._base || '') => { +export const samples = async (sampleMap, baseUrl = sampleMap._base || '', options = {}) => { if (typeof sampleMap === 'string') { if (sampleMap.startsWith('github:')) { let [_, path] = sampleMap.split('github:'); @@ -123,12 +123,13 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '') => { } return fetch(sampleMap) .then((res) => res.json()) - .then((json) => samples(json, baseUrl || json._base || base)) + .then((json) => samples(json, baseUrl || json._base || base, options)) .catch((error) => { console.error(error); throw new Error(`error loading "${sampleMap}"`); }); } + const { prebake } = options; Object.entries(sampleMap).forEach(([key, value]) => { if (typeof value === 'string') { value = [value]; @@ -149,7 +150,12 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '') => { }), ); } - setSound(key, (options) => onTriggerSample(options, value), { type: 'sample', samples: value }); + setSound(key, (options) => onTriggerSample(options, value), { + type: 'sample', + samples: value, + baseUrl, + prebake, + }); }); }; diff --git a/packages/webaudio/synth.mjs b/packages/webaudio/synth.mjs index b840a6aa..d615661a 100644 --- a/packages/webaudio/synth.mjs +++ b/packages/webaudio/synth.mjs @@ -32,7 +32,7 @@ export function registerSynthSounds() { //chain.push(adsr); return o.connect(g).connect(adsr); }, - { type: 'synth' }, + { type: 'synth', prebake: true }, ); }); } diff --git a/website/public/vcsl.json b/website/public/vcsl.json index 9ab06549..983cde89 100644 --- a/website/public/vcsl.json +++ b/website/public/vcsl.json @@ -1986,7 +1986,7 @@ "Idiophones/Struck%20Idiophones/Tambourine%202/Tamb2_Shake_rr3_Mid.wav", "Idiophones/Struck%20Idiophones/Tambourine%202/Tamb2_Shake_rr4_Mid.wav" ], - "triangle": [ + "triangles": [ "Idiophones/Struck%20Idiophones/Triangles/Triangle1_HitFM_v1_rr1_Mid.wav", "Idiophones/Struck%20Idiophones/Triangles/Triangle1_HitFM_v1_rr2_Mid.wav", "Idiophones/Struck%20Idiophones/Triangles/Triangle1_HitM_v1_rr2_Mid.wav", diff --git a/website/src/repl/Footer.jsx b/website/src/repl/Footer.jsx index 947b6b87..f5454541 100644 --- a/website/src/repl/Footer.jsx +++ b/website/src/repl/Footer.jsx @@ -3,7 +3,7 @@ import { logger } from '@strudel.cycles/core'; import { useEvent, cx } from '@strudel.cycles/react'; // import { cx } from '@strudel.cycles/react'; import { nanoid } from 'nanoid'; -import React, { useCallback, useLayoutEffect, useRef, useState } from 'react'; +import React, { useMemo, useCallback, useLayoutEffect, useRef, useState } from 'react'; import { Reference } from './Reference'; import { themes } from './themes.mjs'; import { useSettings, settingsMap, setActiveFooter, defaultSettings } from '../settings.mjs'; @@ -194,19 +194,67 @@ function ConsoleTab({ log }) { ); } +/* +function groupBy(obj = {}, getter) { + const grouped = Object.entries(obj).reduce((acc, [key, value]) => { + const propValue = getter(value, key); + if (!acc.has(propValue)) { + acc.set(propValue, new Map()); + } + acc.get(propValue).set(key, value); + return acc; + }, new Map()); + return grouped; +} + const grouped = useMemo(() => { + if (!sounds) { + return {}; + } + return groupBy(sounds, (s) => + s.data.type === 'sample' ? 'Samples from ' + s.data?.baseUrl : 'Type ' + s.data.type, + ); +}, [sounds]); + + {Array.from(grouped).map(([category, sounds]) => ( + +

{category}:

+
+ ))} +*/ + +const getSamples = (samples) => + Array.isArray(samples) ? samples.length : typeof samples === 'object' ? Object.values(samples).length : 1; + function SoundsTab() { const sounds = useStore(soundMap); - const getSamples = (samples) => - Array.isArray(samples) ? samples.length : typeof samples === 'object' ? Object.values(samples).length : 1; + const { soundsFilter } = useSettings(); + + const soundEntries = useMemo(() => { + if (!sounds) { + return []; + } + if (soundsFilter === 'hideDefaults') { + return Object.entries(sounds).filter(([_, { data }]) => !data.prebake); + } + return Object.entries(sounds); + }, [sounds, soundsFilter]); return (
- {/* {loadedSamples.length} banks loaded: */} - {Object.entries(sounds).map(([name, { data }]) => ( - {}}> - {' '} - {name} {data?.type === 'sample' ? `(${getSamples(data.samples)})` : ''} - - ))} + settingsMap.setKey('soundsFilter', value)} + items={{ all: 'All', hideDefaults: 'Hide Defaults' }} + > +
+ {soundEntries.map(([name, { data }]) => ( + {}}> + {' '} + {name} + {data?.type === 'sample' ? `(${getSamples(data.samples)})` : ''} + + ))} + {!soundEntries.length ? 'No Sounds' : ''} +
); } diff --git a/website/src/repl/prebake.mjs b/website/src/repl/prebake.mjs index c9f92249..e68274cf 100644 --- a/website/src/repl/prebake.mjs +++ b/website/src/repl/prebake.mjs @@ -4,17 +4,17 @@ 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 + registerSynthSounds(); await Promise.all([ - samples(`./piano.json`, `./piano/`), + samples(`./piano.json`, `./piano/`, { prebake: true }), // https://github.com/sgossner/VCSL/ // https://api.github.com/repositories/126427031/contents/ // LICENSE: CC0 general-purpose - samples(`./vcsl.json`, 'github:sgossner/VCSL/master/'), - samples(`./tidal-drum-machines.json`, 'github:ritchse/tidal-drum-machines/main/machines/'), - samples(`./EmuSP12.json`, `./EmuSP12/`), + samples(`./vcsl.json`, 'github:sgossner/VCSL/master/', { prebake: true }), + samples(`./tidal-drum-machines.json`, 'github:ritchse/tidal-drum-machines/main/machines/', { prebake: true }), + samples(`./EmuSP12.json`, `./EmuSP12/`, { prebake: true }), // samples('github:tidalcycles/Dirt-Samples/master'), ]); - registerSynthSounds(); } const maxPan = toMidi('C8'); diff --git a/website/src/settings.mjs b/website/src/settings.mjs index 82888581..583454c7 100644 --- a/website/src/settings.mjs +++ b/website/src/settings.mjs @@ -9,6 +9,7 @@ export const defaultSettings = { fontSize: 18, latestCode: '', isZen: false, + soundsFilter: 'all', }; export const settingsMap = persistentMap('strudel-settings', defaultSettings); diff --git a/website/src/styles/index.css b/website/src/styles/index.css index 61940e02..1e14461b 100644 --- a/website/src/styles/index.css +++ b/website/src/styles/index.css @@ -22,7 +22,6 @@ --app-height: 100vh; } -#console-tab, -#sounds-tab { +#console-tab { font-family: BigBlueTerminal, monospace; }