a9a / Docs

Batch Processing and Automation

Processing many files at once, reusing a procedure with a TOML job file, and building post-export checks and CI quality gates on probe --json.

How to approach it

When processing large numbers of files, it helps to build up in this order.

  1. Dial in one file in the GUI and confirm the result by eye and by ear
  2. Write the same settings out as --op arguments to a9a_cli render and confirm they reproduce on that one file
  3. Widen to many files with --out-dir
  4. Once the procedure is settled, move it into a job file and pass only the inputs and outputs on the command line

The GUI and the CLI share the same processing engine, so what you confirmed in the GUI reproduces exactly in the CLI.

Processing many files at once

Pass multiple inputs and specify the destination with --out-dir. --out cannot be used with multiple inputs.

a9a_cli render assets/*.wav \
  --out-dir build/mastered \
  --same-format \
  --force \
  --op loudness target=-16LUFS max-true-peak=-1dBTP limiter=on

--op arguments are applied in the order given. When the order changes the result — in particular when trim is involved — see the notes in Editing Waveforms.

Add --no-indicator to disable the progress display, or -q / --quiet to suppress warnings and progress output as well. For automated runs that keep logs, --no-indicator makes the output far easier to read.

Moving the procedure into a job file

Collect the processing settings in a TOML file and load it with render --job <JOB>. Keeping one job file per output spec puts the procedure itself under version control.

input = ["../audio/a.wav", "../audio/b.wav"]
out_dir = "../out"
same_format = true
force = true
comment = ["ARTIST=a9a", "ALBUM=demo"]

[[op]]
type = "gain"
db = -3

[[op]]
type = "fade_in"
duration = "100ms"
curve = "equal_power"

[[op]]
type = "loop"
start = 12000
end = 96000
a9a_cli render --job jobs/render.toml

The full key list is in the CLI Reference.

Combining a job file with command-line arguments

Keep the shared processing in the job file and pass the per-job details on the command line.

a9a_cli render \
  --job jobs/master.toml \
  --out-dir build/mastered \
  --comment REVISION=2026-03-20

When both are given, they resolve as follows.

ItemResolution
OperationsCLI --op entries are appended after the job file’s [[op]] entries
inputThe CLI value wins
output / out_dirThe CLI value wins
Output overrides such as sample_rateThe CLI value wins, field by field
force / same_formattrue when either side is true
commentCLI --comment values are appended to the job file array

Checking a set of files

Adding --json to probe prints the measurements to stdout in a machine-readable form, suitable for bulk checks and CI asset quality gates.

a9a_cli probe assets/*.wav --json | jq '.[0].integrated_lufs'

The top level is always an array, even for a single input, with entries in the order the inputs were given.

[
  {
    "file": "input.wav",
    "format": "wav",
    "sample_rate_hz": 48000,
    "bit_depth": 16,
    "channels": 2,
    "duration_seconds": 1.0,
    "integrated_lufs": -14.0,
    "true_peak_dbtp": -1.0,
    "channel_true_peaks_dbtp": [-1.0, -1.0],
    "loudness_range_lu": null,
    "crest_factor_db": 3.0,
    "channel_crest_factor_db": [3.0, 3.0],
    "aac": null,
    "loop": {"start": 4, "end": 12, "length": 8},
    "tags": [{"key": "ARTIST", "value": "a9a"}],
    "warnings": []
  }
]

Values that cannot be measured (±inf / NaN, for example on silence) are null. The field definitions are in the CLI Reference.

How failures are reported

If some files fail to decode or measure, the remaining inputs are still processed. Failed files appear in the same array in this form, and the exit code is non-zero when at least one input failed.

{"file": "broken.wav", "error": "Failed to decode ..."}

Successful entries never include an error key, so its presence tells you which inputs failed.

Quality gate examples

Confirm that every file decoded and that its integrated loudness is within ±1 LU of the target. When the condition fails, jq -e exits non-zero, so this works as a CI step as is.

set -o pipefail
a9a_cli probe assets/*.wav --json \
  | jq -e 'all(.[]; .error == null and .integrated_lufs >= -17 and .integrated_lufs <= -15)'

Detect mixed sample rates or channel counts across a set:

set -o pipefail
a9a_cli probe assets/*.wav --json \
  | jq -e 'all(.[]; .error == null)
           and ([.[] | {sample_rate_hz, channels}] | unique | length == 1)'

This page was written with generative AI.