From 18766f3d8a1a1efd8fe873eb281cc08f3411f915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Forment?= Date: Mon, 6 Oct 2025 02:24:34 +0200 Subject: [PATCH] loop mechanism rework --- src/App.tsx | 36 ++++++++++++++++++++++++++++++++++-- src/config/effects.ts | 12 ++++++------ src/constants/defaults.ts | 2 ++ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a2fda63e..b80539b9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -18,7 +18,7 @@ import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts' import { useTileParams } from './hooks/useTileParams' import type { TileState } from './types/tiles' import { createTileStateFromCurrent, loadTileParams, randomizeTileParams } from './utils/tileState' -import { DEFAULT_VARIABLES, PLAYBACK_ID, TILE_GRID, DEFAULT_DOWNLOAD_OPTIONS } from './constants/defaults' +import { DEFAULT_VARIABLES, PLAYBACK_ID, TILE_GRID, DEFAULT_DOWNLOAD_OPTIONS, LOOP_DURATION } from './constants/defaults' import { getTileId, getTileFromGrid, type FocusedTile } from './utils/tileHelpers' function App() { @@ -38,6 +38,7 @@ function App() { const [showHelp, setShowHelp] = useState(false) const playbackManagerRef = useRef(null) const downloadServiceRef = useRef(new DownloadService()) + const switchTimerRef = useRef(null) const { saveCurrentTileParams } = useTileParams({ tiles, setTiles, customTile, setCustomTile, focusedTile }) @@ -51,12 +52,40 @@ function App() { effectSettings.setKey('masterVolume', engineValues.masterVolume) }, [engineValues.masterVolume]) + const clearSwitchTimer = () => { + if (switchTimerRef.current !== null) { + clearTimeout(switchTimerRef.current) + switchTimerRef.current = null + } + } + + const startSwitchTimer = (queuedId: string) => { + clearSwitchTimer() + + switchTimerRef.current = window.setTimeout(() => { + const [rowStr, colStr] = queuedId.split('-') + const row = parseInt(rowStr, 10) + const col = parseInt(colStr, 10) + const tile = getTileFromGrid(tiles, row, col) + + if (tile) { + playFormula(tile.formula, queuedId) + } + }, engineValues.loopCount * 1000) + } + + useEffect(() => { + return () => clearSwitchTimer() + }, []) + const handleRandom = () => { + clearSwitchTimer() setTiles(generateTileGrid(TILE_GRID.SIZE, TILE_GRID.COLUMNS, engineValues.complexity)) setQueued(null) } const handleRandomizeAllParams = () => { + clearSwitchTimer() let newRandomized: TileState | null = null if (playing === PLAYBACK_ID.CUSTOM) { @@ -111,7 +140,7 @@ function App() { const playFormula = async (formula: string, id: string) => { const sampleRate = getSampleRateFromIndex(engineValues.sampleRate) - const duration = engineValues.loopDuration + const duration = LOOP_DURATION if (!playbackManagerRef.current) { playbackManagerRef.current = new PlaybackManager({ sampleRate, duration }) @@ -156,9 +185,11 @@ function App() { } if (isDoubleClick || playing === null) { + clearSwitchTimer() playFormula(tile.formula, id) } else { setQueued(id) + startSwitchTimer(id) } } @@ -309,6 +340,7 @@ function App() { } const handleStop = () => { + clearSwitchTimer() playbackManagerRef.current?.stop() setPlaying(null) setQueued(null) diff --git a/src/config/effects.ts b/src/config/effects.ts index e87da3ab..30d28a5d 100644 --- a/src/config/effects.ts +++ b/src/config/effects.ts @@ -15,13 +15,13 @@ export const ENGINE_CONTROLS: EffectConfig[] = [ unit: '' }, { - id: 'loopDuration', + id: 'loopCount', label: 'Loop', - min: 2, - max: 8, - default: 4, - step: 2, - unit: 's' + min: 1, + max: 10, + default: 2, + step: 1, + unit: '' }, { id: 'complexity', diff --git a/src/constants/defaults.ts b/src/constants/defaults.ts index 9e006b2c..6f0c2f5d 100644 --- a/src/constants/defaults.ts +++ b/src/constants/defaults.ts @@ -19,3 +19,5 @@ export const DEFAULT_DOWNLOAD_OPTIONS = { DURATION: 4, BIT_DEPTH: 24 } as const + +export const LOOP_DURATION = 4