@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 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
- 'RIFF',
102
+ "RIFF",
103
103
  this._packData(1, 52),
104
- 'WAVE',
104
+ "WAVE",
105
105
  // chunk 1
106
- 'fmt ',
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
- 'data',
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: 'audio/mpeg'
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
- 'C',
158
- 'C#',
159
- 'D',
160
- 'D#',
161
- 'E',
162
- 'F',
163
- 'F#',
164
- 'G',
165
- 'G#',
166
- 'A',
167
- 'A#',
168
- 'B'
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 = 'frequency', minDecibels = -100, maxDecibels = -30) {
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 === 'music' || analysisType === 'voice') {
214
- const useFrequencies = analysisType === 'voice' ? (0, $e10a9de47f58e137$export$dbc1581ed2cfa183) : (0, $e10a9de47f58e137$export$776c63898ae5b636);
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 === 'voice' ? (0, $e10a9de47f58e137$export$dbc1581ed2cfa183) : (0, $e10a9de47f58e137$export$776c63898ae5b636);
226
- labels = analysisType === 'voice' ? (0, $e10a9de47f58e137$export$30a6f2881311088f) : (0, $e10a9de47f58e137$export$facd167cc27ea9b0);
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 = 'frequency', minDecibels = -100, maxDecibels = -30) {
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 === 'suspended') await this.context.resume();
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: 'application/javascript'
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: 'application/javascript'
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: 'audio/wav'
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 'ended';
901
- else if (!this.recording) return 'paused';
902
- else return 'recording';
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('Can not send events without recording first');
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('devicechange', this._deviceChangeCallback);
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('devicechange', cb);
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: 'microphone'
972
+ name: "microphone"
973
973
  });
974
- if (permissionStatus.state === 'denied') {
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 === 'prompt') try {
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 || !('enumerateDevices' in navigator.mediaDevices)) throw new Error('Could not request user devices');
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 === 'audioinput');
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 || !('getUserMedia' in 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('Could not request user media');
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('Could not start media stream');
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('find current device', devices, deviceId, this.stream.getAudioTracks()[0].getSettings());
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('current device', this.deviceSelection);
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, 'audio_processor');
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 === 'receipt') this.eventReceipts[id] = data;
1085
- else if (event === 'chunk') {
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('begin completed');
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 = 'frequency', minDecibels = -100, maxDecibels = -30) {
1126
- if (!this.processor) throw new Error('Session ended: please call .begin() first');
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('Session ended: please call .begin() first');
1135
- else if (!this.recording) throw new Error('Already paused: please call .record() first');
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('Pausing ...');
1138
- await this._event('stop');
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('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`);
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('Recording ...');
1158
- await this._event('start');
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('Session ended: please call .begin() first');
1167
- await this._event('clear');
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('Session ended: please call .begin() first');
1175
- this.log('Reading ...');
1176
- const result = await this._event('read');
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('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');
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('Session ended: please call .begin() first');
1196
+ if (!this.processor) throw new Error("Session ended: please call .begin() first");
1197
1197
  const _processor = this.processor;
1198
- this.log('Stopping ...');
1199
- await this._event('stop');
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('Exporting ...');
1204
- const exportData = await this._event('export', {}, _processor);
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
- const context = new AudioContext({
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, data.raw),
1343
- mono: (0, $a61750b8fbee4dae$export$13afda237b1c9846).mergeBuffers(buffer.mono, data.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(data);
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(_c) {
2487
- switch(_c.label){
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
- 2
2574
+ 7
2496
2575
  ];
2497
2576
  return [
2498
2577
  4 /*yield*/ ,
2499
2578
  this.getAllSpeakers()
2500
2579
  ];
2501
- case 1:
2502
- speakers = _c.sent();
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 = (_a = defaultSpeakerCp === null || defaultSpeakerCp === void 0 ? void 0 : defaultSpeakerCp.deviceId) !== null && _a !== void 0 ? _a : speakerId;
2517
- _c.label = 2;
2518
- case 2:
2519
- (_b = this._wavStreamPlayer) === null || _b === void 0 || _b.updateSpeaker(sID).then(function() {
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, e_1, e_2;
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
- e_1 = _f.sent();
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
- e_2 = _f.sent();
2896
+ e_3 = _f.sent();
2818
2897
  return [
2819
2898
  3 /*break*/ ,
2820
2899
  11