Clean, tune, and master a vocal
An agent recipe: take a raw vocal take and a backing track to a mastered song, deciding each
step from the JSON the previous step returned. Every command below was run with ferraus 0.1.0;
JSON is trimmed to the fields an agent reads. Add --json to every call and read ok,
error.hint, warnings, and metrics.
Inputs: take.wav (dry mono vocal), beat.wav (instrumental).
1. Diagnose
ferraus analyze problems take.wav --json
{"ok": true, "command": "analyze problems",
"metrics": {"high": 0, "medium": 0, "low": 0, "clean": true},
"result": {"clean": true, "counts": {"high": 0, "medium": 0, "low": 0},
"problems": [{"problem": "hum", "severity": "medium", "details": "...", "suggested_fix": "dehum:freq=60Hz"}]}}
Decide: build the cleanup chain from result.problems[].suggested_fix (each is an fx
stage string). If clean is true and problems is empty, skip cleanup: do not denoise a
take that has no noise. On a clean, digitally silent take denoise --auto learns "noise" from
silence and still turned the level down by about 6 dB in our test (output_rms_dbfs -26.3 vs
input_rms_dbfs -20.1), so only run it when the report names hiss or noise.
ferraus fx take.wav -o take.clean.wav --fx "dehum:freq=60Hz" --fx "denoise:auto" --fx "highpass:freq=80Hz" --json
Read back params.noise_region (where the noise was learned; it must be a pause) and
metrics.removed_rms_dbfs (should be far below input_rms_dbfs).
2. Find the key and tune
Prefer the key of the full song, measured on the beat (polyphonic material gives a reliable chroma estimate):
ferraus analyze key beat.wav --json
{"ok": true, "command": "analyze key",
"metrics": {"confidence": 0.595},
"warnings": [{"code": "low_key_confidence", "message": "key confidence 0.59 is below 0.6; 'C major' is only a best guess"}],
"result": {"key": "C major", "tonic": "C", "mode": "major", "confidence": 0.595, "correlation": 0.823,
"alternatives": [{"key": "G major", "correlation": 0.774}, {"key": "A minor", ...}]}}
Decide: if result.confidence is at least 0.6, pass result.tonic and result.mode.
Here it is 0.595 with a low_key_confidence warning, and the runners-up are the neighbouring
key (G major) and the relative minor (A minor), which share most notes with C major. An agent
should ask the user, or check with ferraus analyze chords beat.wav (this song's chords are C,
Am, F, G: C major). Without --key, tune falls back to the chromatic scale by itself and
warns with the same code.
ferraus tune take.clean.wav --key C --scale major --strength 85% --retune-speed 30ms --notes-out notes.json -o take.tuned.wav --json
{"ok": true, "command": "tune",
"params": {"key": "C", "scale": "major", "strength": "85%", "retune_speed": "30ms", ...},
"metrics": {"notes": 14, "corrected_notes": 14, "mean_abs_deviation_cents": 49.44, "max_abs_deviation_cents": 83.77, "resynthesis": "td_psola"}}
Check: a mean_abs_deviation_cents near 50 means notes sit between two scale notes and
may be pulled to the wrong one; open notes.json and look at deviation_cents and
confidence per note. Notes with large deviation and low confidence are tracking errors: lower
--strength, try --tracker crepe -y, or tune to a written melody with --target melody.mid.
3. Vocal effects
ferraus presets list compress --json # vocal, drums, bus, master, tighten
ferraus fx take.tuned.wav -o take.fx.wav --fx "compress:preset=vocal" --fx deess --fx "reverb:type=plate,mix=18%" --json
4. Mix with the beat
ferraus mix take.fx.wav beat.wav --gains 0dB,-6dB -o song.mix.wav --json
{"ok": true, "command": "mix",
"result": {"duration_s": 21.2, "channels": 2, "sample_rate": 48000,
"inputs": [{"path": "take.fx.wav", "gain_db": 0.0, "pan": null, "offset_s": 0.0},
{"path": "beat.wav", "gain_db": -6.0, "pan": null, "offset_s": 0.0}]}}
Use --offsets 0s,2b (with --bpm) to start the vocal later, and --pans to place parts.
5. Master and verify
ferraus master song.mix.wav --target streaming -o song.master.wav --report report.json --json
{"ok": true, "command": "master",
"metrics": {"integrated_lufs": -14.01, "true_peak_dbtp": -1.05, "limiter_gain_reduction_db": 1.68,
"target_reached": true, "verify_pass": true}}
stderr may show warning: the result peaks at +0.55 dBFS and will clip in fixed-point formats
from inner stages; those stages run in floating point, and the final file is what
true_peak_dbtp and verify_pass describe.
Decide: if target_reached is false, the warnings array says why (for example, the
limiter bound of --intensity), with the flag that fixes it. Confirm independently:
ferraus master verify song.master.wav --target streaming --json
Exit code 0 and "ok": true mean every check passed; exit code 9 lists failed_checks, each
with a fix string.
The same job as one file
The whole recipe is also a pipeline file, which caches every stage so
re-running after a change recomputes only what changed. Over MCP, the server's
clean-and-tune-vocal prompt walks a model through these steps (MCP setup).