import { useEvent } from '@strudel.cycles/react'; // import { cx } from '@strudel.cycles/react'; import { useStore } from '@nanostores/react'; import { getAudioContext, soundMap, connectToDestination } from '@strudel.cycles/webaudio'; import React, { useMemo, useRef } from 'react'; import { settingsMap, useSettings } from '../../settings.mjs'; import { ButtonGroup } from './Forms.jsx'; const getSamples = (samples) => Array.isArray(samples) ? samples.length : typeof samples === 'object' ? Object.values(samples).length : 1; export function SoundsTab() { const sounds = useStore(soundMap); const { soundsFilter } = useSettings(); const soundEntries = useMemo(() => { let filtered = Object.entries(sounds).filter(([key]) => !key.startsWith('_')); if (!sounds) { return []; } if (soundsFilter === 'user') { return filtered.filter(([key, { data }]) => !data.prebake); } if (soundsFilter === 'drums') { return filtered.filter(([_, { data }]) => data.type === 'sample' && data.tag === 'drum-machines'); } if (soundsFilter === 'samples') { return filtered.filter(([_, { data }]) => data.type === 'sample' && data.tag !== 'drum-machines'); } if (soundsFilter === 'synths') { return filtered.filter(([_, { data }]) => ['synth', 'soundfont'].includes(data.type)); } return filtered; }, [sounds, soundsFilter]); // holds mutable ref to current triggered sound const trigRef = useRef(); // stop current sound on mouseup useEvent('mouseup', () => { const t = trigRef.current; trigRef.current = undefined; t?.then((ref) => { ref?.stop(getAudioContext().currentTime + 0.01); }); }); return (
settingsMap.setKey('soundsFilter', value)} items={{ samples: 'samples', drums: 'drum-machines', synths: 'Synths', user: 'User', }} >
{soundEntries.map(([name, { data, onTrigger }]) => ( { const ctx = getAudioContext(); const params = { note: ['synth', 'soundfont'].includes(data.type) ? 'a3' : undefined, s: name, clip: 1, release: 0.5, }; const time = ctx.currentTime + 0.05; const onended = () => trigRef.current?.node?.disconnect(); trigRef.current = Promise.resolve(onTrigger(time, params, onended)); trigRef.current.then((ref) => { connectToDestination(ref?.node); }); }} > {' '} {name} {data?.type === 'sample' ? `(${getSamples(data.samples)})` : ''} {data?.type === 'soundfont' ? `(${data.fonts.length})` : ''} ))} {!soundEntries.length ? 'No custom sounds loaded in this pattern (yet).' : ''}
); }