v0.1.0
audio mastering pipeline

mastacraf

A command-line mastering pipeline built in Rust, powered by FFmpeg. Designed for experimental electronic music where you define the standard.

Overview

mastacraf is a command-line audio mastering pipeline that applies a configurable processing chain to audio files. It is built for self-produced, self-mixed experimental electronic music — material where no commercial loudness standard applies and the goal is a repeatable, documented process you own entirely.

It does not make aesthetic decisions. You define the targets, the filters, and the dynamics behavior in a preset file. mastacraf applies them, measures the result, and writes a report documenting exactly what happened.

What it does

  • Measures integrated loudness (LUFS), true peak, loudness range, RMS, crest factor, dynamic range, DC offset, phase correlation, and spectral balance
  • Applies a configurable filter chain: high-pass, optional low-pass, optional compression, peak limiting, two-pass EBU R128 loudness normalization
  • Outputs all files for a track into a dedicated subfolder named after the track stem
  • Generates before/after spectrogram and waveform images
  • Writes a JSON report with all measurements, the exact FFmpeg filter string, and input/output delta

What it does not do

  • Make EQ decisions
  • Automatically match loudness to a reference track
  • Alter the stereo field, phase, or any aspect of the mix outside the defined chain

How it works

The two-pass loudnorm process #

The core of every master run is FFmpeg's loudnorm filter, run twice.

Pass 1 (analysis): The entire file is decoded and fed through loudnorm in measurement-only mode. No audio is written. The filter outputs a JSON block containing the file's measured integrated loudness, true peak, loudness range, and threshold.

Pass 2 (processing): The file is decoded again through the full filter chain. The loudnorm filter on this pass receives the pass-1 measurements and applies a linear gain adjustment. Because it has full-file measurements, it can apply a precise, artifact-free gain change.

Single-pass loudnorm will miss the target
Single-pass loudnorm estimates in real time and will overshoot or undershoot depending on the material's dynamic structure. Two-pass always hits the target.

Extended analysis passes #

After pass 1, three additional FFmpeg passes collect extended analysis data:

  • astats pass — RMS level, peak level (for crest factor), DC offset
  • aphasemeter pass — per-frame stereo phase correlation, averaged across the file; skipped for mono
  • Three band-energy passesvolumedetect on low/mid/high bandpass-filtered signal to compute spectral balance and centroid estimate

All extended passes are non-fatal. If any fail, the core analysis still completes.

Filter chain execution #

The filter chain is a comma-separated FFmpeg -af string built by build_filter_chain() in src/pipeline/process.rs. Disabled stages are omitted entirely — they do not exist in the graph and have zero overhead.

Installation

Prerequisites #

FFmpeg must be in your $PATH, or placed alongside the compiled mastacraf binary.

bash — macOS
brew install ffmpeg rust
bash — Ubuntu / Debian
sudo apt install ffmpeg
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
bash — Arch
sudo pacman -S ffmpeg rust

Build from source #

bash
git clone <your-repo>
cd mastacraf
cargo build --release
# binary: ./target/release/mastacraf

Verify #

bash
mastacraf --version
mastacraf --help

Quick start

1
Analyze before mastering — always
bash
mastacraf analyze track.wav

This runs the full analysis suite and prints all measurements. No audio is written. Use this to understand the material before writing a preset.

2
Master with the default preset
bash
mastacraf master track.wav

Output goes to ./mastered/track/. You get the mastered file, before/after images, and a JSON report.

3
Use a named preset
bash
mastacraf master track.wav --preset noise
mastacraf master track.wav --preset film -o ./deliverables/
4
Test a preset without writing anything
bash
mastacraf master track.wav --preset mypreset --dry-run

CLI — master

Runs the full mastering pipeline: pre-analysis, pre-visualization, processing, post-analysis, post-visualization, report.

