@pipecat-ai/daily-transport 1.4.1 → 1.6.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 +119 -78
- package/dist/index.js.map +1 -1
- package/dist/index.module.js +119 -78
- package/dist/index.module.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -101,11 +101,11 @@ $parcel$export($28bdeb35415d61e8$exports, "DailyTransport", () => $28bdeb35415d6
|
|
|
101
101
|
const { bitsPerSample: bitsPerSample, channels: channels, data: data } = audio;
|
|
102
102
|
const output = [
|
|
103
103
|
// Header
|
|
104
|
-
|
|
104
|
+
"RIFF",
|
|
105
105
|
this._packData(1, 52),
|
|
106
|
-
|
|
106
|
+
"WAVE",
|
|
107
107
|
// chunk 1
|
|
108
|
-
|
|
108
|
+
"fmt ",
|
|
109
109
|
this._packData(1, 16),
|
|
110
110
|
this._packData(0, 1),
|
|
111
111
|
this._packData(0, channels.length),
|
|
@@ -114,12 +114,12 @@ $parcel$export($28bdeb35415d61e8$exports, "DailyTransport", () => $28bdeb35415d6
|
|
|
114
114
|
this._packData(0, channels.length * bitsPerSample / 8),
|
|
115
115
|
this._packData(0, bitsPerSample),
|
|
116
116
|
// chunk 2
|
|
117
|
-
|
|
117
|
+
"data",
|
|
118
118
|
this._packData(1, channels[0].length * channels.length * bitsPerSample / 8),
|
|
119
119
|
data
|
|
120
120
|
];
|
|
121
121
|
const blob = new Blob(output, {
|
|
122
|
-
type:
|
|
122
|
+
type: "audio/mpeg"
|
|
123
123
|
});
|
|
124
124
|
const url = URL.createObjectURL(blob);
|
|
125
125
|
return {
|
|
@@ -156,18 +156,18 @@ const $e10a9de47f58e137$var$octave8Frequencies = [
|
|
|
156
156
|
];
|
|
157
157
|
// Labels for each of the above frequencies
|
|
158
158
|
const $e10a9de47f58e137$var$octave8FrequencyLabels = [
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
159
|
+
"C",
|
|
160
|
+
"C#",
|
|
161
|
+
"D",
|
|
162
|
+
"D#",
|
|
163
|
+
"E",
|
|
164
|
+
"F",
|
|
165
|
+
"F#",
|
|
166
|
+
"G",
|
|
167
|
+
"G#",
|
|
168
|
+
"A",
|
|
169
|
+
"A#",
|
|
170
|
+
"B"
|
|
171
171
|
];
|
|
172
172
|
const $e10a9de47f58e137$export$776c63898ae5b636 = [];
|
|
173
173
|
const $e10a9de47f58e137$export$facd167cc27ea9b0 = [];
|
|
@@ -202,7 +202,7 @@ class $5853841ab58516d4$export$2c3136da0bf130f9 {
|
|
|
202
202
|
* @param {number} [minDecibels] default -100
|
|
203
203
|
* @param {number} [maxDecibels] default -30
|
|
204
204
|
* @returns {AudioAnalysisOutputType}
|
|
205
|
-
*/ static getFrequencies(analyser, sampleRate, fftResult, analysisType =
|
|
205
|
+
*/ static getFrequencies(analyser, sampleRate, fftResult, analysisType = "frequency", minDecibels = -100, maxDecibels = -30) {
|
|
206
206
|
if (!fftResult) {
|
|
207
207
|
fftResult = new Float32Array(analyser.frequencyBinCount);
|
|
208
208
|
analyser.getFloatFrequencyData(fftResult);
|
|
@@ -212,8 +212,8 @@ class $5853841ab58516d4$export$2c3136da0bf130f9 {
|
|
|
212
212
|
let outputValues;
|
|
213
213
|
let frequencies;
|
|
214
214
|
let labels;
|
|
215
|
-
if (analysisType ===
|
|
216
|
-
const useFrequencies = analysisType ===
|
|
215
|
+
if (analysisType === "music" || analysisType === "voice") {
|
|
216
|
+
const useFrequencies = analysisType === "voice" ? (0, $e10a9de47f58e137$export$dbc1581ed2cfa183) : (0, $e10a9de47f58e137$export$776c63898ae5b636);
|
|
217
217
|
const aggregateOutput = Array(useFrequencies.length).fill(minDecibels);
|
|
218
218
|
for(let i = 0; i < fftResult.length; i++){
|
|
219
219
|
const frequency = i * frequencyStep;
|
|
@@ -224,8 +224,8 @@ class $5853841ab58516d4$export$2c3136da0bf130f9 {
|
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
outputValues = aggregateOutput;
|
|
227
|
-
frequencies = analysisType ===
|
|
228
|
-
labels = analysisType ===
|
|
227
|
+
frequencies = analysisType === "voice" ? (0, $e10a9de47f58e137$export$dbc1581ed2cfa183) : (0, $e10a9de47f58e137$export$776c63898ae5b636);
|
|
228
|
+
labels = analysisType === "voice" ? (0, $e10a9de47f58e137$export$30a6f2881311088f) : (0, $e10a9de47f58e137$export$facd167cc27ea9b0);
|
|
229
229
|
} else {
|
|
230
230
|
outputValues = Array.from(fftResult);
|
|
231
231
|
frequencies = outputValues.map((_, i)=>frequencyStep * i);
|
|
@@ -311,7 +311,7 @@ class $5853841ab58516d4$export$2c3136da0bf130f9 {
|
|
|
311
311
|
* @param {number} [minDecibels] default -100
|
|
312
312
|
* @param {number} [maxDecibels] default -30
|
|
313
313
|
* @returns {AudioAnalysisOutputType}
|
|
314
|
-
*/ getFrequencies(analysisType =
|
|
314
|
+
*/ getFrequencies(analysisType = "frequency", minDecibels = -100, maxDecibels = -30) {
|
|
315
315
|
let fftResult = null;
|
|
316
316
|
if (this.audioBuffer && this.fftResults.length) {
|
|
317
317
|
const pct = this.audio.currentTime / this.audio.duration;
|
|
@@ -325,7 +325,7 @@ class $5853841ab58516d4$export$2c3136da0bf130f9 {
|
|
|
325
325
|
* user interaction when the AudioAnalysis was instantiated.
|
|
326
326
|
* @returns {Promise<true>}
|
|
327
327
|
*/ async resumeIfSuspended() {
|
|
328
|
-
if (this.context.state ===
|
|
328
|
+
if (this.context.state === "suspended") await this.context.resume();
|
|
329
329
|
return true;
|
|
330
330
|
}
|
|
331
331
|
}
|
|
@@ -425,7 +425,7 @@ registerProcessor('stream_processor', StreamProcessor);
|
|
|
425
425
|
const $d6d3743ef65c7519$var$script = new Blob([
|
|
426
426
|
$d6d3743ef65c7519$export$50b76700e2b15e9
|
|
427
427
|
], {
|
|
428
|
-
type:
|
|
428
|
+
type: "application/javascript"
|
|
429
429
|
});
|
|
430
430
|
const $d6d3743ef65c7519$var$src = URL.createObjectURL($d6d3743ef65c7519$var$script);
|
|
431
431
|
const $d6d3743ef65c7519$export$bfa8c596114d74df = $d6d3743ef65c7519$var$src;
|
|
@@ -780,7 +780,7 @@ registerProcessor('audio_processor', AudioProcessor);
|
|
|
780
780
|
const $2cf6a2d8a6d031bc$var$script = new Blob([
|
|
781
781
|
$2cf6a2d8a6d031bc$var$AudioProcessorWorklet
|
|
782
782
|
], {
|
|
783
|
-
type:
|
|
783
|
+
type: "application/javascript"
|
|
784
784
|
});
|
|
785
785
|
const $2cf6a2d8a6d031bc$var$src = URL.createObjectURL($2cf6a2d8a6d031bc$var$script);
|
|
786
786
|
const $2cf6a2d8a6d031bc$export$1f65f50a8cbff43c = $2cf6a2d8a6d031bc$var$src;
|
|
@@ -844,7 +844,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
844
844
|
blob = new Blob([
|
|
845
845
|
arrayBuffer
|
|
846
846
|
], {
|
|
847
|
-
type:
|
|
847
|
+
type: "audio/wav"
|
|
848
848
|
});
|
|
849
849
|
} else {
|
|
850
850
|
let float32Array;
|
|
@@ -899,9 +899,9 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
899
899
|
* Retrieves the current status of the recording
|
|
900
900
|
* @returns {"ended"|"paused"|"recording"}
|
|
901
901
|
*/ getStatus() {
|
|
902
|
-
if (!this.processor) return
|
|
903
|
-
else if (!this.recording) return
|
|
904
|
-
else return
|
|
902
|
+
if (!this.processor) return "ended";
|
|
903
|
+
else if (!this.recording) return "paused";
|
|
904
|
+
else return "recording";
|
|
905
905
|
}
|
|
906
906
|
/**
|
|
907
907
|
* Sends an event to the AudioWorklet
|
|
@@ -912,7 +912,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
912
912
|
* @returns {Promise<{[key: string]: any}>}
|
|
913
913
|
*/ async _event(name, data = {}, _processor = null) {
|
|
914
914
|
_processor = _processor || this.processor;
|
|
915
|
-
if (!_processor) throw new Error(
|
|
915
|
+
if (!_processor) throw new Error("Can not send events without recording first");
|
|
916
916
|
const message = {
|
|
917
917
|
event: name,
|
|
918
918
|
id: this._lastEventId++,
|
|
@@ -934,7 +934,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
934
934
|
* @returns {true}
|
|
935
935
|
*/ listenForDeviceChange(callback) {
|
|
936
936
|
if (callback === null && this._deviceChangeCallback) {
|
|
937
|
-
navigator.mediaDevices.removeEventListener(
|
|
937
|
+
navigator.mediaDevices.removeEventListener("devicechange", this._deviceChangeCallback);
|
|
938
938
|
this._deviceChangeCallback = null;
|
|
939
939
|
} else if (callback !== null) {
|
|
940
940
|
// Basically a debounce; we only want this called once when devices change
|
|
@@ -942,7 +942,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
942
942
|
// if a few are operating at the same time
|
|
943
943
|
let lastId = 0;
|
|
944
944
|
let lastDevices = [];
|
|
945
|
-
const serializeDevices = (devices)=>devices.map((d)=>d.deviceId).sort().join(
|
|
945
|
+
const serializeDevices = (devices)=>devices.map((d)=>d.deviceId).sort().join(",");
|
|
946
946
|
const cb = async ()=>{
|
|
947
947
|
let id = ++lastId;
|
|
948
948
|
const devices = await this.listDevices();
|
|
@@ -953,7 +953,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
953
953
|
}
|
|
954
954
|
}
|
|
955
955
|
};
|
|
956
|
-
navigator.mediaDevices.addEventListener(
|
|
956
|
+
navigator.mediaDevices.addEventListener("devicechange", cb);
|
|
957
957
|
cb();
|
|
958
958
|
this._deviceChangeCallback = cb;
|
|
959
959
|
}
|
|
@@ -971,9 +971,9 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
971
971
|
* @returns {Promise<true>}
|
|
972
972
|
*/ async requestPermission() {
|
|
973
973
|
const permissionStatus = await navigator.permissions.query({
|
|
974
|
-
name:
|
|
974
|
+
name: "microphone"
|
|
975
975
|
});
|
|
976
|
-
if (permissionStatus.state ===
|
|
976
|
+
if (permissionStatus.state === "denied") {
|
|
977
977
|
if (this._deviceErrorCallback) this._deviceErrorCallback({
|
|
978
978
|
devices: [
|
|
979
979
|
"mic"
|
|
@@ -981,7 +981,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
981
981
|
type: "unknown",
|
|
982
982
|
error: new Error("Microphone access denied")
|
|
983
983
|
});
|
|
984
|
-
} else if (permissionStatus.state ===
|
|
984
|
+
} else if (permissionStatus.state === "prompt") try {
|
|
985
985
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
986
986
|
audio: true
|
|
987
987
|
});
|
|
@@ -1003,10 +1003,10 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1003
1003
|
* List all eligible devices for recording, will request permission to use microphone
|
|
1004
1004
|
* @returns {Promise<Array<MediaDeviceInfo & {default: boolean}>>}
|
|
1005
1005
|
*/ async listDevices() {
|
|
1006
|
-
if (!navigator.mediaDevices || !(
|
|
1006
|
+
if (!navigator.mediaDevices || !("enumerateDevices" in navigator.mediaDevices)) throw new Error("Could not request user devices");
|
|
1007
1007
|
await this.requestPermission();
|
|
1008
1008
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
1009
|
-
const audioDevices = devices.filter((device)=>device.kind ===
|
|
1009
|
+
const audioDevices = devices.filter((device)=>device.kind === "audioinput");
|
|
1010
1010
|
return audioDevices;
|
|
1011
1011
|
// const defaultDeviceIndex = audioDevices.findIndex(
|
|
1012
1012
|
// (device) => device.deviceId === 'default'
|
|
@@ -1032,7 +1032,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1032
1032
|
* @returns {Promise<true>}
|
|
1033
1033
|
*/ async begin(deviceId) {
|
|
1034
1034
|
if (this.processor) throw new Error(`Already connected: please call .end() to start a new session`);
|
|
1035
|
-
if (!navigator.mediaDevices || !(
|
|
1035
|
+
if (!navigator.mediaDevices || !("getUserMedia" in navigator.mediaDevices)) {
|
|
1036
1036
|
if (this._deviceErrorCallback) this._deviceErrorCallback({
|
|
1037
1037
|
devices: [
|
|
1038
1038
|
"mic",
|
|
@@ -1040,7 +1040,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1040
1040
|
],
|
|
1041
1041
|
type: "undefined-mediadevices"
|
|
1042
1042
|
});
|
|
1043
|
-
throw new Error(
|
|
1043
|
+
throw new Error("Could not request user media");
|
|
1044
1044
|
}
|
|
1045
1045
|
deviceId = deviceId ?? this.deviceSelection?.deviceId;
|
|
1046
1046
|
try {
|
|
@@ -1061,13 +1061,13 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1061
1061
|
type: "unknown",
|
|
1062
1062
|
error: err
|
|
1063
1063
|
});
|
|
1064
|
-
throw new Error(
|
|
1064
|
+
throw new Error("Could not start media stream");
|
|
1065
1065
|
}
|
|
1066
1066
|
this.listDevices().then((devices)=>{
|
|
1067
1067
|
deviceId = this.stream.getAudioTracks()[0].getSettings().deviceId;
|
|
1068
|
-
console.log(
|
|
1068
|
+
console.log("find current device", devices, deviceId, this.stream.getAudioTracks()[0].getSettings());
|
|
1069
1069
|
this.deviceSelection = devices.find((d)=>d.deviceId === deviceId);
|
|
1070
|
-
console.log(
|
|
1070
|
+
console.log("current device", this.deviceSelection);
|
|
1071
1071
|
});
|
|
1072
1072
|
const context = new AudioContext({
|
|
1073
1073
|
sampleRate: this.sampleRate
|
|
@@ -1080,11 +1080,11 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1080
1080
|
console.error(e);
|
|
1081
1081
|
throw new Error(`Could not add audioWorklet module: ${this.scriptSrc}`);
|
|
1082
1082
|
}
|
|
1083
|
-
const processor = new AudioWorkletNode(context,
|
|
1083
|
+
const processor = new AudioWorkletNode(context, "audio_processor");
|
|
1084
1084
|
processor.port.onmessage = (e)=>{
|
|
1085
1085
|
const { event: event, id: id, data: data } = e.data;
|
|
1086
|
-
if (event ===
|
|
1087
|
-
else if (event ===
|
|
1086
|
+
if (event === "receipt") this.eventReceipts[id] = data;
|
|
1087
|
+
else if (event === "chunk") {
|
|
1088
1088
|
if (this._chunkProcessorSize) {
|
|
1089
1089
|
const buffer = this._chunkProcessorBuffer;
|
|
1090
1090
|
this._chunkProcessorBuffer = {
|
|
@@ -1115,7 +1115,7 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1115
1115
|
this.node = node;
|
|
1116
1116
|
this.analyser = analyser;
|
|
1117
1117
|
this.processor = processor;
|
|
1118
|
-
console.log(
|
|
1118
|
+
console.log("begin completed");
|
|
1119
1119
|
return true;
|
|
1120
1120
|
}
|
|
1121
1121
|
/**
|
|
@@ -1124,8 +1124,8 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1124
1124
|
* @param {number} [minDecibels] default -100
|
|
1125
1125
|
* @param {number} [maxDecibels] default -30
|
|
1126
1126
|
* @returns {import('./analysis/audio_analysis.js').AudioAnalysisOutputType}
|
|
1127
|
-
*/ getFrequencies(analysisType =
|
|
1128
|
-
if (!this.processor) throw new Error(
|
|
1127
|
+
*/ getFrequencies(analysisType = "frequency", minDecibels = -100, maxDecibels = -30) {
|
|
1128
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1129
1129
|
return (0, $5853841ab58516d4$export$2c3136da0bf130f9).getFrequencies(this.analyser, this.sampleRate, null, analysisType, minDecibels, maxDecibels);
|
|
1130
1130
|
}
|
|
1131
1131
|
/**
|
|
@@ -1133,11 +1133,11 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1133
1133
|
* Keeps microphone stream open but halts storage of audio
|
|
1134
1134
|
* @returns {Promise<true>}
|
|
1135
1135
|
*/ async pause() {
|
|
1136
|
-
if (!this.processor) throw new Error(
|
|
1137
|
-
else if (!this.recording) throw new Error(
|
|
1136
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1137
|
+
else if (!this.recording) throw new Error("Already paused: please call .record() first");
|
|
1138
1138
|
if (this._chunkProcessorBuffer.raw.byteLength) this._chunkProcessor(this._chunkProcessorBuffer);
|
|
1139
|
-
this.log(
|
|
1140
|
-
await this._event(
|
|
1139
|
+
this.log("Pausing ...");
|
|
1140
|
+
await this._event("stop");
|
|
1141
1141
|
this.recording = false;
|
|
1142
1142
|
return true;
|
|
1143
1143
|
}
|
|
@@ -1147,17 +1147,17 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1147
1147
|
* @param {number} [chunkSize] chunkProcessor will not be triggered until this size threshold met in mono audio
|
|
1148
1148
|
* @returns {Promise<true>}
|
|
1149
1149
|
*/ async record(chunkProcessor = ()=>{}, chunkSize = 8192) {
|
|
1150
|
-
if (!this.processor) throw new Error(
|
|
1151
|
-
else if (this.recording) throw new Error(
|
|
1152
|
-
else if (typeof chunkProcessor !==
|
|
1150
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1151
|
+
else if (this.recording) throw new Error("Already recording: please call .pause() first");
|
|
1152
|
+
else if (typeof chunkProcessor !== "function") throw new Error(`chunkProcessor must be a function`);
|
|
1153
1153
|
this._chunkProcessor = chunkProcessor;
|
|
1154
1154
|
this._chunkProcessorSize = chunkSize;
|
|
1155
1155
|
this._chunkProcessorBuffer = {
|
|
1156
1156
|
raw: new ArrayBuffer(0),
|
|
1157
1157
|
mono: new ArrayBuffer(0)
|
|
1158
1158
|
};
|
|
1159
|
-
this.log(
|
|
1160
|
-
await this._event(
|
|
1159
|
+
this.log("Recording ...");
|
|
1160
|
+
await this._event("start");
|
|
1161
1161
|
this.recording = true;
|
|
1162
1162
|
return true;
|
|
1163
1163
|
}
|
|
@@ -1165,17 +1165,17 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1165
1165
|
* Clears the audio buffer, empties stored recording
|
|
1166
1166
|
* @returns {Promise<true>}
|
|
1167
1167
|
*/ async clear() {
|
|
1168
|
-
if (!this.processor) throw new Error(
|
|
1169
|
-
await this._event(
|
|
1168
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1169
|
+
await this._event("clear");
|
|
1170
1170
|
return true;
|
|
1171
1171
|
}
|
|
1172
1172
|
/**
|
|
1173
1173
|
* Reads the current audio stream data
|
|
1174
1174
|
* @returns {Promise<{meanValues: Float32Array, channels: Array<Float32Array>}>}
|
|
1175
1175
|
*/ async read() {
|
|
1176
|
-
if (!this.processor) throw new Error(
|
|
1177
|
-
this.log(
|
|
1178
|
-
const result = await this._event(
|
|
1176
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1177
|
+
this.log("Reading ...");
|
|
1178
|
+
const result = await this._event("read");
|
|
1179
1179
|
return result;
|
|
1180
1180
|
}
|
|
1181
1181
|
/**
|
|
@@ -1183,10 +1183,10 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1183
1183
|
* @param {boolean} [force] Force saving while still recording
|
|
1184
1184
|
* @returns {Promise<import('./wav_packer.js').WavPackerAudioType>}
|
|
1185
1185
|
*/ async save(force = false) {
|
|
1186
|
-
if (!this.processor) throw new Error(
|
|
1187
|
-
if (!force && this.recording) throw new Error(
|
|
1188
|
-
this.log(
|
|
1189
|
-
const exportData = await this._event(
|
|
1186
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1187
|
+
if (!force && this.recording) throw new Error("Currently recording: please call .pause() first, or call .save(true) to force");
|
|
1188
|
+
this.log("Exporting ...");
|
|
1189
|
+
const exportData = await this._event("export");
|
|
1190
1190
|
const packer = new (0, $a61750b8fbee4dae$export$13afda237b1c9846)();
|
|
1191
1191
|
const result = packer.pack(this.sampleRate, exportData.audio);
|
|
1192
1192
|
return result;
|
|
@@ -1195,15 +1195,15 @@ class $0d471c38435bdab6$export$439b217ca659a877 {
|
|
|
1195
1195
|
* Ends the current recording session and saves the result
|
|
1196
1196
|
* @returns {Promise<import('./wav_packer.js').WavPackerAudioType>}
|
|
1197
1197
|
*/ async end() {
|
|
1198
|
-
if (!this.processor) throw new Error(
|
|
1198
|
+
if (!this.processor) throw new Error("Session ended: please call .begin() first");
|
|
1199
1199
|
const _processor = this.processor;
|
|
1200
|
-
this.log(
|
|
1201
|
-
await this._event(
|
|
1200
|
+
this.log("Stopping ...");
|
|
1201
|
+
await this._event("stop");
|
|
1202
1202
|
this.recording = false;
|
|
1203
1203
|
const tracks = this.stream.getTracks();
|
|
1204
1204
|
tracks.forEach((track)=>track.stop());
|
|
1205
|
-
this.log(
|
|
1206
|
-
const exportData = await this._event(
|
|
1205
|
+
this.log("Exporting ...");
|
|
1206
|
+
const exportData = await this._event("export", {}, _processor);
|
|
1207
1207
|
this.processor.disconnect();
|
|
1208
1208
|
this.source.disconnect();
|
|
1209
1209
|
this.node.disconnect();
|
|
@@ -1234,6 +1234,29 @@ globalThis.WavRecorder = $0d471c38435bdab6$export$439b217ca659a877;
|
|
|
1234
1234
|
|
|
1235
1235
|
|
|
1236
1236
|
|
|
1237
|
+
/**
|
|
1238
|
+
* Resamples audio data from one sample rate to another using linear interpolation
|
|
1239
|
+
* @param {ArrayBuffer} inputBuffer - Input audio data as Int16 PCM
|
|
1240
|
+
* @param {number} inputSampleRate - Sample rate of input audio
|
|
1241
|
+
* @param {number} outputSampleRate - Desired output sample rate
|
|
1242
|
+
* @returns {ArrayBuffer} - Resampled audio data as Int16 PCM
|
|
1243
|
+
*/ function $7cef7a69bdf8f84d$var$resampleAudioBuffer(inputBuffer, inputSampleRate, outputSampleRate) {
|
|
1244
|
+
if (inputSampleRate === outputSampleRate) return inputBuffer;
|
|
1245
|
+
const inputView = new Int16Array(inputBuffer);
|
|
1246
|
+
const ratio = inputSampleRate / outputSampleRate;
|
|
1247
|
+
const outputLength = Math.round(inputView.length / ratio);
|
|
1248
|
+
const outputBuffer = new ArrayBuffer(outputLength * 2);
|
|
1249
|
+
const outputView = new Int16Array(outputBuffer);
|
|
1250
|
+
for(let i = 0; i < outputLength; i++){
|
|
1251
|
+
const srcIndex = i * ratio;
|
|
1252
|
+
const srcIndexFloor = Math.floor(srcIndex);
|
|
1253
|
+
const srcIndexCeil = Math.min(srcIndexFloor + 1, inputView.length - 1);
|
|
1254
|
+
const t = srcIndex - srcIndexFloor;
|
|
1255
|
+
// Linear interpolation
|
|
1256
|
+
outputView[i] = Math.round(inputView[srcIndexFloor] * (1 - t) + inputView[srcIndexCeil] * t);
|
|
1257
|
+
}
|
|
1258
|
+
return outputBuffer;
|
|
1259
|
+
}
|
|
1237
1260
|
class $7cef7a69bdf8f84d$export$2934cf2d25c67a48 {
|
|
1238
1261
|
/**
|
|
1239
1262
|
* Create a new MediaStreamRecorder instance
|
|
@@ -1322,9 +1345,21 @@ class $7cef7a69bdf8f84d$export$2934cf2d25c67a48 {
|
|
|
1322
1345
|
this.stream = new MediaStream([
|
|
1323
1346
|
audioTrack
|
|
1324
1347
|
]);
|
|
1325
|
-
|
|
1348
|
+
// Firefox workaround: Firefox doesn't support connecting AudioNodes from
|
|
1349
|
+
// AudioContexts with different sample rates. So we create the AudioContext
|
|
1350
|
+
// at the native sample rate and resample manually.
|
|
1351
|
+
// This workaround may be temporary; Firefox is in the process of adding
|
|
1352
|
+
// support for this.
|
|
1353
|
+
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1674892
|
|
1354
|
+
const isFirefox = navigator.userAgent.toLowerCase().includes("firefox");
|
|
1355
|
+
let context;
|
|
1356
|
+
if (isFirefox) // Firefox: Use native sample rate and resample manually
|
|
1357
|
+
context = new AudioContext();
|
|
1358
|
+
else // Other browsers: Let the AudioContext handle resampling
|
|
1359
|
+
context = new AudioContext({
|
|
1326
1360
|
sampleRate: this.sampleRate
|
|
1327
1361
|
});
|
|
1362
|
+
const contextSampleRate = context.sampleRate;
|
|
1328
1363
|
const source = context.createMediaStreamSource(this.stream);
|
|
1329
1364
|
// Load and execute the module script.
|
|
1330
1365
|
try {
|
|
@@ -1338,11 +1373,17 @@ class $7cef7a69bdf8f84d$export$2934cf2d25c67a48 {
|
|
|
1338
1373
|
const { event: event, id: id, data: data } = e.data;
|
|
1339
1374
|
if (event === "receipt") this.eventReceipts[id] = data;
|
|
1340
1375
|
else if (event === "chunk") {
|
|
1376
|
+
// Resample chunk data from native sample rate to target sample rate
|
|
1377
|
+
// (no-op if rates match, i.e. for non-Firefox browsers)
|
|
1378
|
+
const resampledData = {
|
|
1379
|
+
raw: $7cef7a69bdf8f84d$var$resampleAudioBuffer(data.raw, contextSampleRate, this.sampleRate),
|
|
1380
|
+
mono: $7cef7a69bdf8f84d$var$resampleAudioBuffer(data.mono, contextSampleRate, this.sampleRate)
|
|
1381
|
+
};
|
|
1341
1382
|
if (this._chunkProcessorSize) {
|
|
1342
1383
|
const buffer = this._chunkProcessorBuffer;
|
|
1343
1384
|
this._chunkProcessorBuffer = {
|
|
1344
|
-
raw: (0, $a61750b8fbee4dae$export$13afda237b1c9846).mergeBuffers(buffer.raw,
|
|
1345
|
-
mono: (0, $a61750b8fbee4dae$export$13afda237b1c9846).mergeBuffers(buffer.mono,
|
|
1385
|
+
raw: (0, $a61750b8fbee4dae$export$13afda237b1c9846).mergeBuffers(buffer.raw, resampledData.raw),
|
|
1386
|
+
mono: (0, $a61750b8fbee4dae$export$13afda237b1c9846).mergeBuffers(buffer.mono, resampledData.mono)
|
|
1346
1387
|
};
|
|
1347
1388
|
if (this._chunkProcessorBuffer.mono.byteLength >= this._chunkProcessorSize) {
|
|
1348
1389
|
this._chunkProcessor(this._chunkProcessorBuffer);
|
|
@@ -1351,7 +1392,7 @@ class $7cef7a69bdf8f84d$export$2934cf2d25c67a48 {
|
|
|
1351
1392
|
mono: new ArrayBuffer(0)
|
|
1352
1393
|
};
|
|
1353
1394
|
}
|
|
1354
|
-
} else this._chunkProcessor(
|
|
1395
|
+
} else this._chunkProcessor(resampledData);
|
|
1355
1396
|
}
|
|
1356
1397
|
};
|
|
1357
1398
|
const node = source.connect(processor);
|
|
@@ -1482,7 +1523,7 @@ globalThis.WavRecorder = WavRecorder;
|
|
|
1482
1523
|
|
|
1483
1524
|
|
|
1484
1525
|
var $c2e964a535c1120e$exports = {};
|
|
1485
|
-
$c2e964a535c1120e$exports = JSON.parse("{\"name\":\"@pipecat-ai/daily-transport\",\"version\":\"1.
|
|
1526
|
+
$c2e964a535c1120e$exports = JSON.parse("{\"name\":\"@pipecat-ai/daily-transport\",\"version\":\"1.6.0\",\"license\":\"BSD-2-Clause\",\"main\":\"dist/index.js\",\"module\":\"dist/index.module.js\",\"types\":\"dist/index.d.ts\",\"source\":\"src/index.ts\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/pipecat-ai/pipecat-client-web-transports.git\"},\"files\":[\"dist\",\"package.json\",\"README.md\"],\"scripts\":{\"build\":\"parcel build --no-cache\",\"dev\":\"parcel watch\",\"lint\":\"eslint . --ext ts --report-unused-disable-directives --max-warnings 0\"},\"devDependencies\":{\"@pipecat-ai/client-js\":\"^1.6.0\",\"eslint\":\"9.39.1\",\"eslint-config-prettier\":\"^9.1.0\",\"eslint-plugin-simple-import-sort\":\"^12.1.1\"},\"peerDependencies\":{\"@pipecat-ai/client-js\":\"~1.6.0\"},\"dependencies\":{\"@daily-co/daily-js\":\"^0.86.0\"},\"description\":\"Pipecat Daily Transport Package\",\"author\":\"Daily.co\",\"bugs\":{\"url\":\"https://github.com/pipecat-ai/pipecat-client-web-transports/issues\"},\"homepage\":\"https://github.com/pipecat-ai/pipecat-client-web-transports/blob/main/transports/daily-webrtc/README.md\"}");
|
|
1486
1527
|
|
|
1487
1528
|
|
|
1488
1529
|
var $28bdeb35415d61e8$export$ef180de88fd317cc;
|