|
1 | | -import { attachElementFxChain } from "./audioFx.js"; |
2 | | -import type { AutomationTiming } from "../audio/audioFxAutomation.js"; |
| 1 | +import { attachElementFxChain, readElementAutomation } from "./audioFx.js"; |
| 2 | +import { |
| 3 | + scheduleParamLane, |
| 4 | + volumeLane, |
| 5 | + type AutomationTiming, |
| 6 | +} from "../audio/audioFxAutomation.js"; |
| 7 | +import { VOLUME_RANGE } from "../audioAutomation.js"; |
3 | 8 | import { swallow } from "./diagnostics"; |
4 | 9 | import { getDebugSurface } from "./globals.js"; |
5 | 10 |
|
@@ -58,6 +63,20 @@ function startBoundedSource( |
58 | 63 | return true; |
59 | 64 | } |
60 | 65 |
|
| 66 | +/** |
| 67 | + * The volume lane rides the fader, after the effects — where a DAW puts it, |
| 68 | + * and the order the render bakes it in. |
| 69 | + */ |
| 70 | +function scheduleVolumeLane( |
| 71 | + el: HTMLMediaElement, |
| 72 | + gainNode: GainNode, |
| 73 | + timing: AutomationTiming, |
| 74 | +): void { |
| 75 | + const lane = volumeLane(readElementAutomation(el)); |
| 76 | + if (!lane) return; |
| 77 | + scheduleParamLane([{ param: gainNode.gain }], lane, VOLUME_RANGE.scale, timing); |
| 78 | +} |
| 79 | + |
61 | 80 | export type ScheduledSource = { |
62 | 81 | el: HTMLMediaElement; |
63 | 82 | sourceNode: AudioBufferSourceNode; |
@@ -200,6 +219,8 @@ export class WebAudioTransport { |
200 | 219 | const fx = attachElementFxChain(this._ctx, el, sourceNode, gainNode, timing); |
201 | 220 | gainNode.connect(this._masterGain); |
202 | 221 |
|
| 222 | + scheduleVolumeLane(el, gainNode, timing); |
| 223 | + |
203 | 224 | this._rate = safeRate; |
204 | 225 | this._rateAnchorCtx = scheduledAt; |
205 | 226 | this._rateAnchorComp = compositionTime; |
|
0 commit comments