@editframe/elements 0.7.0-beta.9 → 0.8.0-beta.10
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/dist/EF_FRAMEGEN.d.ts +43 -0
- package/dist/EF_INTERACTIVE.d.ts +1 -0
- package/dist/assets/dist/EncodedAsset.js +560 -0
- package/dist/assets/dist/MP4File.js +170 -0
- package/dist/assets/dist/memoize.js +14 -0
- package/dist/elements/CrossUpdateController.d.ts +8 -0
- package/dist/elements/EFAudio.d.ts +9 -0
- package/dist/elements/EFCaptions.d.ts +38 -0
- package/dist/elements/EFImage.d.ts +13 -0
- package/dist/elements/EFMedia.d.ts +63 -0
- package/dist/elements/EFSourceMixin.d.ts +11 -0
- package/dist/elements/EFTemporal.d.ts +40 -0
- package/dist/elements/EFTimegroup.browsertest.d.ts +11 -0
- package/dist/elements/EFTimegroup.d.ts +36 -0
- package/dist/elements/EFVideo.d.ts +13 -0
- package/dist/elements/EFWaveform.d.ts +29 -0
- package/dist/elements/FetchMixin.d.ts +7 -0
- package/dist/elements/TimegroupController.d.ts +13 -0
- package/dist/elements/durationConverter.d.ts +12 -0
- package/dist/elements/parseTimeToMs.d.ts +1 -0
- package/{src/EF_FRAMEGEN.ts → dist/elements/src/EF_FRAMEGEN.js} +35 -115
- package/dist/elements/src/EF_INTERACTIVE.js +7 -0
- package/dist/elements/src/elements/CrossUpdateController.js +16 -0
- package/dist/elements/src/elements/EFAudio.js +54 -0
- package/dist/elements/src/elements/EFCaptions.js +169 -0
- package/dist/elements/src/elements/EFImage.js +80 -0
- package/dist/elements/src/elements/EFMedia.js +356 -0
- package/dist/elements/src/elements/EFSourceMixin.js +55 -0
- package/dist/elements/src/elements/EFTemporal.js +283 -0
- package/dist/elements/src/elements/EFTimegroup.js +338 -0
- package/dist/elements/src/elements/EFVideo.js +110 -0
- package/dist/elements/src/elements/EFWaveform.js +226 -0
- package/dist/elements/src/elements/FetchMixin.js +28 -0
- package/dist/elements/src/elements/TimegroupController.js +20 -0
- package/dist/elements/src/elements/durationConverter.js +8 -0
- package/dist/elements/src/elements/parseTimeToMs.js +13 -0
- package/dist/elements/src/elements/util.js +11 -0
- package/dist/elements/src/gui/ContextMixin.js +246 -0
- package/dist/elements/src/gui/EFFilmstrip.js +731 -0
- package/dist/elements/src/gui/EFPreview.js +45 -0
- package/dist/elements/src/gui/EFToggleLoop.js +39 -0
- package/dist/elements/src/gui/EFTogglePlay.js +43 -0
- package/dist/elements/src/gui/EFWorkbench.js +128 -0
- package/dist/elements/src/gui/TWMixin.css.js +4 -0
- package/dist/elements/src/gui/TWMixin.js +36 -0
- package/dist/elements/src/gui/apiHostContext.js +5 -0
- package/dist/elements/src/gui/efContext.js +7 -0
- package/dist/elements/src/gui/fetchContext.js +5 -0
- package/dist/elements/src/gui/focusContext.js +5 -0
- package/dist/elements/src/gui/focusedElementContext.js +7 -0
- package/dist/elements/src/gui/playingContext.js +7 -0
- package/dist/elements/src/index.js +31 -0
- package/dist/elements/src/msToTimeCode.js +15 -0
- package/dist/elements/util.d.ts +3 -0
- package/dist/gui/ContextMixin.d.ts +19 -0
- package/dist/gui/EFFilmstrip.d.ts +148 -0
- package/dist/gui/EFPreview.d.ts +12 -0
- package/dist/gui/EFToggleLoop.d.ts +12 -0
- package/dist/gui/EFTogglePlay.d.ts +12 -0
- package/dist/gui/EFWorkbench.d.ts +18 -0
- package/dist/gui/TWMixin.d.ts +2 -0
- package/dist/gui/apiHostContext.d.ts +3 -0
- package/dist/gui/efContext.d.ts +4 -0
- package/dist/gui/fetchContext.d.ts +3 -0
- package/dist/gui/focusContext.d.ts +6 -0
- package/dist/gui/focusedElementContext.d.ts +3 -0
- package/dist/gui/playingContext.d.ts +6 -0
- package/dist/index.d.ts +12 -0
- package/dist/msToTimeCode.d.ts +1 -0
- package/dist/style.css +802 -0
- package/package.json +7 -10
- package/src/elements/EFAudio.ts +1 -1
- package/src/elements/EFCaptions.ts +23 -17
- package/src/elements/EFImage.ts +3 -3
- package/src/elements/EFMedia.ts +48 -17
- package/src/elements/EFSourceMixin.ts +1 -1
- package/src/elements/EFTemporal.ts +101 -6
- package/src/elements/EFTimegroup.browsertest.ts +3 -3
- package/src/elements/EFTimegroup.ts +30 -47
- package/src/elements/EFVideo.ts +2 -2
- package/src/elements/EFWaveform.ts +9 -9
- package/src/elements/FetchMixin.ts +5 -3
- package/src/elements/TimegroupController.ts +1 -1
- package/src/elements/durationConverter.ts +21 -1
- package/src/elements/parseTimeToMs.ts +1 -0
- package/src/elements/util.ts +1 -1
- package/src/gui/ContextMixin.ts +268 -0
- package/src/gui/EFFilmstrip.ts +61 -171
- package/src/gui/EFPreview.ts +39 -0
- package/src/gui/EFToggleLoop.ts +34 -0
- package/src/gui/EFTogglePlay.ts +38 -0
- package/src/gui/EFWorkbench.ts +11 -109
- package/src/gui/TWMixin.ts +10 -3
- package/src/gui/apiHostContext.ts +3 -0
- package/src/gui/efContext.ts +6 -0
- package/src/gui/fetchContext.ts +5 -0
- package/src/gui/focusContext.ts +7 -0
- package/src/gui/focusedElementContext.ts +5 -0
- package/src/gui/playingContext.ts +5 -0
- package/CHANGELOG.md +0 -7
- package/postcss.config.cjs +0 -12
- package/src/EF_INTERACTIVE.ts +0 -2
- package/src/elements.css +0 -22
- package/src/index.ts +0 -33
- package/tailwind.config.ts +0 -10
- package/tsconfig.json +0 -4
- package/vite.config.ts +0 -8
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { EFAudio } from "./EFAudio";
|
|
1
|
+
import { EFAudio } from "./EFAudio.ts";
|
|
2
2
|
|
|
3
3
|
import { LitElement, html } from "lit";
|
|
4
4
|
import { customElement, property } from "lit/decorators.js";
|
|
5
|
-
import { EFVideo } from "./EFVideo";
|
|
6
|
-
import { EFTemporal } from "./EFTemporal";
|
|
7
|
-
import { CrossUpdateController } from "./CrossUpdateController";
|
|
8
|
-
import { TWMixin } from "../gui/TWMixin";
|
|
5
|
+
import { EFVideo } from "./EFVideo.ts";
|
|
6
|
+
import { EFTemporal } from "./EFTemporal.ts";
|
|
7
|
+
import { CrossUpdateController } from "./CrossUpdateController.ts";
|
|
8
|
+
import { TWMixin } from "../gui/TWMixin.ts";
|
|
9
9
|
import { Task } from "@lit/task";
|
|
10
10
|
import * as d3 from "d3";
|
|
11
11
|
import { type Ref, createRef, ref } from "lit/directives/ref.js";
|
|
12
|
-
import { EF_INTERACTIVE } from "../EF_INTERACTIVE";
|
|
12
|
+
import { EF_INTERACTIVE } from "../EF_INTERACTIVE.ts";
|
|
13
13
|
|
|
14
14
|
@customElement("ef-waveform")
|
|
15
15
|
export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
@@ -351,7 +351,7 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
351
351
|
if (!this.targetElement.audioBufferTask.value) {
|
|
352
352
|
return;
|
|
353
353
|
}
|
|
354
|
-
if (this.targetElement.
|
|
354
|
+
if (this.targetElement.trimAdjustedOwnCurrentTimeMs > 0) {
|
|
355
355
|
const audioContext = new OfflineAudioContext(2, 48000 / 25, 48000);
|
|
356
356
|
const audioBufferSource = audioContext.createBufferSource();
|
|
357
357
|
audioBufferSource.buffer =
|
|
@@ -364,7 +364,7 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
364
364
|
0,
|
|
365
365
|
Math.max(
|
|
366
366
|
0,
|
|
367
|
-
(this.targetElement.
|
|
367
|
+
(this.targetElement.trimAdjustedOwnCurrentTimeMs -
|
|
368
368
|
this.targetElement.audioBufferTask.value.startOffsetMs) /
|
|
369
369
|
1000,
|
|
370
370
|
),
|
|
@@ -412,6 +412,6 @@ export class EFWaveform extends EFTemporal(TWMixin(LitElement)) {
|
|
|
412
412
|
if (target instanceof EFAudio || target instanceof EFVideo) {
|
|
413
413
|
return target;
|
|
414
414
|
}
|
|
415
|
-
throw new Error("Invalid target, must be an EFAudio element");
|
|
415
|
+
throw new Error("Invalid target, must be an EFAudio or EFVideo element");
|
|
416
416
|
}
|
|
417
417
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { consume } from "@lit/context";
|
|
2
1
|
import type { LitElement } from "lit";
|
|
3
|
-
import {
|
|
2
|
+
import { consume } from "@lit/context";
|
|
4
3
|
import { state } from "lit/decorators/state.js";
|
|
5
4
|
|
|
5
|
+
import { fetchContext } from "../gui/fetchContext.ts";
|
|
6
|
+
|
|
6
7
|
export declare class FetchMixinInterface {
|
|
7
8
|
fetch: typeof fetch;
|
|
8
9
|
}
|
|
@@ -12,7 +13,8 @@ export function FetchMixin<T extends Constructor<LitElement>>(superClass: T) {
|
|
|
12
13
|
class FetchElement extends superClass {
|
|
13
14
|
@consume({ context: fetchContext, subscribe: true })
|
|
14
15
|
@state()
|
|
15
|
-
fetch
|
|
16
|
+
fetch: (url: string, init?: RequestInit) => Promise<Response> =
|
|
17
|
+
fetch.bind(window);
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
return FetchElement as Constructor<FetchMixinInterface> & T;
|
|
@@ -1,6 +1,26 @@
|
|
|
1
|
-
import { parseTimeToMs } from "./parseTimeToMs";
|
|
1
|
+
import { parseTimeToMs } from "./parseTimeToMs.ts";
|
|
2
2
|
|
|
3
3
|
export const durationConverter = {
|
|
4
4
|
fromAttribute: (value: string): number => parseTimeToMs(value),
|
|
5
5
|
toAttribute: (value: number) => `${value}s`,
|
|
6
6
|
};
|
|
7
|
+
|
|
8
|
+
const positiveDurationConverter = (error: string) => {
|
|
9
|
+
return {
|
|
10
|
+
fromAttribute: (value: string): number => {
|
|
11
|
+
if (value.startsWith("-")) {
|
|
12
|
+
throw new Error(error);
|
|
13
|
+
}
|
|
14
|
+
return parseTimeToMs(value);
|
|
15
|
+
},
|
|
16
|
+
toAttribute: (value: number) => `${value}s`,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const trimDurationConverter = positiveDurationConverter(
|
|
21
|
+
"Trimstart & trimend must be a positive value in milliseconds or seconds (1s, 1000ms)",
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export const imageDurationConverter = positiveDurationConverter(
|
|
25
|
+
"Image duration must be a positive value in milliseconds or seconds (1s, 1000ms)",
|
|
26
|
+
);
|
package/src/elements/util.ts
CHANGED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import type { LitElement } from "lit";
|
|
2
|
+
import { provide } from "@lit/context";
|
|
3
|
+
import { property, state } from "lit/decorators.js";
|
|
4
|
+
|
|
5
|
+
import { focusContext, type FocusContext } from "./focusContext.ts";
|
|
6
|
+
import { focusedElementContext } from "./focusedElementContext.ts";
|
|
7
|
+
import { fetchContext } from "./fetchContext.ts";
|
|
8
|
+
import { createRef } from "lit/directives/ref.js";
|
|
9
|
+
import { loopContext, playingContext } from "./playingContext.ts";
|
|
10
|
+
import type { EFTimegroup } from "../elements/EFTimegroup.ts";
|
|
11
|
+
import { efContext } from "./efContext.ts";
|
|
12
|
+
|
|
13
|
+
export declare class ContextMixinInterface {
|
|
14
|
+
signingURL?: string;
|
|
15
|
+
rendering: boolean;
|
|
16
|
+
playing: boolean;
|
|
17
|
+
loop: boolean;
|
|
18
|
+
currentTimeMs: number;
|
|
19
|
+
focusedElement?: HTMLElement;
|
|
20
|
+
stageRef: ReturnType<typeof createRef<HTMLDivElement>>;
|
|
21
|
+
canvasRef: ReturnType<typeof createRef<HTMLElement>>;
|
|
22
|
+
targetTimegroup: EFTimegroup | null;
|
|
23
|
+
play(): void;
|
|
24
|
+
pause(): void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type Constructor<T = {}> = new (...args: any[]) => T;
|
|
28
|
+
export function ContextMixin<T extends Constructor<LitElement>>(superClass: T) {
|
|
29
|
+
class ContextElement extends superClass {
|
|
30
|
+
@provide({ context: focusContext })
|
|
31
|
+
focusContext = this as FocusContext;
|
|
32
|
+
|
|
33
|
+
@provide({ context: focusedElementContext })
|
|
34
|
+
@state()
|
|
35
|
+
focusedElement?: HTMLElement;
|
|
36
|
+
|
|
37
|
+
@provide({ context: efContext })
|
|
38
|
+
efContext = this;
|
|
39
|
+
|
|
40
|
+
@provide({ context: fetchContext })
|
|
41
|
+
fetch = async (url: string, init: RequestInit = {}) => {
|
|
42
|
+
init.headers ||= {};
|
|
43
|
+
Object.assign(init.headers, {
|
|
44
|
+
"Content-Type": "application/json",
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (this.signingURL) {
|
|
48
|
+
if (!this.#URLTokens[url]) {
|
|
49
|
+
this.#URLTokens[url] = fetch(this.signingURL, {
|
|
50
|
+
method: "POST",
|
|
51
|
+
body: JSON.stringify({ url }),
|
|
52
|
+
}).then(async (response) => {
|
|
53
|
+
if (response.ok) {
|
|
54
|
+
return (await response.json()).token;
|
|
55
|
+
}
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Failed to sign URL: ${url}. SigningURL: ${this.signingURL} ${response.status} ${response.statusText}`,
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const urlToken = await this.#URLTokens[url];
|
|
63
|
+
|
|
64
|
+
Object.assign(init.headers, {
|
|
65
|
+
authorization: `Bearer ${urlToken}`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return fetch(url, init);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
#URLTokens: Record<string, Promise<string>> = {};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A URL that will be used to generated signed tokens for accessing media files from the
|
|
76
|
+
* editframe API. This is used to authenticate media requests per-user.
|
|
77
|
+
*/
|
|
78
|
+
@property({ type: String })
|
|
79
|
+
signingURL?: string;
|
|
80
|
+
|
|
81
|
+
@provide({ context: playingContext })
|
|
82
|
+
@property({ type: Boolean, reflect: true })
|
|
83
|
+
playing = false;
|
|
84
|
+
|
|
85
|
+
@provide({ context: loopContext })
|
|
86
|
+
@property({ type: Boolean, reflect: true })
|
|
87
|
+
loop = false;
|
|
88
|
+
|
|
89
|
+
@state()
|
|
90
|
+
private stageScale = 1;
|
|
91
|
+
|
|
92
|
+
@property({ type: Boolean })
|
|
93
|
+
rendering = false;
|
|
94
|
+
|
|
95
|
+
@state()
|
|
96
|
+
currentTimeMs = 0;
|
|
97
|
+
|
|
98
|
+
stageRef = createRef<HTMLDivElement>();
|
|
99
|
+
canvasRef = createRef<HTMLSlotElement>();
|
|
100
|
+
|
|
101
|
+
setStageScale = () => {
|
|
102
|
+
if (this.isConnected && !this.rendering) {
|
|
103
|
+
const canvasElement = this.canvasRef.value;
|
|
104
|
+
const stageElement = this.stageRef.value;
|
|
105
|
+
const canvasChild = canvasElement?.assignedElements()[0];
|
|
106
|
+
if (stageElement && canvasElement && canvasChild) {
|
|
107
|
+
// Determine the appropriate scale factor to make the canvas fit into
|
|
108
|
+
canvasElement.style.width = `${canvasChild.clientWidth}px`;
|
|
109
|
+
canvasElement.style.height = `${canvasChild.clientHeight}px`;
|
|
110
|
+
const stageWidth = stageElement.clientWidth;
|
|
111
|
+
const stageHeight = stageElement.clientHeight;
|
|
112
|
+
const canvasWidth = canvasElement.clientWidth;
|
|
113
|
+
const canvasHeight = canvasElement.clientHeight;
|
|
114
|
+
const stageRatio = stageWidth / stageHeight;
|
|
115
|
+
const canvasRatio = canvasWidth / canvasHeight;
|
|
116
|
+
if (stageRatio > canvasRatio) {
|
|
117
|
+
const scale = stageHeight / canvasHeight;
|
|
118
|
+
if (this.stageScale !== scale) {
|
|
119
|
+
canvasElement.style.transform = `scale(${scale})`;
|
|
120
|
+
canvasElement.style.transformOrigin = "top";
|
|
121
|
+
}
|
|
122
|
+
this.stageScale = scale;
|
|
123
|
+
} else {
|
|
124
|
+
const scale = stageWidth / canvasWidth;
|
|
125
|
+
if (this.stageScale !== scale) {
|
|
126
|
+
canvasElement.style.transform = `scale(${scale})`;
|
|
127
|
+
canvasElement.style.transformOrigin = "top";
|
|
128
|
+
}
|
|
129
|
+
this.stageScale = scale;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (this.isConnected) {
|
|
134
|
+
requestAnimationFrame(this.setStageScale);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
connectedCallback(): void {
|
|
139
|
+
super.connectedCallback();
|
|
140
|
+
// Preferrably we would use a resizeObserver, but it is difficult to get the first resize
|
|
141
|
+
// timed correctly. So we use requestAnimationFrame as a stop-gap.
|
|
142
|
+
requestAnimationFrame(this.setStageScale);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
update(changedProperties: Map<string | number | symbol, unknown>) {
|
|
146
|
+
if (changedProperties.has("playing")) {
|
|
147
|
+
if (this.playing) {
|
|
148
|
+
this.#startPlayback();
|
|
149
|
+
} else {
|
|
150
|
+
this.#stopPlayback();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (changedProperties.has("currentTimeMs") && this.targetTimegroup) {
|
|
155
|
+
if (this.targetTimegroup.currentTimeMs !== this.currentTimeMs) {
|
|
156
|
+
this.targetTimegroup.currentTimeMs = this.currentTimeMs;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
super.update(changedProperties);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
get targetTimegroup() {
|
|
163
|
+
return this.querySelector("ef-timegroup");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
play() {
|
|
167
|
+
this.playing = true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
pause() {
|
|
171
|
+
this.playing = false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#playbackAudioContext: AudioContext | null = null;
|
|
175
|
+
#playbackAnimationFrameRequest: number | null = null;
|
|
176
|
+
#AUDIO_PLAYBACK_SLICE_MS = 1000;
|
|
177
|
+
|
|
178
|
+
#syncPlayheadToAudioContext(target: EFTimegroup, startMs: number) {
|
|
179
|
+
this.currentTimeMs =
|
|
180
|
+
startMs + (this.#playbackAudioContext?.currentTime ?? 0) * 1000;
|
|
181
|
+
this.#playbackAnimationFrameRequest = requestAnimationFrame(() => {
|
|
182
|
+
this.#syncPlayheadToAudioContext(target, startMs);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async #stopPlayback() {
|
|
187
|
+
if (this.#playbackAudioContext) {
|
|
188
|
+
if (this.#playbackAudioContext.state !== "closed") {
|
|
189
|
+
await this.#playbackAudioContext.close();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (this.#playbackAnimationFrameRequest) {
|
|
193
|
+
cancelAnimationFrame(this.#playbackAnimationFrameRequest);
|
|
194
|
+
}
|
|
195
|
+
this.#playbackAudioContext = null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async #startPlayback() {
|
|
199
|
+
await this.#stopPlayback();
|
|
200
|
+
const timegroup = this.targetTimegroup;
|
|
201
|
+
if (!timegroup) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let currentMs = timegroup.currentTimeMs;
|
|
206
|
+
let bufferCount = 0;
|
|
207
|
+
this.#playbackAudioContext = new AudioContext({
|
|
208
|
+
latencyHint: "playback",
|
|
209
|
+
});
|
|
210
|
+
if (this.#playbackAnimationFrameRequest) {
|
|
211
|
+
cancelAnimationFrame(this.#playbackAnimationFrameRequest);
|
|
212
|
+
}
|
|
213
|
+
this.#syncPlayheadToAudioContext(timegroup, currentMs);
|
|
214
|
+
const playbackContext = this.#playbackAudioContext;
|
|
215
|
+
await playbackContext.suspend();
|
|
216
|
+
|
|
217
|
+
const fillBuffer = async () => {
|
|
218
|
+
if (bufferCount > 1) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const canFillBuffer = await queueBufferSource();
|
|
222
|
+
if (canFillBuffer) {
|
|
223
|
+
fillBuffer();
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const fromMs = currentMs;
|
|
228
|
+
const toMs = timegroup.endTimeMs;
|
|
229
|
+
|
|
230
|
+
const queueBufferSource = async () => {
|
|
231
|
+
if (currentMs >= toMs) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
const startMs = currentMs;
|
|
235
|
+
const endMs = currentMs + this.#AUDIO_PLAYBACK_SLICE_MS;
|
|
236
|
+
currentMs += this.#AUDIO_PLAYBACK_SLICE_MS;
|
|
237
|
+
const audioBuffer = await timegroup.renderAudio(startMs, endMs);
|
|
238
|
+
bufferCount++;
|
|
239
|
+
const source = playbackContext.createBufferSource();
|
|
240
|
+
source.buffer = audioBuffer;
|
|
241
|
+
source.connect(playbackContext.destination);
|
|
242
|
+
source.start((startMs - fromMs) / 1000);
|
|
243
|
+
source.onended = () => {
|
|
244
|
+
bufferCount--;
|
|
245
|
+
if (endMs >= toMs) {
|
|
246
|
+
this.pause();
|
|
247
|
+
if (this.loop) {
|
|
248
|
+
this.updateComplete.then(() => {
|
|
249
|
+
this.currentTimeMs = 0;
|
|
250
|
+
this.updateComplete.then(() => {
|
|
251
|
+
this.play();
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
fillBuffer();
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
return true;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
await fillBuffer();
|
|
263
|
+
await playbackContext.resume();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return ContextElement as Constructor<ContextMixinInterface> & T;
|
|
268
|
+
}
|