Client-Side Audio Extraction and Podcast Cleanup in the Browser
Recorded interviews, screen captures, and raw podcast tracks are often hundreds of megabytes in size. Uploading a 600 MB .mp4 video recording just to extract a 40 MB .mp3 voice track wastes bandwidth and exposes unedited recordings to external infrastructure. As outlined in our primer on how client-side file processing works, modern browsers include both the Web Audio API (OfflineAudioContext) and WebAssembly SIMD execution, making local media pipelines fast enough for daily production work.
Below are the architectural notes behind the Sunasty Audio Tools and Video Utilities, along with practical techniques we documented in cleaning audio for podcasts in the browser.
1. Demuxing MP4 Containers and Encoding to MP3 or WAV
An .mp4 or .mov file is an ISO Base Media File Format (ISOBMFF) container holding moov track metadata and mdat media sample boxes, typically pairing H.264/HEVC video with AAC-LC audio. When extracting audio in the MP4 to MP3 Converter, decoding the video frames is pure overhead—only the audio elementary stream needs to be demuxed, decoded into planar 32-bit float PCM (Float32Array), and passed either to a WAV RIFF header writer or a WebAssembly LAME MP3 encoder inside a Worker.
Writing a 16-bit PCM .wav file from a decoded AudioBuffer requires just a 44-byte RIFF/WAVE header followed by interleaved little-endian Int16 samples:
export function encodeAudioBufferToWav(audioBuffer) {
const numChannels = audioBuffer.numberOfChannels;
const sampleRate = audioBuffer.sampleRate;
const numFrames = audioBuffer.length;
const bytesPerSample = 2; // 16-bit PCM
const blockAlign = numChannels * bytesPerSample;
const dataSize = numFrames * blockAlign;
const buffer = new ArrayBuffer(44 + dataSize);
const view = new DataView(buffer);
const writeAscii = (offset, text) => {
for (let i = 0; i < text.length; i++) {
view.setUint8(offset + i, text.charCodeAt(i));
}
};
writeAscii(0, 'RIFF');
view.setUint32(4, 36 + dataSize, true);
writeAscii(8, 'WAVE');
writeAscii(12, 'fmt ');
view.setUint32(16, 16, true); // Subchunk1Size for PCM
view.setUint16(20, 1, true); // AudioFormat 1 = Linear PCM
view.setUint16(22, numChannels, true);
view.setUint32(24, sampleRate, true);
view.setUint32(28, sampleRate * blockAlign, true);
view.setUint16(32, blockAlign, true);
view.setUint16(34, 16, true); // BitsPerSample
writeAscii(36, 'data');
view.setUint32(40, dataSize, true);
const channels = [];
for (let ch = 0; ch < numChannels; ch++) {
channels.push(audioBuffer.getChannelData(ch));
}
let offset = 44;
for (let i = 0; i < numFrames; i++) {
for (let ch = 0; ch < numChannels; ch++) {
const sample = Math.max(-1, Math.min(1, channels[ch][i]));
view.setInt16(offset, sample < 0 ? sample * 0x8000 : sample * 0x7fff, true);
offset += 2;
}
}
return buffer;
}
2. High-Pass Rumble Filtering and Peak Normalization via OfflineAudioContext
Spoken-word tracks recorded on desk microphones frequently suffer from low-frequency HVAC hum or desk thumps below 85 Hz, plus uneven peak levels between speakers. Using OfflineAudioContext, we can run a second-order Butterworth high-pass filter (BiquadFilterNode set to highpass at 80 Hz, Q = 0.707) and dynamic compression faster than real time without playing audio through the speakers:
export async function renderCleanedVoiceBuffer(inputBuffer) {
const offlineCtx = new OfflineAudioContext(
inputBuffer.numberOfChannels,
inputBuffer.length,
inputBuffer.sampleRate
);
const source = offlineCtx.createBufferSource();
source.buffer = inputBuffer;
// Roll off sub-80Hz plosive and desk rumble
const highpass = offlineCtx.createBiquadFilter();
highpass.type = 'highpass';
highpass.frequency.value = 80;
highpass.Q.value = Math.SQRT1_2;
// Tame sudden vocal peaks
const compressor = offlineCtx.createDynamicsCompressor();
compressor.threshold.value = -18;
compressor.knee.value = 12;
compressor.ratio.value = 3;
compressor.attack.value = 0.005;
compressor.release.value = 0.15;
source.connect(highpass);
highpass.connect(compressor);
compressor.connect(offlineCtx.destination);
source.start(0);
return await offlineCtx.startRendering();
}
3. Vocal Separation, Time-Stretching, and Screen Capture
Beyond basic EQ and dynamics, several specialized workflows run cleanly in the browser:
-
Mid/Side & Spectral Vocal Isolation: In stereo mixes where lead vocals are panned dead center (
L ≈ R), phase-inverting the right channel (M = (L+R)/2,S = (L-R)/2) and applying band-limited spectral masking separates center vocals from stereo instrumentation. You can test this directly in the browser Vocal Remover and Stem Splitter. -
Time-Domain Pitch-Preserving Speed Adjustment: Reviewing long lectures or interviews at
1.25×or1.5×speed without the "chipmunk" pitch shift uses WSOLA (Waveform Similarity Overlap-Add) windowing, available in the Audio Speed and Pitch Changer. -
ID3v2 Tag Inspection & Cover Art Embedding: MP3 files store episode titles, artist strings, and
APICcover art frames in an ID3v2 header at byte offset0, using 7-bit synchsafe integers ((b0 << 21) | (b1 << 14) | (b2 << 7) | b3) for frame sizing. The Audio Tags and ID3 Editor reads and rewrites these headers in place without re-encoding the MPEG audio frames. -
Zero-Install Screen & System Audio Recording: When recording product walkthroughs or remote calls,
navigator.mediaDevices.getDisplayMediacombined withMediaRecordercaptures both screen video and tab/system audio directly to a local WebM or MP4 blob—used in the Sunasty Browser Screen Recorder.