Files
Pine 7f25c8f6bb refactor(backend): 启动环境初始化与资源项目化(零跨项目依赖)
- main.py:uv run main.py 一键启动——读取 backend/.env、TORCH_HOME/NLTK_DATA 指向项目内缓存、certifi SSL + NO_PROXY 网络直连、默认开启 s2s,启动横幅
- vendor/:补丁后 s2s 云化栈移入项目(backend/vendor/s2s-cloud),pyproject sources 改相对路径
- .gitignore:忽略 .torch-cache / nltk_data 缓存
- AGENTS.md:进程管理铁律(AI 不启动/重启服务,由用户操作)
2026-08-18 01:37:57 +08:00

76 lines
3.2 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>麦克风诊断</title>
<style>
body { font-family: system-ui, -apple-system, "PingFang SC", sans-serif; max-width: 560px;
margin: 0 auto; padding: 16px; background: #14161a; color: #e6e8eb; }
h1 { font-size: 18px; }
button { padding: 10px 18px; border-radius: 8px; border: none; background: #1f6feb;
color: #fff; font-size: 14px; cursor: pointer; }
pre { background: #1c1f24; padding: 12px; border-radius: 8px; overflow-x: auto; font-size: 13px; }
.ok { color: #3fb950; } .bad { color: #f85149; }
.meter { height: 18px; width: 100%; background: #1c1f24; border-radius: 4px; margin: 6px 0; overflow: hidden; }
.meter > div { height: 100%; width: 0%; background: #3fb950; transition: width 60ms linear; }
</style>
</head>
<body>
<h1>🎤 麦克风诊断页(只请求麦克风)</h1>
<p>在 <b>你刚才打开主页面的那个浏览器窗口</b> 打开本页,点下面按钮。</p>
<button id="go">请求麦克风并测试</button>
<pre id="out">等待操作…</pre>
<div class="meter"><div id="bar"></div></div>
<script>
const out = document.getElementById("out");
const bar = document.getElementById("bar");
const log = (s, cls = "") => { out.innerHTML += (cls ? `<span class="${cls}">` : "") + s.replace(/</g, "&lt;") + (cls ? "</span>" : "") + "\n"; };
document.getElementById("go").onclick = async () => {
out.innerHTML = "";
log("1) 环境检查…");
log(" secureContext = " + window.isSecureContext + " (必须是 true)");
log(" navigator.mediaDevices = " + (navigator.mediaDevices ? "存在" : "不存在"));
log("2) 枚举设备…");
try {
const devs = await navigator.mediaDevices.enumerateDevices();
for (const d of devs) log(" [" + d.kind + "] " + (d.label || "(无标签=权限未授予)"));
if (!devs.some(d => d.kind === "audioinput"))
log(" ⚠️ 系统里没发现任何麦克风设备(驱动/虚拟声卡问题)", "bad");
} catch (e) { log(" 枚举失败: " + e.name + ": " + e.message, "bad"); }
log("3) getUserMedia…");
let stream;
try {
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
log(" ✅ 麦克风授权成功", "ok");
} catch (e) {
log(" ❌ " + e.name + ": " + e.message, "bad");
log(" (NotAllowedError=权限被拒;NotFoundError=无设备;NotReadableError=被其他应用占用)");
return;
}
log("4) 电平检测(说话时绿条应跳动)…");
const ctx = new AudioContext();
const src = ctx.createMediaStreamSource(stream);
const an = ctx.createAnalyser(); an.fftSize = 256;
src.connect(an);
const buf = new Uint8Array(an.frequencyBinCount);
let peak = 0;
setInterval(() => {
an.getByteTimeDomainData(buf);
let sum = 0;
for (let i = 0; i < buf.length; i++) sum += Math.abs(buf[i] - 128);
const rms = sum / buf.length;
if (rms > peak) peak = rms;
bar.style.width = Math.min(100, (rms / 40) * 100) + "%";
}, 80);
setTimeout(() => log(" 请对着麦克风说话,看上面的绿条。当前峰值=" + peak.toFixed(1) + "/127(≥10 说明有声音进)"), 2000);
};
</script>
</body>
</html>