var C = Object.defineProperty;
var g = (h, t, e) => t in h ? C(h, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[t] = e;
var s = (h, t, e) => g(h, typeof t != "symbol" ? t + "" : t, e);
import { A as m, e as f, a as c, l as n } from "./index-BB9yoGY2.js";
class y {
  constructor(t) {
    // AudioContext is managed internally
    s(this, "audioContext", null);
    s(this, "sampleRate");
    s(this, "channelCount");
    s(this, "debug");
    // Session-level state
    s(this, "sessionId");
    s(this, "sessionStartTime", 0);
    // AudioContext time when session started
    s(this, "pausedTimeOffset", 0);
    // Accumulated paused time
    s(this, "pausedAt", 0);
    // Time when paused
    s(this, "pausedAudioContextTime", 0);
    // audioContext.currentTime when paused (for resume calculation)
    s(this, "scheduledTime", 0);
    // Next chunk schedule time in AudioContext time
    // Playback state
    s(this, "isPlaying", !1);
    s(this, "isPaused", !1);
    s(this, "autoStartEnabled", !0);
    // Control whether to auto-start when buffer is ready
    // Audio buffer queue
    s(this, "audioChunks", []);
    s(this, "scheduledChunks", 0);
    // Number of chunks already scheduled
    s(this, "activeSources", /* @__PURE__ */ new Set());
    // Volume control
    s(this, "gainNode", null);
    s(this, "volume", 1);
    // Default volume 1.0 (0.0 - 1.0)
    // Event callbacks
    s(this, "onEndedCallback");
    this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, this.sampleRate = (t == null ? void 0 : t.sampleRate) ?? m.audio.sampleRate, this.channelCount = (t == null ? void 0 : t.channelCount) ?? 1, this.debug = (t == null ? void 0 : t.debug) ?? !1;
  }
  /**
   * Initialize audio context (create and ensure it's ready)
   */
  async initialize() {
    if (!this.audioContext)
      try {
        this.audioContext = new AudioContext({
          sampleRate: this.sampleRate
        }), this.gainNode = this.audioContext.createGain(), this.gainNode.gain.value = this.volume, this.gainNode.connect(this.audioContext.destination), this.audioContext.state === "suspended" && await this.audioContext.resume(), this.log("AudioContext initialized", {
          sessionId: this.sessionId,
          sampleRate: this.audioContext.sampleRate,
          state: this.audioContext.state
        });
      } catch (t) {
        const e = f(t);
        throw c.logEvent("activeAudioSessionFailed", "warning", {
          sessionId: this.sessionId,
          reason: e
        }), n.error("Failed to initialize AudioContext:", e), t instanceof Error ? t : new Error(e);
      }
  }
  /**
   * Add audio chunk (16-bit PCM)
   */
  addChunk(t, e = !1) {
    if (!this.audioContext) {
      n.error("AudioContext not initialized");
      return;
    }
    this.audioChunks.push({ data: t, isLast: e }), this.log(`Added chunk ${this.audioChunks.length}`, {
      size: t.length,
      totalChunks: this.audioChunks.length,
      isLast: e,
      isPlaying: this.isPlaying,
      scheduledChunks: this.scheduledChunks
    }), !this.isPlaying && this.autoStartEnabled && this.audioChunks.length > 0 ? (this.log("[StreamingAudioPlayer] Auto-starting playback from addChunk"), this.startPlayback()) : this.isPlaying && !this.isPaused ? (this.log("[StreamingAudioPlayer] Already playing, scheduling next chunk"), this.scheduleNextChunk()) : this.log("[StreamingAudioPlayer] Not playing and no chunks, waiting for more chunks");
  }
  /**
   * Start new session (stop current and start fresh)
   */
  async startNewSession(t) {
    this.stop(), this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, this.audioChunks = [], this.scheduledChunks = 0, this.pausedTimeOffset = 0, this.pausedAt = 0, this.pausedAudioContextTime = 0, this.log("Starting new session", {
      chunks: t.length
    });
    for (const e of t)
      this.addChunk(e.data, e.isLast);
  }
  /**
   * Start playback
   */
  startPlayback() {
    if (!this.audioContext) {
      this.log("[StreamingAudioPlayer] Cannot start playback: AudioContext not initialized");
      return;
    }
    if (this.isPlaying) {
      this.log("[StreamingAudioPlayer] Cannot start playback: Already playing");
      return;
    }
    this.isPlaying = !0, this.sessionStartTime = this.audioContext.currentTime, this.scheduledTime = this.sessionStartTime, this.log("[StreamingAudioPlayer] Starting playback", {
      sessionStartTime: this.sessionStartTime,
      bufferedChunks: this.audioChunks.length,
      scheduledChunks: this.scheduledChunks,
      activeSources: this.activeSources.size
    }), this.scheduleAllChunks();
  }
  /**
   * Schedule all pending chunks
   */
  scheduleAllChunks() {
    for (; this.scheduledChunks < this.audioChunks.length; )
      this.scheduleNextChunk();
  }
  /**
   * Schedule next audio chunk
   */
  scheduleNextChunk() {
    if (!this.audioContext) {
      this.log("[StreamingAudioPlayer] Cannot schedule chunk: AudioContext not initialized");
      return;
    }
    if (!this.isPlaying || this.isPaused) {
      this.log("[StreamingAudioPlayer] Cannot schedule chunk: Not playing or paused");
      return;
    }
    const t = this.scheduledChunks;
    if (t >= this.audioChunks.length) {
      this.log(`[StreamingAudioPlayer] No more chunks to schedule (chunkIndex: ${t}, totalChunks: ${this.audioChunks.length})`);
      return;
    }
    const e = this.audioChunks[t];
    if (e.data.length === 0 && !e.isLast) {
      this.scheduledChunks++;
      return;
    }
    const r = e.data, o = e.isLast, a = this.pcmToAudioBuffer(r);
    if (!a) {
      n.error("Failed to create AudioBuffer from PCM data"), c.logEvent("character_player", "error", {
        sessionId: this.sessionId,
        event: "audio_buffer_creation_failed"
      });
      return;
    }
    try {
      const i = this.audioContext.createBufferSource();
      i.buffer = a, i.connect(this.gainNode), i.start(this.scheduledTime), this.activeSources.add(i), i.onended = () => {
        this.activeSources.delete(i), o && this.activeSources.size === 0 && (this.log("Last audio chunk ended, marking playback as ended"), this.markEnded());
      }, this.scheduledTime += a.duration, this.scheduledChunks++, this.log(`[StreamingAudioPlayer] Scheduled chunk ${t + 1}/${this.audioChunks.length}`, {
        startTime: this.scheduledTime - a.duration,
        duration: a.duration,
        nextScheduleTime: this.scheduledTime,
        isLast: o,
        activeSources: this.activeSources.size
      });
    } catch (i) {
      n.errorWithError("Failed to schedule audio chunk:", i), c.logEvent("character_player", "error", {
        sessionId: this.sessionId,
        event: "schedule_chunk_failed",
        reason: i instanceof Error ? i.message : String(i)
      });
    }
  }
  /**
   * Convert PCM data to AudioBuffer
   * Input: 16-bit PCM (int16), Output: AudioBuffer (float32 [-1, 1])
   */
  pcmToAudioBuffer(t) {
    if (!this.audioContext)
      return null;
    if (t.length === 0) {
      const l = Math.floor(this.sampleRate * 0.01), u = this.audioContext.createBuffer(
        this.channelCount,
        l,
        this.sampleRate
      );
      for (let d = 0; d < this.channelCount; d++)
        u.getChannelData(d).fill(0);
      return u;
    }
    const e = new Uint8Array(t), r = new Int16Array(e.buffer, 0, e.length / 2), o = r.length / this.channelCount, a = this.audioContext.createBuffer(
      this.channelCount,
      o,
      this.sampleRate
    );
    for (let i = 0; i < this.channelCount; i++) {
      const l = a.getChannelData(i);
      for (let u = 0; u < o; u++) {
        const d = u * this.channelCount + i;
        l[u] = r[d] / 32768;
      }
    }
    return a;
  }
  /**
   * Get current playback time (seconds)
   */
  getCurrentTime() {
    if (!this.audioContext || !this.isPlaying)
      return 0;
    if (this.isPaused)
      return this.pausedAt;
    const e = this.audioContext.currentTime - this.sessionStartTime - this.pausedTimeOffset;
    return Math.max(0, e);
  }
  /**
   * Pause playback
   */
  pause() {
    !this.isPlaying || this.isPaused || !this.audioContext || (this.pausedAt = this.getCurrentTime(), this.pausedAudioContextTime = this.audioContext.currentTime, this.isPaused = !0, this.audioContext.state === "running" && this.audioContext.suspend().catch((t) => {
      n.errorWithError("Failed to suspend AudioContext:", t), this.isPaused = !1;
    }), this.log("Playback paused", {
      pausedAt: this.pausedAt,
      pausedAudioContextTime: this.pausedAudioContextTime,
      audioContextState: this.audioContext.state
    }));
  }
  /**
   * Resume playback
   */
  async resume() {
    if (!this.isPaused || !this.audioContext || !this.isPlaying)
      return;
    if (this.audioContext.state === "suspended")
      try {
        await this.audioContext.resume();
      } catch (e) {
        throw n.errorWithError("Failed to resume AudioContext:", e), e;
      }
    const t = this.audioContext.currentTime;
    this.sessionStartTime = this.pausedAudioContextTime - this.pausedAt - this.pausedTimeOffset, this.isPaused = !1, this.scheduledChunks < this.audioChunks.length && this.scheduleAllChunks(), this.log("Playback resumed", {
      pausedAt: this.pausedAt,
      pausedAudioContextTime: this.pausedAudioContextTime,
      currentAudioContextTime: t,
      adjustedSessionStartTime: this.sessionStartTime,
      audioContextState: this.audioContext.state
    });
  }
  /**
   * Stop playback
   */
  stop() {
    if (this.audioContext) {
      this.isPaused && this.audioContext.state === "suspended" && (this.audioContext.resume().catch(() => {
      }), this.isPaused = !1), this.isPlaying = !1, this.isPaused = !1, this.sessionStartTime = 0, this.scheduledTime = 0;
      for (const t of this.activeSources) {
        t.onended = null;
        try {
          t.stop(0);
        } catch {
        }
        try {
          t.disconnect();
        } catch {
        }
      }
      this.activeSources.clear(), this.audioChunks = [], this.scheduledChunks = 0, this.log("[StreamingAudioPlayer] Playback stopped, state reset");
    }
  }
  /**
   * Enable or disable auto-start (for delayed start scenarios)
   */
  setAutoStart(t) {
    this.autoStartEnabled = t, this.log(`Auto-start ${t ? "enabled" : "disabled"}`);
  }
  /**
   * Start playback manually (for delayed start scenarios)
   * This allows starting playback after transition animation completes
   */
  play() {
    this.isPlaying || (this.autoStartEnabled = !0, this.startPlayback());
  }
  /**
   * Mark playback as ended
   */
  markEnded() {
    var t;
    this.log("Playback ended"), this.isPlaying = !1, (t = this.onEndedCallback) == null || t.call(this);
  }
  /**
   * Set ended callback
   */
  onEnded(t) {
    this.onEndedCallback = t;
  }
  /**
   * Check if playing
   */
  isPlayingNow() {
    return this.isPlaying && !this.isPaused;
  }
  /**
   * Get total duration of buffered audio
   */
  getBufferedDuration() {
    if (!this.audioContext)
      return 0;
    let t = 0;
    for (const e of this.audioChunks)
      t += e.data.length / 2 / this.channelCount;
    return t / this.sampleRate;
  }
  /**
   * Get remaining duration (buffered - played) in seconds
   */
  getRemainingDuration() {
    const t = this.getBufferedDuration(), e = this.getCurrentTime();
    return Math.max(0, t - e);
  }
  /**
   * Dispose and cleanup
   */
  dispose() {
    this.stop(), this.audioContext && (this.audioContext.close(), this.audioContext = null, this.gainNode = null), this.audioChunks = [], this.scheduledChunks = 0, this.sessionStartTime = 0, this.pausedTimeOffset = 0, this.pausedAt = 0, this.pausedAudioContextTime = 0, this.scheduledTime = 0, this.onEndedCallback = void 0, this.log("StreamingAudioPlayer disposed");
  }
  /**
   * Flush buffered audio
   * - hard: stops all playing sources and clears all chunks
   * - soft (default): clears UNSCHEDULED chunks only
   */
  flush(t) {
    if ((t == null ? void 0 : t.hard) === !0) {
      this.stop(), this.audioChunks = [], this.scheduledChunks = 0, this.sessionStartTime = 0, this.pausedAt = 0, this.scheduledTime = 0, this.log("Flushed (hard)");
      return;
    }
    this.scheduledChunks < this.audioChunks.length && this.audioChunks.splice(this.scheduledChunks), this.log("Flushed (soft)", { remainingScheduled: this.scheduledChunks });
  }
  /**
   * 设置音量 (0.0 - 1.0)
   * 注意：这仅控制数字人音频播放器的音量，不影响系统音量
   * @param volume 音量值，范围 0.0 到 1.0（0.0 为静音，1.0 为最大音量）
   */
  setVolume(t) {
    (t < 0 || t > 1) && (n.warn(`[StreamingAudioPlayer] Volume out of range: ${t}, clamping to [0, 1]`), t = Math.max(0, Math.min(1, t))), this.volume = t, this.gainNode && (this.gainNode.gain.value = t);
  }
  /**
   * 获取当前音量
   * @returns 当前音量值 (0.0 - 1.0)
   */
  getVolume() {
    return this.volume;
  }
  /**
   * Debug logging
   */
  log(t, e) {
    this.debug && n.log(`[StreamingAudioPlayer] ${t}`, e || "");
  }
}
export {
  y as StreamingAudioPlayer
};
//# sourceMappingURL=StreamingAudioPlayer-DjPD7f6u.js.map
