music/VIRTUAL_ORCHESTRATION_AND_MOCKUP_REALISM.md
CANON SUBORDINATION — this document is PROPOSAL-TIER: it serves canon and never outranks it.
Canon served: the melody-first / thirty-year-bar music direction (memory
music-direction-melody-first-30-year-bar), the composed-never-prompted rule, and the standing
observation that our sfizz CC0 renders read as "noise". This lane owns the REALISATION half of
that complaint — the distance between a correct score and a performance a listener believes.
If this document disagrees with canon, CANON WINS and this document is the defect.
TIER: RESEARCH SYNTHESIS — PROPOSAL-TIER. This document is HOW, never WHAT. It sets no canon, names
no region content, and changes no spine or registry row. Every substantive claim carries a source
(URL, or manual plus publisher, or paper plus authors and DOI); where sources disagree the
disagreement is stated; where a statement is craft consensus rather than evidence it is labelled
CRAFT CONSENSUS. Every principle carries a PIPELINE HOOK line marked CODE-ABLE or MEASURE-ONLY,
naming the file and the operation. Every number attributed to OUR stack in section 1 and in GAP
NOTES was measured on this box during this pass, not recalled.
WHAT THIS LANE ANSWERS. The craft series so far has taught the pipeline what to WRITE — counterpoint,
counter-melody, motivic development, ornament, texture. All of it is symbolic. A cue can satisfy
every rule in every one of those documents and still arrive at the ear as a MIDI file, because the
symbolic layer stops at the note list and the note list is not a performance. This document is about
the layer below: what a sampler is given, how a real ensemble differs from a note list, how the
result is placed in a room, and at what level it is delivered. It is the layer where our renders
currently do essentially nothing, and it is measurable.
Every number in this section was produced during this pass by running our own code and our own
palette. The three pass-5 plans (build/audio/pass5/plans/PASS5_FLORES_ROAD.json, _FALLS,
_ROUNDS) were compiled through harness/music_gen/pass2_realise.py, which is the renderer
harness/music_gen/pass5_realise.py re-points and calls; the SFZ probes were rendered through the
same sfizz_render invocation the pipeline uses.
pass2_realise._write_midi builds a pretty_midi.Instrument, appends notes, and writes the file.
It appends no control_changes and no pitch_bends. A grep for controller emission across
harness/music_gen/*.py returns hits only in complete_track.py — and there the split is exact and
instructive: _write_part_midi, which produces the MIDI that is actually rendered, writes notes and
optional pitch bends only; write_score_midi, which writes the CC11 dynamic envelope, is documented
in its own docstring as "THE NOTATION SEAM… readable in any notation program", and its output is
never handed to sfizz. So across both realisation lanes, the audio a listener hears is produced from
note-on, note-off, pitch and velocity, and nothing else.
This is the single largest structural difference between our renders and every mockup practice
described in sections 2 and 3, all of which treat continuous dynamic data as the primary vehicle of
realism rather than an ornament on top of it.
compile_layer computes each onset as abs_beat * beat_s from integer cycle and beat arithmetic.
Notes from different layers that share a nominal beat therefore share an identical floating-point
timestamp. Measured across the three cues:
| cue | notes | distinct onset TIMES | notes inside a multi-layer cluster | largest cluster | measured inter-layer spread |
|---|---|---|---|---|---|
| FLORES_FALLS | 1838 | 848 | 1367 (74.4%) | 9 layers | 0.000 ms |
| FLORES_ROAD | 4058 | 1028 | 3787 (93.3%) | 17 layers | 0.000 ms |
| FLORES_ROUNDS | 4705 | 1267 | 4305 (91.5%) | 17 layers | 0.000 ms |
Between 74% and 93% of every note in a cue is struck at the same instant as at least one other layer,
to the sample. Section 3.1 gives the measured norm for real ensembles, which is not zero and is not
close to zero.
Velocity is computed once per note as base_vel * vscale * (0.55 + 0.65 * inten), where base_vel
is a per-layer constant from the plan, vscale is the pattern's accent multiplier for that position
in the cycle, and inten is the section's declared intensity. Nothing in that expression varies from
one statement of a figure to the next statement of the same figure inside the same section.
Measured: consecutive notes within a layer carry an IDENTICAL velocity 91.3%, 89.4% and 90.8% of the
time in the three cues. The variation that does exist is entirely the accent pattern and the section
map — it is compositional structure, not performance. Three layers in every cue carry three or fewer
distinct velocities across the whole piece.
Every sustained orchestral sample in a real library begins before it is audible; producers keep the
attack intact, so the perceived note arrives late (section 2.6). Measured directly on our own CC0
sample files — time from file start to the point the envelope first reaches half of the sample's
peak, median over up to ten samples per instrument:
| instrument | to 10% peak | to 50% peak |
|---|---|---|
| clarinet.sus | 90.3 ms | 151.9 ms |
| horn.sus | 30.1 ms | 94.2 ms |
| flute.sus_vib | 46.8 ms | 88.8 ms |
| trombone.sus | 17.1 ms | 84.0 ms |
| bassoon.sus | 32.5 ms | 80.8 ms |
| oboe.sus_vib | 19.0 ms | 59.2 ms |
| trumpet.sus | 18.9 ms | 46.4 ms |
| harp | 25.2 ms | 33.9 ms |
| timpani.hit | 1.5 ms | 5.6 ms |
| bass_drum | 1.1 ms | 6.0 ms |
| conga | 0.5 ms | 3.3 ms |
Across the 22 instruments probed, the median time-to-half-peak is 47.6 ms and the spread between the
fastest and slowest speaker is 148.6 ms. pass2_realise applies no per-instrument offset of any
kind. A downbeat written for conga and clarinet together therefore arrives as a conga followed, most
of a fifth of a second later, by a clarinet — and the clarinet is usually the one carrying the tune.
The perfect simultaneity of section 1.2 is perfect in the score and audibly wrong in the render.
orchestrate.py and pass2_realise.determinism both assert that "the palette's SFZ files do not
use" round-robin and random opcodes. Measured across the 85 palette entries: 20 declare
seq_position, and 10 declare lorand. The claim is false, and the determinism check does not
catch it because sfizz resets its sequence counter at the start of each render, so a repeated render
is still bit-identical while the round robin is cycling inside it.
Rendered probe, eight repeats of one key at a fixed velocity: conga, bass drum and woodblock all
alternate between two distinct recordings, with zero identical repeats out of seven comparisons.
The palette declares seq_length=2 on 202 regions, seq_length=6 on 6, seq_length=3 on 3.
The coverage, however, is inverted relative to need. Round robin by family: drum 7 of 9, rattle 2 of
2, struck_wood 1 of 2 — against woodwind 1 of 13, brass 1 of 7, strings 4 of 21, voice 0 of 4,
plucked 0 of 3, tuned_perc 1 of 7. Across the three pass-5 cues, 11 of 28 melodic and figural layers
(39%) are carried by an instrument with no round robin at all. The machine-gun effect (section 2.5)
is therefore live on exactly the material a listener follows.
Rendered probe through sfizz_render, one three-second note with a CC ramp from 10 to 127 across the
note, compared sample-by-sample against the same note with no controller data:
| instrument | CC1 max difference | CC7 max difference | CC11 max difference |
|---|---|---|---|
| horn.sus | 0.00e+00 | 3.48e-02 | 3.62e-02 |
| clarinet.sus | 0.00e+00 | 5.07e-02 | 5.46e-02 |
| trombone.sus | 0.00e+00 | 1.61e-02 | 1.70e-02 |
CC1 changes nothing, exactly, because no file in the palette declares locc1, xfin_locc1 or any
_oncc1 modulation — zero of 85. CC7 and CC11 both act as gain, because sfizz implements the
registered MIDI volume and expression controllers itself. So the industry's primary realism tool
(section 2.1) is unavailable to us as shipped, and the secondary one is available and unused.
Velocity, by contrast, does reach real dynamic layers: comparing velocity 40 against velocity 110 on
the same note, the peak ratio is 3.4x (clarinet), 5.6x (horn) and 15.1x (trombone), and the
level-normalised envelope difference is 0.17 to 0.19 — far from zero, so the timbre genuinely
changes and not merely the level. Velocity layering across the palette: 5 instruments carry 6 bands,
13 carry 4, 8 carry 3, 27 carry 2, 12 carry 1 and 20 carry none. The vocabulary exists, it is
coarse, and — decisively — it can only be addressed at note-on, so no crescendo inside a held note is
reachable by any means our renderer currently possesses.
mix_policy.apply_space convolves the finished stereo mixdown with a single synthetic impulse
response chosen by cue class. The docstring is honest about what that is: "a DIFFUSE FIELD with no
early-reflection geometry, so it gives distance and decay and it does not give a room shape." There
is one room per cue, applied identically to every instrument; per-part processing amounts to a
static gain_db and a constant-power _pan. A grep for filter design across pass2_realise.py,
orchestrate.py and mix_policy.py finds biquads only inside the loudness meter's K-weighting.
There is no EQ anywhere in the signal path, no high-frequency roll-off with distance, no per-section
reverb, and no pre-delay differentiation between a front-desk soloist and a back-row instrument.
Section 5 is what the practice does instead.
mix_policy.derive measures the acquired exemplar corpus and takes the per-class median as the
target: BATTLE −9.84 LUFS (n=5), EXPLORATION −12.18 (n=8), MELANCHOLIC −13.95 (n=4, and SANCTUARY
borrows it), overall default −11.55 (n=30). The three published pass-5 cues land at −18.81, −17.71
and −17.41 LUFS and all three fail clears_target. LIMITER._why_it_exists reads that gap as a
defect and probes a limiter to close it.
Section 6 argues the gap is largely an artefact of the denominator. Those corpus numbers are
commercial ALBUM masters. ASWG-R001 is a WHOLE-PROGRAMME number. The published measurements of
shipped games put the in-game music BED between −23 and −40 LUFS. Three different quantities, three
different meanings, and MIX_POLICY.json currently declares only the first while clears_target
reads as though it were the delivery bar.
The professional vocabulary is stable across forums, manufacturers and courses, and it distinguishes
three things our pipeline collapses into one.
only act at note-on. On performance patches it also selects articulation and transition type:
Spitfire Chamber Strings documents velocity 1–9 as a smooth attack and 10–127 as three to four
layers of spiccato/staccato attack, and on slower transitions velocity 1–19 gives portamento,
20–84 the fingered slur, 85–127 the heavier bowed change (Spitfire Chamber Strings User Manual,
"PERFORMANCE LEGATO").
to CC1, controls the dynamic of the sound, and "this smoothly crossfades between very soft (or
'pp') recordings, through to very loud (or 'ff')". That is, it changes TIMBRE continuously,
mid-note, which velocity cannot do. CRAFT CONSENSUS: the community restatement that CC1 changes
the sample dynamics "on the fly" comes from VI-Control threads that cannot be fetched (see
SOURCES), so it is carried here as consensus, not as cited text.
changes level and nothing else.
The Spitfire manual reduces its own instruction set to two rules for a new user, and the second is
"On long notes make sure you always use your Modwheel" — verified verbatim on the manual's welcome
page, where it is printed as rule 2 of 2.
PIPELINE HOOK — CODE-ABLE. pass2_realise._write_midi must gain a controller-emission stage. The
palette forces the assignment: CC1 is dead on our files (1.6), so the continuous lane is CC11, and
the timbral lane stays velocity-at-note-on until a palette carries CC1 crossfades (see GAP-VO-02).
The first failure is a static controller. A held note whose dynamic data never moves is the acoustic
signature of a sampler, because no player sustains a note at a fixed pressure. This is the failure
our renders exhibit in its pure form — not a flat curve but no curve at all.
The second failure is subtler and is worth naming before we build the fix, because the naive
implementation lands straight in it: moving CC1 and CC11 together in lockstep. Riding both in the
same direction compounds them — the soft dynamic layer is also attenuated and the loud layer also
boosted — so the dynamic range is exaggerated beyond anything a player produces, and the result
reads as level-riding rather than as performance. The associated rule is a physical-plausibility
constraint: never program a loud dynamic layer (CC1 90–127) against a low expression value (CC11
1–40), because no instrument produces a fortissimo timbre at a pianissimo level.
CRAFT CONSENSUS, and flagged as such. This subsection was previously written around two direct
quotations attributed to practitioner sources. Neither survived verification — they are not in the
Evenant dynamics article and the VI-Control threads that plausibly carry them return HTTP 403 to
automated fetching. The MECHANISM above is sound and is corroborated in substance by Production
Expert, which warns against unnatural CC1/CC11 curve combinations and treats the two controllers as
doing different jobs. The quotations are withdrawn; the rule they supported stands on the
physical-plausibility argument, which needs no citation.
The usual professional resolution is a division of labour: CC7 set and forget; CC11 used for
correction, for fades the library's own softest layer cannot reach, and for divisi or unison
transitions; CC1 in constant motion as the actual dynamic.
PIPELINE HOOK — CODE-ABLE. A validator on the emitted controller stream: reject any frame where the
declared dynamic level and the expression scalar move in opposite senses by more than a declared
tolerance, and reject any layer whose controller stream has zero variance across a sustained note
longer than a declared length.
The concrete shapes practitioners program are all physical rather than decorative.
remedy is literal: insert small pauses and rests between phrases so a wind line is not asked to
play continuously in a way no lungs could sustain. Production Expert reaches the same place
through the controller rather than the note list, citing "an oboist or a singer who needs to
breathe between phrases" as a reason to shape CC11. Note the correction: the sources support a
GAP at the phrase seam and an expression shape around it; neither source lists "simulate
breathing" as a discrete named technique, and the earlier draft of this line overstated that.
is to fade in at the start of a bow, drop slightly through its middle, and lift again at the change
of direction; and, at the note level, to vary staccato velocities according to whether the stroke
is up-bow or down-bow, since the pressures differ. The physical premise — that bow pressure and
speed are not constant across a stroke — is not in dispute, but this lane has no primary source for
the specific envelope shape, so the bow-ripple hook below is proposed as a SHAPE TO TUNE BY EAR
rather than as a documented curve to reproduce.
crescendo, and finishing them with a decrescendo", using "the modwheel (or the expression
controller depending on the library) to create these dynamic swells and releases in your longer
notes and phrases." Production Expert adds the separate vibrato lane, noting that some libraries
put vibrato on its own controller (CC14) independent of the dynamic one.
a direct quotation about phrase ends slowing and tempos accelerating at building moments, and that
quotation could not be located in any source cited here. Rubato at phrase boundaries is
uncontroversial craft, but this lane has no verified citation for it and it grounds no rule below.
Note what the second and fourth of these imply for us. Both are PHRASE-INDEXED. Our plan schema knows
sections, cycles and patterns; it does not currently know where a phrase begins and ends, so the
breath dip has no anchor to attach to. The phrase grammar built in
docs/proposals/music/craft_research/MOTIVIC_DEVELOPMENT_AND_PHRASE_GRAMMAR.md is the natural
supplier of that anchor and is the reason this hook is cheap rather than expensive.
PIPELINE HOOK — CODE-ABLE. A per-layer expression_envelope derived from the layer's phrase
boundaries: a rise over the first fraction of the phrase, a decline over the last, a declared dip at
each phrase seam for wind and brass families, and a bow-cycle ripple for bowed strings whose period
comes from the notated slur length rather than from a clock.
The realism ceiling of a mockup is set by how many different real playing actions it can call on.
Alex Ball's stated first criterion when choosing a library is "as many variations of articulations
as possible", and he cites libraries with three distinct short-note lengths under dynamic control.
Evenant's third tip inverts the beginner's instinct: use MORE articulations and LESS legato — marcato,
détaché, runs, ricochet, portato — and observes that the brass in Star Wars, the Captain America
march and Back to the Future is played marcato and staccato most of the time with legato rare.
Spitfire Chamber Strings ships 244 articulations across 38 shorts, 53 longs, 19 FX and 45 legatos, and
supports layering articulations, switching by CC range, by keyswitch, by velocity range, by MIDI
channel and "By Speed Of Playing" with a configurable millisecond window. That last one is a machine
rule and therefore directly portable: an interval played inside a declared time window selects a
different transition.
CRAFT CONSENSUS, with a caveat for us. Our palette is CC0 and small: 85 entries, largely one or two
articulations per instrument (.sus, .stac, .pizz, .trem, .mute). We cannot buy our way to
244 articulations, so the leverage is in choosing among what we have by rule rather than fixing one
patch per layer for a whole cue — which is what the plan schema does today.
PIPELINE HOOK — CODE-ABLE. Articulation selection per NOTE rather than per layer: a rule that picks
between the .sus and .stac members of the same instrument by notated duration and by the
inter-onset interval, in the manner of Spitfire's speed-of-playing trigger. This requires the layer's
instrument field to become an instrument FAMILY with a per-note resolution, which is a schema change
in pass2_plan.py and a compile-branch change in pass2_realise.py.
The failure mode has a fixed name and a fixed cause. MusicRadar describes it as "the repetitive,
'inhuman' sound of the same sample being used over and over again", which "sounds obviously fake when
played repeatedly". The standard remedy is round robin — several takes of the same note cycled
sequentially or randomly. Spitfire's performance-legato attack layer carries 6 RR specifically to
avoid "the 'machine gun effect' or hearing the same sample again and again", verified verbatim in
the SCS manual's performance-legato section.
MusicRadar adds a rule that is directly portable to a generator and that we would otherwise have
missed: prefer an ODD round-robin count. It recommends "five or seven samples in a 4/4 piece", so the
cycle does not lock in phase with the metre — a 2- or 4-deep round robin on a 4/4 ostinato lands the
same take on the same beat every bar and reads as a deliberate accent pattern rather than as
variation. Our palette declares seq_length=2 on 202 regions, which is exactly the phase-locking
case.
Sound on Sound names the cost, which matters for us more than for anyone else: round robins are
useful but "one potential problem is that they can cause arrangements to never sound the same way
twice". This is why libraries ship a round-robin reset — the SCS manual's Expert View exposes SKIP
THIS RR, and Sound on Sound describes the general mechanism as a reset button that "resets the
instrument's round robin sequence to its start", automatable "by assigning a MIDI note outside the
playable range of the instrument to activate the button, then inserting that MIDI note at the top of
your arrangement", with the further advice to "insert the 'RR reset' note immediately before each
main section in your piece".
Our determinism guarantee survives only because sfizz resets its counter per render; that is a
property we depend on and do not currently assert, and it should become an explicit control rather
than a lucky default. Note that the practice's own reset mechanism is an AUTHORING convenience for a
DAW, while ours is a correctness dependency — which is the stronger reason to assert it.
CORRECTION LOGGED. An earlier draft of this subsection attributed to Sound on Sound the claims that a
fully-featured orchestral instrument "could easily have half a dozen round robins" and that some
EastWest string instruments carry 16. Neither statement appears in Sampled Orchestra Part 9 or Part 3,
and no round-robin count of any kind appears in either. Both were withdrawn. The surviving
deep-sampling evidence for RR counts is Spitfire's own documented 6 RR on the performance-legato
attack layer, above.
Our position, measured (1.5): round robin is present on percussion and absent on the carriers, which
is the wrong way round. Two mitigations are available without acquiring anything. The first is the
lorand/hirand and seq_position machinery already in the format, which we could add in our own
overlay SFZ files where a single instrument has multiple usable samples per key. The second is
cheaper and is what section 3.4 recommends: vary velocity note to note so that repeated notes at
least select different velocity layers where the instrument has them, which converts a machine gun
into an alternation on the 46 palette entries carrying two or more velocity bands.
PIPELINE HOOK — CODE-ABLE. A repeated-note detector on the compiled note list, plus a
velocity-alternation rule when the instrument declares more than one velocity band and no
seq_position. MEASURE-ONLY companion: a per-layer count of immediate same-pitch same-velocity
repeats, reported per cue.
Sound on Sound states the cause exactly: "Orchestral samples almost always 'speak' late, because the
producers (quite rightly) want to preserve the initial, faint stirrings of each sample's note
attack." Every serious library therefore exposes a correction. Spitfire's Expert View calls it SAMPLE
START — "If it feels loose adjust to the right, tight adjust to the left" — and the same manual's
Tightness control makes the trade explicit, noting that cutting into the note "does detract from
realism" and recommending you tighten while playing in, then loosen and put "a negative delay into
your DAW to compensate for ultimate reality". That is the DAW-side equivalent, named by the
manufacturer. The practitioner advice is to "try applying 10, 20, 30 and 40 millisecond advances to
your MIDI regions until the part sounds right", and to note that "expressive strings styles played
with a slow attack often require a surprisingly large amount of timing advancement to sit right in a
track". The correction differs per library and per articulation.
Berklee Online's twelve-week Orchestral Mockups in Film Scoring course teaches "Legato Sample
Pre-Delay" and "Sample Latency and Negative Track Delay/Offset" in Lesson 7, Strings (Part 2) — the
same lesson as long-note string technique, which is a fair statement of how routine the correction
is.
The measured spread on our palette (1.4) is 148.6 ms between the fastest and slowest speaker, which
is larger than the manual advances quoted above because those are corrections for a single library's
house latency and ours is a cross-set correction between percussion and winds.
PIPELINE HOOK — CODE-ABLE. A per-instrument speak_offset_ms, MEASURED from the palette's own
samples rather than declared, subtracted from every onset for that instrument at MIDI-write time.
The measurement is the probe already written for section 1.4 and belongs beside sfz_palette.py as a
derived table with the denominator recorded, in the same style mix_policy.derive already uses.
This is the section where craft folklore and measurement disagree most, so it is built on papers.
Between-player asynchrony in professional ensembles playing at 40–130 bpm is in the tens of
milliseconds, decreasing as tempo rises. Rasch (1979, 1988) measured standard deviations of
between-player asynchrony of 24–73 ms; a string trio averaged 49 ms SD at about 79 bpm and a wind
ensemble 32 ms at 88 bpm. Wing et al. (2014) reported 24 and 28 ms SD for two string quartets at 157
bpm.
The perceptual side bounds the other end, and the sourcing here is weaker than above, so it is
labelled. CRAFT CONSENSUS: onset-discrimination and temporal-order thresholds for musical tones are
conventionally quoted in the region of 20 ms and of roughly 15–35 ms respectively. Those figures are
NOT carried by the Wing et al. article cited for the asynchrony SDs — that paper reports the
ensemble measurements only — and this pass did not locate a primary source for them. Treat them as
an order-of-magnitude bound, not as a measured number. What the verified evidence does support
without them is the comparison that matters: professional ensembles play tens of milliseconds apart
and we play zero milliseconds apart.
Our measured spread is 0.000 ms on three quarters to nine tenths of every note in a cue.
PIPELINE HOOK — CODE-ABLE. A per-note onset perturbation whose scale is declared per stratum and per
tempo, seeded from the cue id so a render stays reproducible. The correct magnitude is NOT the
literature's raw SD — see 3.3.
Gordon (1987) defined perceptual attack time as the moment a sound's rhythmic emphasis is heard,
distinct from both its physical onset and the moment it is first audible (the perceptual onset time
of Vos and Rasch, 1981). The size of the effect is large and instrument-dependent: a closed hi-hat's
PAT is a few milliseconds after physical onset, while a slowly bowed violin note can be 50–100 ms
after it.
Bechtold and Senn (2018) measured saxophone PATs between 23 and 83 ms after physical onset across
nine sounds, with a mean per-stimulus standard deviation of 27 ms; tongue-articulated notes had
significantly earlier PAT than untongued ones (large effect), and loud sounds earlier PAT than
medium. Their negative result is the useful one for a pipeline: onset RISE TIME correlated only
weakly with PAT (r = 0.143), so PAT cannot be predicted from an envelope slope alone and must be
measured or approximated from the envelope's ARRIVAL AT A LEVEL rather than its slope.
This is why section 1.4 measured time-to-half-peak rather than time-to-onset: half-peak is a crude
but defensible proxy for the moment the note is heard to happen, and it is derivable from the sample
files we already have.
PIPELINE HOOK — CODE-ABLE. The speak_offset_ms table of 2.6 should be built from a PAT proxy
(time to a declared fraction of peak, per velocity layer) rather than from first non-silence, and the
choice of fraction declared and defended in the emitting file.
The temptation is to add jitter. The evidence does not support it, and groove.py is already right
to describe its displacement rules as "a rule that displaces, never a random jitter" — that instinct
should be preserved, not overridden, when 3.1 is implemented.
Kilchenmann and Senn (2015) took real swing and funk recordings whose participatory discrepancies
ranged from −67 to +85 ms (swing) and −46 to +58 ms (funk), and rescaled those deviations from full
quantisation to double magnitude in 20% steps, measuring expert listeners' body movement. Full
quantisation was not the best condition, but neither was the original: listeners moved MOST when the
deviations were downscaled by 60%, and the effect was small to moderate and present in experts only.
The operational reading for us is threefold. Deviation should exist. Its magnitude should be well
under the measured ensemble figures — on the order of 40% of them, which lands a 24–49 ms literature
SD at roughly 10–20 ms. And it should be structured, since the deviations that were rescaled in that
study were the systematic discrepancies of a real performance, not noise.
The structure worth encoding, and this is CRAFT CONSENSUS supported by the asynchrony literature's
leader-follower findings rather than proven by it: melody leads or lags the accompaniment
consistently rather than randomly; the bass and the timekeeping layer are the most stable; the
outer voices of a chord are laid slightly around its inner voices rather than all at once.
PIPELINE HOOK — CODE-ABLE. A deterministic per-layer timing profile — a small signed offset per
stratum plus a seeded low-magnitude per-note perturbation bounded by a declared cap — rather than a
uniform random jitter. MEASURE-ONLY companion: report the realised inter-layer onset spread
distribution per cue, so the number in 1.2 stops being 0.000 and becomes a graded axis.
Velocity carries two jobs in our stack at once — it is the accent structure AND the only timbral
dynamic control — so humanising it naively would corrupt the composition. The discipline is to
perturb it where it is currently a literal repeat and to leave the composed contour alone.
The measured target is precise: 89–91% of consecutive notes within a layer currently carry an
identical velocity (1.3). Real players do not repeat a dynamic exactly, and on our palette an
identical velocity on a repeated pitch also guarantees the identical SAMPLE, which is the machine gun
of 2.5. A bounded perturbation that never crosses the accent hierarchy — never letting an off-beat
note exceed its own downbeat, never crossing a velocity-layer boundary in a way that inverts the
composed accent — converts a literal repeat into a variant while leaving the shape intact.
PIPELINE HOOK — CODE-ABLE. A post-compile pass over compile_layer's note list: seeded, bounded,
accent-preserving velocity perturbation, with the pre- and post-perturbation accent ordering asserted
identical as a must-fire control.
pass2_realise.determinism requires two renders of the same plan to be bit-identical, and
pass5_realise inherits it. Every humanisation operation above is compatible with that, provided the
randomness is a seeded PRNG keyed on the cue id and the layer id — the same discipline
mix_policy.synth_ir already uses for the impulse response ("Deterministic on seed, so a render is
reproducible"). The determinism check must be preserved and, per 1.5, extended to assert that round
robin actually resets, since we now know the palette uses it.
PIPELINE HOOK — CODE-ABLE. A humanise_seed field on the plan; every perturbation draws from a PRNG
constructed from that seed plus the layer id, never from an unseeded default.
Spitfire's own orchestration page makes two points a generator can be held to. Strings have only five
real voices, and writing more harmonic lines forces divisi — "a smaller, thinner, and quieter
harmonic line" — which is why sampled strings sound implausibly massive: a five-note chord in a
sampled section is "likely to be producing the noise a band of 200 players would make". And octave
doubling of firsts and seconds "gives an emotional epic scale to top lines" and helps sections tune
against each other, at the cost of harmonic richness.
The generator-facing rule is that a section layer playing N simultaneous pitches is asserting a
divisi of N, and the loudness and weight of that layer should fall accordingly rather than rise.
PIPELINE HOOK — CODE-ABLE. A divisi penalty in the layer gain model: a section instrument's effective
gain per pitch scales down with simultaneous pitch count, and a declared cap refuses divisi beyond
what the named ensemble size supports.
Practitioner practice is to layer more than one library, adding principal-chair close-mic samples
under a section for a live feel, doubling with chamber strings for divisi, and switching library per
passage. The cost is alignment: stereo-field adjustments between blended string libraries are held to
about 10%, because larger corrections break the shared image, and each library's sample latency is
different, which is why 2.6 exists.
We have four CC0 sets (vsco2_ce, vcsl, legato_vocal, body_percussion) and no equivalent of a
second full orchestra, so the transferable part is the DISCIPLINE rather than the practice: when two
sets are layered on one line, their speak offsets must be equalised first and their pans aligned
within a small declared bound.
Spitfire's own recommendation is to get "samples to do what they do well (ie hold a high tremolando
ad nauseum, or play complex pizzicato passages) so you can use your live forces for stuff that
matters, like big sweeping melodies." We have no live forces, which makes this a warning rather than
a workflow: the big sweeping melody is the hardest thing to fake and it is precisely what our
melody-first direction demands, so the realism budget belongs disproportionately on the carrier
layers rather than being spread evenly across 32 of them.
CRAFT CONSENSUS. PIPELINE HOOK — CODE-ABLE. A realism budget that is layer-weighted by melodic role:
the layers holding primary melody get controller envelopes, articulation switching and speak-offset
correction first, and a cue is not "realised" until its carriers are.
Sonible's depth rules, which restate standard practice, are: level (inverse-square); relativity, since
depth is only perceived when elements occupy a VARIETY of distances rather than one; high-frequency
attenuation with distance, applied both to the source and additionally to that source's reverb send;
early reflections carrying distance information while late reflections describe the room; pre-delay,
which reads as intimacy in a small space and as distance in a large one; narrowness reading as
distance and width as proximity; and compression revealing transient detail and therefore reading as
proximity.
Our chain supplies level and a shared tail. It supplies no HF attenuation, no early reflections, no
per-element pre-delay, no width variation and no per-element dynamics. mix_policy.SPACE declares
one hf_damp per class, applied to the room and not to the sources, and one predelay_ms per class,
applied to everything equally — which by rule 2 above means it contributes no depth at all, because
depth is a difference and a constant is not one.
PIPELINE HOOK — CODE-ABLE. A per-layer distance scalar in the plan, driving four coupled
quantities at mix time: gain trim, a high-shelf attenuation, the layer's send level into the room,
and its pre-delay. One number the writer can reason about, four consequences a listener can hear.
The score-mixing practice is consistent. Alan Meyerson works with multiple reverbs per stem and
independent reverb sets for strings, brass and percussion, which "gives him space to play with and
build movement", and when choosing a reverb he is "not trying to match the original recording room
but rather build an extension" so that everything reads as one place. Berklee's mockup course
introduces "reverb as send effect" in its first two lessons and returns to routing, bussing and
multi-mic blending in lessons 8–10.
Feazelle's practitioner formulation adds the ordering and the split: convolution first to establish
early reflections, algorithmic after for tail and cohesion; shorter reverb on strings, brass and
winds, longer on percussion; and a single shared subtle bus at around 9% wet under everything to glue
it.
Our chain has one room, applied once, to the sum, at a per-class wet fraction between 0.11 and 0.26.
PIPELINE HOOK — CODE-ABLE. Restructure mix_policy.apply_space from a master-bus insert into a set
of sends: a per-stratum (or per-family) send into a per-family room, plus one shared glue send, with
the sum still measured by the same meter. The synthetic-IR licence position is unaffected — more
sends, same generator.
Meyerson uses inter-channel delay as a placement and width tool: "One of my favorite tricks is to
make use of the Haas Effect. So, if I have an instrument that is stereo in track but a bit mono in
sound, I delay one channel of that track by usually between 150 and 250 samples." At 48 kHz that is
roughly 3.1 to 5.2 ms — comfortably inside the precedence-effect window, which is the property that
makes it read as position and width rather than as an echo.
CORRECTION LOGGED, and it narrows the claim. An earlier draft of this subsection stated that Meyerson
requests separate high, mid and low string stems and that he uses delay "instead of panning to
simulate section positions". Neither survives verification. The interview describes stem reverbs
split by SECTION (strings, brass, percussion), not by register, and the Haas trick is described as a
fix for a stereo track that sounds mono — a width treatment — not as a declared substitute for
panning. The transferable finding is narrower than the withdrawn one but still decides our rule: a
first-call score mixer places and widens with small inter-channel delays in the low-single-digit
milliseconds, so amplitude pan is demonstrably not the only tool in professional practice.
Spitfire records its players in situ — "Recorded in situ (ie: in the positions they would sit as a
section)" — and its Expert View exposes a STEREO WIDTH control and a CLOSE PAN MIC COLLAPSER rather
than a simple pan knob, precisely because the position is already in the recording. The manual is
explicit that the in-situ seating gives "a fantastic spectral spread when putting all the elements
together, which helps define the detail."
Our palette is CC0 mono-ish material with no in-situ imaging, so the position has to be synthesised.
Constant-power amplitude panning, which is what _pan does, is the weakest of the available tools; a
small inter-channel delay plus a level difference is closer to how the ear locates a source in a
hall.
CRAFT CONSENSUS. PIPELINE HOOK — CODE-ABLE. Replace the pure amplitude pan with a combined
level-plus-delay placement, bounded so the delay stays inside the precedence-effect window, and
derive both from a declared seating position per family rather than from a free-floating pan number.
The practitioner numbers are conservative and consistent: avoid boosting or cutting more than 3–4 dB
and typically use 1–2 dB; cut low-frequency build-up on mid-range instruments (violins, violas,
clarinets) so the cellos, trombones and basses stay clear; and roll the low end off reverb sends to
stop the room turning to mud.
We do none of this, and we currently sum up to 34 parts with no low-end management whatsoever, which
is the most likely single contributor to a render reading as congested.
PIPELINE HOOK — CODE-ABLE. A per-family corrective EQ table with a hard bound on the permitted
magnitude, plus a high-pass on every reverb send, declared as numbers a reader can argue with in the
same style mix_policy.SPACE already uses.
Feazelle records individual parts at −12 to −6 dB so tutti passages do not overwhelm the master, and
applies subtle compression at multiple stages before the master bus rather than heavy compression at
the end. Berklee's final block is "mixbus processing, stem mastering, gain staging, limiting, dither".
mix_policy.LIMITER is well reasoned and its probe is exemplary — 6, 9 and 12 dB tested with the
whole floor battery re-run at each, and the arc unchanged to two decimals at 9 dB. Its _why_this_shape
note is also correct that the residual is a doctrine limit. What section 6 adds is that the residual
may not need closing at all.
ASWG-R001 v1.10 (August 2013), the Sony Worldwide Studios Audio Standards Working Group
recommendation, is the industry's reference document and its numbered recommendations are exact:
"and that this tolerance is acceptable considering the non-linear nature of interactive
entertainment audio content" (rec. 4).
ITU-R BS.1770-3 and EBU Tech 3341 (rec. 7).
(recs. 2 and 3).
music specifically" (rec. 9).
over a representative cross-section of gameplay (rec. 10).
filtering and surround-to-stereo downmix as processes that raise peak levels.
ARIB, and that LKFS and LUFS "are identical and interchangeable".
Recommendation 9 is the one that decides our case: the ASWG number is a property of the WHOLE
programme, not of a music asset. A music stem authored at −24 LUFS would be wrong, because at runtime
it is one of several buses summing to that figure.
Note a source disagreement, resolved in favour of the primary. Wang's 2023 master's thesis states the
Sony standard as −23 LUFS with a −2 dBTP ceiling; the ASWG document itself says −24 (±2) LKFS and −1
dBTP. The primary document is authoritative and the thesis appears to be quoting the pre-2013 revision
for the target and to be in error on the ceiling.
Wang (2023) measured nine shipped titles per audio section (LUFS):
| title | main dialogue | weapon / attack SFX | background music (combat) | background music (exploring) |
|---|---|---|---|---|
| Assassin's Creed Odyssey | −23 | −25 | −35 | −38 |
| Assassin's Creed Syndicate | −23 | −26 | −35 | −37 |
| Cyberpunk 2077 | −20 | −24 | −26 | −30 |
| Final Fantasy VII Remake | −20 | −21 | −25 | −30 |
| Ghost of Tsushima | −21 | −20 | −26 | −30 |
| Hogwarts Legacy | −20 | −21 | −23 | −30 |
| The Last of Us Part II | −20 | −20 | −30 | −38 |
| The Witcher 3 | −23 | −25 | −30 | −35 |
| Uncharted 4 | −20 | −20 | −35 | −40 |
| average | −21.1 | −22.4 | −29.4 | −34.2 |
The thesis's derived hypothesis is main dialogue −21 (±3), sound effects −23 (±3), combat music −29
(±6) and exploration music −35 (±6). Treat the exact figures with the caution the method deserves —
these are measurements of gameplay capture, not of asset files — but the STRUCTURE is the robust
finding and it is large: in shipped AAA games the music bed sits roughly 8 LU below dialogue in
combat and roughly 13 LU below it in exploration, and exploration music sits about 5 LU below combat
music.
mix_policy.derive measures commercial soundtrack ALBUM masters. Those are mastered for standalone
listening and for streaming normalisation, and the file's own note already concedes that the corpus
median "is a median of commercial masters shaped by multiband compression, saturation and clipping —
none of which this chain does". A BATTLE target of −9.84 LUFS is a plausible album number and is
about 20 LU above where combat music actually sits inside a game.
So clears_target: false on all three published cues, and the limiter built to chase it, are
answering a question about album delivery. The right question for an asset is different and has two
parts: is the cue at a stable, declared house reference so that a hundred cues sit consistently
against each other, and does it leave enough headroom that the runtime music bus can be pulled down
to its in-game level and pushed back up for a stinger without either clipping or running out of
resolution.
RECOMMENDATION, and it is a recommendation rather than a finding. Keep the corpus measurement as a
published diagnostic — it is honest evidence about what loved tracks do — and stop treating it as the
pass/fail bar for an asset. Author cue files to a single declared house reference with the true-peak
ceiling held at −1 dBTP per ASWG rec. 7, keep the corpus deviation as a reported number, and move the
in-game placement to a runtime mix document that owns the relationship between dialogue, SFX and the
music bus. The limiter then becomes an option rather than a corrective, which is the position its own
_why_this_shape note argues for anyway.
ASWG requires LRA to be reported per EBU Tech 3342. Our corpus medians run from 2.98 LU (BATTLE) to
10.66 LU (TENSION), and our published cues measure 8.45, 11.91 and 12.25 LU — wider than the corpus
in every class. That is a real and probably good property of a cue with composed drops, and it is
exactly the property a limiter chasing an album target would erode. It is another argument for
holding the limiter to the bounded, arc-verified role mix_policy already gives it.
Ordered by how much a measurement can be trusted.
layer carries continuous controller data; variance of that data per phrase; count of
physical-implausibility violations per 2.2. All computable from the emitted MIDI before a sample is
loaded.
simultaneity, in milliseconds, against the 3.1 band. Currently 0.000 everywhere.
has been corrected by the instrument's measured PAT proxy.
layer and per minute — the machine-gun index of 2.5.
distance scalar of 5.1; a mix whereevery layer is at one distance scores zero by construction, which is the current state.
mix_policy.loudness, alreadycontrolled against ffmpeg's ebur128, and already carrying LRA and true peak. Nothing to add but the
denominator discipline of 6.3.
build-up of 5.4 exists; it cannot say which layer caused it. Report as a proxy, in the register
lane_analysis.confidence already uses.
honest tier for anything this document unblocks is STRUCTURE + MEASURED PERFORMANCE DATA. A listen
remains a listen.
An independent citation-reality pass sampled 20 of the sources below, fetching each and checking that
it exists and says what this document claims. Result: the numeric spine of the document is sound and
the practitioner layer had six defects, all now repaired in place.
Confirmed verbatim, with every number checked: ASWG-R001 v1.10 (all eleven recommendations, the
considerata, and the −23 to −24 LKFS revision note); Wang 2023 (the nine-title table row for row, the
−21.1 / −22.4 / −29.4 / −34.2 averages, and the Table 3 hypothesis); Bechtold and Senn 2018 (23–83 ms
PAT, 27 ms mean SD, r = 0.143); Kilchenmann and Senn 2015 (−67/+85 ms swing, −46/+58 ms funk, 20%
steps, the 60%-reduction peak, small-to-medium, experts only); Wing et al. / PMC4196478 (Rasch's
24–73 ms range, the 49 ms trio at 79 bpm, the 32 ms wind ensemble at 88 bpm, and 24 and 28 ms at
157 bpm); the Spitfire Chamber Strings manual (every quotation, including the velocity-to-transition
map, the 6 RR, SAMPLE START, the divisi and octave-doubling passages, and "By Speed Of Playing");
Gordon 1987; Wright 2008; the Feazelle numbers; the sonible nine rules; Sound on Sound Parts 3 and 9;
and the Berklee syllabus.
Repaired: (1) a Sound on Sound attribution for round-robin counts — "half a dozen" and EastWest at 16
— that appears in neither cited part; (2) two invented machine-gun quotations attributed to
MusicRadar; (3) two invented linked-CC quotations in section 2.2; (4) a Meyerson claim about high/
mid/low string stems and delay "instead of panning", which the interview does not support; (5) an
overstatement that Production Expert lists "simulate breathing" as a discrete technique; (6) a
non-existent Cinematic Composing course title. Also tightened: the Berklee lesson numbering, and a
perceptual-threshold figure in section 3.1 that the cited paper does not carry.
Every repair narrowed a claim or withdrew a quotation. None of them changed an ADOPTED rule, because
each affected rule rests on a measured number from section 1 or on a source that survived
verification — and one verified source added a rule we had missed (7b, the odd alternation period).
CONTENT ON SONY COMPUTER ENTERTAINMENT PLATFORMS, Recommendation ASWG-R001 v1.10, August 2013.
http://gameaudiopodcast.com/ASWG-R001.pdf (7 pages; recommendations 1–11 read in full).
levels. Cited by ASWG-R001 as its measurement basis; BS.1770-4 is the version implemented in
harness/music_gen/mix_policy.py.
http://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.1770-3-201208-I!!PDF-E.pdf
http://tech.ebu.ch/docs/tech/tech3341.pdf · http://tech.ebu.ch/docs/tech/tech3342.pdf
https://sfzformat.com/opcodes/seq_position/ and https://sfzformat.com/opcodes/xfin_loccN/
https://github.com/sfztools/sfizz/issues/23
ORCHESTRATION PRINCIPLES" (divisi, octave doubling, agility, range), "LEGATO" and "PERFORMANCE
LEGATO" (velocity-to-transition mapping, 6 RR), and "THE EXPERT VIEW" (SKIP THIS RR, ADJUST
RELEASE, SAMPLE START, UACC on CC#32, articulation triggering by CC range / keyswitch / velocity /
MIDI channel / speed of playing, microphone mixer, close-mic pan collapser, stereo width).
http://spitfire-www-assets.s3.amazonaws.com/manuals/SpitfireChamberStrings_UserManual.pdf
Society of America, 82(1), 88–105. https://pubmed.ncbi.nlm.nih.gov/3624645/
29(4), 323–335.
Probability Density Functions. PhD dissertation, Stanford University (CCRMA).
https://ccrma.stanford.edu/~matt/diss/Matthew-Wright-Dissertation.pdf
of saxophone sounds. Frontiers in Psychology, 9, 1692. https://doi.org/10.3389/fpsyg.2018.01692
behavior of music expert listeners. Frontiers in Psychology, 6, 1232.
https://doi.org/10.3389/fpsyg.2015.01232
synchronisation, as summarised with the numeric SDs in: Wing, A. M., Endo, S., Yates, T., and
Bradbury, A. Perception of string quartet synchronization. PMC4196478.
https://pmc.ncbi.nlm.nih.gov/articles/PMC4196478/ — VERIFIED verbatim 2026-08-08 for the whole
numeric set used in section 3.1: asynchrony SD "typically in the 10s of milliseconds range" for
professional ensembles at 40–130 bpm; "In a string trio (violin, viola, and cello) the average
between-player asynchrony SD was 49 ms (at an average 79 bpm) while the corresponding value for a
wind ensemble (oboe, clarinet, bassoon) was 32 ms (at 88 bpm). The range was 24–73 ms"; and "Wing
et al. (2014) reported figures for asynchrony SD of 24 and 28 ms (at 157 bpm) for two string
quartets". This article does NOT carry the onset-discrimination or temporal-order thresholds, which
section 3.1 now labels CRAFT CONSENSUS accordingly. See also Optimal feedback correction in string
quartet synchronization, PMC3928944, https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3928944/
(Oxford). https://academic.oup.com/book/8555/chapter/154427776
set). https://en.wikipedia.org/wiki/Perceptual_attack_time
Loudness Standards for Each Section of Game Audio. Master Degree Project in Informatics, University
of Skövde. Supervisor Mikael Johannesson, examiner Per Backlund.
https://his.diva-portal.org/smash/get/diva2:1790653/FULLTEXT01.pdf — Tables 1–3 (per-section
loudness of nine shipped AAA titles) are the source of section 6.2.
Routledge, 2023 (ISBN 9781032406930) — on game-audio dynamic range and the final-mix stage.
https://www.routledge.com/Working-with-Sound-The-Future-of-Audio-Work-in-Interactive-Entertainment/Bridgett/p/book/9781032406930
https://www.gamedeveloper.com/audio/the-game-audio-mixing-revolution
for its lesson ordering. VERIFIED 2026-08-08, with the lesson numbers corrected from an earlier
draft: reverb as a send effect is Lesson 2 (Basic Template and Reverb Setup); "Legato Sample
Pre-Delay" and "Sample Latency and Negative Track Delay/Offset" are Lesson 7 (Strings, Part 2);
multi-mic blending is Lesson 8 (Reverb and Room Placement); routing, bussing and stem concepts are
Lesson 10 (Stereo Panning and Audio Routing/Stem Concepts); mixbus processing, stem mastering, gain
staging, limiting and dither are Lesson 11 (Final Mixing and Mastering). Lesson 12 is touchscreen
controllers and networked host setups and is not cited here.
https://online.berklee.edu/courses/orchestral-mockups-in-film-scoring
Orchestral Mockups". CORRECTION 2026-08-08: an earlier draft cited a course titled "Symphonic
Virtual Orchestration: Orchestral Mockup Production" attributed to Marc Jovani. No course of that
name appears in the catalogue and the instructor attribution could not be confirmed; the title is
withdrawn. This entry grounds no claim in the body. https://cinematiccomposing.com/course786833
the only one of the three that yielded fetchable text. VERIFIED verbatim 2026-08-08 for: "I use
multiple reverbs per stem and have the stem reverbs independent. So, my strings would have a set of
reverbs, brass another set, percussion another etc. This gives me a lot of space to play with and
build the movement"; building "an extension to that room" rather than matching the original; and
the Haas trick, "I delay one channel of that track by usually between 150 and 250 samples".
https://film-mixing.com/2016/07/28/film-score-mixing-with-alan-meyerson/
returns HTTP 403 to automated fetching and NO claim in this document rests on it.
https://mixwiththemasters.com/videos/alan-meyerson-reverb-programs
feature whose page carries no fetchable technical text, and no claim here rests on it.
https://www.soundonsound.com/people/alan-meyerson-mixing-movies
https://vi-control.net/community/threads/alan-meyerson-tips-for-mixing-sampled-orchestra.11849/
https://www.waves.com/get-more-from-reverb
Taming The Robin" is the cited text (round robins, the machine-gun effect, and the round-robin
reset mechanism). VERIFIED 2026-08-08; note that this article gives NO round-robin counts and does
not mention EastWest, contrary to an earlier draft of section 2.5.
https://www.soundonsound.com/techniques/sampled-orchestra-part9
September 2017 — the sample-latency source: samples "speak" late, negative track delay, the 10/20/
30/40 ms advance ladder, and slow-attack expressive strings needing large advancement. VERIFIED
verbatim 2026-08-08. https://www.soundonsound.com/techniques/sampled-orchestra-part3
actual scope is narrower than an earlier draft implied: it is a MIDI-CC article covering CC1 as
dynamic-layer crossfade, CC11 as expression and agogic shaping, CC64 for articulation triggering,
vibrato on its own controller (CC14 in some libraries), drawing CC curves by hand, and avoiding
unnatural curve combinations. It does NOT list "simulate breathing" as a discrete technique —
breathing appears once, as the reason to shape CC11 ("an oboist or a singer who needs to breathe
between phrases") — and it does not discuss round robins or timing humanisation at all.
https://www.production-expert.com/production-expert-1/how-to-improve-realism-in-orchestral-mock-ups
practitioner source found: EQ bounds, record levels, reverb ordering and split, 9% glue bus, 10%
stereo-field limit, vertical bar-by-bar programming). VERIFIED 2026-08-08 — every number used in
sections 4.2, 5.2, 5.4 and 5.5 was confirmed against the article, including the 3–4 dB maximum and
1–2 dB typical EQ bound (attributed there to Jake Jackson), the −12 to −6 dB part-recording level
(Marc Jovani), the convolution-then-algorithmic ordering with shorter reverbs on strings/brass/
winds and longer on percussion (Di Stefano), the 9% glue-bus mix, the 10% stereo-field adjustment
limit, and the Alex Ball articulation-first library criterion.
https://andrewfeazelle.com/how-to-make-an-orchestral-sample-library-sound-real/
to real recordings; polish your orchestration; use more articulations and less legato patches;
bring life to it with dynamics; let the virtual musicians breathe. Sources the marcato/staccato
film-brass observation and the crescendo-into / decrescendo-out-of phrase rule (tip 4, not tip 5).
It does NOT discuss tempo variation and does not describe layered dynamic-plus-vibrato controller
motion — both were attributed to it in an earlier draft and are withdrawn.
https://www.evenant.com/articles/5-tips-to-improve-your-orchestral-mockups
quotations that section 2.2 previously attributed to practitioner sources; those are withdrawn.
https://evenant.com/utilizing-dynamics-for-realism/
machine-gun definition used in section 2.5 ("the repetitive, 'inhuman' sound of the same sample
being used over and over again") and of the ODD-COUNT rule — it recommends "five or seven samples
in a 4/4 piece" so the round-robin cycle does not lock in phase with the metre. VERIFIED
2026-08-08.
https://www.musicradar.com/tuition/tech/how-to-avoid-the-machine-gun-effect-using-round-robin-sampling-632302
"Rant about cc1 cc11 and velocity"
https://vi-control.net/community/threads/rant-about-cc1-cc11-and-velocity.168479/ ;
"Is CC#11 (expression) just volume?"
https://vi-control.net/community/threads/is-cc-11-expression-just-volume.72676/ ;
"The difference between 'Dynamics' (CC#1) and 'Expression' (CC#11)?"
https://vi-control.net/community/threads/the-difference-between-dynamics-cc-1-and-expression-cc-11.58127/ ;
"Poll - How do you use Dynamics and Expression?"
https://www.vi-control.net/community/threads/poll-how-do-you-use-dynamics-and-expression.101377/ ;
"A guide to sample Delay/Latency, Offset/Negative Track Delay & Look Ahead"
https://vi-control.net/community/threads/a-guide-to-sample-delay-latency-offset-negative-track-delay-look-ahead-smart-delay.161372/ ;
"Orchestral Instruments Delay Offset"
https://vi-control.net/community/threads/orchestral-instruments-delay-offset.117434/
(Note: vi-control.net returns HTTP 403 to automated fetching; these threads were read through
search-engine extracts, and the claims drawn from them are labelled CRAFT CONSENSUS rather than
cited as primary text.)
Measured in-repo during this pass (the evidence behind section 1 and GAP NOTES; not external
sources, listed so the numbers are reproducible).
harness/music_gen/pass2_realise.py compile_layer, compile_plan, _write_midi, render, determinism; harness/music_gen/pass5_realise.py in full; harness/music_gen/mix_policy.py in
full; harness/music_gen/orchestrate.py _sfizz, _pan, _wrap_release;
harness/music_gen/complete_track.py _write_part_midi and write_score_midi;
harness/music_gen/sfz_palette.py PALETTE, SETS, PLAYER.
build/audio/pass5/plans/, compiled; build/audio/MIX_POLICY.json; build/audio/pass5/LOUDNESS_REPORT.json.
D:/audio/sfz/, parsed for opcodes and probed for envelope timing; sfizz_render 1.2.3 at D:/audio/tools/sfizz-1.2.3-win64, invoked with the pipeline's own flags.
Each rule names the file that owns it and the operation. Tier is CODE-ABLE unless marked otherwise.
harness/music_gen/pass2_realise.py (the renderer both pass 4 and pass 5 call)1. EMIT CONTROLLER DATA. _write_midi gains an expression stage that writes CC11 events for each
part. Without this, none of rules 2–5 can reach the sampler. Precedent already exists in
complete_track.write_score_midi (CC11) and complete_track._bend_events (pitch bend), which
proves the mechanism; the defect is that neither is on a render path.
2. SHAPE THE PHRASE, NOT THE CUE. The CC11 stream is derived per layer from phrase boundaries — rise
into the phrase, decline out of it, a declared dip at every phrase seam for wind and brass
families, a bow-cycle ripple for bowed strings. A single cue-wide envelope copied to every part is
the linked-CC failure of 2.2 in another costume and must be refused.
3. PLAUSIBILITY VALIDATOR. Refuse any emitted stream where a loud dynamic level coincides with a low
expression value beyond a declared tolerance, and any sustained note longer than a declared length
whose expression stream has zero variance.
4. SPEAK-OFFSET CORRECTION. Subtract a per-instrument speak_offset_ms from every onset at
MIDI-write time. The table is MEASURED from the palette's own samples (time to a declared fraction
of peak, per velocity layer) and emitted with its denominator, in the style of mix_policy.derive.
Measured spread today: 148.6 ms between fastest and slowest palette speaker, uncorrected.
5. ENSEMBLE SPREAD. Add a deterministic per-layer timing profile: a small signed per-stratum offset
plus a seeded, bounded per-note perturbation. Target a realised inter-layer spread on the order of
10–20 ms — roughly 40% of the 24–49 ms measured in real ensembles, per the Kilchenmann and Senn
downscaling result — never the literature's raw SD, and never unseeded noise.
6. VELOCITY VARIANCE, ACCENT-PRESERVING. A post-compile bounded perturbation that breaks literal
repeats without reordering the accent hierarchy, with a must-fire control asserting the accent
ordering is unchanged. Current literal-repeat rate: 89–91%.
7. REPEATED-NOTE ALTERNATION. Where an instrument declares two or more velocity bands and no
seq_position, force consecutive same-pitch notes onto different bands. This is the only
machine-gun mitigation available on 39% of our melodic layers without acquiring samples.
7b. PREFER AN ODD ALTERNATION PERIOD. Where we author overlay SFZ round robins, or where the
velocity-alternation of rule 7 sets its own period, make that period ODD relative to the metre —
MusicRadar's "five or seven samples in a 4/4 piece". An even period on a duple cycle lands the
same take on the same beat of every bar, which reads as a deliberate accent pattern rather than
as variation. Our palette declares seq_length=2 on 202 regions, so the phase-locked case is our
default case, and rule 7's naive two-band alternation would reproduce it exactly.
8. ARTICULATION PER NOTE. Resolve .sus versus .stac (and .pizz, .trem where present) per note
from notated duration and inter-onset interval, in the manner of Spitfire's speed-of-playing
trigger. Requires a schema change in pass2_plan.py from instrument-per-layer to family-per-layer.
9. FIX THE STALE CLAIM AND ARM THE CONTROL. The docstrings in pass2_realise.determinism and
orchestrate.py asserting the palette does not use round-robin opcodes are false (20 of 85 use
seq_position, 10 use lorand). Correct the text and convert the determinism check into an
explicit assertion that sfizz's sequence counter resets per render, since our reproducibility now
depends on it knowingly.
10. SEED EVERYTHING. A humanise_seed on the plan; every perturbation draws from a PRNG built from
that seed plus the layer id. Determinism is preserved by construction, as mix_policy.synth_ir
already does for the impulse response.
harness/music_gen/mix_policy.py11. DEPTH AS ONE NUMBER WITH FOUR CONSEQUENCES. A per-layer distance scalar drives gain trim, a
high-shelf attenuation, send level into the room, and pre-delay. A constant pre-delay applied to
everything contributes no depth, because depth is a difference.
12. SENDS, NOT AN INSERT ON THE SUM. Restructure apply_space into per-family sends into per-family
rooms plus one shared glue send at a small wet fraction, following the Meyerson multi-reverb model
and Feazelle's convolution-then-algorithmic ordering. The synthetic-IR licence position is
untouched: more sends, same generator, still no third-party asset.
13. HIGH-FREQUENCY DISTANCE CUE. Attenuate highs on both the source and, more strongly, its send, per
the inverse of the depth rules. This is the depth cue we are missing that costs the least.
14. CORRECTIVE EQ WITH A HARD BOUND. A per-family table, bounded at 3–4 dB and typically 1–2 dB,
cutting low-mid build-up on the mid-register instruments so the low instruments stay legible, plus
a high-pass on every reverb send. Declared as arguable numbers in the style of SPACE.
15. PLACEMENT BY LEVEL AND DELAY. Replace the pure constant-power _pan (in orchestrate.py) with a
bounded level-plus-inter-channel-delay placement derived from a declared seating position per
family.
16. SEPARATE THE DENOMINATORS. MIX_POLICY.json must carry three distinct, labelled quantities: the
corpus album median (diagnostic), the ASWG whole-programme figure of −24 (±2) LKFS home and −18
(±2) LKFS portable with the −1 dBTP ceiling (compliance, and a property of the game, not of a
cue), and a declared HOUSE ASSET REFERENCE that cue files are actually authored to.
clears_target must read against the third, not the first.
17. HOLD THE CEILING, KEEP THE HEADROOM. −1 dBTP per ASWG rec. 7, retained. The existing
_why_ceiling reasoning — that a game mix sums with SFX and dialogue at runtime — is exactly
right and is reinforced by ASWG's considerata (e) and (f) on runtime DSP and downmix headroom.
18. REPORT LRA, DO NOT CRUSH IT. LRA is an ASWG-required descriptor (rec. 3, EBU Tech 3342). Our cues
measure 8.45 to 12.25 LU against corpus medians of 2.98 to 10.66. That width is a property of
composed drops and the limiter must keep deferring to the arc battery, exactly as _why_this_shape
already specifies.
harness/music_gen/pass5_realise.py19. HONEST TIER UPDATE. TIER_NOTE currently says "no player has touched a note of it", which is
accurate and should stay. When rules 1–10 land, the tier line gains "MEASURED PERFORMANCE DATA"
and must NOT gain any claim about sounding real — no feature in the rig licenses that verdict.
20. NEW SELF-TEST CHECKS. Extend self_test past W1–W5 to assert: controller data present on every
sustained-carrier part; speak-offset table derived on this box (the same shape as the existing W3
"policy is DERIVED, not the fallback" check); and realised inter-layer onset spread non-zero.
harness/music_gen/floor_instruments.py)21. Six symbolic axes, all exact and all computable before a sample loads: controller presence and
variance, inter-layer onset spread distribution, speak-offset compliance, repeated-note index,
velocity-repetition rate, depth spread. Calibrate every threshold on the exemplar corpus before
setting it, because a target derived from our own generator's output is not a bar.
Adopted, in priority order, with the reason each earns its place.
difference between our renders and every practice surveyed, the mechanism is already proven
elsewhere in our own repo, and CC11 is measurably live on our palette today. Everything else in
this document is smaller than this.
correct deterministically, and verify — the cheapest large win available, and it costs no schema
change to the plan.
ensemble magnitudes rather than at literature scale, and as a per-stratum RULE plus small
perturbation rather than as jitter. groove.py's existing refusal of random jitter is upheld, not
overturned — the addition is a bounded seeded layer beneath composed displacement, not a
replacement for it.
in our own docstrings and should land immediately regardless of the rest. Rule 7b — the odd
alternation period — was added by the 2026-08-08 citation-verification pass and matters more than
its size suggests: without it, rule 7's two-band alternation would reproduce the same phase-locked
accent artefact it was written to remove, on every duple cue we generate.
change a decision: the loudness "failure" on all three published cues is substantially a
denominator error, and the honest fix is to publish all three numbers rather than to push a
limiter harder.
Depth applied to a performance with no dynamics only makes the flatness more audible; the ordering
matters.
immediate build. It is the highest-ceiling item here and the most invasive; it should follow the
controller lane so its benefit is measurable against a moving baseline rather than a flat one.
exist to unblock the named artifact — a cue whose realisation is believable — and each must name it.
No axis lands without the operation it grades.
Explicitly NOT adopted.
palette (0.00e+00 effect, 0 of 85 files). Revisit only if a CC1-crossfaded set enters the stack or
we author overlay SFZ files.
DISCIPLINE is adopted for the sets we do layer; the practice is not.
document licenses that. The honest tier ceiling this lane can reach is STRUCTURE + MEASURED
PERFORMANCE DATA + MEASURED MIX.
Deviations already visible between the named stack files and the practice found above. The audit
phase consumes this list. Every number was measured during this pass.
GAP-VO-01 — pass2_realise.py::_write_midi writes ZERO continuous controller events, and so does
complete_track.py::_write_part_midi. Every note in every published cue is a note-on/note-off with a
fixed velocity. The industry's primary and secondary realism tools are both simply absent from the
signal path. complete_track.py::write_score_midi DOES write a CC11 envelope but its output is the
notation seam and is never rendered — the mechanism exists in the repo and has never been connected
to a sampler. SEVERITY: highest. Everything else in this list is smaller.
GAP-VO-02 — CC1 is inert on our palette. Zero of 85 SFZ files declare locc1, xfin_locc1 or any
_oncc1 modulation, and a rendered probe of a CC1 ramp from 10 to 127 produced a max sample-wise
difference of exactly 0.00e+00 against a no-CC render on horn.sus, clarinet.sus and trombone.sus. The
practice's central instruction — keep the modwheel moving on every long note — cannot be executed on
this stack without authoring overlay SFZ files. CC7 and CC11 do work (3.5e-02 to 5.5e-02 max
difference on the same probe), so the available continuous lane is level-only, which is precisely the
"volume does not simulate dynamics because the timbre is unchanging" failure the community names.
GAP-VO-03 — compile_layer places every onset at exact grid arithmetic, so nominally simultaneous
notes from different layers carry identical floating-point timestamps. Measured inter-layer spread:
0.000 ms across 74.4%, 93.3% and 91.5% of all notes in FALLS, ROAD and ROUNDS respectively, with
clusters up to 17 layers wide. The measured norm for professional ensembles is 24–73 ms SD (Rasch),
24–28 ms at 157 bpm (Wing et al.), and the ear's onset-discrimination threshold is around 20 ms.
GAP-VO-04 — No per-instrument speak-offset correction exists anywhere in the chain. Measured on our
own sample files, time to half peak ranges from 3.3 ms (conga) to 151.9 ms (clarinet.sus), median
47.6 ms across 22 probed instruments, spread 148.6 ms. A written downbeat therefore arrives as a
staggered event with percussion first and the tune-carrying winds last by up to a seventh of a
second. Every serious library ships a correction for this (Spitfire's SAMPLE START; DAW negative
track delay) and Berklee's mockup curriculum teaches it as routine. We have none.
GAP-VO-05 — Velocity is a per-section step function. `vel = base_vel * vscale * (0.55 + 0.65 *
inten)` contains no per-note variation, and 89.4% to 91.3% of consecutive notes within a layer carry
an IDENTICAL velocity. Three layers in each cue carry three or fewer distinct velocities across the
entire piece. On an instrument with velocity layers, an identical velocity on a repeated pitch also
guarantees the identical sample, which is the machine-gun effect by construction.
GAP-VO-06 — The docstrings in pass2_realise.determinism and orchestrate.py assert that "the
palette's SFZ files do not use" round-robin and random opcodes. FALSE: 20 of 85 palette entries
declare seq_position and 10 declare lorand; the palette declares seq_length=2 on 202 regions,
=6 on 6 and =3 on 3; and a rendered eight-repeat probe on conga, bass_drum and woodblock shows
real two-way alternation with 0 of 7 identical repeats. The determinism check passes only because
sfizz resets its sequence counter per render — an undeclared dependency our reproducibility now rests
on. This is a stale-claim defect of exactly the class CLAUDE.md's verification discipline warns about
("trust content, not labels").
GAP-VO-07 — Round-robin coverage is inverted relative to need. By family: drum 7/9, rattle 2/2,
struck_wood 1/2 — versus woodwind 1/13, brass 1/7, strings 4/21, tuned_perc 1/7, plucked 0/3, voice
0/4. Across the three pass-5 cues, 11 of 28 melodic and figural layers (39%) run on an instrument
with no round robin at all. The variation we have is on the layers a listener does not follow.
GAP-VO-08 — Dynamic-layer depth is thin and unmeasured. Velocity-band census across the palette: 20
instruments have no velocity bands at all, 12 have one, 27 have two, 8 have three, 13 have four, 5
have six. So 59 of 85 have two bands or fewer, and on those the only "dynamic" reachable is amplitude.
Nothing in the pipeline records this, so a plan can assign a soaring dynamic arc to an instrument
physically incapable of expressing one.
GAP-VO-09 — Articulation is fixed per layer for a whole cue. layer["instrument"] names one SFZ file
and compile_layer never changes it, so a line cannot move between sustain and staccato as its note
lengths change. The practice runs the other way — "use more articulations, less legato" — and the
libraries ship machine rules for it (Spitfire's trigger by velocity range, by CC range, and by speed
of playing with a configurable millisecond window).
GAP-VO-10 — No note-overlap or legato handling. _write_midi writes end = start + max(dur, 0.03)
with no overlap, and the compiler comment records that "sfizz stops a sample at note-off". Legato
transition sampling is the single most-cited realism feature in every manual read here, and even
without transition samples a small deliberate overlap on slurred material versus a deliberate gap on
detached material is a distinction our renders do not make.
GAP-VO-11 — mix_policy.apply_space is one room, applied once, as an INSERT on the finished stereo
sum. Score-mixing practice is per-section sends into per-section rooms plus a shared glue bus, and
the mockup curricula introduce "reverb as a send" in their opening lessons. One room on the sum
cannot produce depth, because depth is a difference between elements and there is none.
GAP-VO-12 — The synthetic IR has no early reflections, which the code declares honestly ("a DIFFUSE
FIELD with no early-reflection geometry… That limit is declared rather than papered over"). Early
reflections are the cue that carries DISTANCE information; late reflections describe the room. So the
one spatial dimension we generate is the one that does not place instruments.
GAP-VO-13 — Pre-delay and HF damping are per CUE CLASS, not per layer: every instrument in a cue gets
the same predelay_ms (10.0 to 22.0) and the same hf_damp (0.28 to 0.55). A constant applied to
everything supplies zero depth spread. There is no per-layer distance parameter in the plan schema at
all.
GAP-VO-14 — There is no EQ anywhere in the render or master chain. A grep for filter design across
pass2_realise.py, orchestrate.py and mix_policy.py finds biquads only inside the loudness
meter's K-weighting. Cues sum up to 34 parts with no low-mid management and no high-pass on the
reverb send, against practitioner guidance to cut low build-up on mid-register instruments and to
high-pass reverb returns. Bound to observe: 1–2 dB typical, 3–4 dB maximum.
GAP-VO-15 — Positioning is amplitude pan only. orchestrate._pan is a constant-power amplitude pan
on an already-stereo stem, with a static per-layer pan number. Meyerson's documented practice adds
inter-channel delay as a placement and width tool — one channel delayed by 150 to 250 samples, about
3.1 to 5.2 ms at 48 kHz — and Spitfire records players in situ and exposes width and collapse
controls rather than a pan. Our palette carries no in-situ imaging at all, so the position must be
synthesised, and the weakest available tool is the only one in use. Scope note: the source supports
delay as a width and placement technique; it does NOT state that delay replaces panning for section
positioning, and this entry no longer claims that it does.
GAP-VO-16 — Per-layer gain is static for the whole cue. part["gain_db"] is applied once as a scalar
in render. There is no balance automation, so the practice of riding a section down when it is
accompanying and up when it takes the tune — which is most of what a score mixer does — has no
representation in the pipeline.
GAP-VO-17 — The loudness target has one denominator and reads as three. MIX_POLICY.json derives
targets from 30 commercial ALBUM masters (BATTLE −9.84 LUFS, n=5; EXPLORATION −12.18, n=8;
MELANCHOLIC −13.95, n=4, borrowed by SANCTUARY), and clears_target is false on all three published
cues (−18.81, −17.71, −17.41 LUFS). ASWG-R001 specifies −24 (±2) LKFS home and −18 (±2) LKFS portable
for the WHOLE programme, explicitly "not… dialogue, sound effects or music specifically" (rec. 9);
and measured shipped titles put the in-game music bed at −23 to −40 LUFS (average −29.4 combat, −34.2
exploring). Three quantities, three meanings, one declared. The limiter's residual-gap anxiety in
LIMITER._why_it_exists is measured against the album number and may be chasing a bar that does not
apply to a game music asset.
GAP-VO-18 — SANCTUARY has no corpus class and borrows MELANCHOLIC's target. The file names this
honestly in CLASS_MAP, and it remains a real gap: one of our three published cues is graded against
a borrowed reference with n=4.
GAP-VO-19 — No runtime headroom budget exists. ASWG's considerata (e) and (f) require headroom for
runtime resampling, filtering and surround-to-stereo downmix, and our own _why_ceiling note reaches
the same conclusion by reasoning. But nothing in the repo states how much headroom the music bus is
allocated, what the dialogue anchor is, or how a cue's authored level relates to its in-game level.
That document does not exist and this lane cannot supply it — it belongs to a runtime mix doctrine
that should be boarded.
GAP-VO-20 — LRA is measured and reported but not policed against a game-appropriate band. Published
cues measure 8.45, 11.91 and 12.25 LU against corpus class medians of 2.98 to 10.66 LU, so all three
are wider than their reference. Wider is probably correct for cues with composed drops, but the
policy's lra_min/lra_max band is derived from album masters, so clears_lra_band inherits
GAP-VO-17's denominator error.
GAP-VO-21 — pass5_realise.self_test checks the plumbing (W1–W5: correct trees, derived policy,
resolvable targets, mix_policy's own controls) and nothing about performance data. There is no check
that any part carries controller data, that any onset was speak-corrected, or that the inter-layer
spread is non-zero — so every deviation in this list can be introduced, or left in place, with the
self-test green.
GAP-VO-22 — No instrument in the grading rig measures any performance-data property. The floor
battery grades structure, melody, harmony, groove, arc and now loudness; nothing grades controller
presence, onset spread, speak-offset compliance, repeat index, velocity repetition or depth spread.
All six are exact symbolic measurements computable before a sample loads, which makes their absence
cheap to fix and expensive to keep.