function debugLog(message, data) {
  if (data !== void 0) {
    console.log(`[sox-shim] ${message}`, data);
  } else {
    console.log(`[sox-shim] ${message}`);
  }
}
function computeRms(samples) {
  let sum = 0;
  for (let i = 0; i < samples.length; i++) {
    sum += samples[i] * samples[i];
  }
  return Math.sqrt(sum / (samples.length || 1));
}
function computePeak(samples) {
  let peak = 0;
  for (let i = 0; i < samples.length; i++) {
    const abs = Math.abs(samples[i]);
    if (abs > peak) peak = abs;
  }
  return peak;
}
const DEFAULT_CONFIG = {
  sampleRate: 16e3,
  bitDepth: 16,
  channels: 1,
  encoding: "signed",
  format: "raw",
  silence: null,
  quiet: false
};
function parseSoxArgs(command, args) {
  const config = { ...DEFAULT_CONFIG };
  const normalizedArgs = [...args];
  if (command === "sox" || command.endsWith("/sox")) {
    const dIdx = normalizedArgs.indexOf("-d");
    if (dIdx !== -1) normalizedArgs.splice(dIdx, 1);
  }
  let i = 0;
  while (i < normalizedArgs.length) {
    const arg = normalizedArgs[i];
    switch (arg) {
      case "-r":
      case "--rate":
        config.sampleRate = parseInt(normalizedArgs[++i], 10) || DEFAULT_CONFIG.sampleRate;
        break;
      case "-b":
      case "--bits":
        config.bitDepth = parseInt(normalizedArgs[++i], 10) || DEFAULT_CONFIG.bitDepth;
        break;
      case "-c":
      case "--channels":
        config.channels = parseInt(normalizedArgs[++i], 10) || DEFAULT_CONFIG.channels;
        break;
      case "-e":
      case "--encoding": {
        const enc = normalizedArgs[++i];
        if (enc === "signed" || enc === "signed-integer") config.encoding = "signed";
        else if (enc === "unsigned" || enc === "unsigned-integer") config.encoding = "unsigned";
        else if (enc === "float" || enc === "floating-point") config.encoding = "float";
        break;
      }
      case "-t":
      case "--type":
        config.format = normalizedArgs[++i] || DEFAULT_CONFIG.format;
        break;
      case "-q":
      case "--quiet":
        config.quiet = true;
        break;
      case "silence":
        config.silence = parseSilenceArgs(normalizedArgs, i + 1);
        i = normalizedArgs.length;
        continue;
    }
    i++;
  }
  return config;
}
function parseSilenceArgs(args, startIdx) {
  const remaining = args.slice(startIdx);
  const silenceConfig = {
    abovePeriods: 1,
    duration: 1,
    threshold: 1e-3,
    belowPeriods: 0,
    belowDuration: 0,
    belowThreshold: 0
  };
  if (remaining.length >= 6) {
    silenceConfig.belowPeriods = parseInt(remaining[0], 10) || 0;
    silenceConfig.belowDuration = parseFloat(remaining[1]) || 0;
    silenceConfig.belowThreshold = parseThreshold(remaining[2]);
    silenceConfig.abovePeriods = parseInt(remaining[3], 10) || 1;
    silenceConfig.duration = parseFloat(remaining[4]) || 1;
    silenceConfig.threshold = parseThreshold(remaining[5]);
  } else if (remaining.length >= 3) {
    silenceConfig.abovePeriods = parseInt(remaining[0], 10) || 1;
    silenceConfig.duration = parseFloat(remaining[1]) || 1;
    silenceConfig.threshold = parseThreshold(remaining[2]);
  }
  return silenceConfig;
}
function parseThreshold(value) {
  if (value.endsWith("%")) {
    return parseFloat(value) / 100;
  }
  if (value.endsWith("d")) {
    const db = parseFloat(value);
    return Math.pow(10, db / 20);
  }
  return parseFloat(value) || 1e-3;
}
function float32ToInt16PCM(samples) {
  const buffer = new Uint8Array(samples.length * 2);
  for (let i = 0; i < samples.length; i++) {
    let s = Math.max(-1, Math.min(1, samples[i]));
    s = s < 0 ? s * 32768 : s * 32767;
    const int16 = Math.round(s);
    buffer[i * 2] = int16 & 255;
    buffer[i * 2 + 1] = int16 >> 8 & 255;
  }
  return buffer;
}
function downsample(samples, fromRate, toRate) {
  if (fromRate === toRate) return samples;
  const ratio = fromRate / toRate;
  const outputLength = Math.floor(samples.length / ratio);
  const output = new Float32Array(outputLength);
  for (let i = 0; i < outputLength; i++) {
    const srcIndex = i * ratio;
    const srcIndexFloor = Math.floor(srcIndex);
    const frac = srcIndex - srcIndexFloor;
    if (srcIndexFloor + 1 < samples.length) {
      output[i] = samples[srcIndexFloor] * (1 - frac) + samples[srcIndexFloor + 1] * frac;
    } else {
      output[i] = samples[srcIndexFloor] || 0;
    }
  }
  return output;
}
function createSilenceDetector(config) {
  let phase = config.belowPeriods > 0 ? "below" : "above";
  let silentSamples = 0;
  let stopped = false;
  function rms(samples) {
    let sum = 0;
    for (let i = 0; i < samples.length; i++) {
      sum += samples[i] * samples[i];
    }
    return Math.sqrt(sum / (samples.length || 1));
  }
  return {
    process(samples, sampleRate) {
      if (stopped) return null;
      const level = rms(samples);
      if (phase === "below") {
        const threshold2 = config.belowThreshold;
        if (level > threshold2) {
          phase = "above";
          silentSamples = 0;
          return samples;
        }
        return null;
      }
      const threshold = config.threshold;
      if (level <= threshold) {
        silentSamples += samples.length;
        const silentDuration = silentSamples / sampleRate;
        if (silentDuration >= config.duration) {
          stopped = true;
          return null;
        }
      } else {
        silentSamples = 0;
      }
      return samples;
    },
    shouldStop() {
      return stopped;
    }
  };
}
function isAudioCaptureCommand(command, args) {
  const basename = command.slice(command.lastIndexOf("/") + 1);
  if (basename === "rec") return true;
  if (basename === "sox" && args.includes("-d")) return true;
  return false;
}
async function startAudioCapture(config, onData, onEnd, onError) {
  debugLog("startAudioCapture called with config:", {
    sampleRate: config.sampleRate,
    bitDepth: config.bitDepth,
    channels: config.channels,
    encoding: config.encoding,
    format: config.format,
    silence: config.silence,
    quiet: config.quiet
  });
  let stream;
  try {
    debugLog("Requesting getUserMedia...");
    stream = await navigator.mediaDevices.getUserMedia({
      audio: {
        echoCancellation: false,
        noiseSuppression: false,
        autoGainControl: false
      }
    });
    debugLog("getUserMedia succeeded, got stream with tracks:", stream.getTracks().length);
  } catch (err) {
    debugLog("getUserMedia FAILED:", err);
    onError(new Error(`Microphone access denied: ${err}`));
    return { cleanup: () => {
    } };
  }
  const audioContext = new AudioContext();
  debugLog("AudioContext created, initial state:", audioContext.state);
  if (audioContext.state === "suspended") {
    debugLog("AudioContext suspended, resuming...");
    await audioContext.resume();
    debugLog("AudioContext resumed, new state:", audioContext.state);
  }
  const source = audioContext.createMediaStreamSource(stream);
  const nativeRate = audioContext.sampleRate;
  debugLog("Native sample rate:", nativeRate);
  const bufferSize = 4096;
  const processor = audioContext.createScriptProcessor(bufferSize, config.channels, config.channels);
  let cleaned = false;
  let chunkCount = 0;
  let totalSamples = 0;
  let maxRms = 0;
  config.silence && false ? createSilenceDetector(config.silence) : null;
  if (config.silence) {
    debugLog("*** BYPASS_SILENCE_DETECTION is ON — ignoring silence config, emitting all audio");
  } else {
    debugLog("No silence config — recording will continue until killed");
  }
  const cleanup = () => {
    var _a, _b;
    if (cleaned) return;
    cleaned = true;
    debugLog("cleanup() called — FINAL STATE:", {
      chunkCount,
      totalSamples,
      maxRms: maxRms.toFixed(6),
      emittedChunks,
      skippedChunks,
      silencePhase,
      threshold: ((_b = (_a = config.silence) == null ? void 0 : _a.belowThreshold) == null ? void 0 : _b.toFixed(6)) || "none"
    });
    try {
      processor.disconnect();
      source.disconnect();
      audioContext.close();
      stream.getTracks().forEach((t) => t.stop());
    } catch {
    }
  };
  let emittedChunks = 0;
  let skippedChunks = 0;
  let silencePhase = "none";
  processor.onaudioprocess = (event) => {
    if (cleaned) return;
    const samples = new Float32Array(event.inputBuffer.getChannelData(0));
    const rms = computeRms(samples);
    const peak = computePeak(samples);
    chunkCount++;
    totalSamples += samples.length;
    if (rms > maxRms) maxRms = rms;
    if (chunkCount <= 5 || chunkCount % 20 === 1) {
      debugLog(`Chunk #${chunkCount}: rms=${rms.toFixed(6)}, peak=${peak.toFixed(6)}, phase=${silencePhase}, emitted=${emittedChunks}, skipped=${skippedChunks}`);
    }
    const processed = nativeRate !== config.sampleRate ? downsample(samples, nativeRate, config.sampleRate) : samples;
    {
      emittedChunks++;
      const pcmBytes = float32ToInt16PCM(processed);
      if (emittedChunks <= 3) {
        const processedRms = computeRms(processed);
        debugLog(`Emitting chunk #${emittedChunks}:`, {
          inputSamples: samples.length,
          outputSamples: processed.length,
          pcmBytes: pcmBytes.length,
          rms: processedRms.toFixed(6),
          peak: computePeak(processed).toFixed(6),
          firstPcmBytes: Array.from(pcmBytes.slice(0, 10)).map((b) => b.toString(16).padStart(2, "0")).join(" "),
          expectedRate: config.sampleRate,
          nativeRate
        });
      }
      onData(pcmBytes);
    }
  };
  source.connect(processor);
  processor.connect(audioContext.destination);
  debugLog("Audio pipeline connected, capture active");
  return { cleanup };
}
export {
  createSilenceDetector,
  downsample,
  float32ToInt16PCM,
  isAudioCaptureCommand,
  parseSoxArgs,
  startAudioCapture
};
//# sourceMappingURL=sox-audio-capture-DU2-wrc7.js.map