usage
mastacraf master <INPUT> [OPTIONS]
FlagDefaultDescription
-p, --presetdefaultPreset name. Searches ./presets/<n>.toml.
-o, --output./mastered/Base output directory. A subfolder named after the input stem is created inside it.
--suffix_masterAppended to input stem for the output filename.
--lufsfrom presetOverride integrated loudness target.
--true-peakfrom presetOverride true peak ceiling (dBTP).
--no-visualizeoffSkip spectrogram and waveform generation.
--dry-runoffAnalyze and print measurements. Write nothing.
-v, --verboseoffPrint every FFmpeg command as it runs.
examples
# Basic master, all defaults
mastacraf master track.wav

# Named preset, custom output directory
mastacraf master track.wav -p film -o ./deliverables/

# Override loudness without editing the preset
mastacraf master track.wav --lufs -14

# Verify what the pipeline would do without writing anything
mastacraf master track.wav -p noise --dry-run -v

# Custom filename suffix
mastacraf master track.wav --suffix _2024_v1
# → mastered/track/track_2024_v1.wav

CLI — analyze

Runs full analysis: loudnorm pass 1 + extended passes. Prints all measurements. No audio written.

usage
mastacraf analyze <INPUT> [OPTIONS]
FlagDefaultDescription
--visualizeoffGenerate spectrogram and waveform images.
-o, --output./Output directory for images (only with --visualize).
-v, --verboseoffPrint FFmpeg commands.
example output
  [analysis]
    integrated loudness        -18.4 LUFS
    true peak                  -1.2 dBTP
    loudness range             14.6 LU
    duration                   00:07:43
    format                     pcm_s24le / 44100 Hz / 2 ch

  [extended analysis]
    RMS level                  -22.1 dBFS
    crest factor               19.7 dB
    dynamic range (DR)         DR17
    DC offset                  0.00012
    phase correlation          0.741  (in phase / wide stereo)
    spectral balance           low 38%  mid 44%  high 18%
    spectral centroid          1420 Hz

CLI — presets / preset

List all presets

bash
mastacraf presets

Lists all presets found across all search directories. Prints name, LUFS target, true peak, LRA, and description.

Inspect a preset

bash
mastacraf preset noise
mastacraf preset film > mybase.toml   # copy as starting point

Preset search order

When you pass --preset name, mastacraf searches for name.toml in this order, using the first match:

  1. ./presets/ relative to your current working directory (use this)
  2. <exe_dir>/presets/ — next to the compiled binary (for bundled installs)
  3. ~/.config/mastacraf/presets/ (or platform equivalent)

Creating a preset

A preset is a TOML file that fully defines one mastering configuration. Every audio-affecting field maps directly to one FFmpeg filter parameter. There is no hidden processing between what you write in the file and what FFmpeg executes.

Always analyze before writing a preset
The measured LRA, crest factor, and phase correlation from mastacraf analyze are the inputs to rational preset decisions. Guessing preset values without measurement produces arbitrary results.
1
Analyze the material
bash
mastacraf analyze track.wav

Note every value. Specifically: integrated loudness (how far loudnorm will push gain), loudness range (your floor for target.lra), crest factor (determines limiter attack setting), phase correlation (negative or very low means a mix problem, not a mastering problem), spectral balance (informs whether highpass matters).

2
Decide where the file is going

This determines target.lufs and target.true_peak.

Destinationlufstrue_peakNotes
Streaming (general)-16.0-1.0Avoids all platform normalization
Film / broadcast-23.0-2.0EBU R128 — non-negotiable for delivery
Vinyl prep-12.0-0.3Cutting lathe adds its own limiting
DJ pool-9.0-0.5Club systems are loud
Personal / archive-18.0-1.0More headroom, full dynamics
3
Copy and edit a preset
bash
cp presets/default.toml presets/mypreset.toml

