Pepper.SFX

Features

Vehicle engines

Car engines from recorded RPM loops: one window builds the whole engine, every loop stays in tune with the RPM, and each car has its own values.

The engine is the sound a racing game plays most. It has to follow the RPM without steps or warble, change on and off the throttle, and every car on the track needs its own. Pepper.SFX builds an engine from loops recorded at known RPMs, keeps them in tune with each other at every RPM, and lets each car drive its own copy.

How it works

An engine is a Blend Container whose layers are your loops.

  • Regions. Give each layer its Root Value (the RPM it was recorded at) and press Auto Layout. Each loop plays in its own band of RPM and crossfades with its neighbours at equal power.
  • Pitch Follow. Each layer plays at RPM ÷ its Root Value: a loop recorded at 4000 RPM plays 1.25 times faster at 5000. The layers stay in tune with each other.
  • A Parameter asset for RPM with Smoothing (separate rise and fall), the same at any frame rate. Your code writes the raw value; the engine hears the smoothed one.
  • One engine per car. Each car posts its engine on its own Audio Emitter and sets RPM and load there. Ten cars share one engine asset.
  • On load and off load. A Blend on load crossfades an on-load set of loops with an off-load set.
  • Silent Layers = Virtualize. A layer below −60 dB (the default threshold) gives up its real voice, keeps its place in the clip and comes back in step. An engine of 14 loops with 2–4 heard at a time uses 2–4 real voices.
  • Persistent events are never stolen to make room for other sounds.

Two windows do the setup:

  • Engine Builder (full version) reads each selected loop’s set and RPM from its name (Eng_OffLoad_2510) and builds the whole tree: a looping container per loop, on-load and off-load Regions Blends with Pitch Follow and Virtualize, a load Blend over them, and one Persistent event. It never overwrites an asset.
  • Loop Analysis (Window > Audio System > Loop Analysis) measures each loop’s root pitch and the RPM it means, whether it clicks at the loop point, the level jump across it, and its loudness in LUFS. Seam ▶ plays the join on its own. It reads Decompress On Load clips only.

Example

using AudioSystem;
using UnityEngine;

[RequireComponent(typeof(AudioEmitter))]
public class CarEngineAudio : MonoBehaviour
{
    [SerializeField] private AudioEvent engineEvent;  // the Persistent event the Engine Builder made
    [SerializeField] private AudioParameter rpm;      // EngineRpm, 0–9000, with Smoothing
    [SerializeField] private AudioParameter load;     // EngineLoad01, 0–1

    public float engineRpm = 900f;  // written by your drivetrain
    public float throttle;          // 0–1
    private AudioEmitter emitter;
    private AudioHandle engine;

    void Start()
    {
        emitter = GetComponent<AudioEmitter>();
        engine = emitter.Post(engineEvent);  // reads this car's values
    }

    void Update()
    {
        emitter.SetParameter(rpm, engineRpm);  // raw: Smoothing eases it
        emitter.SetParameter(load, throttle);
    }

    public void OnCarReset() => emitter.SnapAllParameters();  // no glide after a teleport
    public void StopEngine() => engine?.Stop(0.5f);
}

Setting it up

  1. Import your loops with the RPM in each file name.
  2. Open Window > Audio System > Engine Builder (full version), select the loops and press Use Selection. In the free version, build the same Blends by hand as the cookbook’s engine recipe describes.
  3. Check each row’s set and RPM, or press Detect to measure it.
  4. Pick the RPM and Load parameters, or leave them empty to have EngineRpm and EngineLoad01 made. Pick the Target Bus and press Build.
  5. Open Window > Audio System > Audition, select the new event, press Play and add a sweep on RPM. Tune regions and Root Values by ear.
  6. Add an Audio Emitter to the car, set On Disable to Stop, and add a script like the one above.

Generated engines (full version, experimental, Unity 6000.3 or later)

The Granular Engine plays an engine from sweeps instead of loops. Window > Audio System > Engine Grains cuts an on-load and an off-load sweep into a Grain Table: one grain per engine cycle, tagged with its RPM and cut to join in phase. A fast rev passes through every RPM with no band crossfade, and your code doesn’t change. The Additive Engine adds the engine’s firing orders as sine tones under another engine, for weight.

Both use Unity 6.3’s audio generators. They are new: not yet measured on a Steam Deck (about 0.4% of a CPU core each on a desktop PC), and they hear the RPM about 25–75 ms late. They are in the full version only, like the Engine Builder. Everything else on this page, Loop Analysis included, is in both.

Good to know

  • A virtual layer fades back in over at least three frames. If an off-load set must answer a fast rev-limiter cut, set that Blend’s Silent Layers to Keep Playing.
  • Engine layers that go virtual need Doppler Level 0 or Pepper’s own Doppler to come back in step. The Engine Builder sets 0.
  • With loops from one set only there is no load Blend, so the engine sounds the same on and off the throttle.

Learn more