{"version":3,"sources":["../src/audio_frame.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { OwnedAudioFrameBuffer } from '@livekit/rtc-ffi-bindings';\nimport { AudioFrameBufferInfo } from '@livekit/rtc-ffi-bindings';\nimport { FfiClient, FfiHandle } from './ffi_client.js';\n\nexport class AudioFrame {\n  data: Int16Array;\n  sampleRate: number;\n  channels: number;\n  samplesPerChannel: number;\n\n  private _userdata: Record<string, unknown>;\n\n  // note: if converting from Uint8Array to Int16Array, *do not* use buffer.slice!\n  // it is marked unstable by Node and can cause undefined behaviour, such as massive chunks of\n  // noise being added to the end.\n  // it is recommended to use buffer.subarray instead.\n  // XXX(nbsp): add this when writing proper docs\n  constructor(\n    data: Int16Array,\n    sampleRate: number,\n    channels: number,\n    samplesPerChannel: number,\n    userdata: Record<string, unknown> = {},\n  ) {\n    this.data = data;\n    this.sampleRate = sampleRate;\n    this.channels = channels;\n    this.samplesPerChannel = samplesPerChannel;\n    this._userdata = userdata;\n  }\n\n  static create(\n    sampleRate: number,\n    channels: number,\n    samplesPerChannel: number,\n    userdata?: Record<string, unknown>,\n  ): AudioFrame {\n    const data = new Int16Array(channels * samplesPerChannel);\n    return new AudioFrame(data, sampleRate, channels, samplesPerChannel, userdata);\n  }\n\n  /** @internal */\n  static fromOwnedInfo(owned: OwnedAudioFrameBuffer): AudioFrame {\n    const info = owned.info!;\n    const len = info.numChannels! * info.samplesPerChannel! * 2; // c_int16\n    const data = FfiClient.instance.copyBuffer(info.dataPtr!, len);\n    new FfiHandle(owned.handle!.id!).dispose();\n    return new AudioFrame(\n      new Int16Array(data.buffer),\n      info.sampleRate!,\n      info.numChannels!,\n      info.samplesPerChannel!,\n    );\n  }\n\n  /** @internal */\n  protoInfo(): AudioFrameBufferInfo {\n    return new AudioFrameBufferInfo({\n      dataPtr: FfiClient.instance.retrievePtr(new Uint8Array(this.data.buffer)),\n      sampleRate: this.sampleRate,\n      numChannels: this.channels,\n      samplesPerChannel: this.samplesPerChannel,\n    });\n  }\n\n  /** Returns the user data associated with the audio frame. */\n  get userdata() {\n    return this._userdata;\n  }\n}\n\n/**\n * Combines one or more `rtc.AudioFrame` objects into a single `rtc.AudioFrame`.\n *\n * This function concatenates the audio data from multiple frames, ensuring that all frames have\n * the same sample rate and number of channels. It efficiently merges the data by preallocating the\n * necessary memory and copying the frame data without unnecessary reallocations.\n *\n * @param buffer - a single AudioFrame or list thereof\n */\nexport const combineAudioFrames = (buffer: AudioFrame | AudioFrame[]): AudioFrame => {\n  if (!Array.isArray(buffer)) {\n    return buffer;\n  }\n  buffer = buffer as AudioFrame[];\n\n  if (buffer.length === 0) {\n    throw new Error('buffer is empty');\n  }\n\n  const sampleRate = buffer[0]!.sampleRate;\n  const channels = buffer[0]!.channels;\n\n  let totalSamplesPerChannel = 0;\n  for (const frame of buffer) {\n    if (frame.sampleRate != sampleRate) {\n      throw new Error(`sample rate mismatch: expected ${sampleRate}, got ${frame.sampleRate}`);\n    }\n\n    if (frame.channels != channels) {\n      throw new Error(`channel mismatch: expected ${channels}, got ${frame.channels}`);\n    }\n\n    totalSamplesPerChannel += frame.samplesPerChannel;\n  }\n\n  const data = new Int16Array(buffer.map((x) => [...x.data]).flat());\n\n  // Merge userdata from all frames\n  const mergedUserdata: Record<string, unknown> = {};\n  for (const frame of buffer) {\n    Object.assign(mergedUserdata, frame.userdata);\n  }\n\n  return new AudioFrame(data, sampleRate, channels, totalSamplesPerChannel, mergedUserdata);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,8BAAqC;AACrC,wBAAqC;AAE9B,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAatB,YACE,MACA,YACA,UACA,mBACA,WAAoC,CAAC,GACrC;AACA,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,WAAW;AAChB,SAAK,oBAAoB;AACzB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,OAAO,OACL,YACA,UACA,mBACA,UACY;AACZ,UAAM,OAAO,IAAI,WAAW,WAAW,iBAAiB;AACxD,WAAO,IAAI,WAAW,MAAM,YAAY,UAAU,mBAAmB,QAAQ;AAAA,EAC/E;AAAA;AAAA,EAGA,OAAO,cAAc,OAA0C;AAC7D,UAAM,OAAO,MAAM;AACnB,UAAM,MAAM,KAAK,cAAe,KAAK,oBAAqB;AAC1D,UAAM,OAAO,4BAAU,SAAS,WAAW,KAAK,SAAU,GAAG;AAC7D,QAAI,4BAAU,MAAM,OAAQ,EAAG,EAAE,QAAQ;AACzC,WAAO,IAAI;AAAA,MACT,IAAI,WAAW,KAAK,MAAM;AAAA,MAC1B,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAAA,EACF;AAAA;AAAA,EAGA,YAAkC;AAChC,WAAO,IAAI,6CAAqB;AAAA,MAC9B,SAAS,4BAAU,SAAS,YAAY,IAAI,WAAW,KAAK,KAAK,MAAM,CAAC;AAAA,MACxE,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,mBAAmB,KAAK;AAAA,IAC1B,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,IAAI,WAAW;AACb,WAAO,KAAK;AAAA,EACd;AACF;AAWO,MAAM,qBAAqB,CAAC,WAAkD;AACnF,MAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC1B,WAAO;AAAA,EACT;AACA,WAAS;AAET,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,IAAI,MAAM,iBAAiB;AAAA,EACnC;AAEA,QAAM,aAAa,OAAO,CAAC,EAAG;AAC9B,QAAM,WAAW,OAAO,CAAC,EAAG;AAE5B,MAAI,yBAAyB;AAC7B,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,cAAc,YAAY;AAClC,YAAM,IAAI,MAAM,kCAAkC,UAAU,SAAS,MAAM,UAAU,EAAE;AAAA,IACzF;AAEA,QAAI,MAAM,YAAY,UAAU;AAC9B,YAAM,IAAI,MAAM,8BAA8B,QAAQ,SAAS,MAAM,QAAQ,EAAE;AAAA,IACjF;AAEA,8BAA0B,MAAM;AAAA,EAClC;AAEA,QAAM,OAAO,IAAI,WAAW,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC;AAGjE,QAAM,iBAA0C,CAAC;AACjD,aAAW,SAAS,QAAQ;AAC1B,WAAO,OAAO,gBAAgB,MAAM,QAAQ;AAAA,EAC9C;AAEA,SAAO,IAAI,WAAW,MAAM,YAAY,UAAU,wBAAwB,cAAc;AAC1F;","names":[]}