A blended engine loop
Open the live demo · Read the source · View on GitHub
An engine's note is one or more recordings, each at a different rev range,
crossfaded by whatever number is driving them. A single stretched recording
sounds like a siren at the ends; BlendedLoop mixes a handful of loops
instead, weighted so the change from one to the next is not audible.
Step 1: Bands, each centred on the value it was recorded at #
LoopBand.centre is what a recording actually is: a loop taken at three
quarters of the revs has a centre of 0.75.
_engine = BlendedLoop(
scene: _scene,
bands: <LoopBand>[
LoopBand(
sound: const SoundDef(name: 'idle', asset: 'idle.wav', loop: true),
centre: 0.2,
),
LoopBand(
sound: const SoundDef(name: 'mid', asset: 'mid.wav', loop: true),
centre: 0.6,
),
LoopBand(
sound: const SoundDef(name: 'high', asset: 'high.wav', loop: true),
centre: 1.0,
),
],
);
Step 2: Drive it every frame #
update takes the current value and mixes the bands' gains and playback
rates to match it.
@override
void update(DemoContext context, double dt) {
_engine.update(revs);
_scene.update(AudioListener());
}
Move the slider and the mix shifts smoothly between the idle, mid and high recordings.
Step 3: Check the loudness stays level #
// The bands are normalised, so the total weight is one wherever the
// revs sit, including exactly between two bands.
revs = 0.4;
_engine.update(revs);
var total = 0.0;
for (var i = 0; i < 3; i++) {
total += _engine.gainOf(i);
}
The weights are normalised, so the three bands' gains always add up to full loudness, even sitting exactly between two of them — without that, the middle of a crossfade would read as the engine cutting out.