Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Command line

The binary is sound-to-patch. This page shows the help of each command. The help texts run in the test suite, so they match the built binary. cargo xtask dist also renders them as man pages.

sound-to-patch

$ sound-to-patch --help
Turn an audio stem or a written sound description into a starting point in Vital or Serum 2.

Usage: sound-to-patch <COMMAND>

Commands:
  analyze  Analyse a stem and write layers.json, spectrogram.png and notes.mid
  notes    Transcribe a stem and write notes.mid
  preset   Read, inspect and write synth presets
  render   Render a preset and a MIDI file to a WAV file through the synth
  help     Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version

analyze

$ sound-to-patch analyze --help
Analyse a stem and write layers.json, spectrogram.png and notes.mid.

The tempo is refined from the stem near --bpm, and the stem is transcribed unless --notes gives its notes. Each step prints what it found.

Example: sound-to-patch analyze bass.wav --bpm 140

Usage: sound-to-patch analyze [OPTIONS] --bpm <BPM> <STEM>

Arguments:
  <STEM>
          The audio stem: WAV, AIFF, FLAC, MP3, AAC or M4A

Options:
      --bpm <BPM>
          The tempo of the stem in beats per minute, 4 to 1000. It is refined from the stem, 2.91 % below to 3.09 % above it, unless --bpm-exact is given

      --bpm-exact
          Use --bpm as it is, without refining it from the stem

      --notes <FILE>
          A MIDI file with the stem's notes, instead of transcribing the stem; the f0 track still reads the stem unless the notes play chords. Its tempo must match --bpm or the refined tempo

      --profile <PROFILE>
          Decode with the settings of this kind of stem instead of the detected ones

          Possible values:
          - bass: B0 to F#4, with the minimum length from the BPM
          - lead: C2 to B6, with the minimum length from the BPM. Plucks use it too
          - pad:  C2 to B6, with the decoder's default minimum length. Chords use it too

  -o, --out <DIR>
          The output folder [default: a folder named after the stem, next to it]

      --force
          Replace files in the output folder that this stem's report does not own: a report or notes of another stem, a report of the same name with other content, a file this tool did not write, or a spectrogram.png without a report

      --synth <SYNTH>
          The target synth that the report names [default: vital]

          Possible values:
          - vital:  Vital, with `.vital` preset files
          - serum2: Serum 2, with `.SerumPreset` preset files

  -h, --help
          Print help (see a summary with '-h')

analyze decodes the stem at its native rate, runs the stem estimators and writes three files into the output folder, which it creates:

  • layers.json: the report. The layer report page lists every field.
  • spectrogram.png: a 1500 by 512 pixel image of the busiest 10 s, in two panels. The pipeline page gives its layout.
  • notes.mid: the final note list, the notes rewritten with the layers, as the analyze guide explains. It replaces the first file of notes. A stem without notes writes none.

The files are replaced on the next run of the same stem file; nothing else in the folder changes. A stem of the same name with other bytes, such as a new export, needs --force, below. The same stem and flags give the same bytes. The stem is read once, so a pipe such as /dev/stdin works as a stem. Each file is written to a hidden temporary sibling and renamed into place, notes.mid first and layers.json last; the old layers.json moves aside before any file changes. So no run leaves a cut-short file, and a layers.json that exists belongs to the files next to it. A write that fails, such as on a full disk, puts the old files back, leaves no temporary file and removes a folder the run created. A run killed during the renames, such as by a power loss, can leave the old files as hidden backups named .<file>.<random>.old, such as .layers.json.a1B2c3.old, with no layers.json. The next run stops and names every backup: rename each backup back to its file name to keep the old report, or pass --force to write new files and remove the backups once they are in place. The command prints one wrote <path> line per file to standard output, and a warning line for each corrupt packet that became silence to standard error.

layers.json records the stem’s file name in source and the SHA-256 of its bytes in source_sha256. The command stops before it decodes, names the file and asks for --out or --force when the folder holds:

  • the report of another stem, such as bass.aiff next to bass.wav sharing bass/;
  • a report of the same name with other content: another stem of that name, such as the bass.wav of another song in a shared --out folder, or this stem changed since that run, such as a new export; a report from release 0.31 or earlier has no checksum and is matched by its name;
  • a layers.json that is not a report of this tool: it cannot be read, is not JSON or names no source;
  • a spectrogram.png and no layers.json, a file no report owns;
  • a notes.mid of another stem by its track name, or one with no track name, as in notes.

