<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Tuner</title> <style> * { box-sizing: border-box; margin: 0; padding: 0; } body { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; margin: 0; font-family: Arial, sans-serif; background-color: beige; } canvas { border: none; width: 100%; max-width: 100%; height: auto; } #note { margin-top: 10px; font-size: 18px; font-weight: bold; text-align: center; } #buttons { display: flex; flex-wrap: wrap; justify-content: center; margin-top: 10px; } button { margin: 5px; padding: 10px; font-size: 16px; flex: 1 1 25%; max-width: 100px; } @media (max-width: 600px) { button { flex: 1 1 33.33%; } } </style> </head> <body> <canvas id="waveformCanvas"></canvas> <div id="note">Note: N/A</div> <div id="buttons"></div> <script> const canvas = document.getElementById('waveformCanvas'); const canvasCtx = canvas.getContext('2d'); const noteDisplay = document.getElementById('note'); let audioContext; let analyzer; let dataArray; let bufferLength; // Note frequencies in Hz for A4 = 440Hz const notesFrequencies = { 'C': 261.63, 'C#': 277.18, 'D': 293.66, 'D#': 311.13, 'E': 329.63, 'F': 349.23, 'F#': 369.99, 'G': 392.00, 'G#': 415.30, 'A': 440.00, 'A#': 466.16, 'B': 493.88 }; navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => { audioContext = new (window.AudioContext || window.webkitAudioContext)(); const source = audioContext.createMediaStreamSource(stream); analyzer = audioContext.createAnalyser(); analyzer.fftSize = 2048; bufferLength = analyzer.frequencyBinCount; dataArray = new Uint8Array(bufferLength); source.connect(analyzer); drawWaveform(); setInterval(detectNote, 500); }).catch(err => { console.error('Error accessing the microphone:', err); }); function resizeCanvas() { canvas.width = window.innerWidth - 20; canvas.height = window.innerHeight / 4; } window.addEventListener('resize', resizeCanvas); resizeCanvas(); function drawWaveform() { requestAnimationFrame(drawWaveform); analyzer.getByteTimeDomainData(dataArray); canvasCtx.clearRect(0, 0, canvas.width, canvas.height); canvasCtx.beginPath(); const sliceWidth = canvas.width / bufferLength; let x = 0; for (let i = 0; i < bufferLength; i++) { const v = dataArray[i] / 128.0; const y = v * canvas.height / 2; if (i === 0) { canvasCtx.moveTo(x, y); } else { canvasCtx.lineTo(x, y); } x += sliceWidth; } canvasCtx.lineTo(canvas.width, canvas.height / 2); canvasCtx.stroke(); } // Detect the dominant frequency and map it to a musical note function detectNote() { const freqArray = new Float32Array(analyzer.frequencyBinCount); analyzer.getFloatFrequencyData(freqArray); let maxAmp = -Infinity; let maxIndex = 0; for (let i = 0; i < freqArray.length; i++) { if (freqArray[i] > maxAmp) { maxAmp = freqArray[i]; maxIndex = i; } } // Nyquist frequency is half the sample rate of a signal const nyquist = audioContext.sampleRate / 2; const frequency = maxIndex * nyquist / freqArray.length; const note = getNoteFromFrequency(frequency); noteDisplay.textContent = `Note: ${note}`; } // Convert frequency to musical note function getNoteFromFrequency(frequency) { const notes = [ 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B' ]; const A4 = 440; const semitoneRatio = Math.pow(2, 1 / 12); const noteIndex = Math.round(12 * Math.log2(frequency / A4)); const note = notes[(noteIndex % 12 + 12) % 12]; return frequency ? note : 'N/A'; } const buttonsContainer = document.getElementById('buttons'); Object.keys(notesFrequencies).forEach(note => { const button = document.createElement('button'); button.textContent = note; button.onclick = () => playNote(notesFrequencies[note]); buttonsContainer.appendChild(button); }); function playNote(frequency) { const osc = audioContext.createOscillator(); osc.type = 'sine'; // Valid values include 'sine', 'square', 'triangle', 'sawtooth' osc.frequency.setValueAtTime(frequency, audioContext.currentTime); // Frequency in Hz osc.connect(audioContext.destination); osc.start(); osc.stop(audioContext.currentTime + 1); // Play the note for 1 second } </script> </body> </html>