Open mypreset.toml and make these specific changes:

  1. Set [meta].name to match the filename stem
  2. Set [target].lufs from the destination table
  3. Set [target].true_peak from the destination table
  4. Set [target].lra to the measured LRA value or higher
  5. Set [compressor].enabled — default is false, keep it that way unless you have a specific reason
  6. Set [limiter].attack_ms based on measured crest factor: high crest (>18 dB) → 5–10ms; low crest (<10 dB) → 0.5–2ms
  7. Set [output].sample_rate to 48000 for film delivery, 44100 otherwise
4
Test without writing
bash
mastacraf master track.wav -p mypreset --dry-run
5
Run and check the report delta
bash
mastacraf master track.wav -p mypreset
cat mastered/track/track_master_report.json

Check the delta section. lra_change should be near zero or slightly positive. A large negative value (e.g. -4) means target.lra is set too low — loudnorm is compressing your dynamics. Raise it and re-run.

Preset field reference — [target]

These three values define the loudness signature of the master. They are all direct inputs to FFmpeg's loudnorm filter on pass 2.

lufs float default: -16.0
Integrated loudness target in LUFS. The loudnorm filter adjusts overall gain to hit this value. Lower = quieter master with more headroom. This does not affect relative dynamics within the track — it shifts the overall gain only.
Range: −32.0 to −9.0
true_peak float default: -1.0
Ceiling for intersample peaks in dBTP. Both the limiter and loudnorm enforce this. Intersample peaks occur when a DAC reconstructs the waveform between samples — a file where no sample exceeds 0 dBFS can still clip on playback. Never set this to 0.0.
Range: −3.0 to −0.1
lra float default: 11.0
Loudness range target in Loudness Units. This is the most important value to get right. The loudnorm filter attempts to fit the material's dynamic range into this target. If the material's natural LRA exceeds the target, loudnorm compresses dynamics to fit — silently, with no warning. Set at or above the value reported by mastacraf analyze.
If measured LRA is 14 LU and you set lra = 8, loudnorm compresses 6 LU of your dynamic range away. Check delta.lra_change in the report after every master run.
Range: 1.0 to 20.0

Preset field reference — [filters]

highpass_hz float default: 20.0
Removes all audio below this frequency. Content below 20 Hz is inaudible but contributes to loudnorm energy measurements and occupies limiter headroom. A 20 Hz highpass is transparent to all audible content. For abstract/noise music where sub-bass is compositional, lower to 12 Hz. Set to 0 to disable entirely.
Range: 0 (disabled) to 120.0
lowpass_hz float default: 0.0 (disabled)
Removes all audio above this frequency. Disabled by default. Only relevant for specific deliverable format requirements (broadcast frequency ceilings, telephone, AM radio). Do not enable without a specific reason.
Range: 0 (disabled) to 24000.0

Preset field reference — [compressor]

Disabled by default — intentionally
The compressor processes the stereo bus simultaneously. It responds to the sum of all elements and changes the relationship between them. If you mixed it, you already made those decisions. Enable only with a specific reason.
enabledbooldefault: false
Master switch. true or false.
threshold_dbfloatdefault: -18.0
Level above which compression engages. Everything below is untouched. Lower threshold = compressor engages on more material. For mastering wide-range material, −18 to −12 dB engages on louder sections only.
Range: −60.0 to 0.0
ratiofloatdefault: 2.0
Gain reduction above threshold. At 2:1, every 2 dB over threshold becomes 1 dB. For mastering: 1.5–2.0 is transparent. 4.0 is noticeable. Above 8.0 is limiting territory — use the limiter stage instead.
Range: 1.0 to 20.0
attack_msfloatdefault: 20.0
Response time when signal exceeds threshold. Fast attack (1–5ms) catches the initial transient. Slow attack (20–80ms) lets transients through before clamping. For material with intentionally designed transients, use slow attack to preserve the attack shape of sounds.
Range: 0.1 to 200.0 ms
release_msfloatdefault: 250.0
Recovery time after signal drops below threshold. Fast release can cause pumping on dense material. Slow release (250–500ms) is more transparent but can hold gain reduction into subsequent quiet passages.
Range: 10.0 to 2000.0 ms
makeup_dbfloatdefault: 0.0
Gain added after compression. The loudnorm stage normalizes the final level regardless — makeup gain here effectively shifts the operating threshold, not the output level.
Range: 0.0 to 24.0
knee_dbfloatdefault: 2.0
Width of the soft-knee transition around the threshold. 0 = hard knee (instant compression). 6 = soft knee (gradual onset). Soft knee sounds more natural on material without conventional dynamic structure.
Range: 0.0 to 12.0

