@woosh/meep-engine 3.46.0 → 3.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/editor/actions/concrete/TransformModifyAction.d.ts.map +1 -1
- package/editor/actions/concrete/TransformModifyAction.js +10 -2
- package/editor/ecs/component/FieldDescriptor.js +15 -0
- package/editor/ecs/component/createObjectEditor.js +41 -12
- package/editor/ecs/component/editors/ecs/Transform64Editor.d.ts.map +1 -1
- package/editor/ecs/component/editors/ecs/Transform64Editor.js +77 -70
- package/editor/tools/TransformToolV2.d.ts.map +1 -1
- package/editor/tools/TransformToolV2.js +4 -0
- package/editor/tools/v2/TransformControls.d.ts.map +1 -1
- package/editor/tools/v2/TransformControls.js +7 -0
- package/editor/view/ecs/EntityEditor.d.ts.map +1 -1
- package/editor/view/ecs/EntityEditor.js +4 -2
- package/package.json +1 -1
- package/src/core/collection/array/array_insert_at.d.ts +16 -0
- package/src/core/collection/array/array_insert_at.d.ts.map +1 -0
- package/src/core/collection/array/array_insert_at.js +32 -0
- package/src/core/collection/array/array_remove_at.d.ts +15 -0
- package/src/core/collection/array/array_remove_at.d.ts.map +1 -0
- package/src/core/collection/array/array_remove_at.js +30 -0
- package/src/core/geom/3d/quaternion/quat3_look_rotation.d.ts +37 -0
- package/src/core/geom/3d/quaternion/quat3_look_rotation.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/quat3_look_rotation.js +172 -0
- package/src/core/geom/Quaternion.d.ts +1 -0
- package/src/core/geom/Quaternion.d.ts.map +1 -1
- package/src/core/geom/Quaternion.js +12 -48
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +9 -4
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +10 -0
- package/src/engine/ecs/transform/t64_look_rotation.d.ts +4 -4
- package/src/engine/ecs/transform/t64_look_rotation.d.ts.map +1 -1
- package/src/engine/ecs/transform/t64_look_rotation.js +7 -18
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +30 -1
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +6 -0
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/GPUParticleEmitterSystem.js +72 -29
- package/src/engine/input/devices/KeyboardDevice.d.ts +1 -0
- package/src/engine/input/devices/KeyboardDevice.d.ts.map +1 -1
- package/src/engine/input/devices/KeyboardDevice.js +7 -6
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts.map +1 -1
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +15 -2
- package/src/engine/network/CONGESTION_CONTROL.md +9 -9
- package/src/engine/network/MESSAGE_TRANSPORT.md +364 -0
- package/src/engine/network/NetworkSession.d.ts +31 -11
- package/src/engine/network/NetworkSession.d.ts.map +1 -1
- package/src/engine/network/NetworkSession.js +453 -286
- package/src/engine/network/README.md +77 -27
- package/src/engine/network/SMOOTHNESS_DESIGN.md +13 -5
- package/src/engine/network/diagnostics/BandwidthMeter.d.ts +1 -1
- package/src/engine/network/diagnostics/BandwidthMeter.js +1 -1
- package/src/engine/network/orchestrator/HostJoin.d.ts +111 -0
- package/src/engine/network/orchestrator/HostJoin.d.ts.map +1 -0
- package/src/engine/network/orchestrator/HostJoin.js +271 -0
- package/src/engine/network/orchestrator/NetworkPeer.d.ts +151 -112
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +450 -499
- package/src/engine/network/orchestrator/PendingSlices.d.ts +30 -0
- package/src/engine/network/orchestrator/PendingSlices.d.ts.map +1 -0
- package/src/engine/network/orchestrator/PendingSlices.js +83 -0
- package/src/engine/network/orchestrator/ReliableFrames.d.ts +34 -0
- package/src/engine/network/orchestrator/ReliableFrames.d.ts.map +1 -0
- package/src/engine/network/orchestrator/ReliableFrames.js +96 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +77 -10
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +236 -61
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +20 -6
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +33 -8
- package/src/engine/network/replication/Replicator.d.ts.map +1 -1
- package/src/engine/network/replication/Replicator.js +7 -1
- package/src/engine/network/sim/ActionLog.d.ts +32 -5
- package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
- package/src/engine/network/sim/ActionLog.js +47 -5
- package/src/engine/network/sim/SimActionExecutor.d.ts +42 -2
- package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
- package/src/engine/network/sim/SimActionExecutor.js +100 -4
- package/src/engine/network/sim/Snapshotter.d.ts +12 -0
- package/src/engine/network/sim/Snapshotter.d.ts.map +1 -1
- package/src/engine/network/sim/Snapshotter.js +43 -0
- package/src/engine/network/state/Baseline.d.ts +1 -1
- package/src/engine/network/state/Baseline.js +1 -1
- package/src/engine/network/state/FrameBufferRing.d.ts +25 -6
- package/src/engine/network/state/FrameBufferRing.d.ts.map +1 -1
- package/src/engine/network/state/FrameBufferRing.js +51 -6
- package/src/engine/network/state/InputRing.d.ts +4 -0
- package/src/engine/network/state/InputRing.d.ts.map +1 -1
- package/src/engine/network/state/InputRing.js +7 -0
- package/src/engine/network/transport/DeferredTransport.d.ts.map +1 -1
- package/src/engine/network/transport/DeferredTransport.js +1 -0
- package/src/engine/network/transport/LoopbackTransport.d.ts.map +1 -1
- package/src/engine/network/transport/LoopbackTransport.js +4 -2
- package/src/engine/network/transport/Transport.d.ts +7 -0
- package/src/engine/network/transport/Transport.d.ts.map +1 -1
- package/src/engine/network/transport/Transport.js +8 -0
- package/src/engine/network/transport/adapters/NodeUDPTransport.d.ts.map +1 -1
- package/src/engine/network/transport/adapters/NodeUDPTransport.js +20 -5
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.d.ts.map +1 -1
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +8 -0
- package/src/engine/network/transport/adapters/WebTransportTransport.d.ts.map +1 -1
- package/src/engine/network/transport/adapters/WebTransportTransport.js +6 -0
- package/src/engine/network/transport/connection/Connection.d.ts +191 -0
- package/src/engine/network/transport/connection/Connection.d.ts.map +1 -0
- package/src/engine/network/transport/connection/Connection.js +1262 -0
- package/src/engine/network/transport/connection/RangeSet.d.ts +94 -0
- package/src/engine/network/transport/connection/RangeSet.d.ts.map +1 -0
- package/src/engine/network/transport/connection/RangeSet.js +260 -0
- package/src/engine/network/transport/connection/RxStream.d.ts +48 -0
- package/src/engine/network/transport/connection/RxStream.d.ts.map +1 -0
- package/src/engine/network/transport/connection/RxStream.js +211 -0
- package/src/engine/network/transport/connection/TxStream.d.ts +90 -0
- package/src/engine/network/transport/connection/TxStream.d.ts.map +1 -0
- package/src/engine/network/transport/connection/TxStream.js +243 -0
- package/src/engine/network/transport/connection/bytes_copy.d.ts +17 -0
- package/src/engine/network/transport/connection/bytes_copy.d.ts.map +1 -0
- package/src/engine/network/transport/connection/bytes_copy.js +34 -0
- package/src/engine/network/transport/connection/varint.d.ts +46 -0
- package/src/engine/network/transport/connection/varint.d.ts.map +1 -0
- package/src/engine/network/transport/connection/varint.js +107 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/sound/ecs/audio/AudioEmitter.d.ts +38 -7
- package/src/engine/sound/ecs/audio/AudioEmitter.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitter.js +35 -7
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationAdapter.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationAdapter.js +5 -1
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.d.ts +18 -0
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.js +48 -0
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts +12 -2
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.js +173 -9
- package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts +9 -3
- package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/LiveEmitterSet.js +28 -8
- package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts +8 -0
- package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts.map +1 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.d.ts.map +1 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.js +5 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.d.ts +18 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.js +51 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.js +15 -1
- package/src/engine/sound/sopra/README.md +768 -644
- package/src/engine/sound/sopra/SopraEngine.d.ts +104 -8
- package/src/engine/sound/sopra/SopraEngine.d.ts.map +1 -1
- package/src/engine/sound/sopra/SopraEngine.js +327 -12
- package/src/engine/sound/sopra/definition/EventDescription.d.ts +28 -2
- package/src/engine/sound/sopra/definition/EventDescription.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/EventDescription.js +55 -5
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts +10 -0
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +77 -55
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.d.ts +31 -0
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.js +75 -0
- package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts +38 -9
- package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +34 -5
- package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts +7 -2
- package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +137 -122
- package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts +3 -1
- package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/EqEffect.js +146 -131
- package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts +1 -0
- package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +6 -1
- package/src/engine/sound/sopra/runtime/BusGraph.d.ts +44 -2
- package/src/engine/sound/sopra/runtime/BusGraph.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/BusGraph.js +83 -43
- package/src/engine/sound/sopra/runtime/EffectChain.d.ts +96 -0
- package/src/engine/sound/sopra/runtime/EffectChain.d.ts.map +1 -0
- package/src/engine/sound/sopra/runtime/EffectChain.js +286 -0
- package/src/engine/sound/sopra/runtime/EffectParamOverrides.d.ts +123 -0
- package/src/engine/sound/sopra/runtime/EffectParamOverrides.d.ts.map +1 -0
- package/src/engine/sound/sopra/runtime/EffectParamOverrides.js +320 -0
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts +55 -5
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.js +201 -26
- package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts +2 -1
- package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts.map +1 -1
- package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +6 -1
- package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts +15 -0
- package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts.map +1 -1
- package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +32 -0
- package/src/engine/sound/sopra/util/MockAudioContext.d.ts.map +1 -1
- package/src/engine/sound/sopra/util/MockAudioContext.js +3 -0
- package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts +13 -3
- package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts.map +1 -1
- package/src/engine/sound/sopra/util/fadeOutAndStop.js +25 -13
- package/src/shade/RENDERER_CONTRACT.md +6 -3
- package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts +24 -0
- package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts.map +1 -0
- package/src/shade/device/SHADE_DEVICE_FLOOR.js +34 -0
- package/src/shade/device/ShadeDeviceFailure.d.ts +12 -5
- package/src/shade/device/ShadeDeviceFailure.d.ts.map +1 -1
- package/src/shade/device/ShadeDeviceFailure.js +17 -8
- package/src/shade/device/ShadeDeviceFailureReason.d.ts +1 -1
- package/src/shade/device/ShadeDeviceFailureReason.js +2 -2
- package/src/shade/device/shade_device_floor_unmet.d.ts +13 -0
- package/src/shade/device/shade_device_floor_unmet.d.ts.map +1 -0
- package/src/shade/device/shade_device_floor_unmet.js +32 -0
- package/src/shade/renderer/Renderer.d.ts +2 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +29 -55
- package/src/shade/renderer/buffer/GPUBufferAllocator.d.ts.map +1 -1
- package/src/shade/renderer/buffer/GPUBufferAllocator.js +9 -0
- package/src/shade/renderer/buffer/buffer_pool_ops.d.ts +21 -12
- package/src/shade/renderer/buffer/buffer_pool_ops.d.ts.map +1 -1
- package/src/shade/renderer/buffer/buffer_pool_ops.js +18 -19
- package/src/shade/renderer/gpu_primitive/test/gpu_test.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/test/gpu_test.js +8 -11
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/pool/TransientResourcePool.d.ts +24 -0
- package/src/shade/renderer/pool/TransientResourcePool.d.ts.map +1 -1
- package/src/shade/renderer/pool/TransientResourcePool.js +41 -10
- package/src/shade/renderer/shader/ComputeShader.d.ts.map +1 -1
- package/src/shade/renderer/shader/ComputeShader.js +18 -2
- package/src/shade/renderer/shader/ImageShader.d.ts.map +1 -1
- package/src/shade/renderer/shader/ImageShader.js +230 -218
- package/src/view/graphics/GraphicsContextFailureView.d.ts +1 -2
- package/src/view/graphics/GraphicsContextFailureView.d.ts.map +1 -1
- package/src/view/graphics/GraphicsContextFailureView.js +1 -2
- package/src/engine/network/transport/Channel.js +0 -336
- package/src/engine/network/transport/ReliableCommandPipeline.js +0 -335
- package/src/engine/network/transport/fragments/FragmentAssembler.js +0 -350
- package/src/engine/network/transport/fragments/FragmentRetention.js +0 -191
- package/src/engine/network/transport/fragments/fragment_send.js +0 -157
- package/src/engine/network/transport/fragments/packet_size.js +0 -107
|
@@ -12,6 +12,10 @@ export const BUS_ROOT_PARENT: string;
|
|
|
12
12
|
*
|
|
13
13
|
* Each bus is wired `input → effect[0] → … → effect[n] → gain(output)`; the output then connects to
|
|
14
14
|
* its parent bus's input, or to the engine destination for a root bus (parentId === "").
|
|
15
|
+
*
|
|
16
|
+
* A bus's inserts are an {@link EffectChain}, and their parameters move through {@link setEffectParam} /
|
|
17
|
+
* {@link approachEffectParam} — the same two verbs as the fader's {@link setVolume} / {@link approachVolume}.
|
|
18
|
+
* The graph's nodes stay its own.
|
|
15
19
|
*/
|
|
16
20
|
export class BusGraph {
|
|
17
21
|
/**
|
|
@@ -20,13 +24,16 @@ export class BusGraph {
|
|
|
20
24
|
*/
|
|
21
25
|
constructor(ctx: BaseAudioContext, destination: AudioNode);
|
|
22
26
|
/**
|
|
23
|
-
* id ->
|
|
24
|
-
* @type {Map<string, {definition: BusDefinition, input: AudioNode, output: GainNode}>}
|
|
27
|
+
* id -> the bus's live record
|
|
28
|
+
* @type {Map<string, {definition: BusDefinition, input: AudioNode, output: GainNode, nominal: number, effects: EffectChain|null, sends: GainNode[]}>}
|
|
25
29
|
*/
|
|
26
30
|
buses: Map<string, {
|
|
27
31
|
definition: BusDefinition;
|
|
28
32
|
input: AudioNode;
|
|
29
33
|
output: GainNode;
|
|
34
|
+
nominal: number;
|
|
35
|
+
effects: EffectChain | null;
|
|
36
|
+
sends: GainNode[];
|
|
30
37
|
}>;
|
|
31
38
|
/**
|
|
32
39
|
* Build the graph from a flat list of bus definitions. Order-independent: all nodes are created
|
|
@@ -69,6 +76,40 @@ export class BusGraph {
|
|
|
69
76
|
* @returns {AudioNode}
|
|
70
77
|
*/
|
|
71
78
|
getOutput(id: string): AudioNode;
|
|
79
|
+
/**
|
|
80
|
+
* Move a parameter of one of a bus's insert effects, now. See {@link EffectChain#setParam}.
|
|
81
|
+
* @param {string} id bus
|
|
82
|
+
* @param {number} effectIndex position in the bus definition's `effects`
|
|
83
|
+
* @param {string} name one of that effect class's `parameters` (`'frequency'` on an `EqEffect`)
|
|
84
|
+
* @param {number} value in the parameter's own units
|
|
85
|
+
*/
|
|
86
|
+
setEffectParam(id: string, effectIndex: number, name: string, value: number): void;
|
|
87
|
+
/**
|
|
88
|
+
* Glide a parameter of one of a bus's insert effects toward `value`. See {@link EffectChain#approachParam}.
|
|
89
|
+
* @param {string} id bus
|
|
90
|
+
* @param {number} effectIndex
|
|
91
|
+
* @param {string} name
|
|
92
|
+
* @param {number} value
|
|
93
|
+
* @param {number} timeConstant seconds, above 0
|
|
94
|
+
*/
|
|
95
|
+
approachEffectParam(id: string, effectIndex: number, name: string, value: number, timeConstant: number): void;
|
|
96
|
+
/**
|
|
97
|
+
* Where a bus effect's parameter was last sent (a glide's target, not where it has got to).
|
|
98
|
+
* @param {string} id bus
|
|
99
|
+
* @param {number} effectIndex
|
|
100
|
+
* @param {string} name
|
|
101
|
+
* @returns {number}
|
|
102
|
+
*/
|
|
103
|
+
getEffectParam(id: string, effectIndex: number, name: string): number;
|
|
104
|
+
/**
|
|
105
|
+
* Whether this graph has bus `id`, with an insert at `effectIndex` exposing a parameter `name` — an
|
|
106
|
+
* address {@link setEffectParam} would take. Asks; never throws.
|
|
107
|
+
* @param {string} id bus
|
|
108
|
+
* @param {number} effectIndex
|
|
109
|
+
* @param {string} name
|
|
110
|
+
* @returns {boolean}
|
|
111
|
+
*/
|
|
112
|
+
hasEffectParam(id: string, effectIndex: number, name: string): boolean;
|
|
72
113
|
/**
|
|
73
114
|
* @param {string} id
|
|
74
115
|
* @returns {number} linear gain
|
|
@@ -116,4 +157,5 @@ export class BusGraph {
|
|
|
116
157
|
#private;
|
|
117
158
|
}
|
|
118
159
|
export type BusDefinition = import("../definition/BusDefinition.js").BusDefinition;
|
|
160
|
+
import { EffectChain } from "./EffectChain.js";
|
|
119
161
|
//# sourceMappingURL=BusGraph.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BusGraph.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/sound/sopra/runtime/BusGraph.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"BusGraph.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/sound/sopra/runtime/BusGraph.js"],"names":[],"mappings":"AAKA;;GAEG;AAEH;;;;GAIG;AACH,8BAFU,MAAM,CAEkB;AAElC;;;;;;;;;GASG;AACH;IAkBI;;;OAGG;IACH,iBAHW,gBAAgB,eAChB,SAAS,EAKnB;IAvBD;;;OAGG;IACH,OAFU,IAAI,MAAM,EAAE;QAAC,UAAU,EAAE,aAAa,CAAC;QAAC,KAAK,EAAE,SAAS,CAAC;QAAC,MAAM,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,GAAC,IAAI,CAAC;QAAC,KAAK,EAAE,QAAQ,EAAE,CAAA;KAAC,CAAC,CAEnI;IAqBlB;;;;OAIG;IACH,mBAFW,aAAa,EAAE,QAqEzB;IAED;;;;;;;;;;;;;;;OAeG;IACH,gBAcC;IAED;;;OAGG;IACH,QAHW,MAAM,GACJ,OAAO,CAInB;IAoBD;;;;OAIG;IACH,aAHW,MAAM,GACJ,SAAS,CAIrB;IAED;;;;;OAKG;IACH,cAHW,MAAM,GACJ,SAAS,CAIrB;IAkBD;;;;;;OAMG;IACH,mBALW,MAAM,eACN,MAAM,QACN,MAAM,SACN,MAAM,QAIhB;IAED;;;;;;;OAOG;IACH,wBANW,MAAM,eACN,MAAM,QACN,MAAM,SACN,MAAM,gBACN,MAAM,QAIhB;IAED;;;;;;OAMG;IACH,mBALW,MAAM,eACN,MAAM,QACN,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;;OAOG;IACH,mBALW,MAAM,eACN,MAAM,QACN,MAAM,GACJ,OAAO,CAMnB;IAED;;;OAGG;IACH,cAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;;;;;;;;;;OAeG;IACH,cAHW,MAAM,UACN,MAAM,QAYhB;IAED;;;OAGG;IACH,gBAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;;OAOG;IACH,eALW,MAAM,UACN,MAAM,YACN,MAAM,eACN,MAAM,QAOhB;IAED;;;;;;OAMG;IACH,mBAJW,MAAM,UACN,MAAM,gBACN,MAAM,QAQhB;;CACJ;4BA/UY,OAAO,gCAAgC,EAAE,aAAa;4BAHvC,kBAAkB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { dB2Volume } from "../../dB2Volume.js";
|
|
2
2
|
import { volume2dB } from "../../volume2dB.js";
|
|
3
|
-
import { rampGain } from "../util/fadeOutAndStop.js";
|
|
3
|
+
import { cancelAndHold, rampGain } from "../util/fadeOutAndStop.js";
|
|
4
|
+
import { EffectChain } from "./EffectChain.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {import("../definition/BusDefinition.js").BusDefinition} BusDefinition
|
|
@@ -18,12 +19,16 @@ export const BUS_ROOT_PARENT = "";
|
|
|
18
19
|
*
|
|
19
20
|
* Each bus is wired `input → effect[0] → … → effect[n] → gain(output)`; the output then connects to
|
|
20
21
|
* its parent bus's input, or to the engine destination for a root bus (parentId === "").
|
|
22
|
+
*
|
|
23
|
+
* A bus's inserts are an {@link EffectChain}, and their parameters move through {@link setEffectParam} /
|
|
24
|
+
* {@link approachEffectParam} — the same two verbs as the fader's {@link setVolume} / {@link approachVolume}.
|
|
25
|
+
* The graph's nodes stay its own.
|
|
21
26
|
*/
|
|
22
27
|
export class BusGraph {
|
|
23
28
|
|
|
24
29
|
/**
|
|
25
|
-
* id ->
|
|
26
|
-
* @type {Map<string, {definition: BusDefinition, input: AudioNode, output: GainNode}>}
|
|
30
|
+
* id -> the bus's live record
|
|
31
|
+
* @type {Map<string, {definition: BusDefinition, input: AudioNode, output: GainNode, nominal: number, effects: EffectChain|null, sends: GainNode[]}>}
|
|
27
32
|
*/
|
|
28
33
|
buses = new Map();
|
|
29
34
|
|
|
@@ -63,44 +68,21 @@ export class BusGraph {
|
|
|
63
68
|
gain.gain.setValueAtTime(nominal, 0);
|
|
64
69
|
|
|
65
70
|
let input = gain;
|
|
71
|
+
let effects = null;
|
|
66
72
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const chain = [];
|
|
70
|
-
|
|
71
|
-
const effects = def.effects;
|
|
72
|
-
|
|
73
|
-
if (effects.length > 0) {
|
|
74
|
-
let previousOutput = null;
|
|
75
|
-
|
|
76
|
-
for (let e = 0; e < effects.length; e++) {
|
|
77
|
-
const built = effects[e].build(ctx);
|
|
78
|
-
|
|
79
|
-
chain.push(built.input);
|
|
73
|
+
if (def.effects.length > 0) {
|
|
74
|
+
effects = new EffectChain(ctx, def.effects);
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (previousOutput === null) {
|
|
86
|
-
// first effect is the chain head
|
|
87
|
-
input = built.input;
|
|
88
|
-
} else {
|
|
89
|
-
previousOutput.connect(built.input);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
previousOutput = built.output;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// last effect feeds the bus gain (output)
|
|
96
|
-
previousOutput.connect(gain);
|
|
76
|
+
// the inserts' tail feeds the bus gain (output); their head is the bus input
|
|
77
|
+
effects.output.connect(gain);
|
|
78
|
+
input = effects.input;
|
|
97
79
|
}
|
|
98
80
|
|
|
99
81
|
// `nominal` is the authoritative logical fader gain (linear), tracked separately from the
|
|
100
82
|
// node's instantaneous `.value` — which WebAudio does NOT update under ramps/setTargetAtTime.
|
|
101
83
|
// get/setVolume read/write `nominal`; ducking (approachVolume) is a transient modulation that
|
|
102
84
|
// deliberately does NOT touch it, so the duck can capture + restore the true nominal.
|
|
103
|
-
this.buses.set(def.id, { definition: def, input, output: gain, nominal,
|
|
85
|
+
this.buses.set(def.id, { definition: def, input, output: gain, nominal, effects, sends: [] });
|
|
104
86
|
}
|
|
105
87
|
|
|
106
88
|
// pass 2: route each bus output to its parent's input (or the engine destination)
|
|
@@ -163,8 +145,8 @@ export class BusGraph {
|
|
|
163
145
|
for (const bus of this.buses.values()) {
|
|
164
146
|
bus.output.disconnect();
|
|
165
147
|
|
|
166
|
-
|
|
167
|
-
bus.
|
|
148
|
+
if (bus.effects !== null) {
|
|
149
|
+
bus.effects.disconnect();
|
|
168
150
|
}
|
|
169
151
|
|
|
170
152
|
for (let i = 0; i < bus.sends.length; i++) {
|
|
@@ -189,7 +171,7 @@ export class BusGraph {
|
|
|
189
171
|
* factored here instead of being repeated verbatim in each accessor — one place owns the message and
|
|
190
172
|
* the "unknown bus is fatal" policy.
|
|
191
173
|
* @param {string} id
|
|
192
|
-
* @returns {{definition: BusDefinition, input: AudioNode, output: GainNode}}
|
|
174
|
+
* @returns {{definition: BusDefinition, input: AudioNode, output: GainNode, nominal: number, effects: EffectChain|null, sends: GainNode[]}}
|
|
193
175
|
*/
|
|
194
176
|
#bus(id) {
|
|
195
177
|
const bus = this.buses.get(id);
|
|
@@ -220,6 +202,70 @@ export class BusGraph {
|
|
|
220
202
|
return this.#bus(id).output;
|
|
221
203
|
}
|
|
222
204
|
|
|
205
|
+
/**
|
|
206
|
+
* The bus's inserts — throws if it has none, since addressing an effect on a bus that defines no
|
|
207
|
+
* effects is the same programming error as addressing a bus that does not exist.
|
|
208
|
+
* @param {string} id
|
|
209
|
+
* @returns {EffectChain}
|
|
210
|
+
*/
|
|
211
|
+
#effects(id) {
|
|
212
|
+
const effects = this.#bus(id).effects;
|
|
213
|
+
|
|
214
|
+
if (effects === null) {
|
|
215
|
+
throw new Error(`sopra: bus '${id}' has no effects`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return effects;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Move a parameter of one of a bus's insert effects, now. See {@link EffectChain#setParam}.
|
|
223
|
+
* @param {string} id bus
|
|
224
|
+
* @param {number} effectIndex position in the bus definition's `effects`
|
|
225
|
+
* @param {string} name one of that effect class's `parameters` (`'frequency'` on an `EqEffect`)
|
|
226
|
+
* @param {number} value in the parameter's own units
|
|
227
|
+
*/
|
|
228
|
+
setEffectParam(id, effectIndex, name, value) {
|
|
229
|
+
this.#effects(id).setParam(effectIndex, name, value);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Glide a parameter of one of a bus's insert effects toward `value`. See {@link EffectChain#approachParam}.
|
|
234
|
+
* @param {string} id bus
|
|
235
|
+
* @param {number} effectIndex
|
|
236
|
+
* @param {string} name
|
|
237
|
+
* @param {number} value
|
|
238
|
+
* @param {number} timeConstant seconds, above 0
|
|
239
|
+
*/
|
|
240
|
+
approachEffectParam(id, effectIndex, name, value, timeConstant) {
|
|
241
|
+
this.#effects(id).approachParam(effectIndex, name, value, timeConstant);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Where a bus effect's parameter was last sent (a glide's target, not where it has got to).
|
|
246
|
+
* @param {string} id bus
|
|
247
|
+
* @param {number} effectIndex
|
|
248
|
+
* @param {string} name
|
|
249
|
+
* @returns {number}
|
|
250
|
+
*/
|
|
251
|
+
getEffectParam(id, effectIndex, name) {
|
|
252
|
+
return this.#effects(id).getParam(effectIndex, name);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Whether this graph has bus `id`, with an insert at `effectIndex` exposing a parameter `name` — an
|
|
257
|
+
* address {@link setEffectParam} would take. Asks; never throws.
|
|
258
|
+
* @param {string} id bus
|
|
259
|
+
* @param {number} effectIndex
|
|
260
|
+
* @param {string} name
|
|
261
|
+
* @returns {boolean}
|
|
262
|
+
*/
|
|
263
|
+
hasEffectParam(id, effectIndex, name) {
|
|
264
|
+
const bus = this.buses.get(id);
|
|
265
|
+
|
|
266
|
+
return bus !== undefined && bus.effects !== null && bus.effects.hasParam(effectIndex, name);
|
|
267
|
+
}
|
|
268
|
+
|
|
223
269
|
/**
|
|
224
270
|
* @param {string} id
|
|
225
271
|
* @returns {number} linear gain
|
|
@@ -252,13 +298,7 @@ export class BusGraph {
|
|
|
252
298
|
const param = bus.output.gain;
|
|
253
299
|
const now = this.#ctx.currentTime;
|
|
254
300
|
|
|
255
|
-
|
|
256
|
-
param.cancelAndHoldAtTime(now);
|
|
257
|
-
} else {
|
|
258
|
-
// portable fallback (Firefox), as in `rampGain`
|
|
259
|
-
param.cancelScheduledValues(now);
|
|
260
|
-
}
|
|
261
|
-
|
|
301
|
+
cancelAndHold(param, now);
|
|
262
302
|
param.setValueAtTime(linear, now);
|
|
263
303
|
}
|
|
264
304
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve an effect-parameter address against a list of effect definitions: the position of `name` among
|
|
3
|
+
* the class `parameters` of the effect at `index`, or throw naming what the list does have. The one
|
|
4
|
+
* place an `(effect index, parameter name)` address is checked — a chain resolves through it, and so does
|
|
5
|
+
* anything that holds addresses for a chain not yet built (`EffectParamOverrides`).
|
|
6
|
+
* @param {AbstractAudioEffect[]} effects
|
|
7
|
+
* @param {number} index
|
|
8
|
+
* @param {string} name
|
|
9
|
+
* @returns {number}
|
|
10
|
+
*/
|
|
11
|
+
export function effectParamIndex(effects: AbstractAudioEffect[], index: number, name: string): number;
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {import("../definition/effect/AbstractAudioEffect.js").AbstractAudioEffect} AbstractAudioEffect
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* The live nodes of an ordered list of insert effects, wired `effect[0] → … → effect[n]`, and the one place
|
|
17
|
+
* their parameters are moved at runtime.
|
|
18
|
+
*
|
|
19
|
+
* Shared by a bus's inserts ({@link BusGraph}) and a single play's inserts ({@link EventInstance}), so both
|
|
20
|
+
* address a parameter the same way: by the effect's index in the list it was built from and the parameter's
|
|
21
|
+
* name, one of the effect class's {@link AbstractAudioEffect.parameters} — the name of the definition field
|
|
22
|
+
* it starts from (`EqEffect.frequency` → `'frequency'`).
|
|
23
|
+
*
|
|
24
|
+
* Each parameter's NOMINAL value — where it was last sent — is tracked here rather than read back from the
|
|
25
|
+
* node, because WebAudio does not move `AudioParam.value` under scheduled automation: a glide's target is
|
|
26
|
+
* only visible in the automation list.
|
|
27
|
+
*
|
|
28
|
+
* Built once per bus and once per playing instance, so it is kept small: the parameter names are the
|
|
29
|
+
* effect classes' own static lists (read through the definitions, never copied), and the per-chain state is
|
|
30
|
+
* three flat arrays.
|
|
31
|
+
*/
|
|
32
|
+
export class EffectChain {
|
|
33
|
+
/**
|
|
34
|
+
* @param {BaseAudioContext} ctx
|
|
35
|
+
* @param {AbstractAudioEffect[]} effects at least one
|
|
36
|
+
*/
|
|
37
|
+
constructor(ctx: BaseAudioContext, effects: AbstractAudioEffect[]);
|
|
38
|
+
/**
|
|
39
|
+
* Head of the chain: what feeds it connects here.
|
|
40
|
+
* @type {AudioNode}
|
|
41
|
+
*/
|
|
42
|
+
input: AudioNode;
|
|
43
|
+
/**
|
|
44
|
+
* Tail of the chain: connect this onward.
|
|
45
|
+
* @type {AudioNode}
|
|
46
|
+
*/
|
|
47
|
+
output: AudioNode;
|
|
48
|
+
/**
|
|
49
|
+
* @returns {number} how many effects the chain holds
|
|
50
|
+
*/
|
|
51
|
+
get length(): number;
|
|
52
|
+
/**
|
|
53
|
+
* Whether the chain has an effect at `index` exposing a parameter called `name` — an address
|
|
54
|
+
* {@link setParam} would take. Asks; never throws.
|
|
55
|
+
* @param {number} index
|
|
56
|
+
* @param {string} name
|
|
57
|
+
* @returns {boolean}
|
|
58
|
+
*/
|
|
59
|
+
hasParam(index: number, name: string): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Move an effect's parameter to `value`, now. Whatever automation was in flight on it is cancelled.
|
|
62
|
+
*
|
|
63
|
+
* @param {number} index effect's position in the list the chain was built from
|
|
64
|
+
* @param {string} name one of the effect class's `parameters`
|
|
65
|
+
* @param {number} value in the parameter's own units (Hz, dB, ...)
|
|
66
|
+
*/
|
|
67
|
+
setParam(index: number, name: string, value: number): void;
|
|
68
|
+
/**
|
|
69
|
+
* Glide an effect's parameter toward `value` exponentially, covering ~63% of the way per
|
|
70
|
+
* `timeConstant` — the WebAudio `setTargetAtTime` glide, and the counterpart of
|
|
71
|
+
* {@link BusGraph#approachVolume}. Whatever was in flight is cancelled and held first, so a value sent
|
|
72
|
+
* every frame (a filter tracking a game value) retargets from wherever the last glide had got to, and
|
|
73
|
+
* the automation list never grows.
|
|
74
|
+
*
|
|
75
|
+
* @param {number} index
|
|
76
|
+
* @param {string} name
|
|
77
|
+
* @param {number} value
|
|
78
|
+
* @param {number} timeConstant seconds, above 0
|
|
79
|
+
*/
|
|
80
|
+
approachParam(index: number, name: string, value: number, timeConstant: number): void;
|
|
81
|
+
/**
|
|
82
|
+
* Where an effect's parameter was last sent — the target of a glide still in flight, not the value it
|
|
83
|
+
* has reached — or its defined value if it has not been moved.
|
|
84
|
+
* @param {number} index
|
|
85
|
+
* @param {string} name
|
|
86
|
+
* @returns {number}
|
|
87
|
+
*/
|
|
88
|
+
getParam(index: number, name: string): number;
|
|
89
|
+
/**
|
|
90
|
+
* Unwire every node the chain owns. Idempotent.
|
|
91
|
+
*/
|
|
92
|
+
disconnect(): void;
|
|
93
|
+
#private;
|
|
94
|
+
}
|
|
95
|
+
export type AbstractAudioEffect = import("../definition/effect/AbstractAudioEffect.js").AbstractAudioEffect;
|
|
96
|
+
//# sourceMappingURL=EffectChain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EffectChain.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/sound/sopra/runtime/EffectChain.js"],"names":[],"mappings":"AAoQA;;;;;;;;;GASG;AACH,0CALW,mBAAmB,EAAE,SACrB,MAAM,QACN,MAAM,GACJ,MAAM,CAiBlB;AA1RD;;GAEG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH;IA4CI;;;OAGG;IACH,iBAHW,gBAAgB,WAChB,mBAAmB,EAAE,EA6D/B;IAzGD;;;OAGG;IACH,OAFU,SAAS,CAEb;IAEN;;;OAGG;IACH,QAFU,SAAS,CAEZ;IAiGP;;OAEG;IACH,qBAEC;IAED;;;;;;OAMG;IACH,gBAJW,MAAM,QACN,MAAM,GACJ,OAAO,CAMnB;IAoBD;;;;;;OAMG;IACH,gBAJW,MAAM,QACN,MAAM,SACN,MAAM,QAchB;IAED;;;;;;;;;;;OAWG;IACH,qBALW,MAAM,QACN,MAAM,SACN,MAAM,gBACN,MAAM,QAehB;IAED;;;;;;OAMG;IACH,gBAJW,MAAM,QACN,MAAM,GACJ,MAAM,CAIlB;IAED;;OAEG;IACH,mBAMC;;CACJ;kCA5OY,OAAO,6CAA6C,EAAE,mBAAmB"}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { assert } from "../../../../core/assert.js";
|
|
2
|
+
import { cancelAndHold } from "../util/fadeOutAndStop.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import("../definition/effect/AbstractAudioEffect.js").AbstractAudioEffect} AbstractAudioEffect
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The live nodes of an ordered list of insert effects, wired `effect[0] → … → effect[n]`, and the one place
|
|
10
|
+
* their parameters are moved at runtime.
|
|
11
|
+
*
|
|
12
|
+
* Shared by a bus's inserts ({@link BusGraph}) and a single play's inserts ({@link EventInstance}), so both
|
|
13
|
+
* address a parameter the same way: by the effect's index in the list it was built from and the parameter's
|
|
14
|
+
* name, one of the effect class's {@link AbstractAudioEffect.parameters} — the name of the definition field
|
|
15
|
+
* it starts from (`EqEffect.frequency` → `'frequency'`).
|
|
16
|
+
*
|
|
17
|
+
* Each parameter's NOMINAL value — where it was last sent — is tracked here rather than read back from the
|
|
18
|
+
* node, because WebAudio does not move `AudioParam.value` under scheduled automation: a glide's target is
|
|
19
|
+
* only visible in the automation list.
|
|
20
|
+
*
|
|
21
|
+
* Built once per bus and once per playing instance, so it is kept small: the parameter names are the
|
|
22
|
+
* effect classes' own static lists (read through the definitions, never copied), and the per-chain state is
|
|
23
|
+
* three flat arrays.
|
|
24
|
+
*/
|
|
25
|
+
export class EffectChain {
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Head of the chain: what feeds it connects here.
|
|
29
|
+
* @type {AudioNode}
|
|
30
|
+
*/
|
|
31
|
+
input;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Tail of the chain: connect this onward.
|
|
35
|
+
* @type {AudioNode}
|
|
36
|
+
*/
|
|
37
|
+
output;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @type {BaseAudioContext}
|
|
41
|
+
*/
|
|
42
|
+
#ctx;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The definitions the chain was built from — the source of each effect's parameter names. Shared with
|
|
46
|
+
* the owner (a bus or event definition); never written.
|
|
47
|
+
* @type {AbstractAudioEffect[]}
|
|
48
|
+
*/
|
|
49
|
+
#effects;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Each effect's input and output node, in chain order: `[in0, out0, in1, out1, …]`. For {@link disconnect}.
|
|
53
|
+
* @type {AudioNode[]}
|
|
54
|
+
*/
|
|
55
|
+
#nodes;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Every effect's live params, effect by effect, each effect's in its class's `parameters` order.
|
|
59
|
+
* @type {AudioParam[]}
|
|
60
|
+
*/
|
|
61
|
+
#params;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Where each param of {@link #params} was last sent, same layout.
|
|
65
|
+
* @type {Float64Array}
|
|
66
|
+
*/
|
|
67
|
+
#nominal;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {BaseAudioContext} ctx
|
|
71
|
+
* @param {AbstractAudioEffect[]} effects at least one
|
|
72
|
+
*/
|
|
73
|
+
constructor(ctx, effects) {
|
|
74
|
+
assert.defined(ctx, 'ctx');
|
|
75
|
+
assert.isArray(effects, 'effects');
|
|
76
|
+
assert.greaterThan(effects.length, 0, 'effects.length');
|
|
77
|
+
|
|
78
|
+
this.#ctx = ctx;
|
|
79
|
+
this.#effects = effects;
|
|
80
|
+
|
|
81
|
+
const effectCount = effects.length;
|
|
82
|
+
|
|
83
|
+
let paramCount = 0;
|
|
84
|
+
for (let i = 0; i < effectCount; i++) {
|
|
85
|
+
paramCount += parametersOf(effects[i]).length;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const nodes = new Array(effectCount * 2);
|
|
89
|
+
const params = new Array(paramCount);
|
|
90
|
+
const nominal = new Float64Array(paramCount);
|
|
91
|
+
|
|
92
|
+
let slot = 0;
|
|
93
|
+
let previousOutput = null;
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < effectCount; i++) {
|
|
96
|
+
const effect = effects[i];
|
|
97
|
+
const built = effect.build(ctx);
|
|
98
|
+
const names = parametersOf(effect);
|
|
99
|
+
|
|
100
|
+
nodes[i * 2] = built.input;
|
|
101
|
+
nodes[i * 2 + 1] = built.output;
|
|
102
|
+
|
|
103
|
+
if (previousOutput === null) {
|
|
104
|
+
this.input = built.input;
|
|
105
|
+
} else {
|
|
106
|
+
previousOutput.connect(built.input);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
previousOutput = built.output;
|
|
110
|
+
|
|
111
|
+
if (names.length > 0) {
|
|
112
|
+
const exposed = built.params;
|
|
113
|
+
|
|
114
|
+
// the class's list is the contract `build` answers to; a mismatch would silently move
|
|
115
|
+
// the wrong param, so it is refused here, where the effect that broke it is known
|
|
116
|
+
if (exposed === undefined || exposed.length !== names.length) {
|
|
117
|
+
throw new Error(`sopra: ${effect.constructor.name}.build returned ${exposed === undefined ? 'no' : exposed.length} params, its class declares ${names.length} (${names.join(', ')})`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
for (let p = 0; p < names.length; p++) {
|
|
121
|
+
params[slot] = exposed[p];
|
|
122
|
+
nominal[slot] = exposed[p].value;
|
|
123
|
+
slot++;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.output = previousOutput;
|
|
129
|
+
this.#nodes = nodes;
|
|
130
|
+
this.#params = params;
|
|
131
|
+
this.#nominal = nominal;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @returns {number} how many effects the chain holds
|
|
136
|
+
*/
|
|
137
|
+
get length() {
|
|
138
|
+
return this.#effects.length;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Whether the chain has an effect at `index` exposing a parameter called `name` — an address
|
|
143
|
+
* {@link setParam} would take. Asks; never throws.
|
|
144
|
+
* @param {number} index
|
|
145
|
+
* @param {string} name
|
|
146
|
+
* @returns {boolean}
|
|
147
|
+
*/
|
|
148
|
+
hasParam(index, name) {
|
|
149
|
+
const effect = effectAt(this.#effects, index);
|
|
150
|
+
|
|
151
|
+
return effect !== undefined && parametersOf(effect).indexOf(name) !== -1;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The flat slot of an effect's parameter, or throw naming what does exist.
|
|
156
|
+
* @param {number} index
|
|
157
|
+
* @param {string} name
|
|
158
|
+
* @returns {number}
|
|
159
|
+
*/
|
|
160
|
+
#slot(index, name) {
|
|
161
|
+
const effects = this.#effects;
|
|
162
|
+
const p = effectParamIndex(effects, index, name);
|
|
163
|
+
|
|
164
|
+
let slot = 0;
|
|
165
|
+
for (let i = 0; i < index; i++) {
|
|
166
|
+
slot += parametersOf(effects[i]).length;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return slot + p;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Move an effect's parameter to `value`, now. Whatever automation was in flight on it is cancelled.
|
|
174
|
+
*
|
|
175
|
+
* @param {number} index effect's position in the list the chain was built from
|
|
176
|
+
* @param {string} name one of the effect class's `parameters`
|
|
177
|
+
* @param {number} value in the parameter's own units (Hz, dB, ...)
|
|
178
|
+
*/
|
|
179
|
+
setParam(index, name, value) {
|
|
180
|
+
// an AudioParam throws on a non-finite value, so say which call sent it
|
|
181
|
+
assert.isFinite(value, 'value');
|
|
182
|
+
|
|
183
|
+
const slot = this.#slot(index, name);
|
|
184
|
+
const param = this.#params[slot];
|
|
185
|
+
const now = this.#ctx.currentTime;
|
|
186
|
+
|
|
187
|
+
this.#nominal[slot] = value;
|
|
188
|
+
|
|
189
|
+
cancelAndHold(param, now);
|
|
190
|
+
param.setValueAtTime(value, now);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Glide an effect's parameter toward `value` exponentially, covering ~63% of the way per
|
|
195
|
+
* `timeConstant` — the WebAudio `setTargetAtTime` glide, and the counterpart of
|
|
196
|
+
* {@link BusGraph#approachVolume}. Whatever was in flight is cancelled and held first, so a value sent
|
|
197
|
+
* every frame (a filter tracking a game value) retargets from wherever the last glide had got to, and
|
|
198
|
+
* the automation list never grows.
|
|
199
|
+
*
|
|
200
|
+
* @param {number} index
|
|
201
|
+
* @param {string} name
|
|
202
|
+
* @param {number} value
|
|
203
|
+
* @param {number} timeConstant seconds, above 0
|
|
204
|
+
*/
|
|
205
|
+
approachParam(index, name, value, timeConstant) {
|
|
206
|
+
assert.isFinite(value, 'value');
|
|
207
|
+
assert.isFinite(timeConstant, 'timeConstant');
|
|
208
|
+
assert.greaterThan(timeConstant, 0, 'timeConstant');
|
|
209
|
+
|
|
210
|
+
const slot = this.#slot(index, name);
|
|
211
|
+
const param = this.#params[slot];
|
|
212
|
+
const now = this.#ctx.currentTime;
|
|
213
|
+
|
|
214
|
+
this.#nominal[slot] = value;
|
|
215
|
+
|
|
216
|
+
cancelAndHold(param, now);
|
|
217
|
+
param.setTargetAtTime(value, now, timeConstant);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Where an effect's parameter was last sent — the target of a glide still in flight, not the value it
|
|
222
|
+
* has reached — or its defined value if it has not been moved.
|
|
223
|
+
* @param {number} index
|
|
224
|
+
* @param {string} name
|
|
225
|
+
* @returns {number}
|
|
226
|
+
*/
|
|
227
|
+
getParam(index, name) {
|
|
228
|
+
return this.#nominal[this.#slot(index, name)];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Unwire every node the chain owns. Idempotent.
|
|
233
|
+
*/
|
|
234
|
+
disconnect() {
|
|
235
|
+
const nodes = this.#nodes;
|
|
236
|
+
|
|
237
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
238
|
+
nodes[i].disconnect();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* @param {AbstractAudioEffect} effect
|
|
245
|
+
* @returns {string[]}
|
|
246
|
+
*/
|
|
247
|
+
function parametersOf(effect) {
|
|
248
|
+
return effect.constructor.parameters;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @param {AbstractAudioEffect[]} effects
|
|
253
|
+
* @param {number} index
|
|
254
|
+
* @returns {AbstractAudioEffect|undefined} undefined for an index that is no position in the list
|
|
255
|
+
* (negative, fractional, past the end)
|
|
256
|
+
*/
|
|
257
|
+
function effectAt(effects, index) {
|
|
258
|
+
return Number.isInteger(index) ? effects[index] : undefined;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Resolve an effect-parameter address against a list of effect definitions: the position of `name` among
|
|
263
|
+
* the class `parameters` of the effect at `index`, or throw naming what the list does have. The one
|
|
264
|
+
* place an `(effect index, parameter name)` address is checked — a chain resolves through it, and so does
|
|
265
|
+
* anything that holds addresses for a chain not yet built (`EffectParamOverrides`).
|
|
266
|
+
* @param {AbstractAudioEffect[]} effects
|
|
267
|
+
* @param {number} index
|
|
268
|
+
* @param {string} name
|
|
269
|
+
* @returns {number}
|
|
270
|
+
*/
|
|
271
|
+
export function effectParamIndex(effects, index, name) {
|
|
272
|
+
const effect = effectAt(effects, index);
|
|
273
|
+
|
|
274
|
+
if (effect === undefined) {
|
|
275
|
+
throw new Error(`sopra: no effect at index ${index} (there are ${effects.length})`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const names = parametersOf(effect);
|
|
279
|
+
const p = names.indexOf(name);
|
|
280
|
+
|
|
281
|
+
if (p === -1) {
|
|
282
|
+
throw new Error(`sopra: effect ${index} has no parameter '${name}' (it exposes: ${names.join(', ') || 'none'})`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return p;
|
|
286
|
+
}
|