cacophony 0.35.0 → 0.37.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/CHANGELOG.md +20 -1
- package/README.md +74 -6
- package/dist/index.d.ts +1 -0
- package/dist/playback.cjs +1 -1
- package/dist/playback.cjs.map +1 -1
- package/dist/playback.d.ts +45 -0
- package/dist/playback.mjs +179 -24
- package/dist/playback.mjs.map +1 -1
- package/dist/sound.cjs +1 -1
- package/dist/sound.cjs.map +1 -1
- package/dist/sound.d.ts +2 -0
- package/dist/sound.mjs +6 -1
- package/dist/sound.mjs.map +1 -1
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/AudioCache.html +4 -4
- package/docs/classes/AudioSprite.html +7 -7
- package/docs/classes/BarberpoleEffect.html +6 -6
- package/docs/classes/BiquadEffect.html +3 -3
- package/docs/classes/Bus.html +21 -21
- package/docs/classes/Cacophony.html +92 -92
- package/docs/classes/DynamicsEffect.html +6 -6
- package/docs/classes/FdnReverbEffect.html +6 -6
- package/docs/classes/FoaDecoder.html +5 -5
- package/docs/classes/FoaDecoderEffect.html +3 -3
- package/docs/classes/FrequencyShifterEffect.html +6 -6
- package/docs/classes/Group.html +33 -33
- package/docs/classes/HarmonizerEffect.html +6 -6
- package/docs/classes/ImpulseResponseEffect.html +3 -3
- package/docs/classes/KWeightingFilter.html +4 -4
- package/docs/classes/LoudnessMeter.html +11 -11
- package/docs/classes/MediaStreamPlayback.html +70 -70
- package/docs/classes/MediaStreamSound.html +51 -51
- package/docs/classes/MicrophoneStream.html +52 -52
- package/docs/classes/ModulatedDelayEffect.html +6 -6
- package/docs/classes/PcmStreamPlayback.html +70 -70
- package/docs/classes/PcmStreamSound.html +61 -61
- package/docs/classes/PhaserEffect.html +6 -6
- package/docs/classes/Playback.html +184 -89
- package/docs/classes/ReverbEffect.html +6 -6
- package/docs/classes/Scheduler.html +3 -3
- package/docs/classes/ShareEffect.html +3 -3
- package/docs/classes/Sound.html +85 -73
- package/docs/classes/SpectralFreezeEffect.html +6 -6
- package/docs/classes/StereoWidenerEffect.html +3 -3
- package/docs/classes/Synth.html +63 -63
- package/docs/classes/SynthGroup.html +28 -28
- package/docs/classes/TremoloEffect.html +6 -6
- package/docs/classes/TruePeakDetector.html +8 -8
- package/docs/classes/WaveshaperEffect.html +6 -6
- package/docs/functions/encodeMonoToFoaSN3D.html +1 -1
- package/docs/functions/integratedLoudness.html +1 -1
- package/docs/functions/integratedUngatedLoudness.html +1 -1
- package/docs/functions/loudnessRange.html +1 -1
- package/docs/functions/timeStretch.html +1 -1
- package/docs/functions/timeStretchChannels.html +1 -1
- package/docs/functions/truePeakDb.html +1 -1
- package/docs/hierarchy.html +3 -3
- package/docs/index.html +48 -0
- package/docs/interfaces/AudioBuffer.html +8 -8
- package/docs/interfaces/AudioBufferSourceNode.html +21 -21
- package/docs/interfaces/AudioEventCallbacks.html +1 -1
- package/docs/interfaces/AudioListener.html +12 -12
- package/docs/interfaces/AudioNode.html +13 -13
- package/docs/interfaces/AudioParam.html +7 -7
- package/docs/interfaces/AudioWorklet.html +2 -2
- package/docs/interfaces/AudioWorkletNode.html +15 -15
- package/docs/interfaces/BarberpoleOptions.html +5 -5
- package/docs/interfaces/BaseContext.html +25 -25
- package/docs/interfaces/BaseSound.html +16 -16
- package/docs/interfaces/BiquadCoefficients.html +6 -6
- package/docs/interfaces/BiquadFilterNode.html +19 -19
- package/docs/interfaces/BuiltEffectGraph.html +6 -6
- package/docs/interfaces/CacheErrorEvent.html +5 -5
- package/docs/interfaces/CacheHitEvent.html +4 -4
- package/docs/interfaces/CacheMissEvent.html +4 -4
- package/docs/interfaces/CacophonyEffect.html +2 -2
- package/docs/interfaces/CacophonyLogger.html +4 -4
- package/docs/interfaces/ChannelMergerNode.html +13 -13
- package/docs/interfaces/ChannelSplitterNode.html +13 -13
- package/docs/interfaces/ConvolverNode.html +15 -15
- package/docs/interfaces/CreateSpriteOptions.html +3 -3
- package/docs/interfaces/DeviceChangeEvent.html +3 -3
- package/docs/interfaces/DynamicsOptions.html +7 -7
- package/docs/interfaces/FadeStartEvent.html +4 -4
- package/docs/interfaces/FdnReverbOptions.html +6 -6
- package/docs/interfaces/FoaDecoderOptions.html +2 -2
- package/docs/interfaces/FrequencyShifterOptions.html +3 -3
- package/docs/interfaces/GainNode.html +14 -14
- package/docs/interfaces/GlobalPlaybackEvent.html +3 -3
- package/docs/interfaces/HarmonizerOptions.html +6 -6
- package/docs/interfaces/ImpulseResponseOptions.html +5 -5
- package/docs/interfaces/LoadingCompleteEvent.html +5 -5
- package/docs/interfaces/LoadingErrorEvent.html +5 -5
- package/docs/interfaces/LoadingProgressEvent.html +6 -6
- package/docs/interfaces/LoadingStartEvent.html +3 -3
- package/docs/interfaces/LoudnessChannelInput.html +3 -3
- package/docs/interfaces/LoudnessReading.html +5 -5
- package/docs/interfaces/MediaElementSourceNode.html +14 -14
- package/docs/interfaces/MediaStreamAudioSourceNode.html +14 -14
- package/docs/interfaces/MediaStreamSoundOptions.html +4 -4
- package/docs/interfaces/MicrophoneStreamOptions.html +7 -7
- package/docs/interfaces/ModulatedDelayOptions.html +8 -8
- package/docs/interfaces/OfflineOptions.html +5 -5
- package/docs/interfaces/OscillatorNode.html +19 -19
- package/docs/interfaces/PannerNode.html +27 -27
- package/docs/interfaces/PcmStreamBufferEvent.html +2 -2
- package/docs/interfaces/PcmStreamSoundOptions.html +6 -6
- package/docs/interfaces/PhaserOptions.html +7 -7
- package/docs/interfaces/PlayOptions.html +13 -13
- package/docs/interfaces/PlaybackErrorEvent.html +5 -5
- package/docs/interfaces/PrepareSeekOptions.html +65 -0
- package/docs/interfaces/ReverbOptions.html +13 -13
- package/docs/interfaces/RuntimeOptions.html +8 -8
- package/docs/interfaces/ScheduledCallbackHandle.html +2 -2
- package/docs/interfaces/SendGainOptions.html +3 -3
- package/docs/interfaces/SoundCleanupHoldings.html +4 -4
- package/docs/interfaces/SoundErrorEvent.html +6 -6
- package/docs/interfaces/SpectralFreezeOptions.html +4 -4
- package/docs/interfaces/SpriteRegion.html +4 -4
- package/docs/interfaces/StereoPannerNode.html +14 -14
- package/docs/interfaces/StereoToBFormatOptions.html +2 -2
- package/docs/interfaces/StereoWidenerOptions.html +4 -4
- package/docs/interfaces/StreamCapabilities.html +5 -5
- package/docs/interfaces/TimeStretchOptions.html +5 -5
- package/docs/interfaces/TremoloOptions.html +5 -5
- package/docs/interfaces/WaveshaperOptions.html +5 -5
- package/docs/modules.html +2 -2
- package/docs/types/AudioSinkId.html +1 -1
- package/docs/types/BaseAudioEvents.html +11 -11
- package/docs/types/BuiltEffect.html +1 -1
- package/docs/types/BusConnectionTarget.html +1 -1
- package/docs/types/CacheEventCallback.html +1 -1
- package/docs/types/CacophonyEvents.html +20 -20
- package/docs/types/ErrorEventCallback.html +1 -1
- package/docs/types/FadeType.html +1 -1
- package/docs/types/HrtfPannerOptions.html +1 -1
- package/docs/types/ImpulseResponseSource.html +1 -1
- package/docs/types/LoadingEventCallback.html +1 -1
- package/docs/types/LoopCount.html +1 -1
- package/docs/types/LoudnessChannel.html +1 -1
- package/docs/types/Orientation.html +3 -3
- package/docs/types/PanCloneOverrides.html +6 -6
- package/docs/types/PanType.html +1 -1
- package/docs/types/PcmStreamEvents.html +6 -6
- package/docs/types/PcmStreamState.html +1 -1
- package/docs/types/PlaybackEvents.html +1 -1
- package/docs/types/Position.html +1 -1
- package/docs/types/SoundEvents.html +1 -1
- package/docs/types/SoundType.html +1 -1
- package/docs/types/SourceNode.html +1 -1
- package/docs/types/SpriteMap.html +1 -1
- package/docs/types/SynthEvents.html +1 -1
- package/docs/types/ThreeDOptions.html +1 -1
- package/docs/types/WebCodecsStreamSound.html +1 -1
- package/docs/variables/CHANNEL_WEIGHTS.html +1 -1
- package/docs/variables/K_WEIGHTING_STAGE1_48K.html +1 -1
- package/docs/variables/K_WEIGHTING_STAGE2_48K.html +1 -1
- package/docs/variables/consoleLogger.html +1 -1
- package/docs/variables/noopLogger.html +1 -1
- package/package.json +1 -1
package/dist/playback.mjs
CHANGED
|
@@ -13,7 +13,11 @@ var s = class s extends a {
|
|
|
13
13
|
_offset = 0;
|
|
14
14
|
_startTime = 0;
|
|
15
15
|
_playbackRate = 1;
|
|
16
|
+
_clipRegion;
|
|
17
|
+
_scheduledStop;
|
|
16
18
|
_mediaPlayPromise;
|
|
19
|
+
_cancelPrepareSeek;
|
|
20
|
+
_mediaPreparationGate;
|
|
17
21
|
_fadeInConfig;
|
|
18
22
|
_fadeOutConfig;
|
|
19
23
|
_loopEndCallback;
|
|
@@ -24,7 +28,9 @@ var s = class s extends a {
|
|
|
24
28
|
}
|
|
25
29
|
setupSourceNode(e) {
|
|
26
30
|
if ("mediaElement" in e && e.mediaElement) e.mediaElement.onended = this.loopEnded;
|
|
27
|
-
else if ("onended" in e) e.onended =
|
|
31
|
+
else if ("onended" in e) e.onended = () => {
|
|
32
|
+
this.source === e && this.loopEnded();
|
|
33
|
+
};
|
|
28
34
|
else throw Error("Unsupported source type");
|
|
29
35
|
}
|
|
30
36
|
get occlusion() {
|
|
@@ -41,7 +47,7 @@ var s = class s extends a {
|
|
|
41
47
|
this._occlusion.setAmount(n, t);
|
|
42
48
|
}
|
|
43
49
|
setEffectChainEndpoints(e, t) {
|
|
44
|
-
this._occlusion?.setOutput(t), super.setEffectChainEndpoints(e, this._occlusion?.input ?? t);
|
|
50
|
+
this._occlusion?.setOutput(t), super.setEffectChainEndpoints(this._mediaPreparationGate ?? e, this._occlusion?.input ?? t);
|
|
45
51
|
}
|
|
46
52
|
get duration() {
|
|
47
53
|
if (this.origin.region) return this.origin.region.duration;
|
|
@@ -51,6 +57,37 @@ var s = class s extends a {
|
|
|
51
57
|
get playbackRate() {
|
|
52
58
|
return this._playbackRate;
|
|
53
59
|
}
|
|
60
|
+
get supportsClipLoop() {
|
|
61
|
+
return !!this.source && "buffer" in this.source && !!this.buffer;
|
|
62
|
+
}
|
|
63
|
+
setLoopRegion(e, t) {
|
|
64
|
+
if (this.assertNotCleanedUp(), !this.supportsClipLoop) throw new DOMException("Clip bounds require a decoded buffer", "NotSupportedError");
|
|
65
|
+
if (this._state === "playing") throw Error("Pause playback before configuring clip bounds");
|
|
66
|
+
if (!this.buffer || !Number.isFinite(e) || !Number.isFinite(t) || e < 0 || t > this.duration || (t - e) * this.buffer.sampleRate + 2 ** -52 * Math.max(1, Math.abs(e * this.buffer.sampleRate), Math.abs(t * this.buffer.sampleRate)) * 4 < 1 || Math.round(t * this.buffer.sampleRate) <= Math.round(e * this.buffer.sampleRate)) throw RangeError("Clip bounds must span at least one sample within the Sound duration");
|
|
67
|
+
if (this._offset >= t) throw RangeError("The current offset must precede the clip end");
|
|
68
|
+
if (this.loopCount !== 0 && this.loopCount !== "infinite") throw Error("Clip bounds support only zero or infinite loop count");
|
|
69
|
+
if (this._fadeInConfig?.perLoop) throw Error("Per-loop fades are not supported with clip bounds");
|
|
70
|
+
this._clipRegion = {
|
|
71
|
+
start: e,
|
|
72
|
+
end: t
|
|
73
|
+
}, this.configureBufferLoopSource();
|
|
74
|
+
}
|
|
75
|
+
scheduleStop(e) {
|
|
76
|
+
if (this.assertNotCleanedUp(), !this.supportsClipLoop || !this.source || !("stop" in this.source)) throw new DOMException("Scheduled ends require a decoded buffer", "NotSupportedError");
|
|
77
|
+
if (!Number.isFinite(e) || e < 0) throw RangeError("End time must be finite and non-negative");
|
|
78
|
+
if (this._state !== "playing") throw Error("Start the buffer before scheduling its end");
|
|
79
|
+
if (this.loopCount !== 0 && this.loopCount !== "infinite") throw Error("Scheduled ends do not support legacy numeric repeats");
|
|
80
|
+
if (this._fadeOutConfig) throw Error("An absolute end cannot use an ended-time fade-out");
|
|
81
|
+
let t = Math.max(this.context.currentTime, e);
|
|
82
|
+
this.source.stop(t), this._scheduledStop = {
|
|
83
|
+
source: this.source,
|
|
84
|
+
at: t
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
_validateLoopCount(e) {
|
|
88
|
+
if (this._clipRegion && e !== 0 && e !== "infinite") throw Error("Clip bounds support only zero or infinite loop count");
|
|
89
|
+
if (this._scheduledStop && e !== 0 && e !== "infinite") throw Error("Scheduled ends do not support legacy numeric repeats");
|
|
90
|
+
}
|
|
54
91
|
get volume() {
|
|
55
92
|
return super.volume;
|
|
56
93
|
}
|
|
@@ -67,6 +104,16 @@ var s = class s extends a {
|
|
|
67
104
|
}
|
|
68
105
|
loopEnded = () => {
|
|
69
106
|
if (!(!this.source || this._state !== "playing")) {
|
|
107
|
+
if (this._scheduledStop?.source === this.source) {
|
|
108
|
+
let e = this.source;
|
|
109
|
+
this._scheduledStop = void 0, "onended" in e && (e.onended = null);
|
|
110
|
+
try {
|
|
111
|
+
this.emit("ended", void 0);
|
|
112
|
+
} finally {
|
|
113
|
+
this.source === e && this._state === "playing" && this.stop(), this.source === e && this.state === "stopped" && this.removeFromOrigin();
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
70
117
|
if ("mediaElement" in this.source && this._mediaPlayPromise) {
|
|
71
118
|
this._mediaPlayPromise.then(() => this._runLoopEnded());
|
|
72
119
|
return;
|
|
@@ -75,14 +122,24 @@ var s = class s extends a {
|
|
|
75
122
|
}
|
|
76
123
|
};
|
|
77
124
|
_runLoopEnded() {
|
|
78
|
-
!this.source || this._state !== "playing"
|
|
79
|
-
|
|
80
|
-
|
|
125
|
+
if (!this.source || this._state !== "playing") return;
|
|
126
|
+
let e = this.source;
|
|
127
|
+
if (this.currentLoop++, this.loopCount !== "infinite" && this.currentLoop > this.loopCount) {
|
|
128
|
+
if (this.emit("ended", void 0), this.source !== e || this._state !== "playing") return;
|
|
129
|
+
this._fadeOutConfig ? this.fadeOut(this._fadeOutConfig.duration, this._fadeOutConfig.type).then(() => {
|
|
130
|
+
this._state !== "stopped" && this.source === e && (this.stop(), this.source === e && this.state === "stopped" && this.removeFromOrigin());
|
|
131
|
+
}) : (this.stop(), this.source === e && this.state === "stopped" && this.removeFromOrigin());
|
|
132
|
+
} else {
|
|
133
|
+
if (this._loopEndCallback?.(), this.source !== e || this._state !== "playing") return;
|
|
134
|
+
this.seek(0), this._state !== "playing" && this.source && !("mediaElement" in this.source) && this.play(), this._fadeInConfig?.perLoop && this.gainNode && (this.gainNode.gain.setValueAtTime(1e-4, this.gainNode.context.currentTime), this.fadeTo(this._fadeInConfig.targetVolume, this._fadeInConfig.duration, this._fadeInConfig.type));
|
|
135
|
+
}
|
|
81
136
|
}
|
|
82
137
|
play(e) {
|
|
83
138
|
if (!this.source) throw Error("Cannot play a sound that has been cleaned up");
|
|
84
|
-
if (n(e, this.panType, "mediaElement" in this.source ? "media" : "buffer"), this.
|
|
85
|
-
|
|
139
|
+
if (n(e, this.panType, "mediaElement" in this.source ? "media" : "buffer"), e?.loopCount !== void 0 && this._validateLoopCount(e.loopCount), this._clipRegion && e?.fadeInPerLoop) throw Error("Per-loop fades are not supported with clip bounds");
|
|
140
|
+
if (this._scheduledStop && e?.fadeOut !== void 0) throw Error("An absolute end cannot use an ended-time fade-out");
|
|
141
|
+
if (this._state === "playing") return [this];
|
|
142
|
+
this._cancelPrepareSeek?.(), this._scheduledStop = void 0, t(this, this.context, e), e?.playbackRate !== void 0 && (this.playbackRate = e.playbackRate), e?.loopCount !== void 0 && this.loop(e.loopCount), e?.fadeIn !== void 0 && this.fadeIn(e.fadeIn, e.fadeType, {
|
|
86
143
|
perLoop: e.fadeInPerLoop,
|
|
87
144
|
startTime: e.at
|
|
88
145
|
}), e?.fadeOut !== void 0 && this.configureFadeOut(e.fadeOut, e.fadeType);
|
|
@@ -92,9 +149,9 @@ var s = class s extends a {
|
|
|
92
149
|
try {
|
|
93
150
|
let t;
|
|
94
151
|
if (this._state === "paused") {
|
|
95
|
-
if ("mediaElement" in this.source && this.source.mediaElement) t = this.source.mediaElement.play();
|
|
152
|
+
if ("mediaElement" in this.source && this.source.mediaElement) this.source.mediaElement.currentTime !== this._offset && (this.source.mediaElement.currentTime = this._offset), t = this.source.mediaElement.play();
|
|
96
153
|
else if (this.recreateSource(), "start" in this.source && !this.startBufferSource(e?.at ?? 0)) return [this];
|
|
97
|
-
} else if ("mediaElement" in this.source && this.source.mediaElement) this.source.mediaElement.currentTime = this._offset, t = this.source.mediaElement.play();
|
|
154
|
+
} else if ("mediaElement" in this.source && this.source.mediaElement) this.source.mediaElement.currentTime !== this._offset && (this.source.mediaElement.currentTime = this._offset), t = this.source.mediaElement.play();
|
|
98
155
|
else if ("start" in this.source && (this.recreateSource(), !this.startBufferSource(e?.at ?? 0))) return [this];
|
|
99
156
|
if (t) {
|
|
100
157
|
let e = t.then(() => {
|
|
@@ -128,19 +185,26 @@ var s = class s extends a {
|
|
|
128
185
|
}
|
|
129
186
|
}
|
|
130
187
|
emitPlayStarted(e) {
|
|
131
|
-
this._state === "stopped" && !this.origin.playbacks.includes(this) && this.origin._readmitPlayback(this), super.emitPlayStarted(e);
|
|
188
|
+
this._mediaPreparationGate?.gain.setValueAtTime(1, this.context.currentTime), this._state === "stopped" && !this.origin.playbacks.includes(this) && this.origin._readmitPlayback(this), super.emitPlayStarted(e);
|
|
132
189
|
}
|
|
133
190
|
pause() {
|
|
134
|
-
if (!this.source || this._state !== "playing")
|
|
135
|
-
|
|
136
|
-
|
|
191
|
+
if (!(!this.source || this._state !== "playing")) {
|
|
192
|
+
if (this._mediaPlayPromise = void 0, this._scheduledStop = void 0, "mediaElement" in this.source && this.source.mediaElement) this._mediaPreparationGate?.gain.setValueAtTime(0, this.context.currentTime), this.source.mediaElement.pause(), this._offset = this.source.mediaElement.currentTime;
|
|
193
|
+
else {
|
|
194
|
+
let e = Math.max(0, (this.context.currentTime - this._startTime) * this._playbackRate);
|
|
195
|
+
this._offset = this.regionRelativeOffset(this._offset + e), "stop" in this.source && this.source.stop();
|
|
196
|
+
}
|
|
197
|
+
this.emitPaused();
|
|
198
|
+
}
|
|
137
199
|
}
|
|
138
200
|
stop() {
|
|
139
201
|
if (!this.source) throw Error("Cannot stop a sound that has been cleaned up");
|
|
202
|
+
this._cancelPrepareSeek?.(), this._scheduledStop = void 0, this._mediaPreparationGate?.gain.setValueAtTime(0, this.context.currentTime);
|
|
140
203
|
let e = this._mediaPlayPromise !== void 0;
|
|
141
204
|
this._mediaPlayPromise = void 0, !(!e && (this._state === "stopped" || this._state === "unplayed")) && (this.cancelFade(), "stop" in this.source && this._state === "playing" && this.source.stop(), "mediaElement" in this.source && this.source.mediaElement && (this.source.mediaElement.pause(), this.source.mediaElement.currentTime = 0), this._offset = 0, this._startTime = 0, this.emitStopped());
|
|
142
205
|
}
|
|
143
206
|
fadeIn(e, t, n) {
|
|
207
|
+
if (n?.perLoop && this._clipRegion) throw Error("Per-loop fades are not supported with clip bounds");
|
|
144
208
|
return n?.perLoop && (this._fadeInConfig = {
|
|
145
209
|
duration: e,
|
|
146
210
|
type: t ?? "linear",
|
|
@@ -149,6 +213,7 @@ var s = class s extends a {
|
|
|
149
213
|
}), super.fadeIn(e, t, { startTime: n?.startTime });
|
|
150
214
|
}
|
|
151
215
|
configureFadeOut(e, t) {
|
|
216
|
+
if (this._scheduledStop) throw Error("An absolute end cannot use an ended-time fade-out");
|
|
152
217
|
this._fadeOutConfig = {
|
|
153
218
|
duration: e,
|
|
154
219
|
type: t ?? "linear"
|
|
@@ -159,15 +224,90 @@ var s = class s extends a {
|
|
|
159
224
|
this._state !== "stopped" && this.source && this.stop();
|
|
160
225
|
});
|
|
161
226
|
}
|
|
227
|
+
async prepareSeek(e, t = {}) {
|
|
228
|
+
if (this.assertNotCleanedUp(), !Number.isFinite(e) || e < 0) throw RangeError("Seek time must be finite and non-negative");
|
|
229
|
+
let n = t.timeoutMs ?? 1e4;
|
|
230
|
+
if (!Number.isFinite(n) || n <= 0 || n > 2147483647) throw RangeError("Seek timeout must be positive and at most 2147483647 milliseconds");
|
|
231
|
+
if (t.signal?.aborted) throw new DOMException("Seek preparation aborted", "AbortError");
|
|
232
|
+
if (this._state === "playing" || this._mediaPlayPromise) throw Error("Pause playback before preparing a seek");
|
|
233
|
+
this._cancelPrepareSeek?.();
|
|
234
|
+
let r = this.source;
|
|
235
|
+
if (!("mediaElement" in r)) {
|
|
236
|
+
if (this._clipRegion && e >= this._clipRegion.end) throw RangeError("Seek time must precede the clip end");
|
|
237
|
+
if (!Number.isFinite(this.duration) || e > this.duration) throw RangeError("Seek time exceeds duration");
|
|
238
|
+
this._offset = e, this.emit("seek", e);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
let i = r.mediaElement;
|
|
242
|
+
if (!i.paused) throw Error("Pause playback before preparing a seek");
|
|
243
|
+
if (!this._mediaPreparationGate) {
|
|
244
|
+
let e = this.context.createGain();
|
|
245
|
+
e.gain.value = 0, this._mediaPreparationGate = e, this.setEffectChainEndpoints(r, this.panner), r.connect(e);
|
|
246
|
+
}
|
|
247
|
+
this._mediaPreparationGate.gain.setValueAtTime(0, this.context.currentTime), await new Promise((r, a) => {
|
|
248
|
+
let o = !1, s = !1, c = !1, l = [
|
|
249
|
+
"loadedmetadata",
|
|
250
|
+
"durationchange",
|
|
251
|
+
"progress",
|
|
252
|
+
"loadeddata",
|
|
253
|
+
"canplay",
|
|
254
|
+
"seeked",
|
|
255
|
+
"error",
|
|
256
|
+
"emptied",
|
|
257
|
+
"play"
|
|
258
|
+
], u = (n) => {
|
|
259
|
+
if (!o) {
|
|
260
|
+
o = !0, clearTimeout(p);
|
|
261
|
+
for (let e of l) i.removeEventListener(e, f);
|
|
262
|
+
t.signal?.removeEventListener("abort", d), this._cancelPrepareSeek === d && (this._cancelPrepareSeek = void 0), n === void 0 ? (this._offset = e, r()) : a(n);
|
|
263
|
+
}
|
|
264
|
+
}, d = () => u(new DOMException("Seek preparation aborted", "AbortError")), f = (t) => {
|
|
265
|
+
if (!o) try {
|
|
266
|
+
if (i.error || t?.type === "error") {
|
|
267
|
+
u(Error(i.error?.message || "Media failed during seek preparation"));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (t?.type === "emptied" && s || t?.type === "play") {
|
|
271
|
+
d();
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (i.readyState < 1) return;
|
|
275
|
+
if (!Number.isFinite(i.duration)) {
|
|
276
|
+
u(new DOMException("Seek preparation requires finite-duration media", "NotSupportedError"));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (e > i.duration) {
|
|
280
|
+
u(/* @__PURE__ */ RangeError("Seek time exceeds duration"));
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (s) t?.type === "seeked" && (c = !1);
|
|
284
|
+
else {
|
|
285
|
+
let t = !1;
|
|
286
|
+
for (let n = 0; n < i.seekable.length; n++) e >= i.seekable.start(n) && e <= i.seekable.end(n) && (t = !0);
|
|
287
|
+
if (!t) return;
|
|
288
|
+
s = !0, c = i.seeking || i.currentTime !== e, i.currentTime !== e && (i.currentTime = e);
|
|
289
|
+
}
|
|
290
|
+
!c && !i.seeking && i.readyState >= 2 && Math.abs(i.currentTime - e) < .001 && u();
|
|
291
|
+
} catch (e) {
|
|
292
|
+
u(e);
|
|
293
|
+
}
|
|
294
|
+
}, p = setTimeout(() => u(new DOMException("Seek preparation timed out", "TimeoutError")), n);
|
|
295
|
+
this._cancelPrepareSeek = d;
|
|
296
|
+
for (let e of l) i.addEventListener(e, f);
|
|
297
|
+
t.signal?.addEventListener("abort", d, { once: !0 }), f();
|
|
298
|
+
}), this.emit("seek", e);
|
|
299
|
+
}
|
|
162
300
|
seek(e) {
|
|
163
301
|
if (!this.source || !this.gainNode || !this.panner) throw Error("Cannot seek a sound that has been cleaned up");
|
|
164
302
|
if (!Number.isFinite(e) || e < 0 || this.origin.region !== void 0 && e > this.duration) throw Error("Invalid time value for seek");
|
|
165
|
-
if (this.
|
|
303
|
+
if (this._clipRegion && e >= this._clipRegion.end) throw RangeError("Seek time must precede the clip end");
|
|
304
|
+
if (this._cancelPrepareSeek?.(), this.origin.soundType === "streaming" && "mediaElement" in this.source && (this.source.mediaElement.seekable?.length ?? 0) === 0) throw this.source.mediaElement.duration === Infinity ? Error("Live media-element streams do not expose a seekable range") : Error("Media-element stream does not currently expose a seekable range");
|
|
166
305
|
let t = this._state === "playing";
|
|
167
306
|
t && this.pause(), this._offset = e, "mediaElement" in this.source && this.source.mediaElement && (this.source.mediaElement.currentTime = e), t && this.play(), this.emit("seek", e);
|
|
168
307
|
}
|
|
169
308
|
get currentTime() {
|
|
170
309
|
if (this._state === "playing") {
|
|
310
|
+
if (this.source && "mediaElement" in this.source) return this.source.mediaElement.currentTime;
|
|
171
311
|
let e = Math.max(0, (this.context.currentTime - this._startTime) * this._playbackRate), t = this._offset + e;
|
|
172
312
|
return this.regionRelativeOffset(t);
|
|
173
313
|
} else return this._offset;
|
|
@@ -179,7 +319,7 @@ var s = class s extends a {
|
|
|
179
319
|
recreateSource() {
|
|
180
320
|
if (!this.buffer || !this.panner || !this.context || !this.gainNode) throw Error("Cannot recreate source of a sound that has been cleaned up");
|
|
181
321
|
try {
|
|
182
|
-
this.source && (!("mediaElement" in this.source) && "onended" in this.source && (this.source.onended = null), this.source.disconnect()), this.source = this.context.createBufferSource(), this.source.buffer = this.buffer, this.setEffectChainEndpoints(this.source, this.panner), this.
|
|
322
|
+
this.source && (!("mediaElement" in this.source) && "onended" in this.source && (this.source.onended = null), this.source.disconnect()), this.source = this.context.createBufferSource(), this._scheduledStop = void 0, this.source.buffer = this.buffer, this.setEffectChainEndpoints(this.source, this.panner), this.setupSourceNode(this.source), this.playbackRate = this._playbackRate, this.configureBufferLoopSource();
|
|
183
323
|
} catch (e) {
|
|
184
324
|
throw this.emitAsync("error", {
|
|
185
325
|
error: e,
|
|
@@ -194,7 +334,7 @@ var s = class s extends a {
|
|
|
194
334
|
"loop" in this.source && (this.source.loop = e), "mediaElement" in this.source && this.source.mediaElement && (this.source.mediaElement.loop = e);
|
|
195
335
|
}
|
|
196
336
|
cleanup() {
|
|
197
|
-
if (this._mediaPlayPromise = void 0, this.source) {
|
|
337
|
+
if (this._scheduledStop = void 0, this._cancelPrepareSeek?.(), this._mediaPreparationGate?.gain.setValueAtTime(0, this.context.currentTime), this._mediaPlayPromise = void 0, this.source) {
|
|
198
338
|
if ("mediaElement" in this.source && this.source.mediaElement) {
|
|
199
339
|
let e = this.source.mediaElement;
|
|
200
340
|
e.pause();
|
|
@@ -203,7 +343,7 @@ var s = class s extends a {
|
|
|
203
343
|
} catch {}
|
|
204
344
|
e.loop = !1, e.onended = null, e.src = "", e.load();
|
|
205
345
|
} else "onended" in this.source && (this.source.onended = null, "stop" in this.source && this._state === "playing" && this.source.stop());
|
|
206
|
-
this._offset = 0, this._startTime = 0, this.markStopped(), this.currentLoop = 0, this.source.disconnect(), this.source = void 0, super.cleanup(), this._occlusion?.destroy(), this._occlusion = void 0;
|
|
346
|
+
this._offset = 0, this._startTime = 0, this.markStopped(), this.currentLoop = 0, this.source.disconnect(), this.source = void 0, super.cleanup(), this._mediaPreparationGate?.disconnect(), this._mediaPreparationGate = void 0, this._occlusion?.destroy(), this._occlusion = void 0;
|
|
207
347
|
}
|
|
208
348
|
}
|
|
209
349
|
assertNotCleanedUp() {
|
|
@@ -218,10 +358,15 @@ var s = class s extends a {
|
|
|
218
358
|
loop(e) {
|
|
219
359
|
if (!this.source) throw Error("Cannot loop a sound that has been cleaned up");
|
|
220
360
|
if (e !== void 0) {
|
|
221
|
-
|
|
222
|
-
|
|
361
|
+
this._validateLoopCount(e);
|
|
362
|
+
let t = this.loopCount, n = e === "infinite" ? "infinite" : Math.max(0, e), r = t === "infinite" != (n === "infinite");
|
|
363
|
+
if (r && (this.origin.region || this._clipRegion) && this._state === "playing" && "loop" in this.source && "stop" in this.source) {
|
|
223
364
|
let e = Math.max(this._startTime, this.context.currentTime), r = Math.max(0, (this.context.currentTime - this._startTime) * this._playbackRate);
|
|
224
|
-
return this._offset = this.regionRelativeOffset(this.regionRelativeOffset(this._offset + r, t), n), "onended" in this.source && (this.source.onended = null), this.source.stop(), this.loopCount = n, this.currentLoop = 0, this._startTime = e, this.recreateSource(), this.startBufferSource(e), this.loopCount;
|
|
365
|
+
return this._offset = this.regionRelativeOffset(this.regionRelativeOffset(this._offset + r, t), n), "onended" in this.source && (this.source.onended = null), this.source.stop(), this._scheduledStop = void 0, this.loopCount = n, this.currentLoop = 0, this._startTime = e, this.recreateSource(), this.startBufferSource(e), this.loopCount;
|
|
366
|
+
}
|
|
367
|
+
if (r && this._state === "playing" && "buffer" in this.source) {
|
|
368
|
+
let e = Math.max(0, (this.context.currentTime - this._startTime) * this._playbackRate);
|
|
369
|
+
this._offset = this.regionRelativeOffset(this._offset + e, t), this._startTime = Math.max(this._startTime, this.context.currentTime);
|
|
225
370
|
}
|
|
226
371
|
this.loopCount = n, this.currentLoop = 0;
|
|
227
372
|
}
|
|
@@ -233,15 +378,24 @@ var s = class s extends a {
|
|
|
233
378
|
return this.loopCount;
|
|
234
379
|
}
|
|
235
380
|
configureBufferLoopSource() {
|
|
236
|
-
!this.source || !("loop" in this.source) || (this.source.loop = this.loopCount === "infinite", this.source.buffer && (this.source.loopEnd = this.origin.region ? this.origin.region.start + this.origin.region.duration : this.source.buffer.duration, this.source.loopStart = this.origin.region?.start ?? 0));
|
|
381
|
+
!this.source || !("loop" in this.source) || (this.source.loop = this.loopCount === "infinite", this.source.buffer && (this.source.loopEnd = this._clipRegion ? (this.origin.region?.start ?? 0) + this._clipRegion.end : this.origin.region ? this.origin.region.start + this.origin.region.duration : this.source.buffer.duration, this.source.loopStart = (this.origin.region?.start ?? 0) + (this._clipRegion?.start ?? 0)));
|
|
237
382
|
}
|
|
238
383
|
regionRelativeOffset(e, t = this.loopCount) {
|
|
384
|
+
if (this._clipRegion) {
|
|
385
|
+
let { start: n, end: r } = this._clipRegion;
|
|
386
|
+
return t === "infinite" && e >= r ? n + (e - n) % (r - n) : Math.min(r, e);
|
|
387
|
+
}
|
|
239
388
|
let n = this.origin.region;
|
|
240
|
-
return n ? t === "infinite" ? e % n.duration : Math.min(n.duration, e) : e;
|
|
389
|
+
return n ? t === "infinite" ? e % n.duration : Math.min(n.duration, e) : t === "infinite" && this.buffer && this.buffer.duration > 0 ? e % this.buffer.duration : e;
|
|
241
390
|
}
|
|
242
391
|
startBufferSource(e) {
|
|
243
392
|
if (!this.source || !("start" in this.source)) return !1;
|
|
244
393
|
let t = this.origin.region;
|
|
394
|
+
if (this._clipRegion) {
|
|
395
|
+
if (this._offset >= this._clipRegion.end) return !1;
|
|
396
|
+
let n = (t?.start ?? 0) + this._offset;
|
|
397
|
+
return this.loopCount === "infinite" ? this.source.start(e, n) : this.source.start(e, n, this._clipRegion.end - this._offset), !0;
|
|
398
|
+
}
|
|
245
399
|
if (!t) return this.source.start(e, this._offset), !0;
|
|
246
400
|
if (this._offset === t.duration) return !1;
|
|
247
401
|
let n = t.start + this._offset;
|
|
@@ -283,7 +437,8 @@ var s = class s extends a {
|
|
|
283
437
|
}
|
|
284
438
|
clone(e = {}) {
|
|
285
439
|
if (!this.source || !this.gainNode || !this.context) throw Error("Cannot clone a sound that has been cleaned up");
|
|
286
|
-
|
|
440
|
+
if (this._scheduledStop) throw Error("Cannot clone a playback with an armed absolute end");
|
|
441
|
+
this._validateLoopCount(e.loopCount ?? this.loopCount), i(e.spatialSmoothingTau ?? this.spatialSmoothingTau);
|
|
287
442
|
let t = e.panType ?? this.panType;
|
|
288
443
|
r(e, t);
|
|
289
444
|
let n = this.context.createGain();
|
|
@@ -296,7 +451,7 @@ var s = class s extends a {
|
|
|
296
451
|
a = this.context.createMediaElementSource(e);
|
|
297
452
|
} else throw Error("Unsupported source type");
|
|
298
453
|
let o = e.loopCount === void 0 ? this.loopCount : e.loopCount, c = new s(this.origin, a, n);
|
|
299
|
-
return c.loopCount = o, c.currentLoop = this.currentLoop, c.setPanType(t, this.context), c.spatialSmoothingTau = e.spatialSmoothingTau ?? this.spatialSmoothingTau, t === "HRTF" ? (this.panType === "HRTF" && (c.threeDOptions = this.threeDOptions), e.threeDOptions !== void 0 && (c.threeDOptions = e.threeDOptions), e.position !== void 0 && (c.position = e.position)) : c.stereoPan = e.stereoPan ?? this.stereoPan ?? 0, c.volume = this.volume, c.playbackRate = this._playbackRate, c._offset = this._offset, c.setOcclusion(this.occlusion, 0), this._fadeInConfig && (c._fadeInConfig = { ...this._fadeInConfig }), this._fadeOutConfig && (c._fadeOutConfig = { ...this._fadeOutConfig }), this._state === "paused" ? c.markPaused() : this._state === "stopped" && c.markStopped(), this._filters.forEach((e) => {
|
|
454
|
+
return c.loopCount = o, c._clipRegion = this._clipRegion && { ...this._clipRegion }, c.currentLoop = this.currentLoop, c.setPanType(t, this.context), c.spatialSmoothingTau = e.spatialSmoothingTau ?? this.spatialSmoothingTau, t === "HRTF" ? (this.panType === "HRTF" && (c.threeDOptions = this.threeDOptions), e.threeDOptions !== void 0 && (c.threeDOptions = e.threeDOptions), e.position !== void 0 && (c.position = e.position)) : c.stereoPan = e.stereoPan ?? this.stereoPan ?? 0, c.volume = this.volume, c.playbackRate = this._playbackRate, c._offset = this._clipRegion && this._state === "playing" ? this.currentTime : this._offset, c.setOcclusion(this.occlusion, 0), this._fadeInConfig && (c._fadeInConfig = { ...this._fadeInConfig }), this._fadeOutConfig && (c._fadeOutConfig = { ...this._fadeOutConfig }), this._state === "paused" ? c.markPaused() : this._state === "stopped" && c.markStopped(), this._filters.forEach((e) => {
|
|
300
455
|
let t = this.context.createBiquadFilter();
|
|
301
456
|
t.type = e.type, t.frequency.value = e.frequency.value, t.Q.value = e.Q.value, t.gain.value = e.gain.value, c.addFilter(t);
|
|
302
457
|
}), this._pitchFactor !== 1 && c.setPitchShift(this._pitchFactor).catch(() => {}), this._state === "playing" && c.play(), this.origin.playbacks.push(c), c;
|