{"version":3,"file":"use-audio-player.cjs","names":[],"sources":["../src/use-audio-player.ts"],"sourcesContent":["/* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */\n\n\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { AudioMedia, MediaBase } from \"@langchain/langgraph-sdk/stream\";\n\n/**\n * Lifecycle state of an audio or video player returned by\n * {@link useAudioPlayer} and {@link useVideoPlayer}.\n *\n * Single-enum design (vs. multiple booleans) eliminates impossible\n * states and gives callers a clean `switch` target.\n */\nexport type PlayerStatus =\n  | \"idle\"\n  | \"buffering\"\n  | \"playing\"\n  | \"paused\"\n  | \"finished\"\n  | \"error\";\n\n/**\n * Options for {@link useAudioPlayer}.\n *\n * All fields optional; defaults cover OpenAI `gpt-4o-audio-preview`\n * pcm16 streams (`sampleRate: 24000`, `channels: 1`) and any WAV\n * stream the upstream model emits.\n */\nexport interface UseAudioPlayerOptions {\n  /**\n   * Begin playback as soon as the first byte arrives (PCM strategy)\n   * or the blob settles (`element` strategy). Subject to browser\n   * autoplay policies — on sites without prior user gesture, the\n   * underlying `play()` may be rejected and the hook transitions to\n   * `\"error\"` with a descriptive message.\n   */\n  autoPlay?: boolean;\n\n  /**\n   * Overrides for the PCM strategy. Ignored by the `element` strategy\n   * and by WAV streams (the RIFF `fmt ` chunk is authoritative there).\n   */\n  pcm?: {\n    /** Sample rate in Hz. Defaults to `24000`. */\n    sampleRate?: number;\n    /** Channel count. Defaults to `1` (mono). */\n    channels?: number;\n  };\n\n  /**\n   * Additional mime prefixes that should be treated as raw PCM16\n   * (in addition to `audio/pcm` / `audio/L16`). Use when upstream\n   * reports a custom mime like `audio/pcm16`.\n   */\n  pcmMimePrefixes?: readonly string[];\n\n  /**\n   * Force a specific playback strategy. Default `\"auto\"` picks `\"pcm\"`\n   * for PCM / L16 / WAV mime types and `\"element\"` for everything else.\n   */\n  strategy?: \"auto\" | \"pcm\" | \"element\";\n}\n\n/**\n * Player controls + live state returned by {@link useAudioPlayer}.\n *\n * Shape is shared with {@link useVideoPlayer} where possible — learn\n * one surface, use it for both.\n */\nexport interface AudioPlayerHandle {\n  /** Current lifecycle state. See {@link PlayerStatus}. */\n  status: PlayerStatus;\n\n  /**\n   * Which implementation is active. `\"pcm\"` scheduling through\n   * `AudioContext` starts within one chunk of the first byte; `\"element\"`\n   * waits for `message-finish` before a hidden `HTMLAudioElement` takes\n   * over.\n   */\n  strategy: \"pcm\" | \"element\";\n\n  /** Start (or resume) playback. No-op while `status === \"error\"`. */\n  play(): void;\n  /** Pause without discarding buffered samples / element position. */\n  pause(): void;\n  /**\n   * Hard stop: tears down the `AudioContext` (PCM) or detaches the\n   * element (`element`) and drops any scheduled work.\n   */\n  stop(): void;\n  /** Sugar for `status === \"playing\" ? pause() : play()`. */\n  toggle(): void;\n  /**\n   * Reset back to `\"idle\"` and drop any transient error. The next\n   * `play()` starts fresh from the current position.\n   */\n  reset(): void;\n  /**\n   * Resolve on the next terminal transition (`finished` | `paused` |\n   * `idle`). Reject on transitions to `\"error\"`. Calling `stop()` or\n   * `reset()` resolves the pending promise immediately. Calling\n   * `playToEnd` also triggers `play()` if currently paused/idle.\n   */\n  playToEnd(): Promise<void>;\n\n  /**\n   * Seconds of audio consumed since the current `play()` call. Resets\n   * on `reset()` and on media changes.\n   */\n  currentTime: number;\n\n  /**\n   * Total duration in seconds, when knowable. The `element` strategy\n   * exposes this once `loadedmetadata` fires; the `pcm` strategy leaves\n   * it `undefined` (PCM duration isn't known until `message-finish`).\n   */\n  duration?: number;\n\n  /**\n   * Seek to an absolute timestamp in seconds. Only defined on the\n   * `element` strategy; `undefined` on `pcm` (random-access seeking of\n   * a live scheduled buffer is not supported).\n   */\n  seek?(seconds: number): void;\n\n  /**\n   * RMS level of the last analysed frame, normalised to `[0, 1]`.\n   * Drop-in for a VU meter. `0` when no analyser frame has been read\n   * yet, when paused, or before `play()`.\n   */\n  level: number;\n\n  /**\n   * Current 256-bin frequency-domain snapshot from the internal\n   * {@link AnalyserNode}. Returns `undefined` before the graph is\n   * connected or on environments without Web Audio. Safe to poll\n   * inside `requestAnimationFrame`.\n   */\n  getFrequencyData(): Uint8Array | undefined;\n\n  /**\n   * Current 256-sample waveform snapshot (byte time-domain) from the\n   * internal {@link AnalyserNode}. Returns `undefined` before the graph\n   * is connected or on environments without Web Audio.\n   */\n  getTimeDomainData(): Uint8Array | undefined;\n\n  /** Last error raised by the stream reader, decoder, or element. */\n  error: Error | undefined;\n}\n\nconst DEFAULT_SAMPLE_RATE = 24_000;\nconst DEFAULT_CHANNELS = 1;\nconst ANALYSER_FFT_SIZE = 512;\n\n/**\n * Per-stream audio format descriptor. Populated eagerly for raw PCM\n * streams and lazily (from the RIFF `fmt ` chunk) for WAV streams.\n * `scheduleChunk` and `ensureContext` both refuse to run until this\n * has been resolved.\n */\ninterface AudioFormat {\n  readonly sampleRate: number;\n  readonly channels: number;\n  readonly bitsPerSample: number;\n}\n\n/**\n * Events fanned out from a shared pump controller to each hook\n * subscription.\n */\ntype PumpEvent =\n  | { readonly type: \"chunk\"; readonly bytes: Uint8Array }\n  | { readonly type: \"finished\" }\n  | { readonly type: \"error\"; readonly error: Error };\n\ntype PumpListener = (event: PumpEvent) => void;\n\ninterface PumpController {\n  readonly chunks: Uint8Array[];\n  finished: boolean;\n  error: Error | undefined;\n  readonly listeners: Set<PumpListener>;\n}\n\n/**\n * Module-level registry of shared readers keyed by {@link MediaBase}\n * identity. We hold exactly one {@link ReadableStreamDefaultReader} per\n * media handle and fan the chunks out to every live hook subscriber.\n *\n * Keying on identity (WeakMap) gives us three properties for free:\n * - React StrictMode's simulated unmount/remount finds the same\n *   controller on re-attach, so we never `getReader()` twice on the\n *   same locked stream.\n * - New media instances get a fresh reader — no cross-talk.\n * - When callers drop their last reference to the media handle, the\n *   WeakMap entry is reclaimed alongside it.\n */\nconst pumpRegistry = new WeakMap<MediaBase, PumpController>();\n\nfunction attachToPump(media: MediaBase, listener: PumpListener): () => void {\n  let controller = pumpRegistry.get(media);\n  if (controller == null) {\n    const reader = media.stream.getReader();\n    controller = {\n      chunks: [],\n      finished: false,\n      error: undefined,\n      listeners: new Set<PumpListener>(),\n    };\n    pumpRegistry.set(media, controller);\n    const owned = controller;\n    void (async () => {\n      try {\n        // oxlint-disable-next-line no-constant-condition\n        while (true) {\n          const { value, done } = await reader.read();\n          if (done) break;\n          if (value == null || value.byteLength === 0) continue;\n          owned.chunks.push(value);\n          for (const l of owned.listeners) {\n            try {\n              l({ type: \"chunk\", bytes: value });\n            } catch {\n              // A misbehaving listener must not take down the pump.\n            }\n          }\n        }\n        owned.finished = true;\n        for (const l of owned.listeners) {\n          try {\n            l({ type: \"finished\" });\n          } catch {\n            // Swallow — see above.\n          }\n        }\n      } catch (err) {\n        owned.error = err as Error;\n        for (const l of owned.listeners) {\n          try {\n            l({ type: \"error\", error: err as Error });\n          } catch {\n            // Swallow — see above.\n          }\n        }\n      } finally {\n        try {\n          reader.releaseLock();\n        } catch {\n          // best-effort\n        }\n      }\n    })();\n  }\n\n  // Replay buffered state so late subscribers (StrictMode remount or\n  // a downstream consumer that mounts after the stream has already\n  // delivered bytes) catch up to the current position.\n  for (const chunk of controller.chunks) {\n    listener({ type: \"chunk\", bytes: chunk });\n  }\n  if (controller.finished) listener({ type: \"finished\" });\n  if (controller.error != null) {\n    listener({ type: \"error\", error: controller.error });\n  }\n\n  controller.listeners.add(listener);\n\n  return () => {\n    controller!.listeners.delete(listener);\n  };\n}\n\ntype WavHeaderResult =\n  | { readonly status: \"need-more\" }\n  | { readonly status: \"invalid\"; readonly reason: string }\n  | {\n      readonly status: \"parsed\";\n      readonly format: AudioFormat;\n      /** Byte offset within the input where PCM samples begin. */\n      readonly dataOffset: number;\n    };\n\n/**\n * Parse the RIFF/WAVE header of a WAV stream. Only the `fmt ` and `data`\n * chunks are interpreted; other chunks are skipped. The parser\n * requires the complete `fmt ` chunk and the `data` chunk header before\n * returning `\"parsed\"`, so callers may need several retries while\n * buffering incoming bytes. WAV uses little-endian integers throughout.\n */\nfunction tryParseWavHeader(bytes: Uint8Array): WavHeaderResult {\n  if (bytes.byteLength < 12) return { status: \"need-more\" };\n\n  if (\n    bytes[0] !== 0x52 ||\n    bytes[1] !== 0x49 ||\n    bytes[2] !== 0x46 ||\n    bytes[3] !== 0x46 ||\n    bytes[8] !== 0x57 ||\n    bytes[9] !== 0x41 ||\n    bytes[10] !== 0x56 ||\n    bytes[11] !== 0x45\n  ) {\n    return { status: \"invalid\", reason: \"not a RIFF/WAVE stream\" };\n  }\n\n  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n  let fmt: {\n    audioFormat: number;\n    sampleRate: number;\n    channels: number;\n    bitsPerSample: number;\n  } | null = null;\n\n  let offset = 12;\n  while (offset + 8 <= bytes.byteLength) {\n    const id = String.fromCharCode(\n      bytes[offset]!,\n      bytes[offset + 1]!,\n      bytes[offset + 2]!,\n      bytes[offset + 3]!\n    );\n    const size = view.getUint32(offset + 4, true);\n    const payloadStart = offset + 8;\n\n    if (id === \"fmt \") {\n      if (payloadStart + 16 > bytes.byteLength) return { status: \"need-more\" };\n      fmt = {\n        audioFormat: view.getUint16(payloadStart, true),\n        channels: view.getUint16(payloadStart + 2, true),\n        sampleRate: view.getUint32(payloadStart + 4, true),\n        bitsPerSample: view.getUint16(payloadStart + 14, true),\n      };\n      if (fmt.audioFormat !== 1) {\n        return {\n          status: \"invalid\",\n          reason: `unsupported WAV audioFormat=${fmt.audioFormat} (expected 1, linear PCM)`,\n        };\n      }\n      if (fmt.bitsPerSample !== 16) {\n        return {\n          status: \"invalid\",\n          reason: `unsupported WAV bitsPerSample=${fmt.bitsPerSample} (expected 16)`,\n        };\n      }\n    } else if (id === \"data\") {\n      if (fmt == null) {\n        return { status: \"invalid\", reason: \"data chunk preceded fmt chunk\" };\n      }\n      return {\n        status: \"parsed\",\n        format: {\n          sampleRate: fmt.sampleRate,\n          channels: fmt.channels,\n          bitsPerSample: fmt.bitsPerSample,\n        },\n        dataOffset: payloadStart,\n      };\n    }\n\n    // Chunks are word-aligned: an odd-sized payload carries one pad byte.\n    offset = payloadStart + size + (size & 1);\n  }\n\n  return { status: \"need-more\" };\n}\n\nfunction concatChunks(chunks: readonly Uint8Array[]): Uint8Array {\n  if (chunks.length === 1) return chunks[0]!;\n  let total = 0;\n  for (const c of chunks) total += c.byteLength;\n  const out = new Uint8Array(total);\n  let at = 0;\n  for (const c of chunks) {\n    out.set(c, at);\n    at += c.byteLength;\n  }\n  return out;\n}\n\nfunction resolveAudioContextCtor(): typeof AudioContext | undefined {\n  if (typeof window === \"undefined\") return undefined;\n  return (\n    (window as unknown as { AudioContext?: typeof AudioContext })\n      .AudioContext ??\n    (window as unknown as { webkitAudioContext?: typeof AudioContext })\n      .webkitAudioContext\n  );\n}\n\n/**\n * Decide which playback strategy the hook should use for a handle.\n * PCM16 / L16 / WAV flow through the progressive Web Audio path; every\n * other mime drops to a hidden `HTMLAudioElement`.\n */\nfunction detectStrategy(\n  mimeType: string | undefined,\n  override: UseAudioPlayerOptions[\"strategy\"],\n  pcmPrefixes: readonly string[] | undefined\n): \"pcm\" | \"element\" {\n  if (override === \"pcm\" || override === \"element\") return override;\n  const m = mimeType ?? \"\";\n  const isPcm =\n    m === \"audio/pcm\" ||\n    m === \"audio/L16\" ||\n    m.startsWith(\"audio/pcm;\") ||\n    m.startsWith(\"audio/L16;\") ||\n    m === \"audio/wav\" ||\n    m === \"audio/wave\" ||\n    m === \"audio/x-wav\" ||\n    m === \"audio/vnd.wave\" ||\n    (pcmPrefixes?.some((p) => m.startsWith(p)) ?? false);\n  return isPcm ? \"pcm\" : \"element\";\n}\n\n/**\n * Progressive audio playback for {@link AudioMedia} handles with a\n * uniform surface across PCM (streamed) and container (`HTMLAudioElement`)\n * strategies.\n *\n * ### Behaviour\n *\n * - Strategy selection is derived from `media.mimeType` and may be\n *   overridden via `options.strategy`. PCM / L16 / WAV all flow through\n *   the progressive Web Audio path; every other mime uses a hidden\n *   `HTMLAudioElement`.\n *\n * - **PCM strategy.** Chunks are decoded in real time and scheduled on\n *   a single `AudioContext`; playback begins within one chunk of the\n *   first byte. `seek` / `duration` are `undefined` because random\n *   access on a live scheduled buffer is not supported.\n *\n * - **Element strategy.** `status` stays in `\"buffering\"` until\n *   `message-finish` materialises a blob URL; the element then owns\n *   playback. `seek` / `duration` are available.\n *\n * - Both strategies expose `level`, `getFrequencyData()`, and\n *   `getTimeDomainData()` by tapping an {@link AnalyserNode} in the\n *   audio graph.\n *\n * - React StrictMode's simulated unmount/remount is safe: the shared\n *   reader and replay buffer mean a second attach sees the same bytes\n *   that the first one did.\n *\n * @param media - Audio handle from `useAudio` etc.\n * @param options - Strategy overrides and PCM format hints.\n */\nexport function useAudioPlayer(\n  media: AudioMedia | undefined,\n  options?: UseAudioPlayerOptions\n): AudioPlayerHandle {\n  const sampleRate = options?.pcm?.sampleRate ?? DEFAULT_SAMPLE_RATE;\n  const channels = options?.pcm?.channels ?? DEFAULT_CHANNELS;\n  const pcmPrefixes = options?.pcmMimePrefixes;\n  const strategyOverride = options?.strategy ?? \"auto\";\n  const autoPlay = options?.autoPlay ?? false;\n\n  const strategy: \"pcm\" | \"element\" = media\n    ? detectStrategy(media.mimeType, strategyOverride, pcmPrefixes)\n    : \"element\";\n\n  const [status, setStatus] = useState<PlayerStatus>(\"idle\");\n  const [error, setError] = useState<Error | undefined>(undefined);\n  const [currentTime, setCurrentTime] = useState(0);\n  const [duration, setDuration] = useState<number | undefined>(undefined);\n  const [level, setLevel] = useState(0);\n\n  // ── Shared refs ─────────────────────────────────────────────────────\n  const ctxRef = useRef<AudioContext | null>(null);\n  const analyserRef = useRef<AnalyserNode | null>(null);\n  const freqBufRef = useRef<Uint8Array<ArrayBuffer> | null>(null);\n  const timeBufRef = useRef<Uint8Array<ArrayBuffer> | null>(null);\n  const rafRef = useRef<number | null>(null);\n  const playStartCtxTimeRef = useRef<number>(0);\n\n  // ── PCM strategy refs ───────────────────────────────────────────────\n  const nextStartTimeRef = useRef<number>(0);\n  const shouldPlayRef = useRef<boolean>(false);\n  const pendingChunksRef = useRef<Uint8Array[]>([]);\n  const activeSourcesRef = useRef<Set<AudioBufferSourceNode>>(new Set());\n  const formatRef = useRef<AudioFormat | null>(null);\n  const upstreamFinishedRef = useRef(false);\n\n  // ── Element strategy refs ───────────────────────────────────────────\n  const audioElRef = useRef<HTMLAudioElement | null>(null);\n  const elementSourceRef = useRef<MediaElementAudioSourceNode | null>(null);\n  const pendingSrcRef = useRef<string | undefined>(undefined);\n\n  const statusRef = useRef<PlayerStatus>(\"idle\");\n  useEffect(() => {\n    statusRef.current = status;\n  }, [status]);\n\n  // ── `playToEnd` bookkeeping ─────────────────────────────────────────\n  const pendingResolveRef = useRef<(() => void) | null>(null);\n  const pendingRejectRef = useRef<((err: Error) => void) | null>(null);\n\n  const resolvePending = useCallback(() => {\n    const resolve = pendingResolveRef.current;\n    pendingResolveRef.current = null;\n    pendingRejectRef.current = null;\n    resolve?.();\n  }, []);\n\n  const rejectPending = useCallback((err: Error) => {\n    const reject = pendingRejectRef.current;\n    pendingResolveRef.current = null;\n    pendingRejectRef.current = null;\n    reject?.(err);\n  }, []);\n\n  // Fire pending resolvers whenever the status transitions to a\n  // terminal state. Effect depends on `status` so it runs exactly once\n  // per transition.\n  useEffect(() => {\n    if (status === \"finished\" || status === \"paused\" || status === \"idle\") {\n      resolvePending();\n    } else if (status === \"error\") {\n      rejectPending(error ?? new Error(\"playback error\"));\n    }\n  }, [status, error, resolvePending, rejectPending]);\n\n  // ── Analyser loop ───────────────────────────────────────────────────\n  const tickAnalyser = useCallback(() => {\n    const analyser = analyserRef.current;\n    const ctx = ctxRef.current;\n    if (analyser == null) {\n      rafRef.current = null;\n      return;\n    }\n    const buf = timeBufRef.current;\n    if (buf != null) {\n      analyser.getByteTimeDomainData(buf);\n      // RMS on bytes in [0, 255] centred at 128.\n      let sum = 0;\n      for (let i = 0; i < buf.length; i += 1) {\n        const v = (buf[i]! - 128) / 128;\n        sum += v * v;\n      }\n      setLevel(Math.sqrt(sum / buf.length));\n    }\n    if (ctx != null && statusRef.current === \"playing\") {\n      setCurrentTime(ctx.currentTime - playStartCtxTimeRef.current);\n    }\n    if (typeof window !== \"undefined\") {\n      rafRef.current = window.requestAnimationFrame(tickAnalyser);\n    }\n  }, []);\n\n  const startAnalyserLoop = useCallback(() => {\n    if (rafRef.current != null) return;\n    if (typeof window === \"undefined\") return;\n    rafRef.current = window.requestAnimationFrame(tickAnalyser);\n  }, [tickAnalyser]);\n\n  const stopAnalyserLoop = useCallback(() => {\n    if (rafRef.current == null) return;\n    if (typeof window !== \"undefined\") {\n      window.cancelAnimationFrame(rafRef.current);\n    }\n    rafRef.current = null;\n    setLevel(0);\n  }, []);\n\n  // ── AudioContext / AnalyserNode ─────────────────────────────────────\n  const ensureAnalyser = useCallback((ctx: AudioContext): AnalyserNode => {\n    if (analyserRef.current != null) return analyserRef.current;\n    const analyser = ctx.createAnalyser();\n    analyser.fftSize = ANALYSER_FFT_SIZE;\n    analyser.connect(ctx.destination);\n    analyserRef.current = analyser;\n    freqBufRef.current = new Uint8Array(analyser.frequencyBinCount);\n    timeBufRef.current = new Uint8Array(analyser.fftSize);\n    return analyser;\n  }, []);\n\n  const ensureContextForPcm = useCallback((): AudioContext | null => {\n    if (ctxRef.current != null) return ctxRef.current;\n    const format = formatRef.current;\n    if (format == null) return null;\n    const AudioCtx = resolveAudioContextCtor();\n    if (AudioCtx == null) {\n      setError(new Error(\"Web Audio API is not available in this environment\"));\n      setStatus(\"error\");\n      return null;\n    }\n    const ctx = new AudioCtx({ sampleRate: format.sampleRate });\n    ctxRef.current = ctx;\n    nextStartTimeRef.current = ctx.currentTime;\n    ensureAnalyser(ctx);\n    return ctx;\n  }, [ensureAnalyser]);\n\n  const ensureContextForElement = useCallback((): AudioContext | null => {\n    if (ctxRef.current != null) return ctxRef.current;\n    const AudioCtx = resolveAudioContextCtor();\n    if (AudioCtx == null) return null;\n    // Match device rate — browsers otherwise resample the element.\n    const ctx = new AudioCtx();\n    ctxRef.current = ctx;\n    ensureAnalyser(ctx);\n    return ctx;\n  }, [ensureAnalyser]);\n\n  // ── PCM scheduling ──────────────────────────────────────────────────\n  const scheduleChunk = useCallback((ctx: AudioContext, bytes: Uint8Array) => {\n    const format = formatRef.current;\n    const analyser = analyserRef.current;\n    if (format == null || analyser == null) return;\n    const { sampleRate: bufSampleRate, channels: bufChannels } = format;\n\n    const sampleCount = Math.floor(bytes.byteLength / 2);\n    if (sampleCount === 0) return;\n    const framesPerChannel = Math.floor(sampleCount / bufChannels);\n    if (framesPerChannel === 0) return;\n\n    const buffer = ctx.createBuffer(\n      bufChannels,\n      framesPerChannel,\n      bufSampleRate\n    );\n    const view = new DataView(\n      bytes.buffer,\n      bytes.byteOffset,\n      framesPerChannel * bufChannels * 2\n    );\n    for (let channel = 0; channel < bufChannels; channel += 1) {\n      const channelData = buffer.getChannelData(channel);\n      for (let frame = 0; frame < framesPerChannel; frame += 1) {\n        const sampleOffset = (frame * bufChannels + channel) * 2;\n        const int = view.getInt16(sampleOffset, true);\n        channelData[frame] = int < 0 ? int / 0x8000 : int / 0x7fff;\n      }\n    }\n\n    const source = ctx.createBufferSource();\n    source.buffer = buffer;\n    source.connect(analyser);\n    const now = ctx.currentTime;\n    const startAt = Math.max(now, nextStartTimeRef.current);\n    source.start(startAt);\n    nextStartTimeRef.current = startAt + buffer.duration;\n    activeSourcesRef.current.add(source);\n    source.onended = () => {\n      activeSourcesRef.current.delete(source);\n      if (\n        activeSourcesRef.current.size === 0 &&\n        upstreamFinishedRef.current &&\n        pendingChunksRef.current.length === 0\n      ) {\n        setStatus(\"finished\");\n      }\n    };\n  }, []);\n\n  const flushPendingPcm = useCallback(() => {\n    if (!shouldPlayRef.current) return;\n    const ctx = ensureContextForPcm();\n    if (ctx == null) return;\n    if (ctx.state === \"suspended\") void ctx.resume();\n    const chunks = pendingChunksRef.current;\n    pendingChunksRef.current = [];\n    for (const bytes of chunks) scheduleChunk(ctx, bytes);\n    if (chunks.length > 0 && statusRef.current !== \"playing\") {\n      playStartCtxTimeRef.current = ctx.currentTime;\n      setCurrentTime(0);\n      setStatus(\"playing\");\n      startAnalyserLoop();\n    }\n  }, [ensureContextForPcm, scheduleChunk, startAnalyserLoop]);\n\n  // ── Public controls ─────────────────────────────────────────────────\n  const play = useCallback(() => {\n    if (media == null) return;\n    if (statusRef.current === \"error\") return;\n\n    if (strategy === \"pcm\") {\n      shouldPlayRef.current = true;\n      if (statusRef.current !== \"playing\") setStatus(\"buffering\");\n      const ctx = ensureContextForPcm();\n      if (ctx != null && ctx.state === \"suspended\") void ctx.resume();\n      flushPendingPcm();\n      return;\n    }\n\n    // element strategy\n    const audio = audioElRef.current;\n    if (audio == null) {\n      // Buffer hasn't materialised yet — remember the intent and let\n      // the blob-ready path below start playback.\n      shouldPlayRef.current = true;\n      setStatus(\"buffering\");\n      return;\n    }\n    shouldPlayRef.current = true;\n    const ctx = ensureContextForElement();\n    if (ctx != null && ctx.state === \"suspended\") void ctx.resume();\n    audio.play().catch((err) => {\n      setError(err as Error);\n      setStatus(\"error\");\n    });\n  }, [\n    media,\n    strategy,\n    ensureContextForPcm,\n    ensureContextForElement,\n    flushPendingPcm,\n  ]);\n\n  const pause = useCallback(() => {\n    shouldPlayRef.current = false;\n    if (strategy === \"pcm\") {\n      const ctx = ctxRef.current;\n      if (ctx != null && ctx.state === \"running\") void ctx.suspend();\n    } else {\n      audioElRef.current?.pause();\n    }\n    if (statusRef.current === \"playing\" || statusRef.current === \"buffering\") {\n      setStatus(\"paused\");\n    }\n  }, [strategy]);\n\n  const stop = useCallback(() => {\n    shouldPlayRef.current = false;\n    stopAnalyserLoop();\n\n    if (strategy === \"pcm\") {\n      for (const source of activeSourcesRef.current) {\n        try {\n          source.stop();\n        } catch {\n          // Already stopped\n        }\n      }\n      activeSourcesRef.current.clear();\n      pendingChunksRef.current = [];\n      nextStartTimeRef.current = 0;\n    } else {\n      const audio = audioElRef.current;\n      if (audio != null) {\n        audio.pause();\n        audio.currentTime = 0;\n      }\n    }\n\n    const ctx = ctxRef.current;\n    ctxRef.current = null;\n    analyserRef.current = null;\n    freqBufRef.current = null;\n    timeBufRef.current = null;\n    elementSourceRef.current = null;\n    if (ctx != null) void ctx.close();\n\n    setCurrentTime(0);\n    setStatus(media == null ? \"idle\" : \"paused\");\n  }, [strategy, media, stopAnalyserLoop]);\n\n  const reset = useCallback(() => {\n    stop();\n    setError(undefined);\n    setDuration(undefined);\n    upstreamFinishedRef.current = false;\n    setStatus(\"idle\");\n  }, [stop]);\n\n  const toggle = useCallback(() => {\n    if (statusRef.current === \"playing\") pause();\n    else play();\n  }, [play, pause]);\n\n  const playToEnd = useCallback((): Promise<void> => {\n    // Drop any prior caller — they'd have resolved on the next\n    // terminal anyway, which is about to happen again.\n    pendingResolveRef.current?.();\n    pendingResolveRef.current = null;\n    pendingRejectRef.current = null;\n\n    return new Promise<void>((resolve, reject) => {\n      pendingResolveRef.current = resolve;\n      pendingRejectRef.current = reject;\n      play();\n    });\n  }, [play]);\n\n  const seek = useCallback(\n    (seconds: number) => {\n      if (strategy !== \"element\") return;\n      const audio = audioElRef.current;\n      if (audio == null) return;\n      audio.currentTime = seconds;\n      setCurrentTime(seconds);\n    },\n    [strategy]\n  );\n\n  const getFrequencyData = useCallback((): Uint8Array | undefined => {\n    const analyser = analyserRef.current;\n    const buf = freqBufRef.current;\n    if (analyser == null || buf == null) return undefined;\n    analyser.getByteFrequencyData(buf);\n    return buf;\n  }, []);\n\n  const getTimeDomainData = useCallback((): Uint8Array | undefined => {\n    const analyser = analyserRef.current;\n    const buf = timeBufRef.current;\n    if (analyser == null || buf == null) return undefined;\n    analyser.getByteTimeDomainData(buf);\n    return buf;\n  }, []);\n\n  // ── Media binding ───────────────────────────────────────────────────\n  const autoPlayRef = useRef(autoPlay);\n  useEffect(() => {\n    autoPlayRef.current = autoPlay;\n  }, [autoPlay]);\n\n  // Surface a media-level error as soon as the handle reports one.\n  useEffect(() => {\n    if (media?.error == null) return;\n    setError(new Error(media.error.message));\n    setStatus(\"error\");\n  }, [media]);\n\n  // PCM effect — lifts from upstream stream → `AudioContext`.\n  useEffect(() => {\n    if (media == null || strategy !== \"pcm\") return undefined;\n\n    setError(undefined);\n    setStatus(\"buffering\");\n    setCurrentTime(0);\n    setDuration(undefined);\n    upstreamFinishedRef.current = false;\n    pendingChunksRef.current = [];\n\n    const mimeType = media.mimeType ?? \"\";\n    const isRawPcm =\n      mimeType === \"audio/pcm\" ||\n      mimeType === \"audio/L16\" ||\n      mimeType.startsWith(\"audio/pcm;\") ||\n      mimeType.startsWith(\"audio/L16;\") ||\n      (pcmPrefixes != null &&\n        pcmPrefixes.some((prefix) => mimeType.startsWith(prefix)));\n    const isWav =\n      mimeType === \"audio/wav\" ||\n      mimeType === \"audio/wave\" ||\n      mimeType === \"audio/x-wav\" ||\n      mimeType === \"audio/vnd.wave\";\n\n    if (isRawPcm) {\n      formatRef.current = {\n        sampleRate,\n        channels,\n        bitsPerSample: 16,\n      };\n    } else if (isWav) {\n      formatRef.current = null;\n    } else {\n      // Explicit `strategy: \"pcm\"` override on a non-PCM mime: fail loud.\n      setError(\n        new Error(\n          `useAudioPlayer: forced PCM strategy for unsupported mime ${JSON.stringify(mimeType)}`\n        )\n      );\n      setStatus(\"error\");\n      return undefined;\n    }\n\n    // WAV parser state (ignored on the PCM path). Accumulate inbound\n    // bytes until the RIFF chunks resolve, then flip to passthrough.\n    const wavHeaderChunks: Uint8Array[] = [];\n    let wavHeaderParsed = !isWav;\n    let wavHeaderFailed = false;\n\n    const routeChunk = (bytes: Uint8Array) => {\n      if (wavHeaderFailed) return;\n\n      if (wavHeaderParsed) {\n        pendingChunksRef.current.push(bytes);\n        if (shouldPlayRef.current) flushPendingPcm();\n        return;\n      }\n\n      wavHeaderChunks.push(bytes);\n      const combined = concatChunks(wavHeaderChunks);\n      const result = tryParseWavHeader(combined);\n      if (result.status === \"need-more\") return;\n      if (result.status === \"invalid\") {\n        wavHeaderFailed = true;\n        setError(\n          new Error(`useAudioPlayer: invalid WAV stream: ${result.reason}`)\n        );\n        setStatus(\"error\");\n        return;\n      }\n\n      formatRef.current = result.format;\n      wavHeaderParsed = true;\n      wavHeaderChunks.length = 0;\n\n      const tail = combined.subarray(result.dataOffset);\n      if (tail.byteLength > 0) {\n        pendingChunksRef.current.push(tail);\n        if (shouldPlayRef.current) flushPendingPcm();\n      }\n    };\n\n    if (autoPlayRef.current) {\n      shouldPlayRef.current = true;\n    }\n\n    const unsubscribe = attachToPump(media, (event) => {\n      switch (event.type) {\n        case \"chunk\":\n          routeChunk(event.bytes);\n          break;\n        case \"finished\":\n          upstreamFinishedRef.current = true;\n          if (\n            pendingChunksRef.current.length === 0 &&\n            activeSourcesRef.current.size === 0\n          ) {\n            setStatus(\"finished\");\n          }\n          break;\n        case \"error\":\n          setError(event.error);\n          setStatus(\"error\");\n          break;\n      }\n    });\n\n    return () => {\n      unsubscribe();\n      stop();\n    };\n    // `flushPendingPcm` and `stop` are stable via useCallback deps;\n    // include them to satisfy exhaustive-deps without re-running.\n  }, [\n    media,\n    strategy,\n    sampleRate,\n    channels,\n    pcmPrefixes,\n    flushPendingPcm,\n    stop,\n  ]);\n\n  // Element effect — await blob URL then wire an HTMLAudioElement.\n  useEffect(() => {\n    if (media == null || strategy !== \"element\") return undefined;\n    if (typeof window === \"undefined\") return undefined;\n\n    setError(undefined);\n    setStatus(\"buffering\");\n    setCurrentTime(0);\n    setDuration(undefined);\n\n    let cancelled = false;\n    let audio: HTMLAudioElement | null = null;\n\n    media.objectURL.then(\n      (resolved) => {\n        if (cancelled) return;\n        pendingSrcRef.current = resolved;\n        audio = new Audio(resolved);\n        audio.preload = \"auto\";\n        audioElRef.current = audio;\n\n        const onPlay = () => {\n          if (statusRef.current === \"error\") return;\n          const ctx = ensureContextForElement();\n          if (\n            ctx != null &&\n            elementSourceRef.current == null &&\n            audio != null\n          ) {\n            try {\n              const src = ctx.createMediaElementSource(audio);\n              src.connect(analyserRef.current!);\n              elementSourceRef.current = src;\n            } catch {\n              // Some browsers reject a second `createMediaElementSource`\n              // on the same element; fall through without visualization.\n            }\n          }\n          playStartCtxTimeRef.current = 0;\n          setCurrentTime(audio?.currentTime ?? 0);\n          setStatus(\"playing\");\n          startAnalyserLoop();\n        };\n        const onPause = () => {\n          if (audio != null && audio.ended) return; // ended fires pause too\n          if (statusRef.current === \"playing\") setStatus(\"paused\");\n        };\n        const onEnded = () => {\n          setStatus(\"finished\");\n        };\n        const onTimeUpdate = () => {\n          if (audio != null) setCurrentTime(audio.currentTime);\n        };\n        const onLoadedMetadata = () => {\n          if (audio != null && Number.isFinite(audio.duration)) {\n            setDuration(audio.duration);\n          }\n        };\n        const onError = () => {\n          setError(new Error(\"HTMLAudioElement error\"));\n          setStatus(\"error\");\n        };\n\n        audio.addEventListener(\"play\", onPlay);\n        audio.addEventListener(\"pause\", onPause);\n        audio.addEventListener(\"ended\", onEnded);\n        audio.addEventListener(\"timeupdate\", onTimeUpdate);\n        audio.addEventListener(\"loadedmetadata\", onLoadedMetadata);\n        audio.addEventListener(\"error\", onError);\n\n        if (shouldPlayRef.current || autoPlayRef.current) {\n          audio.play().catch((err) => {\n            setError(err as Error);\n            setStatus(\"error\");\n          });\n        } else {\n          setStatus(\"paused\");\n        }\n      },\n      () => {\n        if (!cancelled) {\n          setError(new Error(\"media failed to materialise\"));\n          setStatus(\"error\");\n        }\n      }\n    );\n\n    return () => {\n      cancelled = true;\n      const el = audioElRef.current;\n      audioElRef.current = null;\n      elementSourceRef.current = null;\n      if (el != null) {\n        try {\n          el.pause();\n          el.removeAttribute(\"src\");\n          el.load();\n        } catch {\n          // best-effort teardown\n        }\n      }\n      stop();\n      try {\n        media.revoke();\n      } catch {\n        // best-effort\n      }\n    };\n  }, [media, strategy, ensureContextForElement, startAnalyserLoop, stop]);\n\n  // Reset state when the media handle is cleared.\n  useEffect(() => {\n    if (media != null) return;\n    setStatus(\"idle\");\n    setError(undefined);\n    setCurrentTime(0);\n    setDuration(undefined);\n    setLevel(0);\n    upstreamFinishedRef.current = false;\n  }, [media]);\n\n  return {\n    status,\n    strategy,\n    play,\n    pause,\n    stop,\n    toggle,\n    reset,\n    playToEnd,\n    currentTime,\n    duration: strategy === \"element\" ? duration : undefined,\n    seek: strategy === \"element\" ? seek : undefined,\n    level,\n    getFrequencyData,\n    getTimeDomainData,\n    error,\n  };\n}\n"],"mappings":"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"}