--out picks another folder, and --force replaces the files. An output folder that is a file, by default or by --out, such as a file bass next to bass.wav, stops the command with a request for --out.

Without --out, the folder is named after the stem file and sits next to it: stems/bass.wav writes stems/bass/. A stem whose name without the extension is . or .., such as ..wav, would name its own folder or the one above, and a stem without an extension, such as bass or .wav, would name the stem file itself, so each needs --out.

  • Tempo: --bpm is refined from the stem’s onsets, from 2.91 % below to 3.09 % above it, as the tempo section describes, and the first line of standard output says which tempo every later step uses and why. --bpm-exact uses --bpm as it is. The report records the given and the refined tempo.
  • Notes: without --notes, the stem runs through the notes step of notes once, and the next lines of standard output are its steps; --profile overrides the detected settings as there. The transcription group of the report records the model and the settings.
  • With --notes, the MIDI file gives the note list and the model never runs; --profile is then a usage error, and transcription is null. The note times follow the file’s tempo map, and every tempo in the file must lie within 0.01 BPM of --bpm or of the refined tempo; a file without a tempo event plays at the MIDI default of 120 BPM. Notes on channel 10, the percussion channel, are left out.
  • Standard output ends with wrote <path> for each file. A warning line for each corrupt packet that became silence goes to standard error.
Exit codeMeaning
0The files were written.
1The stem or the note file cannot be read or is invalid, the notes step fails, the selected synth has no adapter yet, the output folder is a file, has no name of its own or holds a file the run does not own, an output cannot be written, or the analysis fails internally. A refusal and a failure share this code. The message names the cause and the fix, and the file when one is involved.
2A usage error, such as a missing --bpm, a tempo outside 4 to 1000, or --profile with --notes.

notes

$ sound-to-patch notes --help
Transcribe a stem and write notes.mid.

The pitch range, the voicing and the minimum note length come from the stem unless --profile sets them. Each step prints what it found.

Example: sound-to-patch notes bass.wav --bpm 140

Usage: sound-to-patch notes [OPTIONS] --bpm <BPM> <STEM>

Arguments:
  <STEM>
          The audio stem: WAV, AIFF, FLAC, MP3, AAC or M4A

Options:
      --bpm <BPM>
          The tempo of the stem in beats per minute, 4 to 1000. It is refined from the stem, 2.91 % below to 3.09 % above it, unless --bpm-exact is given

      --bpm-exact
          Use --bpm as it is, without refining it from the stem

      --profile <PROFILE>
          Decode with the settings of this kind of stem instead of the detected ones

          Possible values:
          - bass: B0 to F#4, with the minimum length from the BPM
          - lead: C2 to B6, with the minimum length from the BPM. Plucks use it too
          - pad:  C2 to B6, with the decoder's default minimum length. Chords use it too

  -o, --out <DIR>
          The output folder [default: a folder named after the stem, next to it]

      --force
          Write next to a report that is not this stem's, replace a notes.mid of another stem or with no track name, or replace the final notes and remove the report that describes them

  -h, --help
          Print help (see a summary with '-h')

