# RecorderStudio · 高清录音机 一个面向 **高保真、高清晰度** 的 Windows 录音软件:无损 WAV 母版、WASAPI 独占采集、 线性相位滤波、TPDF 抖动、ITU-R BS.1770 标准响度计量,并带完整的录音体检报告、 后期处理与多格式导出。界面用 PyQt5 手绘,深色专业风格。 自检套件 **97 项检查全部通过**,其中响度算法与 ffmpeg 的 `ebur128` 滤波器做过交叉验证, WAV 编码用 Python 标准库与 ffmpeg 双向复核。 --- ## 目录 1. [30 秒上手](#30-秒上手) 2. [为什么它"清晰"](#为什么它清晰) 3. [界面说明](#界面说明) 4. [参数怎么选](#参数怎么选) 5. [命令行用法](#命令行用法) 6. [录音体检报告](#录音体检报告) 7. [后期处理与导出](#后期处理与导出) 8. [自检与验证证据](#自检与验证证据) 9. [项目结构](#项目结构) 10. [常见问题](#常见问题) 11. [已知限制](#已知限制) --- ## 30 秒上手 ```bat :: 1) 安装依赖(装进项目内的 _vendor,不污染系统 Python) python install_deps.py --with-pyqt5 :: 2) 检查依赖与设备 python install_deps.py --check python -m recorder.cli --list-devices :: 3) 启动 双击 启动录音机.bat :: 或 python -m recorder.gui ``` 按下 **空格** 开始录音,再按一次停止;停止后自动弹出体检报告。 > 第一次用,建议先做两件事: > 1. 点左侧「**检测能力**」,确认所选采样率带 ✓(避免设备不支持导致开流失败); > 2. 点「**校准本底噪声**」,安静 3 秒,程序会告出房间本底噪声并给出低切/噪声门建议。 --- ## 为什么它"清晰" "清晰度"在数字录音里由几个环节共同决定,这个软件在每一环都做了明确取舍: | 环节 | 做法 | 为什么重要 | |---|---|---| | **采集通道** | WASAPI **独占模式**(可关闭) | 绕开 Windows 混音器,不重采样、不被其它程序改音量;这是 Windows 上最接近原始信号的方式 | | **内部精度** | 全程 float32 浮点运算 | float32 的 24 位尾数正好无损容纳 24 位 PCM,运算不产生额外量化 | | **写盘位深** | 16 / 24 / 32 位 PCM / 32 位浮点 | 24 位动态范围 144 dB,远超人耳与话筒本底;24 位用真正的 3 字节打包,不做 16 位截断 | | **抖动** | 16/24 位可选 **TPDF 抖动**(1 LSB 峰峰值) | 把量化失真从"与信号相关的非线性失真"变成无关宽带白噪。实测误差与信号相关系数从 0.224 降到 0.0008 | | **低切滤波** | 5000+ 抽头**线性相位 FIR**,并补偿群延迟 | 不引入相位失真;实测直流抑制 -240 dB、1 kHz 通带 +0.0000 dB、样本数严格守恒(不丢头掉尾) | | **实时链路** | 音频回调**只做内存拷贝** | 滤波/FFT/写盘全在写入线程,从根上避免 xrun(爆音、掉采样)。实测长时间录音 0 xrun、0 溢出块 | | **计量标准** | 真峰值 4× 过采样(BS.1770 附录 2)+ K 加权响度(EBU R128) | 能看到"采样点之间"的过冲,避免转码后突然过载;响度按国际标准,不是"看着差不多" | | **崩溃安全** | 每秒回写文件头 | 断电/崩溃后已落盘的数据仍能被播放器正常识别 | 实测的实测数据(自检输出,非估算): ``` K 加权滤波器系数 vs ITU 原文 最大偏差 8.9e-16 (即逐位一致) 响度 vs ffmpeg ebur128 -13.856 LUFS vs -13.8 LUFS(白噪声) 满量程立体声 997 Hz 正弦 0.0007 LUFS(ffmpeg: -0.0) 16/24/32/float32 往返最大误差 4.5e-5 / 1.7e-7 / 6.9e-10 / 0 ffmpeg 独立解码本程序的 24 位文件 最大偏差 4.6e-5 低切:直流 / 20 Hz / 1 kHz -240 dB / -98.6 dB / +0.0000 dB 真峰值捕捉采样点间过冲 采样峰值 0.3536 → 真峰值 0.5050(理论 0.5) ``` --- ## 界面说明 ``` ┌───────────────┬──────────────────────────────────────────────────────┐ │ ① 输入设备 │ ● 00:12.3 [暂停][标记][分段][停止] │ │ 设备 / 检测能力│ 待机 │ │ ② 录音格式 ├──────────────────────────────────────────────────────┤ │ 采样率/位深/声道│ 电平表 RMS 渐变条 + 峰值保持 + 削波锁存 + dB 刻度 │ │ 独占/缓冲/延迟 ├──────────────────────────────────────────────────────┤ │ ③ 信号处理 │ 实时波形(最近 1 秒,min/max 包络) │ │ 低切/增益/抖动 ├──────────────────────────────────────────────────────┤ │ ④ 文件与分段 │ 响度条 瞬时 / 短时 / 整体 + 目标参考线 │ │ 目录/命名/分段 ├──────────────────────────────────────────────────────┤ │ 校准本底噪声 │ 整段总览 + 标记轨道 │ │ ├──────────────────────────────────────────────────────┤ │ │ 文件名 · 磁盘剩余 · 缓冲队列 · 驱动 xrun │ └───────────────┴──────────────────────────────────────────────────────┘ ``` **快捷键** | 键 | 功能 | |---|---| | `空格` | 开始 / 停止录音 | | `P` | 暂停 / 继续(暂停段不写入文件) | | `M` | 添加标记(写入元数据与标记轨道) | | `Ctrl+S` | 立即分段(切到新文件继续录) | | `Ctrl+O` | 打开输出目录 | | `Ctrl+Q` | 退出 | **录音中可以实时调整**:软件增益、响度参考线、暂停/标记/分段。 其余参数在录音期间锁定,避免中途改变格式造成文件不一致。 **状态灯含义** - 「剩余空间」> 2 GB 绿、> 512 MB 黄、再低红(低于 512 MB 时启动也会警告) - 「缓冲队列」积压 < 8 块绿、< 32 块黄、再多红。持续红灯说明磁盘跟不上,会自动记录溢出块数 - 「驱动 xrun」非 0 表示 PortAudio 层出现溢出,报告里会统计 --- ## 参数怎么选 | 参数 | 推荐值 | 理由 | |---|---|---| | 采样率 | **48000 Hz** | 设备原生多为 48 kHz,选它可完全避免重采样;语音/音乐/视频后期通吃 | | 位深 | **24 位 PCM** | 动态范围 144 dB,兼容性极好;要发出去就录 24 位、导出 16 位(带抖动) | | 声道 | 话筒 **1**;立体声素材 **2** | 单声道话筒录成双声道只是浪费一倍体积 | | 独占模式 | **开**(WASAPI) | 保真度最高;若打不开设备再关掉换共享模式 | | 缓冲区 | **自动 / 4096 + 高延迟策略** | 录音不需要低延迟,稳定不出 xrun 才是第一优先级 | | 低切 | 人声 **80 Hz**,男声 60 Hz,播客 180 Hz | 去掉空调/桌面震动/近讲喷麦的低频堆积 | | 软件增益 | **0 dB 起** | 优先调系统麦克风音量(作用在更靠前的增益级);软件增益只在不够用时补 | | 抖动 | **开** | 写 16/24 位时把量化失真变成白噪;写 32 位浮点时自动跳过 | | 响度参考 | 播客 -16 / 流媒体 -14 LUFS | 只是在响度条上画一条目标线,不影响录音 | **电平目标:正常说话时峰值落在 -12 ~ -6 dBFS。** 数字削波不可逆,而且比模拟过载难听得多。 --- ## 命令行用法 适合自动化、定时任务、无界面环境。 ```bat :: 设备与能力 python -m recorder.cli --list-devices python -m recorder.cli --probe 3 :: 录 60 秒到指定目录,24 位/48 kHz、80 Hz 低切、顺带导出 16 位 WAV python -m recorder.cli -d 3 -t 60 -b 24 --lowcut 80 -o D:\rec ^ --name "会议_{datetime}" --report --export wav_16 :: 不定时长,回车停止(Ctrl+C 也可) python -m recorder.cli -d 3 -b 24 --lowcut 80 -o D:\rec :: 静音 10 秒自动停止;每 5 分钟自动分段 python -m recorder.cli -t 3600 --stop-after-silence 10 --split-seconds 300 :: 分析已有录音 python -m recorder.cli --analyze D:\rec\会议.wav :: 后期处理:裁剪静音 + 响度归一化到 -16 LUFS + 混单声道,并导出 FLAC python -m recorder.cli --process D:\rec\会议.wav --trim ^ --normalize lufs --target -16 --mono --export flac ``` 主要参数:`-d 设备` `-r 采样率` `-c 声道` `-b 位深` `--gain dB` `--lowcut Hz` `--no-exclusive` `--no-dither` `--rf64` `-t 秒` `-o 目录` `--name 模板` `--split-seconds` `--split-mb` `--stop-after-silence` `--silence-threshold` `--report` `--export <预设>`;`--gui` 直接启动图形界面。 命名模板变量:`{datetime} {date} {time} {device} {sr} {bits} {ch} {seq}` --- ## 录音体检报告 每次录音结束都会弹出报告,并可生成同名 `.json`(机器可读)与 `.txt`(人读)、 `_waveform.png`(波形预览图,纯 numpy 生成,不依赖 PIL)。 ``` RecorderStudio 录音报告 ============================================== 开始时间 : 2026-09-12T22:02:15 输入设备 : 麦克风 (F20) [Windows WASAPI] 录制格式 : 48000 Hz / 24-bit PCM / 2 声道 独占模式 : 是 软件增益 : +0.0 dB 低切滤波 : 80 Hz 抖动 : 开启 (TPDF) 总时长 : 00:02.7 总采样帧 : 129840 数据量 : 742.5 KB 音质体检 ---------------------------------------------- 采样峰值 : -32.60, -32.60 dBFS 真峰值 : -32.48, -32.48 dBTP RMS 电平 : -68.10, -68.10 dBFS 整体响度 : -55.200 LUFS 动态范围 : 0.0 LU 直流偏移 : [0.0, 0.0] 本底噪声 : -52.3 dBFS 削波样本 : 0 丢弃块/溢出: 0 驱动层 xrun: 0 标记 ---------------------------------------------- 1.234 s 标记 1 (20260912_220215_麦克风 (F20).wav) ``` 说明: - **真峰值(dBTP)** 比采样峰值更能反映真实过载风险;若超过 -0.1 dBTP 报告会提示降低增益 - **本底噪声** 只在录音里确实存在"安静段"时才给出;全程持续发声时明确显示"无法测定", 而不是编造一个数字 - **动态范围** 是短时响度的 10%~95% 分位差(EBU Tech 3342 的工程近似值) --- ## 后期处理与导出 原则:**原始 WAV 母版永不被覆盖**。所有处理另存为 `*_processed.wav`, 导出时即使目标路径与源文件同名也会自动改名。 **后期处理链**(顺序可自由组合,全部离线、可复现) 1. 去除直流偏移 2. 线性相位低切(40 ~ 180 Hz) 3. 噪声门(带保持/攻放包络,避免"抽气"感) 4. 裁剪首尾静音(只有长于设定时长才裁,且保留呼吸空间) 5. 混合单声道 6. 峰值归一化(-1 dBFS,按真峰值保护)或 **响度归一化**(目标 LUFS,并保证真峰值上限) 7. 淡入 / 淡出 **导出格式** | 预设 | 说明 | |---|---| | WAV 16 / 24 / 32位浮点 | 内置编码器,不需要 ffmpeg | | FLAC | 无损压缩,体积约减半 | | MP3 320 kbps / MP3 V0 | 需要 ffmpeg | | Opus 128 kbps | 语音/播客首选 | | AAC / M4A 256 kbps | 苹果生态 | 程序会自动在 PATH 与常见安装位置寻找 ffmpeg;找不到时只有 WAV 导出可用, 界面上会明确提示而不是静默失败。 --- ## 自检与验证证据 ```bat python -m recorder.selftest :: 全部(含真实硬件录音,约 11 秒) python -m recorder.selftest --no-hw :: 只跑离线数学/格式测试(无需麦克风) python dev/gui_check.py --shots :: 界面回归检查(离屏渲染 + 像素校验 + 截图) python dev/dialog_check.py :: 对话框后台任务链路(QThread + 信号) python dev/lint_scan.py :: 静态检查(未使用导入 / 裸 except / 过长函数) ``` 自检覆盖 6 个方面共 **97 项**(含硬件时): | 分组 | 内容 | |---|---| | ITU-R BS.1770 符合性 | 滤波器系数与标准原文逐位比对;响度标定(满量程立体声正弦 = 0 LUFS、单声道 -3.01 LUFS);与 ffmpeg `ebur128` 交叉验证 | | 线性相位滤波器 | 直流/通带/阻带响应、系数对称性、群延迟补偿 | | 真峰值 | 采样点对齐与相移两种情况,验证能捕捉 inter-sample peak | | WAV 编解码 | 四种载荷往返误差在量化极限内;标准库 `wave` 与 ffmpeg 独立复核;RF64;非法文件与零长度边界 | | TPDF 抖动 | 误差-信号相关性、方差分解(1/6 抖动 + 1/12 量化 = 1/4 LSB²) | | 引擎与硬件 | 低切样本数守恒、命名模板、自动分段(1.0 秒分段精确得到 48000 帧)、手动分段、**静音自动停止后音频流确实被关闭**、真实设备录音 xrun 统计 | 界面回归检查(`dev/gui_check.py`)用两种可判定的方式验证渲染, 因为纯截图无法被自动判定: - **几何**:164 个控件无零尺寸、无越界、无横向裁切;大号计时器与状态栏用字体度量 确认最长文本不被截断 - **像素**:注入已知电平验证 dB→像素映射(-3 dB → 90% 表宽、-30 dB → 49%、-60 dB → 0%), 削波锁存显示红色,示波器/总览图有波形,录音按钮在空闲/录音态颜色正确 --- ## 项目结构 ``` RecorderStudio/ ├── 启动录音机.bat / run.bat 启动器(自动寻找带 PyQt5 的解释器) ├── install_deps.py 依赖安装器(pip + 直接下载 wheel 双通道) ├── requirements.txt ├── recorder/ 主程序包 │ ├── dsp.py DSP:滤波器、电平、真峰值、BS.1770 响度、离线处理链 │ ├── wavfile.py 无损 WAV 读写:16/24/32/float32、TPDF、RF64、崩溃安全头 │ ├── engine.py 录音引擎:设备枚举、独占模式、落盘线程、自动分段 │ ├── post.py 后期处理、导出、元数据、纯 numpy 的 PNG 波形图 │ ├── widgets.py 自绘控件:电平表、示波器、响度条、录音按钮 │ ├── window.py 主窗口、报告/处理/导出对话框 │ ├── gui.py 图形界面入口(High-DPI + 深色主题) │ ├── cli.py 命令行录音 │ └── selftest.py 自检套件 ├── dev/gui_check.py 界面回归检查(开发/验收用) ├── _vendor/ 项目内依赖(sounddevice 及其内置 PortAudio) └── recordings/ 默认输出目录 ``` 架构要点:`dsp` / `wavfile` 不依赖 GUI 也不依赖音频后端,可以单独 import 做批量处理; `engine` 是三线程模型(PortAudio 回调线程 / 写入线程 / 界面线程), 只有写入线程接触磁盘,界面只读快照。因此 CLI 与 GUI 共用同一套录音核心, 不存在"两套行为不一致"的问题。 --- ## 常见问题 **打不开设备 / device unavailable** 多半被其它程序独占(浏览器、会议软件、直播工具、DAW)。关掉它们,或在左侧关掉 「WASAPI 独占模式」改用共享模式。 **提示不支持某个采样率** 点「检测能力」看哪些采样率带 ✓。设备原生采样率(通常是 48000)保真度最高; 不要为了"看起来更高"强行选 96 kHz——那只会让驱动做重采样。 **录出来是静音** 1) Windows 设置 → 隐私和安全性 → 麦克风,确认"允许桌面应用访问麦克风"已开; 2) 系统声音设置里确认默认输入设备选对; 3) 用「校准本底噪声」测一下,如果本底是 -inf 说明没有信号进来。 **想录电脑内部声音(内录)** 在设备列表里选 **「立体声混音 / Stereo Mix」** 或 **「主声音捕获驱动程序」** (通常在 WASAPI / WDM-KS 分组下)。若列表中没有,需要在 「声音设置 → 录制 → 显示禁用的设备」里启用"立体声混音"。 **有爆音 / 掉采样** 看状态栏「缓冲队列」和「驱动 xrun」。把缓冲区改成 4096、延迟策略改成"高", 并关闭其它占用磁盘与 CPU 的程序。报告里的「丢弃块/溢出」与「xrun」会给出确切数字。 **提示超过 4 GB** RIFF 容器上限就是 4 GB。开启「RF64 大文件容器」用 RF64 单文件继续录, 或设置「按体积分段」自动切成多个文件。 **FFmpeg 找不到** 只影响 FLAC/MP3/Opus/AAC 导出,WAV 全部功能不受影响。安装 ffmpeg 并加入 PATH 即可。 --- ## 已知限制 诚实说明,避免误用: 1. **平台**:开发与验证都在 Windows 上完成。`sounddevice` 在 macOS/Linux 也可用, 但本项目的设备排序、独占模式说明、批处理启动器都针对 Windows 优化。 2. **ASIO**:`_vendor` 里带了 ASIO 版 PortAudio DLL,但 sounddevice 默认加载非 ASIO 版本; 需要 ASIO 时要把 `libportaudio64bit-asio.dll` 覆盖为 `libportaudio64bit.dll` 后再启动。 本项目未对 ASIO 路径做测试。 3. **内录**:依赖系统的"立体声混音"设备。PortAudio 19.7(sounddevice 0.5.6 内置) 没有暴露 WASAPI loopback,因此不提供"直接抓系统输出"的开关。 4. **响度计量的实现方式**:K 加权在频域按 |H(f)|² 施加。对"能量"计量而言与 时域滤波等价(自检已与 ffmpeg 交叉验证,误差 < 0.1 LU), 但与逐样本时域实现相比,块边界处存在理论上的细微差异。 5. **动态范围(LRA)** 是 EBU Tech 3342 的工程近似值,不用于合规认证。 6. **暂停是"跳切"**:暂停期间的数据直接丢弃,文件里会有一次不连续; 需要保留时间轴连续性请用分段而不是暂停。 --- ## 依赖 | 组件 | 用途 | 许可 | |---|---|---| | Python 3.9+ | 运行环境 | PSF | | numpy | 数值运算 | BSD-3 | | sounddevice | PortAudio 绑定(内置 PortAudio 二进制) | MIT / PortAudio MIT | | PyQt5 | 图形界面(仅 GUI 需要) | GPL v3 / 商业双许可 | | ffmpeg(可选,外部) | FLAC/MP3/Opus/AAC 导出 | LGPL/GPL | > PyQt5 采用 GPL v3 与商业双许可。若要把本软件用于闭源商业分发, > 请改用 PySide6(LGPL)或购买 PyQt 商业许可。