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

FlagTypeDefaultMeaning
--keytextautoKey: auto (detect from the take), or C, F#, Bbm, 'A minor', 'Eb major'
--scaletextautoScale: 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"
--strengthpercent100%How far each note moves toward its target (50% halves the error) Range: 0%..100%.
--retune-speedtime25msTime constant of the correction within a note: 0ms snaps instantly, 100ms or more is gentle
--humanizepercent0%Bounded random pitch drift (up to ±12 cents at 100%), seeded by --seed Range: 0%..100%.
--vibrato-preserveon|offonKeep the 4-8 Hz vibrato out of the correction (on/off)
--formantpreserve | shiftpreservepreserve keeps the voice's tone colour; shift lets formants move with the pitch
--rangetext60Hz..1.2kHzPitch range searched by the tracker, low..high (Hz, kHz or note names)
--trackerpyin | crepe | rmvpe | swiftf0pyinPitch tracker: pyin (built in) or a model tracker (crepe, rmvpe, swiftf0; needs 'ferraus models pull pitch/<name>')
--notes-outpath—Also write the per-note analysis (detected pitch, target, correction) as JSON to this file
--targetpath—Pull notes to the melody in this MIDI file instead of a scale
--tolerancecents|semitones150centsWith --target: notes farther than this from the written note are left alone and reported
--effectnatural | hardnaturalhard = robotic effect: --retune-speed 0ms --strength 100% --humanize 0% (and vibrato correction unless --vibrato-preserve is given)
--mixpercent100%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

CodeMeaning
0Success
1Processing failed
2Usage or parameter error
3Input missing or unreadable
4Unsupported format or codec
5Model not cached and downloads disabled
6Model download or verification failed
8Cancelled
10Network error
70Internal error (bug)

Common mistakes

  • Forgetting that outputs are never overwritten: pass --overwrite or a new -o path when re-running.
  • Beat-based times (4b, 1/4) need a tempo: pass --bpm or write 4b@120.
  • Percentages accept 80% or the fraction 0.8; a bare 80 means 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%"}}