🔧 AI Nachrichten Major AI platforms go down in unprecedented simultaneous outage(03.09.2026 um 17:34 Uhr)
🔧 AI Nachrichten ChatGPT, Claude, and Grok Down? Users Report Widespread Outages(03.09.2026 um 19:14 Uhr)
🔧 AI Nachrichten OpenAI Launches GPT-6 Astra, Says We May Have Entered the AGI Era(03.09.2026 um 22:08 Uhr)
🔧 AI Nachrichten Claude Comes to CarPlay as Fifth Major AI Chatbot App(05.09.2026 um 05:31 Uhr)
🔧 AI Nachrichten OpenAI’s GPT-6 Astra Is AGI, Says NVIDIA CEO Jensen Huang(07.09.2026 um 06:31 Uhr)
🔧 AI Nachrichten Blame AI companies for Mac mini and Mac Studio shortage(31.08.2026 um 10:32 Uhr)
🔧 AI Nachrichten Major AI platforms go down in unprecedented simultaneous outage(03.09.2026 um 17:34 Uhr)
🔧 AI Nachrichten ChatGPT, Claude, and Grok Down? Users Report Widespread Outages(03.09.2026 um 19:14 Uhr)
🔧 AI Nachrichten OpenAI Launches GPT-6 Astra, Says We May Have Entered the AGI Era(03.09.2026 um 22:08 Uhr)
🔧 AI Nachrichten Claude Comes to CarPlay as Fifth Major AI Chatbot App(05.09.2026 um 05:31 Uhr)
🔧 AI Nachrichten OpenAI’s GPT-6 Astra Is AGI, Says NVIDIA CEO Jensen Huang(07.09.2026 um 06:31 Uhr)
🔧 AI Nachrichten Blame AI companies for Mac mini and Mac Studio shortage(31.08.2026 um 10:32 Uhr)

🔧 Programmierung 🕛 kürzlich 6 Min Lesezeit
0

Recording Spec-Compliant WAV Files (16-bit/Mono/Uncompressed) Using Only Web Audio

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

📝 Originally published (in Japanese) at forge.workstyle.tech.







Introduction to Clean Audio Recording



When it comes to recording audio in the browser, the first thing that comes to mind is likely the MediaRecorder API. It's easy to use and only requires a few lines of code to start recording. However, when it comes to using the recorded audio for machine learning preprocessing, such as voice conversion or feature extraction, MediaRecorder becomes inconvenient.



MediaRecorder typically outputs webm/opus, which is a non-reversible compression format. While it's sufficient for human ears, the fine details of the audio are already lost during the preprocessing stage. Moreover, many downstream pipelines require a strictly specified WAV format, which is 16-bit PCM, mono, and uncompressed.



In this article, we'll introduce an implementation that uses Web Audio to record raw PCM and encodes it into a WAV file manually. Although it's a tedious task to write binary data one byte at a time, once it's done, you'll have a high-quality recording with zero degradation.






Background: Why Raw PCM?



The Web Audio API allows us to redirect the microphone input to an AudioContext graph and extract the raw samples (Float32, -1 to 1) at any point. Since we're not passing the audio through any compression codec, we can capture the original signal from the microphone. By encoding these Float32 samples into a 16-bit WAV file, we can create a file that meets the specifications without any degradation.



Our approach consists of two stages:





  1. Recording: Collecting Float32 raw samples from the microphone (using Web Audio)


  2. Encoding: Converting the collected Float32 samples into a 16-bit PCM WAV byte array






Step 1: Opening the Microphone with Clean Settings



We use getUserMedia to access the microphone, but it's essential to disable all browser audio processing. Echo cancellation, noise suppression, and automatic gain control are useful for voice calls, but they're unnecessary for recording and can even degrade the audio quality.




CODE
this.stream = await navigator.mediaDevices.getUserMedia({
audio: { channelCount: 1, echoCancellation: false,
noiseSuppression: false, autoGainControl: false },
});






We set channelCount to 1 to require mono audio. To achieve "clean recording" as specified, it's crucial to explicitly set these flags to false.






Step 2: Collecting Raw Samples with ScriptProcessor



We create a MediaStreamSource from the microphone and capture the samples flowing from it. Here, we use a ScriptProcessorNode.




CODE
this.ctx = new AudioContext();
this.sampleRate = this.ctx.sampleRate; // usually 48000 (>= 44.1kHz)
this.source = this.ctx.createMediaStreamSource(this.stream);
this.node = this.ctx.createScriptProcessor(4096, 1, 1); // buffer, in=1, out=1
this.chunks = [];
this.node.onaudioprocess = (e) => {
// getChannelData uses an internal buffer, so we must copy and escape it
this.chunks.push(new Float32Array(e.inputBuffer.getChannelData(0)));
};
this.source.connect(this.node);
this.node.connect(this.ctx.destination); // required for some browsers to trigger






