ferraus tune
Pitch correction (auto-tune) to a key, scale or written melody
Synopsis
ferraus tune <input> [options] [-o <output>]
What it does
Tracks the pitch of a monophonic vocal (pYIN by default), splits it into notes, picks a target for each note (the nearest note of the detected or given key and scale, or the written note of a MIDI melody with --target), and moves each note there with TD-PSOLA so the voice keeps its tone colour. Correction works per note, not per frame: within a note the deviation is smoothed by --retune-speed (0ms is the hard, robotic snap), vibrato is kept out of the correction, and --strength scales the result. Key detection below 0.6 confidence falls back to the chromatic scale and says why.
This command is also an fx stage and a pipeline op: ferraus fx in.wav -o out.wav --fx "tune:key=value".
Examples
Detect the key and gently correct every note
ferraus tune vocal.wav -o tuned.wav
Correct to F# minor, moving notes 80% of the way
ferraus tune vocal.wav --key F# --scale minor --strength 80% -o tuned.wav
The deliberate hard-tune effect in C major
ferraus tune vocal.wav --effect hard --key C -o robot.wav
Pull the take to a written melody and save the per-note report
ferraus tune vocal.wav --target melody.mid --tolerance 100cents --notes-out notes.json -o tuned.wav
Options
| Flag | Type | Default | Meaning |
|---|---|---|---|
--key | text | auto | Key: auto (detect from the take), or C, F#, Bbm, 'A minor', 'Eb major' |
--scale | text | auto | Scale: auto (the key's mode), major, minor, chromatic, a mode (dorian, mixolydian, harmonic-minor, major-pentatonic, minor-pentatonic, blues, ...), or custom notes such as "C,D,Eb,G,A" |
--strength | percent | 100% | How far each note moves toward its target (50% halves the error) Range: 0%..100%. |
--retune-speed | time | 25ms | Time constant of the correction within a note: 0ms snaps instantly, 100ms or more is gentle |
--humanize | percent | 0% | Bounded random pitch drift (up to ±12 cents at 100%), seeded by --seed Range: 0%..100%. |
--vibrato-preserve | on|off | on | Keep the 4-8 Hz vibrato out of the correction (on/off) |
--formant | preserve | shift | preserve | preserve keeps the voice's tone colour; shift lets formants move with the pitch |
--range | text | 60Hz..1.2kHz | Pitch range searched by the tracker, low..high (Hz, kHz or note names) |
--tracker | pyin | crepe | rmvpe | swiftf0 | pyin | Pitch tracker: pyin (built in) or a model tracker (crepe, rmvpe, swiftf0; needs 'ferraus models pull pitch/<name>') |
--notes-out | path | — | Also write the per-note analysis (detected pitch, target, correction) as JSON to this file |
--target | path | — | Pull notes to the melody in this MIDI file instead of a scale |
--tolerance | cents|semitones | 150cents | With --target: notes farther than this from the written note are left alone and reported |
--effect | natural | hard | natural | hard = robotic effect: --retune-speed 0ms --strength 100% --humanize 0% (and vibrato correction unless --vibrato-preserve is given) |
--mix | percent | 100% | Dry/wet balance: 0% returns the input unchanged, 100% is fully processed |
Global options
Every command also accepts the global options (-o, --json, --dry-run, --overwrite, --progress, ...). See Global options.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Processing failed |
| 2 | Usage or parameter error |
| 3 | Input missing or unreadable |
| 4 | Unsupported format or codec |
| 5 | Model not cached and downloads disabled |
| 6 | Model download or verification failed |
| 8 | Cancelled |
| 10 | Network error |
| 70 | Internal error (bug) |
Common mistakes
- Forgetting that outputs are never overwritten: pass
--overwriteor a new-opath when re-running. - Beat-based times (
4b,1/4) need a tempo: pass--bpmor write4b@120. - Percentages accept
80%or the fraction0.8; a bare80means 80x, not 80%.
For models
- MCP tool:
ferraus_tune - Schema:
ferraus schema tune - Pipeline:
{ op: tune, args: { ... } }(flag names with_instead of-) - fx stage: yes
{"command": "tune", "defaults": {"key": "auto", "scale": "auto", "strength": "100%", "retune_speed": "25ms", "humanize": "0%", "vibrato_preserve": "on", "formant": "preserve", "range": "60Hz..1.2kHz", "tracker": "pyin", "tolerance": "150cents", "effect": "natural", "mix": "100%"}}