本地运行语音识别,录音文件自动转文字,支持中英文混合识别。不上传云端,隐私安全,开箱即用。
项目背景
你是不是也这样:开会时狂录音,会后翻到崩溃——音频两小时,整理要三小时。用讯飞、百度这些在线工具转写速度快,但有个问题:会议内容全上传到对方服务器,敏感信息根本不放心。
本地跑语音识别,文件不出门,转录完直接删录音,安全感拉满。
这个项目的目标是:一个 Python 脚本,输入音频文件,输出文字记录。支持中文、英文、中英文混合,离线运行,零依赖云端。
技术选型
| 组件 | 选型 | 理由 |
|---|---|---|
| 语言 | Python 3.10+ | 语音生态成熟 |
| 语音识别 | faster-whisper | 基于 Whisper,速度快3-4倍,内存占用低 |
| 音频预处理 | ffmpeg / librosa | 格式转换、降噪 |
| 输出格式 | Markdown + JSON | 结构清晰,方便后续处理 |
| 批量处理 | pathlib + multiprocessing | 多文件并发转写 |
不需要数据库,不需要后端服务。一个脚本跑完就完事,适合个人和小型团队。
实现步骤
第一步:安装依赖
pip install faster-whisper librosa ffmpeg-python
faster-whisper 是 OpenAI Whisper 的高效实现,用的是 CTranslate2 引擎,CPU 上也能跑出不错的速度。librosa 负责音频格式转换和降噪。
第二步:创建项目结构
mkdir -p voice_to_text/{input,output,temp}
cd voice_to_text
touch transcribe.py config.yaml
把音频文件丢进 input 文件夹,转写结果自动输出到 output。
第三步:写转写脚本
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import json
import yaml
import librosa
from pathlib import Path
from datetime import datetime
from faster_whisper import WhisperModel
# 加载配置
with open("config.yaml") as f:
cfg = yaml.safe_load(f)
INPUT_DIR = Path(cfg["input_dir"])
OUTPUT_DIR = Path(cfg["output_dir"])
TEMP_DIR = Path(cfg["temp_dir"])
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
TEMP_DIR.mkdir(parents=True, exist_ok=True)
# 初始化模型(base 模型约140MB,medium约450MB)
MODEL_SIZE = cfg.get("model_size", "base")
DEVICE = cfg.get("device", "cpu")
compute_type = cfg.get("compute_type", "int8")
print(f"🔄 加载模型: {MODEL_SIZE} ({DEVICE}/{compute_type})")
model = WhisperModel(MODEL_SIZE, device=DEVICE, compute_type=compute_type)
print("✅ 模型加载完成\n")
def normalize_audio(audio_path: Path) -> Path:
"""将音频转为单声道 16kHz WAV,降低识别难度"""
temp_path = TEMP_DIR / f"{audio_path.stem}_norm.wav"
os.system(
f"ffmpeg -y -i '{audio_path}' "
"-ar 16000 -ac 1 -codec:a pcm_s16le "
f"'{temp_path}' 2>/dev/null"
)
return temp_path
def transcribe(audio_path: Path) -> dict:
"""转写单个音频文件"""
print(f"🎙️ 处理: {audio_path.name}")
# 音频预处理
norm_path = normalize_audio(audio_path)
# faster-whisper 分段识别,自带标点
segments, info = model.transcribe(
str(norm_path),
language=cfg.get("language", "zh"),
beam_size=5,
vad_filter=True, # 自动过滤静音段
)
text_parts = []
for seg in segments:
text_parts.append(seg.text.strip())
# 可选:记录时间戳用于后续校对
# print(f"[{seg.start:.2f}s -> {seg.end:.2f}s] {seg.text}")
full_text = "\n".join(text_parts)
# 清理临时文件
os.remove(norm_path)
return {
"filename": audio_path.name,
"language": info.language,
"language_probability": info.language_probability,
"duration": info.duration,
"text": full_text,
"timestamp": datetime.now().isoformat(),
}
def save_result(result: dict):
"""保存为 Markdown 和 JSON"""
stem = result["filename"].rsplit(".", 1)[0]
# Markdown 格式
md_path = OUTPUT_DIR / f"{stem}.md"
md_content = f"""# 语音转文字记录
**文件**: {result['filename']}
**时长**: {result['duration']:.1f} 秒
**识别语言**: {result['language']} (置信度: {result['language_probability']:.2f})
**转录时间**: {result['timestamp']}
---
{result['text']}
"""
md_path.write_text(md_content, encoding="utf-8")
# JSON 格式(方便程序处理)
json_path = OUTPUT_DIR / f"{stem}.json"
json_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
print(f" ✅ 已保存: {md_path.name}")
def main():
audio_extensions = {".wav", ".mp3", ".m4a", ".flac", ".aac", ".ogg"}
audio_files = [
f for f in INPUT_DIR.iterdir()
if f.suffix.lower() in audio_extensions
]
if not audio_files:
print("⚠️ input/ 目录下没有找到音频文件")
return
print(f"📁 发现 {len(audio_files)} 个音频文件\n")
for audio_path in audio_files:
try:
result = transcribe(audio_path)
save_result(result)
except Exception as e:
print(f" ❌ {audio_path.name}: 处理失败 - {e}")
print("\n🎉 全部完成!")
if __name__ == "__main__":
main()
第四步:配置文件 config.yaml
# 目录配置
input_dir: "input"
output_dir: "output"
temp_dir: "temp"
# 模型配置
model_size: "base" # 可选: tiny / base / small / medium / large
device: "cpu" # 有GPU可改为 "cuda"
compute_type: "int8" # int8 更快更省内存,float32 精度更高
# 识别语言
language: "zh" # 自动检测可设为 null,或固定为 "zh" / "en"
第五步:运行
python transcribe.py
输出示例:
📁 发现 2 个音频文件
🎙️ 处理: meeting_20260805.mp3
✅ 已保存: meeting_20260805.md
🎙️ 处理: interview_zhang.mp3
✅ 已保存: interview_zhang.md
🎉 全部完成!
生成的 Markdown 文件里包含完整文字内容和元数据,可以直接用来做会议纪要。
运行效果
转写后的 Markdown 长这样:
# 语音转文字记录
**文件**: meeting_20260805.mp3
**时长**: 1823.5 秒
**识别语言**: zh (置信度: 0.97)
**转录时间**: 2026-08-08T10:15:32.441023
---
大家好,今天我们开一个产品评审会。
首先请张经理汇报一下 Q3 的数据表现。
...
JSON 文件包含结构化数据,适合后续做统计或接入其他工具:
{
"filename": "meeting_20260805.mp3",
"language": "zh",
"language_probability": 0.97,
"duration": 1823.5,
"text": "大家好,今天我们开一个产品评审会。...",
"timestamp": "2026-08-08T10:15:32.441023"
}
优化方向
多 speaker 分离:接入 PyAnnote 做说话人分离,区分不同发言人的内容,自动生成带角色标注的会议纪要
关键词高亮:识别结果里自动标出会议主题词、待办事项、决策项,一键生成行动清单
GPU 加速:有 NVIDIA 显卡的话把
device改成cuda,compute_type改成float16,速度提升明显实时转写:接入麦克风输入,边说边出文字,适合线上会议记录
摘要生成:把转写文本喂给本地 LLM(比如用 Ollama 跑 Llama 3),自动生成会议摘要和行动项
集成飞书/钉钉:转写完成后自动把文字推送到群组,同事可以直接在文档里编辑
多语言混合场景:中英文混杂的会议(比如技术评审),把
language设为 null 让模型自动检测每段语言
核心思路:先让模型跑起来,再把每一步拆细。语音识别这事,模型选对了,剩下的都是工程优化。
你的电脑有 GPU 吗?没 GPU 用 base 模型也能跑,就是慢一点。试试把一段录音丢进去,看看转写质量怎么样?
这篇文章对你有帮助吗?