about summary refs log blame commit diff stats
path: root/html/tuner/index.html
blob: 20bf125f1aa090f87dbab5b199a638d76171a7d5 (plain) (tree)


































































                                                                          
                     






















                                                                                    


                                                      

                                                     
                                     















                                                                  
                                                      

























                                                                     

                                                                           


















































                                                                                                 
<!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>