@vapi-ai/web 1.0.140 → 1.0.142
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/player.d.ts +3 -3
- package/dist/player.js +23 -22
- package/dist/vapi.d.ts +5 -13
- package/dist/vapi.js +17 -92
- package/package.json +3 -7
- package/vapi.ts +17 -116
- package/AtomicMediaSource.ts +0 -97
- package/player.ts +0 -27
- package/silent.mp3 +0 -0
package/dist/player.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import EventEmitter from "events";
|
|
3
3
|
export declare class ContinuousPlayer extends EventEmitter {
|
|
4
|
-
private
|
|
5
|
-
private
|
|
4
|
+
private atomic;
|
|
5
|
+
private audio;
|
|
6
6
|
constructor();
|
|
7
7
|
start(): void;
|
|
8
|
-
playChunk(audioData: ArrayBuffer):
|
|
8
|
+
playChunk(audioData: ArrayBuffer): void;
|
|
9
9
|
clear(): void;
|
|
10
10
|
}
|
package/dist/player.js
CHANGED
|
@@ -1,37 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
14
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
6
|
exports.ContinuousPlayer = void 0;
|
|
7
|
+
const AtomicMediaSource_1 = __importDefault(require("./AtomicMediaSource"));
|
|
16
8
|
const events_1 = __importDefault(require("events"));
|
|
17
9
|
class ContinuousPlayer extends events_1.default {
|
|
18
10
|
constructor() {
|
|
19
11
|
super();
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
12
|
+
this.audio = document.createElement("audio");
|
|
13
|
+
this.audio.addEventListener("playing", () => this.emit("speech-start"));
|
|
14
|
+
this.audio.addEventListener("play", () => this.emit("speech-start"));
|
|
15
|
+
this.audio.addEventListener("canplay", () => {
|
|
16
|
+
this.audio.play();
|
|
17
|
+
});
|
|
18
|
+
this.audio.addEventListener("waiting", () => this.emit("speech-end"));
|
|
19
|
+
this.atomic = new AtomicMediaSource_1.default();
|
|
20
|
+
this.audio.src = URL.createObjectURL(this.atomic.mediaSource);
|
|
21
|
+
document.body.appendChild(this.audio);
|
|
22
|
+
}
|
|
23
|
+
start() {
|
|
24
|
+
this.audio.play();
|
|
22
25
|
}
|
|
23
|
-
start() { }
|
|
24
26
|
playChunk(audioData) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
var _a;
|
|
28
|
+
(_a = this.atomic.sourceBuffer) === null || _a === void 0 ? void 0 : _a.addEventListener("updateend", () => {
|
|
29
|
+
this.audio.play().catch((error) => console.error("Play error:", error));
|
|
30
|
+
}, { once: true });
|
|
31
|
+
this.atomic.appendBuffer(audioData);
|
|
32
|
+
}
|
|
33
|
+
clear() {
|
|
34
|
+
this.audio.pause();
|
|
35
|
+
this.atomic.clearBuffer();
|
|
34
36
|
}
|
|
35
|
-
clear() { }
|
|
36
37
|
}
|
|
37
38
|
exports.ContinuousPlayer = ContinuousPlayer;
|
package/dist/vapi.d.ts
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { CreateAssistantDTO } from "./api";
|
|
2
|
-
import
|
|
3
|
-
export default class Vapi {
|
|
4
|
-
private audioContext;
|
|
5
|
-
private source;
|
|
3
|
+
import EventEmitter from "events";
|
|
4
|
+
export default class Vapi extends EventEmitter {
|
|
6
5
|
private started;
|
|
7
|
-
private
|
|
8
|
-
|
|
9
|
-
private nextClipTime;
|
|
10
|
-
private decodeQueue;
|
|
11
|
-
constructor(apiToken: string, apiUrl?: string);
|
|
12
|
-
createQueue(): async.QueueObject<ArrayBuffer>;
|
|
6
|
+
private call;
|
|
7
|
+
constructor(apiToken: string, apiBaseUrl?: string);
|
|
13
8
|
start(assistant: CreateAssistantDTO | string): void;
|
|
14
|
-
private startRecording;
|
|
15
|
-
private onMessage;
|
|
16
|
-
clear(): void;
|
|
17
9
|
stop(): void;
|
|
18
10
|
}
|
package/dist/vapi.js
CHANGED
|
@@ -3,39 +3,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
7
|
-
const
|
|
6
|
+
const daily_js_1 = __importDefault(require("@daily-co/daily-js"));
|
|
7
|
+
const events_1 = __importDefault(require("events"));
|
|
8
8
|
const client_1 = require("./client");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
this.audioContext = new standardized_audio_context_1.AudioContext();
|
|
13
|
-
this.source = null;
|
|
9
|
+
class Vapi extends events_1.default {
|
|
10
|
+
constructor(apiToken, apiBaseUrl) {
|
|
11
|
+
super();
|
|
14
12
|
this.started = false;
|
|
15
|
-
this.
|
|
16
|
-
|
|
17
|
-
this.nextClipTime = 0;
|
|
13
|
+
this.call = null;
|
|
14
|
+
client_1.client.baseUrl = apiBaseUrl !== null && apiBaseUrl !== void 0 ? apiBaseUrl : "https://api.vapi.ai";
|
|
18
15
|
client_1.client.setSecurityData(apiToken);
|
|
19
|
-
client_1.client.baseUrl = apiUrl !== null && apiUrl !== void 0 ? apiUrl : "https://api.vapi.ai";
|
|
20
|
-
this.decodeQueue = this.createQueue();
|
|
21
|
-
}
|
|
22
|
-
createQueue() {
|
|
23
|
-
return async_1.default.queue((task, callback) => {
|
|
24
|
-
this.source = this.audioContext.createBufferSource();
|
|
25
|
-
this.audioContext.decodeAudioData(task, (buffer) => {
|
|
26
|
-
if (!this.source)
|
|
27
|
-
return;
|
|
28
|
-
this.source.buffer = buffer;
|
|
29
|
-
this.source.connect(this.audioContext.destination);
|
|
30
|
-
// If the next clip time is in the past, set it to the current time
|
|
31
|
-
this.nextClipTime = Math.max(this.nextClipTime, this.audioContext.currentTime);
|
|
32
|
-
// Start the source at the next clip time
|
|
33
|
-
this.source.start(this.nextClipTime);
|
|
34
|
-
// Schedule the next clip time
|
|
35
|
-
this.nextClipTime += buffer.duration;
|
|
36
|
-
callback();
|
|
37
|
-
});
|
|
38
|
-
}, 1);
|
|
39
16
|
}
|
|
40
17
|
start(assistant) {
|
|
41
18
|
if (this.started) {
|
|
@@ -44,78 +21,26 @@ class Vapi {
|
|
|
44
21
|
this.started = true;
|
|
45
22
|
client_1.client.call
|
|
46
23
|
.callControllerCreateWebCall({
|
|
24
|
+
assistant: typeof assistant === "string" ? undefined : assistant,
|
|
47
25
|
assistantId: typeof assistant === "string" ? assistant : undefined,
|
|
48
|
-
assistant: typeof assistant === "object" ? assistant : undefined,
|
|
49
26
|
})
|
|
50
27
|
.then(({ data }) => {
|
|
51
|
-
const {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
};
|
|
58
|
-
socket.onmessage = (event) => {
|
|
59
|
-
if (!socket)
|
|
60
|
-
return;
|
|
61
|
-
this.onMessage(socket, event);
|
|
62
|
-
};
|
|
63
|
-
socket.onclose = () => {
|
|
64
|
-
this.stop();
|
|
65
|
-
};
|
|
28
|
+
const { url } = data;
|
|
29
|
+
this.call = daily_js_1.default.createCallObject({
|
|
30
|
+
url,
|
|
31
|
+
audioSource: true,
|
|
32
|
+
videoSource: false,
|
|
33
|
+
});
|
|
66
34
|
})
|
|
67
35
|
.catch((error) => {
|
|
68
36
|
console.error(error);
|
|
69
37
|
});
|
|
70
38
|
}
|
|
71
|
-
startRecording() {
|
|
72
|
-
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
|
|
73
|
-
this.mediaRecorder = new MediaRecorder(stream, {
|
|
74
|
-
mimeType: "audio/webm;codecs=opus",
|
|
75
|
-
});
|
|
76
|
-
this.mediaRecorder.start(100);
|
|
77
|
-
this.mediaRecorder.ondataavailable = (event) => {
|
|
78
|
-
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
79
|
-
this.ws.send(event.data);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
onMessage(ws, event) {
|
|
85
|
-
const data = JSON.parse(event.data);
|
|
86
|
-
if (data.event === "media") {
|
|
87
|
-
const audioData = (0, base64_arraybuffer_1.decode)(data.media.payload);
|
|
88
|
-
this.decodeQueue.push(audioData);
|
|
89
|
-
}
|
|
90
|
-
if (data.event === "clear") {
|
|
91
|
-
this.clear();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
clear() {
|
|
95
|
-
this.decodeQueue.kill();
|
|
96
|
-
this.decodeQueue = this.createQueue();
|
|
97
|
-
// Stop the source if it exists
|
|
98
|
-
if (this.source) {
|
|
99
|
-
this.source.stop();
|
|
100
|
-
this.source.disconnect();
|
|
101
|
-
this.source = null;
|
|
102
|
-
}
|
|
103
|
-
// Reset the next clip time
|
|
104
|
-
this.nextClipTime = 0;
|
|
105
|
-
}
|
|
106
39
|
stop() {
|
|
107
|
-
|
|
108
|
-
this.source.stop();
|
|
109
|
-
}
|
|
40
|
+
var _a;
|
|
110
41
|
this.started = false;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
this.ws = null;
|
|
115
|
-
if (this.mediaRecorder) {
|
|
116
|
-
this.mediaRecorder.stop();
|
|
117
|
-
}
|
|
118
|
-
this.mediaRecorder = null;
|
|
42
|
+
(_a = this.call) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
43
|
+
this.call = null;
|
|
119
44
|
}
|
|
120
45
|
}
|
|
121
46
|
exports.default = Vapi;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vapi-ai/web",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.142",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/vapi.js",
|
|
6
6
|
"types": "dist/vapi.d.ts",
|
|
@@ -22,15 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/VapiAI/snippet#readme",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"events": "^3.3.0",
|
|
28
|
-
"standardized-audio-context": "^25.3.57"
|
|
25
|
+
"@daily-co/daily-js": "^0.52.0",
|
|
26
|
+
"events": "^3.3.0"
|
|
29
27
|
},
|
|
30
28
|
"devDependencies": {
|
|
31
|
-
"@types/async": "^3.2.21",
|
|
32
29
|
"@types/node": "^20.6.0",
|
|
33
|
-
"@types/ws": "^8.5.5",
|
|
34
30
|
"swagger-typescript-api": "^13.0.3"
|
|
35
31
|
}
|
|
36
32
|
}
|
package/vapi.ts
CHANGED
|
@@ -1,57 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
AudioContext,
|
|
4
|
-
IAudioBufferSourceNode,
|
|
5
|
-
IAudioContext,
|
|
6
|
-
} from "standardized-audio-context";
|
|
7
|
-
import async, { QueueObject } from "async";
|
|
1
|
+
import DailyIframe, { DailyCall } from "@daily-co/daily-js";
|
|
8
2
|
|
|
3
|
+
import { CreateAssistantDTO } from "./api";
|
|
4
|
+
import EventEmitter from "events";
|
|
9
5
|
import { client } from "./client";
|
|
10
|
-
import { decode } from "base64-arraybuffer";
|
|
11
6
|
|
|
12
|
-
export default class Vapi {
|
|
13
|
-
private audioContext: AudioContext = new AudioContext();
|
|
14
|
-
private source: IAudioBufferSourceNode<IAudioContext> | null = null;
|
|
7
|
+
export default class Vapi extends EventEmitter {
|
|
15
8
|
private started: boolean = false;
|
|
16
|
-
private
|
|
17
|
-
private mediaRecorder: MediaRecorder | null = null;
|
|
9
|
+
private call: DailyCall | null = null;
|
|
18
10
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
constructor(apiToken: string, apiUrl?: string) {
|
|
11
|
+
constructor(apiToken: string, apiBaseUrl?: string) {
|
|
12
|
+
super();
|
|
13
|
+
client.baseUrl = apiBaseUrl ?? "https://api.vapi.ai";
|
|
24
14
|
client.setSecurityData(apiToken);
|
|
25
|
-
client.baseUrl = apiUrl ?? "https://api.vapi.ai";
|
|
26
|
-
|
|
27
|
-
this.decodeQueue = this.createQueue();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
createQueue() {
|
|
31
|
-
return async.queue<ArrayBuffer>((task, callback) => {
|
|
32
|
-
this.source = this.audioContext.createBufferSource();
|
|
33
|
-
|
|
34
|
-
this.audioContext.decodeAudioData(task, (buffer) => {
|
|
35
|
-
if (!this.source) return;
|
|
36
|
-
|
|
37
|
-
this.source.buffer = buffer;
|
|
38
|
-
this.source.connect(this.audioContext.destination);
|
|
39
|
-
|
|
40
|
-
// If the next clip time is in the past, set it to the current time
|
|
41
|
-
this.nextClipTime = Math.max(
|
|
42
|
-
this.nextClipTime,
|
|
43
|
-
this.audioContext.currentTime
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
// Start the source at the next clip time
|
|
47
|
-
this.source.start(this.nextClipTime);
|
|
48
|
-
|
|
49
|
-
// Schedule the next clip time
|
|
50
|
-
this.nextClipTime += buffer.duration;
|
|
51
|
-
|
|
52
|
-
callback();
|
|
53
|
-
});
|
|
54
|
-
}, 1);
|
|
55
15
|
}
|
|
56
16
|
|
|
57
17
|
start(assistant: CreateAssistantDTO | string): void {
|
|
@@ -63,85 +23,26 @@ export default class Vapi {
|
|
|
63
23
|
|
|
64
24
|
client.call
|
|
65
25
|
.callControllerCreateWebCall({
|
|
26
|
+
assistant: typeof assistant === "string" ? undefined : assistant,
|
|
66
27
|
assistantId: typeof assistant === "string" ? assistant : undefined,
|
|
67
|
-
assistant: typeof assistant === "object" ? assistant : undefined,
|
|
68
28
|
})
|
|
69
29
|
.then(({ data }) => {
|
|
70
|
-
const {
|
|
71
|
-
const socket = new WebSocket(url);
|
|
30
|
+
const { url } = data;
|
|
72
31
|
|
|
73
|
-
this.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
};
|
|
79
|
-
socket.onmessage = (event) => {
|
|
80
|
-
if (!socket) return;
|
|
81
|
-
this.onMessage(socket, event);
|
|
82
|
-
};
|
|
83
|
-
socket.onclose = () => {
|
|
84
|
-
this.stop();
|
|
85
|
-
};
|
|
32
|
+
this.call = DailyIframe.createCallObject({
|
|
33
|
+
url,
|
|
34
|
+
audioSource: true,
|
|
35
|
+
videoSource: false,
|
|
36
|
+
});
|
|
86
37
|
})
|
|
87
38
|
.catch((error) => {
|
|
88
39
|
console.error(error);
|
|
89
40
|
});
|
|
90
41
|
}
|
|
91
42
|
|
|
92
|
-
private startRecording(): void {
|
|
93
|
-
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
|
|
94
|
-
this.mediaRecorder = new MediaRecorder(stream, {
|
|
95
|
-
mimeType: "audio/webm;codecs=opus",
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
this.mediaRecorder.start(100);
|
|
99
|
-
this.mediaRecorder.ondataavailable = (event: BlobEvent) => {
|
|
100
|
-
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
101
|
-
this.ws.send(event.data);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
private onMessage(ws: WebSocket, event: MessageEvent): void {
|
|
108
|
-
const data = JSON.parse(event.data);
|
|
109
|
-
if (data.event === "media") {
|
|
110
|
-
const audioData = decode(data.media.payload);
|
|
111
|
-
this.decodeQueue.push(audioData);
|
|
112
|
-
}
|
|
113
|
-
if (data.event === "clear") {
|
|
114
|
-
this.clear();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
clear(): void {
|
|
119
|
-
this.decodeQueue.kill();
|
|
120
|
-
this.decodeQueue = this.createQueue();
|
|
121
|
-
|
|
122
|
-
// Stop the source if it exists
|
|
123
|
-
if (this.source) {
|
|
124
|
-
this.source.stop();
|
|
125
|
-
this.source.disconnect();
|
|
126
|
-
this.source = null;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Reset the next clip time
|
|
130
|
-
this.nextClipTime = 0;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
43
|
stop(): void {
|
|
134
|
-
if (this.source) {
|
|
135
|
-
this.source.stop();
|
|
136
|
-
}
|
|
137
44
|
this.started = false;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
this.ws = null;
|
|
142
|
-
if (this.mediaRecorder) {
|
|
143
|
-
this.mediaRecorder.stop();
|
|
144
|
-
}
|
|
145
|
-
this.mediaRecorder = null;
|
|
45
|
+
this.call?.destroy();
|
|
46
|
+
this.call = null;
|
|
146
47
|
}
|
|
147
48
|
}
|
package/AtomicMediaSource.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import async, { QueueObject } from "async";
|
|
2
|
-
|
|
3
|
-
import EventEmitter from "events";
|
|
4
|
-
|
|
5
|
-
declare global {
|
|
6
|
-
interface Window {
|
|
7
|
-
ManagedMediaSource: any;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// ManagedMediaSource needed for iOS Safari
|
|
12
|
-
function getMediaSource() {
|
|
13
|
-
if (window.ManagedMediaSource) {
|
|
14
|
-
return new window.ManagedMediaSource();
|
|
15
|
-
}
|
|
16
|
-
if (window.MediaSource) {
|
|
17
|
-
return new window.MediaSource();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
class AtomicMediaSource extends EventEmitter {
|
|
24
|
-
mediaSource: any;
|
|
25
|
-
sourceBuffer: SourceBuffer | null = null;
|
|
26
|
-
private operationsQueue: QueueObject<() => Promise<void>>;
|
|
27
|
-
|
|
28
|
-
constructor() {
|
|
29
|
-
super();
|
|
30
|
-
this.mediaSource = getMediaSource();
|
|
31
|
-
this.operationsQueue = this.createOperationQueue();
|
|
32
|
-
|
|
33
|
-
if (!this.mediaSource) {
|
|
34
|
-
console.error("MediaSource is not supported in your browser.");
|
|
35
|
-
// Display a message to the user, or handle this situation in another way
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
this.mediaSource.addEventListener("sourceopen", () => {
|
|
40
|
-
this.mediaSource.duration = Number.POSITIVE_INFINITY;
|
|
41
|
-
this.sourceBuffer = this.mediaSource.addSourceBuffer(
|
|
42
|
-
'audio/webm; codecs="opus"'
|
|
43
|
-
);
|
|
44
|
-
if (this.sourceBuffer) {
|
|
45
|
-
this.sourceBuffer.mode = "sequence";
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
public appendBuffer(buffer: ArrayBuffer) {
|
|
51
|
-
this.operationsQueue.push(() => {
|
|
52
|
-
return new Promise((resolve, reject) => {
|
|
53
|
-
if (this.sourceBuffer) {
|
|
54
|
-
this.sourceBuffer.addEventListener(
|
|
55
|
-
"updateend",
|
|
56
|
-
() => {
|
|
57
|
-
resolve();
|
|
58
|
-
},
|
|
59
|
-
{ once: true }
|
|
60
|
-
);
|
|
61
|
-
this.sourceBuffer.appendBuffer(buffer);
|
|
62
|
-
} else {
|
|
63
|
-
reject();
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
public clearBuffer() {
|
|
70
|
-
// this.operationsQueue.push(() => {
|
|
71
|
-
// return new Promise((resolve, reject) => {
|
|
72
|
-
// if (this.sourceBuffer) {
|
|
73
|
-
// this.sourceBuffer.addEventListener(
|
|
74
|
-
// "updateend",
|
|
75
|
-
// () => {
|
|
76
|
-
// resolve();
|
|
77
|
-
// },
|
|
78
|
-
// { once: true }
|
|
79
|
-
// );
|
|
80
|
-
// if (this.sourceBuffer.buffered.length > 0) {
|
|
81
|
-
// this.sourceBuffer.remove(0, this.sourceBuffer.buffered.end(0));
|
|
82
|
-
// }
|
|
83
|
-
// } else {
|
|
84
|
-
// reject();
|
|
85
|
-
// }
|
|
86
|
-
// });
|
|
87
|
-
// });
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
private createOperationQueue() {
|
|
91
|
-
return async.queue<() => Promise<void>>((task, callback) => {
|
|
92
|
-
task().then(() => callback());
|
|
93
|
-
}, 1);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export default AtomicMediaSource;
|
package/player.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import AtomicMediaSource from "./AtomicMediaSource";
|
|
2
|
-
import EventEmitter from "events";
|
|
3
|
-
|
|
4
|
-
export class ContinuousPlayer extends EventEmitter {
|
|
5
|
-
private audioCtx: AudioContext;
|
|
6
|
-
private source: AudioBufferSourceNode | null = null;
|
|
7
|
-
|
|
8
|
-
constructor() {
|
|
9
|
-
super();
|
|
10
|
-
|
|
11
|
-
this.audioCtx = new AudioContext();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
start() {}
|
|
15
|
-
|
|
16
|
-
async playChunk(audioData: ArrayBuffer) {
|
|
17
|
-
const audioBuffer = await this.audioCtx.decodeAudioData(audioData);
|
|
18
|
-
this.source = this.audioCtx.createBufferSource();
|
|
19
|
-
this.source.buffer = audioBuffer;
|
|
20
|
-
this.source.connect(this.audioCtx.destination);
|
|
21
|
-
this.source.start();
|
|
22
|
-
this.emit("speech-start");
|
|
23
|
-
this.source.onended = () => this.emit("speech-end");
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
clear() {}
|
|
27
|
-
}
|
package/silent.mp3
DELETED
|
Binary file
|