适用版本:macOS Apple Silicon · 26 音色 · 单文件转换 · 本地即时试听版
本文是一份面向接手开发者的完整指南,说明产品为什么这样设计、各阶段如何演进、 当前代码如何运行、如何打包、如何测试,以及遇到故障时应从哪里排查。文档使用标准 HTML 标题、段落、列表、表格和代码块,整页复制到 Word 或常见富文本编辑器后仍能 保持清晰的内容层级。
目录产品概述主要功能完整开发演进当前技术架构项目文件结构开发环境与依赖核心功能实现26 个音色清单搭建开发环境生成试听资源打包 macOS 应用自动化测试与验收常见故障与调试方法分发与安装后续扩展建议发布检查表1. 产品概述听文 MP3 转换器是一款运行在苹果电脑上的图形化文本转语音工具。用户选择一个 .txt 或 .md 文档,选择朗读音色和 MP3 输出文件夹, 点击“立即生成 MP3”,应用便会清理常见 Markdown 标记、分割长文本、调用 Edge TTS 在线语音服务,并输出与原文同名的 MP3。
当前版本面向 Apple Silicon,即 M 系列芯片 Mac。应用已经把 Python、Qt 和主要 Python 依赖封装进 .app,目标电脑不需要单独安装 Python、Edge TTS 或 FFmpeg。正式文本转换需要互联网;26 个音色的试听文件已经内置,可离线即时播放。
当前正式版本:26 个可用中文音色、7 列音色卡片、点击即试听、 单个 TXT/MD 文件选择、指定输出文件夹、长文本分段合成、应用内 MP3 合并。
2. 主要功能2.1 音色选择与即时试听26 个音色以卡片形式直接排列,无需打开下拉菜单。点击任意卡片立即选中,并播放应用内置的中文试听 MP3。点击另一张卡片时,立即停止上一段试听并切换。试听不访问网络,播放器启动测试约为 1~2 毫秒。卡片标明音色名称、地区与性别。2.2 单文件转换只接受 .txt 与 .md 文件。通过系统文件选择器获取输入文档,避免手工输入路径。用户单独选择 MP3 输出文件夹。输出文件名默认使用源文件主文件名,例如 文章.md 输出为 文章.mp3。同名输出文件存在时,通过临时文件完成后再原子替换,减少半成品风险。2.3 Markdown 文本清理转换前会清理以下常见结构:
围栏代码块、行内代码标记;Markdown 图片与链接地址,保留链接显示文字;标题符号、粗体、斜体、引用符号;无序列表和有序列表编号;HTML 标签与多余空行。2.4 长文本处理文本按段落组合,每个语音请求控制在约 2000 个字符以内。超过限制的单段文本会继续 切片。各片段分别生成 MP3,最后按顺序写入完整文件。当前版本不依赖外部 ffmpeg。
2.5 状态、日志与配置界面显示“准备就绪、正在试听、正在生成、生成完成、生成失败”等状态。运行记录显示所选文件、音色、分段进度和最终输出路径。记住最近使用的音色、输入文件和输出文件夹。配置保存在 ~/Library/Application Support/TingwenMP3Converter/config.json。3. 完整开发演进3.1 第一阶段:命令行文件夹监听器最早的 v2 工具位于 tools/md_to_mp3_watch_v2.py。它在终端显示音色菜单, 使用 Watchdog 监听桌面“待转换文件”文件夹,并把 MP3 写入“转换完成MP3”文件夹。 长文本使用多个临时 MP3 和 FFmpeg concat 合并。
这个版本验证了核心业务,但存在三点限制:
用户必须打开终端,操作不够直观;依赖系统 Python、Watchdog、Edge TTS 和 FFmpeg;只能在启动时选择音色,不能直观试听和切换。3.2 第二阶段:Tkinter 图形应用初版 macOS GUI 使用系统 Tkinter,并通过 PyInstaller 打包。源码直接运行时窗口正常, 但 Finder 双击打包应用时表现不稳定。根因是当前 macOS 自带的 Tk 8.5 已被系统标记为 deprecated,PyInstaller 也提示“使用系统 Tcl/Tk framework,不收集数据文件”。
经验:“进程仍在运行”并不等于“应用窗口可用”。GUI 应用必须验证 窗口内容、控件数量、按钮状态和真实交互,不能只用
pgrep
判断成功。
3.3 第三阶段:改用 Qt / PySide6为解决 Finder 启动和系统 Tk 依赖问题,界面重写为 Qt/PySide6。为了控制包体积,只安装 PySide6-Essentials,不安装不需要的 300 MB 以上 Addons 组件。Qt 版本 支持离屏渲染,可在不录制桌面、不申请辅助功能权限的情况下生成应用自身窗口截图, 便于自动检查布局。
3.4 第四阶段:音色卡片化下拉菜单被替换为可直接浏览的音色卡片墙。最初使用两行卡片和 5 列布局,曾出现按钮 被纵向压缩和重叠的问题。经过离屏截图检查,最终将卡片压缩为单行,并在 26 音色版本 中使用 7 列 × 4 行布局。
3.5 第五阶段:从文件夹监听改为单文件生成产品流程从“选择待转换文件夹并持续监听”改为“选择一个 TXT/MD 文件、选择输出文件夹、 立即生成 MP3”。Watchdog 从正式 Qt 源码中移除,界面也不再出现“待转换文件夹”和 “停止监听”等概念,降低用户理解成本。
3.6 第六阶段:本地即时试听早期点击音色后,会先通过网络临时生成一段试听,再调用 afplay 播放。 网络等待造成明显的“点击无反应”。最终方案是预先生成每个音色的中文试听 MP3,并随 应用打包。界面用 Qt QProcess 非阻塞启动 macOS 自带 /usr/bin/afplay,切换卡片时终止上一播放器并立即启动新的播放器。
3.7 第七阶段:扩展到全球可用中文音色音色选择以两个条件为准:微软说明音色支持中文,并且 Edge TTS 免费端点当前能够实际 调用。软件最终收录 8 个中国大陆音色、6 个香港/台湾音色,以及 12 个支持中文自动识别 的海外多语言音色,共 26 个。
4. 当前技术架构层级采用技术职责图形界面PySide6 / QtWidgets音色卡片、文件选择、输出目录、生成按钮、日志与状态。本地试听内置 MP3 + QProcess + afplay离线、即时播放,并支持快速切换。文本预处理Python 正则表达式清理 Markdown、HTML 和多余空行。长文本切分段落组合 + 2000 字符上限控制单次 TTS 请求长度。在线语音合成edge-tts 7.2.8把中文文本生成 MP3 片段。片段合并顺序写入 MP3 字节流在应用内部合并,不要求安装 FFmpeg。后台任务ThreadPoolExecutor + Qt Signal避免生成音频时阻塞 GUI 主线程。配置持久化JSON保存最近音色、输入文件和输出文件夹。macOS 打包PyInstaller 6.21.0封装 Python、Qt、依赖和 26 个试听资源。4.1 生成流程用户选择 TXT 或 MD 文件。用户选择音色和输出文件夹。程序验证文件存在、扩展名正确、输出目录可创建。后台线程读取 UTF-8 文本并清理 Markdown。文本按 2000 字符左右拆分为多个片段。每个片段通过 Edge TTS 生成临时 MP3。按顺序合并为临时完整 MP3。使用 os.replace 原子替换最终输出。Qt Signal 通知主线程更新按钮、状态和运行记录。
5. 项目文件结构文本转音频/ ├── mac_app/ │ ├── mp3_converter_qt_app.py # 当前正式 Qt 应用源码 │ ├── generate_preview_assets.py # 批量生成内置试听 MP3 │ ├── assets/ │ │ └── previews/ # 26 个音色试听文件 │ ├── global_dist/ │ │ └── 听文 MP3 转换器.app # 26 音色正式构建产物 │ ├── global_build/ # PyInstaller 临时构建目录 │ ├── 听文 MP3 转换器.spec # PyInstaller 规格文件 │ └── README.md ├── tools/ │ └── md_to_mp3_watch_v2.py # 原始命令行监听版本 └── docs/ └── 听文MP3转换器_开发调试攻略.html
正式开发入口是
mac_app/mp3_converter_qt_app.py。原始 v2 脚本保留用于 对照和回退,不应在修改 Qt 版本时顺手覆盖。
6. 开发环境与依赖项目当前版本或要求说明macOS建议 macOS 13 或更高当前构建目标为 Apple Silicon arm64。Python3.9.6当前构建环境使用 Command Line Tools Python。edge-tts7.2.8正式文本在线合成。PySide6 Essentials6.10.3QtCore、QtGui、QtWidgets 等核心 GUI 组件。PyInstaller6.21.0生成 arm64 macOS 应用包。afplaymacOS 系统自带播放本地试听 MP3。ffprobe / afinfo仅测试使用验证 MP3 时长、格式和大小,不是应用运行依赖。
7. 核心功能实现7.1 音色数据音色集中定义在 AVAILABLE_VOICES。每项包含服务 ID、界面名称、性别和描述。 服务调用必须使用精确 ID,界面名称可以本地化。
AVAILABLE_VOICES = [("zh-CN-YunxiNeural", "云希", "男", "沉稳大气,清晰有力"),
("zh-HK-WanLungNeural", "Wan Lung", "男", "香港粤语音色,友好自然"),
("zh-TW-HsiaoYuNeural", "Hsiao Yu", "女", "台湾国语音色,友好自然"),
# ...
]7.2 资源路径
开发环境与 PyInstaller 应用的资源根目录不同。开发时从源码旁的 assets/previews 读取;打包后从 sys._MEIPASS/previews 读取。
def preview_asset_path(voice_id):bundle_root = getattr(sys, "_MEIPASS", None)
if bundle_root:
return Path(bundle_root) / "previews" / f"{voice_id}.mp3"
return Path(__file__).resolve().parent / "assets" / "previews" / f"{voice_id}.mp3"7.3 即时试听
不要在点击卡片后调用网络 TTS。卡片点击事件只完成四件事:更新选中状态、保存配置、 停止现有 afplay、从本地启动新的 afplay。
if preview_process.state() != QProcess.ProcessState.NotRunning:preview_process.kill()
preview_process.waitForFinished(500)
preview_process.start("/usr/bin/afplay", [str(preview_file)])7.4 文件选择
输入文件使用 QFileDialog.getOpenFileName,过滤器只显示 TXT 与 Markdown。 输出使用 QFileDialog.getExistingDirectory。输入框设为只读,路径由系统 选择器写入,避免输入无效路径。
7.5 Markdown 清理clean_markdown 使用正则表达式按固定顺序清理。修改时要注意顺序,例如 图片语法应在普通链接语法之前处理,代码块应在行内代码之前处理。
7.6 文本切分split_text 优先保持段落完整。当段落超过 2000 字符时,再进行硬切分。 返回值始终为非空列表。新增分句算法时,应保留这一兜底条件。
7.7 合成与原子输出所有片段先写入系统临时目录。完整 MP3 也先写入临时文件,成功后再 os.replace 到目标位置。异常发生时,TemporaryDirectory 会自动清理片段,目标目录不会留下本次未完成的临时文件。
7.8 线程与 Qt 信号Edge TTS 属于网络任务,不能直接在 GUI 主线程运行。应用用 ThreadPoolExecutor(max_workers=1) 顺序执行转换任务,并通过 Qt Signal 把结果发送回主线程。后台线程不得直接修改 Qt 控件。
8. 26 个音色清单序号界面名称服务 ID地区 / 类型性别1云健zh-CN-YunjianNeural中国大陆男2云希zh-CN-YunxiNeural中国大陆男3云扬zh-CN-YunyangNeural中国大陆男4云霞zh-CN-YunxiaNeural中国大陆男5晓晓zh-CN-XiaoxiaoNeural中国大陆女6小艺zh-CN-XiaoyiNeural中国大陆女7小北zh-CN-liaoning-XiaobeiNeural东北口音女8小妮zh-CN-shaanxi-XiaoniNeural陕西口音女9Hiu Gaaizh-HK-HiuGaaiNeural香港粤语女10Hiu Maanzh-HK-HiuMaanNeural香港粤语女11Wan Lungzh-HK-WanLungNeural香港粤语男12Hsiao Chenzh-TW-HsiaoChenNeural台湾国语女13Hsiao Yuzh-TW-HsiaoYuNeural台湾国语女14Yun Jhezh-TW-YunJheNeural台湾国语男15弗洛里安de-DE-FlorianMultilingualNeural德国多语言男16塞拉菲娜de-DE-SeraphinaMultilingualNeural德国多语言女17威廉en-AU-WilliamMultilingualNeural澳大利亚多语言男18安德鲁en-US-AndrewMultilingualNeural美国多语言男19艾娃en-US-AvaMultilingualNeural美国多语言女20布莱恩en-US-BrianMultilingualNeural美国多语言男21艾玛en-US-EmmaMultilingualNeural美国多语言女22雷米fr-FR-RemyMultilingualNeural法国多语言男23薇薇安fr-FR-VivienneMultilingualNeural法国多语言女24朱塞佩it-IT-GiuseppeMultilingualNeural意大利多语言男25贤洙ko-KR-HyunsuMultilingualNeural韩国多语言男26塔莉塔pt-BR-ThalitaMultilingualNeural巴西多语言女
音色是否加入软件不能只依据 Azure 网页。应同时运行 Edge TTS 实时音色查询,确认免费 端点确实返回该 ID,并使用中文样句生成有效 MP3。
9. 搭建开发环境9.1 创建临时构建环境python3 -m venv --system-site-packages /tmp/tingwen-mac-build-venv /tmp/tingwen-mac-build-venv/bin/python -m pip install --upgrade pip9.2 安装依赖/tmp/tingwen-mac-build-venv/bin/python -m pip install edge-tts==7.2.8 /tmp/tingwen-mac-build-venv/bin/python -m pip install PySide6-Essentials==6.10.3 /tmp/tingwen-mac-build-venv/bin/python -m pip install PyInstaller==6.21.0
不建议直接安装完整
PySide6。完整包会拉取本项目不需要的 Addons, 下载量和应用体积都会明显增加。QtWidgets 所需内容位于
PySide6-Essentials。
9.3 直接运行源码cd "/path/to/文本转音频"/tmp/tingwen-mac-build-venv/bin/python mac_app/mp3_converter_qt_app.py10. 生成本地试听资源
新增音色后,必须生成同名试听文件,否则 UI 自检会失败。脚本会跳过已经存在且非空的 文件,只为新增音色生成资源。
/tmp/tingwen-mac-build-venv/bin/python mac_app/generate_preview_assets.py试听资源命名规则:
mac_app/assets/previews/<voice-id>.mp3示例:
mac_app/assets/previews/zh-HK-HiuGaaiNeural.mp3
检查数量:
find mac_app/assets/previews -type f -name "*.mp3" | wc -l当前正确结果应为 26。
11. 打包 macOS 应用资源路径必须使用绝对路径。若同时使用 --specpath mac_app 和相对 --add-data mac_app/assets/...,PyInstaller 可能把路径重复解析成 mac_app/mac_app/assets/...。
cd "/path/to/文本转音频"PYINSTALLER_CONFIG_DIR=/tmp/tingwen-pyinstaller-cache-global \
/tmp/tingwen-mac-build-venv/bin/python -m PyInstaller \
--noconfirm \
--clean \
--windowed \
--target-architecture arm64 \
--name "听文 MP3 转换器" \
--osx-bundle-identifier com.tingwen.mp3converter \
--add-data "/absolute/path/to/文本转音频/mac_app/assets/previews:previews" \
--distpath mac_app/global_dist \
--workpath mac_app/global_build \
--specpath mac_app \
mac_app/mp3_converter_qt_app.py
构建成功后应用位于:
mac_app/global_dist/听文 MP3 转换器.app当前包约 72 MB。它包含 arm64 启动程序、Python Framework、Qt Framework、 Edge TTS 依赖和 26 个试听 MP3。
12. 自动化测试与验收12.1 语法检查python3 -c "source=open('mac_app/mp3_converter_qt_app.py', encoding='utf-8').read(); compile(source, 'mp3_converter_qt_app.py', 'exec'); print('syntax: OK')"12.2 UI 离屏自检检查 26 个卡片、试听资源、按钮文字、输入框和窗口渲染,并输出 PNG。
"mac_app/global_dist/听文 MP3 转换器.app/Contents/MacOS/听文 MP3 转换器" \--self-test-ui /tmp/tingwen-ui-test.png12.3 本地试听速度测试
检查首次启动和切换音色速度,并输出 JSON 报告。
"mac_app/global_dist/听文 MP3 转换器.app/Contents/MacOS/听文 MP3 转换器" \--self-test-preview /tmp/tingwen-preview-report.json
cat /tmp/tingwen-preview-report.json
当前参考结果:首次启动约 1.8 毫秒,切换约 1.3 毫秒。
12.4 单文件端到端转换test_root=$(mktemp -d /tmp/tingwen-convert-test.XXXXXX)"mac_app/global_dist/听文 MP3 转换器.app/Contents/MacOS/听文 MP3 转换器" \
--self-test-convert "$test_root"
find "$test_root" -maxdepth 2 -type f -print12.5 音频格式检查ffprobe -v error \
-show_entries format=duration,size \
-of default=noprint_wrappers=1 \
"/path/to/output.mp3"
afinfo "/path/to/output.mp3"12.6 应用包检查codesign --verify --deep --strict \
"mac_app/global_dist/听文 MP3 转换器.app"
find "mac_app/global_dist/听文 MP3 转换器.app" \
-type f -path "*/previews/*.mp3" | wc -l12.7 Finder 启动路径检查open -n "mac_app/global_dist/听文 MP3 转换器.app"
pgrep -fl "听文 MP3 转换器"
必须通过
open
或 Finder 双击测试一次。仅在终端直接执行
Contents/MacOS/...
不能覆盖真实用户的启动路径。
13. 常见故障与调试方法13.1 Finder 双击无窗口,终端运行正常优先检查 GUI Framework 是否完整、应用是否立即退出,以及 Finder 和终端运行环境的 差异。旧 Tk 版本就是典型案例。Qt 版本可用以下命令确认实际加载了 Cocoa 平台插件:
app_pid=$(pgrep -f "听文 MP3 转换器.app/Contents/MacOS/听文 MP3 转换器" | tail -n 1)lsof -p "$app_pid" | grep -E "libqcocoa|QtWidgets.framework"13.2 PyInstaller 无权写缓存
报错示例:无法写入 ~/Library/Application Support/pyinstaller。
解决方法:把缓存限定到临时目录。
PYINSTALLER_CONFIG_DIR=/tmp/tingwen-pyinstaller-cache \python -m PyInstaller ...13.3 PySide6 下载哈希不匹配
不要使用 --no-deps 或忽略哈希。先升级 pip,再使用全新的临时缓存重试。
python -m pip install --upgrade pipPIP_CACHE_DIR=/tmp/tingwen-pip-cache-clean \
python -m pip install PySide6-Essentials==6.10.313.4 音色点击后等待很久
检查代码是否错误地在点击事件中调用了 Edge TTS。试听必须走 preview_asset_path 和本地 afplay。网络 TTS 只用于正式转换。
13.5 快速切换音色弹出播放错误主动终止上一段试听不是系统故障。切换时先 kill,等待最多 500 ms,再启动 新进程。播放器错误不应弹出阻塞式对话框,可写入运行记录并允许用户重试。
13.6 UI 卡片重叠或被裁切不要只看源码尺寸。运行 --self-test-ui,打开输出 PNG,检查不同数量音色下 的实际布局。26 音色版本采用 7 列 × 4 行,窗口当前为 1000 × 820。
13.7 “试听资源缺失”确认 AVAILABLE_VOICES 中的 ID 拼写正确;确认 assets/previews/<ID>.mp3 存在;确认打包命令包含正确的绝对 --add-data;确认包内试听文件数量等于音色数量。13.8 正式转换失败按以下顺序排查:
输入文件是否存在、是否为 UTF-8、扩展名是否为 TXT/MD;清理后的文本是否为空;互联网是否可用;当前音色 ID 是否仍在 Edge TTS 实时列表中;输出文件夹是否有写权限;Edge TTS 是否返回服务端错误或连接超时。13.9 MP3 只包含第一段或时长异常用 ffprobe 和 afinfo 检查完整文件。确认所有临时片段非空且按 正确顺序写入。当前 Edge TTS MP3 片段可顺序拼接;若以后更换编码器,应重新验证这一 前提,必要时恢复 FFmpeg concat。
13.10 旧进程干扰新版本替换桌面应用前先确认并关闭旧进程:
pgrep -fl "听文 MP3 转换器"kill <确认无误的旧进程 PID>
不要使用宽泛的 pkill Python,避免结束用户的其他程序。
14. 分发与安装14.1 当前兼容性当前构建:Apple Silicon arm64。建议系统:macOS 13 或更高。Intel Mac 需要单独构建 x86_64 或 Universal 2 版本。目标电脑不需要安装 Python、Qt、Edge TTS 或 FFmpeg。14.2 推荐传输方式不要通过会修改应用包结构的聊天工具直接发送 .app。先压缩再传输:
ditto -c -k --sequesterRsrc --keepParent \"/path/to/听文 MP3 转换器.app" \
"/path/to/听文 MP3 转换器.zip"
在另一台 Mac 解压后,将应用拖入“应用程序”文件夹。
14.3 Gatekeeper 提示当前应用采用临时签名结构,但尚未使用 Apple Developer ID 正式签名和公证。另一台 Mac 首次打开时可能提示无法验证开发者。确认文件来源可信后,可在“系统设置 → 隐私与 安全性”中选择“仍要打开”。
面向公众分发时,应购买 Apple Developer 账号,使用 Developer ID Application 证书 签名,并提交 Apple Notary Service 公证。不要要求普通用户长期关闭系统安全机制。
15. 后续扩展建议15.1 增加音色运行 python3 -m edge_tts --list-voices 获取实时列表;确认微软说明支持中文;使用中文样句实际生成 MP3;把元组加入 AVAILABLE_VOICES;运行试听资源生成脚本;调整 UI 列数或窗口高度;把自检中的预期数量同步更新;重新打包并检查包内资源数量。15.2 支持更多文件格式可增加 DOCX、PDF 或 EPUB 解析,但应把“文件解析”做成独立模块,统一返回纯文本,再复用 现有清理、分段与 TTS 流程。扫描 PDF 还需要 OCR,不应直接塞进 GUI 事件函数。
15.3 支持语速、音调与音量可在界面增加语速、音调和音量控件,并把参数传给 edge_tts.Communicate。 试听资源若要反映参数变化,就不能只使用固定内置 MP3;可采用“默认参数本地试听,修改 参数后明确提示在线生成试听”的双轨方案。
15.4 Universal 2 与正式公证若要同时支持 Intel 和 Apple Silicon,需要确认 Python Framework、Qt Framework 和 PyInstaller Bootloader 都包含两种架构,再使用 Universal 2 目标打包。最后应完成 Developer ID 签名、notarytool 提交和 stapler 装订。
16. 发布检查表源码可以通过语法检查。Edge TTS 实时端点仍返回全部配置音色。每个音色都有非空的本地试听 MP3。UI 自检显示全部音色卡片,没有重叠和裁切。点击音色能立即播放,切换音色不会弹出错误对话框。TXT 文件可以生成同名 MP3。Markdown 清理结果符合预期。长文本可以分段并生成完整 MP3。空文件、错误扩展名、无效输出目录都有清晰提示。打包应用包含正确数量的试听资源。codesign --verify --deep --strict 通过。通过 Finder 或 open 启动后窗口正常。目标 Mac 不安装 Python/Qt 时仍可运行。旧桌面版本先改名备份,不直接删除。发布 ZIP、DMG 或 APP 的文件名和版本说明清楚。
17. 维护原则先验证真实用户路径:Finder 启动、文件选择、点击试听和点击生成必须实际测试。功能与资源同步:新增音色必须同时更新配置、试听 MP3、布局和测试数量。不要阻塞 GUI:网络和文件生成任务必须在后台线程执行。保留可恢复版本:替换桌面应用前先改名备份,不直接覆盖唯一可用版本。只改当前目标:正式 Qt 应用与原始 v2 脚本分开维护,避免无关修改。文档基准日期:2026-07-31。由于 Edge TTS 免费端点的音色可能变化,发布新版本前应重新 查询实时音色列表并执行完整回归测试。
如果你想免费使用,请联系我,我这边会免费提供给你,有苹果版还有 Windows 版。