| | |
| | let audioContext = null; |
| | let isPlaying = false; |
| | let currentMode = 'spatial'; |
| | let rampMode = 'none'; |
| | let rampDuration = 60; |
| | let activeNoiseType = null; |
| |
|
| | |
| | let binauralNodes = { |
| | leftOsc: null, |
| | rightOsc: null, |
| | leftGain: null, |
| | rightGain: null, |
| | merger: null, |
| | masterGain: null, |
| | modulator: null |
| | }; |
| |
|
| | let noiseNodes = { |
| | leftBuffer: null, |
| | rightBuffer: null, |
| | leftSource: null, |
| | rightSource: null, |
| | leftGain: null, |
| | rightGain: null, |
| | leftPanner: null, |
| | rightPanner: null, |
| | masterGain: null, |
| | filter: null |
| | }; |
| |
|
| | let visualizer = { |
| | analyser: null, |
| | canvas: null, |
| | ctx: null, |
| | animationId: null |
| | }; |
| |
|
| | |
| | function initAudioContext() { |
| | if (!audioContext) { |
| | audioContext = new (window.AudioContext || window.webkitAudioContext)(); |
| | setupVisualizer(); |
| | } |
| | if (audioContext.state === 'suspended') { |
| | audioContext.resume(); |
| | } |
| | } |
| |
|
| | |
| | function setupVisualizer() { |
| | const canvas = document.getElementById('audioVisualizer'); |
| | const ctx = canvas.getContext('2d'); |
| | |
| | |
| | const dpr = window.devicePixelRatio || 1; |
| | const rect = canvas.getBoundingClientRect(); |
| | canvas.width = rect.width * dpr; |
| | canvas.height = rect.height * dpr; |
| | ctx.scale(dpr, dpr); |
| | |
| | visualizer.canvas = canvas; |
| | visualizer.ctx = ctx; |
| | |
| | visualizer.analyser = audioContext.createAnalyser(); |
| | visualizer.analyser.fftSize = 2048; |
| | visualizer.analyser.smoothingTimeConstant = 0.8; |
| | |
| | drawVisualizer(); |
| | } |
| |
|
| | function drawVisualizer() { |
| | if (!visualizer.ctx) return; |
| | |
| | const { ctx, canvas, analyser } = visualizer; |
| | const width = canvas.width / (window.devicePixelRatio || 1); |
| | const height = canvas.height / (window.devicePixelRatio || 1); |
| | |
| | ctx.fillStyle = 'rgba(0, 0, 0, 0.2)'; |
| | ctx.fillRect(0, 0, width, height); |
| | |
| | if (isPlaying && analyser) { |
| | const bufferLength = analyser.frequencyBinCount; |
| | const dataArray = new Uint8Array(bufferLength); |
| | analyser.getByteFrequencyData(dataArray); |
| | |
| | const barWidth = (width / bufferLength) * 2.5; |
| | let barHeight; |
| | let x = 0; |
| | |
| | for (let i = 0; i < bufferLength; i++) { |
| | barHeight = (dataArray[i] / 255) * height * 0.8; |
| | |
| | |
| | const gradient = ctx.createLinearGradient(0, height, 0, height - barHeight); |
| | gradient.addColorStop(0, 'rgba(6, 182, 212, 0.8)'); |
| | gradient.addColorStop(0.5, 'rgba(168, 85, 247, 0.8)'); |
| | gradient.addColorStop(1, 'rgba(236, 72, 153, 0.8)'); |
| | |
| | ctx.fillStyle = gradient; |
| | ctx.fillRect(x, height - barHeight, barWidth, barHeight); |
| | |
| | |
| | ctx.shadowBlur = 10; |
| | ctx.shadowColor = 'rgba(236, 72, 153, 0.5)'; |
| | |
| | x += barWidth + 1; |
| | } |
| | ctx.shadowBlur = 0; |
| | } else { |
| | |
| | const time = Date.now() * 0.001; |
| | ctx.strokeStyle = 'rgba(6, 182, 212, 0.3)'; |
| | ctx.lineWidth = 2; |
| | ctx.beginPath(); |
| | |
| | for (let x = 0; x < width; x++) { |
| | const y = height / 2 + Math.sin(x * 0.01 + time) * 20 * Math.sin(time * 0.5); |
| | if (x === 0) ctx.moveTo(x, y); |
| | else ctx.lineTo(x, y); |
| | } |
| | ctx.stroke(); |
| | } |
| | |
| | visualizer.animationId = requestAnimationFrame(drawVisualizer); |
| | } |
| |
|
| | |
| | function createBinauralBeats() { |
| | const carrier = parseFloat(document.getElementById('carrierSlider').value); |
| | const beat = parseFloat(document.getElementById('beatSlider').value); |
| | |
| | |
| | stopBinaural(); |
| | |
| | |
| | binauralNodes.masterGain = audioContext.createGain(); |
| | binauralNodes.masterGain.connect(audioContext.destination); |
| | binauralNodes.masterGain.connect(visualizer.analyser); |
| | |
| | if (currentMode === 'spatial') { |
| | |
| | binauralNodes.leftOsc = audioContext.createOscillator(); |
| | binauralNodes.rightOsc = audioContext.createOscillator(); |
| | binauralNodes.leftGain = audioContext.createGain(); |
| | binauralNodes.rightGain = audioContext.createGain(); |
| | binauralNodes.merger = audioContext.createChannelMerger(2); |
| | |
| | |
| | binauralNodes.leftOsc.frequency.value = carrier; |
| | binauralNodes.leftOsc.connect(binauralNodes.leftGain); |
| | binauralNodes.leftGain.connect(binauralNodes.merger, 0, 0); |
| | |
| | |
| | binauralNodes.rightOsc.frequency.value = carrier + beat; |
| | binauralNodes.rightOsc.connect(binauralNodes.rightGain); |
| | binauralNodes.rightGain.connect(binauralNodes.merger, 0, 1); |
| | |
| | binauralNodes.merger.connect(binauralNodes.masterGain); |
| | |
| | binauralNodes.leftOsc.start(); |
| | binauralNodes.rightOsc.start(); |
| | } else { |
| | |
| | binauralNodes.leftOsc = audioContext.createOscillator(); |
| | binauralNodes.modulator = audioContext.createOscillator(); |
| | binauralNodes.modulatorGain = audioContext.createGain(); |
| | |
| | |
| | binauralNodes.leftOsc.frequency.value = carrier; |
| | |
| | |
| | binauralNodes.modulator.frequency.value = beat; |
| | binauralNodes.modulatorGain.gain.value = 0.5; |
| | |
| | |
| | binauralNodes.modulator.connect(binauralNodes.modulatorGain.gain); |
| | |
| | |
| | const constantOffset = audioContext.createGain(); |
| | constantOffset.gain.value = 0.5; |
| | binauralNodes.leftOsc.connect(constantOffset); |
| | constantOffset.connect(binauralNodes.modulatorGain); |
| | |
| | |
| | binauralNodes.merger = audioContext.createChannelMerger(2); |
| | binauralNodes.modulatorGain.connect(binauralNodes.merger, 0, 0); |
| | binauralNodes.modulatorGain.connect(binauralNodes.merger, 0, 1); |
| | binauralNodes.merger.connect(binauralNodes.masterGain); |
| | |
| | binauralNodes.leftOsc.start(); |
| | binauralNodes.modulator.start(); |
| | } |
| | |
| | |
| | applyRamp(binauralNodes.masterGain.gain, 0.3); |
| | } |
| |
|
| | function stopBinaural() { |
| | if (binauralNodes.leftOsc) { |
| | try { binauralNodes.leftOsc.stop(); } catch(e) {} |
| | binauralNodes.leftOsc = null; |
| | } |
| | if (binauralNodes.rightOsc) { |
| | try { binauralNodes.rightOsc.stop(); } catch(e) {} |
| | binauralNodes.rightOsc = null; |
| | } |
| | if (binauralNodes.modulator) { |
| | try { binauralNodes.modulator.stop(); } catch(e) {} |
| | binauralNodes.modulator = null; |
| | } |
| | if (binauralNodes.masterGain) { |
| | binauralNodes.masterGain.disconnect(); |
| | } |
| | } |
| |
|
| | |
| | function createNoise(type) { |
| | stopNoise(); |
| | activeNoiseType = type; |
| | |
| | const bufferSize = 2 * audioContext.sampleRate; |
| | const numChannels = 2; |
| | const buffer = audioContext.createBuffer(numChannels, bufferSize, audioContext.sampleRate); |
| | |
| | |
| | for (let channel = 0; channel < numChannels; channel++) { |
| | const data = buffer.getChannelData(channel); |
| | |
| | if (type === 'white') { |
| | |
| | for (let i = 0; i < bufferSize; i++) { |
| | data[i] = Math.random() * 2 - 1; |
| | } |
| | } else if (type === 'pink') { |
| | |
| | let b0 = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0, b5 = 0, b6 = 0; |
| | for (let i = 0; i < bufferSize; i++) { |
| | const white = Math.random() * 2 - 1; |
| | b0 = 0.99886 * b0 + white * 0.0555179; |
| | b1 = 0.99332 * b1 + white * 0.0750759; |
| | b2 = 0.96900 * b2 + white * 0.1538520; |
| | b3 = 0.86650 * b3 + white * 0.3104856; |
| | b4 = 0.55000 * b4 + white * 0.5329522; |
| | b5 = -0.7616 * b5 - white * 0.0168980; |
| | data[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; |
| | data[i] *= 0.11; |
| | b6 = white * 0.115926; |
| | } |
| | } else if (type === 'brown') { |
| | |
| | let lastOut = 0; |
| | for (let i = 0; i < bufferSize; i++) { |
| | const white = Math.random() * 2 - 1; |
| | data[i] = (lastOut + (0.02 * white)) / 1.02; |
| | lastOut = data[i]; |
| | data[i] *= 3.5; |
| | } |
| | } |
| | } |
| | |
| | |
| | noiseNodes.masterGain = audioContext.createGain(); |
| | noiseNodes.leftSource = audioContext.createBufferSource(); |
| | noiseNodes.rightSource = audioContext.createBufferSource(); |
| | noiseNodes.leftGain = audioContext.createGain(); |
| | noiseNodes.rightGain = audioContext.createGain(); |
| | noiseNodes.leftPanner = audioContext.createStereoPanner(); |
| | noiseNodes.rightPanner = audioContext.createStereoPanner(); |
| | |
| | |
| | noiseNodes.leftSource.buffer = buffer; |
| | noiseNodes.rightSource.buffer = buffer; |
| | noiseNodes.leftSource.loop = true; |
| | noiseNodes.rightSource.loop = true; |
| | |
| | |
| | const spatialWidth = parseInt(document.getElementById('spatialWidthSlider').value) / 100; |
| | const panValue = spatialWidth; |
| | |
| | |
| | noiseNodes.leftPanner.pan.value = -panValue; |
| | noiseNodes.leftSource.connect(noiseNodes.leftGain); |
| | noiseNodes.leftGain.connect(noiseNodes.leftPanner); |
| | noiseNodes.leftPanner.connect(noiseNodes.masterGain); |
| | |
| | |
| | noiseNodes.rightPanner.pan.value = panValue; |
| | noiseNodes.rightSource.connect(noiseNodes.rightGain); |
| | noiseNodes.rightGain.connect(noiseNodes.rightPanner); |
| | noiseNodes.rightPanner.connect(noiseNodes.masterGain); |
| | |
| | |
| | if (spatialWidth > 0.5) { |
| | const delay = audioContext.createDelay(); |
| | delay.delayTime.value = 0.01; |
| | noiseNodes.rightPanner.disconnect(); |
| | noiseNodes.rightPanner.connect(delay); |
| | delay.connect(noiseNodes.masterGain); |
| | } |
| | |
| | noiseNodes.masterGain.connect(audioContext.destination); |
| | noiseNodes.masterGain.connect(visualizer.analyser); |
| | |
| | |
| | const volume = parseInt(document.getElementById('noiseVolumeSlider').value) / 100; |
| | noiseNodes.masterGain.gain.value = volume; |
| | |
| | noiseNodes.leftSource.start(); |
| | noiseNodes.rightSource.start(); |
| | |
| | |
| | |
| | noiseNodes.rightSource.playbackRate.value = 0.999; |
| | |
| | updateNoiseButtonStates(); |
| | } |
| |
|
| | function stopNoise() { |
| | if (noiseNodes.leftSource) { |
| | try { noiseNodes.leftSource.stop(); } catch(e) {} |
| | noiseNodes.leftSource = null; |
| | } |
| | if (noiseNodes.rightSource) { |
| | try { noiseNodes.rightSource.stop(); } catch(e) {} |
| | noiseNodes.rightSource = null; |
| | } |
| | if (noiseNodes.masterGain) { |
| | noiseNodes.masterGain.disconnect(); |
| | } |
| | activeNoiseType = null; |
| | updateNoiseButtonStates(); |
| | } |
| |
|
| | function updateNoiseButtonStates() { |
| | const buttons = ['pinkNoiseBtn', 'brownNoiseBtn', 'whiteNoiseBtn']; |
| | const types = ['pink', 'brown', 'white']; |
| | |
| | buttons.forEach((id, index) => { |
| | const btn = document.getElementById(id); |
| | if (activeNoiseType === types[index]) { |
| | btn.classList.add('playing'); |
| | btn.querySelector('i[data-lucide="play-circle"]').setAttribute('data-lucide', 'stop-circle'); |
| | } else { |
| | btn.classList.remove('playing'); |
| | btn.querySelector('i[data-lucide="stop-circle"]')?.setAttribute('data-lucide', 'play-circle'); |
| | } |
| | }); |
| | |
| | lucide.createIcons(); |
| | } |
| |
|
| | |
| | function applyRamp(gainNode, targetValue) { |
| | const now = audioContext.currentTime; |
| | |
| | if (rampMode === 'none') { |
| | gainNode.setValueAtTime(targetValue, now); |
| | } else if (rampMode === 'up') { |
| | gainNode.setValueAtTime(0.001, now); |
| | gainNode.exponentialRampToValueAtTime(targetValue, now + rampDuration); |
| | } else if (rampMode === 'down') { |
| | gainNode.setValueAtTime(targetValue, now); |
| | gainNode.exponentialRampToValueAtTime(0.001, now + rampDuration); |
| | |
| | setTimeout(() => { |
| | stopAll(); |
| | }, rampDuration * 1000); |
| | } |
| | } |
| |
|
| | function stopAll() { |
| | stopBinaural(); |
| | stopNoise(); |
| | isPlaying = false; |
| | updatePlayButton(); |
| | } |
| |
|
| | function updatePlayButton() { |
| | const btn = document.getElementById('playBinauralBtn'); |
| | const stopBtn = document.getElementById('stopAllBtn'); |
| | |
| | if (isPlaying) { |
| | btn.innerHTML = '<i data-lucide="pause" class="w-6 h-6"></i><span>Stop Binaural</span>'; |
| | btn.classList.remove('from-cyan-400', 'via-purple-500', 'to-pink-500'); |
| | btn.classList.add('from-red-400', 'via-red-500', 'to-rose-600'); |
| | |
| | stopBtn.disabled = false; |
| | stopBtn.classList.remove('opacity-50', 'cursor-not-allowed'); |
| | } else { |
| | btn.innerHTML = '<i data-lucide="play" class="w-6 h-6"></i><span>Generate Binaural Beat</span>'; |
| | btn.classList.add('from-cyan-400', 'via-purple-500', 'to-pink-500'); |
| | btn.classList.remove('from-red-400', 'via-red-500', 'to-rose-600'); |
| | |
| | stopBtn.disabled = true; |
| | stopBtn.classList.add('opacity-50', 'cursor-not-allowed'); |
| | } |
| | lucide.createIcons(); |
| | } |
| |
|
| | |
| | document.addEventListener('DOMContentLoaded', () => { |
| | |
| | document.getElementById('spatialBtn').addEventListener('click', () => { |
| | currentMode = 'spatial'; |
| | document.getElementById('spatialBtn').classList.add('active', 'from-cyan-500', 'to-blue-500', 'text-white'); |
| | document.getElementById('spatialBtn').classList.remove('bg-white/10', 'text-purple-200'); |
| | document.getElementById('monauralBtn').classList.remove('active', 'from-purple-500', 'to-pink-500', 'text-white'); |
| | document.getElementById('monauralBtn').classList.add('bg-white/10', 'text-purple-200'); |
| | document.getElementById('modeDescription').textContent = 'Different frequencies in each ear create the beat perception'; |
| | }); |
| | |
| | document.getElementById('monauralBtn').addEventListener('click', () => { |
| | currentMode = 'monaural'; |
| | document.getElementById('monauralBtn').classList.add('active', 'from-purple-500', 'to-pink-500', 'text-white'); |
| | document.getElementById('monauralBtn').classList.remove('bg-white/10', 'text-purple-200'); |
| | document.getElementById('spatialBtn').classList.remove('active', 'from-cyan-500', 'to-blue-500', 'text-white'); |
| | document.getElementById('spatialBtn').classList.add('bg-white/10', 'text-purple-200'); |
| | document.getElementById('modeDescription').textContent = 'Same frequency in both ears with amplitude modulation'; |
| | }); |
| | |
| | |
| | const rampButtons = { |
| | 'rampNone': 'none', |
| | 'rampUp': 'up', |
| | 'rampDown': 'down' |
| | }; |
| | |
| | Object.keys(rampButtons).forEach(id => { |
| | document.getElementById(id).addEventListener('click', () => { |
| | rampMode = rampButtons[id]; |
| | Object.keys(rampButtons).forEach(btnId => { |
| | const btn = document.getElementById(btnId); |
| | if (btnId === id) { |
| | btn.classList.add('active', 'from-purple-500', 'to-pink-500', 'text-white'); |
| | btn.classList.remove('bg-white/10', 'text-purple-200'); |
| | } else { |
| | btn.classList.remove('active', 'from-purple-500', 'to-pink-500', 'text-white'); |
| | btn.classList.add('bg-white/10', 'text-purple-200'); |
| | } |
| | }); |
| | }); |
| | }); |
| | |
| | |
| | document.getElementById('carrierSlider').addEventListener('input', (e) => { |
| | document.getElementById('carrierValue').textContent = e.target.value + ' Hz'; |
| | if (isPlaying && currentMode === 'spatial') { |
| | const beat = parseFloat(document.getElementById('beatSlider').value); |
| | binauralNodes.leftOsc.frequency.setValueAtTime(parseFloat(e.target.value), audioContext.currentTime); |
| | binauralNodes.rightOsc.frequency.setValueAtTime(parseFloat(e.target.value) + beat, audioContext.currentTime); |
| | } else if (isPlaying) { |
| | binauralNodes.leftOsc.frequency.setValueAtTime(parseFloat(e.target.value), audioContext.currentTime); |
| | } |
| | }); |
| | |
| | document.getElementById('beatSlider').addEventListener('input', (e) => { |
| | document.getElementById('beatValue').textContent = e.target.value + ' Hz'; |
| | if (isPlaying) { |
| | const carrier = parseFloat(document.getElementById('carrierSlider').value); |
| | const beat = parseFloat(e.target.value); |
| | if (currentMode === 'spatial') { |
| | binauralNodes.rightOsc.frequency.setValueAtTime(carrier + beat, audioContext.currentTime); |
| | } else { |
| | binauralNodes.modulator.frequency.setValueAtTime(beat, audioContext.currentTime); |
| | } |
| | } |
| | }); |
| | |
| | document.getElementById('rampDuration').addEventListener('input', (e) => { |
| | rampDuration = parseInt(e.target.value); |
| | document.getElementById('rampDurationValue').textContent = rampDuration + 's'; |
| | }); |
| | |
| | document.getElementById('noiseVolumeSlider').addEventListener('input', (e) => { |
| | const value = e.target.value; |
| | document.getElementById('noiseVolumeValue').textContent = value + '%'; |
| | if (noiseNodes.masterGain) { |
| | noiseNodes.masterGain.gain.setValueAtTime(value / 100, audioContext.currentTime); |
| | } |
| | }); |
| | |
| | document.getElementById('spatialWidthSlider').addEventListener('input', (e) => { |
| | const value = parseInt(e.target.value); |
| | const label = value < 30 ? 'Narrow' : value < 70 ? 'Medium' : 'Wide'; |
| | document.getElementById('spatialWidthValue').textContent = label; |
| | |
| | if (activeNoiseType) { |
| | |
| | createNoise(activeNoiseType); |
| | } |
| | }); |
| | |
| | |
| | document.querySelectorAll('.brainwave-btn').forEach(btn => { |
| | btn.addEventListener('click', () => { |
| | const carrier = btn.dataset.carrier; |
| | const beat = btn.dataset.beat; |
| | document.getElementById('carrierSlider').value = carrier; |
| | document.getElementById('beatSlider').value = beat; |
| | document.getElementById('carrierValue').textContent = carrier + ' Hz'; |
| | document.getElementById('beatValue').textContent = beat + ' Hz'; |
| | |
| | |
| | btn.style.transform = 'scale(0.95)'; |
| | setTimeout(() => btn.style.transform = '', 100); |
| | }); |
| | }); |
| | |
| | |
| | document.getElementById('playBinauralBtn').addEventListener('click', () => { |
| | initAudioContext(); |
| | if (isPlaying) { |
| | stopBinaural(); |
| | isPlaying = false; |
| | } else { |
| | createBinauralBeats(); |
| | isPlaying = true; |
| | } |
| | updatePlayButton(); |
| | }); |
| | |
| | |
| | document.getElementById('pinkNoiseBtn').addEventListener('click', () => { |
| | initAudioContext(); |
| | if (activeNoiseType === 'pink') { |
| | stopNoise(); |
| | } else { |
| | createNoise('pink'); |
| | } |
| | }); |
| | |
| | document.getElementById('brownNoiseBtn').addEventListener('click', () => { |
| | initAudioContext(); |
| | if (activeNoiseType === 'brown') { |
| | stopNoise(); |
| | } else { |
| | createNoise('brown'); |
| | } |
| | }); |
| | |
| | document.getElementById('whiteNoiseBtn').addEventListener('click', () => { |
| | initAudioContext(); |
| | if (activeNoiseType === 'white') { |
| | stopNoise(); |
| | } else { |
| | createNoise('white'); |
| | } |
| | }); |
| | |
| | document.getElementById('stopAllBtn').addEventListener('click', stopAll); |
| | |
| | |
| | initParticles(); |
| | }); |
| |
|
| | |
| | function initParticles() { |
| | const canvas = document.getElementById('particleCanvas'); |
| | const ctx = canvas.getContext('2d'); |
| | |
| | function resize() { |
| | canvas.width = window.innerWidth; |
| | canvas.height = window.innerHeight; |
| | } |
| | resize(); |
| | window.addEventListener('resize', resize); |
| | |
| | const particles = []; |
| | const particleCount = 50; |
| | |
| | for (let i = 0; i < particleCount; i++) { |
| | particles.push({ |
| | x: Math.random() * canvas.width, |
| | y: Math.random() * canvas.height, |
| | radius: Math.random() * 3 + 1, |
| | vx: (Math.random() - 0.5) * 0.5, |
| | vy: (Math.random() - 0.5) * 0.5, |
| | color: `hsla(${Math.random() * 60 + 240}, 70%, 60%, ${Math.random() * 0.3})` |
| | }); |
| | } |
| | |
| | function animate() { |
| | ctx.clearRect(0, 0, canvas.width, canvas.height); |
| | |
| | particles.forEach(p => { |
| | p.x += p.vx; |
| | p.y += p.vy; |
| | |
| | if (p.x < 0 || p.x > canvas.width) p.vx *= -1; |
| | if (p.y < 0 || p.y > canvas.height) p.vy *= -1; |
| | |
| | ctx.beginPath(); |
| | ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); |
| | ctx.fillStyle = p.color; |
| | ctx.fill(); |
| | }); |
| | |
| | |
| | particles.forEach((p1, i) => { |
| | particles.slice(i + 1).forEach(p2 => { |
| | const dx = p1.x - p2.x; |
| | const dy = p1.y - p2.y; |
| | const distance = Math.sqrt(dx * dx + dy * dy); |
| | |
| | if (distance < 100) { |
| | ctx.beginPath(); |
| | ctx.moveTo(p1.x, p1.y); |
| | ctx.lineTo(p2.x, p2.y); |
| | ctx.strokeStyle = `rgba(147, 51, 234, ${0.1 * (1 - distance / 100)})`; |
| | ctx.stroke(); |
| | } |
| | }); |
| | }); |
| | |
| | requestAnimationFrame(animate); |
| | } |
| | animate(); |
| | } |
| |
|
| | |
| | window.addEventListener('beforeunload', () => { |
| | stopAll(); |
| | if (audioContext) { |
| | audioContext.close(); |
| | } |
| | }); |