{"version":3,"file":"audio.mjs","names":[],"sources":["../../../src/lib/utils/audio.ts"],"sourcesContent":["/**\n * Environment-neutral PCM audio primitives shared across the media battery and the on-device\n * specialist batteries.\n *\n * @module @nhtio/adk/lib/utils/audio\n *\n * @remarks\n * These are pure helpers over `Float32Array` — no DOM, no Node built-ins, no core class coupling.\n * They are shared (not duplicated) by `src/batteries/media` (which re-exports {@link resampleTo}\n * from its `image_audio` step and calls {@link downmixToMono} from its `audio_decode` engine) and\n * by `src/batteries/specialists/_shared` (whose default audio decoder downmixes before handing PCM\n * to a specialist model). The bundler inlines this module into each consumer, so sharing introduces\n * no build coupling.\n */\n\n/**\n * Linearly resamples a mono PCM buffer from one sample rate to another.\n *\n * @remarks\n * Pure resample only — no channel/downmix logic lives here (see {@link downmixToMono} for that).\n * Uses linear interpolation between the two nearest source samples for each output sample; a\n * no-op (returns `pcm` as-is) when `fromRate === toRate`.\n *\n * @param pcm - The source mono PCM samples.\n * @param fromRate - The source sample rate, in Hz.\n * @param toRate - The target sample rate, in Hz.\n * @returns The resampled mono PCM samples, `Math.floor(pcm.length * toRate / fromRate)` long.\n */\nexport const resampleTo = (pcm: Float32Array, fromRate: number, toRate: number): Float32Array => {\n  if (fromRate === toRate) return pcm\n  const ratio = fromRate / toRate\n  const outLength = Math.floor(pcm.length / ratio)\n  const out = new Float32Array(outLength)\n  for (let i = 0; i < outLength; i++) {\n    const pos = i * ratio\n    const left = Math.floor(pos)\n    const right = Math.min(left + 1, pcm.length - 1)\n    const frac = pos - left\n    out[i] = pcm[left] * (1 - frac) + pcm[right] * frac\n  }\n  return out\n}\n\n/**\n * Downmixes one or more PCM channels to a single mono channel by averaging samples across\n * channels.\n *\n * @remarks\n * A single channel is returned as-is (a copy, not the original reference — callers may safely\n * mutate the result). Two or more channels are averaged sample-by-sample: `mono[i] = mean(channels\n * .map(c => c[i]))`, using each channel's own length contribution (`data[i] / channels.length`\n * accumulated across channels) so the result is numerically identical regardless of channel order.\n *\n * @param channels - The per-channel PCM sample arrays, all the same length.\n * @returns The mono PCM samples, the same length as each input channel.\n */\nexport const downmixToMono = (channels: Float32Array[]): Float32Array => {\n  if (channels.length <= 1) return new Float32Array(channels[0] ?? [])\n  const length = channels[0].length\n  const mono = new Float32Array(length)\n  for (const data of channels) {\n    for (let i = 0; i < length; i++) mono[i] += data[i] / channels.length\n  }\n  return mono\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,IAAa,cAAc,KAAmB,UAAkB,WAAiC;CAC/F,IAAI,aAAa,QAAQ,OAAO;CAChC,MAAM,QAAQ,WAAW;CACzB,MAAM,YAAY,KAAK,MAAM,IAAI,SAAS,KAAK;CAC/C,MAAM,MAAM,IAAI,aAAa,SAAS;CACtC,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,KAAK;EAClC,MAAM,MAAM,IAAI;EAChB,MAAM,OAAO,KAAK,MAAM,GAAG;EAC3B,MAAM,QAAQ,KAAK,IAAI,OAAO,GAAG,IAAI,SAAS,CAAC;EAC/C,MAAM,OAAO,MAAM;EACnB,IAAI,KAAK,IAAI,SAAS,IAAI,QAAQ,IAAI,SAAS;CACjD;CACA,OAAO;AACT;;;;;;;;;;;;;;AAeA,IAAa,iBAAiB,aAA2C;CACvE,IAAI,SAAS,UAAU,GAAG,OAAO,IAAI,aAAa,SAAS,MAAM,CAAC,CAAC;CACnE,MAAM,SAAS,SAAS,GAAG;CAC3B,MAAM,OAAO,IAAI,aAAa,MAAM;CACpC,KAAK,MAAM,QAAQ,UACjB,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK,KAAK,MAAM,KAAK,KAAK,SAAS;CAEjE,OAAO;AACT"}