Skip to main content

Convert raw MP3 audio into pretraining-ready WAV/FLAC, with validation and dataset manifest output.

Project description

audio-prep

CI PyPI Python 3.10+ License: MIT Code style: Ruff Type checked: mypy Requires FFmpeg

Convert source audio into pretraining-ready WAV/FLAC, with validation and a dataset manifest as the handoff artifact to the pretraining pipeline. Discovery supports common FFmpeg-readable audio/video containers by default and can scan all files with --extensions all.

Defaults: 16 kHz, mono — the standard input spec for Wav2Vec2 / XLS-R style self-supervised speech pretraining. Override via CLI flags if a different downstream model needs something else.

Repo layout

audio-prep-pipeline/
├── src/audio_prep/
│   ├── config.py        # ConversionConfig: target spec + behavior knobs
│   ├── converter.py      # find_audio_files, convert_file, convert_batch
│   ├── validator.py      # probe_duration, validate_output
│   ├── chunker.py         # (optional) ChunkConfig, chunk_file, chunk_batch -- Silero VAD speech chunking
│   ├── manifest.py       # build_manifest, write_manifest (JSONL output)
│   ├── exceptions.py     # ConversionError, ProbeError, ChunkingError
│   └── cli.py             # `audio-prep convert ...` / `audio-prep chunk ...` entry point
├── tests/
│   ├── conftest.py        # synthetic-audio fixtures (no binary files checked in)
│   ├── test_converter.py
│   ├── test_validator.py
│   ├── test_chunker.py    # fake-detector tests, no real VAD model needed
│   ├── test_manifest.py
│   └── test_cli.py
├── .github/workflows/ci.yml   # lint + typecheck + test matrix (3.10-3.12)
├── .pre-commit-config.yaml     # ruff + mypy + basic hygiene hooks, runs on every commit
├── pyproject.toml               # deps, ruff config, mypy config, pytest config
└── Makefile                      # `make check` runs everything CI runs, locally

Commands

audio-prep has two independent subcommands. Neither depends on the other running first -- both scan --input-dir for supported source files directly.

  • audio-prep convert - conversion only: ffmpeg resample/remix/re-encode into the target WAV/FLAC spec, then validation, then an optional manifest. Does not chunk.
  • audio-prep chunk - chunking only: Silero VAD speech chunking straight from source audio, with its own resample/format/manifest options. Does not convert or validate.

convert pipeline stages

  1. discovery (find_audio_files) — recursively find supported source files under an input directory.
  2. conversion (convert_file / convert_batch) — shell out to ffmpeg to resample/remix/re-encode into the target WAV/FLAC spec. Mirrors the input directory's subfolder structure on output. Runs in a process pool since each conversion is an independent subprocess call.
  3. validation (validate_output) — re-opens each converted file with soundfile and checks it actually matches the requested sample rate, channel count, and minimum duration. This catches the case where ffmpeg exits 0 but silently produced something degenerate.
  4. manifest (build_manifest / write_manifest) — JSONL file, one row per source file, recording status (ok / conversion_failed / validation_failed), output path, duration, sample rate, and any error.

Conversion failures don't abort the batch — a bad file in a 50,000-file corpus shows up as one conversion_failed row in the manifest, not a crashed job three hours in.

chunk pipeline stages

  1. discovery (find_audio_files) — recursively find supported source files under an input directory.
  2. chunking (chunk_file / chunk_batch) — runs Silero VAD over each file (decoding/resampling via ffmpeg) and splits it into speech-only chunks bounded by a [min, max] duration window, so silence-heavy source recordings don't waste pretraining compute. Needs the chunking extra (torch + silero-vad, see Setup below).
  3. manifest (build_chunk_manifest / write_manifest), optional — JSONL file, one row per source file, recording status (ok / chunking_failed), chunk count, and chunk paths.

Chunking failures (e.g. no speech detected) work the same way: chunk_batch returns a ChunkResult per file instead of raising.

Setup

# ffmpeg is a system dependency, not a pip package
sudo apt-get install ffmpeg   # or: brew install ffmpeg

pip install audio-prep-pipeline

Install directly from GitHub:

pip install "audio-prep-pipeline @ git+https://github.com/nattkorat/audio-prep-pipeline.git"

For local development:

make install   # pip install -e ".[dev]" + pre-commit install

Chunking needs an extra install -- make install alone does not pull in torch/silero-vad/tqdm, since convert doesn't need them:

pip install -e ".[chunking]"

Or from GitHub:

pip install "audio-prep-pipeline[chunking] @ git+https://github.com/nattkorat/audio-prep-pipeline.git"

