AshbloomSound
Ashbloom's soundtrack. One entity carries this — `Sound`. It stands the film's nine world sources and its two music decks, then registers the `sound` stage so the director drives the whole mix off the same film clock the picture runs on.
AshbloomSound
Ashbloom's soundtrack. One entity carries this — Sound. It stands the film's
nine world sources and its two music decks, then registers the sound stage so
the director drives the whole mix off the same film clock the picture runs on.
entity.spawn("Sound").component.add("ashbloom.sound.AshbloomSound")
Nothing here decides anything: every number comes from ashbloom.sound.score,
which is a pure function of t. That is what lets the mix be read back at any
film time with score.snapshot(t) without entering play, and what keeps the
soundtrack agreeing with the rain, the growth and the glow rather than running on
a schedule of its own.
Fields
| field | default | what it does |
|---|---|---|
masterTrim | 1.0 | a trim on every source this slice owns, for balancing against a take. Leave at 1 for a film. |
active | true | turn the whole slice off without removing it |
What it stands
Eleven child entities, all named Snd_*, each carrying one Audio component:
the nine sources score.SOURCES names, plus Snd_Deck_1 and Snd_Deck_2 for
the score. They are found-or-created by name on every awake, so a hot-reload
re-binds rather than building a second copy of every sound in the film.
What it does per frame
- writes the three mixer levels from
score.channels(t)— a level can be set but never unset, so every channel is written explicitly rather than cleared, andonDestroyhands all three back at unity; - writes each source's own volume from its curve;
- moves
Snd_Mothsalongscore.mothPos(t)— the one source that travels, and why the swarm reads as an arrival and a departure; - loads, starts and stops the two decks so a cue is playing exactly across the window the score gives it.
A write is skipped when the value has not moved by more than 0.002, so a held
level is not churn on the mixer — but a gain the score has taken to zero is
written as EXACTLY zero rather than left on the deadband's last value. A source
parked at 0.001 is inaudible and still counts as a live voice, so the mix would
report eleven voices at every film time and whySilent would have no reason to
give for a source the score had deliberately taken out.
Checking it
audio.observe() is the read that matters — a source's own fields tell you what
was asked for, not what the mixer is doing.
local score = require("ashbloom.sound.score")
score.observe() -- levels, mixer, listener, device, every voice and why
audio.whySilent(entity.find("Snd_Pool").id)
Every authored source answers either as audible or as gainZero — its own curve
is at zero at that film time. Any other reason (notResident, neverStarted,
noDevice, refused, outOfRange) is a fault.
Scoped to this part · feeds back into the world's score.