about summary refs log tree commit diff stats
path: root/html/tuner
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2024-09-25 22:35:30 -0400
committerelioat <elioat@tilde.institute>2024-09-25 22:35:30 -0400
commit1d73f5df208dffe157aff4f6261518e852d873fb (patch)
tree695083aa155029c097be6e66b06dbc258b54da3f /html/tuner
parente010163faaca10c20d5301af99f1505bb361d5a9 (diff)
downloadtour-1d73f5df208dffe157aff4f6261518e852d873fb.tar.gz
*
Diffstat (limited to 'html/tuner')
-rw-r--r--html/tuner/index.html193
1 files changed, 193 insertions, 0 deletions
diff --git a/html/tuner/index.html b/html/tuner/index.html
new file mode 100644
index 0000000..4fe848d
--- /dev/null
+++ b/html/tuner/index.html
@@ -0,0 +1,193 @@
+<!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 analyser;
+        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);
+
+            analyser = audioContext.createAnalyser();
+            analyser.fftSize = 2048;
+            bufferLength = analyser.frequencyBinCount;
+            dataArray = new Uint8Array(bufferLength);
+
+            source.connect(analyser);
+            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);
+
+            analyser.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(analyser.frequencyBinCount);
+            analyser.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>