Preset field reference — [limiter]

enabledbooldefault: true
Keep this on. The only reason to disable it is if you are certain your material never exceeds the true peak ceiling and want to skip the processing overhead — which is negligible.
attack_msfloatdefault: 5.0
Response time when a peak is detected. Use the measured crest factor to set this:
Crest factorattack_msWhy
> 18 dB5–10 msVery transient — preserve attack shape
10–18 dB2–5 msModerate — catch peaks without dulling
< 10 dB0.5–2 msDense / noise — catch everything
Range: 0.1 to 20.0 ms
release_msfloatdefault: 50.0
Recovery time after a peak. Short (20–50ms) recovers fast; on dense material with consecutive peaks, this can cause a pumping texture. Long (100–300ms) is smoother but can hold gain reduction into quieter passages.
Range: 10.0 to 500.0 ms

Preset field reference — [output]

formatstringdefault: "wav"
Output file format. For archival and delivery, use wav or flac. WAV is uncompressed. FLAC is lossless compressed. MP3/AAC only for platform-specific delivery when an uncompressed master already exists.
Values: wav · flac · mp3 · aac
bit_depthintegerdefault: 24
PCM bit depth. 24-bit is the mastering standard. 16-bit for CD replication only. 32-bit float for intermediates going into another DAW for further processing. Applies to wav and flac only.
Values: 16 · 24 · 32
sample_rateintegerdefault: 44100
Output sample rate in Hz. 44100 for music delivery. 48000 for anything going into a film or video pipeline. Do not upsample — setting this higher than the input creates a file with no additional information.
Common values: 44100 · 48000 · 96000

Preset field reference — [visualization]

FieldDefaultDescription
enabledtrueMaster switch. Overridden per-run by --no-visualize.
spectrogramtrueGenerate spectrogram image. Log-scale frequency axis.
waveformtrueGenerate waveform image. L/R channels split.
width1920Horizontal resolution in pixels. Increase for long pieces needing time resolution.
spectrogram_height512Vertical resolution. Increase to 768–1024 for dense spectral content.
waveform_height200Vertical resolution.

Analysis — core measurements (EBU R128)

Integrated loudness
LUFS
Average perceived loudness computed over all non-silent sections using ITU-R BS.1770. The number streaming platforms use for normalization. Use it to set target.lufs.
True peak
dBTP
Maximum intersample peak measured at 4× oversampling. Accounts for DAC reconstruction between samples. Use it to set target.true_peak.
Loudness range
LU
Statistical spread between quiet and loud sections (short-term loudness, 10th to 95th percentile). Set target.lra at or above this value.
Threshold
LUFS
Loudness gate used during measurement. Sections below this level are excluded from the integrated loudness calculation.

Analysis — extended measurements

