@vapi-ai/web 1.0.106 → 1.0.107
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/AtomicMediaSource.ts +58 -0
- package/dist/AtomicMediaSource.d.ts +10 -0
- package/dist/AtomicMediaSource.js +54 -0
- package/dist/player.d.ts +1 -9
- package/dist/player.js +5 -66
- package/package.json +1 -1
- package/player.ts +6 -87
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import async, { QueueObject } from "async";
|
|
2
|
+
|
|
3
|
+
class AtomicMediaSource {
|
|
4
|
+
mediaSource: MediaSource;
|
|
5
|
+
private sourceBuffer: SourceBuffer | null = null;
|
|
6
|
+
private operationsQueue: QueueObject<() => Promise<void>>;
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
this.mediaSource = new MediaSource();
|
|
10
|
+
this.operationsQueue = this.createOperationQueue();
|
|
11
|
+
|
|
12
|
+
this.mediaSource.addEventListener("sourceopen", () => {
|
|
13
|
+
this.mediaSource.duration = Number.POSITIVE_INFINITY;
|
|
14
|
+
this.sourceBuffer = this.mediaSource.addSourceBuffer(
|
|
15
|
+
'audio/webm; codecs="opus"'
|
|
16
|
+
);
|
|
17
|
+
this.sourceBuffer.mode = "sequence";
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public appendBuffer(buffer: ArrayBuffer) {
|
|
22
|
+
this.operationsQueue.push(() => {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
if (this.sourceBuffer) {
|
|
25
|
+
this.sourceBuffer.addEventListener("updateend", () => {
|
|
26
|
+
resolve();
|
|
27
|
+
});
|
|
28
|
+
this.sourceBuffer.appendBuffer(buffer);
|
|
29
|
+
} else {
|
|
30
|
+
reject();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public clearBuffer() {
|
|
37
|
+
this.operationsQueue.push(() => {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
if (this.sourceBuffer) {
|
|
40
|
+
this.sourceBuffer.addEventListener("updateend", () => {
|
|
41
|
+
resolve();
|
|
42
|
+
});
|
|
43
|
+
this.sourceBuffer.remove(0, this.mediaSource.duration);
|
|
44
|
+
} else {
|
|
45
|
+
reject();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private createOperationQueue() {
|
|
52
|
+
return async.queue<() => Promise<void>>((task, callback) => {
|
|
53
|
+
task().then(() => callback());
|
|
54
|
+
}, 1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default AtomicMediaSource;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare class AtomicMediaSource {
|
|
2
|
+
mediaSource: MediaSource;
|
|
3
|
+
private sourceBuffer;
|
|
4
|
+
private operationsQueue;
|
|
5
|
+
constructor();
|
|
6
|
+
appendBuffer(buffer: ArrayBuffer): void;
|
|
7
|
+
clearBuffer(): void;
|
|
8
|
+
private createOperationQueue;
|
|
9
|
+
}
|
|
10
|
+
export default AtomicMediaSource;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const async_1 = __importDefault(require("async"));
|
|
7
|
+
class AtomicMediaSource {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.sourceBuffer = null;
|
|
10
|
+
this.mediaSource = new MediaSource();
|
|
11
|
+
this.operationsQueue = this.createOperationQueue();
|
|
12
|
+
this.mediaSource.addEventListener("sourceopen", () => {
|
|
13
|
+
this.mediaSource.duration = Number.POSITIVE_INFINITY;
|
|
14
|
+
this.sourceBuffer = this.mediaSource.addSourceBuffer('audio/webm; codecs="opus"');
|
|
15
|
+
this.sourceBuffer.mode = "sequence";
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
appendBuffer(buffer) {
|
|
19
|
+
this.operationsQueue.push(() => {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
if (this.sourceBuffer) {
|
|
22
|
+
this.sourceBuffer.addEventListener("updateend", () => {
|
|
23
|
+
resolve();
|
|
24
|
+
});
|
|
25
|
+
this.sourceBuffer.appendBuffer(buffer);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
reject();
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
clearBuffer() {
|
|
34
|
+
this.operationsQueue.push(() => {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
if (this.sourceBuffer) {
|
|
37
|
+
this.sourceBuffer.addEventListener("updateend", () => {
|
|
38
|
+
resolve();
|
|
39
|
+
});
|
|
40
|
+
this.sourceBuffer.remove(0, this.mediaSource.duration);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
reject();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
createOperationQueue() {
|
|
49
|
+
return async_1.default.queue((task, callback) => {
|
|
50
|
+
task().then(() => callback());
|
|
51
|
+
}, 1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.default = AtomicMediaSource;
|
package/dist/player.d.ts
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import async from "async";
|
|
3
2
|
import EventEmitter from "events";
|
|
4
3
|
export declare class ContinuousPlayer extends EventEmitter {
|
|
5
|
-
private
|
|
6
|
-
private sourceBuffer;
|
|
4
|
+
private atomic;
|
|
7
5
|
private audio;
|
|
8
|
-
private operationsQueue;
|
|
9
6
|
constructor();
|
|
10
|
-
createOperationQueue(): async.QueueObject<() => Promise<void>>;
|
|
11
7
|
start(): void;
|
|
12
8
|
playChunk(audioData: ArrayBuffer): void;
|
|
13
|
-
private performBufferOperation;
|
|
14
|
-
private appendNextChunk;
|
|
15
|
-
private removeBuffer;
|
|
16
|
-
private resetTimestampOffset;
|
|
17
9
|
clear(): void;
|
|
18
10
|
}
|
package/dist/player.js
CHANGED
|
@@ -4,88 +4,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ContinuousPlayer = void 0;
|
|
7
|
-
const
|
|
7
|
+
const AtomicMediaSource_1 = __importDefault(require("./AtomicMediaSource"));
|
|
8
8
|
const events_1 = __importDefault(require("events"));
|
|
9
9
|
class ContinuousPlayer extends events_1.default {
|
|
10
10
|
constructor() {
|
|
11
11
|
super();
|
|
12
|
-
this.
|
|
13
|
-
this.mediaSource = new MediaSource();
|
|
12
|
+
this.atomic = new AtomicMediaSource_1.default();
|
|
14
13
|
this.audio = document.createElement("audio");
|
|
15
14
|
this.audio.addEventListener("playing", () => this.emit("speech-start"));
|
|
16
15
|
this.audio.addEventListener("play", () => this.emit("speech-start"));
|
|
17
16
|
this.audio.addEventListener("waiting", () => this.emit("speech-end"));
|
|
18
|
-
this.audio.src = URL.createObjectURL(this.mediaSource);
|
|
17
|
+
this.audio.src = URL.createObjectURL(this.atomic.mediaSource);
|
|
19
18
|
document.body.appendChild(this.audio);
|
|
20
|
-
this.operationsQueue = this.createOperationQueue();
|
|
21
|
-
this.mediaSource.addEventListener("sourceopen", () => {
|
|
22
|
-
this.mediaSource.duration = Number.POSITIVE_INFINITY;
|
|
23
|
-
this.sourceBuffer = this.mediaSource.addSourceBuffer('audio/webm; codecs="opus"');
|
|
24
|
-
this.sourceBuffer.mode = "sequence";
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
createOperationQueue() {
|
|
28
|
-
return async_1.default.queue((task, callback) => {
|
|
29
|
-
task().then(() => callback());
|
|
30
|
-
}, 1);
|
|
31
19
|
}
|
|
32
20
|
start() {
|
|
33
21
|
this.audio.play();
|
|
34
22
|
}
|
|
35
23
|
playChunk(audioData) {
|
|
36
|
-
|
|
37
|
-
this.operationsQueue.push(() => this.appendNextChunk(audioData));
|
|
38
|
-
}
|
|
39
|
-
performBufferOperation(operation) {
|
|
40
|
-
return new Promise((resolve) => {
|
|
41
|
-
if (!this.sourceBuffer) {
|
|
42
|
-
resolve();
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (this.sourceBuffer.updating) {
|
|
46
|
-
this.sourceBuffer.addEventListener("updateend", () => {
|
|
47
|
-
operation();
|
|
48
|
-
resolve();
|
|
49
|
-
}, {
|
|
50
|
-
once: true,
|
|
51
|
-
});
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
operation();
|
|
55
|
-
resolve();
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
appendNextChunk(chunk) {
|
|
59
|
-
return this.performBufferOperation(() => {
|
|
60
|
-
if (!this.sourceBuffer)
|
|
61
|
-
return;
|
|
62
|
-
console.log("appending");
|
|
63
|
-
this.sourceBuffer.appendBuffer(chunk);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
removeBuffer() {
|
|
67
|
-
return this.performBufferOperation(() => {
|
|
68
|
-
if (!this.sourceBuffer)
|
|
69
|
-
return;
|
|
70
|
-
if (this.sourceBuffer.buffered.length > 0) {
|
|
71
|
-
this.sourceBuffer.remove(0, this.sourceBuffer.buffered.end(this.sourceBuffer.buffered.length - 1));
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
resetTimestampOffset() {
|
|
76
|
-
return this.performBufferOperation(() => {
|
|
77
|
-
if (!this.sourceBuffer)
|
|
78
|
-
return;
|
|
79
|
-
this.sourceBuffer.timestampOffset = 0;
|
|
80
|
-
});
|
|
24
|
+
this.atomic.appendBuffer(audioData);
|
|
81
25
|
}
|
|
82
26
|
clear() {
|
|
83
|
-
|
|
84
|
-
// this.sourceBuffer.abort();
|
|
85
|
-
// this.operationsQueue.kill();
|
|
86
|
-
// this.operationsQueue = this.createOperationQueue();
|
|
87
|
-
// this.operationsQueue.push(() => this.removeBuffer());
|
|
88
|
-
// this.operationsQueue.push(() => this.resetTimestampOffset());
|
|
27
|
+
this.atomic.clearBuffer();
|
|
89
28
|
}
|
|
90
29
|
}
|
|
91
30
|
exports.ContinuousPlayer = ContinuousPlayer;
|
package/package.json
CHANGED
package/player.ts
CHANGED
|
@@ -1,42 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import AtomicMediaSource from "./AtomicMediaSource";
|
|
3
2
|
import EventEmitter from "events";
|
|
4
3
|
|
|
5
4
|
export class ContinuousPlayer extends EventEmitter {
|
|
6
|
-
private
|
|
7
|
-
private sourceBuffer: SourceBuffer | null = null;
|
|
5
|
+
private atomic: AtomicMediaSource;
|
|
8
6
|
private audio: HTMLAudioElement;
|
|
9
|
-
private operationsQueue: QueueObject<() => Promise<void>>;
|
|
10
7
|
|
|
11
8
|
constructor() {
|
|
12
9
|
super();
|
|
13
|
-
this.
|
|
10
|
+
this.atomic = new AtomicMediaSource();
|
|
14
11
|
|
|
15
12
|
this.audio = document.createElement("audio");
|
|
16
13
|
|
|
17
14
|
this.audio.addEventListener("playing", () => this.emit("speech-start"));
|
|
18
15
|
this.audio.addEventListener("play", () => this.emit("speech-start"));
|
|
19
|
-
|
|
20
16
|
this.audio.addEventListener("waiting", () => this.emit("speech-end"));
|
|
21
17
|
|
|
22
|
-
this.audio.src = URL.createObjectURL(this.mediaSource);
|
|
18
|
+
this.audio.src = URL.createObjectURL(this.atomic.mediaSource);
|
|
23
19
|
document.body.appendChild(this.audio);
|
|
24
|
-
|
|
25
|
-
this.operationsQueue = this.createOperationQueue();
|
|
26
|
-
|
|
27
|
-
this.mediaSource.addEventListener("sourceopen", () => {
|
|
28
|
-
this.mediaSource.duration = Number.POSITIVE_INFINITY;
|
|
29
|
-
this.sourceBuffer = this.mediaSource.addSourceBuffer(
|
|
30
|
-
'audio/webm; codecs="opus"'
|
|
31
|
-
);
|
|
32
|
-
this.sourceBuffer.mode = "sequence";
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
createOperationQueue() {
|
|
37
|
-
return async.queue<() => Promise<void>>((task, callback) => {
|
|
38
|
-
task().then(() => callback());
|
|
39
|
-
}, 1);
|
|
40
20
|
}
|
|
41
21
|
|
|
42
22
|
start() {
|
|
@@ -44,71 +24,10 @@ export class ContinuousPlayer extends EventEmitter {
|
|
|
44
24
|
}
|
|
45
25
|
|
|
46
26
|
playChunk(audioData: ArrayBuffer) {
|
|
47
|
-
|
|
48
|
-
this.operationsQueue.push(() => this.appendNextChunk(audioData));
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private performBufferOperation(operation: () => void) {
|
|
52
|
-
return new Promise<void>((resolve) => {
|
|
53
|
-
if (!this.sourceBuffer) {
|
|
54
|
-
resolve();
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (this.sourceBuffer.updating) {
|
|
59
|
-
this.sourceBuffer.addEventListener(
|
|
60
|
-
"updateend",
|
|
61
|
-
() => {
|
|
62
|
-
operation();
|
|
63
|
-
resolve();
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
once: true,
|
|
67
|
-
}
|
|
68
|
-
);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
operation();
|
|
73
|
-
resolve();
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
private appendNextChunk(chunk: ArrayBuffer) {
|
|
78
|
-
return this.performBufferOperation(() => {
|
|
79
|
-
if (!this.sourceBuffer) return;
|
|
80
|
-
console.log("appending");
|
|
81
|
-
|
|
82
|
-
this.sourceBuffer.appendBuffer(chunk);
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
private removeBuffer() {
|
|
87
|
-
return this.performBufferOperation(() => {
|
|
88
|
-
if (!this.sourceBuffer) return;
|
|
89
|
-
|
|
90
|
-
if (this.sourceBuffer.buffered.length > 0) {
|
|
91
|
-
this.sourceBuffer.remove(
|
|
92
|
-
0,
|
|
93
|
-
this.sourceBuffer.buffered.end(this.sourceBuffer.buffered.length - 1)
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
private resetTimestampOffset() {
|
|
100
|
-
return this.performBufferOperation(() => {
|
|
101
|
-
if (!this.sourceBuffer) return;
|
|
102
|
-
this.sourceBuffer.timestampOffset = 0;
|
|
103
|
-
});
|
|
27
|
+
this.atomic.appendBuffer(audioData);
|
|
104
28
|
}
|
|
105
29
|
|
|
106
30
|
clear() {
|
|
107
|
-
|
|
108
|
-
// this.sourceBuffer.abort();
|
|
109
|
-
// this.operationsQueue.kill();
|
|
110
|
-
// this.operationsQueue = this.createOperationQueue();
|
|
111
|
-
// this.operationsQueue.push(() => this.removeBuffer());
|
|
112
|
-
// this.operationsQueue.push(() => this.resetTimestampOffset());
|
|
31
|
+
this.atomic.clearBuffer();
|
|
113
32
|
}
|
|
114
33
|
}
|