首页 / 资讯中心 / 文章详情

VOICEVOX 引擎 Mock(Engine Mock)完全指南:不依赖通信的确定性语音合成模拟实现剖析

VOICEVOX 引擎 Mock(Engine Mock)完全指南:不依赖通信的确定性语音合成模拟实现剖析 ★ FEATURED ARTICLE
桌面应用语音【免费下载链接】voicevox無料で使える中品質なテキスト読み上げソフトウェア、VOICEVOXのエディター项目地址https://gitcode.com/gh_mirrors/vo/voicevox点击查看免费下载VOICEVOX 编辑器本仓库在开发与测试过程中需要一套无需启动真实引擎进程、无需网络通信即可完成文本 → 音声全流程的模拟实现这就是src/mock/engineMock模块。本文以该模块的官方说明文档src/mock/engineMock/README.md为主体骨架结合仓库内各 Mock 文件的实际实现与 EngineConnector.ts 的接入方式系统讲解其设计理念、构建策略、文件构成与逐文件源码级原理。读完本文你将理解 VOICEVOX 如何用一套确定性算法模拟形态素解析、音高/时长估计与波形合成并能在自己的前端工程中复刻同样的确定性 Mock 引擎思路。一、什么是引擎 Mock设计理念与核心约束1.1 概述不做通信的引擎根据 README 的定义引擎 Mock 是不通过通信即可进行语音合成的引擎模拟。真实 VOICEVOX 引擎以 HTTP 服务形式运行编辑器通过 OpenAPI 客户端与其交互而 Mock 则在编辑器进程内直接实现同一套 OpenAPI 接口DefaultApiInterface从而让 UI 与业务逻辑可以脱离真实引擎独立开发、演示与测试。1.2 两条核心设计原则确定性Deterministic同样的输入必须返回同样的输出不同的输入必须返回不同的输出。这是 Mock 能被用于自动化测试的前提——测试断言必须可复现。直觉可验证Intuitive输出尽量符合直觉以便通过观察输出发现 UI 或处理实现的异常。README 给出的两个例子非常直观调低音量参数 → 生成的音频音量变小音高pitch与频率f0保持一致。这两条原则贯穿全部 Mock 文件是理解每一处魔法数字的钥匙。1.3 变更自由README 明确说明Mock 的实现可以放心地进行破坏性变更。这意味着 Mock 代码不承担任何向后兼容义务只服务于编辑器自身的开发调试重构时可以大胆修改。二、构建策略轻量、可嵌入、浏览器可用README 规定 Mock 引擎必须以可嵌入软件的形态实现从而保证浏览器版Browser 构建也能使用。为此制定了两条构建期策略策略具体做法对应实现不执行任何重处理避免词典初始化、图片加载等开销见下方 talkModelMock 与 characterResourceMock 的轻量化手段尽量不把重文件打进构建产物形态素解析词典文件、占位图片等不入包kuromoji 词典在 Node 下从node_modules读取、在浏览器下走 CDN图片通过import.meta.glob按需打包这一策略在 talkModelMock.ts 中体现得最直接getDicPath()根据运行环境选择词典路径——Node 环境指向本地node_modules/kuromoji/dict浏览器环境则指向 jsDelivr CDN从而避免把体积较大的形态素词典随构建产物一起分发。三、文件构成总览README 给出了模块的七个核心文件及其职责结合仓库实际目录src/mock/engineMock/完整清单如下文件职责talkModelMock.tsトーク语音合成 Talk用音声查询生成之前的处理即文本 → アクセント句accent phrasesingModelMock.tsソング歌唱合成 Song用音声查询生成之前的处理即音符Note→ 音素/音高/音量audioQueryMock.ts音声查询AudioQuery / FrameAudioQuery相关负责把查询参数落实到帧级数据synthesisMock.ts音声波形合成把 f0/音量/音素序列变成 WAV 音频数据characterResourceMock.ts角色名、立绘、图标等资源phonemeMock.ts音素表片假名モーラ→ 音素辅音/元音的静态映射manifestMock.ts引擎清单Engine Manifest此外还有两个 README 未列出的配套文件dictMock.ts用户词典 Mock词条增删改查 文本替换index.ts模块总入口createOpenAPIEngineMock()把所有 Mock 组装成DefaultApiInterface。四、总入口createOpenAPIEngineMock 与 EngineConnector 的接入4.1 组装入口index.ts 定义createOpenAPIEngineMock(): DefaultApiInterface通过satisfies PartialDefaultApiInterface实现了一套完整但不全量的 OpenAPI 接口。它实现的函数按功能分组为元信息version()固定返回mock、engineManifest()、supportedDevices()返回{ cpu: true, cuda: false, dml: false }角色信息speakers()、speakerInfo()、singers()、singerInfo()、isInitializedSpeaker()、initializeSpeaker()トーク系audioQuery()、accentPhrases()、moraData()、synthesis()ソング系singFrameAudioQuery()、singFrameF0()、singFrameVolume()、frameSynthesis()辞書系由dictMock.createDictMockApi()展开的getUserDictWords/addUserDictWord/rewriteUserDictWord/deleteUserDictWord。其中synthesis()与frameSynthesis()都返回Blobaudio/wav与真实引擎的 HTTP 响应形态一致。4.2 通过 EngineConnector 接入编辑器Mock 并非绕过架构被硬编码调用而是通过 src/infrastructures/EngineConnector.ts 与真实引擎统一管理OpenAPIEngineConnectorFactory按host缓存真实引擎的DefaultApi实例OpenAPIMockEngineConnectorFactory单例缓存createOpenAPIEngineMock()的结果OpenAPIEngineAndMockConnectorFactory当 host 等于mock://mockprotocol: mock:、hostname: mock时返回 Mock 实例否则返回真实引擎实例见 EngineConnector.ts。也就是说Mock 引擎与真实引擎对上层完全透明切换只需改变引擎 URL。这也是为什么 README 强调 Mock 必须实现与 OpenAPI 相同的接口。五、トーク系文本如何变成アクセント句talkModelMock.ts5.1 形态素解析kuromoji.js 及其 ForkREADME 特别说明本家 kuromoji.js 在路径操作上会报错因此使用 Fork 版且除 Mock 用途外没有使用 kuromoji.js 的计划若 Fork 失效会考虑移除依赖。在 talkModelMock.ts 中可以看到它从kuromoji导入builder、Tokenizer。Tokenizer 采用惰性单例_tokenizer缓存createOrGetTokenizer()避免重复构建且 Node 与浏览器使用不同的dicPath见上文构建策略。5.2 哈希式伪随机Mock 确定性的根基alphabetsToNumber(text)talkModelMock.ts把任意字符串的字符码求和后对 256 取模映射到 0~1 区间——这是一个纯函数哈希同一字符串永远得到同一数值这正是相同输入 → 相同输出的底层保证。基于它衍生出两个确定性估值函数phonemeToLengthMock音素时长 ∈ [0.01, 0.25] 秒phonemeToPitchMock音高 ∈ [3, 5]。5.3 文本 → アクセント句的处理管线textToActtentPhrasesMock(text, styleId)talkModelMock.ts完整流程分词用 kuromoji 将文本切为 token按词性分组遇到記号则插入pauseMora无声vowel: pau并切句遇到助詞则切出一个アクセント句其余 token 按reading读音拼进当前句去掉句末无声最后一个アクセント句不保留pauseMora无声化处理带无声的アクセント句若末尾モーラ为ス/ツ将其元音改为U且音高置 0模拟日语无声音节赋值时长与音高调用replaceLengthMock与replacePitchMock。其中两个赋值函数同样遵循确定性 直觉原则replaceLengthMocktalkModelMock.ts每句最后一个モーラ加长 0.05s句末拉长的直觉辅音时长再除以 5replacePitchMocktalkModelMock.tsアクセント位置accent的モー拉音高 0.3无声音vowel U音高为 0——这正是 README音高与频率一致原则在モーラ层面的体现两者都会叠加i * 0.01 styleId * 0.03的偏移确保不同アクセント句、不同角色输出不雷同。此外aquestalkLikeToAccentPhrasesMock处理 AquesTalk 风格记法由parseKana解析仅复用上述两个赋值函数逻辑与产品版假名指定アクセント接口对应。5.4 音素表phonemeMock.tsphonemeMock.ts 维护了一个从片假名含拗音、促音、ヴァ行、外来语小写假名等到[辅音, 元音]的静态映射表moraToPhonemes。例如ア → [undefined, a]无辅音、カ → [k, a]、ン → [undefined, N]、ッ → [undefined, cl]。它是 talk 与 sing 两条管线共用的基础数据。六、AudioQuery → FrameAudioQuery参数如何落到帧数据audioQueryMock.tsaudioQueryMock.ts 头部注释明确写道与 VOICEVOX ENGINE 仓库的处理几乎相同——即它不是随意造假而是复刻真实引擎的查询转换逻辑。核心函数audioQueryToFrameAudioQueryMock(audioQuery, { enableInterrogativeUpspeak })依次应用处理作用关键公式applyInterrogativeUpspeak疑问句句尾追加高音モーラvowelLength: 0.15音高 0.3需isInterrogative且句末音高 0applyPrePostSilence前后各加一个无声モーラ长度取prePhonemeLength/postPhonemeLengthvowel: silapplyPauseLength统一替换pau时长取pauseLengthapplyPauseLengthScale缩放pau时长vowelLength * pauseLengthScaleapplySpeedScale话速时长/ speedScale越快越短applyPitchScale音高pitch * 2 ** pitchScaleapplyIntonationScale抑扬以有声モー拉平均音高为基准做线性缩放帧化阶段secondToFrame使用固定帧率FRAME_RATE 24000 / 256与 manifest 中defaultSamplingRate: 24000、frameRate: 93.75吻合将每个モーラ/音素的秒数换算为帧数随后生成三路帧序列f0pitch 0 ? 0 : Math.exp(pitch)——音高对数刻度转线性频率再次体现音程与频率一致volume全帧填充volumeScalephonemes每个音素附带frameLength。七、波形合成四种波形的确定性混音synthesisMock.ts7.1 伪随机与四种波形synthesisMock.ts 用线性同余生成器LCGRandom(seed)产生 0~1 的伪随机数seed 由 f0 与 volume 之和取整导出保证同一查询 → 同一波形。generateWave支持四种波形类型sine正弦、square方波、noise噪声、silence静音。7.2 音素特征与波形配合率phonemeFeatures将音素分为五类有声母音、无声母音、无音sil/pau/cl、有声子音、无声子音getWaveRate按类别返回四种波形的配合率无音 → 几乎全静音有声母音 → 无噪声正弦/方波按索引比例分配无声母音 → 噪声占比 0.3有声子音 → 噪声占比 0.2音量偏大无声子音 → 噪声占比 0.1音量偏小。这使不同音素听起来不同且每帧波形随 f0 频率变化能直观听出音高差异。7.3 合成与输出synthesisFrameAudioQueryMock(frameAudioQuery, styleId)以每帧 256 个采样samplePerFrame 256展开波形对配合率做约 10ms 的高斯滤波移动平均applyGaussianFilter见 src/song/utility.ts以避免配合率突变造成刺耳声最终混合后限制在 [-1, 1] 并将音量压到 1/10防爆音叠加(styleId % 977) / 977 / 20的角色偏移977 是注释中随便选的素数让不同角色波形不雷同依据outputStereo决定声道数调用generateWavFileData以 float32 生成 WAV 字节见 src/helpers/fileDataGenerator.ts。八、ソング系音符如何变成音素/音高/音量singModelMock.ts8.1 音符 → 音素含子音挤入notesToFramePhonemesMocksingModelMock.ts逐音符处理休符key undefined且歌词为空→ 生成pau音素长度等于音符帧长普通音符 → 用moraToPhonemesconvertHiraToKanasrc/domain/japanese把歌词假名转为[辅音, 元音]辅音时长取哈希估值并挤入前一个音符若超过前一音符帧长则取其一半元音占满本音符帧长——模拟真实歌唱中子音起始于前音符的发声特性。8.2 音符 → 音高notesAndFramePhonemesToPitchMock用noteNumberToFrequency440 * 2^((noteNumber-69)/12)标准十二平均律 A4440Hz把音符编号转为基频再按音素哈希在±30 cent范围内摇摆phonemeAndKeyToPitchMock并按1 styleId * 0.03偏移休符音高为 0。8.3 音符 → 音量notesAndFramePhonemesAndPitchToVolumeMock依据音高越高音量越大的直觉phonemeAndPitchToVolumeMock取值约 0.8~1.0其中normalized按 note 1~128 的频率范围归一化再乘1 - styleId * 0.03做角色区分。8.4 关于 styleId 的一个特殊兼容notesAndFramePhonemesToPitchMock开头有styleId % 6000singModelMock.ts注释说明出于对产品版引擎的特殊处理可能出现 styleId6000——这是一个来自真实业务场景的兼容性细节说明 Mock 会同步产品版引擎的调用习惯。九、角色资源与引擎清单9.1 characterResourceMock.ts用import.meta.globVite 特性eager: true把 assets/ 下的portrait_*.png与icon_*.png按序打包成 URL 数组避免硬编码路径characterResourceMock.tsbaseCharactersMock定义了 4 个 dummy 角色dummy1~dummy4其 styles 组合刻意覆盖各种情况dummy1 为2 トーク 2 frame_decode、dummy2 为2 トーク 1 frame_decode 1 sing、dummy3 为仅 1 トーク、dummy4 为仅 1 sing——用于测试角色筛选逻辑getSpeakersMock过滤出只能说话的角色style.type 为undefined或talkgetSingersMock过滤出能唱歌的角色frame_decode或sing空角色被剔除getSpeakerInfoMock返回policy、portrait与各 style 的iconvoiceSamples为空数组。9.2 manifestMock.tsmanifestMock.ts 返回名为DUMMY Engine的清单manifestVersion: 0.13.1、defaultSamplingRate: 24000、frameRate: 93.75、1px 的 base64 图标以及supportedFeatures能力表能力字段值含义adjustMoraPitch / adjustPhonemeLength / adjustSpeedScale / adjustPitchScale / adjustIntonationScale / adjustVolumeScale / adjustPauseLengthtrue支持各参数调整interrogativeUpspeaktrue支持疑问句上扬synthesisMorphingfalse不支持变声融合singtrue支持歌唱manageLibraryfalse不支持库管理returnResourceUrltrue资源以 URL 形式返回十、用户词典 MockdictMock.tsdictMock.ts 的DictMock类内部用MapUserDictWordId, UserDictWord管理词条applyDict对文本做简单的正则替换new RegExp(word.surface, g)→word.pronunciationcreateDictMockApi输出四个字典 OpenAPI 函数getUserDictWords、addUserDictWorduuid4()生成 ID默认priority: 5词性固定为名词、rewriteUserDictWord、deleteUserDictWord。它被index.ts的audioQuery/accentPhrases管线通过dictMock.applyDict(payload.text)调用模拟用户词典影响朗读的链路。十一、Mock 引擎在测试中的角色从仓库测试结构看Mock 引擎承担了两类用途单元快照测试tests/unit/mock/engineMock/ 下存在针对 Mock 输出的快照snapshot测试利用其确定性保证输出可断言、可对比E2E 测试浏览器端 E2E 测试如 tests/e2e/browser/音声.spec.ts、音声パラメータ.spec.ts 等依赖 Mock 引擎在无真实引擎环境下的完整合成能力包括音声写出wav与参数调整验证——这也正是直觉可验证原则的最大受益场景。十二、维护注意事项与总结破坏性变更自由Mock 实现以编辑器开发调试为唯一服务对象README 明确允许随意破坏性变更无需维护兼容层外部依赖风险kuromoji.js 使用 Fork 版规避路径操作 bug若 Fork 失效将考虑移除依赖README 已声明迁移时需评估替代的形态素解析方案确定性是底线任何新增 Mock 功能都应沿用纯函数哈希 固定偏移的模式保证相同输入必得相同输出否则会破坏快照与 E2E 测试的可复现性与真实引擎对齐audioQueryMock 声明与 VOICEVOX ENGINE 仓库处理几乎相同新增/修改参数语义时应尽量与真实引擎保持一致避免 Mock 与真实行为出现偏差。综上VOICEVOX 的引擎 Mock 是一套设计精巧的确定性模拟层以createOpenAPIEngineMock为门面、七个职责单一的文件为骨架、哈希伪随机与固定偏移为确定性基石既能在浏览器/Node 双环境下零通信运行又能产出符合直觉、可听辨、可断言的合成结果。对于需要无后端可用但又要全链路可测的桌面/Web 应用这是一个值得直接借鉴的工程范式。赞分享桌面应用语音【免费下载链接】voicevox無料で使える中品質なテキスト読み上げソフトウェア、VOICEVOXのエディター项目地址https://gitcode.com/gh_mirrors/vo/voicevox点击查看免费下载相关推荐SSLsplit终极指南透明SSL/TLS拦截工具完全解析SSLsplit终极指南透明SSL/TLS拦截工具完全解析 SSLsplit是一款强大的透明SSL/TLS拦截工具专门设计用于网络取证、应用安全分析和渗透测Moto Polly 模拟服务完全指南语音描述、发音词典操作与合成语音的本地 Mock 实践Moto Polly 模拟服务完全指南语音描述、发音词典操作与合成语音的本地 Mock 实践 导读 本文以 docs/docs/services/polly.Mock测试ngx-admin API 模拟数据工具Mockaroo 使用指南ngx admin API 模拟数据工具Mockaroo 使用指南 你是否还在为后端接口未就绪而阻碍前端开发进度是否需要大量真实感的测试数据却无从获取本文前端UI组件上一篇daily.dev缓存失效策略如何避免缓存一致性问题与性能瓶颈下一篇Shopware 6 完整部署指南从零开始构建现代化电商平台创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站