RMS leveldBFS
Root mean square of the signal — average energy, not peak. More representative of perceived loudness than peak level. If RMS is −22 dBFS and true peak is −0.5 dBTP, the crest factor is 21.5 dB — very dynamic.
Crest factordB
Difference between true peak and RMS level. Measures the ratio of transient peaks to average energy. Use this to set limiter.attack_ms.
Crest factorCharacter
> 20 dBVery transient / dynamic
14–20 dBNormal range for mixed material
8–14 dBDense, compressed, or sustained
< 8 dBHeavily limited or noise-like
Dynamic range (DR)score
Approximate DR score derived from crest factor. DR14+ is considered excellent dynamics. DR8–13 moderate. Below DR8 indicates heavy limiting or compression already in the mix. Note: this is a crest-factor-based approximation, not a true block-based DR Meter computation.
DC offsetlinear
Constant bias in the signal at 0 Hz. Should be near zero. Values above ±0.001 indicate a DC problem: it consumes headroom, can cause clicks at cut points, and can cause the highpass filter to produce a click at the start of the file. Address at the mix or recording stage — the 20 Hz highpass will remove most DC offset in practice.
Phase correlation-1.0 to +1.0
Measures how correlated L and R channels are, averaged across the file. Phase correlation below 0.3 or negative is a mix problem — the master does not address it. Fix at the mix stage.
ValueMeaning
+0.8 to +1.0Strong mono compatibility
+0.3 to +0.8Normal stereo
0.0 to +0.3Wide stereo — check mono fold-down
-0.2 to 0.0Approaching out-of-phase
Below -0.2Significant phase problem
Spectral balance% per band
Approximate percentage of total energy in three bands: low (<250 Hz), mid (250 Hz–4 kHz), high (>4 kHz). A rough diagnostic — use it to understand general spectral character before making EQ decisions or choosing highpass settings.
Spectral centroidHz
Energy-weighted average frequency — the "center of mass" of the spectrum.
RangeCharacter
< 400 HzSub/bass-dominant
400–1500 HzLow-mid heavy
1500–3000 HzBalanced
3000–5000 HzBright / presence-heavy
> 5000 HzVery bright / high-frequency dominant

The filter chain

Structure and execution order #

highpass
lowpass (opt)
compressor (opt)
limiter
loudnorm

Built in src/pipeline/process.rs, function build_filter_chain(). Disabled stages are omitted entirely from the FFmpeg filter graph — they have zero overhead.

Example output #

ffmpeg -af value (default preset)
highpass=f=20.0,alimiter=level_in=1:level_out=0.891251:limit=0.891251:attack=5.0:release=50.0:asc=1,loudnorm=I=-16.0:TP=-1.0:LRA=11.0:measured_I=-18.43:measured_LRA=14.2:measured_TP=-2.1:measured_thresh=-28.7:offset=0.0:linear=true:print_format=summary

Viewing the chain used #

bash
mastacraf master track.wav -v              # prints during run
cat mastered/track/track_master_report.json  # stored in report

True peak conversion #

The limiter's level_out takes linear amplitude. The tool converts target.true_peak from dBTP automatically: linear = 10 ^ (true_peak / 20)

dBTPLinear
-0.50.944061
-1.00.891251
-2.00.794328
-3.00.707946

Output folder structure

Each master run creates a dedicated subfolder inside the base output directory, named after the input file stem.

file tree
mastered/
  track01/
    track01_master.wav

    track01_pre_spectrogram.png        individual pre-master spectrogram
    track01_pre_waveform.png           individual pre-master waveform
    track01_post_spectrogram.png       individual post-master spectrogram
    track01_post_waveform.png          individual post-master waveform

    track01_compare_spectrogram.png    pre (top) + post (bottom), amber separator
    track01_compare_waveform.png       pre (top) + post (bottom), amber separator
    track01_diff_spectrogram.png       contrast-amplified pixel difference
    track01_diff_waveform.png          contrast-amplified pixel difference

    track01_master_report.json
  track02/
    ...
The --suffix flag affects the audio filename only, not the folder name. --suffix _v2 produces mastered/track01/track01_v2.wav.

Visualization output

Six images per master run. Individual pre/post images are the raw material. Compare and diff images are the quick-review tools — open those first.

Individual visuals #

Spectrogram (_pre_spectrogram.png, _post_spectrogram.png)

FFmpeg: showspectrumpic=mode=combined:color=intensity:scale=log:legend=1 — X: time, Y: frequency (log scale), color: intensity. Legend on right edge.

Waveform (_pre_waveform.png, _post_waveform.png)

