Quill使用指南:~/Recordings里的mic.caf、meta.json、transcript.md逐个文件讲明白
【免费下载链接】quillUltra-minimalist macOS recording + transcription.项目地址: https://gitcode.com/gh_mirrors/quill26/quill
Quill 是一款完全本地化的 macOS 会议录音 + 语音转写工具:它把麦克风(你)和系统声音(对方)录成两条独立音轨,在设备上自动转写,并生成带时间戳和me/them说话人标记的会议记录。所有数据都留在你自己的 Mac 上。这篇指南带你逐个看懂~/Recordings里生成的mic.caf、meta.json、transcript.md等文件。
📁 录音存在哪里?
每次点击菜单栏羽毛图标开始录音,Quill 就会在~/Recordings/下创建一个以时间戳命名的文件夹,例如:
~/Recordings/2026.09.29-1430/命名格式为yyyy.MM.dd-HHmm(年月日-时分),撞名时自动追加-2、-3后缀。停录后,转写自动开始,完成后你会收到一条桌面通知。
一个完整会话文件夹里通常有这些文件:
| 文件 | 内容 | 谁生成的 |
|---|---|---|
mic.caf | 你的声音(默认输入设备,AAC 编码) | 录音时实时写入 |
system.caf | Mac 播放的一切声音——通话的另一方(AAC 编码) | 录音时实时写入 |
mic-002.caf、system-002.caf… | 附加音频分段,仅当录音中途需要恢复采集时出现 | 录音时实时写入 |
meta.json | 起止时间、时长、分段偏移、采集状态 | 停止录音时原子写入 |
transcript.json | 规范转写结果——引擎信息 + 带时间戳、说话人标记的段落 | 转写完成时原子写入 |
transcript.md | 同一份转写内容的人类可读版 | 转写完成时原子写入 |
transcribe.log | 本次会话的转写进度与错误日志 | 转写过程中追加 |
🎙️ mic.caf 与 system.caf:为什么是两条音轨?
分成两条轨是刻意为之,有两个实际好处:
- 识别更准——语音模型对干净的单声源音频表现更好;
- 免费的说话人分离——
mic轨天然就是me,system轨天然就是them,不需要任何说话人识别模型,转写结果自动标记谁说了什么。
为什么用 CAF 格式而不是常见的 m4a?因为 CAF 写入时不需要"收尾定稿"步骤。即使录音中途进程崩溃,已经写下的音频依然可读,不会整段丢失。
什么时候会出现mic-002.caf这样的文件?
macOS 的音频路由在长时间会议中并不稳定——连接/断开 AirPods、切换默认音频设备,都可能让某条音轨悄悄停录。Quill 用 1 秒看门狗监控两条轨,发现卡住就在当前路由上重开一段新文件(mic-002.caf、mic-003.caf…),已录好的分段绝不会被改写或覆盖。
💡 所以如果你看到
-002文件,不必紧张:说明中途有一次自动恢复,转写时会按meta.json里的偏移量把各段拼回同一条时间轴,中断造成的空隙会以时间戳跳变的形式保留在转写里。
录音中菜单栏图标的颜色也是信号:🔴 红色 = 两轨健康;🟠 橙色◐ recovering= 某轨正在恢复;⚠ capture lost= 恢复失败,会话将被标记为incomplete。
📄 meta.json:会话的"体检报告"
meta.json在停止录音时一次性原子写入,结构定义见 SessionMeta.swift。当前为 schema v2,长这样(取自测试夹具的真实样例):
{ "schema_version": 2, "started": "2026-08-03T19:32:55Z", "ended": "2026-08-03T20:09:35Z", "duration_seconds": 2200, "status": "recovered", "tracks": [ { "kind": "mic", "speaker": "me", "status": "recovered", "segments": [ { "file": "mic.caf", "start_offset_ms": 18, "end_offset_ms": 1687869, "frames_written": 81016832, "sample_rate_hz": 48000, "channels": 1 }, { "file": "mic-002.caf", "start_offset_ms": 1691120, "end_offset_ms": 2200014, "frames_written": 24426720, "sample_rate_hz": 48000, "channels": 1 } ], "interruptions": [ { "detected_offset_ms": 1690869, "recovered_offset_ms": 1691120, "reason": "callback_stalled", "attempts": 1 } ], "warnings": [] }, { "kind": "system", "speaker": "them", "status": "complete", "segments": [ { "file": "system.caf", "start_offset_ms": 0, "end_offset_ms": 2200000, "frames_written": 105600000, "sample_rate_hz": 48000, "channels": 2 } ], "interruptions": [], "warnings": ["exact digital silence for 520s"] } ] }关键字段一眼读懂:
started/ended/duration_seconds——会议起止(ISO 8601,仅用于展示)与总时长;status——整场会话的最终状态,取各轨中最差的一个,取值见下;tracks[].speaker——me(mic 轨)/them(system 轨);tracks[].segments[]——每个音频文件的start_offset_ms/end_offset_ms(毫秒级会话时钟偏移)、写入帧数、采样率、声道数;tracks[].interruptions[]——每次中断的检出时刻、恢复时刻、原因和重试次数;tracks[].warnings[]——非致命提醒,例如exact digital silence for 520s(系统轨全程数字静音,可能只是当时没声音播放)。
三种状态:complete / recovered / incomplete
| 状态 | 含义 | 可用性 |
|---|---|---|
complete | 从开始到停止没有检测到任何中断 | ✅ 完整 |
recovered | 出现过中断但已自动恢复,缺口有界 | ✅ 可用,但不会被谎报为"无中断" |
incomplete | 采集无法恢复,或停止时已失效 | ⚠️ 尾部可能有缺口 |
这套规则的定义在 docs/architecture.md 中有完整说明,状态枚举定义见 CaptureHealth.swift。
🔍排查技巧:如果停录后收到"会话非 complete"的通知,直接打开该文件夹的
meta.json看interruptions和status,就能知道中断发生在哪一刻、恢复了没有。
📝 transcript.md 与 transcript.json:转写成果
停止录音后 Quill 自动转写(默认引擎Parakeet TDT 0.6B v2,纯设备端 Core ML,Apple Silicon 上约每小时音频 20 秒)。每段音频独立转写后按start_offset_ms平移到同一条会话时钟上,再按时间戳合并。
transcript.json是规范格式(机器可读):引擎/模型信息 +created_at+ 段落数组,每段含speaker、start_ms、end_ms、text,结构定义在 TranscriptionCoordinator.swift。
transcript.md是给人读的渲染版(同一份数据的另一种呈现),格式如下:
# 2026.09.29-1430 engine: parakeet (TDT 0.6B v2) capture: recovered **[00:03] me:** 喂,能听到我吗? **[00:05] them:** 可以可以,我们开始吧。 **[12:41] me:** 那这个方案我周五前给你初稿。渲染逻辑见 TranscriptionCoordinator.swift。注意两点:
capture: recovered这一行——仅当会话状态不是complete时出现,让"录音有缺口"这件事在通知消失后依然肉眼可见;- 时间戳是相对会话起点的秒数(
m:ss,超 1 小时为h:mm:ss)。如果中间发生过采集中断,你会看到时间戳"跳了一段"——这正是中断留下的可见空隙,而不会被悄悄抹平。
关于"文件系统即队列"
转写任务的排障全靠文件夹本身:有meta.json但没有transcript.json= 待转写。Quill 每次启动都会扫描~/Recordings,把未完成的会话按文件夹名(时间戳序)从旧到新重新排入队列——即使上次转写中途崩溃或退出,重新打开应用就会自动续上。单个会话转写失败只会把错误追加进它自己的transcribe.log,绝不阻塞后面的任务。
🔧 快速上手与常用命令
./scripts/build-macos # 构建 sudo ./scripts/install-macos # 安装到 /usr/local/bin(见 scripts/install-macos) quill # 启动菜单栏守护进程 quill doctor # 检查权限、录音目录、模型缓存 quill install --launch-at-login # 可选:登录时后台自启可选配置放在~/.config/quill/config.json,比如改录音目录、关闭转写(transcription.enabled: false)、或设置on_stop钩子在转写写完后自动跑你的命令(摘要、归档、索引都行)。完整说明见 macos/README.md。
❓ 常见问题速查
- 录音是静音的?检查 系统设置 → 隐私与安全性 → 屏幕与系统音频录制 是否授权。
- 转写支持中文吗?默认 Parakeet v2 仅支持英文;Whisper 引擎(多语言)在路线图上。
- 想只看结论?直接打开
transcript.md;想核对音频完整性,看meta.json的status。
📂 相关文件索引
- 平台文档与文件表:macos/README.md
- 会话生命周期(分轨、恢复、写 meta.json):macos/Sources/quill/RecordingSession.swift
- 元数据契约(v1/v2 兼容读取):macos/Sources/quill/SessionMeta.swift
- 转写队列与 transcript 渲染:macos/Sources/quill/Transcription/TranscriptionCoordinator.swift
- 跨平台共享契约说明:docs/architecture.md
- 菜单栏图标实现:macos/Sources/quill/UI/MenuBarController.swift
一句话总结:mic.caf/system.caf是原始声音,meta.json是完整性档案,transcript.md是你最终要读的东西——三者放在同一个时间戳文件夹里,构成了 Quill"录音—体检—转写"的完整闭环。
【免费下载链接】quillUltra-minimalist macOS recording + transcription.项目地址: https://gitcode.com/gh_mirrors/quill26/quill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考