Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>3D Audio Spectrum Analyzer</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: Arial, sans-serif; | |
| } | |
| body { | |
| background: #1a1a1a; | |
| color: #fff; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| } | |
| .header { | |
| text-align: center; | |
| padding: 20px 0; | |
| } | |
| .controls { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
| gap: 20px; | |
| margin: 20px 0; | |
| } | |
| .control-panel { | |
| background: #2a2a2a; | |
| padding: 20px; | |
| border-radius: 8px; | |
| } | |
| .visualization { | |
| display: grid; | |
| grid-template-columns: 2fr 1fr; | |
| gap: 20px; | |
| margin: 20px 0; | |
| } | |
| canvas { | |
| width: 100%; | |
| height: 400px; | |
| background: #2a2a2a; | |
| border-radius: 8px; | |
| } | |
| button { | |
| background: #4CAF50; | |
| color: white; | |
| padding: 10px 20px; | |
| border: none; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| margin: 5px; | |
| } | |
| button:hover { | |
| background: #45a049; | |
| } | |
| input[type="number"] { | |
| width: 100%; | |
| padding: 8px; | |
| margin: 5px 0; | |
| border-radius: 4px; | |
| border: 1px solid #444; | |
| background: #333; | |
| color: white; | |
| } | |
| .device-status { | |
| background: #2a2a2a; | |
| padding: 20px; | |
| border-radius: 8px; | |
| margin-top: 20px; | |
| } | |
| .preset-container { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); | |
| gap: 10px; | |
| margin: 20px 0; | |
| } | |
| .preset { | |
| background: #333; | |
| padding: 10px; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| } | |
| .preset:hover { | |
| background: #444; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>3D Audio Spectrum Analyzer</h1> | |
| </div> | |
| <div class="controls"> | |
| <div class="control-panel"> | |
| <h3>Device Configuration</h3> | |
| <button id="startAnalysis">Start Analysis</button> | |
| <button id="stopAnalysis">Stop Analysis</button> | |
| <button id="calibrate">Calibrate Devices</button> | |
| <div> | |
| <label>Device Role:</label> | |
| <select id="deviceRole"> | |
| <option value="left">Left Channel</option> | |
| <option value="right">Right Channel</option> | |
| <option value="center">Center Channel</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="control-panel"> | |
| <h3>Binaural Beat Generator</h3> | |
| <div> | |
| <label>Base Frequency (Hz):</label> | |
| <input type="number" id="baseFreq" value="432" min="20" max="1000"> | |
| </div> | |
| <div> | |
| <label>Beat Frequency (Hz):</label> | |
| <input type="number" id="beatFreq" value="7" min="1" max="40"> | |
| </div> | |
| <button id="startBinaural">Generate Binaural Beat</button> | |
| <button id="stopBinaural">Stop</button> | |
| </div> | |
| </div> | |
| <div class="visualization"> | |
| <canvas id="spectrumCanvas"></canvas> | |
| <canvas id="3dRoomCanvas"></canvas> | |
| </div> | |
| <div class="preset-container"> | |
| <div class="preset"> | |
| <h4>Alpha Wave</h4> | |
| <p>8-12 Hz</p> | |
| </div> | |
| <div class="preset"> | |
| <h4>Theta Wave</h4> | |
| <p>4-7 Hz</p> | |
| </div> | |
| <div class="preset"> | |
| <h4>Delta Wave</h4> | |
| <p>0.5-4 Hz</p> | |
| </div> | |
| </div> | |
| <div class="device-status"> | |
| <h3>Connected Devices</h3> | |
| <div id="deviceList"></div> | |
| </div> | |
| </div> | |
| <script> | |
| class AudioAnalyzer { | |
| constructor() { | |
| this.audioContext = null; | |
| this.analyser = null; | |
| this.oscillator = null; | |
| this.isPlaying = false; | |
| } | |
| async initialize() { | |
| try { | |
| this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| this.analyser = this.audioContext.createAnalyser(); | |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
| const source = this.audioContext.createMediaStreamSource(stream); | |
| source.connect(this.analyser); | |
| this.setupVisualization(); | |
| } catch (error) { | |
| console.error('Error initializing audio:', error); | |
| } | |
| } | |
| generateBinauralBeat(baseFreq, beatFreq) { | |
| if (this.isPlaying) this.stopBinauralBeat(); | |
| const leftOsc = this.audioContext.createOscillator(); | |
| const rightOsc = this.audioContext.createOscillator(); | |
| const leftGain = this.audioContext.createGain(); | |
| const rightGain = this.audioContext.createGain(); | |
| const merger = this.audioContext.createChannelMerger(2); | |
| leftOsc.frequency.value = baseFreq; | |
| rightOsc.frequency.value = baseFreq + beatFreq; | |
| leftOsc.connect(leftGain); | |
| rightOsc.connect(rightGain); | |
| leftGain.connect(merger, 0, 0); | |
| rightGain.connect(merger, 0, 1); | |
| merger.connect(this.audioContext.destination); | |
| leftOsc.start(); | |
| rightOsc.start(); | |
| this.oscillator = { left: leftOsc, right: rightOsc }; | |
| this.isPlaying = true; | |
| } | |
| stopBinauralBeat() { | |
| if (this.oscillator) { | |
| this.oscillator.left.stop(); | |
| this.oscillator.right.stop(); | |
| this.isPlaying = false; | |
| } | |
| } | |
| setupVisualization() { | |
| const canvas = document.getElementById('spectrumCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const width = canvas.width; | |
| const height = canvas.height; | |
| const draw = () => { | |
| requestAnimationFrame(draw); | |
| const dataArray = new Uint8Array(this.analyser.frequencyBinCount); | |
| this.analyser.getByteFrequencyData(dataArray); | |
| ctx.fillStyle = '#2a2a2a'; | |
| ctx.fillRect(0, 0, width, height); | |
| const barWidth = width / dataArray.length; | |
| let x = 0; | |
| dataArray.forEach(value => { | |
| const barHeight = value * height / 255; | |
| ctx.fillStyle = `hsl(${value}, 100%, 50%)`; | |
| ctx.fillRect(x, height - barHeight, barWidth, barHeight); | |
| x += barWidth; | |
| }); | |
| }; | |
| draw(); | |
| } | |
| } | |
| // Initialize application | |
| const analyzer = new AudioAnalyzer(); | |
| document.getElementById('startAnalysis').addEventListener('click', () => analyzer.initialize()); | |
| document.getElementById('startBinaural').addEventListener('click', () => { | |
| const baseFreq = parseFloat(document.getElementById('baseFreq').value); | |
| const beatFreq = parseFloat(document.getElementById('beatFreq').value); | |
| analyzer.generateBinauralBeat(baseFreq, beatFreq); | |
| }); | |
| document.getElementById('stopBinaural').addEventListener('click', () => analyzer.stopBinauralBeat()); | |
| // Setup room visualization | |
| const room3d = document.getElementById('3dRoomCanvas'); | |
| const ctx3d = room3d.getContext('2d'); | |
| function draw3dRoom() { | |
| // Basic 3D room visualization implementation would go here | |
| // This would require a more complex 3D rendering library for proper implementation | |
| ctx3d.fillStyle = '#2a2a2a'; | |
| ctx3d.fillRect(0, 0, room3d.width, room3d.height); | |
| ctx3d.strokeStyle = '#4CAF50'; | |
| ctx3d.beginPath(); | |
| ctx3d.moveTo(50, 50); | |
| ctx3d.lineTo(room3d.width - 50, 50); | |
| ctx3d.lineTo(room3d.width - 50, room3d.height - 50); | |
| ctx3d.lineTo(50, room3d.height - 50); | |
| ctx3d.closePath(); | |
| ctx3d.stroke(); | |
| } | |
| draw3dRoom(); | |
| </script> | |
| </body> | |
| </html><script async data-explicit-opt-in="true" data-cookie-opt-in="true" src="https://vercel.live/_next-live/feedback/feedback.js"></script> |