import ShapeCanvas from "@/components/canvas/ShapeCanvas"; import PresetSelector from "@/components/controls/PresetSelector"; import { Slider } from "@/components/ui/slider"; import { Toggle } from "@/components/ui/toggle"; import { useAudioContext } from "@/hooks/useAudioContext"; import { useShapeState } from "@/hooks/useShapeState"; import { useSynth } from "@/hooks/useSynth"; import { useWobbleAnimation } from "@/hooks/useWobbleAnimation"; import { mapGrainToNoise, mapPresetToOscType, mapRoundnessToFade, mapSizeToGain, mapWobbleToDetune, } from "@/lib/audio/mapping"; import { useEffect, useState } from "react"; import * as Tone from "tone"; import Layout from "./Layout"; import { cn } from "./lib/utils"; function Index() { const [dimensions, setDimensions] = useState({ width: window.innerWidth - 320, height: window.innerHeight, }); useEffect(() => { const handleResize = () => { setDimensions({ width: window.innerWidth - 320, height: window.innerHeight, }); }; window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); const centerX = dimensions.width / 2; const centerY = dimensions.height / 2; const [state, setState] = useShapeState(centerX, centerY); const { isMuted, toggleMute } = useAudioContext(); const synthRef = useSynth(); const pitchTime = useWobbleAnimation(state.wobbleSpeed); useEffect(() => { if (!synthRef.current) return; const nodes = synthRef.current; nodes.oscillatorA.type = mapPresetToOscType(state.preset); nodes.crossFade.fade.value = mapRoundnessToFade(state.roundness); nodes.gain.gain.value = Tone.dbToGain(mapSizeToGain(state.size)); const detuneDepth = mapWobbleToDetune(state.wobble); const detune = Math.sin(pitchTime * Math.PI * 2) * detuneDepth; nodes.oscillatorA.detune.value = detune; nodes.oscillatorB.detune.value = detune; const grain = mapGrainToNoise(state.grain); const noiseDb = grain === 0 ? Number.NEGATIVE_INFINITY : -40 + (-12 - -40) * grain; nodes.noise.volume.value = noiseDb; }, [state.preset, state.roundness, state.size, state.wobble, state.grain, synthRef, pitchTime]); const sidebarContent = (
Audio {isMuted ? "Unmute" : "Mute"}
Shape setState({ ...state, preset })} />
Roundness setState({ ...state, roundness: v })} className={state.preset === "circle" ? "opacity-50 pointer-events-none" : ""} />
Wobble setState({ ...state, wobble: v })} />
Wobble Speed setState({ ...state, wobbleSpeed: v })} />
Wobble Randomness setState({ ...state, wobbleRandomness: v })} />
Noise setState({ ...state, grain: v })} />
); return ( ); } export default Index;