Everything is a bit better
This commit is contained in:
114
src/App.tsx
114
src/App.tsx
@ -3,20 +3,22 @@ import { useStore } from '@nanostores/react'
|
||||
import { Square, Archive, Dices } from 'lucide-react'
|
||||
import { PlaybackManager } from './services/PlaybackManager'
|
||||
import { DownloadService } from './services/DownloadService'
|
||||
import { generateFormulaGrid, generateRandomFormula } from './utils/bytebeatFormulas'
|
||||
import { generateTileGrid, generateRandomFormula } 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 { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'
|
||||
import type { TileState } from './types/tiles'
|
||||
import { createTileStateFromCurrent, loadTileParams, saveTileParams } from './utils/tileState'
|
||||
|
||||
function App() {
|
||||
const engineValues = useStore(engineSettings)
|
||||
const effectValues = useStore(effectSettings)
|
||||
|
||||
const [formulas, setFormulas] = useState<string[][]>(() =>
|
||||
generateFormulaGrid(100, 2, engineValues.complexity)
|
||||
const [tiles, setTiles] = useState<TileState[][]>(() =>
|
||||
generateTileGrid(100, 2, engineValues.complexity)
|
||||
)
|
||||
const [playing, setPlaying] = useState<string | null>(null)
|
||||
const [queued, setQueued] = useState<string | null>(null)
|
||||
@ -27,18 +29,18 @@ function App() {
|
||||
const playbackManagerRef = useRef<PlaybackManager | null>(null)
|
||||
const downloadServiceRef = useRef<DownloadService>(new DownloadService())
|
||||
const animationFrameRef = useRef<number | null>(null)
|
||||
const formulasRef = useRef<string[][]>(formulas)
|
||||
const tilesRef = useRef<TileState[][]>(tiles)
|
||||
|
||||
useEffect(() => {
|
||||
formulasRef.current = formulas
|
||||
}, [formulas])
|
||||
tilesRef.current = tiles
|
||||
}, [tiles])
|
||||
|
||||
useEffect(() => {
|
||||
effectSettings.setKey('masterVolume', engineValues.masterVolume)
|
||||
}, [engineValues.masterVolume])
|
||||
|
||||
const handleRandom = () => {
|
||||
setFormulas(generateFormulaGrid(100, 2, engineValues.complexity))
|
||||
setTiles(generateTileGrid(100, 2, engineValues.complexity))
|
||||
setQueued(null)
|
||||
}
|
||||
|
||||
@ -82,8 +84,20 @@ function App() {
|
||||
updatePosition()
|
||||
}
|
||||
|
||||
const handleTileClick = (formula: string, row: number, col: number, isDoubleClick: boolean = false) => {
|
||||
const handleTileClick = (_formula: string, row: number, col: number, isDoubleClick: boolean = false) => {
|
||||
const id = `${row}-${col}`
|
||||
const tile = tiles[row]?.[col]
|
||||
|
||||
if (!tile) return
|
||||
|
||||
if (focusedTile.row !== row || focusedTile.col !== col) {
|
||||
const currentTile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||
if (currentTile) {
|
||||
saveTileParams(currentTile)
|
||||
}
|
||||
loadTileParams(tile)
|
||||
}
|
||||
|
||||
setFocusedTile({ row, col })
|
||||
|
||||
if (playing === id) {
|
||||
@ -98,14 +112,15 @@ function App() {
|
||||
}
|
||||
|
||||
if (isDoubleClick || playing === null) {
|
||||
playFormula(formula, id)
|
||||
playFormula(tile.formula, id)
|
||||
} else {
|
||||
setQueued(id)
|
||||
if (playbackManagerRef.current) {
|
||||
playbackManagerRef.current.scheduleNextTrack(() => {
|
||||
const queuedFormula = formulasRef.current.flat()[parseInt(id.split('-')[0]) * 2 + parseInt(id.split('-')[1])]
|
||||
if (queuedFormula) {
|
||||
playFormula(queuedFormula, id)
|
||||
const queuedTile = tilesRef.current[row]?.[col]
|
||||
if (queuedTile) {
|
||||
loadTileParams(queuedTile)
|
||||
playFormula(queuedTile.formula, id)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -120,6 +135,11 @@ function App() {
|
||||
const handleEngineChange = (parameterId: string, value: number) => {
|
||||
engineSettings.setKey(parameterId as keyof typeof engineValues, value)
|
||||
|
||||
const currentTile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||
if (currentTile) {
|
||||
saveTileParams(currentTile)
|
||||
}
|
||||
|
||||
if (parameterId === 'masterVolume' && playbackManagerRef.current) {
|
||||
playbackManagerRef.current.setEffects({ ...effectValues, masterVolume: value })
|
||||
}
|
||||
@ -131,6 +151,12 @@ function App() {
|
||||
|
||||
const handleEffectChange = (parameterId: string, value: number | boolean | string) => {
|
||||
effectSettings.setKey(parameterId as any, value as any)
|
||||
|
||||
const currentTile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||
if (currentTile) {
|
||||
saveTileParams(currentTile)
|
||||
}
|
||||
|
||||
if (playbackManagerRef.current) {
|
||||
playbackManagerRef.current.setEffects(effectValues)
|
||||
}
|
||||
@ -138,6 +164,7 @@ function App() {
|
||||
|
||||
const handleDownloadAll = async () => {
|
||||
setDownloading(true)
|
||||
const formulas = tiles.map(row => row.map(tile => tile.formula))
|
||||
await downloadServiceRef.current.downloadAll(formulas, { duration: 10, bitDepth: 8 })
|
||||
setDownloading(false)
|
||||
}
|
||||
@ -149,25 +176,27 @@ function App() {
|
||||
const handleRegenerate = (row: number, col: number) => {
|
||||
const id = `${row}-${col}`
|
||||
const newFormula = generateRandomFormula(engineValues.complexity)
|
||||
const newTile = createTileStateFromCurrent(newFormula)
|
||||
|
||||
if (playing === id && playbackManagerRef.current) {
|
||||
setRegenerating(id)
|
||||
playbackManagerRef.current.scheduleNextTrack(() => {
|
||||
setFormulas(prevFormulas => {
|
||||
const newFormulas = [...prevFormulas]
|
||||
newFormulas[row] = [...newFormulas[row]]
|
||||
newFormulas[row][col] = newFormula
|
||||
return newFormulas
|
||||
setTiles(prevTiles => {
|
||||
const newTiles = [...prevTiles]
|
||||
newTiles[row] = [...newTiles[row]]
|
||||
newTiles[row][col] = newTile
|
||||
return newTiles
|
||||
})
|
||||
playFormula(newFormula, id)
|
||||
loadTileParams(newTile)
|
||||
playFormula(newTile.formula, id)
|
||||
setRegenerating(null)
|
||||
})
|
||||
} else {
|
||||
setFormulas(prevFormulas => {
|
||||
const newFormulas = [...prevFormulas]
|
||||
newFormulas[row] = [...newFormulas[row]]
|
||||
newFormulas[row][col] = newFormula
|
||||
return newFormulas
|
||||
setTiles(prevTiles => {
|
||||
const newTiles = [...prevTiles]
|
||||
newTiles[row] = [...newTiles[row]]
|
||||
newTiles[row][col] = newTile
|
||||
return newTiles
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -183,10 +212,15 @@ function App() {
|
||||
}
|
||||
|
||||
const moveFocus = (direction: 'up' | 'down' | 'left' | 'right', step: number = 1) => {
|
||||
const currentTile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||
if (currentTile) {
|
||||
saveTileParams(currentTile)
|
||||
}
|
||||
|
||||
setFocusedTile(prev => {
|
||||
let { row, col } = prev
|
||||
const maxRow = formulas.length - 1
|
||||
const maxCol = (formulas[row]?.length || 1) - 1
|
||||
const maxRow = tiles.length - 1
|
||||
const maxCol = (tiles[row]?.length || 1) - 1
|
||||
|
||||
switch (direction) {
|
||||
case 'up':
|
||||
@ -202,6 +236,12 @@ function App() {
|
||||
col = Math.min(maxCol, col + step)
|
||||
break
|
||||
}
|
||||
|
||||
const newTile = tiles[row]?.[col]
|
||||
if (newTile) {
|
||||
loadTileParams(newTile)
|
||||
}
|
||||
|
||||
return { row, col }
|
||||
})
|
||||
}
|
||||
@ -210,24 +250,24 @@ function App() {
|
||||
if (playing) {
|
||||
handleStop()
|
||||
} else {
|
||||
const formula = formulas[focusedTile.row]?.[focusedTile.col]
|
||||
if (formula) {
|
||||
handleTileClick(formula, focusedTile.row, focusedTile.col, true)
|
||||
const tile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||
if (tile) {
|
||||
handleTileClick(tile.formula, focusedTile.row, focusedTile.col, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyboardEnter = () => {
|
||||
const formula = formulas[focusedTile.row]?.[focusedTile.col]
|
||||
if (formula) {
|
||||
handleTileClick(formula, focusedTile.row, focusedTile.col, false)
|
||||
const tile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||
if (tile) {
|
||||
handleTileClick(tile.formula, focusedTile.row, focusedTile.col, false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyboardDoubleEnter = () => {
|
||||
const formula = formulas[focusedTile.row]?.[focusedTile.col]
|
||||
if (formula) {
|
||||
handleTileClick(formula, focusedTile.row, focusedTile.col, true)
|
||||
const tile = tiles[focusedTile.row]?.[focusedTile.col]
|
||||
if (tile) {
|
||||
handleTileClick(tile.formula, focusedTile.row, focusedTile.col, true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,13 +333,13 @@ function App() {
|
||||
</header>
|
||||
|
||||
<div className="flex-1 grid grid-cols-2 auto-rows-min gap-[1px] bg-white p-[1px] overflow-auto">
|
||||
{formulas.map((row, i) =>
|
||||
row.map((formula, j) => {
|
||||
{tiles.map((row, i) =>
|
||||
row.map((tile, j) => {
|
||||
const id = `${i}-${j}`
|
||||
return (
|
||||
<BytebeatTile
|
||||
key={id}
|
||||
formula={formula}
|
||||
formula={tile.formula}
|
||||
row={i}
|
||||
col={j}
|
||||
isPlaying={playing === id}
|
||||
|
||||
Reference in New Issue
Block a user