Rendering a preset
sound-to-patch render plays a MIDI file through a preset in the synth’s
own plug-in and writes the audio as a WAV file. It runs on macOS with
Vital. Serum 2 follows. The CLI reference
lists every flag.
What it needs
- Vital, installed as a VST3 plug-in in
/Library/Audio/Plug-Ins/VST3or in the same folder under your user’sLibrary.--pluginnames another bundle. sound-to-patch-host, the program that loads the plug-in. It ships in the release archive next tosound-to-patch, andrenderlooks for it there.SOUND_TO_PATCH_HOSTnames another copy.- Rosetta on Apple silicon. Vital 1.0.7 holds Intel code only, so the host
runs as an Intel program for it.
softwareupdate --install-rosettainstalls Rosetta.
The plug-in loads as a library, with no window, and the Vital app never starts.
Run it
Pass the preset, a MIDI file with its tempo, and the WAV file to write:
sound-to-patch render bass.vital --midi notes.mid --bpm 120 -o bass.wav
plug-in: Vital 1.0.7 from /Library/Audio/Plug-Ins/VST3/Vital.vst3, in sound-to-patch-host 0.18.0 (vst3-host 0.10.0) as x86_64
preset: routes, 2x oversampling
notes: 3, the last note-off at 3.000 s, 120 BPM
init patch: 2 renders, 0.04 dB apart
load check: passed; the saved state matches, and the render lies 58.20 dB from the init patch, beyond the margin of 0.11 dB
clip: 5.589 s, the tail went quiet
wrote bass.wav
wrote bass.json
This is one local run: it played the routes.vital test fixture, the
built-in template with three routes, through Vital 1.0.7. The MIDI file
holds MIDI 48, 55 and 60 at velocity 100, one second each, back to back.
The dB figures change a little from run to run.
Each line names one step:
plug-in: the synth, its build, the bundle and the host, with the architecture the host runs as.preset: the preset’s name and the oversampling of the render. Every Vital render uses 2x oversampling, whatever the preset holds, because oversampling changes the sound.notes: how many notes the MIDI file holds, when the last one ends and the tempo. The file must be one thatanalyze --notesreads: every tempo in it within 0.01 BPM of--bpm, and the notes off channel 10.init patch: the plug-in renders the same notes twice with its own init patch before the preset loads. The two renders differ a little: Vital’s oscillators randomize their start phase fully by default (osc_1_random_phasedefaults to 1 in the registry).load check: see below.clip: the length of the WAV file. A clip is at least 4 s long. It ends once the output has stayed below -96 dBFS for 1 s after the last note-off. A tail still sounding 30 s after the last note-off is cut there, and the line says so.
The load check
A plug-in can report that it loaded a preset and still play its old sound. So a render counts only when two checks pass:
- The clip sounds. A clip whose every sample stays below -96 dBFS is silent, and the error names what in the preset can explain it, such as no oscillator and no sample being on.
- The state the plug-in saves after the render holds the preset: its
name, its tuning and every numeric control, each number within one step
of a 32-bit float of the sent value. The tempo control is the
exception: the plug-in sets it from
--bpm, so it must lie within one step of the tempo Vital computes from--bpm, in beats per second. A preset without a tuning plays in Vital’s default tuning, never in the tuning of the preset rendered before it. When the saved state is the init patch, the plug-in did not load the preset at all, as with a preset saved by a newer build of Vital.
Each failure has its own message, and the command writes no file.
A third check only warns. Each clip is measured as its level in octave
bands, and the distance from the init render is the largest band
difference in dB. Only the bands within 30 dB of the loudest band count,
and only those above a floor of -90 dB of mean bin power. That floor is
not a level in dBFS: a band of many bins reads lower than a band of few
for one tone, so the floor passes a sine above about -50 dBFS in the top
band and above about -74 dBFS in the lowest band at 44.1 kHz. A clip within three times the distance between the two
init renders, and within at least 0.1 dB, sounds like the init patch. The
render still counts, and render prints a warning. Mean band levels miss
a change of envelope or modulation: a slower attack on the init patch can
lie within the margin. The margin is a stopgap. Two init renders give no
distribution, and a later release measures the spread over many renders.
The output
The WAV file holds 32-bit float stereo at 44.1 kHz. The record next to it,
bass.json here, names everything the audio depends on: the synth and its
build, the host, the recipe version, the sample rate and block size, the
settle time, the oversampling, the tempo, the clip length, the SHA-256 of
the MIDI file and the load check. The
render record page lists every
field.
Pass the WAV file and the MIDI file to analyze to measure the render as
you would measure a stem:
sound-to-patch analyze bass.wav --bpm 120 --notes notes.mid
Two renders of one preset are not identical, so compare renders within their spread, never sample by sample.