diff --git a/index.html b/index.html index c742303a..a72add2c 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,10 @@ - bytesample + + + + Bruitiste
diff --git a/src/App.tsx b/src/App.tsx index 3364dfe9..bc2fc0da 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,14 +1,13 @@ import { useState, useRef, useEffect } from 'react' import { useStore } from '@nanostores/react' -import JSZip from 'jszip' -import { BytebeatGenerator } from './lib/bytebeat' +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' -import type { EffectValues } from './types/effects' function App() { const engineValues = useStore(engineSettings) @@ -21,7 +20,8 @@ function App() { const [queued, setQueued] = useState(null) const [playbackPosition, setPlaybackPosition] = useState(0) const [downloading, setDownloading] = useState(false) - const generatorRef = useRef(null) + const playbackManagerRef = useRef(null) + const downloadServiceRef = useRef(new DownloadService()) const animationFrameRef = useRef(null) useEffect(() => { @@ -37,22 +37,23 @@ function App() { const sampleRate = getSampleRateFromIndex(engineValues.sampleRate) const duration = engineValues.loopDuration - if (!generatorRef.current) { - generatorRef.current = new BytebeatGenerator({ sampleRate, duration }) + if (!playbackManagerRef.current) { + playbackManagerRef.current = new PlaybackManager({ sampleRate, duration }) } else { - generatorRef.current.updateOptions({ sampleRate, duration }) + playbackManagerRef.current.updateOptions({ sampleRate, duration }) } - try { - generatorRef.current.stop() - generatorRef.current.setFormula(formula) - generatorRef.current.setEffects(effectValues) - generatorRef.current.play() + playbackManagerRef.current.stop() + playbackManagerRef.current.setEffects(effectValues) + + const success = playbackManagerRef.current.play(formula, sampleRate, duration) + + if (success) { setPlaying(id) setQueued(null) startPlaybackTracking() - } catch (error) { - console.error('Failed to play formula:', error) + } else { + console.error('Failed to play formula') } } @@ -62,8 +63,8 @@ function App() { } const updatePosition = () => { - if (generatorRef.current) { - const position = generatorRef.current.getPlaybackPosition() + if (playbackManagerRef.current) { + const position = playbackManagerRef.current.getPlaybackPosition() setPlaybackPosition(position) animationFrameRef.current = requestAnimationFrame(updatePosition) } @@ -75,7 +76,7 @@ function App() { const id = `${row}-${col}` if (playing === id) { - generatorRef.current?.stop() + playbackManagerRef.current?.stop() setPlaying(null) setQueued(null) if (animationFrameRef.current) { @@ -89,8 +90,8 @@ function App() { playFormula(formula, id) } else { setQueued(id) - if (generatorRef.current) { - generatorRef.current.scheduleNextTrack(() => { + if (playbackManagerRef.current) { + playbackManagerRef.current.scheduleNextTrack(() => { const queuedFormula = formulas.flat()[parseInt(id.split('-')[0]) * 2 + parseInt(id.split('-')[1])] if (queuedFormula) { playFormula(queuedFormula, id) @@ -106,54 +107,35 @@ function App() { const handleEngineChange = (parameterId: string, value: number) => { - engineSettings.setKey(parameterId, value) + engineSettings.setKey(parameterId as keyof typeof engineValues, value) - if (parameterId === 'masterVolume' && generatorRef.current) { - generatorRef.current.setEffects(effectValues) + if (parameterId === 'masterVolume' && playbackManagerRef.current) { + playbackManagerRef.current.setEffects(effectValues) } } const handleEffectChange = (parameterId: string, value: number) => { - effectSettings.setKey(parameterId, value) - if (generatorRef.current) { - generatorRef.current.setEffects(effectValues) + effectSettings.setKey(parameterId as keyof typeof effectValues, value) + if (playbackManagerRef.current) { + playbackManagerRef.current.setEffects(effectValues) } } const handleDownloadAll = async () => { setDownloading(true) - const zip = new JSZip() - const gen = new BytebeatGenerator({ duration: 10 }) - - formulas.forEach((row, i) => { - row.forEach((formula, j) => { - try { - gen.setFormula(formula) - const blob = gen.exportWAV(8) - zip.file(`bytebeat_${i}_${j}.wav`, blob) - } catch (error) { - console.error(`Failed to generate ${i}_${j}:`, error) - } - }) - }) - - const content = await zip.generateAsync({ type: 'blob' }) - const url = URL.createObjectURL(content) - const a = document.createElement('a') - a.href = url - a.download = 'bytebeats.zip' - a.click() - URL.revokeObjectURL(url) - - gen.dispose() + 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 (
-

BYTEBEAT

+

BRUITISTE