FFmpeg: showwavespic=split_channels=1 — X: time, Y: amplitude, L channel top, R channel below in distinct colors.

Stacked comparison (_compare_spectrogram.png, _compare_waveform.png) #

Pre on top, post below, amber 3px separator line between them. Both aligned on the same time axis — scan vertically to compare any moment.

filter complex
[pre][post] → pad pre bottom +3px amber → vstack

Stacked spectrogram: Frequency content and brightness should be similar top and bottom. A band brighter in the bottom (post) image gained energy at those frequencies. Non-uniform differences — some bands brighter, some darker — indicate the compressor or limiter shaped specific moments rather than applying clean linear gain.

Stacked waveform: Envelope shape should look similar. A noticeably different envelope in the post (bottom) image means target.lra is set too low and loudnorm is compressing dynamics to fit.

Diff image (_diff_spectrogram.png, _diff_waveform.png) #

Absolute pixel difference between pre and post, contrast-amplified and saturation-boosted. Black = no change. Bright = change.

filter complex
[pre][post] → blend=difference → curves (12% maps to 100%) → hue=s=6
Diff appearanceMeaning
Black / near-blackNo change. Pipeline made no audible difference here.
Bright uniform regionSignificant change — limiter peak, loudnorm gain, or compressor.
Yellow-tinted bright regionHigh-frequency energy increased at that moment.
Blue-tinted bright regionHigh-frequency energy decreased.
Isolated bright spikes (waveform diff)Limiter caught specific transient peaks.
Dense uniform brightness (waveform diff)Large, clean linear gain change from loudnorm — expected.
Nearly black waveform diff, faint glowIdeal: clean linear gain, no dynamic processing artifacts.
Quick review order
Open _compare_spectrogram.png first — full picture at a glance. Then _diff_spectrogram.png if you need to know specifically what changed. Individual pre/post images for detailed inspection.

The mastering report

Written to <out_dir>/<stem>/<stem><suffix>_report.json after every master run.

json — structure
{
  "generated_at": "2024-01-15T10:23:44Z",
  "input_file": "track.wav",
  "output_file": "mastered/track/track_master.wav",
  "preset": { ... full preset config ... },
  "pre_analysis": {
    "integrated_lufs": -18.4,
    "true_peak_dbtp": -2.1,
    "loudness_range_lu": 14.6,
    "extended": {
      "rms_dbfs": -22.1,
      "crest_factor_db": 20.0,
      "dynamic_range_dr": 17.0,
      "dc_offset": 0.00012,
      "phase_correlation": 0.74,
      "spectral_balance": { "low_pct": 38.2, "mid_pct": 44.1, "high_pct": 17.7 },
      "spectral_centroid_hz": 1420.0
    }
  },
  "post_analysis": { ... same structure ... },
  "filter_chain": "highpass=f=20.0,alimiter=...,loudnorm=...",
  "delta": {
    "lufs_change": 2.4,
    "true_peak_change": 1.8,
    "lra_change": -0.3,
    "crest_factor_change": -0.2
  }
}

The filter_chain field contains the exact string passed to FFmpeg. A master is fully reproducible:

bash — reproduce a master manually
ffmpeg -i input.wav -af "<filter_chain>" -acodec pcm_s24le -ar 44100 output.wav

Extending the pipeline

Adding a filter stage #

Four changes across two files. Example: adding a configurable high-shelf EQ.

1
Add the field to src/config.rs
rust
pub struct Filters {
    pub highpass_hz:   f32,
    pub lowpass_hz:    f32,
    pub high_shelf_db: f32,   // new field
}
2
Add a default in impl Default for Preset
rust
filters: Filters {
    highpass_hz:   20.0,
    lowpass_hz:    0.0,
    high_shelf_db: 0.0,   // 0 = disabled by convention
},
3
Push the filter in build_filter_chain()src/pipeline/process.rs
rust
if preset.filters.high_shelf_db != 0.0 {
    filters.push(format!(
        "treble=g={:.1}:f=8000",
        preset.filters.high_shelf_db
    ));
}
4
Add to your preset .toml
toml
[filters]
highpass_hz   = 20.0
lowpass_hz    = 0.0
high_shelf_db = 1.5   # gentle high shelf boost

