import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
import { clsx } from 'clsx';

// src/audioDetection/core/AudioInputService.ts
var AudioInputService = class {
  constructor(config = {}) {
    this.audioContext = null;
    this.mediaStream = null;
    this.sourceNode = null;
    this.analyserNode = null;
    this.state = "idle";
    this.config = {
      sampleRate: config.sampleRate ?? 44100,
      fftSize: config.fftSize ?? 4096,
      minVolume: config.minVolume ?? 1e-3,
      // 降低默认阈值，使其更敏感
      minConfidence: config.minConfidence ?? 0.5,
      // 降低默认置信度，更容易检测
      smoothing: config.smoothing ?? 0.8,
      frequencyRange: config.frequencyRange ?? { min: 27.5, max: 4186 }
      // A0 到 C8
    };
  }
  /**
   * 初始化音频输入
   * Initialize audio input
   */
  async initialize() {
    if (this.state === "active") {
      console.warn("AudioInputService \u5DF2\u7ECF\u5904\u4E8E\u6D3B\u52A8\u72B6\u6001");
      return;
    }
    try {
      this.setState("initializing");
      this.mediaStream = await navigator.mediaDevices.getUserMedia({
        audio: {
          echoCancellation: false,
          noiseSuppression: false,
          autoGainControl: false,
          sampleRate: this.config.sampleRate
        }
      });
      this.audioContext = new AudioContext({
        sampleRate: this.config.sampleRate
      });
      this.sourceNode = this.audioContext.createMediaStreamSource(this.mediaStream);
      this.analyserNode = this.audioContext.createAnalyser();
      this.analyserNode.fftSize = this.config.fftSize;
      this.analyserNode.smoothingTimeConstant = this.config.smoothing;
      this.sourceNode.connect(this.analyserNode);
      this.setState("active");
      console.log("AudioInputService \u521D\u59CB\u5316\u6210\u529F");
    } catch (error) {
      this.setState("error");
      console.error("AudioInputService \u521D\u59CB\u5316\u5931\u8D25:", error);
      throw new Error("\u9EA6\u514B\u98CE\u521D\u59CB\u5316\u5931\u8D25: " + (error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"));
    }
  }
  /**
   * 获取时域数据
   * Get time domain data
   */
  getTimeDomainData() {
    if (!this.analyserNode) {
      throw new Error("AnalyserNode \u672A\u521D\u59CB\u5316");
    }
    const bufferLength = this.analyserNode.fftSize;
    const dataArray = new Float32Array(bufferLength);
    this.analyserNode.getFloatTimeDomainData(dataArray);
    return dataArray;
  }
  /**
   * 获取频域数据
   * Get frequency domain data
   */
  getFrequencyData() {
    if (!this.analyserNode) {
      throw new Error("AnalyserNode \u672A\u521D\u59CB\u5316");
    }
    const bufferLength = this.analyserNode.frequencyBinCount;
    const dataArray = new Float32Array(bufferLength);
    this.analyserNode.getFloatFrequencyData(dataArray);
    return dataArray;
  }
  /**
   * 获取当前音量
   * Get current volume
   */
  getCurrentVolume() {
    const timeDomainData = this.getTimeDomainData();
    let sum = 0;
    for (let i = 0; i < timeDomainData.length; i++) {
      const val = timeDomainData[i];
      if (val !== void 0) {
        sum += val * val;
      }
    }
    return Math.sqrt(sum / timeDomainData.length);
  }
  /**
   * 停止音频输入
   * Stop audio input
   */
  stop() {
    if (this.mediaStream) {
      this.mediaStream.getTracks().forEach((track) => track.stop());
      this.mediaStream = null;
    }
    if (this.sourceNode) {
      this.sourceNode.disconnect();
      this.sourceNode = null;
    }
    if (this.analyserNode) {
      this.analyserNode.disconnect();
      this.analyserNode = null;
    }
    if (this.audioContext) {
      this.audioContext.close();
      this.audioContext = null;
    }
    this.setState("stopped");
    console.log("AudioInputService \u5DF2\u505C\u6B62");
  }
  /**
   * 获取当前状态
   * Get current state
   */
  getState() {
    return this.state;
  }
  /**
   * 获取配置
   * Get configuration
   */
  getConfig() {
    return { ...this.config };
  }
  /**
   * 获取音频上下文
   * Get audio context
   */
  getAudioContext() {
    return this.audioContext;
  }
  /**
   * 获取分析器节点
   * Get analyser node
   */
  getAnalyserNode() {
    return this.analyserNode;
  }
  /**
   * 设置状态
   * Set state
   */
  setState(state) {
    this.state = state;
  }
  /**
   * 检查是否正在运行
   * Check if running
   */
  isRunning() {
    return this.state === "active";
  }
};

// src/audioDetection/core/PitchDetector.ts
var _PitchDetector = class _PitchDetector {
  constructor(sampleRate = 44100, minFrequency = 27.5, maxFrequency = 4186) {
    this.sampleRate = sampleRate;
    this.minFrequency = minFrequency;
    this.maxFrequency = maxFrequency;
  }
  /**
   * 检测音高
   * Detect pitch using autocorrelation
   */
  detectPitch(audioBuffer, minVolume = 0.01) {
    const volume = this.calculateRMS(audioBuffer);
    if (volume < minVolume) {
      return null;
    }
    const frequency = this.autoCorrelate(audioBuffer);
    if (frequency === -1 || frequency < this.minFrequency || frequency > this.maxFrequency) {
      return null;
    }
    return frequency;
  }
  /**
   * 自相关算法
   * Autocorrelation algorithm
   */
  autoCorrelate(buffer) {
    const SIZE = buffer.length;
    const MAX_SAMPLES = Math.floor(SIZE / 2);
    let best_offset = -1;
    let best_correlation = 0;
    let rms = 0;
    for (let i = 0; i < SIZE; i++) {
      const val = buffer[i];
      if (val !== void 0) {
        rms += val * val;
      }
    }
    rms = Math.sqrt(rms / SIZE);
    if (rms < 0.01) {
      return -1;
    }
    let lastCorrelation = 1;
    for (let offset = 0; offset < MAX_SAMPLES; offset++) {
      let correlation = 0;
      for (let i = 0; i < MAX_SAMPLES; i++) {
        const val1 = buffer[i];
        const val2 = buffer[i + offset];
        if (val1 !== void 0 && val2 !== void 0) {
          correlation += Math.abs(val1 - val2);
        }
      }
      correlation = 1 - correlation / MAX_SAMPLES;
      if (correlation > 0.9 && correlation > lastCorrelation) {
        const foundGoodCorrelation = correlation > best_correlation;
        if (foundGoodCorrelation) {
          best_correlation = correlation;
          best_offset = offset;
        }
      }
      lastCorrelation = correlation;
    }
    if (best_correlation > 0.01 && best_offset !== -1) {
      const frequency = this.sampleRate / best_offset;
      return frequency;
    }
    return -1;
  }
  /**
   * 计算RMS音量
   * Calculate RMS volume
   */
  calculateRMS(buffer) {
    let sum = 0;
    for (let i = 0; i < buffer.length; i++) {
      const val = buffer[i];
      if (val !== void 0) {
        sum += val * val;
      }
    }
    return Math.sqrt(sum / buffer.length);
  }
  /**
   * 将频率转换为音符信息
   * Convert frequency to note information
   */
  frequencyToNote(frequency, volume = 1) {
    if (frequency <= 0) {
      return null;
    }
    const midi = Math.round(12 * Math.log2(frequency / 440) + 69);
    if (midi < 0 || midi > 127) {
      return null;
    }
    const noteIndex = midi % 12;
    const octave = Math.floor(midi / 12) - 1;
    const noteName = _PitchDetector.NOTE_NAMES[noteIndex] || "C";
    const name = noteName + octave;
    const standardFrequency = 440 * Math.pow(2, (midi - 69) / 12);
    const cents = 1200 * Math.log2(frequency / standardFrequency);
    const confidence = Math.max(0, 1 - Math.abs(cents) / 50);
    return {
      name,
      frequency,
      noteName,
      octave,
      midi,
      volume,
      confidence
    };
  }
  /**
   * 使用FFT检测主频率
   * Detect dominant frequency using FFT
   */
  detectDominantFrequency(frequencyData, minVolume = -60) {
    const nyquist = this.sampleRate / 2;
    const binWidth = nyquist / frequencyData.length;
    let maxIndex = -1;
    let maxValue = -Infinity;
    const minBin = Math.floor(this.minFrequency / binWidth);
    const maxBin = Math.ceil(this.maxFrequency / binWidth);
    for (let i = minBin; i < Math.min(maxBin, frequencyData.length); i++) {
      const value = frequencyData[i];
      if (value !== void 0 && value > maxValue && value > minVolume) {
        maxValue = value;
        maxIndex = i;
      }
    }
    if (maxIndex === -1) {
      return null;
    }
    const frequency = this.parabolicInterpolation(frequencyData, maxIndex, binWidth);
    return frequency;
  }
  /**
   * 抛物线插值
   * Parabolic interpolation for better frequency estimation
   */
  parabolicInterpolation(data, index, binWidth) {
    if (index <= 0 || index >= data.length - 1) {
      return index * binWidth;
    }
    const y1 = data[index - 1];
    const y2 = data[index];
    const y3 = data[index + 1];
    if (y1 === void 0 || y2 === void 0 || y3 === void 0) {
      return index * binWidth;
    }
    const delta = 0.5 * (y3 - y1) / (2 * y2 - y1 - y3);
    const interpolatedIndex = index + delta;
    return interpolatedIndex * binWidth;
  }
  /**
   * 检测多个音高（和弦）
   * Detect multiple pitches (chords)
   */
  detectMultiplePitches(frequencyData, minVolume = -60) {
    const notes = [];
    const nyquist = this.sampleRate / 2;
    const binWidth = nyquist / frequencyData.length;
    const peaks = [];
    for (let i = 1; i < frequencyData.length - 1; i++) {
      const freq = i * binWidth;
      if (freq < this.minFrequency || freq > this.maxFrequency) {
        continue;
      }
      const value = frequencyData[i];
      if (value !== void 0 && value > minVolume && value > (frequencyData[i - 1] ?? -Infinity) && value > (frequencyData[i + 1] ?? -Infinity)) {
        peaks.push({ index: i, value });
      }
    }
    peaks.sort((a, b) => b.value - a.value);
    const topPeaks = peaks.slice(0, 5);
    for (const peak of topPeaks) {
      const frequency = this.parabolicInterpolation(frequencyData, peak.index, binWidth);
      const volume = Math.pow(10, peak.value / 20);
      const note = this.frequencyToNote(frequency, volume);
      if (note) {
        notes.push(note);
      }
    }
    return notes;
  }
};
// 音符名称映射
_PitchDetector.NOTE_NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
var PitchDetector = _PitchDetector;

// src/audioDetection/core/ChordRecognizer.ts
var _ChordRecognizer = class _ChordRecognizer {
  /**
   * 识别和弦
   * Recognize chord from notes
   */
  recognizeChord(notes) {
    if (notes.length < 2) {
      return null;
    }
    const sortedNotes = [...notes].sort((a, b) => a.midi - b.midi);
    const uniqueNoteClasses = this.getUniqueNoteClasses(sortedNotes);
    if (uniqueNoteClasses.length < 2) {
      return null;
    }
    let bestMatch = null;
    for (let i = 0; i < uniqueNoteClasses.length; i++) {
      const root = uniqueNoteClasses[i];
      if (!root) continue;
      const intervals = this.calculateIntervals(root, uniqueNoteClasses);
      for (const pattern of _ChordRecognizer.CHORD_PATTERNS) {
        const confidence = this.matchPattern(intervals, pattern.intervals);
        if (confidence > 0.8 && (!bestMatch || confidence > bestMatch.confidence)) {
          bestMatch = { root, pattern, confidence };
        }
      }
    }
    if (!bestMatch) {
      return null;
    }
    const chordName = bestMatch.root + this.getChordSuffix(bestMatch.pattern);
    return {
      name: chordName,
      root: bestMatch.root,
      type: bestMatch.pattern.name,
      notes: sortedNotes,
      confidence: bestMatch.confidence
    };
  }
  /**
   * 获取唯一的音符类别（不考虑八度）
   * Get unique note classes (ignoring octave)
   */
  getUniqueNoteClasses(notes) {
    const noteSet = /* @__PURE__ */ new Set();
    for (const note of notes) {
      noteSet.add(note.noteName);
    }
    return Array.from(noteSet);
  }
  /**
   * 计算音程
   * Calculate intervals from root
   */
  calculateIntervals(root, noteClasses) {
    const rootIndex = _ChordRecognizer.NOTE_NAMES.indexOf(root);
    const intervals = [];
    for (const noteClass of noteClasses) {
      const noteIndex = _ChordRecognizer.NOTE_NAMES.indexOf(noteClass);
      let interval = (noteIndex - rootIndex + 12) % 12;
      intervals.push(interval);
    }
    return intervals.sort((a, b) => a - b);
  }
  /**
   * 匹配和弦模式
   * Match chord pattern
   */
  matchPattern(intervals, pattern) {
    if (intervals.length < pattern.length) {
      return 0;
    }
    let matches = 0;
    for (const patternInterval of pattern) {
      if (intervals.includes(patternInterval)) {
        matches++;
      }
    }
    const precision = matches / pattern.length;
    const recall = matches / intervals.length;
    if (precision + recall === 0) {
      return 0;
    }
    return 2 * precision * recall / (precision + recall);
  }
  /**
   * 获取和弦后缀
   * Get chord suffix for display
   */
  getChordSuffix(pattern) {
    if (pattern.aliases && pattern.aliases.length > 0) {
      return pattern.aliases[0] || "";
    }
    const suffixMap = {
      "major": "",
      "minor": "m",
      "diminished": "dim",
      "augmented": "aug",
      "dominant7": "7"
    };
    return suffixMap[pattern.name] ?? pattern.name;
  }
  /**
   * 识别八度内的音程（用于简单的音程识别）
   * Recognize interval within an octave
   */
  recognizeInterval(note1, note2) {
    const semitones = Math.abs(note1.midi - note2.midi) % 12;
    const intervalNames = {
      0: "\u7EAF\u4E00\u5EA6 (Unison)",
      1: "\u5C0F\u4E8C\u5EA6 (Minor 2nd)",
      2: "\u5927\u4E8C\u5EA6 (Major 2nd)",
      3: "\u5C0F\u4E09\u5EA6 (Minor 3rd)",
      4: "\u5927\u4E09\u5EA6 (Major 3rd)",
      5: "\u7EAF\u56DB\u5EA6 (Perfect 4th)",
      6: "\u589E\u56DB\u5EA6/\u51CF\u4E94\u5EA6 (Tritone)",
      7: "\u7EAF\u4E94\u5EA6 (Perfect 5th)",
      8: "\u5C0F\u516D\u5EA6 (Minor 6th)",
      9: "\u5927\u516D\u5EA6 (Major 6th)",
      10: "\u5C0F\u4E03\u5EA6 (Minor 7th)",
      11: "\u5927\u4E03\u5EA6 (Major 7th)"
    };
    return intervalNames[semitones] || "\u672A\u77E5\u97F3\u7A0B";
  }
  /**
   * 分析和弦质量
   * Analyze chord quality
   */
  analyzeChordQuality(chord) {
    const qualities = [];
    if (chord.type.includes("major")) {
      qualities.push("\u5927\u8C03");
    } else if (chord.type.includes("minor")) {
      qualities.push("\u5C0F\u8C03");
    }
    if (chord.type.includes("diminished")) {
      qualities.push("\u51CF");
    } else if (chord.type.includes("augmented")) {
      qualities.push("\u589E");
    }
    if (chord.type.includes("7")) {
      qualities.push("\u4E03\u548C\u5F26");
    } else if (chord.type.includes("9")) {
      qualities.push("\u4E5D\u548C\u5F26");
    }
    if (chord.type.includes("sus")) {
      qualities.push("\u6302\u7559");
    }
    return qualities.join(" ") || "\u57FA\u7840\u548C\u5F26";
  }
};
// 和弦模式定义
_ChordRecognizer.CHORD_PATTERNS = [
  // 三和弦
  { name: "major", intervals: [0, 4, 7], aliases: ["maj", "M"] },
  { name: "minor", intervals: [0, 3, 7], aliases: ["min", "m"] },
  { name: "diminished", intervals: [0, 3, 6], aliases: ["dim", "\xB0"] },
  { name: "augmented", intervals: [0, 4, 8], aliases: ["aug", "+"] },
  { name: "sus2", intervals: [0, 2, 7] },
  { name: "sus4", intervals: [0, 5, 7] },
  // 七和弦
  { name: "major7", intervals: [0, 4, 7, 11], aliases: ["maj7", "M7"] },
  { name: "minor7", intervals: [0, 3, 7, 10], aliases: ["min7", "m7"] },
  { name: "dominant7", intervals: [0, 4, 7, 10], aliases: ["7"] },
  { name: "diminished7", intervals: [0, 3, 6, 9], aliases: ["dim7", "\xB07"] },
  { name: "half-diminished7", intervals: [0, 3, 6, 10], aliases: ["m7b5", "\xF87"] },
  { name: "minor-major7", intervals: [0, 3, 7, 11], aliases: ["mM7", "m(maj7)"] },
  { name: "augmented7", intervals: [0, 4, 8, 10], aliases: ["aug7", "+7"] },
  // 扩展和弦
  { name: "major9", intervals: [0, 4, 7, 11, 14], aliases: ["maj9", "M9"] },
  { name: "minor9", intervals: [0, 3, 7, 10, 14], aliases: ["min9", "m9"] },
  { name: "dominant9", intervals: [0, 4, 7, 10, 14], aliases: ["9"] },
  { name: "add9", intervals: [0, 4, 7, 14] },
  { name: "madd9", intervals: [0, 3, 7, 14], aliases: ["m(add9)"] }
];
_ChordRecognizer.NOTE_NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
var ChordRecognizer = _ChordRecognizer;

// src/audioDetection/core/AudioDetector.ts
var AudioDetector = class {
  constructor(config = {}, events = {}) {
    this.isDetecting = false;
    this.animationFrameId = null;
    this.lastResult = null;
    /**
     * 检测循环
     * Detection loop
     */
    this.detectLoop = () => {
      if (!this.isDetecting) {
        return;
      }
      try {
        const result = this.detect();
        if (result) {
          this.lastResult = result;
          this.events.onDetection?.(result);
        }
      } catch (error) {
        console.error("\u68C0\u6D4B\u8FC7\u7A0B\u4E2D\u51FA\u9519:", error);
        this.events.onError?.(error instanceof Error ? error : new Error(String(error)));
      }
      this.animationFrameId = requestAnimationFrame(this.detectLoop);
    };
    this.audioInput = new AudioInputService(config);
    const audioConfig = this.audioInput.getConfig();
    this.pitchDetector = new PitchDetector(
      audioConfig.sampleRate,
      audioConfig.frequencyRange.min,
      audioConfig.frequencyRange.max
    );
    this.chordRecognizer = new ChordRecognizer();
    this.events = events;
  }
  /**
   * 启动音频检测
   * Start audio detection
   */
  async start() {
    if (this.isDetecting) {
      console.warn("AudioDetector \u5DF2\u7ECF\u5728\u8FD0\u884C");
      return;
    }
    try {
      await this.audioInput.initialize();
      this.events.onStateChange?.("active");
      this.isDetecting = true;
      this.detectLoop();
      console.log("AudioDetector \u5DF2\u542F\u52A8");
    } catch (error) {
      this.events.onStateChange?.("error");
      this.events.onError?.(error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }
  /**
   * 停止音频检测
   * Stop audio detection
   */
  stop() {
    if (!this.isDetecting) {
      return;
    }
    this.isDetecting = false;
    if (this.animationFrameId !== null) {
      cancelAnimationFrame(this.animationFrameId);
      this.animationFrameId = null;
    }
    this.audioInput.stop();
    this.events.onStateChange?.("stopped");
    console.log("AudioDetector \u5DF2\u505C\u6B62");
  }
  /**
   * 执行一次检测
   * Perform single detection
   */
  detect() {
    const config = this.audioInput.getConfig();
    const timeDomainData = this.audioInput.getTimeDomainData();
    const frequencyData = this.audioInput.getFrequencyData();
    const volume = this.audioInput.getCurrentVolume();
    if (Date.now() % 2e3 < 100) {
      console.log("[AudioDetector] \u5F53\u524D\u97F3\u91CF: " + volume.toFixed(6) + ", \u9608\u503C: " + config.minVolume + ", \u72B6\u6001: " + (volume >= config.minVolume ? "\u2705 \u6709\u58F0\u97F3" : "\u274C \u97F3\u91CF\u592A\u4F4E"));
    }
    if (volume < config.minVolume) {
      return {
        notes: [],
        timestamp: Date.now(),
        isDetecting: false
      };
    }
    const dominantFrequency = this.pitchDetector.detectPitch(timeDomainData, config.minVolume);
    const detectedNotes = this.pitchDetector.detectMultiplePitches(frequencyData, -60);
    if (dominantFrequency && dominantFrequency > 0) {
      const dominantNote = this.pitchDetector.frequencyToNote(dominantFrequency, volume);
      if (dominantNote) {
        const exists = detectedNotes.some((n) => Math.abs(n.midi - dominantNote.midi) < 1);
        if (!exists) {
          detectedNotes.unshift(dominantNote);
        }
      }
    }
    const filteredNotes = detectedNotes.filter((note) => note.confidence >= config.minConfidence);
    let chord;
    if (filteredNotes.length >= 2) {
      chord = this.chordRecognizer.recognizeChord(filteredNotes) ?? void 0;
    }
    return {
      notes: filteredNotes,
      chord,
      timestamp: Date.now(),
      isDetecting: filteredNotes.length > 0
    };
  }
  /**
   * 获取当前状态
   * Get current state
   */
  getState() {
    return this.audioInput.getState();
  }
  /**
   * 获取最后的检测结果
   * Get last detection result
   */
  getLastResult() {
    return this.lastResult;
  }
  /**
   * 检查是否正在运行
   * Check if running
   */
  isRunning() {
    return this.isDetecting && this.audioInput.isRunning();
  }
  /**
   * 获取音频输入服务
   * Get audio input service
   */
  getAudioInput() {
    return this.audioInput;
  }
};

// src/audioDetection/core/AudioDetectorDebugger.ts
var AudioDetectorDebugger = class {
  constructor(config = {}) {
    this.debugInterval = null;
    this.detector = new AudioDetector(config, {
      onDetection: (result) => {
        if (result.isDetecting) {
          console.log("\u{1F3B5} \u68C0\u6D4B\u5230\u97F3\u7B26:", result.notes.map((n) => n.name + "(" + n.frequency.toFixed(1) + "Hz)").join(", "));
          if (result.chord) {
            console.log("\u{1F3B9} \u68C0\u6D4B\u5230\u548C\u5F26:", result.chord.name);
          }
        }
      },
      onStateChange: (state) => {
        console.log("\u{1F4CA} \u72B6\u6001\u53D8\u5316:", state);
      },
      onError: (error) => {
        console.error("\u274C \u9519\u8BEF:", error);
      }
    });
  }
  async start() {
    await this.detector.start();
    this.debugInterval = window.setInterval(() => {
      const audioInput = this.detector.getAudioInput();
      const volume = audioInput.getCurrentVolume();
      const config = audioInput.getConfig();
      const analyser = audioInput.getAnalyserNode();
      if (analyser) {
        const freqData = new Float32Array(analyser.frequencyBinCount);
        analyser.getFloatFrequencyData(freqData);
        let maxFreqValue = -Infinity;
        for (let i = 0; i < freqData.length; i++) {
          const val = freqData[i];
          if (val !== void 0 && val > maxFreqValue) {
            maxFreqValue = val;
          }
        }
        console.log(`
\u{1F3A4} \u97F3\u9891\u8C03\u8BD5\u4FE1\u606F:
  - \u97F3\u91CF (RMS): ${volume.toFixed(6)}
  - \u97F3\u91CF\u9608\u503C: ${config.minVolume}
  - \u97F3\u91CF\u72B6\u6001: ${volume >= config.minVolume ? "\u2705 \u8D85\u8FC7\u9608\u503C" : "\u274C \u4F4E\u4E8E\u9608\u503C"}
  - \u6700\u5927\u9891\u7387\u5F3A\u5EA6: ${maxFreqValue.toFixed(2)} dB
  - \u91C7\u6837\u7387: ${config.sampleRate} Hz
  - FFT\u5927\u5C0F: ${config.fftSize}
  - \u5E73\u6ED1\u7CFB\u6570: ${config.smoothing}
        `);
      }
    }, 2e3);
  }
  stop() {
    if (this.debugInterval !== null) {
      clearInterval(this.debugInterval);
      this.debugInterval = null;
    }
    this.detector.stop();
  }
  getDetector() {
    return this.detector;
  }
  /**
   * 测试麦克风是否正常工作
   */
  async testMicrophone() {
    console.log("\u{1F50D} \u5F00\u59CB\u6D4B\u8BD5\u9EA6\u514B\u98CE...");
    await this.detector.start();
    const volumes = [];
    const audioInput = this.detector.getAudioInput();
    return new Promise((resolve) => {
      const sampleInterval = setInterval(() => {
        volumes.push(audioInput.getCurrentVolume());
      }, 100);
      setTimeout(() => {
        clearInterval(sampleInterval);
        const averageVolume = volumes.reduce((a, b) => a + b, 0) / volumes.length;
        const peakVolume = Math.max(...volumes);
        const isReceivingAudio = peakVolume > 1e-4;
        console.log(`
\u2705 \u9EA6\u514B\u98CE\u6D4B\u8BD5\u5B8C\u6210:
  - \u6743\u9650\u72B6\u6001: \u2705 \u5DF2\u6388\u6743
  - \u63A5\u6536\u97F3\u9891: ${isReceivingAudio ? "\u2705 \u662F" : "\u274C \u5426"}
  - \u5E73\u5747\u97F3\u91CF: ${averageVolume.toFixed(6)}
  - \u5CF0\u503C\u97F3\u91CF: ${peakVolume.toFixed(6)}
  - \u5EFA\u8BAE\u9608\u503C: ${(peakVolume * 0.1).toFixed(6)}
        `);
        resolve({
          hasPermission: true,
          isReceivingAudio,
          averageVolume,
          peakVolume
        });
      }, 5e3);
    });
  }
};
async function debugAudioDetection(config) {
  const dbg = new AudioDetectorDebugger(config);
  await dbg.start();
  return dbg;
}
async function testMicrophone() {
  const dbg = new AudioDetectorDebugger();
  await dbg.testMicrophone();
  dbg.stop();
}
function useAudioDetection(options = {}) {
  const { autoStart = false, updateInterval = 100, ...config } = options;
  const [result, setResult] = useState(null);
  const [state, setState] = useState("idle");
  const [error, setError] = useState(null);
  const [isDetecting, setIsDetecting] = useState(false);
  const detectorRef = useRef(null);
  const lastUpdateRef = useRef(0);
  useEffect(() => {
    const detector = new AudioDetector(config, {
      onDetection: (detectionResult) => {
        const now = Date.now();
        if (now - lastUpdateRef.current >= updateInterval) {
          setResult(detectionResult);
          lastUpdateRef.current = now;
        }
      },
      onStateChange: (newState) => {
        setState(newState);
      },
      onError: (err) => {
        setError(err);
        setIsDetecting(false);
      }
    });
    detectorRef.current = detector;
    if (autoStart) {
      detector.start().catch((err) => {
        setError(err);
      });
      setIsDetecting(true);
    }
    return () => {
      if (detector.isRunning()) {
        detector.stop();
      }
    };
  }, []);
  const start = useCallback(async () => {
    if (!detectorRef.current) {
      const err = new Error("\u68C0\u6D4B\u5668\u672A\u521D\u59CB\u5316");
      setError(err);
      throw err;
    }
    try {
      setError(null);
      await detectorRef.current.start();
      setIsDetecting(true);
    } catch (err) {
      const error2 = err instanceof Error ? err : new Error(String(err));
      setError(error2);
      setIsDetecting(false);
      throw error2;
    }
  }, []);
  const stop = useCallback(() => {
    if (detectorRef.current) {
      detectorRef.current.stop();
      setIsDetecting(false);
      setResult(null);
    }
  }, []);
  const getDetector = useCallback(() => {
    return detectorRef.current;
  }, []);
  return {
    result,
    state,
    isDetecting,
    error,
    start,
    stop,
    getDetector
  };
}
var AudioDetectionDisplay = ({
  className = "",
  showDebugInfo = false,
  renderNote,
  renderChord,
  startButtonText = "\u5F00\u59CB\u68C0\u6D4B",
  stopButtonText = "\u505C\u6B62\u68C0\u6D4B",
  ...options
}) => {
  const { result, state, isDetecting, error, start, stop } = useAudioDetection(options);
  const handleToggle = async () => {
    if (isDetecting) {
      stop();
    } else {
      try {
        await start();
      } catch (err) {
        console.error("\u542F\u52A8\u5931\u8D25:", err);
      }
    }
  };
  return /* @__PURE__ */ React.createElement("div", { className: clsx("audio-detection-display", className) }, /* @__PURE__ */ React.createElement("div", { className: "audio-detection-controls" }, /* @__PURE__ */ React.createElement(
    "button",
    {
      onClick: handleToggle,
      disabled: state === "initializing",
      className: clsx("audio-detection-button", isDetecting ? "active" : "")
    },
    state === "initializing" ? "\u521D\u59CB\u5316\u4E2D..." : isDetecting ? stopButtonText : startButtonText
  ), /* @__PURE__ */ React.createElement("div", { className: clsx("audio-detection-status status-", state) }, "\u72B6\u6001: ", getStateLabel(state))), error && /* @__PURE__ */ React.createElement("div", { className: "audio-detection-error" }, /* @__PURE__ */ React.createElement("strong", null, "\u9519\u8BEF:"), " ", error.message), result && result.isDetecting && /* @__PURE__ */ React.createElement("div", { className: "audio-detection-result" }, result.notes.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "audio-detection-notes" }, /* @__PURE__ */ React.createElement("h3", null, "\u68C0\u6D4B\u5230\u7684\u97F3\u7B26:"), /* @__PURE__ */ React.createElement("div", { className: "notes-grid" }, result.notes.map((note, index) => /* @__PURE__ */ React.createElement("div", { key: index, className: "note-item" }, renderNote ? renderNote(note) : /* @__PURE__ */ React.createElement(DefaultNoteDisplay, { note }))))), result.chord && /* @__PURE__ */ React.createElement("div", { className: "audio-detection-chord" }, /* @__PURE__ */ React.createElement("h3", null, "\u8BC6\u522B\u7684\u548C\u5F26:"), renderChord ? renderChord(result.chord) : /* @__PURE__ */ React.createElement(DefaultChordDisplay, { chord: result.chord })), showDebugInfo && /* @__PURE__ */ React.createElement("div", { className: "audio-detection-debug" }, /* @__PURE__ */ React.createElement("h4", null, "\u8C03\u8BD5\u4FE1\u606F:"), /* @__PURE__ */ React.createElement("pre", null, JSON.stringify(result, null, 2)))), isDetecting && (!result || !result.isDetecting) && /* @__PURE__ */ React.createElement("div", { className: "audio-detection-waiting" }, "\u6B63\u5728\u76D1\u542C... \u8BF7\u5F39\u594F\u7535\u5B50\u7434"));
};
var DefaultNoteDisplay = ({ note }) => /* @__PURE__ */ React.createElement("div", { className: "default-note-display" }, /* @__PURE__ */ React.createElement("div", { className: "note-name" }, note.name), /* @__PURE__ */ React.createElement("div", { className: "note-frequency" }, note.frequency.toFixed(2), " Hz"), /* @__PURE__ */ React.createElement("div", { className: "note-confidence" }, "\u7F6E\u4FE1\u5EA6: ", (note.confidence * 100).toFixed(0), "%"));
var DefaultChordDisplay = ({ chord }) => /* @__PURE__ */ React.createElement("div", { className: "default-chord-display" }, /* @__PURE__ */ React.createElement("div", { className: "chord-name" }, chord.name), /* @__PURE__ */ React.createElement("div", { className: "chord-type" }, "\u7C7B\u578B: ", chord.type), /* @__PURE__ */ React.createElement("div", { className: "chord-notes" }, "\u97F3\u7B26: ", chord.notes.map((n) => n.name).join(", ")), /* @__PURE__ */ React.createElement("div", { className: "chord-confidence" }, "\u7F6E\u4FE1\u5EA6: ", (chord.confidence * 100).toFixed(0), "%"));
function getStateLabel(state) {
  const labels = {
    idle: "\u7A7A\u95F2",
    initializing: "\u521D\u59CB\u5316\u4E2D",
    active: "\u8FD0\u884C\u4E2D",
    error: "\u9519\u8BEF",
    stopped: "\u5DF2\u505C\u6B62"
  };
  return labels[state] || state;
}
var audioDetectionStyles = `
.audio-detection-display {
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

.audio-detection-controls {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-bottom: 20px;
}

.audio-detection-button {
  padding: 10px 20px;
  font-size: 16px;
  border: none;
  border-radius: 6px;
  background-color: #007bff;
  color: white;
  cursor: pointer;
  transition: background-color 0.3s;
}

.audio-detection-button:hover {
  background-color: #0056b3;
}

.audio-detection-button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}

.audio-detection-button.active {
  background-color: #dc3545;
}

.audio-detection-button.active:hover {
  background-color: #c82333;
}

.audio-detection-status {
  padding: 8px 16px;
  border-radius: 4px;
  font-size: 14px;
}

.status-idle { background-color: #e7f3ff; color: #004085; }
.status-initializing { background-color: #fff3cd; color: #856404; }
.status-active { background-color: #d4edda; color: #155724; }
.status-error { background-color: #f8d7da; color: #721c24; }
.status-stopped { background-color: #e2e3e5; color: #383d41; }

.audio-detection-error {
  padding: 12px;
  background-color: #f8d7da;
  border: 1px solid #f5c6cb;
  border-radius: 4px;
  color: #721c24;
  margin-bottom: 16px;
}

.audio-detection-result {
  margin-top: 20px;
}

.audio-detection-notes h3,
.audio-detection-chord h3 {
  margin-bottom: 12px;
  font-size: 18px;
}

.notes-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
}

.note-item {
  padding: 12px;
  border: 2px solid #007bff;
  border-radius: 6px;
  background-color: #f8f9fa;
}

.default-note-display {
  text-align: center;
}

.note-name {
  font-size: 24px;
  font-weight: bold;
  color: #007bff;
  margin-bottom: 4px;
}

.note-frequency {
  font-size: 14px;
  color: #666;
  margin-bottom: 4px;
}

.note-confidence {
  font-size: 12px;
  color: #999;
}

.audio-detection-chord {
  margin-top: 20px;
  padding: 16px;
  border: 2px solid #28a745;
  border-radius: 8px;
  background-color: #f8f9fa;
}

.default-chord-display {
  text-align: center;
}

.chord-name {
  font-size: 32px;
  font-weight: bold;
  color: #28a745;
  margin-bottom: 8px;
}

.chord-type,
.chord-notes,
.chord-confidence {
  margin-bottom: 4px;
  color: #666;
}

.audio-detection-waiting {
  text-align: center;
  padding: 40px;
  color: #666;
  font-size: 18px;
}

.audio-detection-debug {
  margin-top: 20px;
  padding: 12px;
  background-color: #f4f4f4;
  border-radius: 4px;
}

.audio-detection-debug pre {
  margin: 8px 0 0 0;
  font-size: 12px;
  overflow-x: auto;
}
`;
var PianoKeyboard = ({
  activeNotes = [],
  startOctave = 2,
  endOctave = 6,
  className = "",
  showNoteNames = true
}) => {
  const keys = useMemo(() => {
    const noteNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
    const blackKeys2 = /* @__PURE__ */ new Set(["C#", "D#", "F#", "G#", "A#"]);
    const allKeys = [];
    for (let octave = startOctave; octave <= endOctave; octave++) {
      for (const noteName of noteNames) {
        const midi = (octave + 1) * 12 + noteNames.indexOf(noteName);
        const isActive = activeNotes.some((note) => note.midi === midi);
        allKeys.push({
          noteName,
          octave,
          midi,
          isBlack: blackKeys2.has(noteName),
          isActive
        });
      }
    }
    return allKeys;
  }, [startOctave, endOctave, activeNotes]);
  const whiteKeys = keys.filter((k) => !k.isBlack);
  const blackKeys = keys.filter((k) => k.isBlack);
  return /* @__PURE__ */ React.createElement("div", { className: clsx("piano-keyboard", className) }, /* @__PURE__ */ React.createElement("div", { className: "piano-keys-container" }, /* @__PURE__ */ React.createElement("div", { className: "white-keys" }, whiteKeys.map((key, index) => /* @__PURE__ */ React.createElement(
    "div",
    {
      key: "white-" + key.midi,
      className: clsx("piano-key white-key", key.isActive ? "active" : ""),
      title: key.noteName + key.octave
    },
    showNoteNames && /* @__PURE__ */ React.createElement("span", { className: "key-label" }, key.noteName, key.octave)
  ))), /* @__PURE__ */ React.createElement("div", { className: "black-keys" }, blackKeys.map((key) => {
    const whiteKeyIndex = getWhiteKeyIndexBeforeBlack(key.noteName);
    const octaveOffset = (key.octave - startOctave) * 7;
    const position = whiteKeyIndex + octaveOffset;
    return /* @__PURE__ */ React.createElement(
      "div",
      {
        key: "black-" + key.midi,
        className: clsx("piano-key black-key", key.isActive ? "active" : ""),
        style: { left: (position + 0.7) * (100 / whiteKeys.length) + "%" },
        title: key.noteName + key.octave
      },
      showNoteNames && /* @__PURE__ */ React.createElement("span", { className: "key-label" }, key.noteName, key.octave)
    );
  }))));
};
function getWhiteKeyIndexBeforeBlack(noteName) {
  const map = {
    "C#": 0,
    "D#": 1,
    "F#": 3,
    "G#": 4,
    "A#": 5
  };
  return map[noteName] || 0;
}
var pianoKeyboardStyles = `
.piano-keyboard {
  width: 100%;
  padding: 20px;
  background-color: #2c3e50;
  border-radius: 8px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}

.piano-keys-container {
  position: relative;
  width: 100%;
  height: 200px;
}

.white-keys {
  display: flex;
  height: 100%;
  gap: 2px;
}

.piano-key {
  position: relative;
  border: 1px solid #000;
  cursor: pointer;
  transition: all 0.15s ease;
}

.white-key {
  flex: 1;
  background: linear-gradient(to bottom, #ffffff 0%, #f0f0f0 100%);
  border-radius: 0 0 4px 4px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 8px;
}

.white-key:hover {
  background: linear-gradient(to bottom, #f8f8f8 0%, #e8e8e8 100%);
}

.white-key.active {
  background: linear-gradient(to bottom, #4CAF50 0%, #45a049 100%) !important;
  box-shadow: 0 0 20px rgba(76, 175, 80, 0.8);
}

.black-keys {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 60%;
  pointer-events: none;
}

.black-key {
  position: absolute;
  width: calc(100% / 52 * 0.6); /* \u5047\u8BBE\u670952\u4E2A\u767D\u952E\u7684\u5BBD\u5EA6 */
  height: 100%;
  background: linear-gradient(to bottom, #2c3e50 0%, #1a252f 100%);
  border-radius: 0 0 3px 3px;
  pointer-events: all;
  transform: translateX(-50%);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 6px;
  z-index: 10;
}

.black-key:hover {
  background: linear-gradient(to bottom, #34495e 0%, #2c3e50 100%);
}

.black-key.active {
  background: linear-gradient(to bottom, #4CAF50 0%, #388E3C 100%) !important;
  box-shadow: 0 0 20px rgba(76, 175, 80, 0.8);
}

.key-label {
  font-size: 11px;
  font-weight: bold;
  user-select: none;
}

.white-key .key-label {
  color: #666;
}

.white-key.active .key-label {
  color: white;
}

.black-key .key-label {
  color: #ddd;
}

.black-key.active .key-label {
  color: white;
}
`;

export { AudioDetectionDisplay, AudioDetector, AudioDetectorDebugger, AudioInputService, ChordRecognizer, PianoKeyboard, PitchDetector, audioDetectionStyles, debugAudioDetection, pianoKeyboardStyles, testMicrophone, useAudioDetection };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map