{"version":3,"sources":["jsdelivr-header.js","/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/lib/wavtools/lib/wav_packer.js","/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/lib/wavtools/lib/analysis/constants.js","/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/lib/wavtools/lib/analysis/audio_analysis.js","/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/lib/wavtools/lib/worklets/stream_processor.js","/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/lib/wavtools/lib/wav_stream_player.js","/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/lib/wavtools/lib/worklets/audio_processor.js","/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/lib/wavtools/lib/wav_recorder.js","/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/lib/wavtools/lib/mediastream_recorder.js","/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/lib/media-mgmt/mediaManager.ts","/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/lib/media-mgmt/dailyMediaManager.ts","/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/transports/small-webrtc-transport/src/smallWebRTCTransport.ts"],"names":[],"mappings":"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","file":"/npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/@pipecat-ai/small-webrtc-transport@1.9.0/dist/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/**\n * Raw wav audio file contents\n * @typedef {Object} WavPackerAudioType\n * @property {Blob} blob\n * @property {string} url\n * @property {number} channelCount\n * @property {number} sampleRate\n * @property {number} duration\n */\n\n/**\n * Utility class for assembling PCM16 \"audio/wav\" data\n * @class\n */\nexport class WavPacker {\n  /**\n   * Converts Float32Array of amplitude data to ArrayBuffer in Int16Array format\n   * @param {Float32Array} float32Array\n   * @returns {ArrayBuffer}\n   */\n  static floatTo16BitPCM(float32Array) {\n    const buffer = new ArrayBuffer(float32Array.length * 2);\n    const view = new DataView(buffer);\n    let offset = 0;\n    for (let i = 0; i < float32Array.length; i++, offset += 2) {\n      let s = Math.max(-1, Math.min(1, float32Array[i]));\n      view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n    }\n    return buffer;\n  }\n\n  /**\n   * Concatenates two ArrayBuffers\n   * @param {ArrayBuffer} leftBuffer\n   * @param {ArrayBuffer} rightBuffer\n   * @returns {ArrayBuffer}\n   */\n  static mergeBuffers(leftBuffer, rightBuffer) {\n    const tmpArray = new Uint8Array(\n      leftBuffer.byteLength + rightBuffer.byteLength\n    );\n    tmpArray.set(new Uint8Array(leftBuffer), 0);\n    tmpArray.set(new Uint8Array(rightBuffer), leftBuffer.byteLength);\n    return tmpArray.buffer;\n  }\n\n  /**\n   * Packs data into an Int16 format\n   * @private\n   * @param {number} size 0 = 1x Int16, 1 = 2x Int16\n   * @param {number} arg value to pack\n   * @returns\n   */\n  _packData(size, arg) {\n    return [\n      new Uint8Array([arg, arg >> 8]),\n      new Uint8Array([arg, arg >> 8, arg >> 16, arg >> 24]),\n    ][size];\n  }\n\n  /**\n   * Packs audio into \"audio/wav\" Blob\n   * @param {number} sampleRate\n   * @param {{bitsPerSample: number, channels: Array<Float32Array>, data: Int16Array}} audio\n   * @returns {WavPackerAudioType}\n   */\n  pack(sampleRate, audio) {\n    if (!audio?.bitsPerSample) {\n      throw new Error(`Missing \"bitsPerSample\"`);\n    } else if (!audio?.channels) {\n      throw new Error(`Missing \"channels\"`);\n    } else if (!audio?.data) {\n      throw new Error(`Missing \"data\"`);\n    }\n    const { bitsPerSample, channels, data } = audio;\n    const output = [\n      // Header\n      \"RIFF\",\n      this._packData(\n        1,\n        4 + (8 + 24) /* chunk 1 length */ + (8 + 8) /* chunk 2 length */\n      ), // Length\n      \"WAVE\",\n      // chunk 1\n      \"fmt \", // Sub-chunk identifier\n      this._packData(1, 16), // Chunk length\n      this._packData(0, 1), // Audio format (1 is linear quantization)\n      this._packData(0, channels.length),\n      this._packData(1, sampleRate),\n      this._packData(1, (sampleRate * channels.length * bitsPerSample) / 8), // Byte rate\n      this._packData(0, (channels.length * bitsPerSample) / 8),\n      this._packData(0, bitsPerSample),\n      // chunk 2\n      \"data\", // Sub-chunk identifier\n      this._packData(\n        1,\n        (channels[0].length * channels.length * bitsPerSample) / 8\n      ), // Chunk length\n      data,\n    ];\n    const blob = new Blob(output, { type: \"audio/mpeg\" });\n    const url = URL.createObjectURL(blob);\n    return {\n      blob,\n      url,\n      channelCount: channels.length,\n      sampleRate,\n      duration: data.byteLength / (channels.length * sampleRate * 2),\n    };\n  }\n}\n\nglobalThis.WavPacker = WavPacker;\n","/**\n * Constants for help with visualization\n * Helps map frequency ranges from Fast Fourier Transform\n * to human-interpretable ranges, notably music ranges and\n * human vocal ranges.\n */\n\n// Eighth octave frequencies\nconst octave8Frequencies = [\n  4186.01, 4434.92, 4698.63, 4978.03, 5274.04, 5587.65, 5919.91, 6271.93,\n  6644.88, 7040.0, 7458.62, 7902.13,\n];\n\n// Labels for each of the above frequencies\nconst octave8FrequencyLabels = [\n  \"C\",\n  \"C#\",\n  \"D\",\n  \"D#\",\n  \"E\",\n  \"F\",\n  \"F#\",\n  \"G\",\n  \"G#\",\n  \"A\",\n  \"A#\",\n  \"B\",\n];\n\n/**\n * All note frequencies from 1st to 8th octave\n * in format \"A#8\" (A#, 8th octave)\n */\nexport const noteFrequencies = [];\nexport const noteFrequencyLabels = [];\nfor (let i = 1; i <= 8; i++) {\n  for (let f = 0; f < octave8Frequencies.length; f++) {\n    const freq = octave8Frequencies[f];\n    noteFrequencies.push(freq / Math.pow(2, 8 - i));\n    noteFrequencyLabels.push(octave8FrequencyLabels[f] + i);\n  }\n}\n\n/**\n * Subset of the note frequencies between 32 and 2000 Hz\n * 6 octave range: C1 to B6\n */\nconst voiceFrequencyRange = [32.0, 2000.0];\nexport const voiceFrequencies = noteFrequencies.filter((_, i) => {\n  return (\n    noteFrequencies[i] > voiceFrequencyRange[0] &&\n    noteFrequencies[i] < voiceFrequencyRange[1]\n  );\n});\nexport const voiceFrequencyLabels = noteFrequencyLabels.filter((_, i) => {\n  return (\n    noteFrequencies[i] > voiceFrequencyRange[0] &&\n    noteFrequencies[i] < voiceFrequencyRange[1]\n  );\n});\n","import {\n  noteFrequencies,\n  noteFrequencyLabels,\n  voiceFrequencies,\n  voiceFrequencyLabels,\n} from \"./constants.js\";\n\n/**\n * Output of AudioAnalysis for the frequency domain of the audio\n * @typedef {Object} AudioAnalysisOutputType\n * @property {Float32Array} values Amplitude of this frequency between {0, 1} inclusive\n * @property {number[]} frequencies Raw frequency bucket values\n * @property {string[]} labels Labels for the frequency bucket values\n */\n\n/**\n * Analyzes audio for visual output\n * @class\n */\nexport class AudioAnalysis {\n  /**\n   * Retrieves frequency domain data from an AnalyserNode adjusted to a decibel range\n   * returns human-readable formatting and labels\n   * @param {AnalyserNode} analyser\n   * @param {number} sampleRate\n   * @param {Float32Array} [fftResult]\n   * @param {\"frequency\"|\"music\"|\"voice\"} [analysisType]\n   * @param {number} [minDecibels] default -100\n   * @param {number} [maxDecibels] default -30\n   * @returns {AudioAnalysisOutputType}\n   */\n  static getFrequencies(\n    analyser,\n    sampleRate,\n    fftResult,\n    analysisType = \"frequency\",\n    minDecibels = -100,\n    maxDecibels = -30\n  ) {\n    if (!fftResult) {\n      fftResult = new Float32Array(analyser.frequencyBinCount);\n      analyser.getFloatFrequencyData(fftResult);\n    }\n    const nyquistFrequency = sampleRate / 2;\n    const frequencyStep = (1 / fftResult.length) * nyquistFrequency;\n    let outputValues;\n    let frequencies;\n    let labels;\n    if (analysisType === \"music\" || analysisType === \"voice\") {\n      const useFrequencies =\n        analysisType === \"voice\" ? voiceFrequencies : noteFrequencies;\n      const aggregateOutput = Array(useFrequencies.length).fill(minDecibels);\n      for (let i = 0; i < fftResult.length; i++) {\n        const frequency = i * frequencyStep;\n        const amplitude = fftResult[i];\n        for (let n = useFrequencies.length - 1; n >= 0; n--) {\n          if (frequency > useFrequencies[n]) {\n            aggregateOutput[n] = Math.max(aggregateOutput[n], amplitude);\n            break;\n          }\n        }\n      }\n      outputValues = aggregateOutput;\n      frequencies =\n        analysisType === \"voice\" ? voiceFrequencies : noteFrequencies;\n      labels =\n        analysisType === \"voice\" ? voiceFrequencyLabels : noteFrequencyLabels;\n    } else {\n      outputValues = Array.from(fftResult);\n      frequencies = outputValues.map((_, i) => frequencyStep * i);\n      labels = frequencies.map((f) => `${f.toFixed(2)} Hz`);\n    }\n    // We normalize to {0, 1}\n    const normalizedOutput = outputValues.map((v) => {\n      return Math.max(\n        0,\n        Math.min((v - minDecibels) / (maxDecibels - minDecibels), 1)\n      );\n    });\n    const values = new Float32Array(normalizedOutput);\n    return {\n      values,\n      frequencies,\n      labels,\n    };\n  }\n\n  /**\n   * Creates a new AudioAnalysis instance for an HTMLAudioElement\n   * @param {HTMLAudioElement} audioElement\n   * @param {AudioBuffer|null} [audioBuffer] If provided, will cache all frequency domain data from the buffer\n   * @returns {AudioAnalysis}\n   */\n  constructor(audioElement, audioBuffer = null) {\n    this.fftResults = [];\n    if (audioBuffer) {\n      /**\n       * Modified from\n       * https://stackoverflow.com/questions/75063715/using-the-web-audio-api-to-analyze-a-song-without-playing\n       *\n       * We do this to populate FFT values for the audio if provided an `audioBuffer`\n       * The reason to do this is that Safari fails when using `createMediaElementSource`\n       * This has a non-zero RAM cost so we only opt-in to run it on Safari, Chrome is better\n       */\n      const { length, sampleRate } = audioBuffer;\n      const offlineAudioContext = new OfflineAudioContext({\n        length,\n        sampleRate,\n      });\n      const source = offlineAudioContext.createBufferSource();\n      source.buffer = audioBuffer;\n      const analyser = offlineAudioContext.createAnalyser();\n      analyser.fftSize = 8192;\n      analyser.smoothingTimeConstant = 0.1;\n      source.connect(analyser);\n      // limit is :: 128 / sampleRate;\n      // but we just want 60fps - cuts ~1s from 6MB to 1MB of RAM\n      const renderQuantumInSeconds = 1 / 60;\n      const durationInSeconds = length / sampleRate;\n      const analyze = (index) => {\n        const suspendTime = renderQuantumInSeconds * index;\n        if (suspendTime < durationInSeconds) {\n          offlineAudioContext.suspend(suspendTime).then(() => {\n            const fftResult = new Float32Array(analyser.frequencyBinCount);\n            analyser.getFloatFrequencyData(fftResult);\n            this.fftResults.push(fftResult);\n            analyze(index + 1);\n          });\n        }\n        if (index === 1) {\n          offlineAudioContext.startRendering();\n        } else {\n          offlineAudioContext.resume();\n        }\n      };\n      source.start(0);\n      analyze(1);\n      this.audio = audioElement;\n      this.context = offlineAudioContext;\n      this.analyser = analyser;\n      this.sampleRate = sampleRate;\n      this.audioBuffer = audioBuffer;\n    } else {\n      const audioContext = new AudioContext();\n      const track = audioContext.createMediaElementSource(audioElement);\n      const analyser = audioContext.createAnalyser();\n      analyser.fftSize = 8192;\n      analyser.smoothingTimeConstant = 0.1;\n      track.connect(analyser);\n      analyser.connect(audioContext.destination);\n      this.audio = audioElement;\n      this.context = audioContext;\n      this.analyser = analyser;\n      this.sampleRate = this.context.sampleRate;\n      this.audioBuffer = null;\n    }\n  }\n\n  /**\n   * Gets the current frequency domain data from the playing audio track\n   * @param {\"frequency\"|\"music\"|\"voice\"} [analysisType]\n   * @param {number} [minDecibels] default -100\n   * @param {number} [maxDecibels] default -30\n   * @returns {AudioAnalysisOutputType}\n   */\n  getFrequencies(\n    analysisType = \"frequency\",\n    minDecibels = -100,\n    maxDecibels = -30\n  ) {\n    let fftResult = null;\n    if (this.audioBuffer && this.fftResults.length) {\n      const pct = this.audio.currentTime / this.audio.duration;\n      const index = Math.min(\n        (pct * this.fftResults.length) | 0,\n        this.fftResults.length - 1\n      );\n      fftResult = this.fftResults[index];\n    }\n    return AudioAnalysis.getFrequencies(\n      this.analyser,\n      this.sampleRate,\n      fftResult,\n      analysisType,\n      minDecibels,\n      maxDecibels\n    );\n  }\n\n  /**\n   * Resume the internal AudioContext if it was suspended due to the lack of\n   * user interaction when the AudioAnalysis was instantiated.\n   * @returns {Promise<true>}\n   */\n  async resumeIfSuspended() {\n    if (this.context.state === \"suspended\") {\n      await this.context.resume();\n    }\n    return true;\n  }\n}\n\nglobalThis.AudioAnalysis = AudioAnalysis;\n","export const StreamProcessorWorklet = `\nclass StreamProcessor extends AudioWorkletProcessor {\n  constructor() {\n    super();\n    this.hasStarted = false;\n    this.hasInterrupted = false;\n    this.outputBuffers = [];\n    this.bufferLength = 128;\n    this.write = { buffer: new Float32Array(this.bufferLength), trackId: null };\n    this.writeOffset = 0;\n    this.trackSampleOffsets = {};\n    this.port.onmessage = (event) => {\n      if (event.data) {\n        const payload = event.data;\n        if (payload.event === 'write') {\n          const int16Array = payload.buffer;\n          const float32Array = new Float32Array(int16Array.length);\n          for (let i = 0; i < int16Array.length; i++) {\n            float32Array[i] = int16Array[i] / 0x8000; // Convert Int16 to Float32\n          }\n          this.writeData(float32Array, payload.trackId);\n        } else if (\n          payload.event === 'offset' ||\n          payload.event === 'interrupt'\n        ) {\n          const requestId = payload.requestId;\n          const trackId = this.write.trackId;\n          const offset = this.trackSampleOffsets[trackId] || 0;\n          this.port.postMessage({\n            event: 'offset',\n            requestId,\n            trackId,\n            offset,\n          });\n          if (payload.event === 'interrupt') {\n            this.hasInterrupted = true;\n          }\n        } else {\n          throw new Error(\\`Unhandled event \"\\${payload.event}\"\\`);\n        }\n      }\n    };\n  }\n\n  writeData(float32Array, trackId = null) {\n    let { buffer } = this.write;\n    let offset = this.writeOffset;\n    for (let i = 0; i < float32Array.length; i++) {\n      buffer[offset++] = float32Array[i];\n      if (offset >= buffer.length) {\n        this.outputBuffers.push(this.write);\n        this.write = { buffer: new Float32Array(this.bufferLength), trackId };\n        buffer = this.write.buffer;\n        offset = 0;\n      }\n    }\n    this.writeOffset = offset;\n    return true;\n  }\n\n  process(inputs, outputs, parameters) {\n    const output = outputs[0];\n    const outputChannelData = output[0];\n    const outputBuffers = this.outputBuffers;\n    if (this.hasInterrupted) {\n      this.port.postMessage({ event: 'stop' });\n      return false;\n    } else if (outputBuffers.length) {\n      this.hasStarted = true;\n      const { buffer, trackId } = outputBuffers.shift();\n      for (let i = 0; i < outputChannelData.length; i++) {\n        outputChannelData[i] = buffer[i] || 0;\n      }\n      if (trackId) {\n        this.trackSampleOffsets[trackId] =\n          this.trackSampleOffsets[trackId] || 0;\n        this.trackSampleOffsets[trackId] += buffer.length;\n      }\n      return true;\n    } else if (this.hasStarted) {\n      this.port.postMessage({ event: 'stop' });\n      return false;\n    } else {\n      return true;\n    }\n  }\n}\n\nregisterProcessor('stream_processor', StreamProcessor);\n`;\n\nconst script = new Blob([StreamProcessorWorklet], {\n  type: \"application/javascript\",\n});\nconst src = URL.createObjectURL(script);\nexport const StreamProcessorSrc = src;\n","import { StreamProcessorSrc } from \"./worklets/stream_processor.js\";\nimport { AudioAnalysis } from \"./analysis/audio_analysis.js\";\n\n/**\n * Plays audio streams received in raw PCM16 chunks from the browser\n * @class\n */\nexport class WavStreamPlayer {\n  /**\n   * Creates a new WavStreamPlayer instance\n   * @param {{sampleRate?: number}} options\n   * @returns {WavStreamPlayer}\n   */\n  constructor({ sampleRate = 44100 } = {}) {\n    this.scriptSrc = StreamProcessorSrc;\n    this.sampleRate = sampleRate;\n    this.context = null;\n    this.stream = null;\n    this.analyser = null;\n    this.trackSampleOffsets = {};\n    this.interruptedTrackIds = {};\n  }\n\n  /**\n   * Connects the audio context and enables output to speakers\n   * @returns {Promise<true>}\n   */\n  async connect() {\n    this.context = new AudioContext({ sampleRate: this.sampleRate });\n    if (this._speakerID) {\n      this.context.setSinkId(this._speakerID);\n    }\n    if (this.context.state === \"suspended\") {\n      await this.context.resume();\n    }\n    try {\n      await this.context.audioWorklet.addModule(this.scriptSrc);\n    } catch (e) {\n      console.error(e);\n      throw new Error(`Could not add audioWorklet module: ${this.scriptSrc}`);\n    }\n    const analyser = this.context.createAnalyser();\n    analyser.fftSize = 8192;\n    analyser.smoothingTimeConstant = 0.1;\n    this.analyser = analyser;\n    return true;\n  }\n\n  /**\n   * Gets the current frequency domain data from the playing track\n   * @param {\"frequency\"|\"music\"|\"voice\"} [analysisType]\n   * @param {number} [minDecibels] default -100\n   * @param {number} [maxDecibels] default -30\n   * @returns {import('./analysis/audio_analysis.js').AudioAnalysisOutputType}\n   */\n  getFrequencies(\n    analysisType = \"frequency\",\n    minDecibels = -100,\n    maxDecibels = -30\n  ) {\n    if (!this.analyser) {\n      throw new Error(\"Not connected, please call .connect() first\");\n    }\n    return AudioAnalysis.getFrequencies(\n      this.analyser,\n      this.sampleRate,\n      null,\n      analysisType,\n      minDecibels,\n      maxDecibels\n    );\n  }\n\n  /**\n   * @param {string} speaker deviceId\n   */\n  async updateSpeaker(speaker) {\n    const _prevSpeaker = this._speakerID;\n    this._speakerID = speaker;\n    if (this.context) {\n      try {\n        if (speaker === \"default\") {\n          await this.context.setSinkId();\n        } else {\n          await this.context.setSinkId(speaker);\n        }\n      } catch (e) {\n        console.error(`Could not set sinkId to ${speaker}: ${e}`);\n        this._speakerID = _prevSpeaker;\n      }\n    }\n  }\n\n  /**\n   * Starts audio streaming\n   * @private\n   * @returns {Promise<true>}\n   */\n  _start() {\n    const streamNode = new AudioWorkletNode(this.context, \"stream_processor\");\n    streamNode.connect(this.context.destination);\n    streamNode.port.onmessage = (e) => {\n      const { event } = e.data;\n      if (event === \"stop\") {\n        streamNode.disconnect();\n        this.stream = null;\n      } else if (event === \"offset\") {\n        const { requestId, trackId, offset } = e.data;\n        const currentTime = offset / this.sampleRate;\n        this.trackSampleOffsets[requestId] = { trackId, offset, currentTime };\n      }\n    };\n    this.analyser.disconnect();\n    streamNode.connect(this.analyser);\n    this.stream = streamNode;\n    return true;\n  }\n\n  /**\n   * Adds 16BitPCM data to the currently playing audio stream\n   * You can add chunks beyond the current play point and they will be queued for play\n   * @param {ArrayBuffer|Int16Array} arrayBuffer\n   * @param {string} [trackId]\n   * @returns {Int16Array}\n   */\n  add16BitPCM(arrayBuffer, trackId = \"default\") {\n    if (typeof trackId !== \"string\") {\n      throw new Error(`trackId must be a string`);\n    } else if (this.interruptedTrackIds[trackId]) {\n      return;\n    }\n    if (!this.stream) {\n      this._start();\n    }\n    let buffer;\n    if (arrayBuffer instanceof Int16Array) {\n      buffer = arrayBuffer;\n    } else if (arrayBuffer instanceof ArrayBuffer) {\n      buffer = new Int16Array(arrayBuffer);\n    } else {\n      throw new Error(`argument must be Int16Array or ArrayBuffer`);\n    }\n    this.stream.port.postMessage({ event: \"write\", buffer, trackId });\n    return buffer;\n  }\n\n  /**\n   * Gets the offset (sample count) of the currently playing stream\n   * @param {boolean} [interrupt]\n   * @returns {{trackId: string|null, offset: number, currentTime: number}}\n   */\n  async getTrackSampleOffset(interrupt = false) {\n    if (!this.stream) {\n      return null;\n    }\n    const requestId = crypto.randomUUID();\n    this.stream.port.postMessage({\n      event: interrupt ? \"interrupt\" : \"offset\",\n      requestId,\n    });\n    let trackSampleOffset;\n    while (!trackSampleOffset) {\n      trackSampleOffset = this.trackSampleOffsets[requestId];\n      await new Promise((r) => setTimeout(() => r(), 1));\n    }\n    const { trackId } = trackSampleOffset;\n    if (interrupt && trackId) {\n      this.interruptedTrackIds[trackId] = true;\n    }\n    return trackSampleOffset;\n  }\n\n  /**\n   * Strips the current stream and returns the sample offset of the audio\n   * @param {boolean} [interrupt]\n   * @returns {{trackId: string|null, offset: number, currentTime: number}}\n   */\n  async interrupt() {\n    return this.getTrackSampleOffset(true);\n  }\n}\n\nglobalThis.WavStreamPlayer = WavStreamPlayer;\n","const AudioProcessorWorklet = `\nclass AudioProcessor extends AudioWorkletProcessor {\n\n  constructor() {\n    super();\n    this.port.onmessage = this.receive.bind(this);\n    this.initialize();\n  }\n\n  initialize() {\n    this.foundAudio = false;\n    this.recording = false;\n    this.chunks = [];\n  }\n\n  /**\n   * Concatenates sampled chunks into channels\n   * Format is chunk[Left[], Right[]]\n   */\n  readChannelData(chunks, channel = -1, maxChannels = 9) {\n    let channelLimit;\n    if (channel !== -1) {\n      if (chunks[0] && chunks[0].length - 1 < channel) {\n        throw new Error(\n          \\`Channel \\${channel} out of range: max \\${chunks[0].length}\\`\n        );\n      }\n      channelLimit = channel + 1;\n    } else {\n      channel = 0;\n      channelLimit = Math.min(chunks[0] ? chunks[0].length : 1, maxChannels);\n    }\n    const channels = [];\n    for (let n = channel; n < channelLimit; n++) {\n      const length = chunks.reduce((sum, chunk) => {\n        return sum + chunk[n].length;\n      }, 0);\n      const buffers = chunks.map((chunk) => chunk[n]);\n      const result = new Float32Array(length);\n      let offset = 0;\n      for (let i = 0; i < buffers.length; i++) {\n        result.set(buffers[i], offset);\n        offset += buffers[i].length;\n      }\n      channels[n] = result;\n    }\n    return channels;\n  }\n\n  /**\n   * Combines parallel audio data into correct format,\n   * channels[Left[], Right[]] to float32Array[LRLRLRLR...]\n   */\n  formatAudioData(channels) {\n    if (channels.length === 1) {\n      // Simple case is only one channel\n      const float32Array = channels[0].slice();\n      const meanValues = channels[0].slice();\n      return { float32Array, meanValues };\n    } else {\n      const float32Array = new Float32Array(\n        channels[0].length * channels.length\n      );\n      const meanValues = new Float32Array(channels[0].length);\n      for (let i = 0; i < channels[0].length; i++) {\n        const offset = i * channels.length;\n        let meanValue = 0;\n        for (let n = 0; n < channels.length; n++) {\n          float32Array[offset + n] = channels[n][i];\n          meanValue += channels[n][i];\n        }\n        meanValues[i] = meanValue / channels.length;\n      }\n      return { float32Array, meanValues };\n    }\n  }\n\n  /**\n   * Converts 32-bit float data to 16-bit integers\n   */\n  floatTo16BitPCM(float32Array) {\n    const buffer = new ArrayBuffer(float32Array.length * 2);\n    const view = new DataView(buffer);\n    let offset = 0;\n    for (let i = 0; i < float32Array.length; i++, offset += 2) {\n      let s = Math.max(-1, Math.min(1, float32Array[i]));\n      view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n    }\n    return buffer;\n  }\n\n  /**\n   * Retrieves the most recent amplitude values from the audio stream\n   * @param {number} channel\n   */\n  getValues(channel = -1) {\n    const channels = this.readChannelData(this.chunks, channel);\n    const { meanValues } = this.formatAudioData(channels);\n    return { meanValues, channels };\n  }\n\n  /**\n   * Exports chunks as an audio/wav file\n   */\n  export() {\n    const channels = this.readChannelData(this.chunks);\n    const { float32Array, meanValues } = this.formatAudioData(channels);\n    const audioData = this.floatTo16BitPCM(float32Array);\n    return {\n      meanValues: meanValues,\n      audio: {\n        bitsPerSample: 16,\n        channels: channels,\n        data: audioData,\n      },\n    };\n  }\n\n  receive(e) {\n    const { event, id } = e.data;\n    let receiptData = {};\n    switch (event) {\n      case 'start':\n        this.recording = true;\n        break;\n      case 'stop':\n        this.recording = false;\n        break;\n      case 'clear':\n        this.initialize();\n        break;\n      case 'export':\n        receiptData = this.export();\n        break;\n      case 'read':\n        receiptData = this.getValues();\n        break;\n      default:\n        break;\n    }\n    // Always send back receipt\n    this.port.postMessage({ event: 'receipt', id, data: receiptData });\n  }\n\n  sendChunk(chunk) {\n    const channels = this.readChannelData([chunk]);\n    const { float32Array, meanValues } = this.formatAudioData(channels);\n    const rawAudioData = this.floatTo16BitPCM(float32Array);\n    const monoAudioData = this.floatTo16BitPCM(meanValues);\n    this.port.postMessage({\n      event: 'chunk',\n      data: {\n        mono: monoAudioData,\n        raw: rawAudioData,\n      },\n    });\n  }\n\n  process(inputList, outputList, parameters) {\n    // Copy input to output (e.g. speakers)\n    // Note that this creates choppy sounds with Mac products\n    const sourceLimit = Math.min(inputList.length, outputList.length);\n    for (let inputNum = 0; inputNum < sourceLimit; inputNum++) {\n      const input = inputList[inputNum];\n      const output = outputList[inputNum];\n      const channelCount = Math.min(input.length, output.length);\n      for (let channelNum = 0; channelNum < channelCount; channelNum++) {\n        input[channelNum].forEach((sample, i) => {\n          output[channelNum][i] = sample;\n        });\n      }\n    }\n    const inputs = inputList[0];\n    // There's latency at the beginning of a stream before recording starts\n    // Make sure we actually receive audio data before we start storing chunks\n    let sliceIndex = 0;\n    if (!this.foundAudio) {\n      for (const channel of inputs) {\n        sliceIndex = 0; // reset for each channel\n        if (this.foundAudio) {\n          break;\n        }\n        if (channel) {\n          for (const value of channel) {\n            if (value !== 0) {\n              // find only one non-zero entry in any channel\n              this.foundAudio = true;\n              break;\n            } else {\n              sliceIndex++;\n            }\n          }\n        }\n      }\n    }\n    if (inputs && inputs[0] && this.foundAudio && this.recording) {\n      // We need to copy the TypedArray, because the \\`process\\`\n      // internals will reuse the same buffer to hold each input\n      const chunk = inputs.map((input) => input.slice(sliceIndex));\n      this.chunks.push(chunk);\n      this.sendChunk(chunk);\n    }\n    return true;\n  }\n}\n\nregisterProcessor('audio_processor', AudioProcessor);\n`;\n\nconst script = new Blob([AudioProcessorWorklet], {\n  type: \"application/javascript\",\n});\nconst src = URL.createObjectURL(script);\nexport const AudioProcessorSrc = src;\n","import { AudioProcessorSrc } from \"./worklets/audio_processor.js\";\nimport { AudioAnalysis } from \"./analysis/audio_analysis.js\";\nimport { WavPacker } from \"./wav_packer.js\";\n\n/**\n * Decodes audio into a wav file\n * @typedef {Object} DecodedAudioType\n * @property {Blob} blob\n * @property {string} url\n * @property {Float32Array} values\n * @property {AudioBuffer} audioBuffer\n */\n\n/**\n * Records live stream of user audio as PCM16 \"audio/wav\" data\n * @class\n */\nexport class WavRecorder {\n  /**\n   * Create a new WavRecorder instance\n   * @param {{sampleRate?: number, outputToSpeakers?: boolean, debug?: boolean}} [options]\n   * @returns {WavRecorder}\n   */\n  constructor({\n    sampleRate = 44100,\n    outputToSpeakers = false,\n    debug = false,\n  } = {}) {\n    // Script source\n    this.scriptSrc = AudioProcessorSrc;\n    // Config\n    this.sampleRate = sampleRate;\n    this.outputToSpeakers = outputToSpeakers;\n    this.debug = !!debug;\n    this._deviceChangeCallback = null;\n    this._deviceErrorCallback = null;\n    this._devices = [];\n    this.deviceSelection = null;\n    // State variables\n    this.stream = null;\n    this.processor = null;\n    this.source = null;\n    this.node = null;\n    this.recording = false;\n    // Event handling with AudioWorklet\n    this._lastEventId = 0;\n    this.eventReceipts = {};\n    this.eventTimeout = 5000;\n    // Process chunks of audio\n    this._chunkProcessor = () => {};\n    this._chunkProcessorSize = void 0;\n    this._chunkProcessorBuffer = {\n      raw: new ArrayBuffer(0),\n      mono: new ArrayBuffer(0),\n    };\n  }\n\n  /**\n   * Decodes audio data from multiple formats to a Blob, url, Float32Array and AudioBuffer\n   * @param {Blob|Float32Array|Int16Array|ArrayBuffer|number[]} audioData\n   * @param {number} sampleRate\n   * @param {number} fromSampleRate\n   * @returns {Promise<DecodedAudioType>}\n   */\n  static async decode(audioData, sampleRate = 44100, fromSampleRate = -1) {\n    const context = new AudioContext({ sampleRate });\n    let arrayBuffer;\n    let blob;\n    if (audioData instanceof Blob) {\n      if (fromSampleRate !== -1) {\n        throw new Error(\n          `Can not specify \"fromSampleRate\" when reading from Blob`\n        );\n      }\n      blob = audioData;\n      arrayBuffer = await blob.arrayBuffer();\n    } else if (audioData instanceof ArrayBuffer) {\n      if (fromSampleRate !== -1) {\n        throw new Error(\n          `Can not specify \"fromSampleRate\" when reading from ArrayBuffer`\n        );\n      }\n      arrayBuffer = audioData;\n      blob = new Blob([arrayBuffer], { type: \"audio/wav\" });\n    } else {\n      let float32Array;\n      let data;\n      if (audioData instanceof Int16Array) {\n        data = audioData;\n        float32Array = new Float32Array(audioData.length);\n        for (let i = 0; i < audioData.length; i++) {\n          float32Array[i] = audioData[i] / 0x8000;\n        }\n      } else if (audioData instanceof Float32Array) {\n        float32Array = audioData;\n      } else if (audioData instanceof Array) {\n        float32Array = new Float32Array(audioData);\n      } else {\n        throw new Error(\n          `\"audioData\" must be one of: Blob, Float32Arrray, Int16Array, ArrayBuffer, Array<number>`\n        );\n      }\n      if (fromSampleRate === -1) {\n        throw new Error(\n          `Must specify \"fromSampleRate\" when reading from Float32Array, In16Array or Array`\n        );\n      } else if (fromSampleRate < 3000) {\n        throw new Error(`Minimum \"fromSampleRate\" is 3000 (3kHz)`);\n      }\n      if (!data) {\n        data = WavPacker.floatTo16BitPCM(float32Array);\n      }\n      const audio = {\n        bitsPerSample: 16,\n        channels: [float32Array],\n        data,\n      };\n      const packer = new WavPacker();\n      const result = packer.pack(fromSampleRate, audio);\n      blob = result.blob;\n      arrayBuffer = await blob.arrayBuffer();\n    }\n    const audioBuffer = await context.decodeAudioData(arrayBuffer);\n    const values = audioBuffer.getChannelData(0);\n    const url = URL.createObjectURL(blob);\n    return {\n      blob,\n      url,\n      values,\n      audioBuffer,\n    };\n  }\n\n  /**\n   * Logs data in debug mode\n   * @param {...any} arguments\n   * @returns {true}\n   */\n  log() {\n    if (this.debug) {\n      this.log(...arguments);\n    }\n    return true;\n  }\n\n  /**\n   * Retrieves the current sampleRate for the recorder\n   * @returns {number}\n   */\n  getSampleRate() {\n    return this.sampleRate;\n  }\n\n  /**\n   * Retrieves the current status of the recording\n   * @returns {\"ended\"|\"paused\"|\"recording\"}\n   */\n  getStatus() {\n    if (!this.processor) {\n      return \"ended\";\n    } else if (!this.recording) {\n      return \"paused\";\n    } else {\n      return \"recording\";\n    }\n  }\n\n  /**\n   * Sends an event to the AudioWorklet\n   * @private\n   * @param {string} name\n   * @param {{[key: string]: any}} data\n   * @param {AudioWorkletNode} [_processor]\n   * @returns {Promise<{[key: string]: any}>}\n   */\n  async _event(name, data = {}, _processor = null) {\n    _processor = _processor || this.processor;\n    if (!_processor) {\n      throw new Error(\"Can not send events without recording first\");\n    }\n    const message = {\n      event: name,\n      id: this._lastEventId++,\n      data,\n    };\n    _processor.port.postMessage(message);\n    const t0 = new Date().valueOf();\n    while (!this.eventReceipts[message.id]) {\n      if (new Date().valueOf() - t0 > this.eventTimeout) {\n        throw new Error(`Timeout waiting for \"${name}\" event`);\n      }\n      await new Promise((res) => setTimeout(() => res(true), 1));\n    }\n    const payload = this.eventReceipts[message.id];\n    delete this.eventReceipts[message.id];\n    return payload;\n  }\n\n  /**\n   * Sets device change callback, remove if callback provided is `null`\n   * @param {(Array<MediaDeviceInfo & {default: boolean}>): void|null} callback\n   * @returns {true}\n   */\n  listenForDeviceChange(callback) {\n    if (callback === null && this._deviceChangeCallback) {\n      navigator.mediaDevices.removeEventListener(\n        \"devicechange\",\n        this._deviceChangeCallback\n      );\n      this._deviceChangeCallback = null;\n    } else if (callback !== null) {\n      // Basically a debounce; we only want this called once when devices change\n      // And we only want the most recent callback() to be executed\n      // if a few are operating at the same time\n      let lastId = 0;\n      let lastDevices = [];\n      const serializeDevices = (devices) =>\n        devices\n          .map((d) => d.deviceId)\n          .sort()\n          .join(\",\");\n      const cb = async () => {\n        let id = ++lastId;\n        const devices = await this.listDevices();\n        if (id === lastId) {\n          if (serializeDevices(lastDevices) !== serializeDevices(devices)) {\n            lastDevices = devices;\n            callback(devices.slice());\n          }\n        }\n      };\n      navigator.mediaDevices.addEventListener(\"devicechange\", cb);\n      cb();\n      this._deviceChangeCallback = cb;\n    }\n    return true;\n  }\n\n  /**\n   * Provide a callback for if/when device errors occur\n   * @param {(({devices: Array<\"cam\" | \"mic\">, type: string, error?: Error}) => void) | null} callback\n   * @returns {true}\n   */\n  listenForDeviceErrors(callback) {\n    this._deviceErrorCallback = callback;\n  }\n\n  /**\n   * Manually request permission to use the microphone\n   * @returns {Promise<true>}\n   */\n  async requestPermission() {\n    const permissionStatus = await navigator.permissions.query({\n      name: \"microphone\",\n    });\n    if (permissionStatus.state === \"denied\") {\n      if (this._deviceErrorCallback) {\n        this._deviceErrorCallback({\n          devices: [\"mic\"],\n          type: \"unknown\",\n          error: new Error(\"Microphone access denied\"),\n        });\n      }\n    } else if (permissionStatus.state === \"prompt\") {\n      try {\n        const stream = await navigator.mediaDevices.getUserMedia({\n          audio: true,\n        });\n        const tracks = stream.getTracks();\n        tracks.forEach((track) => track.stop());\n      } catch (e) {\n        console.error(\"Error accessing microphone.\");\n        if (this._deviceErrorCallback) {\n          this._deviceErrorCallback({\n            devices: [\"mic\"],\n            type: \"unknown\",\n            error: e,\n          });\n        }\n      }\n    }\n    return true;\n  }\n\n  /**\n   * List all eligible devices for recording, will request permission to use microphone\n   * @returns {Promise<Array<MediaDeviceInfo & {default: boolean}>>}\n   */\n  async listDevices() {\n    if (\n      !navigator.mediaDevices ||\n      !(\"enumerateDevices\" in navigator.mediaDevices)\n    ) {\n      throw new Error(\"Could not request user devices\");\n    }\n    await this.requestPermission();\n    const devices = await navigator.mediaDevices.enumerateDevices();\n    const audioDevices = devices.filter(\n      (device) => device.kind === \"audioinput\"\n    );\n    return audioDevices;\n    // const defaultDeviceIndex = audioDevices.findIndex(\n    //   (device) => device.deviceId === 'default'\n    // );\n    // const deviceList = [];\n    // if (defaultDeviceIndex !== -1) {\n    //   let defaultDevice = audioDevices.splice(defaultDeviceIndex, 1)[0];\n    //   let existingIndex = audioDevices.findIndex(\n    //     (device) => device.groupId === defaultDevice.groupId\n    //   );\n    //   if (existingIndex !== -1) {\n    //     defaultDevice = audioDevices.splice(existingIndex, 1)[0];\n    //   }\n    //   defaultDevice.default = true;\n    //   deviceList.push(defaultDevice);\n    // }\n    // return deviceList.concat(audioDevices);\n  }\n\n  /**\n   * Begins a recording session and requests microphone permissions if not already granted\n   * Microphone recording indicator will appear on browser tab but status will be \"paused\"\n   * @param {string} [deviceId] if no device provided, default device will be used\n   * @returns {Promise<true>}\n   */\n  async begin(deviceId) {\n    if (this.processor) {\n      throw new Error(\n        `Already connected: please call .end() to start a new session`\n      );\n    }\n\n    if (\n      !navigator.mediaDevices ||\n      !(\"getUserMedia\" in navigator.mediaDevices)\n    ) {\n      if (this._deviceErrorCallback) {\n        this._deviceErrorCallback({\n          devices: [\"mic\", \"cam\"],\n          type: \"undefined-mediadevices\",\n        });\n      }\n      throw new Error(\"Could not request user media\");\n    }\n    deviceId = deviceId ?? this.deviceSelection?.deviceId;\n    try {\n      const config = { audio: true };\n      if (deviceId) {\n        config.audio = { deviceId: { exact: deviceId } };\n      }\n      this.stream = await navigator.mediaDevices.getUserMedia(config);\n    } catch (err) {\n      if (this._deviceErrorCallback) {\n        this._deviceErrorCallback({\n          devices: [\"mic\"],\n          type: \"unknown\",\n          error: err,\n        });\n      }\n      throw new Error(\"Could not start media stream\");\n    }\n\n    this.listDevices().then((devices) => {\n      deviceId = this.stream.getAudioTracks()[0].getSettings().deviceId;\n      console.log(\n        \"find current device\",\n        devices,\n        deviceId,\n        this.stream.getAudioTracks()[0].getSettings()\n      );\n      this.deviceSelection = devices.find((d) => d.deviceId === deviceId);\n      console.log(\"current device\", this.deviceSelection);\n    });\n    const context = new AudioContext({ sampleRate: this.sampleRate });\n    const source = context.createMediaStreamSource(this.stream);\n    // Load and execute the module script.\n    try {\n      await context.audioWorklet.addModule(this.scriptSrc);\n    } catch (e) {\n      console.error(e);\n      throw new Error(`Could not add audioWorklet module: ${this.scriptSrc}`);\n    }\n    const processor = new AudioWorkletNode(context, \"audio_processor\");\n    processor.port.onmessage = (e) => {\n      const { event, id, data } = e.data;\n      if (event === \"receipt\") {\n        this.eventReceipts[id] = data;\n      } else if (event === \"chunk\") {\n        if (this._chunkProcessorSize) {\n          const buffer = this._chunkProcessorBuffer;\n          this._chunkProcessorBuffer = {\n            raw: WavPacker.mergeBuffers(buffer.raw, data.raw),\n            mono: WavPacker.mergeBuffers(buffer.mono, data.mono),\n          };\n          if (\n            this._chunkProcessorBuffer.mono.byteLength >=\n            this._chunkProcessorSize\n          ) {\n            this._chunkProcessor(this._chunkProcessorBuffer);\n            this._chunkProcessorBuffer = {\n              raw: new ArrayBuffer(0),\n              mono: new ArrayBuffer(0),\n            };\n          }\n        } else {\n          this._chunkProcessor(data);\n        }\n      }\n    };\n\n    const node = source.connect(processor);\n    const analyser = context.createAnalyser();\n    analyser.fftSize = 8192;\n    analyser.smoothingTimeConstant = 0.1;\n    node.connect(analyser);\n    if (this.outputToSpeakers) {\n      // eslint-disable-next-line no-console\n      console.warn(\n        \"Warning: Output to speakers may affect sound quality,\\n\" +\n          \"especially due to system audio feedback preventative measures.\\n\" +\n          \"use only for debugging\"\n      );\n      analyser.connect(context.destination);\n    }\n\n    this.source = source;\n    this.node = node;\n    this.analyser = analyser;\n    this.processor = processor;\n    console.log(\"begin completed\");\n    return true;\n  }\n\n  /**\n   * Gets the current frequency domain data from the recording track\n   * @param {\"frequency\"|\"music\"|\"voice\"} [analysisType]\n   * @param {number} [minDecibels] default -100\n   * @param {number} [maxDecibels] default -30\n   * @returns {import('./analysis/audio_analysis.js').AudioAnalysisOutputType}\n   */\n  getFrequencies(\n    analysisType = \"frequency\",\n    minDecibels = -100,\n    maxDecibels = -30\n  ) {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    }\n    return AudioAnalysis.getFrequencies(\n      this.analyser,\n      this.sampleRate,\n      null,\n      analysisType,\n      minDecibels,\n      maxDecibels\n    );\n  }\n\n  /**\n   * Pauses the recording\n   * Keeps microphone stream open but halts storage of audio\n   * @returns {Promise<true>}\n   */\n  async pause() {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    } else if (!this.recording) {\n      throw new Error(\"Already paused: please call .record() first\");\n    }\n    if (this._chunkProcessorBuffer.raw.byteLength) {\n      this._chunkProcessor(this._chunkProcessorBuffer);\n    }\n    this.log(\"Pausing ...\");\n    await this._event(\"stop\");\n    this.recording = false;\n    return true;\n  }\n\n  /**\n   * Start recording stream and storing to memory from the connected audio source\n   * @param {(data: { mono: Int16Array; raw: Int16Array }) => any} [chunkProcessor]\n   * @param {number} [chunkSize] chunkProcessor will not be triggered until this size threshold met in mono audio\n   * @returns {Promise<true>}\n   */\n  async record(chunkProcessor = () => {}, chunkSize = 8192) {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    } else if (this.recording) {\n      throw new Error(\"Already recording: please call .pause() first\");\n    } else if (typeof chunkProcessor !== \"function\") {\n      throw new Error(`chunkProcessor must be a function`);\n    }\n    this._chunkProcessor = chunkProcessor;\n    this._chunkProcessorSize = chunkSize;\n    this._chunkProcessorBuffer = {\n      raw: new ArrayBuffer(0),\n      mono: new ArrayBuffer(0),\n    };\n    this.log(\"Recording ...\");\n    await this._event(\"start\");\n    this.recording = true;\n    return true;\n  }\n\n  /**\n   * Clears the audio buffer, empties stored recording\n   * @returns {Promise<true>}\n   */\n  async clear() {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    }\n    await this._event(\"clear\");\n    return true;\n  }\n\n  /**\n   * Reads the current audio stream data\n   * @returns {Promise<{meanValues: Float32Array, channels: Array<Float32Array>}>}\n   */\n  async read() {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    }\n    this.log(\"Reading ...\");\n    const result = await this._event(\"read\");\n    return result;\n  }\n\n  /**\n   * Saves the current audio stream to a file\n   * @param {boolean} [force] Force saving while still recording\n   * @returns {Promise<import('./wav_packer.js').WavPackerAudioType>}\n   */\n  async save(force = false) {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    }\n    if (!force && this.recording) {\n      throw new Error(\n        \"Currently recording: please call .pause() first, or call .save(true) to force\"\n      );\n    }\n    this.log(\"Exporting ...\");\n    const exportData = await this._event(\"export\");\n    const packer = new WavPacker();\n    const result = packer.pack(this.sampleRate, exportData.audio);\n    return result;\n  }\n\n  /**\n   * Ends the current recording session and saves the result\n   * @returns {Promise<import('./wav_packer.js').WavPackerAudioType>}\n   */\n  async end() {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    }\n\n    const _processor = this.processor;\n\n    this.log(\"Stopping ...\");\n    await this._event(\"stop\");\n    this.recording = false;\n    const tracks = this.stream.getTracks();\n    tracks.forEach((track) => track.stop());\n\n    this.log(\"Exporting ...\");\n    const exportData = await this._event(\"export\", {}, _processor);\n\n    this.processor.disconnect();\n    this.source.disconnect();\n    this.node.disconnect();\n    this.analyser.disconnect();\n    this.stream = null;\n    this.processor = null;\n    this.source = null;\n    this.node = null;\n\n    const packer = new WavPacker();\n    const result = packer.pack(this.sampleRate, exportData.audio);\n    return result;\n  }\n\n  /**\n   * Performs a full cleanup of WavRecorder instance\n   * Stops actively listening via microphone and removes existing listeners\n   * @returns {Promise<true>}\n   */\n  async quit() {\n    this.listenForDeviceChange(null);\n    // we do not reset this on end so that selections persist across starts\n    this.deviceSelection = null;\n    if (this.processor) {\n      await this.end();\n    }\n    return true;\n  }\n}\n\nglobalThis.WavRecorder = WavRecorder;\n","import { AudioProcessorSrc } from \"./worklets/audio_processor.js\";\nimport { AudioAnalysis } from \"./analysis/audio_analysis.js\";\nimport { WavPacker } from \"./wav_packer.js\";\n\n/**\n * Resamples audio data from one sample rate to another using linear interpolation\n * @param {ArrayBuffer} inputBuffer - Input audio data as Int16 PCM\n * @param {number} inputSampleRate - Sample rate of input audio\n * @param {number} outputSampleRate - Desired output sample rate\n * @returns {ArrayBuffer} - Resampled audio data as Int16 PCM\n */\nfunction resampleAudioBuffer(inputBuffer, inputSampleRate, outputSampleRate) {\n  if (inputSampleRate === outputSampleRate) {\n    return inputBuffer;\n  }\n\n  const inputView = new Int16Array(inputBuffer);\n  const ratio = inputSampleRate / outputSampleRate;\n  const outputLength = Math.round(inputView.length / ratio);\n  const outputBuffer = new ArrayBuffer(outputLength * 2);\n  const outputView = new Int16Array(outputBuffer);\n\n  for (let i = 0; i < outputLength; i++) {\n    const srcIndex = i * ratio;\n    const srcIndexFloor = Math.floor(srcIndex);\n    const srcIndexCeil = Math.min(srcIndexFloor + 1, inputView.length - 1);\n    const t = srcIndex - srcIndexFloor;\n\n    // Linear interpolation\n    outputView[i] = Math.round(\n      inputView[srcIndexFloor] * (1 - t) + inputView[srcIndexCeil] * t\n    );\n  }\n\n  return outputBuffer;\n}\n\n/**\n * Decodes audio into a wav file\n * @typedef {Object} DecodedAudioType\n * @property {Blob} blob\n * @property {string} url\n * @property {Float32Array} values\n * @property {AudioBuffer} audioBuffer\n */\n\n/**\n * Records live stream of user audio as PCM16 \"audio/wav\" data\n * @class\n */\nexport class MediaStreamRecorder {\n  /**\n   * Create a new MediaStreamRecorder instance\n   * @param {{sampleRate?: number, outputToSpeakers?: boolean, debug?: boolean}} [options]\n   * @returns {MediaStreamRecorder}\n   */\n  constructor({\n    sampleRate = 44100,\n    outputToSpeakers = false,\n    debug = false,\n  } = {}) {\n    // Script source\n    this.scriptSrc = AudioProcessorSrc;\n    // Config\n    this.sampleRate = sampleRate;\n    this.outputToSpeakers = outputToSpeakers;\n    this.debug = !!debug;\n    // State variables\n    this.stream = null;\n    this.processor = null;\n    this.source = null;\n    this.node = null;\n    this.recording = false;\n    // Event handling with AudioWorklet\n    this._lastEventId = 0;\n    this.eventReceipts = {};\n    this.eventTimeout = 5000;\n    // Process chunks of audio\n    this._chunkProcessor = () => {};\n    this._chunkProcessorSize = void 0;\n    this._chunkProcessorBuffer = {\n      raw: new ArrayBuffer(0),\n      mono: new ArrayBuffer(0),\n    };\n  }\n\n  /**\n   * Logs data in debug mode\n   * @param {...any} arguments\n   * @returns {true}\n   */\n  log() {\n    if (this.debug) {\n      this.log(...arguments);\n    }\n    return true;\n  }\n\n  /**\n   * Retrieves the current sampleRate for the recorder\n   * @returns {number}\n   */\n  getSampleRate() {\n    return this.sampleRate;\n  }\n\n  /**\n   * Retrieves the current status of the recording\n   * @returns {\"ended\"|\"paused\"|\"recording\"}\n   */\n  getStatus() {\n    if (!this.processor) {\n      return \"ended\";\n    } else if (!this.recording) {\n      return \"paused\";\n    } else {\n      return \"recording\";\n    }\n  }\n\n  /**\n   * Sends an event to the AudioWorklet\n   * @private\n   * @param {string} name\n   * @param {{[key: string]: any}} data\n   * @param {AudioWorkletNode} [_processor]\n   * @returns {Promise<{[key: string]: any}>}\n   */\n  async _event(name, data = {}, _processor = null) {\n    _processor = _processor || this.processor;\n    if (!_processor) {\n      throw new Error(\"Can not send events without recording first\");\n    }\n    const message = {\n      event: name,\n      id: this._lastEventId++,\n      data,\n    };\n    _processor.port.postMessage(message);\n    const t0 = new Date().valueOf();\n    while (!this.eventReceipts[message.id]) {\n      if (new Date().valueOf() - t0 > this.eventTimeout) {\n        throw new Error(`Timeout waiting for \"${name}\" event`);\n      }\n      await new Promise((res) => setTimeout(() => res(true), 1));\n    }\n    const payload = this.eventReceipts[message.id];\n    delete this.eventReceipts[message.id];\n    return payload;\n  }\n\n  /**\n   * Begins a recording session for the given audioTrack\n   * Microphone recording indicator will appear on browser tab but status will be \"paused\"\n   * @param {MediaStreamTrack} [audioTrack] if no device provided, default device will be used\n   * @returns {Promise<true>}\n   */\n  async begin(audioTrack) {\n    if (this.processor) {\n      throw new Error(\n        `Already connected: please call .end() to start a new session`\n      );\n    }\n\n    if (!audioTrack || audioTrack.kind !== \"audio\") {\n      throw new Error(\"No audio track provided\");\n    }\n\n    this.stream = new MediaStream([audioTrack]);\n\n    // Firefox workaround: Firefox doesn't support connecting AudioNodes from\n    // AudioContexts with different sample rates. So we create the AudioContext\n    // at the native sample rate and resample manually.\n    // This workaround may be temporary; Firefox is in the process of adding\n    // support for this.\n    // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1674892\n    const isFirefox = navigator.userAgent.toLowerCase().includes(\"firefox\");\n\n    let context;\n    if (isFirefox) {\n      // Firefox: Use native sample rate and resample manually\n      context = new AudioContext();\n    } else {\n      // Other browsers: Let the AudioContext handle resampling\n      context = new AudioContext({ sampleRate: this.sampleRate });\n    }\n    const contextSampleRate = context.sampleRate;\n\n    const source = context.createMediaStreamSource(this.stream);\n    // Load and execute the module script.\n    try {\n      await context.audioWorklet.addModule(this.scriptSrc);\n    } catch (e) {\n      console.error(e);\n      throw new Error(`Could not add audioWorklet module: ${this.scriptSrc}`);\n    }\n    const processor = new AudioWorkletNode(context, \"audio_processor\");\n    processor.port.onmessage = (e) => {\n      const { event, id, data } = e.data;\n      if (event === \"receipt\") {\n        this.eventReceipts[id] = data;\n      } else if (event === \"chunk\") {\n        // Resample chunk data from native sample rate to target sample rate\n        // (no-op if rates match, i.e. for non-Firefox browsers)\n        const resampledData = {\n          raw: resampleAudioBuffer(\n            data.raw,\n            contextSampleRate,\n            this.sampleRate\n          ),\n          mono: resampleAudioBuffer(\n            data.mono,\n            contextSampleRate,\n            this.sampleRate\n          ),\n        };\n\n        if (this._chunkProcessorSize) {\n          const buffer = this._chunkProcessorBuffer;\n          this._chunkProcessorBuffer = {\n            raw: WavPacker.mergeBuffers(buffer.raw, resampledData.raw),\n            mono: WavPacker.mergeBuffers(buffer.mono, resampledData.mono),\n          };\n          if (\n            this._chunkProcessorBuffer.mono.byteLength >=\n            this._chunkProcessorSize\n          ) {\n            this._chunkProcessor(this._chunkProcessorBuffer);\n            this._chunkProcessorBuffer = {\n              raw: new ArrayBuffer(0),\n              mono: new ArrayBuffer(0),\n            };\n          }\n        } else {\n          this._chunkProcessor(resampledData);\n        }\n      }\n    };\n\n    const node = source.connect(processor);\n    const analyser = context.createAnalyser();\n    analyser.fftSize = 8192;\n    analyser.smoothingTimeConstant = 0.1;\n    node.connect(analyser);\n    if (this.outputToSpeakers) {\n      // eslint-disable-next-line no-console\n      console.warn(\n        \"Warning: Output to speakers may affect sound quality,\\n\" +\n          \"especially due to system audio feedback preventative measures.\\n\" +\n          \"use only for debugging\"\n      );\n      analyser.connect(context.destination);\n    }\n\n    this.source = source;\n    this.node = node;\n    this.analyser = analyser;\n    this.processor = processor;\n    return true;\n  }\n\n  /**\n   * Gets the current frequency domain data from the recording track\n   * @param {\"frequency\"|\"music\"|\"voice\"} [analysisType]\n   * @param {number} [minDecibels] default -100\n   * @param {number} [maxDecibels] default -30\n   * @returns {import('./analysis/audio_analysis.js').AudioAnalysisOutputType}\n   */\n  getFrequencies(\n    analysisType = \"frequency\",\n    minDecibels = -100,\n    maxDecibels = -30\n  ) {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    }\n    return AudioAnalysis.getFrequencies(\n      this.analyser,\n      this.sampleRate,\n      null,\n      analysisType,\n      minDecibels,\n      maxDecibels\n    );\n  }\n\n  /**\n   * Pauses the recording\n   * Keeps microphone stream open but halts storage of audio\n   * @returns {Promise<true>}\n   */\n  async pause() {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    } else if (!this.recording) {\n      throw new Error(\"Already paused: please call .record() first\");\n    }\n    if (this._chunkProcessorBuffer.raw.byteLength) {\n      this._chunkProcessor(this._chunkProcessorBuffer);\n    }\n    this.log(\"Pausing ...\");\n    await this._event(\"stop\");\n    this.recording = false;\n    return true;\n  }\n\n  /**\n   * Start recording stream and storing to memory from the connected audio source\n   * @param {(data: { mono: Int16Array; raw: Int16Array }) => any} [chunkProcessor]\n   * @param {number} [chunkSize] chunkProcessor will not be triggered until this size threshold met in mono audio\n   * @returns {Promise<true>}\n   */\n  async record(chunkProcessor = () => {}, chunkSize = 8192) {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    } else if (this.recording) {\n      throw new Error(\"Already recording: HELLO please call .pause() first\");\n    } else if (typeof chunkProcessor !== \"function\") {\n      throw new Error(`chunkProcessor must be a function`);\n    }\n    this._chunkProcessor = chunkProcessor;\n    this._chunkProcessorSize = chunkSize;\n    this._chunkProcessorBuffer = {\n      raw: new ArrayBuffer(0),\n      mono: new ArrayBuffer(0),\n    };\n    this.log(\"Recording ...\");\n    await this._event(\"start\");\n    this.recording = true;\n    return true;\n  }\n\n  /**\n   * Clears the audio buffer, empties stored recording\n   * @returns {Promise<true>}\n   */\n  async clear() {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    }\n    await this._event(\"clear\");\n    return true;\n  }\n\n  /**\n   * Reads the current audio stream data\n   * @returns {Promise<{meanValues: Float32Array, channels: Array<Float32Array>}>}\n   */\n  async read() {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    }\n    this.log(\"Reading ...\");\n    const result = await this._event(\"read\");\n    return result;\n  }\n\n  /**\n   * Saves the current audio stream to a file\n   * @param {boolean} [force] Force saving while still recording\n   * @returns {Promise<import('./wav_packer.js').WavPackerAudioType>}\n   */\n  async save(force = false) {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    }\n    if (!force && this.recording) {\n      throw new Error(\n        \"Currently recording: please call .pause() first, or call .save(true) to force\"\n      );\n    }\n    this.log(\"Exporting ...\");\n    const exportData = await this._event(\"export\");\n    const packer = new WavPacker();\n    const result = packer.pack(this.sampleRate, exportData.audio);\n    return result;\n  }\n\n  /**\n   * Ends the current recording session and saves the result\n   * @returns {Promise<import('./wav_packer.js').WavPackerAudioType>}\n   */\n  async end() {\n    if (!this.processor) {\n      throw new Error(\"Session ended: please call .begin() first\");\n    }\n\n    const _processor = this.processor;\n\n    this.log(\"Stopping ...\");\n    await this._event(\"stop\");\n    this.recording = false;\n\n    this.log(\"Exporting ...\");\n    const exportData = await this._event(\"export\", {}, _processor);\n\n    this.processor.disconnect();\n    this.source.disconnect();\n    this.node.disconnect();\n    this.analyser.disconnect();\n    this.stream = null;\n    this.processor = null;\n    this.source = null;\n    this.node = null;\n\n    const packer = new WavPacker();\n    const result = packer.pack(this.sampleRate, exportData.audio);\n    return result;\n  }\n\n  /**\n   * Performs a full cleanup of WavRecorder instance\n   * Stops actively listening via microphone and removes existing listeners\n   * @returns {Promise<true>}\n   */\n  async quit() {\n    this.listenForDeviceChange(null);\n    if (this.processor) {\n      await this.end();\n    }\n    return true;\n  }\n}\n\nglobalThis.WavRecorder = WavRecorder;\n","import { WavRecorder, WavStreamPlayer } from \"../wavtools\";\n\nimport {\n  DeviceError,\n  DeviceErrorType,\n  PipecatClientOptions,\n  RTVIEventCallbacks,\n  Tracks,\n} from \"@pipecat-ai/client-js\";\n\nexport abstract class MediaManager {\n  declare protected _userAudioCallback: (data: ArrayBuffer) => void;\n  declare protected _options: PipecatClientOptions;\n  protected _callbacks: RTVIEventCallbacks = {};\n\n  protected _micEnabled: boolean;\n  protected _camEnabled: boolean;\n\n  protected _supportsScreenShare: boolean;\n\n  constructor() {\n    this._micEnabled = true;\n    this._camEnabled = false;\n    this._supportsScreenShare = false;\n  }\n\n  setUserAudioCallback(userAudioCallback: (data: ArrayBuffer) => void) {\n    this._userAudioCallback = userAudioCallback;\n  }\n  setClientOptions(options: PipecatClientOptions, override: boolean = false) {\n    if (this._options && !override) return;\n    this._options = options;\n    this._callbacks = options.callbacks ?? {};\n    this._micEnabled = options.enableMic ?? true;\n    this._camEnabled = options.enableCam ?? false;\n  }\n\n  abstract initialize(): Promise<void>;\n  abstract connect(): Promise<void>;\n  abstract disconnect(): Promise<void>;\n\n  abstract userStartedSpeaking(): Promise<unknown>;\n  abstract bufferBotAudio(\n    data: ArrayBuffer | Int16Array,\n    id?: string\n  ): Int16Array | undefined;\n\n  abstract getAllMics(): Promise<MediaDeviceInfo[]>;\n  abstract getAllCams(): Promise<MediaDeviceInfo[]>;\n  abstract getAllSpeakers(): Promise<MediaDeviceInfo[]>;\n\n  abstract updateMic(micId: string): void;\n  abstract updateCam(camId: string): void;\n  abstract updateSpeaker(speakerId: string): void;\n\n  abstract get selectedMic(): MediaDeviceInfo | Record<string, never>;\n  abstract get selectedCam(): MediaDeviceInfo | Record<string, never>;\n  abstract get selectedSpeaker(): MediaDeviceInfo | Record<string, never>;\n\n  abstract enableMic(enable: boolean): void;\n  abstract enableCam(enable: boolean): void;\n  abstract enableScreenShare(enable: boolean): void;\n\n  abstract get isCamEnabled(): boolean;\n  abstract get isMicEnabled(): boolean;\n  abstract get isSharingScreen(): boolean;\n\n  abstract tracks(): Tracks;\n\n  get supportsScreenShare(): boolean {\n    return this._supportsScreenShare;\n  }\n}\n\nexport class WavMediaManager extends MediaManager {\n  private _wavRecorder;\n  private _wavStreamPlayer;\n\n  private _initialized = false;\n  private _recorderChunkSize: number | undefined = undefined;\n\n  constructor(\n    recorderChunkSize: number | undefined = undefined,\n    recorderSampleRate: number | undefined = 24000\n  ) {\n    super();\n    this._recorderChunkSize = recorderChunkSize;\n    this._wavRecorder = new WavRecorder({ sampleRate: recorderSampleRate });\n    this._wavStreamPlayer = new WavStreamPlayer({ sampleRate: 24000 });\n  }\n\n  async initialize(): Promise<void> {\n    try {\n      await this._wavRecorder.begin();\n    } catch (error) {\n      // void. swallow the error. we still want to set up\n      // the listeners and the player.\n    }\n    this._wavRecorder.listenForDeviceChange(null);\n    this._wavRecorder.listenForDeviceChange(\n      this._handleAvailableDevicesUpdated.bind(this)\n    );\n    this._wavRecorder.listenForDeviceErrors(null);\n    this._wavRecorder.listenForDeviceErrors(this._handleDeviceError.bind(this));\n    await this._wavStreamPlayer.connect();\n    this._initialized = true;\n  }\n\n  async connect(): Promise<void> {\n    if (!this._initialized) {\n      await this.initialize();\n    }\n    const isAlreadyRecording = this._wavRecorder.getStatus() == \"recording\";\n    if (this._micEnabled && !isAlreadyRecording) {\n      await this._startRecording();\n    }\n    if (this._camEnabled) {\n      console.warn(\"WavMediaManager does not support video input.\");\n    }\n  }\n\n  async disconnect(): Promise<void> {\n    if (!this._initialized) {\n      return;\n    }\n    await this._wavRecorder.end();\n    await this._wavStreamPlayer.interrupt();\n    this._initialized = false;\n  }\n\n  async userStartedSpeaking(): Promise<unknown> {\n    return this._wavStreamPlayer.interrupt();\n  }\n\n  bufferBotAudio(data: ArrayBuffer | Int16Array, id?: string): Int16Array {\n    return this._wavStreamPlayer.add16BitPCM(data, id);\n  }\n\n  getAllMics(): Promise<MediaDeviceInfo[]> {\n    return this._wavRecorder.listDevices();\n  }\n  getAllCams(): Promise<MediaDeviceInfo[]> {\n    // TODO: Video not supported yet\n    return Promise.resolve([]);\n  }\n  getAllSpeakers(): Promise<MediaDeviceInfo[]> {\n    // TODO: Implement speaker support\n    return Promise.resolve([]);\n  }\n\n  async updateMic(micId: string): Promise<void> {\n    const prevMic = this._wavRecorder.deviceSelection;\n    if (this._wavRecorder.getStatus() !== \"ended\") {\n      await this._wavRecorder.end();\n    }\n    try {\n      await this._wavRecorder.begin(micId);\n      if (this._micEnabled) {\n        await this._startRecording();\n      }\n      const curMic = this._wavRecorder.deviceSelection;\n      if (curMic && prevMic && prevMic.label !== curMic.label) {\n        this._callbacks.onMicUpdated?.(curMic);\n      }\n    } catch (error) {\n      // void. If begin() fails, it will have already logged and called\n      // onDeviceError if necessary.\n    }\n  }\n\n  updateCam(camId: string): void {\n    // TODO: Video not supported yet\n  }\n  updateSpeaker(speakerId: string): void {\n    // TODO: Implement speaker support\n  }\n\n  get selectedMic(): MediaDeviceInfo | Record<string, never> {\n    return this._wavRecorder.deviceSelection ?? {};\n  }\n  get selectedCam(): MediaDeviceInfo | Record<string, never> {\n    // TODO: Video not supported yet\n    return {};\n  }\n  get selectedSpeaker(): MediaDeviceInfo | Record<string, never> {\n    // TODO: Implement speaker support\n    return {};\n  }\n\n  async enableMic(enable: boolean): Promise<void> {\n    this._micEnabled = enable;\n    if (!this._wavRecorder.stream) return;\n    this._wavRecorder.stream\n      .getAudioTracks()\n      .forEach((track: MediaStreamTrack) => {\n        track.enabled = enable;\n        if (!enable) {\n          this._callbacks.onTrackStopped?.(track, localParticipant());\n        }\n      });\n    if (enable) {\n      await this._startRecording();\n    } else {\n      await this._wavRecorder.pause();\n    }\n  }\n  enableCam(enable: boolean): void {\n    // TODO: Video not supported yet\n    console.warn(\"WavMediaManager does not support video input.\");\n  }\n  enableScreenShare(enable: boolean): void {\n    // TODO: Screensharing not supported yet\n    console.warn(\"WavMediaManager does not support screen sharing.\");\n  }\n\n  get isCamEnabled(): boolean {\n    // TODO: Video not supported yet\n    return false;\n  }\n  get isMicEnabled(): boolean {\n    return this._micEnabled;\n  }\n  get isSharingScreen(): boolean {\n    // TODO: Screensharing not supported yet\n    return false;\n  }\n\n  tracks(): Tracks {\n    const tracks = this._wavRecorder.stream?.getTracks()[0];\n    return { local: tracks ? { audio: tracks } : {} };\n  }\n\n  private async _startRecording() {\n    await this._wavRecorder.record((data) => {\n      this._userAudioCallback?.(data.mono);\n    }, this._recorderChunkSize);\n    const track = this._wavRecorder.stream?.getAudioTracks()[0];\n    if (track) {\n      this._callbacks.onTrackStarted?.(track, localParticipant());\n    }\n  }\n\n  private _handleAvailableDevicesUpdated(devices: MediaDeviceInfo[]) {\n    this._callbacks.onAvailableCamsUpdated?.(\n      devices.filter((d) => d.kind === \"videoinput\")\n    );\n    this._callbacks.onAvailableMicsUpdated?.(\n      devices.filter((d) => d.kind === \"audioinput\")\n    );\n    // if the current device went away or we're using the default and\n    // the default changed, reset the mic.\n    const defaultDevice = devices.find((d) => d.deviceId === \"default\");\n    const currentDevice = this._wavRecorder.deviceSelection;\n    if (\n      currentDevice &&\n      (!devices.some((d) => d.deviceId === currentDevice.deviceId) ||\n        (currentDevice.deviceId === \"default\" &&\n          currentDevice.label !== defaultDevice?.label))\n    ) {\n      this.updateMic(\"\");\n    }\n  }\n\n  private _handleDeviceError({\n    devices,\n    type,\n    error,\n  }: {\n    devices: Array<\"cam\" | \"mic\">;\n    type: DeviceErrorType;\n    error?: Error;\n  }) {\n    const deviceError = new DeviceError(\n      devices,\n      type,\n      error?.message,\n      error ? { sourceError: error } : undefined\n    );\n    this._callbacks.onDeviceError?.(deviceError);\n  }\n}\n\nconst localParticipant = () => {\n  return {\n    id: \"local\",\n    name: \"\",\n    local: true,\n  };\n};\n","import { MediaManager } from \"./mediaManager\";\nimport { MediaStreamRecorder, WavStreamPlayer } from \"../wavtools\";\n\nimport Daily, {\n  DailyCall,\n  DailyCameraErrorObject,\n  DailyCameraErrorType,\n  DailyEventObjectAvailableDevicesUpdated,\n  DailyEventObjectCameraError,\n  DailyEventObjectLocalAudioLevel,\n  DailyEventObjectSelectedDevicesUpdated,\n  DailyEventObjectTrack,\n  DailyParticipant,\n  DailyParticipantsObject,\n} from \"@daily-co/daily-js\";\nimport {\n  DeviceArray,\n  DeviceError,\n  Participant,\n  Tracks,\n} from \"@pipecat-ai/client-js\";\n\nexport class DailyMediaManager extends MediaManager {\n  private _daily: DailyCall;\n  private _mediaStreamRecorder: MediaStreamRecorder | undefined;\n  private _wavStreamPlayer: WavStreamPlayer | undefined;\n\n  private _initialized: boolean;\n  private _connected: boolean;\n  private _connectResolve: ((value: void | PromiseLike<void>) => void) | null;\n\n  private _currentAudioTrack: MediaStreamTrack | null;\n  private _selectedCam: MediaDeviceInfo | Record<string, never> = {};\n  private _selectedMic: MediaDeviceInfo | Record<string, never> = {};\n  private _selectedSpeaker: MediaDeviceInfo | Record<string, never> = {};\n\n  private _remoteAudioLevelInterval: NodeJS.Timeout | null = null;\n\n  private onTrackStartedCallback?: (event: DailyEventObjectTrack) => void;\n  private onTrackStoppedCallback?: (event: DailyEventObjectTrack) => void;\n\n  private _recorderChunkSize: number | undefined = undefined;\n\n  constructor(\n    enablePlayer: boolean = true,\n    enableRecording: boolean = true,\n    onTrackStartedCallback?: (event: DailyEventObjectTrack) => void,\n    onTrackStoppedCallback?: (event: DailyEventObjectTrack) => void,\n    recorderChunkSize: number | undefined = undefined,\n    recorderSampleRate: number = 24000,\n    playerSampleRate: number = 24000\n  ) {\n    super();\n    this._initialized = false;\n    this._connected = false;\n    this._currentAudioTrack = null;\n    this._connectResolve = null;\n    this.onTrackStartedCallback = onTrackStartedCallback;\n    this.onTrackStoppedCallback = onTrackStoppedCallback;\n    this._recorderChunkSize = recorderChunkSize;\n\n    this._supportsScreenShare = true;\n\n    this._daily = Daily.getCallInstance() ?? Daily.createCallObject();\n\n    if (enableRecording) {\n      this._mediaStreamRecorder = new MediaStreamRecorder({\n        sampleRate: recorderSampleRate,\n      });\n    }\n    if (enablePlayer) {\n      this._wavStreamPlayer = new WavStreamPlayer({\n        sampleRate: playerSampleRate,\n      });\n    }\n\n    this._daily.on(\"track-started\", this.handleTrackStarted.bind(this));\n    this._daily.on(\"track-stopped\", this.handleTrackStopped.bind(this));\n    this._daily.on(\n      \"available-devices-updated\",\n      this._handleAvailableDevicesUpdated.bind(this)\n    );\n    this._daily.on(\n      \"selected-devices-updated\",\n      this._handleSelectedDevicesUpdated.bind(this)\n    );\n    this._daily.on(\"camera-error\", this.handleDeviceError.bind(this));\n    this._daily.on(\"local-audio-level\", this._handleLocalAudioLevel.bind(this));\n  }\n\n  async initialize(): Promise<void> {\n    if (this._initialized) {\n      console.warn(\"DailyMediaManager already initialized\");\n      return;\n    }\n    const infos = await this._daily.startCamera({\n      startVideoOff: !this._camEnabled,\n      startAudioOff: !this._micEnabled,\n      dailyConfig: { useDevicePreferenceCookies: true },\n    });\n    const { devices } = await this._daily.enumerateDevices();\n    const cams = devices.filter((d) => d.kind === \"videoinput\");\n    const mics = devices.filter((d) => d.kind === \"audioinput\");\n    const speakers = devices.filter((d) => d.kind === \"audiooutput\");\n    this._callbacks.onAvailableCamsUpdated?.(cams);\n    this._callbacks.onAvailableMicsUpdated?.(mics);\n    this._callbacks.onAvailableSpeakersUpdated?.(speakers);\n    this._selectedCam = infos.camera;\n    this._callbacks.onCamUpdated?.(infos.camera as MediaDeviceInfo);\n    this._selectedMic = infos.mic;\n    this._callbacks.onMicUpdated?.(infos.mic as MediaDeviceInfo);\n    this._selectedSpeaker = infos.speaker;\n    this._callbacks.onSpeakerUpdated?.(infos.speaker as MediaDeviceInfo);\n\n    // Instantiate audio observers\n    if (!this._daily.isLocalAudioLevelObserverRunning())\n      await this._daily.startLocalAudioLevelObserver(100);\n\n    if (this._wavStreamPlayer) {\n      await this._wavStreamPlayer.connect();\n      if (!this._remoteAudioLevelInterval) {\n        this._remoteAudioLevelInterval = setInterval(() => {\n          const frequencies = this._wavStreamPlayer!.getFrequencies();\n          let aveVal = 0;\n          if (frequencies.values?.length) {\n            aveVal =\n              frequencies.values.reduce((a, c) => a + c, 0) /\n              frequencies.values.length;\n          }\n          this._handleRemoteAudioLevel(aveVal);\n        }, 100);\n      }\n    }\n    this._initialized = true;\n  }\n\n  async connect(): Promise<void> {\n    if (this._connected) {\n      console.warn(\"DailyMediaManager already connected\");\n      return;\n    }\n    this._connected = true;\n    if (!this._initialized) {\n      return new Promise((resolve) => {\n        (async () => {\n          this._connectResolve = resolve;\n          await this.initialize();\n        })();\n      });\n    }\n    if (this._micEnabled) {\n      this._startRecording();\n    }\n  }\n\n  async disconnect(): Promise<void> {\n    if (this._remoteAudioLevelInterval) {\n      clearInterval(this._remoteAudioLevelInterval);\n    }\n    this._remoteAudioLevelInterval = null;\n    this._daily.leave();\n    this._currentAudioTrack = null;\n    await this._mediaStreamRecorder?.end();\n    this._wavStreamPlayer?.interrupt();\n    this._initialized = false;\n    this._connected = false;\n  }\n\n  async userStartedSpeaking(): Promise<unknown> {\n    return this._wavStreamPlayer?.interrupt();\n  }\n\n  bufferBotAudio(\n    data: ArrayBuffer | Int16Array,\n    id?: string\n  ): Int16Array | undefined {\n    return this._wavStreamPlayer?.add16BitPCM(data, id);\n  }\n\n  async getAllMics(): Promise<MediaDeviceInfo[]> {\n    let devices = (await this._daily.enumerateDevices()).devices;\n    return devices.filter((device) => device.kind === \"audioinput\");\n  }\n  async getAllCams(): Promise<MediaDeviceInfo[]> {\n    let devices = (await this._daily.enumerateDevices()).devices;\n    return devices.filter((device) => device.kind === \"videoinput\");\n  }\n  async getAllSpeakers(): Promise<MediaDeviceInfo[]> {\n    let devices = (await this._daily.enumerateDevices()).devices;\n    return devices.filter((device) => device.kind === \"audiooutput\");\n  }\n\n  updateMic(micId: string) {\n    this._daily\n      .setInputDevicesAsync({ audioDeviceId: micId })\n      .then((deviceInfo) => {\n        this._selectedMic = deviceInfo.mic;\n      });\n  }\n  updateCam(camId: string) {\n    this._daily\n      .setInputDevicesAsync({ videoDeviceId: camId })\n      .then((deviceInfo) => {\n        this._selectedCam = deviceInfo.camera;\n      });\n  }\n  async updateSpeaker(speakerId: string): Promise<void> {\n    if (!this._wavStreamPlayer) {\n      try {\n        const dInfo = await this._daily.setOutputDeviceAsync({\n          outputDeviceId: speakerId,\n        });\n        this._selectedSpeaker = dInfo.speaker as MediaDeviceInfo;\n        this._callbacks.onSpeakerUpdated?.(this._selectedSpeaker);\n      } catch (e) {\n        console.error(\"Error setting output device\", e);\n      }\n      return;\n    }\n\n    if (speakerId !== \"default\" && this._selectedSpeaker.deviceId === speakerId)\n      return;\n\n    let sID = speakerId;\n    if (sID === \"default\") {\n      const speakers = await this.getAllSpeakers();\n      const defaultSpeaker = speakers.find((s) => s.deviceId === \"default\");\n      if (!defaultSpeaker) {\n        console.warn(\"No default speaker found\");\n        return;\n      }\n      speakers.splice(speakers.indexOf(defaultSpeaker), 1);\n      const defaultSpeakerCp = speakers.find((s) =>\n        defaultSpeaker.label.includes(s.label)\n      );\n      sID = defaultSpeakerCp?.deviceId ?? speakerId;\n    }\n    this._wavStreamPlayer?.updateSpeaker(sID).then(() => {\n      this._selectedSpeaker = { deviceId: speakerId } as MediaDeviceInfo;\n      this._callbacks.onSpeakerUpdated?.(this._selectedSpeaker);\n    });\n  }\n\n  get selectedMic(): MediaDeviceInfo | Record<string, never> {\n    return this._selectedMic;\n  }\n  get selectedCam(): MediaDeviceInfo | Record<string, never> {\n    return this._selectedCam;\n  }\n  get selectedSpeaker(): MediaDeviceInfo | Record<string, never> {\n    return this._selectedSpeaker;\n  }\n\n  async enableMic(enable: boolean): Promise<void> {\n    this._micEnabled = enable;\n    if (!this._daily.participants()?.local) return;\n    this._daily.setLocalAudio(enable);\n    if (this._mediaStreamRecorder) {\n      if (enable) {\n        if (this._mediaStreamRecorder.getStatus() === \"paused\") {\n          this._startRecording();\n        } // else, we'll record on the track-started event\n      } else {\n        if (this._mediaStreamRecorder.getStatus() === \"recording\") {\n          this._mediaStreamRecorder.pause();\n        }\n      }\n    }\n  }\n  enableCam(enable: boolean): void {\n    this._camEnabled = enable;\n    this._daily.setLocalVideo(enable);\n  }\n  enableScreenShare(enable: boolean): void {\n    if (enable) {\n      this._daily.startScreenShare();\n    } else {\n      this._daily.stopScreenShare();\n    }\n  }\n\n  get isCamEnabled(): boolean {\n    return this._daily.localVideo();\n  }\n  get isMicEnabled(): boolean {\n    return this._daily.localAudio();\n  }\n  get isSharingScreen(): boolean {\n    return this._daily.localScreenAudio() || this._daily.localScreenVideo();\n  }\n\n  tracks(): Tracks {\n    const participants: DailyParticipantsObject = this._daily.participants();\n    return {\n      local: {\n        audio: participants?.local?.tracks?.audio?.persistentTrack,\n        screenAudio: participants?.local?.tracks?.screenAudio?.persistentTrack,\n        screenVideo: participants?.local?.tracks?.screenVideo?.persistentTrack,\n        video: participants?.local?.tracks?.video?.persistentTrack,\n      },\n    };\n  }\n\n  private _startRecording(): void {\n    if (!this._connected || !this._mediaStreamRecorder) return;\n    try {\n      this._mediaStreamRecorder.record((data) => {\n        this._userAudioCallback(data.mono);\n      }, this._recorderChunkSize);\n    } catch (e) {\n      const err = e as Error;\n      if (!err.message.includes(\"Already recording\")) {\n        console.error(\"Error starting recording\", e);\n      }\n    }\n  }\n\n  private _handleAvailableDevicesUpdated(\n    event: DailyEventObjectAvailableDevicesUpdated\n  ) {\n    this._callbacks.onAvailableCamsUpdated?.(\n      event.availableDevices.filter((d) => d.kind === \"videoinput\")\n    );\n    this._callbacks.onAvailableMicsUpdated?.(\n      event.availableDevices.filter((d) => d.kind === \"audioinput\")\n    );\n    this._callbacks.onAvailableSpeakersUpdated?.(\n      event.availableDevices.filter((d) => d.kind === \"audiooutput\")\n    );\n    if (this._selectedSpeaker.deviceId === \"default\") {\n      this.updateSpeaker(\"default\");\n    }\n  }\n\n  private _handleSelectedDevicesUpdated(\n    event: DailyEventObjectSelectedDevicesUpdated\n  ) {\n    if (this._selectedCam?.deviceId !== event.devices.camera) {\n      this._selectedCam = event.devices.camera;\n      this._callbacks.onCamUpdated?.(event.devices.camera as MediaDeviceInfo);\n    }\n    if (this._selectedMic?.deviceId !== event.devices.mic) {\n      this._selectedMic = event.devices.mic;\n      this._callbacks.onMicUpdated?.(event.devices.mic as MediaDeviceInfo);\n    }\n  }\n\n  private handleDeviceError(ev: DailyEventObjectCameraError) {\n    const generateDeviceError = (\n      error: DailyCameraErrorObject<DailyCameraErrorType>\n    ) => {\n      const devices: DeviceArray = [];\n      switch (error.type) {\n        case \"permissions\": {\n          error.blockedMedia.forEach((d) => {\n            devices.push(d === \"video\" ? \"cam\" : \"mic\");\n          });\n          return new DeviceError(devices, error.type, error.msg, {\n            blockedBy: error.blockedBy,\n          });\n        }\n        case \"not-found\": {\n          error.missingMedia.forEach((d) => {\n            devices.push(d === \"video\" ? \"cam\" : \"mic\");\n          });\n          return new DeviceError(devices, error.type, error.msg);\n        }\n        case \"constraints\": {\n          error.failedMedia.forEach((d) => {\n            devices.push(d === \"video\" ? \"cam\" : \"mic\");\n          });\n          return new DeviceError(devices, error.type, error.msg, {\n            reason: error.reason,\n          });\n        }\n        case \"cam-in-use\": {\n          devices.push(\"cam\");\n          return new DeviceError(devices, \"in-use\", error.msg);\n        }\n        case \"mic-in-use\": {\n          devices.push(\"mic\");\n          return new DeviceError(devices, \"in-use\", error.msg);\n        }\n        case \"cam-mic-in-use\": {\n          devices.push(\"cam\");\n          devices.push(\"mic\");\n          return new DeviceError(devices, \"in-use\", error.msg);\n        }\n        case \"undefined-mediadevices\":\n        case \"unknown\":\n        default: {\n          devices.push(\"cam\");\n          devices.push(\"mic\");\n          return new DeviceError(devices, error.type, error.msg);\n        }\n      }\n    };\n    this._callbacks.onDeviceError?.(generateDeviceError(ev.error));\n  }\n\n  private _handleLocalAudioLevel(ev: DailyEventObjectLocalAudioLevel) {\n    this._callbacks.onLocalAudioLevel?.(ev.audioLevel);\n  }\n\n  private _handleRemoteAudioLevel(audioLevel: number) {\n    this._callbacks.onRemoteAudioLevel?.(audioLevel, botParticipant());\n  }\n\n  protected async handleTrackStarted(event: DailyEventObjectTrack) {\n    if (!event.participant?.local) return;\n    if (event.track.kind === \"audio\") {\n      if (this._mediaStreamRecorder) {\n        const status = this._mediaStreamRecorder.getStatus();\n        switch (status) {\n          case \"ended\":\n            try {\n              await this._mediaStreamRecorder.begin(event.track);\n              if (this._connected) {\n                this._startRecording();\n                if (this._connectResolve) {\n                  this._connectResolve();\n                  this._connectResolve = null;\n                }\n              }\n            } catch (e) {\n              // void. nothing to do.\n            }\n            break;\n          case \"paused\":\n            this._startRecording();\n            break;\n          case \"recording\":\n          default:\n            if (this._currentAudioTrack !== event.track) {\n              await this._mediaStreamRecorder.end();\n              try {\n                await this._mediaStreamRecorder.begin(event.track);\n                this._startRecording();\n              } catch (e) {\n                // void. nothing to do.\n              }\n            } else {\n              console.warn(\n                \"track-started event received for current track and already recording\"\n              );\n            }\n            break;\n        }\n      }\n      this._currentAudioTrack = event.track;\n    }\n    this._callbacks.onTrackStarted?.(\n      event.track,\n      event.participant\n        ? dailyParticipantToParticipant(event.participant)\n        : undefined\n    );\n    this.onTrackStartedCallback?.(event);\n  }\n\n  protected handleTrackStopped(event: DailyEventObjectTrack) {\n    if (!event.participant?.local) return;\n    if (event.track.kind === \"audio\") {\n      if (\n        this._mediaStreamRecorder &&\n        this._mediaStreamRecorder.getStatus() === \"recording\"\n      ) {\n        this._mediaStreamRecorder.pause();\n      }\n    }\n    this._callbacks.onTrackStopped?.(\n      event.track,\n      event.participant\n        ? dailyParticipantToParticipant(event.participant)\n        : undefined\n    );\n    this.onTrackStoppedCallback?.(event);\n  }\n}\n\nconst dailyParticipantToParticipant = (p: DailyParticipant): Participant => ({\n  id: p.user_id,\n  local: p.local,\n  name: p.user_name,\n});\n\nconst botParticipant = () => ({\n  id: \"bot\",\n  local: false,\n  name: \"Bot\",\n});\n","import cloneDeep from \"lodash/cloneDeep\";\nimport {\n  logger,\n  makeRequest,\n  RTVIError,\n  RTVIMessage,\n  PipecatClientOptions,\n  Tracks,\n  Transport,\n  TransportStartError,\n  TransportState,\n  UnsupportedFeatureError,\n  APIRequest,\n  isAPIRequest,\n} from \"@pipecat-ai/client-js\";\nimport { MediaManager } from \"../../../lib/media-mgmt/mediaManager\";\nimport { DailyMediaManager } from \"../../../lib/media-mgmt/dailyMediaManager\";\n\nclass TrackStatusMessage {\n  type = \"trackStatus\";\n  receiver_index: number;\n  enabled: boolean;\n  constructor(receiver_index: number, enabled: boolean) {\n    this.receiver_index = receiver_index;\n    this.enabled = enabled;\n  }\n}\n\nclass WebRTCTrack {\n  track: MediaStreamTrack;\n  status: \"new\" | \"muted\" | \"unmuted\" | \"ended\";\n\n  constructor(track: MediaStreamTrack) {\n    this.track = track;\n    this.status = \"new\";\n  }\n}\n\nexport type IceConfig = {\n  iceServers?: RTCIceServer[];\n};\n\nexport type SmallWebRTCTransportConnectionOptions = {\n  /** @deprecated Use webrtcRequestParams instead */\n  connectionUrl?: string;\n  /** @deprecated Use webrtcRequestParams instead */\n  webrtcUrl?: string;\n  webrtcRequestParams?: APIRequest;\n  iceConfig?: IceConfig;\n};\n\nexport interface SmallWebRTCTransportConstructorOptions\n  extends SmallWebRTCTransportConnectionOptions {\n  iceServers?: RTCIceServer[];\n  waitForICEGathering?: boolean;\n  audioCodec?: string;\n  videoCodec?: string;\n  mediaManager?: MediaManager;\n  offerUrlTemplate?: string;\n}\n\nconst RENEGOTIATE_TYPE = \"renegotiate\";\nclass RenegotiateMessage {\n  type = RENEGOTIATE_TYPE;\n}\n\nconst PEER_LEFT_TYPE = \"peerLeft\";\nclass PeerLeftMessageMessage {\n  type = PEER_LEFT_TYPE;\n}\n\ntype OutboundSignallingMessage = TrackStatusMessage;\n\ntype InboundSignallingMessage = RenegotiateMessage | PeerLeftMessageMessage;\n\n// Interface for the structure of the signalling message\nconst SIGNALLING_TYPE = \"signalling\";\nclass SignallingMessageObject {\n  type: typeof SIGNALLING_TYPE = SIGNALLING_TYPE;\n  message: InboundSignallingMessage | OutboundSignallingMessage;\n  constructor(message: InboundSignallingMessage | OutboundSignallingMessage) {\n    this.message = message;\n  }\n}\n\nconst AUDIO_TRANSCEIVER_INDEX = 0;\nconst VIDEO_TRANSCEIVER_INDEX = 1;\nconst SCREEN_VIDEO_TRANSCEIVER_INDEX = 2;\n\n/**\n * SmallWebRTCTransport is a class that provides a client-side\n * interface for connecting to the SmallWebRTCTransport provided by Pipecat\n */\nexport class SmallWebRTCTransport extends Transport {\n  public static SERVICE_NAME = \"small-webrtc-transport\";\n\n  private _webrtcRequest: APIRequest | null = null;\n\n  // Trigger when the peer connection is finally ready or in case it has failed all the attempts to connect\n  private _connectResolved: ((value: PromiseLike<void> | void) => void) | null =\n    null;\n  private _connectFailed: ((reason?: any) => void) | null = null;\n\n  // Utilities for audio.\n  declare private mediaManager: MediaManager;\n\n  private pc: RTCPeerConnection | null = null;\n  private dc: RTCDataChannel | null = null;\n  private audioCodec: string | null | \"default\" = null;\n  private videoCodec: string | null | \"default\" = null;\n  private pc_id: string | null = null;\n  private offerUrlTemplate: string | null = null;\n\n  private reconnectionAttempts = 0;\n  private maxReconnectionAttempts = 3;\n  private isReconnecting = false;\n  private keepAliveInterval: number | null = null;\n\n  private _iceServers: RTCIceServer[] = [];\n  private readonly _waitForICEGathering: boolean;\n\n  private _incomingTracks: Map<string, WebRTCTrack> = new Map();\n\n  private _canSendIceCandidates: boolean = false;\n  private _candidateQueue: RTCIceCandidate[] = [];\n  private __flushTimeout: ReturnType<typeof setTimeout> | null = null;\n  private _flushDelay = 200;\n\n  constructor(opts: SmallWebRTCTransportConstructorOptions = {}) {\n    super();\n    this._iceServers = opts.iceServers ?? [];\n    this._waitForICEGathering = opts.waitForICEGathering ?? false;\n    this.audioCodec = opts.audioCodec ?? null;\n    this.videoCodec = opts.videoCodec ?? null;\n    this.offerUrlTemplate = opts.offerUrlTemplate ?? null;\n\n    this._webrtcRequest = this._resolveRequestInfo(opts);\n\n    this.mediaManager =\n      opts.mediaManager ||\n      new DailyMediaManager(\n        false,\n        false,\n        async (event) => {\n          if (!this.pc) {\n            return;\n          }\n          if (event.type == \"audio\") {\n            logger.info(\"SmallWebRTCMediaManager replacing audio track\");\n            await this.getAudioTransceiver().sender.replaceTrack(event.track);\n          } else if (event.type == \"video\") {\n            logger.info(\"SmallWebRTCMediaManager replacing video track\");\n            await this.getVideoTransceiver().sender.replaceTrack(event.track);\n          } else if (event.type == \"screenVideo\") {\n            logger.info(\"SmallWebRTCMediaManager replacing screen video track\");\n            await this.getScreenVideoTransceiver().sender.replaceTrack(\n              event.track\n            );\n          } else if (event.type == \"screenAudio\") {\n            logger.info(\n              \"SmallWebRTCMediaManager does not yet support screen audio. Track is ignored.\"\n            );\n          }\n        },\n        (event) => logger.debug(\"SmallWebRTCMediaManager Track stopped:\", event)\n      );\n  }\n\n  public initialize(\n    options: PipecatClientOptions,\n    messageHandler: (ev: RTVIMessage) => void\n  ): void {\n    this._options = options;\n    this._callbacks = options.callbacks ?? {};\n    this._onMessage = messageHandler;\n    this.mediaManager.setClientOptions(options);\n\n    this.state = \"disconnected\";\n    logger.debug(\"[RTVI Transport] Initialized\");\n  }\n\n  async initDevices() {\n    this.state = \"initializing\";\n    await this.mediaManager.initialize();\n    this.state = \"initialized\";\n  }\n\n  setAudioCodec(audioCodec: string | null): void {\n    this.audioCodec = audioCodec;\n  }\n\n  setVideoCodec(videoCodec: string | null): void {\n    this.videoCodec = videoCodec;\n  }\n\n  _resolveRequestInfo(\n    params:\n      | SmallWebRTCTransportConstructorOptions\n      | SmallWebRTCTransportConnectionOptions\n  ): APIRequest | null {\n    let requestInfo: APIRequest | null = null;\n    const _webrtcUrl = params.webrtcUrl ?? params.connectionUrl ?? null;\n    if (_webrtcUrl) {\n      const key = params.webrtcUrl ? \"webrtcUrl\" : \"connectionUrl\";\n      logger.warn(`${key} is deprecated. Use webrtcRequestParams instead.`);\n      if (params.webrtcRequestParams) {\n        logger.warn(\n          `Both ${key} and webrtcRequestParams provided. Using webrtcRequestParams.`\n        );\n      } else {\n        if (typeof _webrtcUrl === \"string\") {\n          requestInfo = { endpoint: _webrtcUrl };\n        } else {\n          logger.error(`Invalid ${key} provided in params. Ignoring.`);\n        }\n      }\n    }\n    if (params.webrtcRequestParams) {\n      if (isAPIRequest(params.webrtcRequestParams)) {\n        // Override any previous request set in the constructor, do not try to merge\n        requestInfo = params.webrtcRequestParams;\n      } else {\n        logger.error(\n          `Invalid webrtcRequestParams provided in params. Ignoring.`\n        );\n      }\n    }\n    return requestInfo ?? this._webrtcRequest;\n  }\n\n  _getStartEndpointAsString(): string | undefined {\n    const startEndpoint = this.startBotParams?.endpoint;\n    switch (typeof startEndpoint) {\n      case \"string\":\n        return startEndpoint;\n      case \"object\":\n        if (startEndpoint instanceof URL) {\n          return startEndpoint.toString();\n        }\n        if (\n          typeof Request !== \"undefined\" &&\n          startEndpoint instanceof Request\n        ) {\n          return startEndpoint.url;\n        }\n    }\n    return;\n  }\n\n  private _isValidObject(value: unknown): value is object {\n    if (value === null || value === undefined) return false;\n    if (typeof value !== \"object\") {\n      throw new RTVIError(\"Invalid connection parameters\");\n    }\n    return true;\n  }\n\n  private _fixConnectionOptionsParams(\n    params: Record<string, any>,\n    supportedKeys: string[]\n  ): SmallWebRTCTransportConnectionOptions {\n    const snakeToCamel = (snakeCaseString: string) => {\n      return snakeCaseString.replace(/_([a-z,A-Z])/g, (_, letter) =>\n        letter.toUpperCase()\n      );\n    };\n\n    let result: SmallWebRTCTransportConnectionOptions = {};\n    let sessionId;\n    for (const [key, val] of Object.entries(params)) {\n      const camelKey = snakeToCamel(key);\n      if (camelKey === \"sessionId\") {\n        sessionId = val;\n        continue;\n      }\n      if (!supportedKeys.includes(camelKey)) {\n        logger.warn(`Unrecognized connection parameter: ${key}. Ignored.`);\n        continue;\n      }\n      result[camelKey as keyof SmallWebRTCTransportConnectionOptions] =\n        val as any;\n    }\n\n    if (sessionId && this._shouldUseStartBotFallback(result)) {\n      result.webrtcRequestParams =\n        this._buildRequestParamsBasedOnStartBotParams(sessionId);\n    }\n\n    return result;\n  }\n\n  private _shouldUseStartBotFallback(\n    options: SmallWebRTCTransportConnectionOptions\n  ): boolean {\n    const hasStartEndpoint = !!this._getStartEndpointAsString();\n\n    const hasNoConnectionParams =\n      !options.webrtcUrl &&\n      !options.connectionUrl &&\n      !options.webrtcRequestParams;\n\n    return hasStartEndpoint && hasNoConnectionParams;\n  }\n\n  private _buildRequestParamsBasedOnStartBotParams(\n    sessionId: string\n  ): APIRequest {\n    const startEndpoint = this._getStartEndpointAsString()!;\n    const offerUrl = this.offerUrlTemplate\n      ? this.offerUrlTemplate.replace(\":sessionId\", sessionId)\n      : startEndpoint.replace(\"/start\", `/sessions/${sessionId}/api/offer`);\n    if (\n      typeof Request !== \"undefined\" &&\n      this.startBotParams!.endpoint instanceof Request\n    ) {\n      return {\n        endpoint: new Request(offerUrl, this.startBotParams?.endpoint),\n      };\n    }\n    return {\n      endpoint: offerUrl,\n      headers: this.startBotParams!.headers,\n    };\n  }\n\n  _validateConnectionParams(\n    connectParams: unknown\n  ): SmallWebRTCTransportConnectionOptions | undefined {\n    if (!this._isValidObject(connectParams)) return undefined;\n\n    const params = connectParams as Record<string, any>;\n\n    const supportedKeys = [\n      \"webrtcUrl\",\n      \"connectionUrl\",\n      \"webrtcRequestParams\",\n      \"iceConfig\",\n    ];\n\n    const fixedParams = this._fixConnectionOptionsParams(params, supportedKeys);\n    const webrtcRequestParams = this._resolveRequestInfo(fixedParams);\n    if (webrtcRequestParams) {\n      fixedParams.webrtcRequestParams = webrtcRequestParams;\n    }\n    delete fixedParams.connectionUrl;\n    delete fixedParams.webrtcUrl;\n\n    if (Object.keys(fixedParams).length === 0) {\n      return undefined;\n    }\n    return fixedParams;\n  }\n\n  async _connect(\n    connectParams?: SmallWebRTCTransportConnectionOptions\n  ): Promise<void> {\n    if (this._abortController?.signal.aborted) return;\n\n    this.state = \"connecting\";\n\n    if (connectParams?.iceConfig?.iceServers) {\n      this._iceServers = connectParams?.iceConfig?.iceServers;\n    }\n\n    // Note: There is no need to validate the params here, as they were already\n    //       validated and fixed in the parent class's connect() method (which calls\n    //       _validateConnectionParams() and passes the result to _connect()).\n    this._webrtcRequest =\n      connectParams?.webrtcRequestParams ?? this._webrtcRequest;\n    if (!this._webrtcRequest) {\n      logger.error(\"No request details provided for WebRTC connection\");\n      this.state = \"error\";\n      throw new TransportStartError();\n    }\n\n    await this.mediaManager.connect();\n\n    await this.startNewPeerConnection();\n\n    if (this._abortController?.signal.aborted) return;\n\n    if (this.dc?.readyState !== \"open\") {\n      // Wait until we are actually connected and the data channel is ready\n      await new Promise<void>((resolve, reject) => {\n        this._connectResolved = resolve;\n        this._connectFailed = reject;\n      });\n    }\n\n    this.state = \"connected\";\n    this._callbacks.onConnected?.();\n  }\n\n  private syncTrackStatus() {\n    // Sending the current status from the tracks to Pipecat\n    this.sendSignallingMessage(\n      new TrackStatusMessage(\n        AUDIO_TRANSCEIVER_INDEX,\n        this.mediaManager.isMicEnabled\n      )\n    );\n    this.sendSignallingMessage(\n      new TrackStatusMessage(\n        VIDEO_TRANSCEIVER_INDEX,\n        this.mediaManager.isCamEnabled\n      )\n    );\n    if (this.mediaManager.supportsScreenShare) {\n      this.sendSignallingMessage(\n        new TrackStatusMessage(\n          SCREEN_VIDEO_TRANSCEIVER_INDEX,\n          this.mediaManager.isSharingScreen &&\n            !!this.mediaManager.tracks().local.screenVideo\n        )\n      );\n    }\n  }\n\n  sendReadyMessage() {\n    this.state = \"ready\";\n    // Sending message that the client is ready, just for testing\n    //this.dc?.send(JSON.stringify({id: 'clientReady', label: 'rtvi-ai', type:'client-ready'}))\n    this.sendMessage(RTVIMessage.clientReady());\n  }\n\n  sendMessage(message: RTVIMessage) {\n    if (!this.dc || this.dc.readyState !== \"open\") {\n      logger.warn(`Datachannel is not ready. Message not sent: ${message}`);\n      return;\n    }\n    this.dc?.send(JSON.stringify(message));\n  }\n\n  private sendSignallingMessage(message: OutboundSignallingMessage) {\n    if (!this.dc || this.dc.readyState !== \"open\") {\n      logger.warn(`Datachannel is not ready. Message not sent: ${message}`);\n      return;\n    }\n    const signallingMessage = new SignallingMessageObject(message);\n    this.dc?.send(JSON.stringify(signallingMessage));\n  }\n\n  async _disconnect(): Promise<void> {\n    this.state = \"disconnecting\";\n    await this.stop();\n    this.state = \"disconnected\";\n  }\n\n  private createPeerConnection(): RTCPeerConnection {\n    const config: RTCConfiguration = {\n      iceServers: this._iceServers,\n    };\n\n    let pc = new RTCPeerConnection(config);\n\n    pc.onicecandidate = async (event) => {\n      if (event.candidate) {\n        logger.debug(\"New ICE candidate:\", event.candidate);\n        await this.sendIceCandidate(event.candidate);\n      } else {\n        logger.info(\"All ICE candidates have been sent.\");\n      }\n    };\n\n    pc.addEventListener(\"icegatheringstatechange\", () => {\n      if (\n        pc.iceGatheringState === \"complete\" &&\n        pc.iceConnectionState === \"checking\" &&\n        this._waitForICEGathering\n      ) {\n        logger.info(\n          \"Ice gathering completed and connection is still checking. Trying to reconnect.\"\n        );\n        // If ICE gathering has completed and the previous connection was still in the \"checking\" state,\n        // we will reconnect to use all the new ICE candidates.\n        void this.attemptReconnection(false);\n      }\n    });\n\n    pc.addEventListener(\"iceconnectionstatechange\", () =>\n      this.handleICEConnectionStateChange()\n    );\n\n    logger.debug(`iceConnectionState: ${pc.iceConnectionState}`);\n\n    pc.addEventListener(\"signalingstatechange\", () => {\n      logger.debug(`signalingState: ${this.pc!.signalingState}`);\n      if (this.pc!.signalingState == \"stable\") {\n        this.handleReconnectionCompleted();\n      }\n    });\n    logger.debug(`signalingState: ${pc.signalingState}`);\n\n    pc.addEventListener(\"track\", (evt: RTCTrackEvent) => {\n      const streamType = evt.transceiver\n        ? evt.transceiver.mid === \"0\"\n          ? \"microphone\"\n          : evt.transceiver.mid === \"1\"\n            ? \"camera\"\n            : \"screenVideo\"\n        : null;\n      if (!streamType) {\n        logger.warn(\"Received track without transceiver mid\", evt);\n        return;\n      }\n      logger.debug(`Received new remote track for ${streamType}`);\n      this._incomingTracks.set(streamType, new WebRTCTrack(evt.track));\n      evt.track.addEventListener(\"unmute\", () => {\n        const t = this._incomingTracks.get(streamType);\n        if (!t) return;\n        logger.debug(`Remote track unmuted: ${streamType}`);\n        t.status = \"unmuted\";\n        this._callbacks.onTrackStarted?.(evt.track);\n      });\n      evt.track.addEventListener(\"mute\", () => {\n        const t = this._incomingTracks.get(streamType);\n        if (!t || t.status !== \"unmuted\") return;\n        logger.debug(`Remote track muted: ${streamType}`);\n        t.status = \"muted\";\n        this._callbacks.onTrackStopped?.(evt.track);\n      });\n      evt.track.addEventListener(\"ended\", () => {\n        logger.debug(`Remote track ended: ${streamType}`);\n        this._callbacks.onTrackStopped?.(evt.track);\n        this._incomingTracks.delete(streamType);\n      });\n    });\n\n    return pc;\n  }\n\n  private handleICEConnectionStateChange(): void {\n    if (!this.pc) return;\n    logger.debug(`ICE Connection State: ${this.pc.iceConnectionState}`);\n\n    if (this.pc.iceConnectionState === \"failed\") {\n      logger.debug(\"ICE connection failed, attempting restart.\");\n      void this.attemptReconnection(true);\n    } else if (this.pc.iceConnectionState === \"disconnected\") {\n      // Waiting before trying to reconnect to see if it handles it automatically\n      setTimeout(() => {\n        if (this.pc?.iceConnectionState === \"disconnected\") {\n          logger.debug(\"Still disconnected, attempting reconnection.\");\n          void this.attemptReconnection(true);\n        }\n      }, 5000);\n    }\n  }\n\n  private handleReconnectionCompleted() {\n    this.reconnectionAttempts = 0;\n    this.isReconnecting = false;\n  }\n\n  private async attemptReconnection(\n    recreatePeerConnection: boolean = false\n  ): Promise<void> {\n    if (this.isReconnecting) {\n      logger.debug(\"Reconnection already in progress, skipping.\");\n      return;\n    }\n    if (this.reconnectionAttempts >= this.maxReconnectionAttempts) {\n      logger.debug(\"Max reconnection attempts reached. Stopping transport.\");\n      await this.stop();\n      return;\n    }\n    this.isReconnecting = true;\n    this.reconnectionAttempts++;\n    logger.debug(`Reconnection attempt ${this.reconnectionAttempts}...`);\n    // aiortc does not seem to work when just trying to restart the ice\n    // so for this case we create a new peer connection on both sides\n    if (recreatePeerConnection) {\n      const oldPC = this.pc;\n      await this.startNewPeerConnection(recreatePeerConnection);\n      if (oldPC) {\n        logger.debug(\"closing old peer connection\");\n        this.closePeerConnection(oldPC);\n      }\n    } else {\n      await this.negotiate();\n    }\n  }\n\n  private async waitForIceGatheringComplete(timeoutMs = 2000): Promise<void> {\n    const pc = this.pc!;\n    if (pc.iceGatheringState === \"complete\") return;\n\n    logger.info(\n      \"Waiting for ICE gathering to complete. Current state:\",\n      pc.iceGatheringState\n    );\n\n    return new Promise<void>((resolve) => {\n      let timeoutId: ReturnType<typeof setTimeout>;\n      const cleanup = () => {\n        pc.removeEventListener(\"icegatheringstatechange\", checkState);\n        clearTimeout(timeoutId);\n      };\n      const checkState = () => {\n        logger.debug(\"icegatheringstatechange:\", pc.iceGatheringState);\n        if (pc.iceGatheringState === \"complete\") {\n          cleanup();\n          resolve();\n        }\n      };\n      const onTimeout = () => {\n        logger.debug(`ICE gathering timed out after ${timeoutMs} ms.`);\n        cleanup();\n        resolve();\n      };\n      pc.addEventListener(\"icegatheringstatechange\", checkState);\n      timeoutId = setTimeout(onTimeout, timeoutMs);\n      // Checking the state again to avoid race conditions\n      checkState();\n    });\n  }\n\n  private async sendIceCandidate(candidate: RTCIceCandidate): Promise<void> {\n    if (!this._webrtcRequest) {\n      logger.error(\"No request details provided for WebRTC connection\");\n      return;\n    }\n    this._candidateQueue.push(candidate);\n    // We are sending all the ice candidates each 200ms\n    if (!this.__flushTimeout) {\n      this.__flushTimeout = setTimeout(\n        () => this.flushIceCandidates(),\n        this._flushDelay\n      );\n    }\n  }\n\n  private async flushIceCandidates(): Promise<void> {\n    this.__flushTimeout = null;\n    if (\n      !this._webrtcRequest ||\n      this._candidateQueue.length === 0 ||\n      !this._canSendIceCandidates\n    )\n      return;\n\n    // Drain queue\n    const candidates = this._candidateQueue.splice(\n      0,\n      this._candidateQueue.length\n    );\n\n    let headers;\n    try {\n      if (\n        typeof Request !== \"undefined\" &&\n        this._webrtcRequest.endpoint instanceof Request\n      ) {\n        console.log(\"Using Request object headers\");\n        headers = this._webrtcRequest.endpoint.headers;\n      } else {\n        headers = new Headers({\n          \"Content-Type\": \"application/json\",\n          ...Object.fromEntries(\n            (this._webrtcRequest.headers ?? new Headers()).entries()\n          ),\n        });\n      }\n\n      const payload = {\n        pc_id: this.pc_id,\n        candidates: candidates.map((c) => ({\n          candidate: c.candidate,\n          sdp_mid: c.sdpMid,\n          sdp_mline_index: c.sdpMLineIndex,\n        })),\n      };\n\n      await fetch(this._webrtcRequest.endpoint, {\n        method: \"PATCH\",\n        headers,\n        body: JSON.stringify(payload),\n      });\n    } catch (e) {\n      logger.error(`Failed to send ICE candidate: ${e}`);\n    }\n  }\n\n  private async negotiate(\n    recreatePeerConnection: boolean = false\n  ): Promise<void> {\n    if (!this.pc) {\n      return Promise.reject(\"Peer connection is not initialized\");\n    }\n    if (!this._webrtcRequest) {\n      logger.error(\"No request details provided for WebRTC connection\");\n      this.state = \"error\";\n      throw new TransportStartError();\n    }\n\n    try {\n      // Create offer\n      const offer = await this.pc.createOffer();\n      await this.pc.setLocalDescription(offer);\n\n      // Wait for ICE gathering to complete\n      if (this._waitForICEGathering) {\n        await this.waitForIceGatheringComplete();\n      }\n\n      let offerSdp = this.pc!.localDescription!;\n      // Filter audio codec\n      if (this.audioCodec && this.audioCodec !== \"default\") {\n        // @ts-ignore\n        offerSdp.sdp = this.sdpFilterCodec(\n          \"audio\",\n          this.audioCodec,\n          offerSdp.sdp\n        );\n      }\n      // Filter video codec\n      if (this.videoCodec && this.videoCodec !== \"default\") {\n        // @ts-ignore\n        offerSdp.sdp = this.sdpFilterCodec(\n          \"video\",\n          this.videoCodec,\n          offerSdp.sdp\n        );\n      }\n\n      logger.debug(`Will create offer for peerId: ${this.pc_id}`);\n\n      // Send offer to server\n      const requestData: {\n        sdp: string;\n        type: string;\n        pc_id: string | null;\n        restart_pc: boolean;\n        requestData?: any;\n      } = {\n        sdp: offerSdp.sdp,\n        type: offerSdp.type as string,\n        pc_id: this.pc_id,\n        restart_pc: recreatePeerConnection,\n      };\n      let request: APIRequest;\n      if (\n        typeof Request !== \"undefined\" &&\n        this._webrtcRequest.endpoint instanceof Request\n      ) {\n        request = {\n          endpoint: new Request(this._webrtcRequest.endpoint, {\n            body: JSON.stringify(requestData),\n          }),\n        };\n      } else {\n        request = cloneDeep(this._webrtcRequest);\n        if (this._webrtcRequest.requestData) {\n          requestData.requestData = this._webrtcRequest.requestData;\n        }\n        request.requestData = requestData;\n      }\n      const answer: RTCSessionDescriptionInit = (await makeRequest(\n        request\n      )) as RTCSessionDescriptionInit;\n\n      // @ts-ignore\n      this.pc_id = answer.pc_id;\n      // @ts-ignore\n      logger.debug(`Received answer for peer connection id ${answer.pc_id}`);\n      await this.pc!.setRemoteDescription(answer);\n    } catch (e) {\n      logger.debug(\n        `Reconnection attempt ${this.reconnectionAttempts} failed: ${e}`\n      );\n      this.isReconnecting = false;\n      setTimeout(() => this.attemptReconnection(true), 2000);\n    }\n  }\n\n  private addInitialTransceivers() {\n    // Transceivers always appear in creation-order for both peers\n    // For now we support 3 transceivers meant to hold the following\n    // tracks in the given order:\n    // audio, video, screenVideo\n    this.pc!.addTransceiver(\"audio\", { direction: \"sendrecv\" });\n    this.pc!.addTransceiver(\"video\", { direction: \"sendrecv\" });\n    if (this.mediaManager.supportsScreenShare) {\n      // For now, we only support receiving a single video track\n      this.pc!.addTransceiver(\"video\", { direction: \"sendonly\" });\n    }\n  }\n\n  private getAudioTransceiver() {\n    // Transceivers always appear in creation-order for both peers\n    // Look at addInitialTransceivers\n    return this.pc!.getTransceivers()[AUDIO_TRANSCEIVER_INDEX];\n  }\n\n  private getVideoTransceiver() {\n    // Transceivers always appear in creation-order for both peers\n    // Look at addInitialTransceivers\n    return this.pc!.getTransceivers()[VIDEO_TRANSCEIVER_INDEX];\n  }\n\n  private getScreenVideoTransceiver() {\n    // Transceivers always appear in creation-order for both peers\n    // Look at addInitialTransceivers\n    return this.pc!.getTransceivers()[SCREEN_VIDEO_TRANSCEIVER_INDEX];\n  }\n\n  private async startNewPeerConnection(\n    recreatePeerConnection: boolean = false\n  ) {\n    this.pc = this.createPeerConnection();\n    this.addInitialTransceivers();\n    this.dc = this.createDataChannel(\"chat\", { ordered: true });\n    await this.addUserMedia();\n    await this.negotiate(recreatePeerConnection);\n    // Sending the ice candidates\n    this._canSendIceCandidates = true;\n    await this.flushIceCandidates();\n  }\n\n  private async addUserMedia(): Promise<void> {\n    logger.debug(`addUserMedia this.tracks(): ${this.tracks()}`);\n\n    let audioTrack = this.tracks().local.audio;\n    logger.debug(`addUserMedia audioTrack: ${audioTrack}`);\n    if (audioTrack) {\n      await this.getAudioTransceiver().sender.replaceTrack(audioTrack);\n    }\n\n    let videoTrack = this.tracks().local.video;\n    logger.debug(`addUserMedia videoTrack: ${videoTrack}`);\n    if (videoTrack) {\n      await this.getVideoTransceiver().sender.replaceTrack(videoTrack);\n    }\n\n    if (this.mediaManager.supportsScreenShare) {\n      videoTrack = this.tracks().local.screenVideo;\n      logger.debug(`addUserMedia screenVideoTrack: ${videoTrack}`);\n      if (videoTrack) {\n        await this.getScreenVideoTransceiver().sender.replaceTrack(videoTrack);\n      }\n    }\n  }\n\n  // Method to handle a general message (this can be expanded for other types of messages)\n  handleMessage(message: string): void {\n    try {\n      const messageObj = JSON.parse(message); // Type is `any` initially\n      logger.debug(\"received message:\", messageObj);\n\n      // Check if it's a signalling message\n      if (messageObj.type === SIGNALLING_TYPE) {\n        void this.handleSignallingMessage(\n          messageObj as SignallingMessageObject\n        ); // Delegate to handleSignallingMessage\n      } else {\n        // Bubble any messages with rtvi-ai label\n        if (messageObj.label === \"rtvi-ai\") {\n          this._onMessage({\n            id: messageObj.id,\n            type: messageObj.type,\n            data: messageObj.data,\n          } as RTVIMessage);\n        }\n      }\n    } catch (error) {\n      logger.error(\"Failed to parse JSON message:\", error);\n    }\n  }\n\n  // Method to handle signalling messages specifically\n  async handleSignallingMessage(\n    messageObj: SignallingMessageObject\n  ): Promise<void> {\n    // Cast the object to the correct type after verification\n    const signallingMessage = messageObj as SignallingMessageObject;\n\n    // Handle different signalling message types\n    switch (signallingMessage.message.type) {\n      case RENEGOTIATE_TYPE:\n        void this.attemptReconnection(false);\n        break;\n      case PEER_LEFT_TYPE:\n        void this.disconnect();\n        break;\n      default:\n        logger.warn(\"Unknown signalling message:\", signallingMessage.message);\n    }\n  }\n\n  private createDataChannel(\n    label: string,\n    options: RTCDataChannelInit\n  ): RTCDataChannel {\n    const dc = this.pc!.createDataChannel(label, options);\n\n    dc.addEventListener(\"close\", () => {\n      logger.debug(\"datachannel closed\");\n      if (this.keepAliveInterval) {\n        clearInterval(this.keepAliveInterval);\n        this.keepAliveInterval = null;\n      }\n    });\n\n    dc.addEventListener(\"open\", () => {\n      logger.debug(\"datachannel opened\");\n      if (this._connectResolved) {\n        this.syncTrackStatus();\n        this._connectResolved();\n        this._connectResolved = null;\n        this._connectFailed = null;\n      }\n      // @ts-ignore\n      this.keepAliveInterval = setInterval(() => {\n        const message = \"ping: \" + new Date().getTime();\n        dc.send(message);\n      }, 1000);\n    });\n\n    dc.addEventListener(\"message\", (evt: MessageEvent) => {\n      let message = evt.data;\n      this.handleMessage(message);\n    });\n\n    return dc;\n  }\n\n  private closePeerConnection(pc: RTCPeerConnection) {\n    pc.getTransceivers().forEach((transceiver) => {\n      if (transceiver.stop) {\n        transceiver.stop();\n      }\n    });\n\n    pc.getSenders().forEach((sender) => {\n      sender.track?.stop();\n    });\n\n    pc.close();\n  }\n\n  private async stop(): Promise<void> {\n    if (!this.pc) {\n      logger.debug(\"Peer connection is already closed or null.\");\n      return;\n    }\n\n    if (this.dc) {\n      this.dc.close();\n    }\n\n    this.closePeerConnection(this.pc);\n    this.pc = null;\n\n    await this.mediaManager.disconnect();\n\n    // For some reason after we close the peer connection, it is not triggering the listeners\n    this.pc_id = null;\n    this.reconnectionAttempts = 0;\n    this.isReconnecting = false;\n    this._callbacks.onDisconnected?.();\n\n    this._candidateQueue = [];\n    this._canSendIceCandidates = false;\n\n    if (this._connectFailed) {\n      this._connectFailed();\n    }\n    this._connectFailed = null;\n    this._connectResolved = null;\n  }\n\n  getAllMics(): Promise<MediaDeviceInfo[]> {\n    return this.mediaManager.getAllMics();\n  }\n  getAllCams(): Promise<MediaDeviceInfo[]> {\n    return this.mediaManager.getAllCams();\n  }\n  getAllSpeakers(): Promise<MediaDeviceInfo[]> {\n    return this.mediaManager.getAllSpeakers();\n  }\n\n  async updateMic(micId: string): Promise<void> {\n    return this.mediaManager.updateMic(micId);\n  }\n  updateCam(camId: string): void {\n    return this.mediaManager.updateCam(camId);\n  }\n  updateSpeaker(speakerId: string): void {\n    return this.mediaManager.updateSpeaker(speakerId);\n  }\n\n  get selectedMic(): MediaDeviceInfo | Record<string, never> {\n    return this.mediaManager.selectedMic;\n  }\n  get selectedCam(): MediaDeviceInfo | Record<string, never> {\n    return this.mediaManager.selectedCam;\n  }\n  get selectedSpeaker(): MediaDeviceInfo | Record<string, never> {\n    return this.mediaManager.selectedSpeaker;\n  }\n\n  set iceServers(iceServers: RTCIceServer[]) {\n    this._iceServers = iceServers;\n  }\n\n  get iceServers() {\n    return this._iceServers;\n  }\n\n  enableMic(enable: boolean): void {\n    this.mediaManager.enableMic(enable);\n    this.sendSignallingMessage(\n      new TrackStatusMessage(AUDIO_TRANSCEIVER_INDEX, enable)\n    );\n  }\n  enableCam(enable: boolean): void {\n    this.mediaManager.enableCam(enable);\n    this.sendSignallingMessage(\n      new TrackStatusMessage(VIDEO_TRANSCEIVER_INDEX, enable)\n    );\n  }\n  async enableScreenShare(enable: boolean): Promise<void> {\n    if (!this.mediaManager.supportsScreenShare) {\n      throw new UnsupportedFeatureError(\n        \"enableScreenShare\",\n        \"mediaManager\",\n        \"Screen sharing is not supported by the current media manager\"\n      );\n    }\n    this.mediaManager.enableScreenShare(enable);\n    this.sendSignallingMessage(\n      new TrackStatusMessage(SCREEN_VIDEO_TRANSCEIVER_INDEX, enable)\n    );\n  }\n\n  get isCamEnabled(): boolean {\n    return this.mediaManager.isCamEnabled;\n  }\n  get isMicEnabled(): boolean {\n    return this.mediaManager.isMicEnabled;\n  }\n  get isSharingScreen(): boolean {\n    return this.mediaManager.isSharingScreen;\n  }\n\n  get state(): TransportState {\n    return this._state;\n  }\n\n  set state(state: TransportState) {\n    if (this._state === state) return;\n\n    this._state = state;\n    this._callbacks.onTransportStateChanged?.(state);\n  }\n\n  tracks(): Tracks {\n    return this.mediaManager.tracks();\n  }\n\n  private sdpFilterCodec(kind: string, codec: string, realSdp: string): string {\n    const allowed: number[] = [];\n    const rtxRegex = new RegExp(\"a=fmtp:(\\\\d+) apt=(\\\\d+)\\\\r$\");\n    const codecRegex = new RegExp(\n      \"a=rtpmap:([0-9]+) \" + this.escapeRegExp(codec)\n    );\n    const videoRegex = new RegExp(\"(m=\" + kind + \" .*?)( ([0-9]+))*\\\\s*$\");\n\n    const lines = realSdp.split(\"\\n\");\n\n    let isKind = false;\n    for (let i = 0; i < lines.length; i++) {\n      if (lines[i].startsWith(\"m=\" + kind + \" \")) {\n        isKind = true;\n      } else if (lines[i].startsWith(\"m=\")) {\n        isKind = false;\n      }\n\n      if (isKind) {\n        const match = lines[i].match(codecRegex);\n        if (match) {\n          allowed.push(parseInt(match[1]));\n        }\n\n        const matchRtx = lines[i].match(rtxRegex);\n        if (matchRtx && allowed.includes(parseInt(matchRtx[2]))) {\n          allowed.push(parseInt(matchRtx[1]));\n        }\n      }\n    }\n\n    const skipRegex = \"a=(fmtp|rtcp-fb|rtpmap):([0-9]+)\";\n    let sdp = \"\";\n\n    isKind = false;\n    for (let i = 0; i < lines.length; i++) {\n      if (lines[i].startsWith(\"m=\" + kind + \" \")) {\n        isKind = true;\n      } else if (lines[i].startsWith(\"m=\")) {\n        isKind = false;\n      }\n\n      if (isKind) {\n        const skipMatch = lines[i].match(skipRegex);\n        if (skipMatch && !allowed.includes(parseInt(skipMatch[2]))) {\n          continue;\n        } else if (lines[i].match(videoRegex)) {\n          sdp += lines[i].replace(videoRegex, \"$1 \" + allowed.join(\" \")) + \"\\n\";\n        } else {\n          sdp += lines[i] + \"\\n\";\n        }\n      } else {\n        sdp += lines[i] + \"\\n\";\n      }\n    }\n\n    return sdp;\n  }\n\n  private escapeRegExp(string: string): string {\n    return string.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n  }\n}\n"]}