notes decodes the stem at its native rate, runs the transcription model once and the f0 track once, and writes notes.mid into the output folder, which it creates. The pipeline page gives each step.

  • Settings: without --profile, the pitch range, the voicing and the minimum note length come from the stem. --profile bass, lead or pad sets them instead; detection still runs and prints its findings.
  • Tempo: --bpm is refined from the stem as for analyze, and every later step uses the printed tempo; --bpm-exact uses --bpm as it is.
  • Output: standard output gets one line per step, in order: the tempo, the detected range, the detected voicing, the register window, the voicing the cleanup used and where it came from, the settings used, the decoded count, then the note count after the octave check, the ghost merge, the attack split, the split join and the quiet-note guard with its drop count, and last wrote <path>. A warning line for each corrupt packet that became silence goes to standard error.
  • The file: a format 0 MIDI file with 480 ticks per beat, the stem’s file name as the track name, the printed tempo and 4/4 at tick 0, and every note on channel 1 at velocity 100. It is written to a hidden temporary sibling and renamed into place. A failed write leaves the old files and no temporary file. Only notes.mid is replaced; nothing else in the folder changes, except the report that --force removes, below.
  • The folder: as for analyze, stems/bass.wav writes into stems/bass/ without --out, and a stem such as ..wav, .wav or bass needs --out. When the folder holds a notes.mid whose track name is another stem, or none, the command stops before it decodes and names the file; --force replaces that file. When it holds a layers.json of another stem, of the same name with other content, or one that is not a report, it stops the same way; --force writes notes.mid next to that file and leaves it. When it holds a layers.json with a notes group, the report of analyze that describes the final notes.mid, it stops whatever the stem, because the first list would replace the file the report describes; --force replaces notes.mid and removes that report once the new file is in place, so no report describes a file that is gone. A backup that a killed run left, as for analyze, stops it too; --force writes notes.mid and then removes the backups. --out picks another folder in every case.
Exit codeMeaning
0notes.mid was written.
1The stem cannot be read, gives no note (digital silence, for one), or cannot be transcribed; the output folder is a file, has no name of its own or holds a file the run does not own; or the file cannot be written. Nothing is written. A refusal and a failure share this code. The message names the cause and the fix.
2A usage error: a missing --bpm, a tempo outside 4 to 1000 or not a number, or a profile other than bass, lead and pad.

preset

$ sound-to-patch preset --help
Read, inspect and write synth presets

Usage: sound-to-patch preset <COMMAND>

Commands:
  inspect  Print the header fields and the decoded tree of a preset file
  apply    Apply an edit list to a template and write a preset
  help     Print this message or the help of the given subcommand(s)

Options:
  -h, --help  Print help

preset inspect

$ sound-to-patch preset inspect --help
Print the header fields and the decoded tree of a preset file.

Example: sound-to-patch preset inspect bass.vital

Usage: sound-to-patch preset inspect [OPTIONS] <FILE>

Arguments:
  <FILE>
          The preset file, such as `bass.vital`

Options:
      --synth <SYNTH>
          The target synth [default: the extension of the preset file, else vital]

          Possible values:
          - vital:  Vital, with `.vital` preset files
          - serum2: Serum 2, with `.SerumPreset` preset files

      --full
          Print every list element and every text in full

      --shown <N>
          List elements printed before the rest is counted
          
          [default: 3]

  -h, --help
          Print help (see a summary with '-h')

preset inspect refuses a file without a stamp, settings or sample, and a file with a known control that holds a text, a list, an object or null in place of a number. Vital fails to load a text in a control and loads a flag, so a flag reads. The message names the key.

preset apply

$ sound-to-patch preset apply --help
Apply an edit list to a template and write a preset.

Values are written with their unit, such as "1.2 kHz", "800 ms", "-6 dB", "+12 st" or "25%".

Example: sound-to-patch preset apply edits.json -o bass.vital

Usage: sound-to-patch preset apply [OPTIONS] --out <FILE> <EDITS>

Arguments:
  <EDITS>
          The edit list, a JSON file

Options:
  -o, --out <FILE>
          The preset to write, such as `bass.vital`

      --template <FILE>
          A preset to start from instead of the synth's built-in template

      --name <NAME>
          The preset name. Without it, the name of the output file

      --force
          Replace the output file if it exists

      --synth <SYNTH>
          The target synth [default: the extension of the preset file, else vital]

          Possible values:
          - vital:  Vital, with `.vital` preset files
          - serum2: Serum 2, with `.SerumPreset` preset files

  -h, --help
          Print help (see a summary with '-h')

The output file:

  • must carry the synth’s extension, such as .vital;
  • must not exist, unless --force is given;
  • must not be the edit list or the template;
  • must not lie inside Vital’s own Factory folder.

