@idlebox/browser 0.0.51 → 0.0.52
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/package.json +6 -10
- package/lib/dom copy/web-audio/audio-player.d.ts +0 -44
- package/lib/dom copy/web-audio/audio-player.d.ts.map +0 -1
- package/lib/dom copy/web-audio/audio-player.js +0 -242
- package/lib/dom copy/web-audio/audio-player.js.map +0 -1
- package/lib/index.d.ts +0 -4
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -4
- package/lib/index.js.map +0 -1
- package/src/dom copy/web-audio/audio-player.ts +0 -278
- package/src/index.ts +0 -3
package/package.json
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idlebox/browser",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.52",
|
|
5
5
|
"exports": {
|
|
6
|
-
".": {
|
|
7
|
-
"default": "./lib/index.js"
|
|
8
|
-
},
|
|
9
6
|
"./react": {
|
|
10
7
|
"default": "./lib/react/autoindex.generated.js"
|
|
11
8
|
},
|
|
@@ -16,14 +13,14 @@
|
|
|
16
13
|
},
|
|
17
14
|
"sideEffects": false,
|
|
18
15
|
"dependencies": {
|
|
19
|
-
"@idlebox/common": "^1.5.
|
|
16
|
+
"@idlebox/common": "^1.5.11"
|
|
20
17
|
},
|
|
21
18
|
"devDependencies": {
|
|
22
|
-
"@types/react": "^19.
|
|
23
|
-
"@types/react-dom": "^19.
|
|
19
|
+
"@types/react": "^19.2.8",
|
|
20
|
+
"@types/react-dom": "^19.2.3",
|
|
24
21
|
"@build-script/single-dog-asset": "latest",
|
|
25
|
-
"@
|
|
26
|
-
"@
|
|
22
|
+
"@types/node": "npm:@idlebox/empty@1.0.1",
|
|
23
|
+
"@mpis/run": "^0.0.17"
|
|
27
24
|
},
|
|
28
25
|
"optionalDependencies": {
|
|
29
26
|
"react": "*"
|
|
@@ -31,7 +28,6 @@
|
|
|
31
28
|
"license": "MIT",
|
|
32
29
|
"author": "GongT <admin@gongt.me>",
|
|
33
30
|
"repository": "https://github.com/GongT/baobao",
|
|
34
|
-
"main": "./lib/index.js",
|
|
35
31
|
"scripts": {
|
|
36
32
|
"build": "mpis-run build",
|
|
37
33
|
"watch": "mpis-run watch",
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { type IDisposable } from '@idlebox/common';
|
|
2
|
-
export declare class StreamAppender {
|
|
3
|
-
private readonly stream;
|
|
4
|
-
private readonly dfd;
|
|
5
|
-
private readonly queue;
|
|
6
|
-
private _finished;
|
|
7
|
-
constructor(stream: SourceBuffer);
|
|
8
|
-
private _pump;
|
|
9
|
-
private _error;
|
|
10
|
-
append(buffer: ArrayBuffer): void;
|
|
11
|
-
finish(): void;
|
|
12
|
-
terminate(): void;
|
|
13
|
-
wait(): Promise<void>;
|
|
14
|
-
}
|
|
15
|
-
export declare class MediaForPlayback {
|
|
16
|
-
private readonly mediaSource;
|
|
17
|
-
readonly ready: Promise<void>;
|
|
18
|
-
private readonly endDfd;
|
|
19
|
-
readonly id: number;
|
|
20
|
-
private static guid;
|
|
21
|
-
constructor();
|
|
22
|
-
private opened;
|
|
23
|
-
open(mime: string): Promise<StreamAppender>;
|
|
24
|
-
playToNewAudioElement(): HtmlAudioPlayer;
|
|
25
|
-
finish(): Promise<void>;
|
|
26
|
-
private disList;
|
|
27
|
-
_register(d: IDisposable): void;
|
|
28
|
-
dispose(): void;
|
|
29
|
-
}
|
|
30
|
-
export declare class HtmlAudioPlayer {
|
|
31
|
-
readonly element: HTMLAudioElement;
|
|
32
|
-
private readonly _humanSpeaking;
|
|
33
|
-
readonly onHumanSpeaking: import("@idlebox/common").EventRegister<boolean>;
|
|
34
|
-
constructor();
|
|
35
|
-
private tmr?;
|
|
36
|
-
private aboutToSetSpeaking;
|
|
37
|
-
private speaking;
|
|
38
|
-
private _moveSpeakState;
|
|
39
|
-
private disposed;
|
|
40
|
-
dispose(): void;
|
|
41
|
-
onEnd(fn: () => void): void;
|
|
42
|
-
}
|
|
43
|
-
export declare function disposeAudioElement(audio: HTMLAudioElement): void;
|
|
44
|
-
//# sourceMappingURL=audio-player.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"audio-player.d.ts","sourceRoot":"","sources":["../../../src/dom copy/web-audio/audio-player.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI7E,qBAAa,cAAc;IAKd,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA+B;IACnD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,SAAS,CAAS;gBAEG,MAAM,EAAE,YAAY;IAOjD,OAAO,CAAC,KAAK;IAgBb,OAAO,CAAC,MAAM;IAYd,MAAM,CAAC,MAAM,EAAE,WAAW;IAc1B,MAAM;IAMN,SAAS;IAOT,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrB;AAED,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,SAAgB,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IAEtD,SAAgB,EAAE,EAAE,MAAM,CAAC;IAE3B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAK;;IAkBxB,OAAO,CAAC,MAAM,CAAS;IACjB,IAAI,CAAC,IAAI,EAAE,MAAM;IAoBvB,qBAAqB;IAWd,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B,OAAO,CAAC,OAAO,CAAqB;IACpC,SAAS,CAAC,CAAC,EAAE,WAAW;IAIxB,OAAO;CAMP;AAED,qBAAa,eAAe;IAC3B,SAAgB,OAAO,EAAE,gBAAgB,CAAC;IAE1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0B;IACzD,SAAgB,eAAe,mDAA6B;;IA4B5D,OAAO,CAAC,GAAG,CAAC,CAAS;IACrB,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO;IAaP,KAAK,CAAC,EAAE,EAAE,MAAM,IAAI;CAWpB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,QAO1D"}
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import { DeferredPromise, Emitter } from '@idlebox/common';
|
|
2
|
-
const DELAY_CLOSE_MOUTH = 700;
|
|
3
|
-
export class StreamAppender {
|
|
4
|
-
stream;
|
|
5
|
-
dfd = new DeferredPromise();
|
|
6
|
-
queue = [];
|
|
7
|
-
_finished = false;
|
|
8
|
-
constructor(stream) {
|
|
9
|
-
this.stream = stream;
|
|
10
|
-
stream.mode = 'sequence';
|
|
11
|
-
stream.addEventListener('updateend', this._pump.bind(this));
|
|
12
|
-
stream.addEventListener('error', this._error.bind(this));
|
|
13
|
-
}
|
|
14
|
-
_pump() {
|
|
15
|
-
// console.log('[stream] pump %s', this.queue.length);
|
|
16
|
-
if (this.stream.updating) {
|
|
17
|
-
// console.error(' - busy');
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const next = this.queue.shift();
|
|
21
|
-
if (next) {
|
|
22
|
-
// console.log('playing %s bytes', next.byteLength);
|
|
23
|
-
this.stream.appendBuffer(next);
|
|
24
|
-
}
|
|
25
|
-
else if (this._finished) {
|
|
26
|
-
// console.log('StreamAppender: dispose');
|
|
27
|
-
this.dfd.complete();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
_error(e) {
|
|
31
|
-
// console.log('[stream] pump fail', e);
|
|
32
|
-
this.queue.length = 0;
|
|
33
|
-
this.finish();
|
|
34
|
-
const err = e.error;
|
|
35
|
-
if (!(err instanceof Error)) {
|
|
36
|
-
console.error('e似乎不是ErrorEvent', e);
|
|
37
|
-
}
|
|
38
|
-
this.dfd.error(err);
|
|
39
|
-
}
|
|
40
|
-
append(buffer) {
|
|
41
|
-
if (this._finished) {
|
|
42
|
-
throw new Error('不能在finish之后append');
|
|
43
|
-
}
|
|
44
|
-
this.queue.push(buffer);
|
|
45
|
-
if (!this.stream.updating) {
|
|
46
|
-
// console.log('[stream] pump start');
|
|
47
|
-
this._pump();
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
// console.log('[stream] queue data');
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
finish() {
|
|
54
|
-
// console.log('[stream] done.');
|
|
55
|
-
this._finished = true;
|
|
56
|
-
this._pump();
|
|
57
|
-
}
|
|
58
|
-
terminate() {
|
|
59
|
-
// console.log('[stream] done. (terminate)');
|
|
60
|
-
this._finished = true;
|
|
61
|
-
this.queue.length = 0;
|
|
62
|
-
this._pump();
|
|
63
|
-
}
|
|
64
|
-
wait() {
|
|
65
|
-
return this.dfd.p;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
export class MediaForPlayback {
|
|
69
|
-
mediaSource;
|
|
70
|
-
ready;
|
|
71
|
-
endDfd = new DeferredPromise();
|
|
72
|
-
id;
|
|
73
|
-
static guid = 0;
|
|
74
|
-
constructor() {
|
|
75
|
-
this.id = ++MediaForPlayback.guid;
|
|
76
|
-
// console.log('[media source %d] create', this.id);
|
|
77
|
-
const source = new window.MediaSource();
|
|
78
|
-
this.ready = new Promise((resolve) => {
|
|
79
|
-
const wrap = () => {
|
|
80
|
-
source.removeEventListener('sourceopen', wrap);
|
|
81
|
-
// console.log('[media source %d] sourceopen', this.id);
|
|
82
|
-
resolve();
|
|
83
|
-
};
|
|
84
|
-
source.addEventListener('sourceopen', wrap);
|
|
85
|
-
});
|
|
86
|
-
this.mediaSource = source;
|
|
87
|
-
}
|
|
88
|
-
opened = false;
|
|
89
|
-
async open(mime) {
|
|
90
|
-
if (this.opened)
|
|
91
|
-
throw new Error('duplicate call to MediaForPlayback.open()');
|
|
92
|
-
this.opened = true;
|
|
93
|
-
// console.log('[media source %d] open', this.id, mime);
|
|
94
|
-
await this.ready;
|
|
95
|
-
const buffer = this.mediaSource.addSourceBuffer(mime);
|
|
96
|
-
const appender = new StreamAppender(buffer);
|
|
97
|
-
appender.wait().finally(() => {
|
|
98
|
-
// console.log('[media source %d] appender finished', this.id);
|
|
99
|
-
this.mediaSource.endOfStream();
|
|
100
|
-
this.endDfd.complete();
|
|
101
|
-
this.dispose();
|
|
102
|
-
});
|
|
103
|
-
return appender;
|
|
104
|
-
}
|
|
105
|
-
playToNewAudioElement() {
|
|
106
|
-
const audio = new HtmlAudioPlayer();
|
|
107
|
-
audio.onEnd(() => {
|
|
108
|
-
this.dispose();
|
|
109
|
-
});
|
|
110
|
-
audio.element.src = URL.createObjectURL(this.mediaSource);
|
|
111
|
-
return audio;
|
|
112
|
-
}
|
|
113
|
-
finish() {
|
|
114
|
-
return this.endDfd.p;
|
|
115
|
-
}
|
|
116
|
-
disList = [];
|
|
117
|
-
_register(d) {
|
|
118
|
-
this.disList.push(d);
|
|
119
|
-
}
|
|
120
|
-
dispose() {
|
|
121
|
-
// console.log('[media source %d] dispose()', this.id);
|
|
122
|
-
for (const obj of this.disList) {
|
|
123
|
-
obj.dispose();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
export class HtmlAudioPlayer {
|
|
128
|
-
element;
|
|
129
|
-
_humanSpeaking = new Emitter();
|
|
130
|
-
onHumanSpeaking = this._humanSpeaking.event;
|
|
131
|
-
constructor() {
|
|
132
|
-
const audio = new Audio();
|
|
133
|
-
this.element = audio;
|
|
134
|
-
// for (const n of mdevents) {
|
|
135
|
-
// audio.addEventListener(n, () => {
|
|
136
|
-
// console.log('[audio] event: %s', n);
|
|
137
|
-
// });
|
|
138
|
-
// }
|
|
139
|
-
audio.addEventListener('ended', () => {
|
|
140
|
-
this.dispose();
|
|
141
|
-
});
|
|
142
|
-
audio.addEventListener('canplay', () => {
|
|
143
|
-
this.aboutToSetSpeaking(true);
|
|
144
|
-
});
|
|
145
|
-
audio.addEventListener('pause', () => {
|
|
146
|
-
this.aboutToSetSpeaking(false);
|
|
147
|
-
});
|
|
148
|
-
audio.addEventListener('waiting', () => {
|
|
149
|
-
this.aboutToSetSpeaking(false, DELAY_CLOSE_MOUTH / 2);
|
|
150
|
-
});
|
|
151
|
-
audio.autoplay = true;
|
|
152
|
-
}
|
|
153
|
-
tmr;
|
|
154
|
-
aboutToSetSpeaking(target, timeout = DELAY_CLOSE_MOUTH) {
|
|
155
|
-
if (this.tmr) {
|
|
156
|
-
clearTimeout(this.tmr);
|
|
157
|
-
this.tmr = 0;
|
|
158
|
-
}
|
|
159
|
-
if (target) {
|
|
160
|
-
this._moveSpeakState(true);
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
this.tmr = setTimeout(() => {
|
|
164
|
-
this.tmr = 0;
|
|
165
|
-
this._moveSpeakState(false);
|
|
166
|
-
}, timeout);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
speaking = false;
|
|
170
|
-
_moveSpeakState(target) {
|
|
171
|
-
if (this.speaking === target)
|
|
172
|
-
return;
|
|
173
|
-
this.speaking = target;
|
|
174
|
-
// console.log('human-speaking:', target);
|
|
175
|
-
this._humanSpeaking.fireNoError(this.speaking);
|
|
176
|
-
if (this.disposed) {
|
|
177
|
-
this._humanSpeaking.dispose();
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
disposed = false;
|
|
181
|
-
dispose() {
|
|
182
|
-
// console.log('HtmlAudioPlayer: dispose');
|
|
183
|
-
this.disposed = true;
|
|
184
|
-
disposeAudioElement(this.element);
|
|
185
|
-
if (this.speaking) {
|
|
186
|
-
this.aboutToSetSpeaking(false);
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
this._humanSpeaking.dispose();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
onEnd(fn) {
|
|
193
|
-
if (this.element.ended) {
|
|
194
|
-
fn();
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
const once = () => {
|
|
198
|
-
fn();
|
|
199
|
-
this.element.removeEventListener('ended', once);
|
|
200
|
-
};
|
|
201
|
-
this.element.addEventListener('ended', once);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
export function disposeAudioElement(audio) {
|
|
206
|
-
if (!audio.ended && !('__my_end' in audio)) {
|
|
207
|
-
Object.assign(audio, { __my_end: true });
|
|
208
|
-
const ee = new Event('ended');
|
|
209
|
-
Object.assign(ee, { error: new Error('canceled') });
|
|
210
|
-
audio.dispatchEvent(ee);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
// biome-ignore lint/suspicious/noTsIgnore: 可能用到
|
|
214
|
-
// @ts-ignore
|
|
215
|
-
// biome-ignore lint/correctness/noUnusedVariables: debug
|
|
216
|
-
const mdevents = [
|
|
217
|
-
'abort',
|
|
218
|
-
'canplay',
|
|
219
|
-
'canplaythrough',
|
|
220
|
-
'durationchange',
|
|
221
|
-
'emptied',
|
|
222
|
-
'encrypted',
|
|
223
|
-
'ended',
|
|
224
|
-
'error',
|
|
225
|
-
'loadeddata',
|
|
226
|
-
'loadedmetadata',
|
|
227
|
-
'loadstart',
|
|
228
|
-
'pause',
|
|
229
|
-
'play',
|
|
230
|
-
'playing',
|
|
231
|
-
'progress',
|
|
232
|
-
'ratechange',
|
|
233
|
-
'seeked',
|
|
234
|
-
'seeking',
|
|
235
|
-
'stalled',
|
|
236
|
-
'suspend',
|
|
237
|
-
'timeupdate',
|
|
238
|
-
'volumechange',
|
|
239
|
-
'waiting',
|
|
240
|
-
'waitingforkey',
|
|
241
|
-
];
|
|
242
|
-
//# sourceMappingURL=audio-player.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"audio-player.js","sourceRoot":"","sources":["../../../src/dom copy/web-audio/audio-player.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
DELETED
package/lib/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC"}
|
package/lib/index.js
DELETED
package/lib/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC"}
|
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
import { DeferredPromise, Emitter, type IDisposable } from '@idlebox/common';
|
|
2
|
-
|
|
3
|
-
const DELAY_CLOSE_MOUTH = 700;
|
|
4
|
-
|
|
5
|
-
export class StreamAppender {
|
|
6
|
-
private readonly dfd = new DeferredPromise<void>();
|
|
7
|
-
private readonly queue: ArrayBuffer[] = [];
|
|
8
|
-
private _finished = false;
|
|
9
|
-
|
|
10
|
-
constructor(private readonly stream: SourceBuffer) {
|
|
11
|
-
stream.mode = 'sequence';
|
|
12
|
-
|
|
13
|
-
stream.addEventListener('updateend', this._pump.bind(this));
|
|
14
|
-
stream.addEventListener('error', this._error.bind(this));
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
private _pump() {
|
|
18
|
-
// console.log('[stream] pump %s', this.queue.length);
|
|
19
|
-
if (this.stream.updating) {
|
|
20
|
-
// console.error(' - busy');
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
const next = this.queue.shift();
|
|
24
|
-
if (next) {
|
|
25
|
-
// console.log('playing %s bytes', next.byteLength);
|
|
26
|
-
this.stream.appendBuffer(next);
|
|
27
|
-
} else if (this._finished) {
|
|
28
|
-
// console.log('StreamAppender: dispose');
|
|
29
|
-
this.dfd.complete();
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
private _error(e: Event) {
|
|
34
|
-
// console.log('[stream] pump fail', e);
|
|
35
|
-
this.queue.length = 0;
|
|
36
|
-
this.finish();
|
|
37
|
-
|
|
38
|
-
const err = (e as any).error;
|
|
39
|
-
if (!(err instanceof Error)) {
|
|
40
|
-
console.error('e似乎不是ErrorEvent', e);
|
|
41
|
-
}
|
|
42
|
-
this.dfd.error(err);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
append(buffer: ArrayBuffer) {
|
|
46
|
-
if (this._finished) {
|
|
47
|
-
throw new Error('不能在finish之后append');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
this.queue.push(buffer);
|
|
51
|
-
if (!this.stream.updating) {
|
|
52
|
-
// console.log('[stream] pump start');
|
|
53
|
-
this._pump();
|
|
54
|
-
} else {
|
|
55
|
-
// console.log('[stream] queue data');
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
finish() {
|
|
60
|
-
// console.log('[stream] done.');
|
|
61
|
-
this._finished = true;
|
|
62
|
-
this._pump();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
terminate() {
|
|
66
|
-
// console.log('[stream] done. (terminate)');
|
|
67
|
-
this._finished = true;
|
|
68
|
-
this.queue.length = 0;
|
|
69
|
-
this._pump();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
wait(): Promise<void> {
|
|
73
|
-
return this.dfd.p;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export class MediaForPlayback {
|
|
78
|
-
private readonly mediaSource: MediaSource;
|
|
79
|
-
public readonly ready: Promise<void>;
|
|
80
|
-
private readonly endDfd = new DeferredPromise<void>();
|
|
81
|
-
|
|
82
|
-
public readonly id: number;
|
|
83
|
-
|
|
84
|
-
private static guid = 0;
|
|
85
|
-
|
|
86
|
-
constructor() {
|
|
87
|
-
this.id = ++MediaForPlayback.guid;
|
|
88
|
-
// console.log('[media source %d] create', this.id);
|
|
89
|
-
|
|
90
|
-
const source = new window.MediaSource();
|
|
91
|
-
this.ready = new Promise<void>((resolve) => {
|
|
92
|
-
const wrap = () => {
|
|
93
|
-
source.removeEventListener('sourceopen', wrap);
|
|
94
|
-
// console.log('[media source %d] sourceopen', this.id);
|
|
95
|
-
resolve();
|
|
96
|
-
};
|
|
97
|
-
source.addEventListener('sourceopen', wrap);
|
|
98
|
-
});
|
|
99
|
-
this.mediaSource = source;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private opened = false;
|
|
103
|
-
async open(mime: string) {
|
|
104
|
-
if (this.opened) throw new Error('duplicate call to MediaForPlayback.open()');
|
|
105
|
-
this.opened = true;
|
|
106
|
-
|
|
107
|
-
// console.log('[media source %d] open', this.id, mime);
|
|
108
|
-
await this.ready;
|
|
109
|
-
|
|
110
|
-
const buffer = this.mediaSource.addSourceBuffer(mime);
|
|
111
|
-
const appender = new StreamAppender(buffer);
|
|
112
|
-
appender.wait().finally(() => {
|
|
113
|
-
// console.log('[media source %d] appender finished', this.id);
|
|
114
|
-
|
|
115
|
-
this.mediaSource.endOfStream();
|
|
116
|
-
this.endDfd.complete();
|
|
117
|
-
this.dispose();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
return appender;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
playToNewAudioElement() {
|
|
124
|
-
const audio = new HtmlAudioPlayer();
|
|
125
|
-
|
|
126
|
-
audio.onEnd(() => {
|
|
127
|
-
this.dispose();
|
|
128
|
-
});
|
|
129
|
-
audio.element.src = URL.createObjectURL(this.mediaSource);
|
|
130
|
-
|
|
131
|
-
return audio;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
public finish(): Promise<void> {
|
|
135
|
-
return this.endDfd.p;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
private disList: IDisposable[] = [];
|
|
139
|
-
_register(d: IDisposable) {
|
|
140
|
-
this.disList.push(d);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
dispose() {
|
|
144
|
-
// console.log('[media source %d] dispose()', this.id);
|
|
145
|
-
for (const obj of this.disList) {
|
|
146
|
-
obj.dispose();
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export class HtmlAudioPlayer {
|
|
152
|
-
public readonly element: HTMLAudioElement;
|
|
153
|
-
|
|
154
|
-
private readonly _humanSpeaking = new Emitter<boolean>();
|
|
155
|
-
public readonly onHumanSpeaking = this._humanSpeaking.event;
|
|
156
|
-
|
|
157
|
-
constructor() {
|
|
158
|
-
const audio = new Audio();
|
|
159
|
-
this.element = audio;
|
|
160
|
-
|
|
161
|
-
// for (const n of mdevents) {
|
|
162
|
-
// audio.addEventListener(n, () => {
|
|
163
|
-
// console.log('[audio] event: %s', n);
|
|
164
|
-
// });
|
|
165
|
-
// }
|
|
166
|
-
|
|
167
|
-
audio.addEventListener('ended', () => {
|
|
168
|
-
this.dispose();
|
|
169
|
-
});
|
|
170
|
-
audio.addEventListener('canplay', () => {
|
|
171
|
-
this.aboutToSetSpeaking(true);
|
|
172
|
-
});
|
|
173
|
-
audio.addEventListener('pause', () => {
|
|
174
|
-
this.aboutToSetSpeaking(false);
|
|
175
|
-
});
|
|
176
|
-
audio.addEventListener('waiting', () => {
|
|
177
|
-
this.aboutToSetSpeaking(false, DELAY_CLOSE_MOUTH / 2);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
audio.autoplay = true;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
private tmr?: number;
|
|
184
|
-
private aboutToSetSpeaking(target: boolean, timeout = DELAY_CLOSE_MOUTH) {
|
|
185
|
-
if (this.tmr) {
|
|
186
|
-
clearTimeout(this.tmr);
|
|
187
|
-
this.tmr = 0;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (target) {
|
|
191
|
-
this._moveSpeakState(true);
|
|
192
|
-
} else {
|
|
193
|
-
this.tmr = setTimeout(() => {
|
|
194
|
-
this.tmr = 0;
|
|
195
|
-
|
|
196
|
-
this._moveSpeakState(false);
|
|
197
|
-
}, timeout);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
private speaking = false;
|
|
202
|
-
private _moveSpeakState(target: boolean) {
|
|
203
|
-
if (this.speaking === target) return;
|
|
204
|
-
|
|
205
|
-
this.speaking = target;
|
|
206
|
-
// console.log('human-speaking:', target);
|
|
207
|
-
this._humanSpeaking.fireNoError(this.speaking);
|
|
208
|
-
|
|
209
|
-
if (this.disposed) {
|
|
210
|
-
this._humanSpeaking.dispose();
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
private disposed = false;
|
|
215
|
-
dispose() {
|
|
216
|
-
// console.log('HtmlAudioPlayer: dispose');
|
|
217
|
-
this.disposed = true;
|
|
218
|
-
|
|
219
|
-
disposeAudioElement(this.element);
|
|
220
|
-
|
|
221
|
-
if (this.speaking) {
|
|
222
|
-
this.aboutToSetSpeaking(false);
|
|
223
|
-
} else {
|
|
224
|
-
this._humanSpeaking.dispose();
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
onEnd(fn: () => void) {
|
|
229
|
-
if (this.element.ended) {
|
|
230
|
-
fn();
|
|
231
|
-
} else {
|
|
232
|
-
const once = () => {
|
|
233
|
-
fn();
|
|
234
|
-
this.element.removeEventListener('ended', once);
|
|
235
|
-
};
|
|
236
|
-
this.element.addEventListener('ended', once);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export function disposeAudioElement(audio: HTMLAudioElement) {
|
|
242
|
-
if (!audio.ended && !('__my_end' in audio)) {
|
|
243
|
-
Object.assign(audio, { __my_end: true });
|
|
244
|
-
const ee = new Event('ended');
|
|
245
|
-
Object.assign(ee, { error: new Error('canceled') });
|
|
246
|
-
audio.dispatchEvent(ee);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// biome-ignore lint/suspicious/noTsIgnore: 可能用到
|
|
251
|
-
// @ts-ignore
|
|
252
|
-
// biome-ignore lint/correctness/noUnusedVariables: debug
|
|
253
|
-
const mdevents = [
|
|
254
|
-
'abort',
|
|
255
|
-
'canplay',
|
|
256
|
-
'canplaythrough',
|
|
257
|
-
'durationchange',
|
|
258
|
-
'emptied',
|
|
259
|
-
'encrypted',
|
|
260
|
-
'ended',
|
|
261
|
-
'error',
|
|
262
|
-
'loadeddata',
|
|
263
|
-
'loadedmetadata',
|
|
264
|
-
'loadstart',
|
|
265
|
-
'pause',
|
|
266
|
-
'play',
|
|
267
|
-
'playing',
|
|
268
|
-
'progress',
|
|
269
|
-
'ratechange',
|
|
270
|
-
'seeked',
|
|
271
|
-
'seeking',
|
|
272
|
-
'stalled',
|
|
273
|
-
'suspend',
|
|
274
|
-
'timeupdate',
|
|
275
|
-
'volumechange',
|
|
276
|
-
'waiting',
|
|
277
|
-
'waitingforkey',
|
|
278
|
-
];
|
package/src/index.ts
DELETED