Every time you re-encode an MP3 it loses quality. Lossy compression applied twice produces worse results than lossy compression applied once. The solution is to cut at frame boundaries without decoding and re-encoding.
The MP3 frame structure
MP3 files are not continuous streams. They are sequences of independent frames, each containing a header and compressed audio data. Each frame represents a fixed duration of audio:
- MPEG1 Layer 3: 1152 samples per frame
- At 44.1 kHz: each frame = 26.12 milliseconds
This means you can cut an MP3 at frame boundaries without decoding. Cutting between frames preserves the compression of each frame perfectly. Cutting within a frame requires decoding the frame, truncating the samples, and re-encoding.
Lossless cutting
The term "lossless" in audio cutting means no quality degradation. For MP3, this is achieved by cutting only at frame boundaries.
Frame 1: 0-26ms
Frame 2: 26-52ms
Frame 3: 52-78ms
...
If you want to cut at exactly 50ms, you have two choices:
- Cut at 26ms (frame boundary, lossless)
- Cut at 52ms (frame boundary, lossless)
- Cut at 50ms (requires decoding frame 2, re-encoding, quality loss)
For most use cases, cutting at the nearest frame boundary (within 26ms accuracy) is imperceptible. The human ear cannot distinguish a 26ms timing difference in a cut point.
Common use cases
Ringtone creation. Cut a 30-second clip from a song. The optimal approach: find the frame nearest to your desired start and end points, copy all frames between them.
Podcast editing. Remove dead air, ums, or mistakes. Frame-accurate cuts are more than sufficient for speech editing.
Sample creation. Extract a drum loop or vocal sample from a track. Here, precise start/end points matter more, and you may need sample-accurate (not frame-accurate) cuts, which require decode/re-encode.
The Web Audio approach
In a browser, cutting audio involves decoding the file, slicing the audio buffer, and encoding the result:
async function cutAudio(file, startSec, endSec) {
const ctx = new AudioContext();
const buffer = await file.arrayBuffer();
const audioBuffer = await ctx.decodeAudioData(buffer);
const startSample = Math.floor(startSec * audioBuffer.sampleRate);
const endSample = Math.floor(endSec * audioBuffer.sampleRate);
const length = endSample - startSample;
const newBuffer = ctx.createBuffer(
audioBuffer.numberOfChannels,
length,
audioBuffer.sampleRate
);
for (let ch = 0; ch < audioBuffer.numberOfChannels; ch++) {
const oldData = audioBuffer.getChannelData(ch);
const newData = newBuffer.getChannelData(ch);
for (let i = 0; i < length; i++) {
newData[i] = oldData[startSample + i];
}
}
return newBuffer;
}
For cutting MP3 files with visual waveform display and precise selection, I built a cutter at zovo.one/free-tools/mp3-cutter. It runs entirely in the browser, shows the waveform for visual cutting, and exports the result without uploading your audio to any server.
I'm Michael Lip. I build free developer tools at zovo.one. 500+ tools, all private, all free.
Top comments (0)