Created
April 7, 2025 17:43
Revisions
-
ggorlen created this gist
Apr 7, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,97 @@ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Record Mic to WAV</title> </head> <body> <button id="record">Record Mic</button> <script> document.getElementById("record").onclick = async () => { const stream = await navigator.mediaDevices.getUserMedia({ audio: true, }); const mediaRecorder = new MediaRecorder(stream); const chunks = []; mediaRecorder.ondataavailable = (e) => { if (e.data.size > 0) { chunks.push(e.data); } }; mediaRecorder.onstop = async () => { const blob = new Blob(chunks, { type: "audio/webm" }); // Decode audio data const arrayBuffer = await blob.arrayBuffer(); const audioCtx = new AudioContext(); const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer); // Convert to WAV const wavBlob = encodeWAV(audioBuffer); // Download const url = URL.createObjectURL(wavBlob); const a = document.createElement("a"); a.href = url; a.download = "mic.wav"; a.click(); URL.revokeObjectURL(url); }; mediaRecorder.start(); // Stop after 5 seconds setTimeout(() => mediaRecorder.stop(), 5000); }; function encodeWAV(audioBuffer) { const numChannels = audioBuffer.numberOfChannels; const sampleRate = audioBuffer.sampleRate; const numSamples = audioBuffer.length; const format = 1; // PCM const bitsPerSample = 16; const blockAlign = (numChannels * bitsPerSample) / 8; const byteRate = sampleRate * blockAlign; const wavBuffer = new ArrayBuffer(44 + numSamples * blockAlign); const view = new DataView(wavBuffer); function writeString(view, offset, string) { for (let i = 0; i < string.length; i++) { view.setUint8(offset + i, string.charCodeAt(i)); } } writeString(view, 0, "RIFF"); view.setUint32(4, 36 + numSamples * blockAlign, true); writeString(view, 8, "WAVE"); writeString(view, 12, "fmt "); view.setUint32(16, 16, true); // Subchunk1Size view.setUint16(20, format, true); // AudioFormat view.setUint16(22, numChannels, true); view.setUint32(24, sampleRate, true); view.setUint32(28, byteRate, true); view.setUint16(32, blockAlign, true); view.setUint16(34, bitsPerSample, true); writeString(view, 36, "data"); view.setUint32(40, numSamples * blockAlign, true); // Interleave and write PCM samples let offset = 44; for (let i = 0; i < numSamples; i++) { for (let ch = 0; ch < numChannels; ch++) { let sample = audioBuffer.getChannelData(ch)[i]; sample = Math.max(-1, Math.min(1, sample)); view.setInt16(offset, sample * 0x7fff, true); offset += 2; } } return new Blob([view], { type: "audio/wav" }); } </script> </body> </html> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,50 @@ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Sine Wave to WAV</title> </head> <body> <button id="start">Start Recording</button> <a id="download" style="pointer-events: none; color: gray;">Download WAV</a> <script> let audioCtx, oscillator, mediaRecorder, audioChunks = []; document.getElementById('start').onclick = async () => { // Reset chunks and set up audio context audioChunks = []; audioCtx = new (window.AudioContext || window.webkitAudioContext)(); oscillator = audioCtx.createOscillator(); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(440, audioCtx.currentTime); // A4 const dest = audioCtx.createMediaStreamDestination(); oscillator.connect(dest); mediaRecorder = new MediaRecorder(dest.stream); mediaRecorder.ondataavailable = e => audioChunks.push(e.data); mediaRecorder.onstop = () => { const blob = new Blob(audioChunks, { type: 'audio/wav' }); // type may be ignored const url = URL.createObjectURL(blob); const downloadLink = document.getElementById('download'); downloadLink.href = url; downloadLink.download = 'sine.wav'; downloadLink.style.pointerEvents = 'auto'; downloadLink.style.color = 'blue'; downloadLink.textContent = 'Click to Download WAV'; }; mediaRecorder.start(); oscillator.start(); setTimeout(() => { oscillator.stop(); mediaRecorder.stop(); }, 2000); // Record for 2s }; </script> </body> </html> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,68 @@ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Generate Sine WAV</title> </head> <body> <!-- these are all from chatgpt https://chatgpt.com/c/67f3f926-e0c0-8012-99e8-7b3341545b31 --> <button id="generate">Generate WAV</button> <script> function generateSineWaveWav({ duration = 2, frequency = 440, sampleRate = 44100, volume = 0.5 }) { const numSamples = duration * sampleRate; const buffer = new Array(numSamples); // Generate raw PCM samples for (let i = 0; i < numSamples; i++) { buffer[i] = volume * Math.sin(2 * Math.PI * frequency * (i / sampleRate)); } // Convert to 16-bit PCM const wavBuffer = new ArrayBuffer(44 + numSamples * 2); const view = new DataView(wavBuffer); function writeString(view, offset, str) { for (let i = 0; i < str.length; i++) { view.setUint8(offset + i, str.charCodeAt(i)); } } // WAV file header writeString(view, 0, 'RIFF'); view.setUint32(4, 36 + numSamples * 2, true); // file size writeString(view, 8, 'WAVE'); writeString(view, 12, 'fmt '); view.setUint32(16, 16, true); // subchunk1 size view.setUint16(20, 1, true); // audio format (1 = PCM) view.setUint16(22, 1, true); // channels view.setUint32(24, sampleRate, true); view.setUint32(28, sampleRate * 2, true); // byte rate view.setUint16(32, 2, true); // block align view.setUint16(34, 16, true); // bits per sample writeString(view, 36, 'data'); view.setUint32(40, numSamples * 2, true); // Write samples let offset = 44; for (let i = 0; i < numSamples; i++) { const s = Math.max(-1, Math.min(1, buffer[i])); view.setInt16(offset, s * 0x7FFF, true); offset += 2; } return new Blob([view], { type: 'audio/wav' }); } document.getElementById('generate').onclick = () => { const blob = generateSineWaveWav({ duration: 2, frequency: 440 }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'sine.wav'; a.click(); URL.revokeObjectURL(url); }; </script> </body> </html>