The output keeps the template’s stamp, and the summary prints it. A stamp with a newer first or second number than the stable build 1.0.7, such as 1.1.0, gives a warning: that build refuses it. The load gate ignores the third number, so 1.0.8 gives none. A run that ends with an error leaves no file. A run killed during the write leaves at most a temporary file whose name ends in .partial next to the output. A folder without hard links, such as one on an exFAT or FAT32 drive, gets the file too, and an existing file there is still replaced only with --force. There, without --force, the file is written in place: it is visible while it is written, an error removes it, and a run killed during the write leaves a partial output file. An empty --name, such as an unset shell variable, is a usage error.

The edit list

preset apply reads an edit list: a JSON object with the target synth and a list of edits, in a UTF-8 file of at most 1 MiB. A leading byte order mark is skipped. Each edit is one of four records, and each carries a reason.

RecordFieldsExample
A controlparam, value{"param": "filter_1_cutoff", "value": "400 Hz", "reason": "dark"}
A menu choicemenu, choice{"menu": "filter_1_model", "choice": "Ladder", "reason": "warm"}
A routeroute: source, dest, amount, optional polarity{"route": {"source": "env_2", "dest": "filter_1_cutoff", "amount": "+24 st"}, "reason": "pluck"}
A raw keyraw: key, value{"raw": {"key": "custom_warps_count", "value": 2}, "reason": "a newer build"}
  • A control value carries its unit, as the units page lists: "1.2 kHz", "800 ms", "-6 dB", "+12 st" or "25%". A switch takes true or false, and a count takes a number.
  • A route amount is in semitones when the destination is stored in semitones, such as a cutoff or a pitch, and in percent otherwise. A depth in semitones is divided by the destination’s stored span, as Vital shows it.
  • A route may target a number, a count, a menu such as lfo_1_tempo, or the amount or power of another slot. A switch and the bipolar, stereo and bypass flags of a slot take no route.
  • A route amount lies within -100% to 100% of the destination’s range.
  • A route that joins the same source and destination as an existing one edits it in place and keeps its polarity, curve and flags unless the edit sets them.
  • A raw key is written as it is, outside the registry, and the output lists it as raw. A raw edit never replaces a key of the template that holds a list or an object.
  • A list edits each control once and each source and destination once. A second edit of either is refused with the number of the first.
  • Every problem of a list is reported at once, up to 50, each with the file, the edit, the cause and the fix.

The guide walks through one edit list.

render

$ sound-to-patch render --help
Render a preset and a MIDI file to a WAV file through the synth.

The synth's plug-in loads headless in the plug-in host process. A render counts only after the load check: the clip sounds, and the state the plug-in saves back holds the preset. A clip whose octave bands match the plug-in's init patch gives a warning. Each step prints what it found.

Example: sound-to-patch render bass.vital --midi notes.mid --bpm 140 -o bass.wav

Usage: sound-to-patch render [OPTIONS] --midi <FILE> --bpm <BPM> --out <WAV> <PRESET>

Arguments:
  <PRESET>
          The preset to render

Options:
      --midi <FILE>
          The MIDI file with the notes, read as `analyze --notes` reads it

      --bpm <BPM>
          The tempo of the MIDI file in beats per minute, 4 to 1000

  -o, --out <WAV>
          The WAV file to write; the render record goes next to it with .json

      --plugin <BUNDLE>
          The synth's .vst3 bundle [default: the bundle in /Library/Audio/Plug-Ins/VST3, else in the user's VST3 folder]

      --synth <SYNTH>
          The target synth [default: the extension of the preset file, else vital]

          Possible values:
          - vital:  Vital, with `.vital` preset files
          - serum2: Serum 2, with `.SerumPreset` preset files

      --force
          Replace an existing WAV or record file

  -h, --help
          Print help (see a summary with '-h')

render writes two files: the WAV file named by --out, 32-bit float stereo at 44.1 kHz, and the render record next to it, with .json in place of .wav. The record follows schemas/render-record.schema.json; the render record page lists its fields. Both go to temporary files in the same folder first, and replace the outputs only once both writes succeeded. Without --force, a failed write leaves the folder as it was. With --force, a failure while placing the files can leave the new WAV file beside the old record. The error then names both files and says that they no longer match. Make the record replaceable and render again with --force.

The plug-in loads in sound-to-patch-host, a second program that ships next to sound-to-patch. SOUND_TO_PATCH_HOST names another copy. The guide walks through one render and the load check.