import { useEffect, useState, useRef, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Modal } from '@/components/modal'; import { Slider } from '@/components/slider'; import styles from './isochornic.module.css'; interface IsochronicProps { onClose: () => void; show: boolean; } interface Preset { baseFrequency: number; beatFrequency: number; name: string; translationKey: string; } const presets: Preset[] = [ { baseFrequency: 100, beatFrequency: 2, name: 'Delta (Deep Sleep) 2 Hz', translationKey: 'modals.generators.presets.delta', }, { baseFrequency: 100, beatFrequency: 5, name: 'Theta (Meditation) 5 Hz', translationKey: 'modals.generators.presets.theta', }, { baseFrequency: 100, beatFrequency: 10, name: 'Alpha (Relaxation) 10 Hz', translationKey: 'modals.generators.presets.alpha', }, { baseFrequency: 100, beatFrequency: 20, name: 'Beta (Focus) 20 Hz', translationKey: 'modals.generators.presets.beta', }, { baseFrequency: 100, beatFrequency: 40, name: 'Gamma (Cognition) 40 Hz', translationKey: 'modals.generators.presets.gamma', }, { baseFrequency: 440, beatFrequency: 10, name: 'Custom', translationKey: 'modals.generators.presets.custom', }, ]; export function IsochronicModal({ onClose, show }: IsochronicProps) { const { t } = useTranslation(); const [baseFrequency, setBaseFrequency] = useState(440); // Default A4 note const [beatFrequency, setBeatFrequency] = useState(10); // Default 10 Hz beat const [volume, setVolume] = useState(0.5); // Default volume at 50% const [waveform] = useState('sine'); // Default waveform const [isPlaying, setIsPlaying] = useState(false); const [selectedPreset, setSelectedPreset] = useState('Custom'); const audioContextRef = useRef(null); const oscillatorRef = useRef(null); const gainNodeRef = useRef(null); const beatGainRef = useRef(null); const modulatorRef = useRef(null); const startSound = () => { if (isPlaying) return; audioContextRef.current = new window.AudioContext(); const audioContext = audioContextRef.current; if (!audioContext) return; // Main gain node for volume control gainNodeRef.current = audioContext.createGain(); gainNodeRef.current.gain.value = volume; // Oscillator for the base tone oscillatorRef.current = audioContext.createOscillator(); oscillatorRef.current.frequency.value = baseFrequency; oscillatorRef.current.type = waveform; // Gain node to create isochronic beats beatGainRef.current = audioContext.createGain(); beatGainRef.current.gain.value = 0; // Start with silence // Oscillator for modulation modulatorRef.current = audioContext.createOscillator(); modulatorRef.current.frequency.value = beatFrequency; modulatorRef.current.type = 'square'; // Square wave for on/off effect // Modulator gain to adjust modulation depth const modulatorGain = audioContext.createGain(); modulatorGain.gain.value = 0.5; // Modulation depth // Connect modulator to the beat gain node modulatorRef.current .connect(modulatorGain) .connect(beatGainRef.current.gain); // Connect oscillator through beat gain and main gain to destination oscillatorRef.current .connect(beatGainRef.current) .connect(gainNodeRef.current) .connect(audioContext.destination); // Start oscillators oscillatorRef.current.start(); modulatorRef.current.start(); setIsPlaying(true); }; const stopSound = useCallback(() => { if (!isPlaying) return; oscillatorRef.current?.stop(); modulatorRef.current?.stop(); audioContextRef.current?.close(); setIsPlaying(false); }, [isPlaying]); useEffect(() => { // Update gain when volume changes if (gainNodeRef.current) { gainNodeRef.current.gain.value = volume; } }, [volume]); useEffect(() => { // Update base frequency when it changes if (oscillatorRef.current) { oscillatorRef.current.frequency.value = baseFrequency; } }, [baseFrequency]); useEffect(() => { // Update beat frequency when it changes if (modulatorRef.current) { modulatorRef.current.frequency.value = beatFrequency; } }, [beatFrequency]); useEffect(() => { // Update waveform when it changes if (oscillatorRef.current) { oscillatorRef.current.type = waveform; } }, [waveform]); useEffect(() => { // Cleanup when component unmounts return () => { if (isPlaying) { stopSound(); } }; }, [isPlaying, stopSound]); useEffect(() => { // Update frequencies when a preset is selected if (selectedPreset !== 'Custom') { const preset = presets.find(p => p.name === selectedPreset); if (preset) { setBaseFrequency(preset.baseFrequency); setBeatFrequency(preset.beatFrequency); } } }, [selectedPreset]); const handlePresetChange = (e: React.ChangeEvent) => { const selected = e.target.value; setSelectedPreset(selected); if (selected === 'Custom') { // Allow user to input custom frequencies return; } const preset = presets.find(p => p.name === selected); if (preset) { setBaseFrequency(preset.baseFrequency); setBeatFrequency(preset.beatFrequency); } }; return (

{t('modals.isochronic.title')}

{t('modals.isochronic.description')}

{selectedPreset === 'Custom' && ( <>
{/*
*/} )}
); }