There are a few key points to note:




  • Copy the samples: The Float32Array returned by getChannelData(0) is an internal buffer that will be overwritten on the next callback. If we don't copy it using new Float32Array(...), the entire recording will be overwritten with the last frame.

  • Connect to destination: Some browsers require the ScriptProcessorNode to be connected to the ctx.destination to trigger the onaudioprocess event. Even if we don't need to play the audio, we connect it to ensure the event is triggered.

  • Sample rate is device-dependent: ctx.sampleRate is dependent on the environment, and most devices use 48000 (which is greater than or equal to 44.1kHz). We record the actual sample rate here and use it to write the correct header later. Using the actual recorded sample rate in the header is crucial for creating a compliant file without any resampling.




Note: ScriptProcessorNode is deprecated, and its successor is AudioWorklet. However, for simple use cases like collecting samples, ScriptProcessorNode is still sufficient. If you need low latency or heavy DSP processing, consider migrating to AudioWorklet.




When stopping the recording, we concatenate the collected chunks into a single Float32Array, release the microphone and AudioContext, and then proceed to the next encoding step.






Step 3: Manually Building the 16-bit PCM WAV



Now, let's create the WAV byte array. The WAV format consists of a 44-byte header followed by the PCM data. We use an ArrayBuffer and DataView to write the fields one by one.




CODE
export function encodeWav(samples: Float32Array, sampleRate: number): Blob {
const buffer = new ArrayBuffer(44 + samples.length * 2); // 16bit = 2byte/sample
const view = new DataView(buffer);
const writeStr = (o: number, s: string) => {
for (let i = 0; i < s.length; i++) view.setUint8(o + i, s.charCodeAt(i));
};
writeStr(0, 'RIFF');
view.setUint32(4, 36 + samples.length * 2, true); // file size - 8
writeStr(8, 'WAVE');
writeStr(12, 'fmt ');
view.setUint32(16, 16, true); // fmt chunk size
view.setUint16(20, 1, true); // format = 1 (PCM uncompressed)
view.setUint16(22, 1, true); // channel count = mono
view.setUint32(24, sampleRate, true); // sample rate
view.setUint32(28, sampleRate * 2, true);// bytes per second = sample rate * block align
view.setUint16(32, 2, true); // block align = mono * 16bit/8
view.setUint16(34, 16, true); // bit depth = 16
writeStr(36, 'data');
view.setUint32(40, samples.length * 2, true); // data length
// ... body ...
}






The key points in the header are:




  • Format number 1 means PCM (uncompressed).

  • Multi-byte values are little-endian, so we write them with true as the third argument to DataView.

  • Block align and byte rate are calculated from the channel count and bit depth (for mono 16-bit, block align = 2, byte rate = sample rate * 2). By writing the actual sample rate here, we ensure that the WAV file plays at the correct speed.



The body of the WAV file is where we quantize the Float32 samples into 16-bit integers. Note that the scale is asymmetric:




CODE
let off = 44;
for (let i = 0; i < samples.length; i++) {
const s = Math.max(-1, Math.min(1, samples[i])); // clamp to range
view.setInt16(off, s < 0 ? s * 0x8000 : s * 0x7fff, true); // asymmetric scale
off += 2;
}
return new Blob([view], { type: 'audio/wav' });






The 16-bit signed range is asymmetric: -32768 to +32767. For negative values, we multiply by 0x8000 (32768), and for positive values, we multiply by 0x7fff (32767) to utilize the full scale correctly. Some implementations may multiply both by 0x7fff, but this would lose some dynamic range on the negative side. Although it's a minor detail, it's essential for creating a compliant file. Clamping the input to the -1 to 1 range before quantization also prevents overflow.



Finally, we create a Blob (with type: 'audio/wav') that can be used as needed, such as downloading it using URL.createObjectURL or sending it to a server via FormData.






Conclusion



To achieve clean audio recording for preprocessing, we must:




  • Use Web Audio to record raw PCM instead of MediaRecorder.

  • Disable all browser audio processing using getUserMedia.

  • Collect Float32 samples using ScriptProcessorNode and copy them to avoid overwriting.

  • Connect the ScriptProcessorNode to the destination to ensure the onaudioprocess event is triggered.

  • Use the actual recorded sample rate in the WAV header.

  • Build the WAV file manually using ArrayBuffer and DataView, considering little-endian and asymmetric quantization.



By following these steps, you'll be able to create high-quality, degradation-free audio recordings that meet the required specifications for machine learning preprocessing.

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
3 Quellen
GPT-6 Astra Release Today? OpenAI’s Next Major AI Model Is Almost Here
1 Quelle
Apple accuses OpenAI of destroying evidence as trade-secrets fight intensifies
1 Quelle
Major AI platforms go down in unprecedented simultaneous outage