Then rebuild: cargo build --release

Useful FFmpeg audio filters #

Full reference: ffmpeg.org/ffmpeg-filters.html

ffmpeg filter strings
# Parametric EQ band
equalizer=f=1000:width_type=o:width=2:g=-3.0

# High shelf boost
treble=g=2.0:f=8000

# Low shelf boost
bass=g=1.0:f=100

# Stereo widening
extrastereo=m=1.5

# Noise gate
agate=threshold=-40dB:ratio=2:attack=20:release=250

# M/S encode
pan=stereo|c0=0.5*c0+0.5*c1|c1=0.5*c0-0.5*c1

# Static gain
volume=2dB

AI integration

matchering — reference-based mastering #

matchering analyzes a reference track and applies its spectral and loudness character to your track. Run it as a pre-pass before mastacraf.

bash
pip install matchering

python3 << 'EOF'
import matchering as mg
mg.process(
    target='track.wav',
    reference='reference.wav',
    results=[mg.Result('track_matched.wav', use_limiter=False)]
)
EOF

mastacraf master track_matched.wav --preset default

Pass use_limiter=False to matchering. mastacraf's limiter handles the peak ceiling.

demucs — source separation #

demucs separates stems. For mastering experimental music this is primarily useful for analysis — inspecting spectral and dynamic character of individual elements before mastering decisions.

bash
pip install demucs
python3 -m demucs --two-stems=vocals track.wav
# examine separated/ directory
mastacraf master track.wav --preset default

Bundling (Tauri)

When you are ready to wrap mastacraf in a desktop UI via Tauri:

1. mastacraf as a Tauri sidecar

json — tauri.conf.json
"bundle": {
  "externalBin": ["../mastacraf/target/release/mastacraf"]
}

2. FFmpeg as a sidecar

Use the ffmpeg-sidecar crate for automatic platform-specific FFmpeg download on first run:

toml — Cargo.toml
ffmpeg-sidecar = "1.1"

Update src/ffmpeg.rs to check the sidecar path before the $PATH lookup.

3. Preset resolution

The preset loader already searches <exe_dir>/presets/. Bundle your .toml files there and they resolve automatically.

4. Frontend interface

The Tauri frontend calls the sidecar, waits for exit, and reads the JSON report from the output directory. No additional IPC beyond filesystem reads is needed for the initial UI.

Troubleshooting

ffmpeg not found
FFmpeg is not in $PATH and not placed next to the mastacraf binary. Install it with your package manager, or copy the binary to the same directory as mastacraf.
Preset 'name' not found
presets/name.toml does not exist in any search directory. Run mastacraf presets to see what is found. Confirm you are running from the directory containing ./presets/.
Post-master LRA much lower than pre-master LRA
target.lra is set below the measured LRA. Loudnorm is compressing dynamics to fit. Raise target.lra to at or above the measured value and re-run.
Phase correlation is negative or very low
Stereo phase issue in the mix. The mastering pipeline does not address this. Fix at the mix stage before mastering.
Extended analysis missing from output
The extended analysis passes ran but failed non-fatally. The core master still completed. Run with --verbose to see which pass failed and why. Likely cause: very short file, or an FFmpeg build missing a specific filter.
Output louder or quieter than expected
Check the report's delta.lufs_change. If it does not match target.lufs − pre.integrated_lufs, the pass-1 JSON was not correctly parsed. Run with --verbose and inspect the raw loudnorm JSON in FFmpeg's stderr output.
Spectrogram or waveform images missing
Requires FFmpeg compiled with lavfi virtual device support. Verify: ffmpeg -filters | grep showspectrum. If missing, your FFmpeg build is minimal — install a full build from your package manager.