import { useState, useRef, useEffect } from 'react' import { useStore } from '@nanostores/react' import { PlaybackManager } from './services/PlaybackManager' import { DownloadService } from './services/DownloadService' import { generateFormulaGrid } from './utils/bytebeatFormulas' import { BytebeatTile } from './components/BytebeatTile' import { EffectsBar } from './components/EffectsBar' import { EngineControls } from './components/EngineControls' import { getSampleRateFromIndex } from './config/effects' import { engineSettings, effectSettings } from './stores/settings' function App() { const engineValues = useStore(engineSettings) const effectValues = useStore(effectSettings) const [formulas, setFormulas] = useState(() => generateFormulaGrid(100, 2, engineValues.complexity) ) const [playing, setPlaying] = useState(null) const [queued, setQueued] = useState(null) const [playbackPosition, setPlaybackPosition] = useState(0) const [downloading, setDownloading] = useState(false) const playbackManagerRef = useRef(null) const downloadServiceRef = useRef(new DownloadService()) const animationFrameRef = useRef(null) useEffect(() => { effectSettings.setKey('masterVolume', engineValues.masterVolume) }, [engineValues.masterVolume]) const handleRandom = () => { setFormulas(generateFormulaGrid(100, 2, engineValues.complexity)) setQueued(null) } const playFormula = (formula: string, id: string) => { const sampleRate = getSampleRateFromIndex(engineValues.sampleRate) const duration = engineValues.loopDuration if (!playbackManagerRef.current) { playbackManagerRef.current = new PlaybackManager({ sampleRate, duration }) } else { playbackManagerRef.current.updateOptions({ sampleRate, duration }) } playbackManagerRef.current.stop() playbackManagerRef.current.setEffects(effectValues) const success = playbackManagerRef.current.play(formula, sampleRate, duration) if (success) { setPlaying(id) setQueued(null) startPlaybackTracking() } else { console.error('Failed to play formula') } } const startPlaybackTracking = () => { if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current) } const updatePosition = () => { if (playbackManagerRef.current) { const position = playbackManagerRef.current.getPlaybackPosition() setPlaybackPosition(position) animationFrameRef.current = requestAnimationFrame(updatePosition) } } updatePosition() } const handleTileClick = (formula: string, row: number, col: number, isDoubleClick: boolean = false) => { const id = `${row}-${col}` if (playing === id) { playbackManagerRef.current?.stop() setPlaying(null) setQueued(null) if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current) animationFrameRef.current = null } return } if (isDoubleClick || playing === null) { playFormula(formula, id) } else { setQueued(id) if (playbackManagerRef.current) { playbackManagerRef.current.scheduleNextTrack(() => { const queuedFormula = formulas.flat()[parseInt(id.split('-')[0]) * 2 + parseInt(id.split('-')[1])] if (queuedFormula) { playFormula(queuedFormula, id) } }) } } } const handleTileDoubleClick = (formula: string, row: number, col: number) => { handleTileClick(formula, row, col, true) } const handleEngineChange = (parameterId: string, value: number) => { engineSettings.setKey(parameterId as keyof typeof engineValues, value) if (parameterId === 'masterVolume' && playbackManagerRef.current) { playbackManagerRef.current.setEffects(effectValues) } } const handleEffectChange = (parameterId: string, value: number | boolean) => { effectSettings.setKey(parameterId as any, value as any) if (playbackManagerRef.current) { playbackManagerRef.current.setEffects(effectValues) } } const handleDownloadAll = async () => { setDownloading(true) await downloadServiceRef.current.downloadAll(formulas, { duration: 10, bitDepth: 8 }) setDownloading(false) } const handleDownloadFormula = (formula: string, filename: string) => { downloadServiceRef.current.downloadFormula(formula, filename, { duration: 10, bitDepth: 8 }) } return (

BRUITISTE

{formulas.map((row, i) => row.map((formula, j) => { const id = `${i}-${j}` return ( ) }) )}
) } export default App