Repository navigation
feat(tts): separate quality voice design and base cloning - #46
Conversation
0b17a4f to
0971692
Compare
|
本轮本地复核已恢复完整 Git 历史及离线 Python 3.12 环境。检测到分支存在并行推进(参考音频 conditioning 和 Ruff 修复材料),不会覆盖这些变更。 当前本轮增量范围:质量探针提前退出时显式关闭生成器;CapabilityRouter 关闭子流后才释放模型槽,且 start 不重复加载已 warm 的另一 capability;ASR 前 eviction 纳入统一 deadline;ASR 验证器异常仅记录稳定错误码、不记录 vendor 异常正文/traceback;对应失败回归和 README/架构状态同步。四个新增回归已在原始基线上复现失败并完成本地修复。提交时将对齐最新 head,使用非强制更新,保留并行 reference-conditioning 改动。 |
本轮增量已提交并通过双平台 CI:
|
生成参考注册已提交,双平台 CI 通过:
|
Close child generators before releasing the TTS capability slot, preserve an already warm capability on repeated start, and apply the request deadline to TTS eviction before ASR. Keep vendor exception text and tracebacks out of intelligibility logs. Add lifecycle/privacy regressions and align readiness fixtures. Update README and architecture docs with implemented ASR validation, reference-conditioning limits, and explicitly deferred voice stabilization. Preserves the concurrent reference canonicalization and ASR request-binding changes on f9b10d5. Tested tree: 32657cf. Ubuntu Python 3.12: 1687 passed, 7 skipped; 83.06% coverage; Ruff/mypy/wheel pass. Validation run: 34676837025. Real MLX/Apple Silicon voice-quality validation remains outstanding; no merge or deployment is performed.
Add an explicit Quality-only POST /v1/voices/designs operation. Generate a bounded VoiceDesign reference, validate raw and canonical signal, evict TTS before local ASR with an empty prompt, then atomically create a new Base-bound clone profile. Existing instruction voices and creation routes are unchanged. Persist optional validated generation provenance and reference hashes. Reject ID conflicts under the registry lock; failed/cancelled validation does not publish a voice. Registration reports synthesis_validation=unevaluated and never claims Base output or independent speaker verification has passed. Add 36 registration, cancellation, persistence, concurrency and schema tests. Update OpenAPI, API docs, architecture, English/Chinese README and changelog. Local Python 3.12: 1722 passed, 7 skipped, one existing zsh-only launcher test excluded because /bin/zsh is unavailable. Coverage 83.20%; Ruff/mypy and wheel checks pass using offline installed build dependencies. Final platform CI and real Apple Silicon acoustic evaluation remain required. Verified patch SHA256: 091f8cc0126b8b28acd8735d9fbe60a18a127ffeb852fb731b3bb60a86b88f1a Verified source tree: d5a931f Staging run: 34680935426
0c403ab to
98abb28
Compare
|
Sona 客户端配套工作已保存为 Draft PR:hrygo/sona#15(https://github.com/hrygo/sona/pull/15),当前提交 包含六种可编辑的声音描述示例与修改保护;显式 客户端用 未合并或部署,也未执行真实 MLX、麦克风、Safari 或声纹/噪声听感验收。配套架构与 README 已在 Sona PR 更新,完整方案保留在两个 PR 中。 |
Route the Quality Base artifact through its verified ModelScope revision, persist clone capability identity so profile changes reactivate the service, and share canonical reference quality evaluation between validation and registration.
Status — incremental implementation, not an audio-quality sign-off
This PR preserves the complete architecture and implementation plan for SpeechRail/Sona voice creation. Continue on
feat/quality-voice-capabilities; do not merge based on unit-test success alone. Real Apple Silicon synthesis, identity evaluation and recording acceptance remain required.Original source audit:
main@9dec75bed02bddecd4be56d0b5491c4b772f0a32. Mainline loudness changes from merged PR #45 (e22738e) are incorporated; #45 is not an outstanding draft dependency.Implemented: catalog-governed Quality VoiceDesign/Base split; Base public reference generation; independent dual-resident/concurrent TTS capability workers with cooldown group eviction; fail-closed variant handshake; signal/repetition quality checks; staged local ASR intelligibility; first-stage server reference canonicalization; and now explicit, create-only generated-reference registration through
POST /v1/voices/designs(0c403ab).Not implemented or not acoustically accepted: Sona integration with the new registration endpoint and PCM capture changes, automatic migration of old instruction voices, neural VAD/conditional denoise, independent speaker validation, reference-feature caching, professional delivery and full voice revision history. New generated-reference registration does not itself execute Base synthesis or certify identity stability; subsequent synthesis and quality validation remain separate.
1. Problem and source evidence
Sona has two distinct creation experiences: prompt-based voice design creates a new voice from a description; reference-audio cloning reproduces an authorized recorded/uploaded speaker.
The audited clone implementation invoked VoiceDesign's private ICL path. In locked MLX-Audio, Base provides the dedicated speaker encoder condition that VoiceDesign lacks. Reference acoustic codes were still present in the old path; it was not completely unconditioned. The model-role mismatch is a strong candidate for cross-text identity drift, not proof that it explains every audible defect.
The initial audit reproduced invalid output falsely passing quality checks: silence, clipping and noise-like signals could pass because generation success was treated as quality success; determinism was asserted rather than measured; only a prefix of the declared probe set was reachable. Recording gain was sensitive to pause duration, with separate client/vendor normalization. These require model-selection, reference-handling and acceptance changes, not merely stronger denoising.
2. Architecture decision and both Sona paths
Quality exclusively owns voice creation; lower tiers remain lightweight:
VoiceDesign creates a voice; Base performs reference cloning and stable-reproduction experiments. Base is not a prompt-to-voice-design replacement. Sona requests business capabilities rather than encoding model repository names.
Existing metadata-only instruction voices still synthesize through VoiceDesign. Their APIs and stored assets are not silently changed. Clients must explicitly call the new generated-reference endpoint; migrating existing voices into revisioned Base-backed identities remains a separate operation. Reference clones never fall back to VoiceDesign private ICL.
Normative architecture:
docs/architecture/quality-voice-capabilities.md; implemented registration contract:docs/architecture/generated-voice-registration.md.3. Catalog, installation and capability contracts
ModelPresetincludes optionaltts_clone, alongsideasrand primarytts:tts_clone: Base.Base is pinned to
mlx-community/Qwen3-TTS-12Hz-1.7B-Base-8bit, revision73ae2cb59832ed1eb13c249e378b854cfc643131(ModelScope); required-file hashes are catalog governed.Installer, model store, preflight, profile selection, catalog/build tools, system reporting, HTTP, MCP and realtime contracts are updated together. Model IDs/private paths are not a frontend requirement. Inference must not silently download missing models. The new generated-registration operation is HTTP-only in this increment; existing MCP creation tools remain compatible and do not silently call it.
4. Runtime lifecycle and identity boundary
Quality owns two independent large-TTS-model workers: VoiceDesign serves the
voice_designlane and Base serves thevoice_clonelane. They are not a request-level mutually exclusive slot. The router keeps the worker lifecycles separate, and each worker keeps its own request lock: different lanes can synthesize concurrently, while requests sharing one lane remain serialized.SPEECHRAIL_WORKER_LAZY_LOAD=falsestarts both workers sequentially at lifecycle startup. With lazy loading enabled, the first request starts only the worker it needs; the other starts on its first request and stays resident. Switching from VoiceDesign to Base, or back, changes routing only and does not close the sibling worker, so the normal “design voice → use existing clone” flow does not repeatedly load/unload either model.ResourceGovernorreceives the internalvoice_design/voice_clonelane key. Distinct Quality lanes may be admitted concurrently when total capacity permits; the same lane, unkeyed TTS, Light/Balanced TTS and unknown injected synthesizers retain conservative serialization. ASR∥TTS remains controlled separately bySPEECHRAIL_ALLOW_HEAVY_OVERLAPand the declared memory budget; Quality TTS resident bytes are counted per possible worker.WorkerIdleEvictortreats the router as one lifecycle group. Warm standby trims both workers, and after the configured idle cooldown it closes both together; the router uses the latest child activity timestamp and each worker lock lets an active stream finish before close. After group eviction, the next request lazily restores only the selected worker. This preserves automatic memory release without reintroducing request-level capability switching.mlx-qwen3-ttsidentifies the backend family;model_variantseparately identifiesvoice_design,custom_voiceorbase. Composition obtains the expected variant from managed metadata or local snapshot inspection. Config and child ready handshake must agree; missing/mismatched variant isbackend_identity_mismatch, never a VoiceDesign fallback. Lifecycle diagnostics reportwarm_capability=bothplus the explicitwarm_capabilitieslist when both workers are ready.5. Quality acceptance — implemented checks and limits
Registration success, reference-input acceptance and synthesis-output acceptance are separate states.
Fixed probe matrix and signal/repetition checks
All six declared Chinese probe classes are covered.
runs=1..3means repetitions per probe; default is 18 TTS samples, not three samples from the list prefix. Checks include malformed/empty PCM, ineffective/silent output, clipping and per-probe repeated PCM SHA-256 comparison. Differences receiveoutput_nondeterministic; fixed seed or a single repetition does not prove repeatability. Valid waveforms/matching hashes do not establish identity or intelligibility.Probe work uses ResourceGovernor, a shared absolute request deadline and public AudioChunk validation. Oversized streams close before proceeding. Queue-full/timeouts remain 429/503, not a hidden quality pass.
Staged local ASR intelligibility
Expected text is never supplied as an ASR prompt. Provisional match thresholds:
>=0.92 pass,0.80..0.92 warn,<0.80 reject. Missing/failing ASR makes synthesis validationunevaluated/transcription_unavailable, never automatically passing. Validation error logging excludes vendor exception text/tracebacks and retains only stable code/request ID.The current bounded 24 kHz → 16 kHz validation resampling is linear, not a claimed high-quality anti-aliasing implementation. Thresholds require real speech, numbers/units/dates, accents and noise calibration. ASR can err/hallucinate; it is not a universal noise detector, perceptual MOS measure or speaker verifier.
6. New explicit generated-reference registration
POST /v1/voices/designsis opt-in and requires Quality VoiceDesign/Base plus local TTS and Batch ASR. JSON fields: requiredid,name,instruction,reference_text; optionalseed=42,language=zh. Initial language is Chinese only. IDs are 1–64 lowercase letters/digits/underscore/hyphen and cannot be system IDs/aliases. Reference text is bounded to 20–240 characters, including validation after TTS normalization; generated PCM is bounded to 2–30 seconds. Extra fields, URLs, uploaded audio and speed options are not accepted here.Flow:
No temporary public voice or reference file is published while TTS/ASR is running. Existing private permissions, immutable audio path, atomic metadata commit and failure cleanup are reused. Concurrent requests cannot overwrite the winner. Existing create-clone callers keep their request/response shape, but the clone quality report is now graded on the persisted canonical reference rather than the raw upload; create-only behavior is explicit for this new operation.
201 returns
voiceplussynthesis_validation: unevaluated. The voice binds to Base through its clone mode, but registration does not invoke Base or execute the six-probe output matrix. Its reference report is not output-quality acceptance. Missing/failing ASR prevents registration (503), rather than publishing an unverified reference. ID conflict is 409; invalid signal/transcript is 400; malformed/oversized model output is 502; queue/mode conflict is 429; shared deadline expiration is 503.New profiles include optional immutable
VoiceCreationmetadata: generated origin, methodvoice_design_reference_v1, VoiceDesign artifact/revision/seed, SHA-256 of instruction, actual reference text and canonical WAV, and preprocessing versionenergy_v1. Registry validates audio/text hashes before write and validates metadata on reload. Public responses do not expose local audio paths. These hashes provide traceability, not signatures or full revision history.Old
/v1/voicesinstruction creation,/v1/voices/previewsand old stored records stay unchanged;/v1/voices/clonekeeps its request/response shape, but its quality report is now graded on the canonicalized reference and persisted accordingly (docs/users/api-contract.md§5.7.1), and stored reports for old clones are not rewritten. This is not an in-place migration or a durable Idempotency-Key implementation: retries using the same ID receive 409 and must inspect the existing asset. Sona must explicitly integrate the new action; this increment does not modify Sona UI.7. Reference conditioning — current implementation and complete roadmap
New reference registration canonicalizes accepted audio and saves the resulting WAV. Vendor loading uses
volume_normalize=False.Engineering initial values: mono PCM16/24 kHz; 20 ms energy windows with −45 dBFS active threshold; active target −20 dBFS; gain cap +9 dB and attenuation bound 12 dB with peak safety taking priority; peak ceiling 0.95; trim only leading/trailing low-energy areas with about 200 ms padding and minimum-duration handling. Internal pauses are not rewritten.
This is energy selection/bounded gain, not neural VAD or denoising, and parameters are not acoustically calibrated optima. Loud noise, reverb, multiple speakers and delivery cadence remain unsolved. Sona capture gain still needs coordinated removal/redesign; old references are not automatically rewritten, so end-to-end single normalization and legacy migration are not claimed complete.
Full intended pipeline:
Sona evolution: AudioWorklet → PCM/WAV with actual device/channel/rate/processing settings; WebM/Opus remains fallback. Prefer several natural short recordings and selecting a reliable reference over requiring professional narration. Distinguish raw recording, conditioned reference and actual synthesized preview.
Noise estimates need credible non-speech evidence; amplitude percentiles are not reliable SNR. Without evidence report not evaluated. Severe clipping/reverb/overlap should trigger re-recording, not promises of restoration. DeepFilterNet3 remains a conditional registration-time enhancement candidate, not mandatory processing on every request; validate local assets before invoking any loader that could download implicitly.
8. Stable voice abstraction, caching and migration
Both creation paths should converge on full VoiceProfile/VoiceRevision semantics: generated/recorded origin, provenance, canonical reference, exact transcript, preprocessing/model identity, hashes and scoped quality evidence. This increment adds generated provenance and immutable reference publication, not a complete multi-revision history or legacy migration system.
Do not silently change existing identities or synthesis meaning. Old voices require explicit revision creation, revalidation and rollback. Metadata-only save is not synthesis validation.
Future feature caches bind immutable audio/text hashes, preprocessing version, tokenizer/model revision and conditioning mode. Current waveform cache is not cached speaker embeddings/reference acoustic codes. Do not share mutable target-text decoder/KV state across requests. Multiple recordings should first improve reference selection; do not average codec tokens or concatenate arbitrary emotions by default.
9. Professional delivery and evaluation experiments
Identity-faithful cloning and professional cadence are separate objectives. Professionalized delivery seeks preserved identity with independent pace, pause and emphasis control; whole-audio speed/gain changes are not that capability.
Evaluate sequentially: A original private-ICL baseline for comparison only; B Base with identical reference to isolate model-path effects; C Base with canonical reference/accurate transcript, separately ablating cleaning/gain/transcription; D speaker-only/x-vector-style conditioning where actually supported, measuring identity/quality tradeoffs; E an expressive backend such as VoxCPM2 for offline narration. Only consider professional TTS → voice conversion if simpler separation is insufficient.
VoxCPM2/VC are research tracks, not deployed realtime capabilities. Validate actual streaming, first-audible latency, output resampling, memory and consistent RTF definitions first.
10. Acceptance, rollout and rollback
Dimensions: independent speaker similarity/cross-text identity distribution; intelligibility, omissions/repetitions/numbers/tail truncation; valid signal; credible noise/perceptual evidence; natural delivery in matched-loudness blind listening; first-audible latency, RTF, memory, cancellation and model switching.
Minimum experiments: six text classes × three repetitions per configuration, cold/warm reference cache, worker restart, short/long outputs. Calibrate independent speaker thresholds on actual data instead of assuming a universal cosine cutoff.
Implementation progression:
Rollback preserves old voice/reference assets. Disable an unavailable Base capability rather than silently restoring incorrect VoiceDesign private ICL; Balanced/Light remain isolated. New generated voices use new IDs, so rejecting/deleting an unsatisfactory new voice does not overwrite its predecessor. CI success does not mean audio acceptance.
11. Validation evidence and remaining checklist
Current head:
66351a6— adds Quality dual-resident VoiceDesign/Base workers, capability-lane admission, concurrent cross-lane synthesis and cooldown group eviction on top of the earlier ModelScope Base and clone-path fixes. Earlier hashes remain for traceability.66351a6: fullpytestpasses (1739 passed, 1 skipped); Ruff, mypy, compileall, OpenAPI lint and diff checks also pass.091f8ccpatch SHA-256, staging run 34680935426, earlier-head CI 34680986292) validated superseded feature tips and does not substitute for the head below.95345b0: run 34688844618 — Ubuntu + macOS-15 / Python 3.12 success.5ff9ef1passed Ubuntu/macOS CI 34677080174. Its resource-safety regressions, ASR/privacy/cleanup improvements and complete plan remain preserved.Before merge:
66351a6(new run pending), including packaging, contracts, Ruff, mypy and coverage.12. Documentation
English/Chinese README, architecture index, Quality voice architecture, clone and VoiceDesign design documents, generated-registration architecture, ASR-intelligibility contract, runtime/operations docs, OpenAPI/HTTP/MCP/realtime contracts and changelog are maintained across this PR. New generated-registration details and errors are in
docs/architecture/generated-voice-registration.mdanddocs/users/api-contract.md. Documents distinguish implemented server/API behavior from pending client integration, migration and acoustic acceptance.