Spaces:
Running
Running
add demo video + corrected RTF table (5-round medians)
Browse files- .gitattributes +1 -0
- README.md +16 -2
- demo.mp4 +3 -0
- demo.mp4.json +75 -0
- index.html +21 -8
.gitattributes
CHANGED
|
@@ -46,3 +46,4 @@ static/videos/shiba.mp4 filter=lfs diff=lfs merge=lfs -text
|
|
| 46 |
static/videos/steve.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 47 |
static/videos/teaser.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 48 |
static/videos/toby.mp4 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 46 |
static/videos/steve.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 47 |
static/videos/teaser.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 48 |
static/videos/toby.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
demo.mp4 filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -13,7 +13,7 @@ short_description: 100% offline ASR for meetings — local, no upload
|
|
| 13 |
|
| 14 |
100% 离线的会议 / 课堂转写。没有账号,没有云端,拔掉网线它还在写。
|
| 15 |
|
| 16 |
-
这个 Space 是**静态证据页**:五个语种的实测输出、RTF
|
| 17 |
不放可交互 demo 的原因很实际——转写要在服务端跑 229 MiB 的模型,
|
| 18 |
而 Hugging Face 免费账号不能运行 CPU 上的 Gradio 容器(`cpu-basic` 返回配额错误,
|
| 19 |
`zero-a10g` 返回 402,官方文档写明 Gradio/Docker Space 需要付费计划)。
|
|
@@ -23,10 +23,24 @@ short_description: 100% offline ASR for meetings — local, no upload
|
|
| 23 |
| 文件 | 用途 |
|
| 24 |
|---|---|
|
| 25 |
| `index.html` | 实际展示的页面 |
|
|
|
|
|
|
|
| 26 |
| `README.md` | 本文件,Space 卡片元数据 |
|
| 27 |
| `app.py`、`requirements.txt` | **Gradio 版**,本地已跑通;当前 `sdk: static` 不执行它。升级 PRO 后把 frontmatter 改回 `sdk: gradio` 即可切换 |
|
| 28 |
|
| 29 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
权重在 [ToothpasteLake/offlisten-models](https://huggingface.co/ToothpasteLake/offlisten-models),
|
| 32 |
6 个文件 229.07 MiB,逐文件 sha256 对应发行清单。
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
100% 离线的会议 / 课堂转写。没有账号,没有云端,拔掉网线它还在写。
|
| 15 |
|
| 16 |
+
这个 Space 是**静态证据页**:五个语种的实测输出、RTF 分布、演示视频。
|
| 17 |
不放可交互 demo 的原因很实际——转写要在服务端跑 229 MiB 的模型,
|
| 18 |
而 Hugging Face 免费账号不能运行 CPU 上的 Gradio 容器(`cpu-basic` 返回配额错误,
|
| 19 |
`zero-a10g` 返回 402,官方文档写明 Gradio/Docker Space 需要付费计划)。
|
|
|
|
| 23 |
| 文件 | 用途 |
|
| 24 |
|---|---|
|
| 25 |
| `index.html` | 实际展示的页面 |
|
| 26 |
+
| `demo.mp4` | 42 秒演示:五段音频真跑,听完才落字;`tools/record-demo.py` 生成 |
|
| 27 |
+
| `demo.mp4.json` | 那次录制的实测记录:每段文本、耗时、音频 sha256 |
|
| 28 |
| `README.md` | 本文件,Space 卡片元数据 |
|
| 29 |
| `app.py`、`requirements.txt` | **Gradio 版**,本地已跑通;当前 `sdk: static` 不执行它。升级 PRO 后把 frontmatter 改回 `sdk: gradio` 即可切换 |
|
| 30 |
|
| 31 |
+
## 演示是怎么录的
|
| 32 |
+
|
| 33 |
+
不是手工录屏。`tools/record-demo.py` 做三件事,所以视频和上表不可能对不上:
|
| 34 |
+
|
| 35 |
+
1. 用同一份权重、同样 2 线程,真跑五段 `test_wavs`,拿到文本与耗时
|
| 36 |
+
2. 起本地 `web_live` 服务与无头 Edge,把每段结果按真实时钟推给页面,逐帧截图
|
| 37 |
+
3. 按同一时钟把五段音频混成音轨,ffmpeg 合成 H.264 + AAC
|
| 38 |
+
|
| 39 |
+
因此视频里“字幕出现的时刻”就是产品里“一段话说完的时刻”,音画不会漂。
|
| 40 |
+
|
| 41 |
+
## 模型与音频
|
| 42 |
|
| 43 |
权重在 [ToothpasteLake/offlisten-models](https://huggingface.co/ToothpasteLake/offlisten-models),
|
| 44 |
6 个文件 229.07 MiB,逐文件 sha256 对应发行清单。
|
| 45 |
+
表里那五段音频在同一个仓库的 `test_wavs/` 下,点语种名可以直接听,也可以下载后自己复跑。
|
| 46 |
+
|
demo.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f6a598fae1ae7550cbf7aece507cac5ce8a0457634f3e4456c7e9e8038f16ac6
|
| 3 |
+
size 801685
|
demo.mp4.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"generated_at": "2026-11-12T23:15:38",
|
| 3 |
+
"model": "SenseVoice int8, use_itn=True, threads=2",
|
| 4 |
+
"video": "demo.mp4",
|
| 5 |
+
"video_bytes": 801685,
|
| 6 |
+
"video_seconds": 41.85,
|
| 7 |
+
"frames": 189,
|
| 8 |
+
"rows": [
|
| 9 |
+
{
|
| 10 |
+
"lang": "zh",
|
| 11 |
+
"label": "中",
|
| 12 |
+
"wav": "zh.wav",
|
| 13 |
+
"duration": 5.59,
|
| 14 |
+
"elapsed": 0.45,
|
| 15 |
+
"rtf": 0.081,
|
| 16 |
+
"rtf_min": 0.081,
|
| 17 |
+
"rtf_max": 0.081,
|
| 18 |
+
"rounds": 1,
|
| 19 |
+
"text": "开饭时间早上9点至下午5点。",
|
| 20 |
+
"sha256": "b77f1794fe374a0ba1ee1dc458bfaf9349496cbbfc32780c50ba3c5a7ad8e373"
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"lang": "en",
|
| 24 |
+
"label": "英",
|
| 25 |
+
"wav": "en.wav",
|
| 26 |
+
"duration": 7.15,
|
| 27 |
+
"elapsed": 0.805,
|
| 28 |
+
"rtf": 0.113,
|
| 29 |
+
"rtf_min": 0.113,
|
| 30 |
+
"rtf_max": 0.113,
|
| 31 |
+
"rounds": 1,
|
| 32 |
+
"text": "The tribal chieftain called for the boy and presented him with 50 pieces of code.",
|
| 33 |
+
"sha256": "eb1eb008904465b74c304aad8342e8c7d3c6e61ffe9f66adcaca9cf0f76a93f4"
|
| 34 |
+
},
|
| 35 |
+
{
|
| 36 |
+
"lang": "ja",
|
| 37 |
+
"label": "日",
|
| 38 |
+
"wav": "ja.wav",
|
| 39 |
+
"duration": 7.2,
|
| 40 |
+
"elapsed": 0.623,
|
| 41 |
+
"rtf": 0.087,
|
| 42 |
+
"rtf_min": 0.087,
|
| 43 |
+
"rtf_max": 0.087,
|
| 44 |
+
"rounds": 1,
|
| 45 |
+
"text": "うちの中学は弁当制で持っていきない場合は、50円の学校販売のパンを買う。",
|
| 46 |
+
"sha256": "460bd8dccb0d2a5f4e29c628f837be4082d13defc64c3fc21dd1b6bb0e119095"
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"lang": "ko",
|
| 50 |
+
"label": "韩",
|
| 51 |
+
"wav": "ko.wav",
|
| 52 |
+
"duration": 4.61,
|
| 53 |
+
"elapsed": 0.352,
|
| 54 |
+
"rtf": 0.076,
|
| 55 |
+
"rtf_min": 0.076,
|
| 56 |
+
"rtf_max": 0.076,
|
| 57 |
+
"rounds": 1,
|
| 58 |
+
"text": "조금만 생각을 하면서 살면 훨씬 편할 거야.",
|
| 59 |
+
"sha256": "0dc797a5c81ed30fc339d91f3da718ab02854e17ffa37cb93c4c039ac5c6bb9c"
|
| 60 |
+
},
|
| 61 |
+
{
|
| 62 |
+
"lang": "yue",
|
| 63 |
+
"label": "粤",
|
| 64 |
+
"wav": "yue.wav",
|
| 65 |
+
"duration": 5.15,
|
| 66 |
+
"elapsed": 0.402,
|
| 67 |
+
"rtf": 0.078,
|
| 68 |
+
"rtf_min": 0.078,
|
| 69 |
+
"rtf_max": 0.078,
|
| 70 |
+
"rounds": 1,
|
| 71 |
+
"text": "呢几个字都表达唔到我想讲嘅意思。",
|
| 72 |
+
"sha256": "0960b2db54ae202071d250e6462fbf74a3c863f0e3e7f01273e4939c996875a0"
|
| 73 |
+
}
|
| 74 |
+
]
|
| 75 |
+
}
|
index.html
CHANGED
|
@@ -39,6 +39,8 @@
|
|
| 39 |
tr:last-child td { border-bottom: 0; }
|
| 40 |
td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
|
| 41 |
td.out { font-size: 13px; color: #d4d9de; }
|
|
|
|
|
|
|
| 42 |
video { width: 100%; border: 1px solid var(--line); border-radius: 8px; background: #000; display: block; }
|
| 43 |
.videohint {
|
| 44 |
border: 1px dashed var(--line); border-radius: 8px; padding: 28px 20px;
|
|
@@ -73,20 +75,25 @@
|
|
| 73 |
<div class="tablewrap">
|
| 74 |
<table>
|
| 75 |
<thead>
|
| 76 |
-
<tr><th>音频</th><th>时长</th><th>耗时</th><th>RTF</th><th>输出</th></tr>
|
| 77 |
</thead>
|
| 78 |
<tbody>
|
| 79 |
-
<tr><td>中</td><td class="num">5.6 s</td><td class="num">0.
|
| 80 |
-
<tr><td>英</td><td class="num">7.2 s</td><td class="num">0.
|
| 81 |
-
<tr><td>日</td><td class="num">7.2 s</td><td class="num">0.
|
| 82 |
-
<tr><td>韩</td><td class="num">4.6 s</td><td class="num">0.
|
| 83 |
-
<tr><td>粤</td><td class="num">5.1 s</td><td class="num">0.
|
| 84 |
</tbody>
|
| 85 |
</table>
|
| 86 |
</div>
|
| 87 |
|
| 88 |
<p class="note" style="margin-top:12px">
|
| 89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
<br>
|
| 91 |
一处如实标注:同一段中文,官方示例作「<b>开放</b>时间」,int8 量化下这里是「<b>开饭</b>时间」——
|
| 92 |
我们把它留在表里,而不是挑一条好看的。
|
|
@@ -95,7 +102,13 @@ RTF = 转写耗时 ÷ 音频时长,小于 1 表示比实时快。上表是 Win
|
|
| 95 |
<h2>演示</h2>
|
| 96 |
|
| 97 |
<video id="demo" controls preload="metadata" src="./demo.mp4" hidden></video>
|
| 98 |
-
<p class="videohint" id="demo-hint">演示视频
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
<h2>它和云端转写的区别</h2>
|
| 101 |
<ul>
|
|
|
|
| 39 |
tr:last-child td { border-bottom: 0; }
|
| 40 |
td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
|
| 41 |
td.out { font-size: 13px; color: #d4d9de; }
|
| 42 |
+
th .thsub { font-weight: 400; font-size: 11px; color: var(--dim); }
|
| 43 |
+
td .rng { font-size: 11px; color: var(--dim); font-weight: 400; }
|
| 44 |
video { width: 100%; border: 1px solid var(--line); border-radius: 8px; background: #000; display: block; }
|
| 45 |
.videohint {
|
| 46 |
border: 1px dashed var(--line); border-radius: 8px; padding: 28px 20px;
|
|
|
|
| 75 |
<div class="tablewrap">
|
| 76 |
<table>
|
| 77 |
<thead>
|
| 78 |
+
<tr><th>音频</th><th>时长</th><th>耗时</th><th>RTF<br><span class="thsub">5 轮中位数</span></th><th>输出</th></tr>
|
| 79 |
</thead>
|
| 80 |
<tbody>
|
| 81 |
+
<tr><td><a href="https://huggingface.co/ToothpasteLake/offlisten-models/resolve/main/test_wavs/zh.wav">中</a></td><td class="num">5.6 s</td><td class="num">0.56 s</td><td class="num">0.100<br><span class="rng">0.087–0.117</span></td><td class="out">开饭时间早上9点至下午5点。</td></tr>
|
| 82 |
+
<tr><td><a href="https://huggingface.co/ToothpasteLake/offlisten-models/resolve/main/test_wavs/en.wav">英</a></td><td class="num">7.2 s</td><td class="num">0.59 s</td><td class="num">0.083<br><span class="rng">0.077–0.090</span></td><td class="out">The tribal chieftain called for the boy and presented him with 50 pieces of code.</td></tr>
|
| 83 |
+
<tr><td><a href="https://huggingface.co/ToothpasteLake/offlisten-models/resolve/main/test_wavs/ja.wav">日</a></td><td class="num">7.2 s</td><td class="num">0.65 s</td><td class="num">0.090<br><span class="rng">0.080–0.108</span></td><td class="out">うちの中学は弁当制で持っていきない場合は、50円の学校販売のパンを買う。</td></tr>
|
| 84 |
+
<tr><td><a href="https://huggingface.co/ToothpasteLake/offlisten-models/resolve/main/test_wavs/ko.wav">韩</a></td><td class="num">4.6 s</td><td class="num">0.38 s</td><td class="num">0.082<br><span class="rng">0.080–0.087</span></td><td class="out">조금만 생각을 하면서 살면 훨씬 편할 거야.</td></tr>
|
| 85 |
+
<tr><td><a href="https://huggingface.co/ToothpasteLake/offlisten-models/resolve/main/test_wavs/yue.wav">粤</a></td><td class="num">5.1 s</td><td class="num">0.42 s</td><td class="num">0.081<br><span class="rng">0.078–0.099</span></td><td class="out">呢几个字都表达唔到我想讲嘅意思。</td></tr>
|
| 86 |
</tbody>
|
| 87 |
</table>
|
| 88 |
</div>
|
| 89 |
|
| 90 |
<p class="note" style="margin-top:12px">
|
| 91 |
+
点语种名可以直接听那段音频——五段都在
|
| 92 |
+
<a href="https://huggingface.co/ToothpasteLake/offlisten-models/tree/main/test_wavs">模型仓库的 test_wavs/</a>,
|
| 93 |
+
和模型一样可下载。转写耗时是 2 个 CPU 线程的中位数,括号里是五轮的极值。
|
| 94 |
+
<br>
|
| 95 |
+
<b>一行精确到三位小数的 RTF 是噪声,不是一个值得炫耀的指标。</b>同一段音频连跑五次能落在 0.077–0.117 之间,
|
| 96 |
+
所以上表只能读出一件事:处理速度大约是实时的十倍。要当真 benchmark 用,得固定机器、关掉后台、多轮取分布。
|
| 97 |
<br>
|
| 98 |
一处如实标注:同一段中文,官方示例作「<b>开放</b>时间」,int8 量化下这里是「<b>开饭</b>时间」——
|
| 99 |
我们把它留在表里,而不是挑一条好看的。
|
|
|
|
| 102 |
<h2>演示</h2>
|
| 103 |
|
| 104 |
<video id="demo" controls preload="metadata" src="./demo.mp4" hidden></video>
|
| 105 |
+
<p class="videohint" id="demo-hint">演示视频加载中;若浏览器不支持 mp4,可直接<a href="./demo.mp4">下载观看</a>。</p>
|
| 106 |
+
|
| 107 |
+
<p class="note">
|
| 108 |
+
这 42 秒里没有写好的台词:五段音频先被模型听,听完才落字,和产品里一样按停顿切段。
|
| 109 |
+
界面是产品的本地文本页,以纯回放模式启动,所以控制区显示「只读文本页」——正常桌面运行时那里是开始/暂停。
|
| 110 |
+
视频里出现的每个字节都只走过 <code>127.0.0.1</code>。
|
| 111 |
+
</p>
|
| 112 |
|
| 113 |
<h2>它和云端转写的区别</h2>
|
| 114 |
<ul>
|