Audio conversion is one of the most common file tasks: a podcast editor needs WAV from a voice memo, a game dev needs OGG from MP3, a music fan wants FLAC from M4A. Each format makes different trade-offs between file size, quality, and compatibility — and converting between them correctly takes more than just renaming an extension.
Audio format quick reference
| Format | Type | Typical use | Avg. size (3 min song) |
|---|---|---|---|
| MP3 | Lossy | Universal compatibility, streaming | 3–8 MB |
| AAC / M4A | Lossy | Apple ecosystem, YouTube, better quality/size than MP3 | 3–6 MB |
| OGG (Vorbis) | Lossy | Web browsers, games (royalty-free) | 3–6 MB |
| WAV | Lossless (PCM) | Studio editing, no compression | 30–50 MB |
| FLAC | Lossless | Archiving, audiophile playback | 15–30 MB |
| AIFF | Lossless (PCM) | macOS / Pro Tools editing | 30–50 MB |
| OPUS | Lossy | VoIP, Discord, low-bitrate streaming | 1–3 MB |
Key rule: converting lossy → lossy (MP3 → OGG) re-encodes and adds generation loss. Always archive from the highest-quality source available.
Converting with ffmpeg (CLI)
ffmpeg is the Swiss army knife of media conversion. Install it once and use it everywhere.
# MP3 → WAV (lossless PCM)
ffmpeg -i input.mp3 output.wav
# WAV → MP3 at 192 kbps
ffmpeg -i input.wav -b:a 192k output.mp3
# FLAC → AAC at 256 kbps
ffmpeg -i input.flac -c:a aac -b:a 256k output.m4a
# MP4 / video → extract audio as MP3
ffmpeg -i input.mp4 -vn -b:a 192k output.mp3
# Batch: convert all FLAC in a folder to MP3
for f in *.flac; do ffmpeg -i "$f" -b:a 256k "${f%.flac}.mp3"; done
Flag cheat sheet:
| Flag | Meaning |
|---|---|
-i |
Input file |
-vn |
No video (audio only) |
-c:a aac |
Audio codec (aac / libmp3lame / flac / pcm_s16le) |
-b:a 192k |
Audio bitrate |
-ar 44100 |
Sample rate |
-ac 2 |
Channel count (2 = stereo) |
JavaScript — browser (ffmpeg.wasm)
Convert audio entirely in the browser with no server upload using the @ffmpeg/ffmpeg package.
import { FFmpeg } from "@ffmpeg/ffmpeg";
import { fetchFile, toBlobURL } from "@ffmpeg/util";
const CORE = "https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd";
async function convertAudio(
file: File,
targetFormat: "mp3" | "wav" | "ogg" | "m4a" | "aac"
): Promise<Blob> {
const ffmpeg = new FFmpeg();
await ffmpeg.load({
coreURL: await toBlobURL(`${CORE}/ffmpeg-core.js`, "text/javascript"),
wasmURL: await toBlobURL(`${CORE}/ffmpeg-core.wasm`, "application/wasm"),
});
const ext = file.name.split(".").pop() ?? "mp3";
const inputName = `input.${ext}`;
const outputName = `output.${targetFormat}`;
await ffmpeg.writeFile(inputName, await fetchFile(file));
await ffmpeg.exec(["-i", inputName, "-b:a", "192k", outputName]);
const data = await ffmpeg.readFile(outputName);
const mimeMap: Record<string, string> = {
mp3: "audio/mpeg",
wav: "audio/wav",
ogg: "audio/ogg",
m4a: "audio/mp4",
aac: "audio/aac",
};
return new Blob([data], { type: mimeMap[targetFormat] });
}
// Usage
const input = document.querySelector<HTMLInputElement>("#file")!.files![0];
const blob = await convertAudio(input, "mp3");
const url = URL.createObjectURL(blob);
Note: The first load downloads ~30 MB of WASM. Cache it with a service worker for repeat visits. ffmpeg.wasm is single-threaded in most browsers; large files (>100 MB) will block the UI — use a Web Worker.
JavaScript — Node.js (fluent-ffmpeg)
For server-side conversion, fluent-ffmpeg wraps the system ffmpeg binary:
import ffmpeg from "fluent-ffmpeg";
function convertAudio(inputPath, outputPath, bitrate = "192k") {
return new Promise((resolve, reject) => {
ffmpeg(inputPath)
.noVideo()
.audioBitrate(bitrate)
.save(outputPath)
.on("end", resolve)
.on("error", reject);
});
}
// MP3 → WAV
await convertAudio("input.mp3", "output.wav");
// FLAC → AAC
await convertAudio("input.flac", "output.m4a", "256k");
Python (pydub + ffmpeg)
pydub provides a high-level API over ffmpeg. Install with pip install pydub (ffmpeg must be on your PATH).
from pydub import AudioSegment
import os
def convert_audio(
input_path: str,
output_path: str,
bitrate: str = "192k"
) -> None:
"""Convert any audio format to any other using pydub + ffmpeg."""
ext = os.path.splitext(output_path)[1].lstrip(".")
audio = AudioSegment.from_file(input_path) # auto-detects input format
audio.export(output_path, format=ext, bitrate=bitrate)
# Examples
convert_audio("input.mp3", "output.wav") # WAV is lossless — ignore bitrate
convert_audio("input.flac", "output.mp3", "320k") # FLAC → MP3 at 320 kbps
convert_audio("input.m4a", "output.ogg", "160k") # M4A → OGG
# Batch conversion: all WAV files → MP3
from pathlib import Path
for wav in Path(".").glob("*.wav"):
convert_audio(str(wav), str(wav.with_suffix(".mp3")))
Tip: For WAV output, skip the bitrate argument — WAV is PCM and uncompressed bitrate is determined by sample rate × bit depth × channels.
Go (exec wrapper)
Go's standard library has no audio codec support; the idiomatic approach is to shell out to ffmpeg:
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)
// ConvertAudio converts src to dst using ffmpeg.
// targetFormat: "mp3", "wav", "ogg", "m4a", "aac", etc.
// bitrate: "192k", "256k", "320k" (ignored for lossless WAV/FLAC)
func ConvertAudio(src, dst, bitrate string) error {
args := []string{"-y", "-i", src}
ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(dst), "."))
switch ext {
case "wav":
args = append(args, "-c:a", "pcm_s16le") // lossless PCM
case "flac":
args = append(args, "-c:a", "flac")
case "mp3":
args = append(args, "-c:a", "libmp3lame", "-b:a", bitrate)
default:
args = append(args, "-b:a", bitrate) // aac / ogg / m4a
}
args = append(args, "-vn", dst) // -vn: strip video tracks
cmd := exec.Command("ffmpeg", args...)
cmd.Stderr = os.Stderr
return cmd.Run()
}
func main() {
if err := ConvertAudio("input.flac", "output.mp3", "256k"); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println("Done")
}
PHP (exec + ffmpeg)
<?php
function convertAudio(
string $inputPath,
string $outputPath,
string $bitrate = '192k'
): bool {
// Validate paths — never pass unsanitised user input to exec()
foreach ([$inputPath, $outputPath] as $p) {
if (!preg_match('/^[\w\-\/\\.]+$/', $p)) {
throw new \InvalidArgumentException("Unsafe path: $p");
}
}
$ext = strtolower(pathinfo($outputPath, PATHINFO_EXTENSION));
$codecArgs = match ($ext) {
'wav' => '-c:a pcm_s16le',
'flac' => '-c:a flac',
'mp3' => "-c:a libmp3lame -b:a {$bitrate}",
default => "-b:a {$bitrate}", // aac, ogg, m4a
};
$cmd = sprintf(
'ffmpeg -y -i %s -vn %s %s 2>&1',
escapeshellarg($inputPath),
$codecArgs,
escapeshellarg($outputPath)
);
exec($cmd, $output, $returnCode);
return $returnCode === 0;
}
// Examples
convertAudio('input.mp3', 'output.wav');
convertAudio('input.flac', 'output.mp3', '320k');
Quick reference
| Task | ffmpeg command | Library |
|---|---|---|
| MP3 → WAV | ffmpeg -i in.mp3 out.wav |
pydub / fluent-ffmpeg |
| WAV → MP3 | ffmpeg -i in.wav -b:a 192k out.mp3 |
pydub / fluent-ffmpeg |
| FLAC → AAC | ffmpeg -i in.flac -c:a aac -b:a 256k out.m4a |
pydub |
| Video → MP3 | ffmpeg -i in.mp4 -vn -b:a 192k out.mp3 |
fluent-ffmpeg |
| OGG → M4A | ffmpeg -i in.ogg -b:a 192k out.m4a |
pydub |
| Browser convert | — | ffmpeg.wasm |
| Batch (shell) | for f in *.flac; do ... |
glob + pydub |
6 common mistakes
1. Renaming the file extension instead of converting
mv song.mp3 song.wav does not convert anything — the bytes stay as MP3. Always use a proper encoder.
2. Lossy → lossy re-encoding Going MP3 → OGG → MP3 compounds quality loss each time. Keep your master as WAV or FLAC and encode to lossy only for distribution.
3. Using the wrong codec for WAV
WAV is a container — it can hold compressed audio. Specify -c:a pcm_s16le to guarantee standard uncompressed PCM that every player reads.
4. Forgetting -vn when extracting audio from video
Without -vn, ffmpeg copies the video stream and fails if the output container (MP3) doesn't support it.
5. Setting a bitrate for lossless output
-b:a 320k is meaningless for WAV or FLAC — they encode all audio data regardless. Omit -b:a for lossless targets.
6. Passing unsanitised user input to exec()
If your PHP or Node.js code wraps ffmpeg, always validate or escapeshellarg() file paths. Arbitrary command injection via file names is a real attack vector.
Frequently asked questions
What is the best audio format for quality? For lossless archival: FLAC (smaller than WAV, same quality). For streaming at a given bitrate: AAC at 256 kbps edges out MP3; OPUS at 128 kbps beats both.
Does converting MP3 to WAV improve quality? No. WAV is lossless, but the quality ceiling is already set by the MP3 source. You get a larger file with the same audible quality — useful for editing in a DAW that requires PCM input, not for fidelity.
What bitrate should I use for MP3? 128 kbps for voice/podcasts, 192 kbps for music streaming, 320 kbps for archival/maximum quality. Most listeners cannot distinguish 192 kbps from 320 kbps in a blind test.
Can I convert a video file to audio?
Yes. ffmpeg -vn strips the video stream and outputs only audio. ffmpeg -i input.mp4 -vn -b:a 192k output.mp3 works for any video container.
Why does OGG sound different from MP3 at the same bitrate? OGG Vorbis and MP3 use different psychoacoustic models. Vorbis is generally considered more efficient at low bitrates (below 128 kbps); the difference is minor at 192 kbps+.
Is it legal to convert M4A/AAC files I downloaded? Converting files you own for personal use is generally legal in most jurisdictions. Distribution or stripping DRM (FairPlay-protected iTunes purchases) is a different matter — check local law and the content's licence.