Tier 2 · Audio
Audio
Loudness, voice pools, mixers, 3D settings and clip imports.
Loudness, voice pools, mixers, 3D settings and clip imports.
Sound banks, voice pools, mixers, 3D settings and clip imports. save "all these effects polish and everything else into workflow and pipeline info ... so u can reuse it later for other projects". This spoke holds the reusable part of one full sound polish pass.
The polish pass, in order
- Census before any edit: every bank row and clip, measured loudness of each imported clip (
AudioClip.GetData, loudest 100 ms RMS; compressed or streamed clips per GETDATA-NEEDS-A-DECOMPRESSED-CLIP), clip lengths on frequent rows, and a Play census (voice starts, steals, pool-full share, master peak) with the listener where the game camera puts it. - Imports (AUDIO-IMPORT-PER-PLATFORM), then gains from the imported clips (SET-GAIN-FROM-THE-IMPORTED-CLIP).
- Rows: fill silent families from clips already in the project, short clips on frequent rows, per-topic cooldown, pitch and volume jitter, priority (LONG-CLIP-ON-A-FREQUENT-ROW-IS-CUT, NEW-GAIN-FIELD-IS-DB).
- Voices and 3D: per-device-class voice budget, priority stealing, rolloff from the listener distance (LISTENER-DISTANCE-SETS-THE-ROLLOFF).
- Mixer: limiter on master, ducking by routing (MIXER-GRAPH-FROM-CODE); end-of-run ring-out (END-OF-RUN-RINGS-OUT).
- Play census again against step 1, then a developer listen list: every clip choice and the mix are developer-listen-pending until a human hears them, and device memory and voice counts need a device measure.
LISTENER-DISTANCE-SETS-THE-ROLLOFF
- The AudioListener sits on the camera. In a top-down or orbit game that camera is 30 m or more from the action, so default logarithmic rolloff with min distance 1 m attenuates every world sound by about 30 dB and makes loudness depend on where units stand. Measured: -28 to -32 dB at a 31 m listener distance. Authored Linear rolloff with min distance at or above the camera distance (30-90 m) brought it to 0 to -2 dB and kept full 3D pan. Set it on the pooled voices as authored data, and re-check it after any camera distance change.
LONG-CLIP-ON-A-FREQUENT-ROW-IS-CUT
- A long clip on a row that fires often holds a voice in a small steal-oldest pool and is mostly cut. Measured: a 6.8 s clip on a weapon row was cut 94 % of the time, and 27 % of all voice starts were steals.
- Frequent rows get short clips (under about 1 s). Steal by priority, then age; drop a new play that ranks below every busy voice; pick the voice budget per device class with the same rule the effects budget uses (12 / 10 / 6 voices for desktop / high mobile / low mobile here). Measure steal share and pool-full share in a Play census before and after: 27 % to 4 % steals, 13 % to 1.1 % of samples with the pool full.
NEW-GAIN-FIELD-IS-DB
- A new serialized field loads as 0 on every existing row. A linear "volume" multiplier added that way silences the whole bank. Author per-row gain in dB (0 = unchanged) and jitter as +/- dB and +/- pitch fraction, so the default is today's behaviour.
SET-GAIN-FROM-THE-IMPORTED-CLIP
- Import settings change level: Force To Mono raised measured stereo loudness by up to about 4 dB (see FORCE-TO-MONO-GATES-NORMALIZE). Change imports first, then read loudness from the imported clip and set each row's gain against a per-topic target in dBFS. Frequent small sounds sit lowest (shots about -27, impacts -24), rare moments highest (-15 to -20). Row loudness went from an unplanned -19.8 to -5.8 spread to an authored -27 to -15.
- A source clip sitting 5 dB or more under its target is swapped, not gained: gain cannot fix a quiet click without raising its noise.
MIXER-GRAPH-FROM-CODE
- There is no public API to add mixer groups or effects. The Editor's internal
UnityEditor.Audio.AudioMixerControllerdoes it with the calls the Audio Mixer window makes:CreateNewGroup+AddChildToParent;new AudioMixerEffectController(name)+AssetDatabase.AddObjectToAsset+PreallocateGUIDs+AudioMixerGroupController.InsertEffect;SetValueForParameterwith names fromMixerEffectDefinitions.GetEffectParameters. A Send needssendTargetplusSetValueForMixLevel(0): it defaults to -80 dB, which is silent. Read the mixer back from disk after saving. - Shape that worked: a compressor on Master after the fader as a safety limiter (threshold -6 dB, attack 10 ms, release 150 ms, no make-up gain); under SFX, a Combat group carrying Duck Volume (threshold -30 dB, ratio 4, attack 0.05 s, release 0.6 s) and a Moments group with a 0 dB Send into that ducker. High-priority moments route to Moments and duck combat in the mixer, with no code timer. Keep the exposed volume parameter on the parent group so the settings slider still covers both children. Measured after: master peak -8.2 dBFS, 0 clipped frames.
AUDIO-IMPORT-PER-PLATFORM
- World SFX: Force To Mono; Vorbis quality 0.7 or more on desktop (pack clips imported at quality 0.0 are audibly degraded); Android override Vorbis 0.5 at 22,050 Hz; Decompress On Load under about 1.5 s, Compressed In Memory above. UI clicks stay stereo, ADPCM on Android for clicks under 0.25 s.
- Retire long clips nothing needs (a 6.8 s, 5 MB mp3 on a weapon row) rather than compressing them harder.
END-OF-RUN-RINGS-OUT
- A results or defeat transition that stops every voice cuts the death sound it just triggered. Measured: the tower death sound was cut at the results switch (1.5 s); with ring-out it played to 2.0 s. On that transition disconnect gameplay audio (no new plays) and let playing voices finish; stop everything on reset, session end and scene unload.
AUDIO-IMPORT-COMPARES-FIELDS-NOT-EQUALS
AudioImporterSampleSettings.Equalsnever matched a freshly read copy, so an authoring step that "reimports only when the settings changed" reimported on every run. Each reimport unloads the clip, and the next sample read failed with "Cannot read clip".- Compare loadType, compressionFormat, quality and sampleRateSetting field by field, and write only on a real difference.
- Before reading samples, call
AudioClip.LoadAudioDatain a separate step and wait untilloadStateis Loaded. The load is asynchronous, so a read in the same call can still find the clip empty.
GETDATA-NEEDS-A-DECOMPRESSED-CLIP
AudioClip.GetDatafails on clips imported as CompressedInMemory or Streaming. Those are the load types this spoke recommends for clips longer than about 1.5 s, so a loudness census that reads every clip throughGetDatabreaks on exactly the long clips.- For those clips, measure loudness from the source audio file (decode the WAV and take the loudest 100 ms RMS), then add the import's own level change: Force To Mono can raise a stereo clip up to ~4 dB (SET-GAIN-FROM-THE-IMPORTED-CLIP). Alternatively, measure a temporary DecompressOnLoad import and restore the original settings. Record in the census which method measured each clip.
HIT-IS-MEASURED-AGAINST-ITS-FRAME (agent default — unconfirmed)
- A hit plan names the frame the picture peaks and the frame the sound peaks. The picture's frame comes first. When they share a clock, the sound peaks on the next frame.
- Prove it on the played or decoded result: the listener mix (RECORDED-AUDIO-IS-THE-LISTENER-MIX in the Live play testing via Unity MCP page) or the clip sample time. When the picture clock and the audio clock differ, name the clock and both endpoints (NAME-THE-CLOCK-AND-ENDPOINTS).
- Loudness stays the dBFS rows in SET-GAIN-FROM-THE-IMPORTED-CLIP. The developer-listen step in the polish pass still stands.
- Why: the offset is a pair of frame numbers a sample can read.
CUE-TIME-IS-READ-FROM-THE-PICTURE (agent default — unconfirmed)
- A cue's time is read from the picture that plays it: an animation event, an Animator state, a Timeline marker, a tween callback, or the VFX time that effect already uses. Write the time down from that source. Read it with the gameplay mix silent, then place the clips.
- Each motion or UI cue carries one tag: cut (the frame the picture changes across the whole frame), move (the frame of peak speed), land (the frame motion stops), appear (the frame an element pops in). A cue exists for one of those actions. Gameplay rows the bank already has (shots, impacts) stay that bank.
- On a logo, a reveal, or one authored UI moment, one hero sound per about 4 seconds of that moment. World and combat voice caps stay the Audio section of the perf memory page. Ducking under a hero stays MIXER-GRAPH-FROM-CODE.
- HIT-IS-MEASURED-AGAINST-ITS-FRAME still names both frames and measures the played result. The picture frame in that rule is this derived time.
- Why: the cue time is a sample the picture already produced.
PHYSICAL-HIT-IS-A-RECORDING (agent default — unconfirmed)
- A hit, click or whoosh that stands in for a physical action is a recorded clip, or a licensed clip (CC0 when the project does not already own one). The samples come from a microphone or a library take of that action. An oscillator written to stand in for the hit stays out, and so does a joke or meme sample.
- A missing type is searched by the material and the action. Before the clip enters the bank, look at its waveform or spectrogram. Reject a clip that holds two events, a steady noise bed, clicks inside a whoosh, or clean sine lines on a physical sound.
- Music is the developer's track, or a CC0 loop the developer has accepted. Loudness of an accepted clip stays SET-GAIN-FROM-THE-IMPORTED-CLIP.
- Why: the clip is evidence of the action, and the trace is how you see that.
SOUND-FAIL-STAYS-A-FAIL (agent default — unconfirmed)
- A failing sound check is fixed by changing the cue or the clip. The check's threshold stays the number it was written with.
- Two such fixes are the cap. After the second, report every remaining FAIL with the check's output. A remaining FAIL is unfinished work. The report names it.
- At each hero cue, describe the waveform or spectrogram in view: what the trace shows at that time.
- The cap stops a retune of the check. NO-FIRST-ITERATION stays as it is: a failed check is not a finished mix.
- Why: the threshold is part of the check.
THIRD-PARTY-CLIP-HAS-A-CREDIT (agent default — unconfirmed)
- Every clip the project did not record itself has a credits line: source, license, and the file it landed in. CC0 is still named.
- The line lives in the project's credits file for third-party audio. A clip with no line stays out of the build.
- Why: the license travels with the file.
LISTEN-LIST-NAMES-FIVE-MOMENTS (agent default — unconfirmed)
- The handoff names five timecodes or moments for the developer to hear, each with its cue tag and clip.
- The report quotes the check results. The verdict is the developer's ear.
- The developer-listen step in the polish pass still stands: clip choice and the mix stay developer-listen-pending until a human hears them.
- Why: five named moments are the listen.