@pipecat-ai/gemini-live-websocket-transport 1.3.0 → 1.5.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/dist/index.js +170 -91
- package/dist/index.js.map +1 -1
- package/dist/index.module.js +170 -91
- package/dist/index.module.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -99,11 +99,11 @@ $parcel$export($9a9a227726ea6e1a$exports, "GeminiLiveWebsocketTransport", () =>
|
|
|
99
99
|
const { bitsPerSample: bitsPerSample, channels: channels, data: data } = audio;
|
|
100
100
|
const output = [
|
|
101
101
|
// Header
|
|
102
|
-
|
|
102
|
+
"RIFF",
|
|
103
103
|
this._packData(1, 52),
|
|
104
|
-
|
|
104
|
+
"WAVE",
|
|
105
105
|
// chunk 1
|
|
106
|
-
|
|
106
|
+
"fmt ",
|
|
107
107
|
this._packData(1, 16),
|
|
108
108
|
this._packData(0, 1),
|
|
109
109
|
this._packData(0, channels.length),
|
|
@@ -112,12 +112,12 @@ $parcel$export($9a9a227726ea6e1a$exports, "GeminiLiveWebsocketTransport", () =>
|
|
|
112
112
|
this._packData(0, channels.length * bitsPerSample / 8),
|
|
113
113
|
this._packData(0, bitsPerSample),
|
|
114
114
|
// chunk 2
|
|
115
|
-
|
|
115
|
+
"data",
|
|
116
116
|
this._packData(1, channels[0].length * channels.length * bitsPerSample / 8),
|
|
117
117
|
data
|
|
118
118
|
];
|
|
119
119
|
const blob = new Blob(output, {
|
|
120
|
-
type:
|
|
120
|
+
type: "audio/mpeg"
|
|
121
121
|
});
|
|
122
122
|
const url = URL.createObjectURL(blob);
|
|
123
123
|
return {
|
|
@@ -154,18 +154,18 @@ const $e10a9de47f58e137$var$octave8Frequencies = [
|
|
|
154
154
|
];
|
|
155
155
|
// Labels for each of the above frequencies
|
|
156
156
|
const $e10a9de47f58e137$var$octave8FrequencyLabels = [
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
157
|
+
"C",
|
|
158
|
+
"C#",
|
|
159
|
+
"D",
|
|
160
|
+
"D#",
|
|
161
|
+
"E",
|
|
162
|
+
"F",
|
|
163
|
+
"F#",
|
|
164
|
+
"G",
|
|
165
|
+
"G#",
|
|
166
|
+
"A",
|
|
167
|
+
"A#",
|
|
168
|
+
"B"
|
|
169
169
|
];
|
|
170
170
|
const $e10a9de47f58e137$export$776c63898ae5b636 = [];
|
|
171
171
|
const $e10a9de47f58e137$export$facd167cc27ea9b0 = [];
|
|
@@ -200,7 +200,7 @@ class $5853841ab58516d4$export$2c3136da0bf130f9 {
|
|
|
200
200
|
* @param {number} [minDecibels] default -100
|
|
201
201
|
* @param {number} [maxDecibels] default -30
|
|
202
202
|
* @returns {AudioAnalysisOutputType}
|
|
203
|
-
*/ static getFrequencies(analyser, sampleRate, fftResult, analysisType =
|
|
203
|
+
*/ static getFrequencies(analyser, sampleRate, fftResult, analysisType = "frequency", minDecibels = -100, maxDecibels = -30) {
|
|
204
204
|
if (!fftResult) {
|
|
205
205
|
fftResult = new Float32Array(analyser.frequencyBinCount);
|
|
206
206
|
analyser.getFloatFrequencyData(fftResult);
|
|
@@ -210,8 +210,8 @@ class $5853841ab58516d4$export$2c3136da0bf130f9 {
|
|
|
210
210
|
let outputValues;
|
|
211
211
|
let frequencies;
|
|
212
212
|
let labels;
|
|
213
|
-
if (analysisType ===
|
|
214
|
-
const useFrequencies = analysisType ===
|
|
213
|
+
if (analysisType === "music" || analysisType === "voice") {
|
|
214
|
+
const useFrequencies = analysisType === "voice" ? (0, $e10a9de47f58e137$export$dbc1581ed2cfa183) : (0, $e10a9de47f58e137$export$776c63898ae5b636);
|
|
215
215
|
const aggregateOutput = Array(useFrequencies.length).fill(minDecibels);
|
|
216
216
|
for(let i = 0; i < fftResult.length; i++){
|
|
217
217
|
const frequency = i * frequencyStep;
|
|
@@ -222,8 +222,8 @@ class $5853841ab58516d4$export$2c3136da0bf130f9 {
|
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
224
|
outputValues = aggregateOutput;
|
|
225
|
-
frequencies = analysisType ===
|
|
226
|
-
labels = analysisType ===
|
|
225
|
+
frequencies = analysisType === "voice" ? (0, $e10a9de47f58e137$export$dbc1581ed2cfa183) : (0, $e10a9de47f58e137$export$776c63898ae5b636);
|
|
226
|
+
labels = analysisType === "voice" ? (0, $e10a9de47f58e137$export$30a6f2881311088f) : (0, $e10a9de47f58e137$export$facd167cc27ea9b0);
|
|
227
227
|
} else {
|
|
228
228
|
outputValues = Array.from(fftResult);
|
|
229
229
|
frequencies = outputValues.map((_, i)=>frequencyStep * i);
|
|
@@ -309,7 +309,7 @@ class $5853841ab58516d4$export$2c3136da0bf130f9 {
|
|
|
309
309
|
* @param {number} [minDecibels] default -100
|
|
310
310
|
* @param {number} [maxDecibels] default -30
|
|
311
311
|
* @returns {AudioAnalysisOutputType}
|
|
312
|
-
*/ getFrequencies(analysisType =
|
|
312
|
+
*/ getFrequencies(analysisType = "frequency", minDecibels = -100, maxDecibels = -30) {
|
|
313
313
|
let fftResult = null;
|
|
314
314
|
if (this.audioBuffer && this.fftResults.length) {
|
|
315
315
|
const pct = this.audio.currentTime / this.audio.duration;
|
|
@@ -323,7 +323,7 @@ class $5853841ab58516d4$export$2c3136da0bf130f9 {
|
|
|
323
323
|
* user interaction when the AudioAnalysis was instantiated.
|
|
324
324
|
* @returns {Promise<true>}
|
|
325
325
|
*/ async resumeIfSuspended() {
|
|
326
|
-
if (this.context.state ===
|
|
326
|
+
if (this.context.state === "suspended") await this.context.resume();
|
|
327
327
|
return true;
|
|
328
328
|
}
|
|
329
329
|
}
|
|
@@ -423,7 +423,7 @@ registerProcessor('stream_processor', StreamProcessor);
|
|
|
423
423
|
const $d6d3743ef65c7519$var$script = new Blob([
|
|
424
424
|
$d6d3743ef65c7519$export$50b76700e2b15e9
|
|
425
425
|
], {
|
|
426
|
-
type:
|
|
426
|
+
type: "application/javascript"
|
|
427
427
|
});
|
|
428
428
|
const $d6d3743ef65c7519$var$src = URL.createObjectURL($d6d3743ef65c7519$var$script);
|
|
429
429
|
const $d6d3743ef65c7519$export$bfa8c596114d74df = $d6d3743ef65c7519$var$src;
|
|
@@ -778,7 +778,7 @@ registerProcessor('audio_processor', AudioProcessor);
|
|
|
778
778
|
const $2cf6a2d8a6d031bc$var$script = new Blob([
|
|
779
779
|
$2cf6a2d8a6d031bc$var$AudioProcessorWorklet
|
|
780
780
|
], {
|
|
781
|
-
type:
|
|
781
|
+
type: "application/javascript"
|
|
782
782
|
});
|
|
783
783
|
const $2cf6a2d8a6d031bc$var$src = URL.createObjectURL($2cf6a2d8a6d031bc$var$script);
|
|
784
784
|
const $2cf6a2d8a6d031bc$export$1f65f50a8cbff43c = $2cf6a2d8a6d031bc$var$src;
|
|
@@ -842,7 +842,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
842
842
|
blob = new Blob([
|
|
843
843
|
arrayBuffer
|
|
844
844
|
], {
|
|
845
|
-
type:
|
|
845
|
+
type: "audio/wav"
|
|
846
846
|
});
|
|
847
847
|
} else {
|
|
848
848
|
let float32Array;
|
|
@@ -897,9 +897,9 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
897
897
|
* Retrieves the current status of the recording
|
|
898
898
|
* @returns {"ended"|"paused"|"recording"}
|
|
899
899
|
*/ getStatus() {
|
|
900
|
-
if (!this.processor) return
|
|
901
|
-
else if (!this.recording) return
|
|
902
|
-
else return
|
|
900
|
+
if (!this.processor) return "ended";
|
|
901
|
+
else if (!this.recording) return "paused";
|
|
902
|
+
else return "recording";
|
|
903
903
|
}
|
|
904
904
|
/**
|
|
905
905
|
* Sends an event to the AudioWorklet
|
|
@@ -910,7 +910,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
910
910
|
* @returns {Promise<{[key: string]: any}>}
|
|
911
911
|
*/ async _event(name, data = {}, _processor = null) {
|
|
912
912
|
_processor = _processor || this.processor;
|
|
913
|
-
if (!_processor) throw new Error(
|
|
913
|
+
if (!_processor) throw new Error("Can not send events without recording first");
|
|
914
914
|
const message = {
|
|
915
915
|
event: name,
|
|
916
916
|
id: this._lastEventId++,
|
|
@@ -932,7 +932,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
932
932
|
* @returns {true}
|
|
933
933
|
*/ listenForDeviceChange(callback) {
|
|
934
934
|
if (callback === null && this._deviceChangeCallback) {
|
|
935
|
-
navigator.mediaDevices.removeEventListener(
|
|
935
|
+
navigator.mediaDevices.removeEventListener("devicechange", this._deviceChangeCallback);
|
|
936
936
|
this._deviceChangeCallback = null;
|
|
937
937
|
} else if (callback !== null) {
|
|
938
938
|
// Basically a debounce; we only want this called once when devices change
|
|
@@ -940,7 +940,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
940
940
|
// if a few are operating at the same time
|
|
941
941
|
let lastId = 0;
|
|
942
942
|
let lastDevices = [];
|
|
943
|
-
const serializeDevices = (devices)=>devices.map((d)=>d.deviceId).sort().join(
|
|
943
|
+
const serializeDevices = (devices)=>devices.map((d)=>d.deviceId).sort().join(",");
|
|
944
944
|
const cb = async ()=>{
|
|
945
945
|
let id = ++lastId;
|
|
946
946
|
const devices = await this.listDevices();
|
|
@@ -951,7 +951,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
951
951
|
}
|
|
952
952
|
}
|
|
953
953
|
};
|
|
954
|
-
navigator.mediaDevices.addEventListener(
|
|
954
|
+
navigator.mediaDevices.addEventListener("devicechange", cb);
|
|
955
955
|
cb();
|
|
956
956
|
this._deviceChangeCallback = cb;
|
|
957
957
|
}
|
|
@@ -969,9 +969,9 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
969
969
|
* @returns {Promise<true>}
|
|
970
970
|
*/ async requestPermission() {
|
|
971
971
|
const permissionStatus = await navigator.permissions.query({
|
|
972
|
-
name:
|
|
972
|
+
name: "microphone"
|
|
973
973
|
});
|
|
974
|
-
if (permissionStatus.state ===
|
|
974
|
+
if (permissionStatus.state === "denied") {
|
|
975
975
|
if (this._deviceErrorCallback) this._deviceErrorCallback({
|
|
976
976
|
devices: [
|
|
977
977
|
"mic"
|
|
@@ -979,7 +979,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
979
979
|
type: "unknown",
|
|
980
980
|
error: new Error("Microphone access denied")
|
|
981
981
|
});
|
|
982
|
-
} else if (permissionStatus.state ===
|
|
982
|
+
} else if (permissionStatus.state === "prompt") try {
|
|
983
983
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
984
984
|
audio: true
|
|
985
985
|
});
|
|
@@ -1001,10 +1001,10 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1001
1001
|
* List all eligible devices for recording, will request permission to use microphone
|
|
1002
1002
|
* @returns {Promise<Array<MediaDeviceInfo & {default: boolean}>>}
|
|
1003
1003
|
*/ async listDevices() {
|
|
1004
|
-
if (!navigator.mediaDevices || !(
|
|
1004
|
+
if (!navigator.mediaDevices || !("enumerateDevices" in navigator.mediaDevices)) throw new Error("Could not request user devices");
|
|
1005
1005
|
await this.requestPermission();
|
|
1006
1006
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
1007
|
-
const audioDevices = devices.filter((device)=>device.kind ===
|
|
1007
|
+
const audioDevices = devices.filter((device)=>device.kind === "audioinput");
|
|
1008
1008
|
return audioDevices;
|
|
1009
1009
|
// const defaultDeviceIndex = audioDevices.findIndex(
|
|
1010
1010
|
// (device) => device.deviceId === 'default'
|
|
@@ -1030,7 +1030,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1030
1030
|
* @returns {Promise<true>}
|
|
1031
1031
|
*/ async begin(deviceId) {
|
|
1032
1032
|
if (this.processor) throw new Error(`Already connected: please call .end() to start a new session`);
|
|
1033
|
-
if (!navigator.mediaDevices || !(
|
|
1033
|
+
if (!navigator.mediaDevices || !("getUserMedia" in navigator.mediaDevices)) {
|
|
1034
1034
|
if (this._deviceErrorCallback) this._deviceErrorCallback({
|
|
1035
1035
|
devices: [
|
|
1036
1036
|
"mic",
|
|
@@ -1038,7 +1038,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1038
1038
|
],
|
|
1039
1039
|
type: "undefined-mediadevices"
|
|
1040
1040
|
});
|
|
1041
|
-
throw new Error(
|
|
1041
|
+
throw new Error("Could not request user media");
|
|
1042
1042
|
}
|
|
1043
1043
|
deviceId = deviceId ?? this.deviceSelection?.deviceId;
|
|
1044
1044
|
try {
|
|
@@ -1059,13 +1059,13 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1059
1059
|
type: "unknown",
|
|
1060
1060
|
error: err
|
|
1061
1061
|
});
|
|
1062
|
-
throw new Error(
|
|
1062
|
+
throw new Error("Could not start media stream");
|
|
1063
1063
|
}
|
|
1064
1064
|
this.listDevices().then((devices)=>{
|
|
1065
1065
|
deviceId = this.stream.getAudioTracks()[0].getSettings().deviceId;
|
|
1066
|
-
console.log(
|
|
1066
|
+
console.log("find current device", devices, deviceId, this.stream.getAudioTracks()[0].getSettings());
|
|
1067
1067
|
this.deviceSelection = devices.find((d)=>d.deviceId === deviceId);
|
|
1068
|
-
console.log(
|
|
1068
|
+
console.log("current device", this.deviceSelection);
|
|
1069
1069
|
});
|
|
1070
1070
|
const context = new AudioContext({
|
|
1071
1071
|
sampleRate: this.sampleRate
|
|
@@ -1078,11 +1078,11 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1078
1078
|
console.error(e);
|
|
1079
1079
|
throw new Error(`Could not add audioWorklet module: ${this.scriptSrc}`);
|
|
1080
1080
|
}
|
|
1081
|
-
const processor = new AudioWorkletNode(context,
|
|
1081
|
+
const processor = new AudioWorkletNode(context, "audio_processor");
|
|
1082
1082
|
processor.port.onmessage = (e)=>{
|
|
1083
1083
|
const { event: event, id: id, data: data } = e.data;
|
|
1084
|
-
if (event ===
|
|
1085
|
-
else if (event ===
|
|
1084
|
+
if (event === "receipt") this.eventReceipts[id] = data;
|
|
1085
|
+
else if (event === "chunk") {
|
|
1086
1086
|
if (this._chunkProcessorSize) {
|
|
1087
1087
|
const buffer = this._chunkProcessorBuffer;
|
|
1088
1088
|
this._chunkProcessorBuffer = {
|
|
@@ -1113,7 +1113,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1113
1113
|
this.node = node;
|
|
1114
1114
|
this.analyser = analyser;
|
|
1115
1115
|
this.processor = processor;
|
|
1116
|
-
console.log(
|
|
1116
|
+
console.log("begin completed");
|
|
1117
1117
|
return true;
|
|
1118
1118
|
}
|
|
1119
1119
|
/**
|
|
@@ -1122,8 +1122,8 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1122
1122
|
* @param {number} [minDecibels] default -100
|
|
1123
1123
|
* @param {number} [maxDecibels] default -30
|
|
1124
1124
|
* @returns {import('./analysis/audio_analysis.js').AudioAnalysisOutputType}
|
|
1125
|
-
*/ getFrequencies(analysisType =
|
|
1126
|
-
if (!this.processor) throw new Error(
|
|
1125
|
+
*/ getFrequencies(analysisType = "frequency", minDecibels = -100, maxDecibels = -30) {
|
|
1126
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1127
1127
|
return (0, $5853841ab58516d4$export$2c3136da0bf130f9).getFrequencies(this.analyser, this.sampleRate, null, analysisType, minDecibels, maxDecibels);
|
|
1128
1128
|
}
|
|
1129
1129
|
/**
|
|
@@ -1131,11 +1131,11 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1131
1131
|
* Keeps microphone stream open but halts storage of audio
|
|
1132
1132
|
* @returns {Promise<true>}
|
|
1133
1133
|
*/ async pause() {
|
|
1134
|
-
if (!this.processor) throw new Error(
|
|
1135
|
-
else if (!this.recording) throw new Error(
|
|
1134
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1135
|
+
else if (!this.recording) throw new Error("Already paused: please call .record() first");
|
|
1136
1136
|
if (this._chunkProcessorBuffer.raw.byteLength) this._chunkProcessor(this._chunkProcessorBuffer);
|
|
1137
|
-
this.log(
|
|
1138
|
-
await this._event(
|
|
1137
|
+
this.log("Pausing ...");
|
|
1138
|
+
await this._event("stop");
|
|
1139
1139
|
this.recording = false;
|
|
1140
1140
|
return true;
|
|
1141
1141
|
}
|
|
@@ -1145,17 +1145,17 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1145
1145
|
* @param {number} [chunkSize] chunkProcessor will not be triggered until this size threshold met in mono audio
|
|
1146
1146
|
* @returns {Promise<true>}
|
|
1147
1147
|
*/ async record(chunkProcessor = ()=>{}, chunkSize = 8192) {
|
|
1148
|
-
if (!this.processor) throw new Error(
|
|
1149
|
-
else if (this.recording) throw new Error(
|
|
1150
|
-
else if (typeof chunkProcessor !==
|
|
1148
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1149
|
+
else if (this.recording) throw new Error("Already recording: please call .pause() first");
|
|
1150
|
+
else if (typeof chunkProcessor !== "function") throw new Error(`chunkProcessor must be a function`);
|
|
1151
1151
|
this._chunkProcessor = chunkProcessor;
|
|
1152
1152
|
this._chunkProcessorSize = chunkSize;
|
|
1153
1153
|
this._chunkProcessorBuffer = {
|
|
1154
1154
|
raw: new ArrayBuffer(0),
|
|
1155
1155
|
mono: new ArrayBuffer(0)
|
|
1156
1156
|
};
|
|
1157
|
-
this.log(
|
|
1158
|
-
await this._event(
|
|
1157
|
+
this.log("Recording ...");
|
|
1158
|
+
await this._event("start");
|
|
1159
1159
|
this.recording = true;
|
|
1160
1160
|
return true;
|
|
1161
1161
|
}
|
|
@@ -1163,17 +1163,17 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1163
1163
|
* Clears the audio buffer, empties stored recording
|
|
1164
1164
|
* @returns {Promise<true>}
|
|
1165
1165
|
*/ async clear() {
|
|
1166
|
-
if (!this.processor) throw new Error(
|
|
1167
|
-
await this._event(
|
|
1166
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1167
|
+
await this._event("clear");
|
|
1168
1168
|
return true;
|
|
1169
1169
|
}
|
|
1170
1170
|
/**
|
|
1171
1171
|
* Reads the current audio stream data
|
|
1172
1172
|
* @returns {Promise<{meanValues: Float32Array, channels: Array<Float32Array>}>}
|
|
1173
1173
|
*/ async read() {
|
|
1174
|
-
if (!this.processor) throw new Error(
|
|
1175
|
-
this.log(
|
|
1176
|
-
const result = await this._event(
|
|
1174
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1175
|
+
this.log("Reading ...");
|
|
1176
|
+
const result = await this._event("read");
|
|
1177
1177
|
return result;
|
|
1178
1178
|
}
|
|
1179
1179
|
/**
|
|
@@ -1181,10 +1181,10 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1181
1181
|
* @param {boolean} [force] Force saving while still recording
|
|
1182
1182
|
* @returns {Promise<import('./wav_packer.js').WavPackerAudioType>}
|
|
1183
1183
|
*/ async save(force = false) {
|
|
1184
|
-
if (!this.processor) throw new Error(
|
|
1185
|
-
if (!force && this.recording) throw new Error(
|
|
1186
|
-
this.log(
|
|
1187
|
-
const exportData = await this._event(
|
|
1184
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1185
|
+
if (!force && this.recording) throw new Error("Currently recording: please call .pause() first, or call .save(true) to force");
|
|
1186
|
+
this.log("Exporting ...");
|
|
1187
|
+
const exportData = await this._event("export");
|
|
1188
1188
|
const packer = new (0, $a61750b8fbee4dae$export$13afda237b1c9846)();
|
|
1189
1189
|
const result = packer.pack(this.sampleRate, exportData.audio);
|
|
1190
1190
|
return result;
|
|
@@ -1193,15 +1193,15 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1193
1193
|
* Ends the current recording session and saves the result
|
|
1194
1194
|
* @returns {Promise<import('./wav_packer.js').WavPackerAudioType>}
|
|
1195
1195
|
*/ async end() {
|
|
1196
|
-
if (!this.processor) throw new Error(
|
|
1196
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1197
1197
|
const _processor = this.processor;
|
|
1198
|
-
this.log(
|
|
1199
|
-
await this._event(
|
|
1198
|
+
this.log("Stopping ...");
|
|
1199
|
+
await this._event("stop");
|
|
1200
1200
|
this.recording = false;
|
|
1201
1201
|
const tracks = this.stream.getTracks();
|
|
1202
1202
|
tracks.forEach((track)=>track.stop());
|
|
1203
|
-
this.log(
|
|
1204
|
-
const exportData = await this._event(
|
|
1203
|
+
this.log("Exporting ...");
|
|
1204
|
+
const exportData = await this._event("export", {}, _processor);
|
|
1205
1205
|
this.processor.disconnect();
|
|
1206
1206
|
this.source.disconnect();
|
|
1207
1207
|
this.node.disconnect();
|
|
@@ -1232,6 +1232,29 @@ globalThis.WavRecorder = $0d471c38435bdab6$export$439b217ca659a877;
|
|
|
1232
1232
|
|
|
1233
1233
|
|
|
1234
1234
|
|
|
1235
|
+
/**
|
|
1236
|
+
* Resamples audio data from one sample rate to another using linear interpolation
|
|
1237
|
+
* @param {ArrayBuffer} inputBuffer - Input audio data as Int16 PCM
|
|
1238
|
+
* @param {number} inputSampleRate - Sample rate of input audio
|
|
1239
|
+
* @param {number} outputSampleRate - Desired output sample rate
|
|
1240
|
+
* @returns {ArrayBuffer} - Resampled audio data as Int16 PCM
|
|
1241
|
+
*/ function $7cef7a69bdf8f84d$var$resampleAudioBuffer(inputBuffer, inputSampleRate, outputSampleRate) {
|
|
1242
|
+
if (inputSampleRate === outputSampleRate) return inputBuffer;
|
|
1243
|
+
const inputView = new Int16Array(inputBuffer);
|
|
1244
|
+
const ratio = inputSampleRate / outputSampleRate;
|
|
1245
|
+
const outputLength = Math.round(inputView.length / ratio);
|
|
1246
|
+
const outputBuffer = new ArrayBuffer(outputLength * 2);
|
|
1247
|
+
const outputView = new Int16Array(outputBuffer);
|
|
1248
|
+
for(let i = 0; i < outputLength; i++){
|
|
1249
|
+
const srcIndex = i * ratio;
|
|
1250
|
+
const srcIndexFloor = Math.floor(srcIndex);
|
|
1251
|
+
const srcIndexCeil = Math.min(srcIndexFloor + 1, inputView.length - 1);
|
|
1252
|
+
const t = srcIndex - srcIndexFloor;
|
|
1253
|
+
// Linear interpolation
|
|
1254
|
+
outputView[i] = Math.round(inputView[srcIndexFloor] * (1 - t) + inputView[srcIndexCeil] * t);
|
|
1255
|
+
}
|
|
1256
|
+
return outputBuffer;
|
|
1257
|
+
}
|
|
1235
1258
|
class $7cef7a69bdf8f84d$export$2934cf2d25c67a48 {
|
|
1236
1259
|
/**
|
|
1237
1260
|
* Create a new MediaStreamRecorder instance
|
|
@@ -1320,9 +1343,21 @@ class $7cef7a69bdf8f84d$export$2934cf2d25c67a48 {
|
|
|
1320
1343
|
this.stream = new MediaStream([
|
|
1321
1344
|
audioTrack
|
|
1322
1345
|
]);
|
|
1323
|
-
|
|
1346
|
+
// Firefox workaround: Firefox doesn't support connecting AudioNodes from
|
|
1347
|
+
// AudioContexts with different sample rates. So we create the AudioContext
|
|
1348
|
+
// at the native sample rate and resample manually.
|
|
1349
|
+
// This workaround may be temporary; Firefox is in the process of adding
|
|
1350
|
+
// support for this.
|
|
1351
|
+
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1674892
|
|
1352
|
+
const isFirefox = navigator.userAgent.toLowerCase().includes("firefox");
|
|
1353
|
+
let context;
|
|
1354
|
+
if (isFirefox) // Firefox: Use native sample rate and resample manually
|
|
1355
|
+
context = new AudioContext();
|
|
1356
|
+
else // Other browsers: Let the AudioContext handle resampling
|
|
1357
|
+
context = new AudioContext({
|
|
1324
1358
|
sampleRate: this.sampleRate
|
|
1325
1359
|
});
|
|
1360
|
+
const contextSampleRate = context.sampleRate;
|
|
1326
1361
|
const source = context.createMediaStreamSource(this.stream);
|
|
1327
1362
|
// Load and execute the module script.
|
|
1328
1363
|
try {
|
|
@@ -1336,11 +1371,17 @@ class $7cef7a69bdf8f84d$export$2934cf2d25c67a48 {
|
|
|
1336
1371
|
const { event: event, id: id, data: data } = e.data;
|
|
1337
1372
|
if (event === "receipt") this.eventReceipts[id] = data;
|
|
1338
1373
|
else if (event === "chunk") {
|
|
1374
|
+
// Resample chunk data from native sample rate to target sample rate
|
|
1375
|
+
// (no-op if rates match, i.e. for non-Firefox browsers)
|
|
1376
|
+
const resampledData = {
|
|
1377
|
+
raw: $7cef7a69bdf8f84d$var$resampleAudioBuffer(data.raw, contextSampleRate, this.sampleRate),
|
|
1378
|
+
mono: $7cef7a69bdf8f84d$var$resampleAudioBuffer(data.mono, contextSampleRate, this.sampleRate)
|
|
1379
|
+
};
|
|
1339
1380
|
if (this._chunkProcessorSize) {
|
|
1340
1381
|
const buffer = this._chunkProcessorBuffer;
|
|
1341
1382
|
this._chunkProcessorBuffer = {
|
|
1342
|
-
raw: (0, $a61750b8fbee4dae$export$13afda237b1c9846).mergeBuffers(buffer.raw,
|
|
1343
|
-
mono: (0, $a61750b8fbee4dae$export$13afda237b1c9846).mergeBuffers(buffer.mono,
|
|
1383
|
+
raw: (0, $a61750b8fbee4dae$export$13afda237b1c9846).mergeBuffers(buffer.raw, resampledData.raw),
|
|
1384
|
+
mono: (0, $a61750b8fbee4dae$export$13afda237b1c9846).mergeBuffers(buffer.mono, resampledData.mono)
|
|
1344
1385
|
};
|
|
1345
1386
|
if (this._chunkProcessorBuffer.mono.byteLength >= this._chunkProcessorSize) {
|
|
1346
1387
|
this._chunkProcessor(this._chunkProcessorBuffer);
|
|
@@ -1349,7 +1390,7 @@ class $7cef7a69bdf8f84d$export$2934cf2d25c67a48 {
|
|
|
1349
1390
|
mono: new ArrayBuffer(0)
|
|
1350
1391
|
};
|
|
1351
1392
|
}
|
|
1352
|
-
} else this._chunkProcessor(
|
|
1393
|
+
} else this._chunkProcessor(resampledData);
|
|
1353
1394
|
}
|
|
1354
1395
|
};
|
|
1355
1396
|
const node = source.connect(processor);
|
|
@@ -2480,26 +2521,64 @@ var $1c088932741d88e6$export$c95c65abc5f47125 = /** @class */ function(_super) {
|
|
|
2480
2521
|
};
|
|
2481
2522
|
DailyMediaManager.prototype.updateSpeaker = function(speakerId) {
|
|
2482
2523
|
return $1c088932741d88e6$var$__awaiter(this, void 0, Promise, function() {
|
|
2483
|
-
var sID, speakers, defaultSpeaker_1, defaultSpeakerCp;
|
|
2524
|
+
var dInfo, e_1, sID, speakers, defaultSpeaker_1, defaultSpeakerCp;
|
|
2484
2525
|
var _this = this;
|
|
2485
|
-
var _a, _b;
|
|
2486
|
-
return $1c088932741d88e6$var$__generator(this, function(
|
|
2487
|
-
switch(
|
|
2526
|
+
var _a, _b, _c, _d;
|
|
2527
|
+
return $1c088932741d88e6$var$__generator(this, function(_e) {
|
|
2528
|
+
switch(_e.label){
|
|
2488
2529
|
case 0:
|
|
2530
|
+
if (!!this._wavStreamPlayer) return [
|
|
2531
|
+
3 /*break*/ ,
|
|
2532
|
+
5
|
|
2533
|
+
];
|
|
2534
|
+
_e.label = 1;
|
|
2535
|
+
case 1:
|
|
2536
|
+
_e.trys.push([
|
|
2537
|
+
1,
|
|
2538
|
+
3,
|
|
2539
|
+
,
|
|
2540
|
+
4
|
|
2541
|
+
]);
|
|
2542
|
+
return [
|
|
2543
|
+
4 /*yield*/ ,
|
|
2544
|
+
this._daily.setOutputDeviceAsync({
|
|
2545
|
+
outputDeviceId: speakerId
|
|
2546
|
+
})
|
|
2547
|
+
];
|
|
2548
|
+
case 2:
|
|
2549
|
+
dInfo = _e.sent();
|
|
2550
|
+
this._selectedSpeaker = dInfo.speaker;
|
|
2551
|
+
(_b = (_a = this._callbacks).onSpeakerUpdated) === null || _b === void 0 || _b.call(_a, this._selectedSpeaker);
|
|
2552
|
+
return [
|
|
2553
|
+
3 /*break*/ ,
|
|
2554
|
+
4
|
|
2555
|
+
];
|
|
2556
|
+
case 3:
|
|
2557
|
+
e_1 = _e.sent();
|
|
2558
|
+
console.error("Error setting output device", e_1);
|
|
2559
|
+
return [
|
|
2560
|
+
3 /*break*/ ,
|
|
2561
|
+
4
|
|
2562
|
+
];
|
|
2563
|
+
case 4:
|
|
2564
|
+
return [
|
|
2565
|
+
2 /*return*/
|
|
2566
|
+
];
|
|
2567
|
+
case 5:
|
|
2489
2568
|
if (speakerId !== "default" && this._selectedSpeaker.deviceId === speakerId) return [
|
|
2490
2569
|
2 /*return*/
|
|
2491
2570
|
];
|
|
2492
2571
|
sID = speakerId;
|
|
2493
2572
|
if (!(sID === "default")) return [
|
|
2494
2573
|
3 /*break*/ ,
|
|
2495
|
-
|
|
2574
|
+
7
|
|
2496
2575
|
];
|
|
2497
2576
|
return [
|
|
2498
2577
|
4 /*yield*/ ,
|
|
2499
2578
|
this.getAllSpeakers()
|
|
2500
2579
|
];
|
|
2501
|
-
case
|
|
2502
|
-
speakers =
|
|
2580
|
+
case 6:
|
|
2581
|
+
speakers = _e.sent();
|
|
2503
2582
|
defaultSpeaker_1 = speakers.find(function(s) {
|
|
2504
2583
|
return s.deviceId === "default";
|
|
2505
2584
|
});
|
|
@@ -2513,10 +2592,10 @@ var $1c088932741d88e6$export$c95c65abc5f47125 = /** @class */ function(_super) {
|
|
|
2513
2592
|
defaultSpeakerCp = speakers.find(function(s) {
|
|
2514
2593
|
return defaultSpeaker_1.label.includes(s.label);
|
|
2515
2594
|
});
|
|
2516
|
-
sID = (
|
|
2517
|
-
|
|
2518
|
-
case
|
|
2519
|
-
(
|
|
2595
|
+
sID = (_c = defaultSpeakerCp === null || defaultSpeakerCp === void 0 ? void 0 : defaultSpeakerCp.deviceId) !== null && _c !== void 0 ? _c : speakerId;
|
|
2596
|
+
_e.label = 7;
|
|
2597
|
+
case 7:
|
|
2598
|
+
(_d = this._wavStreamPlayer) === null || _d === void 0 || _d.updateSpeaker(sID).then(function() {
|
|
2520
2599
|
var _a, _b;
|
|
2521
2600
|
_this._selectedSpeaker = {
|
|
2522
2601
|
deviceId: speakerId
|
|
@@ -2703,7 +2782,7 @@ var $1c088932741d88e6$export$c95c65abc5f47125 = /** @class */ function(_super) {
|
|
|
2703
2782
|
};
|
|
2704
2783
|
DailyMediaManager.prototype.handleTrackStarted = function(event) {
|
|
2705
2784
|
return $1c088932741d88e6$var$__awaiter(this, void 0, void 0, function() {
|
|
2706
|
-
var status, _a,
|
|
2785
|
+
var status, _a, e_2, e_3;
|
|
2707
2786
|
var _b, _c, _d, _e;
|
|
2708
2787
|
return $1c088932741d88e6$var$__generator(this, function(_f) {
|
|
2709
2788
|
switch(_f.label){
|
|
@@ -2767,7 +2846,7 @@ var $1c088932741d88e6$export$c95c65abc5f47125 = /** @class */ function(_super) {
|
|
|
2767
2846
|
4
|
|
2768
2847
|
];
|
|
2769
2848
|
case 3:
|
|
2770
|
-
|
|
2849
|
+
e_2 = _f.sent();
|
|
2771
2850
|
return [
|
|
2772
2851
|
3 /*break*/ ,
|
|
2773
2852
|
4
|
|
@@ -2814,7 +2893,7 @@ var $1c088932741d88e6$export$c95c65abc5f47125 = /** @class */ function(_super) {
|
|
|
2814
2893
|
11
|
|
2815
2894
|
];
|
|
2816
2895
|
case 10:
|
|
2817
|
-
|
|
2896
|
+
e_3 = _f.sent();
|
|
2818
2897
|
return [
|
|
2819
2898
|
3 /*break*/ ,
|
|
2820
2899
|
11
|