The first chunk run needs either the silero-vad pip package installed, or network access so torch.hub can download snakers4/silero-vad once (it's then cached under ~/.cache/torch/hub). If neither is available, pass --allow-energy-fallback to use a lower-quality offline detector instead of failing.

Usage

audio-prep convert

audio-prep convert \
    --input-dir data/raw_mp3 \
    --output-dir data/wav16k \
    --format wav \
    --sample-rate 16000 \
    --workers 8 \
    --manifest data/manifest.jsonl
Flag Default Meaning
--input-dir (required) directory of source audio files
--output-dir (required) where converted output is written
--extensions common FFmpeg audio/video extensions comma-separated source extensions, or all to pass every regular file to FFmpeg
--format wav output format (wav or flac)
--sample-rate 16000 target sample rate
--channels 1 target channel count
--workers 4 parallel conversion workers
--min-duration-sec 0.5 validation fails files shorter than this
--overwrite off re-convert even if output already exists and passes validation
--normalize-loudness off apply EBU R128 loudness normalization (-23 LUFS)
--manifest none path to write a JSONL manifest

audio-prep chunk

Independent of convert -- scans --input-dir for supported source files and runs VAD chunking directly against them, decoding (and resampling, if --sample-rate doesn't match the source) via ffmpeg:

audio-prep chunk \
    --input-dir data/raw_mp3 \
    --output-dir data/chunks \
    --sample-rate 16000 \
    --format flac \
    --min-duration-sec 5 \
    --max-duration-sec 20 \
    --workers 4 \
    --manifest data/chunk_manifest.jsonl
Flag Default Meaning
--input-dir (required) directory of source audio files to scan and chunk
--output-dir <input-dir>/chunks where chunks are written
--extensions common FFmpeg audio/video extensions comma-separated source extensions, or all to pass every regular file to FFmpeg
--format wav output chunk format (wav or flac)
--sample-rate 16000 resample (via ffmpeg) to this rate before chunking if the source doesn't already match it
--min-duration-sec 5.0 drop chunks shorter than this
--max-duration-sec 20.0 split longer speech into windows this size
--workers 4 parallel chunking workers
--overwrite off re-chunk even if valid output already exists
--allow-energy-fallback off fall back to a low-quality energy detector if Silero can't load, instead of raising
--manifest none path to write a JSONL chunk manifest (source file, status, chunk count/paths)

chunk_file itself doesn't care about source extension -- it decodes whatever path it's given -- so the Python API can also chunk an existing WAV/FLAC corpus (e.g. convert_batch output) by passing source_files explicitly instead of relying on chunk_batch discovery:

from audio_prep import ConversionConfig, convert_batch, build_manifest, validate_output
from audio_prep import ChunkConfig, chunk_batch

config = ConversionConfig(output_format="wav", sample_rate=16_000, channels=1, num_workers=8)
results = convert_batch("data/raw_mp3", "data/wav16k", config)
validations = {r.output: validate_output(r.output, config) for r in results if r.success}
records = build_manifest(results, validations)

# source_files bypasses chunk_batch discovery, so this works
# directly against the already-converted WAV output above.
valid_outputs = [path for path, v in validations.items() if v.valid]
chunk_config = ChunkConfig(min_duration_sec=5, max_duration_sec=20, num_workers=4)
chunk_results = chunk_batch("data/wav16k", "data/wav16k/chunks", chunk_config, source_files=valid_outputs)

Development workflow

make format     # ruff format + autofix
make lint       # ruff check
make typecheck  # mypy --strict
make test       # pytest with coverage
make check      # all of the above -- run this before opening a PR

pre-commit (installed via make install) runs ruff + mypy + basic hygiene checks automatically on every commit. CI (.github/workflows/ci.yml) re-runs the same checks plus the full test matrix across Python 3.10–3.12 on every push and PR.

Extending this

Natural next additions, in roughly the order they'd come up:

  • Streaming manifest writes for very large corpora, instead of holding all ConversionResults in memory before writing.

Note: This is the template, that you have to extend from. Main branch is protected so you have to create another branch to work on.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

audio_prep_pipeline-0.1.0.tar.gz (36.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

audio_prep_pipeline-0.1.0-py3-none-any.whl (22.5 kB view details)

Uploaded Python 3

File details

Details for the file audio_prep_pipeline-0.1.0.tar.gz.

File metadata

  • Download URL: audio_prep_pipeline-0.1.0.tar.gz
  • Upload date:
  • Size: 36.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.4

File hashes

Hashes for audio_prep_pipeline-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1897c722b3302160d0e9a58c55ea3ff3e348aa63e5759d6478636822c3659f24
MD5 3fb558b415b25408a84a323725204c8e
BLAKE2b-256 8d560ca8dd43bd1c2ac47ab608fa09faecf8c43caa59d7be13085a8d3be06add

See more details on using hashes here.

File details

Details for the file audio_prep_pipeline-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for audio_prep_pipeline-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9e6ca07f17379433b0a05987c84014c29e50f2e0a85a67845fe8c47baa1d1373
MD5 e848dd447eb0b41d683007eddaf28b65
BLAKE2b-256 bf8092c397cc621e789d501a216636feadf16ce648490b8060479b952408722f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page