A complete, breaking rewrite. AudioCLI is repositioned from "ML data-prep tool" to "scriptable, batch-capable, cross-platform audio power-tool that runs DAW-quality effects from the command line." The engine is now Spotify's pedalboard; the v1 ML-flavored stack is gone.
Migration from v1
- Package rename:
AudioCLI(capital A) →audiocli(lowercase). Update imports:from AudioCLI.modules.core.process import ...→from audiocli import run_per_file, AudioBuffer, op, .... - Command flattening: the
process/target/downloadgrouping is gone. Every command is now top-level.process resample 44100→audiocli resample --target … --sr 44100process mono -o→audiocli mono --target … -o(per-op flag)target set <path>→ in REPL:set targets <path>; in one-shot:--target <path>target output <path>→ in REPL:set output <path>; in one-shot:--output <path>process file script.acli→audiocli run-script script.acliprocess hook script.py→audiocli hook script.py --target …
phasefliprenamed topolarity.- v1
.acliscripts will not run on v2 — the command grammar changed. Update each line to the new shape. pip install audioclinow works on Windows (v1'sreadlineinstall requirement broke Windows entirely).
Removed
- Torch and torchaudio entirely. The default install no longer pulls torch. The
--ptsave flag is gone. ML-flavored ops (RandPool, the noise-injection primitives) are removed; the new plugin system is the path forward for users who want them. librosa,scipy,aeiou,tqdm,termcolor,readline,icli,einops,pdocare no longer dependencies.download httprecursive scrape — punted to a possible futureaudiocli-plugin-http-scrapeplugin.process fileunreachable command path — its loader-confusion bug is fixed by the newrun-scriptcommand.one_shot_argsglobal state — replaced by explicit per-callrun_per_file(...)parameters.
Added — engine
pedalboardis the engine. All effects (Compressor,Limiter,HighpassFilter,LowpassFilter,Reverb,Delay,Chorus,Phaser,Distortion,Bitcrush,PitchShift, libsamplerateResample), file I/O (WAV/FLAC/MP3/OGG/AAC viapedalboard.io.AudioFile), and VST3/AU hosting come from pedalboard's pre-built wheels. No systemffmpeg/soxrequired on any platform.AudioBuffervalue type:(channels, samples) float32ndarray +sr+subtype+ optionalformat/qualityoverrides. Same shape pedalboard expects, so passing buffers in/out of pedalboard chains is a noop.
Added — pipeline
- Real parallelism. Single
ThreadPoolExecutor, all files submitted at once, results consumed viaas_completedso worker exceptions surface asResult(ok=False, error=…)instead of vanishing into an unconsumed iterator (the v1 batcher's bug). - Per-file
Result+ end-of-jobJobReportwithok_count,failed_count,failures,duration_s,exit_code(capped at 255). - Cancellation.
run_per_fileaccepts acancel_token: threading.Event; flipping it from any thread skips pending submissions, lets in-flight files finish, and returns a partialJobReport. - Bulletproof: a job over 1000 files where 50 are deliberately broken finishes, reports 950 successes + 50 failures with reasons, exits non-zero, and never hangs.
- Modular execution seams. Event dispatch, output path resolution, worker-count policy, and CLI rendering live in focused modules so the pipeline remains library-only execution code.
Added — CLI
- Flat command namespace. Every op lives at the top level (
audiocli normalize, notaudiocli process normalize). - Consistent flags across every op:
--target(repeatable),--output,--workers N,--recursive/--no-recursive,-o,--json. - Lazy imports:
audiocli --helpreturns in ~120 ms.numpy/pedalboard/pyloudnorm/platformdirs/click_repl/prompt_toolkit/richonly load when an op actually runs. - Cross-platform REPL via
click-repl+prompt_toolkit(replaces v1's Unix-onlyreadline-based shell). Same Typer app powers REPL and one-shot; commands are identical.;chaining preserved. History persists across sessions. --jsonevent mode: every op emits newline-delimited{"type": "start" | "progress" | "file_done" | "error" | "done", ...}events on stdout. Same protocol the eventual desktop app uses to subscribe viaon_event=.- Persistent settings at the
platformdirsuser-config location (XDG on Linux,~/Library/Application Support/AudioCLIon macOS,%APPDATA%\AudioCLIon Windows), JSON with a"schema": 1field for future migrations. - First-party command modules. Special-case commands (
info,remove-silent,chunk) are direct Typer commands, with reusable analysis, silence detection, and chunking logic split into focused library modules.
Added — first-party ops
| Group | Ops |
|---|---|
| Levels & channels | gain, normalize (peak / LUFS), polarity, mono, stereo |
| Time-domain | resample, pitch, trim, fade (linear / exp / cosine) |
| Format | convert (wav/flac/mp3/ogg), bitdepth (8/16/24/32) |
| Effects (pedalboard) | compress, limit, highpass, lowpass, reverb, delay, chorus, phaser, distortion, bitcrush |
| VST/AU | vst --plugin-path … --param key=value |
| Special-case (analysis / side-effect / multi-output) | info, remove-silent, chunk |
| Power-user | hook, run-script, shell |
Added — plugin system
- Public
@opdecorator (from audiocli import op, AudioBuffer). First-party ops use the same decorator plugin authors do. - Discovery via Python entry-points:
[project.entry-points."audiocli.ops"] my-op = "my_pkg:my_func". AudioCLI loads them viaimportlib.metadata.entry_points(group="audiocli.ops")at startup. - Conflict resolution: first-party wins on name collision; the conflict is logged at startup. Malformed plugin signatures raise
PluginErrorat registration time. - Plugin contract is filter-shape only in v2.0 (
(buf: AudioBuffer, **params) -> AudioBuffer). The contract may broaden in v2.1+.
Added — library API
- Public surface:
from audiocli import run_per_file, run_one, list_ops, get_op, AudioBuffer, op, Op, OpInfo, ParamInfo, AudioCLIError, LoadError, SaveError, OpError, PluginError, ConfigError. list_ops()returns frozen dataclasses describing every registered op (name, help, kind, params with type/default/help/required) — designed to drive dynamic UI in a future desktop frontend.- No
print()/sys.exit()in any library module. The CLI layer renders; the library raises.
Added — error model
Public exception hierarchy:
AudioCLIError
├── LoadError
├── SaveError
├── OpError
├── PluginError
└── ConfigError
Tooling
- Ruff lints + formats.
ruff check . && ruff format --check .is the full lint pass. - 313 tests covering every op (round-trip + correctness + negative), the pipeline (50-files-with-3-corrupt isolation, cancellation mid-batch, no-thread-leak), the CLI (every command's
--help,--jsonJSON parseability, exit codes), the REPL (;-chaining, history,setpersistence), the plugin loader (entry-point discovery, conflict resolution, malformed signature →PluginError), and the library API (list_ops()shape,run_per_filecallable surface). - CI matrix:
{ubuntu, macos, windows}-latest × {3.10, 3.11, 3.12}runningruff check,ruff format --check,pytest --cov.