Make a bus of the mix at its level, so a quiet bus starts quiet

A bus of the project's mix was made at unity and then glided to its
level. Where the audio context comes with the first sound of a run,
that sound already plays on the new bus. On a bus at volume 0 it was
heard for a moment, loud, while the level glided down.

- setMix now sets the level of a bus it makes at once, without a
  glide. A bus that is there already glides to a new level as before.
- Found while building #436: the e2e check with a sound on a bus at
  volume 0 measured -7.8 dB on that bus. With the fix it is silent.

Tests:
- New unit test in sound-library: buses made at their level with no
  glide, a later change glides. Red without the fix.
- sound-library 130/130, tsc clean on this change alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-18 22:13:48 +02:00
1 parent 70402aa19a
commit dd2096e0df
2 files changed
+29 -1

No files matched your search

+13 -1
View File
@@ -909,10 +909,15 @@ export class SoundLibrary implements AudioSink {
const names = new Set(this.mix.map((bus) => bus.name));
const waits: Promise<void>[] = [];
for (const { name, volume, into, sends, fx } of this.mix) {
const made = !this.buses.has(name);
const bus = this.bus(name, into);
if (sends) this.setSends(bus, sends);
else this.route(name, bus, into);
this.glideLevel(bus, this.mixVolumes.get(name) ?? volume);
const level = this.mixVolumes.get(name) ?? volume;
// A bus made now is at its level at once: made with the context, it carries the first sound of
// a run, which would otherwise be heard through a glide down from unity (found by #436).
if (made) this.setLevel(bus, level);
else this.glideLevel(bus, level);
waits.push(this.setFx(bus, fx));
}
const gone = [...this.buses.keys()].filter(
@@ -923,6 +928,13 @@ export class SoundLibrary implements AudioSink {
await Promise.all(waits);
}
/** The level of a bus at `gain` from now on, without a glide. */
private setLevel(bus: Bus, gain: number): void {
const to = Math.max(0, gain);
bus.gain.gain.value = to;
bus.set = { ...bus.set, gain: to };
}
/** A quick glide of the level of a bus to `gain`. */
private glideLevel(bus: Bus, gain: number, time = 0, ease: EaseShare = 'linear'): void {
const to = Math.max(0, gain);
+16
View File
@@ -2539,6 +2539,22 @@ describe('the master and the buses of the project (#325)', () => {
expect(wires.filter((wire) => wire.endsWith('> destination'))).toEqual(['panner > destination']);
});
it('makes a bus at its level, so the first sound of a run on a quiet bus is not heard gliding down (#436)', async () => {
const library = new SoundLibrary(false);
await library.setMix(mix);
await play(library);
expect(library.busLevels().map(({ name, level }) => [name, level])).toEqual([
['mix', 0.5],
['mix:a', 1],
['mix:b', 0.25],
]);
expect(gains.filter((gain) => gain.glides.length > 0)).toEqual([]);
// One there already glides to a new level, as before.
await library.setMix([bus('mix', 0.25, null), mix[1]!, mix[2]!]);
expect(library.busLevels()[0]).toMatchObject({ gain: 0.25, level: 0.5 });
expect(gains.some((gain) => gain.glides.includes(0.25))).toBe(true);
});
it('keeps the mix where the first thing heard is a tab of a song, which names its bus before there is a context', async () => {
const library = new SoundLibrary(false);
await library.setMix(mix);