feat(tts): 播放中切音色/语速/音调即时重读 + Edge 重入修复 (#370)#461
Open
chy5301 wants to merge 7 commits into
Open
Conversation
- 新增 respeak.ts,提供跨平台的合成参数变化判定 - isActivePlay() 判定播放状态是否活跃(playing/loading) - shouldRespeakForSynthChange() 判定参数变化是否需要重读 - DashScope:仅需 apiKey 存在时才检查 voice 变化 - Edge:检查 voice/rate/pitch 三个参数 - System:永远返回 false - 新增 VOICE_RESPEAK_DEBOUNCE_MS 常量(250ms 去抖时间) - 单测覆盖 11 个场景
播放中切音色/语速/音调会经 jumpToChunk 对单例 EdgeTTSPlayer 做同步 stop()+speak() 重入。原 speak() 入口同步将 aborted 由 true 复位为 false,悬停在 await 的旧消费循环恢复时守卫全部放行,新旧 async 循环并发、 共享 audioCtx/scheduledEnd/fetchBuffer/onChunkChange 互相污染,导致跳段、 反复读同段、旧音色残留。 镜像 DashScopeTTSPlayer:移除易被同步抹除的 aborted 标志,引入 per-run runId——speak() 入口 ++runId 立即作废旧 run,每个 await 续体/timer/回调以 myRun !== this.runId 早退,decodeAndSchedule 捕获本地 ctx 并在解码后校验 ctx 身份,杜绝旧 run 排程进新 ctx。 仅做 codedogQBY#372 重入对齐一项,不抽流式基类、不动进度/暂停恢复/防抖。
现有用例只覆盖 supersede 路径。补一例锁定「stop() 仅靠 _playing=false 让在途解码续体 bail」这条更微妙的不变量,防 runId 重构后回归。
改音色排下 250ms respeak 定时器后,若窗口内又做一次非重读变更(切引擎、 或改当前引擎不关心的字段),原先不取消该定时器,导致它 fire 后经 jumpToChunk 强制重启播放。在 updateConfig 非重读分支补 clearRespeakTimer(),桌面与移动端一致。
重入时后继 run 会 close 掉前一个 run 正 await 的 AudioContext,使 resume() 以 InvalidStateError reject,经无 .catch 的 speak() 调用变成未处理拒绝。 给 resume() 加 .catch;后续的 myRun !== this.runId 守卫本就会丢弃该 run。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概述
实现 #370:朗读过程中修改音色 / 语速 / 音调即时生效——防抖 250ms 后从当前句重读,覆盖桌面端(core)与移动端(app-expo),合成参数变化判定抽到
@readany/core共享。同时修复实现 #370 时在 Edge 引擎暴露的重入并发 bug:单例
EdgeTTSPlayer在「即时重读」触发的同步stop()+speak()重入下,新旧异步消费循环并发、污染共享状态,表现为切音色时往后跳段、在跳到处反复读同段、先用旧音色读一段再用新音色重读。镜像DashScopeTTSPlayer早有的 per-runrunId隔离根除之。改动
#370 即时重读
core/src/tts/respeak.ts(新增):shouldRespeakForSynthChange(按当前引擎判定 voice/rate/pitch 变化;DashScope 仅认 voice)、250ms 防抖常量、isActivePlay。tts-store.ts:updateConfig接入防抖重读;非重读变更(如切引擎)取消上一次排下的待执行重读,避免陈旧定时器误触发重启。Edge 重入修复(属 #372「抽取流式音频 TTS 播放器基类」中的「重入对齐」一块,被本次 bug 倒逼提前落地)
core/src/tts/tts-players.tsEdgeTTSPlayer:移除会被同步抹除的aborted标志,改 per-runrunId;每个await/timer/回调以myRun !== this.runId早退;decodeAndSchedule捕获本地 ctx 并在解码后校验 ctx 身份;吞掉重入下resume()的 reject。runId写法刻意保持对将来抽基类友好。测试
@readany/core457 测试通过,含 respeak 判定、updateConfig防抖 / 取消、EdgerunIdsupersede /stop()路径 /resume()reject。与 #349 / #427 的关系
#370(改合成配置 → 重读)与 #349(换引擎 → 停旧引擎)是「播放中修改 TTS 设置」同一问题的两半,互补不冲突。#349 已有维护者先于本 PR 创建的在建 PR #427 负责引擎切换那一半;本 PR 只覆盖合成配置这一半,引擎切换停播交由既有的 #427。
updateConfig中保留占位注释[占位 · #427/#349]标明两者整合点。二者都改tts-store.ts,合并时会有冲突,需按互补语义整合(engine 分支停播 +clearRespeakTimer(),synth 分支重读)。已知 / 范围外
Closes #370