ok
This commit is contained in:
95
src/App.tsx
95
src/App.tsx
@ -1,33 +1,46 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import JSZip from 'jszip'
|
||||
import { BytebeatGenerator } from './lib/bytebeat'
|
||||
import { generateFormulaGrid } from './utils/bytebeatFormulas'
|
||||
import { BytebeatTile } from './components/BytebeatTile'
|
||||
import { EffectsBar } from './components/EffectsBar'
|
||||
import { getDefaultEffectValues } from './config/effects'
|
||||
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 [formulas, setFormulas] = useState<string[][]>(() => generateFormulaGrid(100, 2))
|
||||
const engineValues = useStore(engineSettings)
|
||||
const effectValues = useStore(effectSettings)
|
||||
|
||||
const [formulas, setFormulas] = useState<string[][]>(() =>
|
||||
generateFormulaGrid(100, 2, engineValues.complexity)
|
||||
)
|
||||
const [playing, setPlaying] = useState<string | null>(null)
|
||||
const [queued, setQueued] = useState<string | null>(null)
|
||||
const [playbackPosition, setPlaybackPosition] = useState<number>(0)
|
||||
const [downloading, setDownloading] = useState(false)
|
||||
const [effectValues, setEffectValues] = useState<EffectValues>(getDefaultEffectValues())
|
||||
const generatorRef = useRef<BytebeatGenerator | null>(null)
|
||||
const animationFrameRef = useRef<number | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
effectSettings.setKey('masterVolume', engineValues.masterVolume)
|
||||
}, [engineValues.masterVolume])
|
||||
|
||||
const handleRandom = () => {
|
||||
if (generatorRef.current) {
|
||||
generatorRef.current.stop()
|
||||
setPlaying(null)
|
||||
}
|
||||
setFormulas(generateFormulaGrid(100, 2))
|
||||
setFormulas(generateFormulaGrid(100, 2, engineValues.complexity))
|
||||
setQueued(null)
|
||||
}
|
||||
|
||||
const playFormula = (formula: string, id: string) => {
|
||||
const sampleRate = getSampleRateFromIndex(engineValues.sampleRate)
|
||||
const duration = engineValues.loopDuration
|
||||
|
||||
if (!generatorRef.current) {
|
||||
generatorRef.current = new BytebeatGenerator({ duration: 30 })
|
||||
generatorRef.current = new BytebeatGenerator({ sampleRate, duration })
|
||||
} else {
|
||||
generatorRef.current.updateOptions({ sampleRate, duration })
|
||||
}
|
||||
|
||||
try {
|
||||
@ -76,6 +89,14 @@ function App() {
|
||||
playFormula(formula, id)
|
||||
} else {
|
||||
setQueued(id)
|
||||
if (generatorRef.current) {
|
||||
generatorRef.current.scheduleNextTrack(() => {
|
||||
const queuedFormula = formulas.flat()[parseInt(id.split('-')[0]) * 2 + parseInt(id.split('-')[1])]
|
||||
if (queuedFormula) {
|
||||
playFormula(queuedFormula, id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,14 +105,19 @@ function App() {
|
||||
}
|
||||
|
||||
|
||||
const handleEngineChange = (parameterId: string, value: number) => {
|
||||
engineSettings.setKey(parameterId, value)
|
||||
|
||||
if (parameterId === 'masterVolume' && generatorRef.current) {
|
||||
generatorRef.current.setEffects(effectValues)
|
||||
}
|
||||
}
|
||||
|
||||
const handleEffectChange = (parameterId: string, value: number) => {
|
||||
setEffectValues(prev => {
|
||||
const newValues = { ...prev, [parameterId]: value }
|
||||
if (generatorRef.current) {
|
||||
generatorRef.current.setEffects(newValues)
|
||||
}
|
||||
return newValues
|
||||
})
|
||||
effectSettings.setKey(parameterId, value)
|
||||
if (generatorRef.current) {
|
||||
generatorRef.current.setEffects(effectValues)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDownloadAll = async () => {
|
||||
@ -125,23 +151,26 @@ function App() {
|
||||
|
||||
return (
|
||||
<div className="w-screen h-screen flex flex-col bg-black overflow-hidden">
|
||||
<header className="bg-black border-b-2 border-white flex items-center justify-between px-6 py-3">
|
||||
<h1 className="font-mono text-sm tracking-[0.3em] text-white">BYTEBEAT</h1>
|
||||
<div className="flex gap-4">
|
||||
<button
|
||||
onClick={handleRandom}
|
||||
className="px-6 py-2 bg-white text-black font-mono text-[11px] tracking-[0.2em] hover:bg-black hover:text-white border-2 border-white transition-all"
|
||||
>
|
||||
RANDOM
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDownloadAll}
|
||||
disabled={downloading}
|
||||
className="px-6 py-2 bg-black text-white border-2 border-white font-mono text-[11px] tracking-[0.2em] hover:bg-white hover:text-black transition-all disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
>
|
||||
{downloading ? 'DOWNLOADING...' : 'DOWNLOAD ALL'}
|
||||
</button>
|
||||
<header className="bg-black border-b-2 border-white px-6 py-3">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h1 className="font-mono text-sm tracking-[0.3em] text-white">BYTEBEAT</h1>
|
||||
<div className="flex gap-4">
|
||||
<button
|
||||
onClick={handleRandom}
|
||||
className="px-6 py-2 bg-white text-black font-mono text-[11px] tracking-[0.2em] hover:bg-black hover:text-white border-2 border-white transition-all"
|
||||
>
|
||||
RANDOM
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDownloadAll}
|
||||
disabled={downloading}
|
||||
className="px-6 py-2 bg-black text-white border-2 border-white font-mono text-[11px] tracking-[0.2em] hover:bg-white hover:text-black transition-all disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
>
|
||||
{downloading ? 'DOWNLOADING...' : 'DOWNLOAD ALL'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<EngineControls values={engineValues} onChange={handleEngineChange} />
|
||||
</header>
|
||||
|
||||
<div className="flex-1 grid grid-cols-2 auto-rows-min gap-[1px] bg-white p-[1px] overflow-auto">
|
||||
|
||||
Reference in New Issue
Block a user