Instructions to use Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- CosyVoice
How to use Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64 with CosyVoice:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Release AiTable CosyVoice Vulkan runtime and native fix scripts 20261021-01
Browse filesVersioned binary release plus checked source patch/application script for streaming handoff, silent resampling guard and retained native integrations. No models, private voices or full source snapshot. Retain immutable releases.
- AiTable-CosyVoice-Native-Fixes-20261021-01.zip +3 -0
- AiTable-CosyVoice-Vulkan-Windows-x64-20261021-01.zip +3 -0
- LICENSE +21 -0
- README-20261021-01.md +91 -0
- README.md +91 -3
- SHA256SUMS-20261021-01.txt +10 -0
- THIRD_PARTY_NOTICES.md +9 -0
- aitable-native-fixes-20261021-01.patch +739 -0
- apply-cosyvoice-fixes-20261021-01.py +67 -0
- build-manifest-20261021-01.json +177 -0
- fixes-manifest-20261021-01.json +70 -0
AiTable-CosyVoice-Native-Fixes-20261021-01.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7bae74c8d7c925845fe47c8cd868099ece682f6612641c4edd0d23856021261e
|
| 3 |
+
size 18511
|
AiTable-CosyVoice-Vulkan-Windows-x64-20261021-01.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:38ee37f972d36afb0e6cfbbe16d790bd63d0a9c6e00348fe9ca21f8e272b62c3
|
| 3 |
+
size 107404582
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Lourdle
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README-20261021-01.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
tags:
|
| 4 |
+
- cosyvoice
|
| 5 |
+
- ggml
|
| 6 |
+
- vulkan
|
| 7 |
+
- windows
|
| 8 |
+
- inference-engine
|
| 9 |
+
- community-build
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# CosyVoice.cpp — AiTable Windows Vulkan community build
|
| 13 |
+
|
| 14 |
+
## 下載 / Downloads
|
| 15 |
+
|
| 16 |
+
This is an **inference runtime**, not a voice/model release. 本倉庫提供我們目前使用的 CosyVoice.cpp Vulkan 後端,以及可套用修正的腳本;不含模型、私人參考音色、角色資料或 LoRA 權重。
|
| 17 |
+
|
| 18 |
+
| Version | Binary runtime | Fix script bundle | Notes | Checksums |
|
| 19 |
+
| --- | --- | --- | --- | --- |
|
| 20 |
+
| 20261021-01 | [Runtime ZIP](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/resolve/main/AiTable-CosyVoice-Vulkan-Windows-x64-20261021-01.zip) | [Fixes ZIP](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/resolve/main/AiTable-CosyVoice-Native-Fixes-20261021-01.zip) | [Version notes](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/blob/main/README-20261021-01.md) | [SHA256](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/resolve/main/SHA256SUMS-20261021-01.txt) |
|
| 21 |
+
|
| 22 |
+
**舊版本保留:**每版 ZIP、腳本、patch、manifest、說明與校驗碼使用固定版本名稱,後續新增版本,不覆蓋、不刪除舊版;首頁 README 僅作可更新索引。
|
| 23 |
+
|
| 24 |
+
## Upstream and provenance
|
| 25 |
+
|
| 26 |
+
- Upstream: [Lourdle/cosyvoice.cpp](https://github.com/Lourdle/cosyvoice.cpp), based on **v0.2.0 / 67d45feb2f654bbc66cca25bff78464b3ec08fa1** plus local native changes. This package is not a claim to contain current upstream main.
|
| 27 |
+
- This is an unofficial community build, not an official CosyVoice, Lourdle or AMD release.
|
| 28 |
+
- The binary payload is copied from the installed AiTable build, not rebuilt for publication. The release manifest records each included binary's SHA256.
|
| 29 |
+
- Backend: **Vulkan and CPU**. No ROCm/HIP backend is included. Windows x64; existing local deployment uses Radeon RX 9070. Other GPU/platform compatibility is not validated here.
|
| 30 |
+
- Models can be obtained separately from the upstream [GGUF model repository](https://huggingface.co/Lourdle/Fun-CosyVoice3-0.5B-2512-GGUF). Follow the upstream model/frontend instructions and their separate licenses. Our private fine-tuned voices are not included.
|
| 31 |
+
|
| 32 |
+
## Our native changes / 解決辦法
|
| 33 |
+
|
| 34 |
+
1. **Streaming cumulative mel and quiet handoff:** retain the same job's mel history within existing context capacity, generate the cumulative HiFT prefix, and emit only samples after the committed cursor. Select a low-energy handoff within the available tail (20ms energy window, normally 20–250ms held tail); finalization emits remaining samples. Short prefixes do not invent silence. Cleanup resets per-job state. This reduces some join artifacts; it does not guarantee artifact-free speech.
|
| 35 |
+
2. **Silent-token resampling guard:** a rejected sampled silent token has not advanced the logits/KV state. Check cancellation before sampling, bound consecutive rejected draws to `uint64_t(limit)+2`, reset that count on acceptance, and use the existing failure cleanup when exhausted. This is not a timeout/cancellation mechanism for an already blocked native graph, and does not prove the cause of earlier live timeouts.
|
| 36 |
+
3. **Text buffer reset:** clear the per-request emoji/text output buffer before appending, avoiding stale text across requests.
|
| 37 |
+
4. **Bounded stage diagnostics:** observe processing stages without including request text. A stage marker is not proof of completion or a stack trace.
|
| 38 |
+
5. **Optional dynamic LoRA interface:** retains the experimental rank-4 Q/V adapter API and HTTP integration used by AiTable. No trained adapter or private voice is distributed, and normal TTS does not become ASMR merely by downloading this runtime. A compatible model, validated factors and integration are still required.
|
| 39 |
+
|
| 40 |
+
The source patch contains these changes and their necessary native API/server wiring, not just the two algorithm snippets. It excludes local dependency path rewrites, build caches, full source snapshots and model weights. Rebuilding these sources is not promised to reproduce the published binary bit-for-bit.
|
| 41 |
+
|
| 42 |
+
## Use the binary runtime
|
| 43 |
+
|
| 44 |
+
Extract the complete ZIP. Keep all DLLs beside the EXEs in `runtime/`; do not replace these GGML DLLs with an unrelated llama.cpp build. Python and the compiler toolchain are not needed to run the binary package. A current compatible Vulkan driver and Microsoft Visual C++ x64 runtime may be required.
|
| 45 |
+
|
| 46 |
+
With your own compatible CosyVoice3-2512 GGUF and pre-encoded prompt_speech:
|
| 47 |
+
|
| 48 |
+
```bat
|
| 49 |
+
runtime\cosyvoice-cli.exe --model "D:\Models\CosyVoice3-2512_F16.gguf" --prompt-speech "D:\Voices\prompt_speech.gguf" --mode cross-lingual --text "This is a short test." --output "test.wav" --backend vulkan0
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
For a localhost API example (not started automatically):
|
| 53 |
+
|
| 54 |
+
```bat
|
| 55 |
+
runtime\cosyvoice-server.exe --api --model "D:\Models\CosyVoice3-2512_F16.gguf" --voice-prompt "demo=D:\Voices\prompt_speech.gguf" --backend vulkan0 --host 127.0.0.1 --port 9880 --chunk-tokens 125 --inference-buffer-policy dedicated --simd-level auto
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
Use `--backend cpu` for CPU execution. `--help` lists this binary's supported options. With no pre-encoded prompt, obtain the frontend ONNX assets separately and follow upstream's `--frontend-only` flow. ONNX Runtime DLLs are included; tokenizer/campplus model files are not. Audio output is produced by the model; the runtime does not upscale its training sample rate into higher-fidelity speech.
|
| 59 |
+
|
| 60 |
+
## Apply the fix script / 套用修正腳本
|
| 61 |
+
|
| 62 |
+
Download and extract the Fixes ZIP, or download these three versioned files into one folder:
|
| 63 |
+
|
| 64 |
+
- [Python application script](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/blob/main/apply-cosyvoice-fixes-20261021-01.py)
|
| 65 |
+
- [Native source patch](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/blob/main/aitable-native-fixes-20261021-01.patch)
|
| 66 |
+
- [Pinned source/hash manifest](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/blob/main/fixes-manifest-20261021-01.json)
|
| 67 |
+
|
| 68 |
+
Requires Python 3.9+ and Git. Obtain a **separate upstream checkout at the exact commit**, preserving LF line endings:
|
| 69 |
+
|
| 70 |
+
```bat
|
| 71 |
+
git -c core.autocrlf=false clone https://github.com/Lourdle/cosyvoice.cpp.git cosyvoice-patch-work
|
| 72 |
+
git -C cosyvoice-patch-work checkout 67d45feb2f654bbc66cca25bff78464b3ec08fa1
|
| 73 |
+
python apply-cosyvoice-fixes-20261021-01.py --source cosyvoice-patch-work
|
| 74 |
+
python apply-cosyvoice-fixes-20261021-01.py --source cosyvoice-patch-work --apply
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
Default is check-only. The script verifies the patch and every affected original file, rejects a different/already-patched source, preserves backups and a restore manifest beside the checkout, then checks every output hash. It explicitly disables Git automatic newline conversion for the patch operation. Do not point it at an installed runtime or a working tree with your own modifications to affected files. This does **not** compile, download models, replace installed binaries, restart services or repair arbitrary upstream versions.
|
| 78 |
+
|
| 79 |
+
To restore, consult the backup's `restore-manifest.json`: restore originals and remove only listed newly added files, after checking no later edits would be overwritten. After patching, use upstream build instructions with your own compatible GGML/ONNX/ICU/FFmpeg dependencies. This release's script application was tested; a fresh complete source rebuild was not performed for this publication.
|
| 80 |
+
|
| 81 |
+
## Validation and remaining limits
|
| 82 |
+
|
| 83 |
+
- This publication: packaged CLI/server/quantizer `--help` checks passed with PATH restricted to the packaged runtime and Windows System32. No model/server was launched and all checks exited. ZIP integrity and source-patch application/output hashes are checked separately.
|
| 84 |
+
- Prior validation of the same silent-resampling DLL: CPU fault injection returned failure with zero KV/PCM on a forced-silent case, cancellation completed, and a subsequent synthesis on the same context succeeded. Normal CPU PCM matched the previous core in the tested case. These are limited tests, not full GPU/live validation.
|
| 85 |
+
- Earlier streaming seam and loudness investigations remain unresolved in some listening cases. The package retains our local cumulative-mel/quiet-handoff approach, not an assertion that every pop, loudness jump or strange utterance is fixed. Changing chunk length reduces seam frequency but is not by itself proof of correctness.
|
| 86 |
+
- Cumulative mel/HiFT work may increase time/memory versus upstream overlap-only behavior. This release adds no new RTF, first-audio latency or VRAM benchmark; performance depends on model, reference, chunking and hardware.
|
| 87 |
+
- Large F32 models may exceed a single Vulkan allocation on AMD. This release does not include a multi-buffer loader fix or override that hardware limit.
|
| 88 |
+
|
| 89 |
+
## Licensing
|
| 90 |
+
|
| 91 |
+
CosyVoice.cpp and our native changes use the preserved MIT terms; bundled dependencies retain their own licenses. **FFmpeg components in this package identify as LGPL-3.0-or-later**, not the upstream notice's generic LGPL-2.1 default. See `THIRD_PARTY_NOTICES.md` and `licenses/` for included texts, provenance and source links. MIT repository metadata describes the core and does not relicense FFmpeg, model weights or every dependency.
|
README.md
CHANGED
|
@@ -1,3 +1,91 @@
|
|
| 1 |
-
---
|
| 2 |
-
license: mit
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
tags:
|
| 4 |
+
- cosyvoice
|
| 5 |
+
- ggml
|
| 6 |
+
- vulkan
|
| 7 |
+
- windows
|
| 8 |
+
- inference-engine
|
| 9 |
+
- community-build
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# CosyVoice.cpp — AiTable Windows Vulkan community build
|
| 13 |
+
|
| 14 |
+
## 下載 / Downloads
|
| 15 |
+
|
| 16 |
+
This is an **inference runtime**, not a voice/model release. 本倉庫提供我們目前使用的 CosyVoice.cpp Vulkan 後端,以及可套用修正的腳本;不含模型、私人參考音色、角色資料或 LoRA 權重。
|
| 17 |
+
|
| 18 |
+
| Version | Binary runtime | Fix script bundle | Notes | Checksums |
|
| 19 |
+
| --- | --- | --- | --- | --- |
|
| 20 |
+
| 20261021-01 | [Runtime ZIP](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/resolve/main/AiTable-CosyVoice-Vulkan-Windows-x64-20261021-01.zip) | [Fixes ZIP](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/resolve/main/AiTable-CosyVoice-Native-Fixes-20261021-01.zip) | [Version notes](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/blob/main/README-20261021-01.md) | [SHA256](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/resolve/main/SHA256SUMS-20261021-01.txt) |
|
| 21 |
+
|
| 22 |
+
**舊版本保留:**每版 ZIP、腳本、patch、manifest、說明與校驗碼使用固定版本名稱,後續新增版本,不覆蓋、不刪除舊版;首頁 README 僅作可更新索引。
|
| 23 |
+
|
| 24 |
+
## Upstream and provenance
|
| 25 |
+
|
| 26 |
+
- Upstream: [Lourdle/cosyvoice.cpp](https://github.com/Lourdle/cosyvoice.cpp), based on **v0.2.0 / 67d45feb2f654bbc66cca25bff78464b3ec08fa1** plus local native changes. This package is not a claim to contain current upstream main.
|
| 27 |
+
- This is an unofficial community build, not an official CosyVoice, Lourdle or AMD release.
|
| 28 |
+
- The binary payload is copied from the installed AiTable build, not rebuilt for publication. The release manifest records each included binary's SHA256.
|
| 29 |
+
- Backend: **Vulkan and CPU**. No ROCm/HIP backend is included. Windows x64; existing local deployment uses Radeon RX 9070. Other GPU/platform compatibility is not validated here.
|
| 30 |
+
- Models can be obtained separately from the upstream [GGUF model repository](https://huggingface.co/Lourdle/Fun-CosyVoice3-0.5B-2512-GGUF). Follow the upstream model/frontend instructions and their separate licenses. Our private fine-tuned voices are not included.
|
| 31 |
+
|
| 32 |
+
## Our native changes / 解決辦法
|
| 33 |
+
|
| 34 |
+
1. **Streaming cumulative mel and quiet handoff:** retain the same job's mel history within existing context capacity, generate the cumulative HiFT prefix, and emit only samples after the committed cursor. Select a low-energy handoff within the available tail (20ms energy window, normally 20–250ms held tail); finalization emits remaining samples. Short prefixes do not invent silence. Cleanup resets per-job state. This reduces some join artifacts; it does not guarantee artifact-free speech.
|
| 35 |
+
2. **Silent-token resampling guard:** a rejected sampled silent token has not advanced the logits/KV state. Check cancellation before sampling, bound consecutive rejected draws to `uint64_t(limit)+2`, reset that count on acceptance, and use the existing failure cleanup when exhausted. This is not a timeout/cancellation mechanism for an already blocked native graph, and does not prove the cause of earlier live timeouts.
|
| 36 |
+
3. **Text buffer reset:** clear the per-request emoji/text output buffer before appending, avoiding stale text across requests.
|
| 37 |
+
4. **Bounded stage diagnostics:** observe processing stages without including request text. A stage marker is not proof of completion or a stack trace.
|
| 38 |
+
5. **Optional dynamic LoRA interface:** retains the experimental rank-4 Q/V adapter API and HTTP integration used by AiTable. No trained adapter or private voice is distributed, and normal TTS does not become ASMR merely by downloading this runtime. A compatible model, validated factors and integration are still required.
|
| 39 |
+
|
| 40 |
+
The source patch contains these changes and their necessary native API/server wiring, not just the two algorithm snippets. It excludes local dependency path rewrites, build caches, full source snapshots and model weights. Rebuilding these sources is not promised to reproduce the published binary bit-for-bit.
|
| 41 |
+
|
| 42 |
+
## Use the binary runtime
|
| 43 |
+
|
| 44 |
+
Extract the complete ZIP. Keep all DLLs beside the EXEs in `runtime/`; do not replace these GGML DLLs with an unrelated llama.cpp build. Python and the compiler toolchain are not needed to run the binary package. A current compatible Vulkan driver and Microsoft Visual C++ x64 runtime may be required.
|
| 45 |
+
|
| 46 |
+
With your own compatible CosyVoice3-2512 GGUF and pre-encoded prompt_speech:
|
| 47 |
+
|
| 48 |
+
```bat
|
| 49 |
+
runtime\cosyvoice-cli.exe --model "D:\Models\CosyVoice3-2512_F16.gguf" --prompt-speech "D:\Voices\prompt_speech.gguf" --mode cross-lingual --text "This is a short test." --output "test.wav" --backend vulkan0
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
For a localhost API example (not started automatically):
|
| 53 |
+
|
| 54 |
+
```bat
|
| 55 |
+
runtime\cosyvoice-server.exe --api --model "D:\Models\CosyVoice3-2512_F16.gguf" --voice-prompt "demo=D:\Voices\prompt_speech.gguf" --backend vulkan0 --host 127.0.0.1 --port 9880 --chunk-tokens 125 --inference-buffer-policy dedicated --simd-level auto
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
Use `--backend cpu` for CPU execution. `--help` lists this binary's supported options. With no pre-encoded prompt, obtain the frontend ONNX assets separately and follow upstream's `--frontend-only` flow. ONNX Runtime DLLs are included; tokenizer/campplus model files are not. Audio output is produced by the model; the runtime does not upscale its training sample rate into higher-fidelity speech.
|
| 59 |
+
|
| 60 |
+
## Apply the fix script / 套用修正腳本
|
| 61 |
+
|
| 62 |
+
Download and extract the Fixes ZIP, or download these three versioned files into one folder:
|
| 63 |
+
|
| 64 |
+
- [Python application script](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/blob/main/apply-cosyvoice-fixes-20261021-01.py)
|
| 65 |
+
- [Native source patch](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/blob/main/aitable-native-fixes-20261021-01.patch)
|
| 66 |
+
- [Pinned source/hash manifest](https://huggingface.co/Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64/blob/main/fixes-manifest-20261021-01.json)
|
| 67 |
+
|
| 68 |
+
Requires Python 3.9+ and Git. Obtain a **separate upstream checkout at the exact commit**, preserving LF line endings:
|
| 69 |
+
|
| 70 |
+
```bat
|
| 71 |
+
git -c core.autocrlf=false clone https://github.com/Lourdle/cosyvoice.cpp.git cosyvoice-patch-work
|
| 72 |
+
git -C cosyvoice-patch-work checkout 67d45feb2f654bbc66cca25bff78464b3ec08fa1
|
| 73 |
+
python apply-cosyvoice-fixes-20261021-01.py --source cosyvoice-patch-work
|
| 74 |
+
python apply-cosyvoice-fixes-20261021-01.py --source cosyvoice-patch-work --apply
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
Default is check-only. The script verifies the patch and every affected original file, rejects a different/already-patched source, preserves backups and a restore manifest beside the checkout, then checks every output hash. It explicitly disables Git automatic newline conversion for the patch operation. Do not point it at an installed runtime or a working tree with your own modifications to affected files. This does **not** compile, download models, replace installed binaries, restart services or repair arbitrary upstream versions.
|
| 78 |
+
|
| 79 |
+
To restore, consult the backup's `restore-manifest.json`: restore originals and remove only listed newly added files, after checking no later edits would be overwritten. After patching, use upstream build instructions with your own compatible GGML/ONNX/ICU/FFmpeg dependencies. This release's script application was tested; a fresh complete source rebuild was not performed for this publication.
|
| 80 |
+
|
| 81 |
+
## Validation and remaining limits
|
| 82 |
+
|
| 83 |
+
- This publication: packaged CLI/server/quantizer `--help` checks passed with PATH restricted to the packaged runtime and Windows System32. No model/server was launched and all checks exited. ZIP integrity and source-patch application/output hashes are checked separately.
|
| 84 |
+
- Prior validation of the same silent-resampling DLL: CPU fault injection returned failure with zero KV/PCM on a forced-silent case, cancellation completed, and a subsequent synthesis on the same context succeeded. Normal CPU PCM matched the previous core in the tested case. These are limited tests, not full GPU/live validation.
|
| 85 |
+
- Earlier streaming seam and loudness investigations remain unresolved in some listening cases. The package retains our local cumulative-mel/quiet-handoff approach, not an assertion that every pop, loudness jump or strange utterance is fixed. Changing chunk length reduces seam frequency but is not by itself proof of correctness.
|
| 86 |
+
- Cumulative mel/HiFT work may increase time/memory versus upstream overlap-only behavior. This release adds no new RTF, first-audio latency or VRAM benchmark; performance depends on model, reference, chunking and hardware.
|
| 87 |
+
- Large F32 models may exceed a single Vulkan allocation on AMD. This release does not include a multi-buffer loader fix or override that hardware limit.
|
| 88 |
+
|
| 89 |
+
## Licensing
|
| 90 |
+
|
| 91 |
+
CosyVoice.cpp and our native changes use the preserved MIT terms; bundled dependencies retain their own licenses. **FFmpeg components in this package identify as LGPL-3.0-or-later**, not the upstream notice's generic LGPL-2.1 default. See `THIRD_PARTY_NOTICES.md` and `licenses/` for included texts, provenance and source links. MIT repository metadata describes the core and does not relicense FFmpeg, model weights or every dependency.
|
SHA256SUMS-20261021-01.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
7bae74c8d7c925845fe47c8cd868099ece682f6612641c4edd0d23856021261e AiTable-CosyVoice-Native-Fixes-20261021-01.zip
|
| 2 |
+
38ee37f972d36afb0e6cfbbe16d790bd63d0a9c6e00348fe9ca21f8e272b62c3 AiTable-CosyVoice-Vulkan-Windows-x64-20261021-01.zip
|
| 3 |
+
9cc2f8fe88f5704b39c80ea85a1ce67405ef01a8e3e400472c6fd045219db066 aitable-native-fixes-20261021-01.patch
|
| 4 |
+
c0a0353f6fb83e3fa6f7e8b0535c606de46cbb8832bc1cecbbd47f8985fdecd1 apply-cosyvoice-fixes-20261021-01.py
|
| 5 |
+
bb36c53f2d6db5810424f6adc0b8919a7c43ca829230a5fc78b642b752529863 build-manifest-20261021-01.json
|
| 6 |
+
06e470aeff1db8deb42ec4a31801900ba0b8ebb35385c8e39f379be1cef9c807 fixes-manifest-20261021-01.json
|
| 7 |
+
1626472fca8e6ff90fa9d1561cff421b8b67f3383d171b76ae93e7ed404a3ab2 LICENSE
|
| 8 |
+
2ad119df4c4021e7cfbdf0e0ea6ff6dc4a588567f6a422a114314a8f132e78c5 README-20261021-01.md
|
| 9 |
+
2ad119df4c4021e7cfbdf0e0ea6ff6dc4a588567f6a422a114314a8f132e78c5 README.md
|
| 10 |
+
e92b1154b164468aaddc7f4c2fe18ef62f240f765c47e59f78e2065cc0c5aa09 THIRD_PARTY_NOTICES.md
|
THIRD_PARTY_NOTICES.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Binary dependency notices
|
| 2 |
+
|
| 3 |
+
Core: CosyVoice.cpp, copyright 2026 Lourdle, MIT (LICENSE). AiTable native modifications retain MIT terms.
|
| 4 |
+
|
| 5 |
+
Included DLL components: GGML/llama.cpp (MIT), ONNX Runtime (MIT plus third-party notices), ICU 78.1 (Unicode license), LLVM OpenMP libomp (Apache-2.0 WITH LLVM-exception), FFmpeg shared libraries (this build identifies LGPL-3.0-or-later). Static/header dependencies include PCRE2 (BSD-3-Clause with its upstream exception where applicable), cpp-httplib (MIT), nlohmann/json (MIT), KissFFT-derived FFT references (BSD-3-Clause), and SIMD math references (zlib). Texts and upstream component notices are in licenses/. The upstream notice's statement about no local GGML changes is not a certification of every binary in this community distribution.
|
| 6 |
+
|
| 7 |
+
FFmpeg runtime provenance: ffmpeg executable from the same installed distribution reports n8.1.3-20260921, gcc 16.2.0, --enable-version3 --enable-shared --disable-static, with no --enable-gpl. The package contains unchanged FFmpeg shared libraries, not our own FFmpeg modifications. The executable itself and unrelated ffplay/ffprobe are omitted. Original producer and build recipes: https://github.com/BtbN/FFmpeg-Builds ; FFmpeg versioned source: https://github.com/FFmpeg/FFmpeg/tree/n8.1.3 ; source archive: https://ffmpeg.org/releases/ffmpeg-8.1.3.tar.xz ; source/build dependency recipes: https://github.com/BtbN/FFmpeg-Builds/tree/master/scripts.d . LGPL and GPL license texts are included. Preserve these notices and recipients' rights to modify/replace the dynamically linked libraries for their own use; no restriction on reverse engineering for debugging those modifications is added by this package.
|
| 8 |
+
|
| 9 |
+
Other upstream sources: https://github.com/Lourdle/cosyvoice.cpp ; https://github.com/ggml-org/ggml ; https://github.com/microsoft/onnxruntime ; https://github.com/unicode-org/icu ; https://github.com/llvm/llvm-project ; https://github.com/PCRE2Project/pcre2 ; https://github.com/yhirose/cpp-httplib ; https://github.com/nlohmann/json ; https://github.com/mborgerding/kissfft ; https://zlib.net . Dependency source is not included in the native-fixes archive. This package does not include model licenses as a grant to any private model/voice.
|
aitable-native-fixes-20261021-01.patch
ADDED
|
@@ -0,0 +1,739 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--- a/CMakeLists.txt
|
| 2 |
+
+++ b/CMakeLists.txt
|
| 3 |
+
@@ -399,9 +399,11 @@
|
| 4 |
+
# -----------------------------------------------------------------------------
|
| 5 |
+
# Tools (Executables)
|
| 6 |
+
# -----------------------------------------------------------------------------
|
| 7 |
+
+# Diagnostic library build: reuse the unchanged installed server executable.
|
| 8 |
+
+
|
| 9 |
+
+# -----------------------------------------------------------------------------
|
| 10 |
+
+# Post-Build & Installation Rules
|
| 11 |
+
+# -----------------------------------------------------------------------------
|
| 12 |
+
+include(cmake/Install.cmake)
|
| 13 |
+
+
|
| 14 |
+
add_subdirectory(tools)
|
| 15 |
+
-
|
| 16 |
+
-# -----------------------------------------------------------------------------
|
| 17 |
+
-# Post-Build & Installation Rules
|
| 18 |
+
-# -----------------------------------------------------------------------------
|
| 19 |
+
-include(cmake/Install.cmake)
|
| 20 |
+
--- a/include/cosyvoice.h
|
| 21 |
+
+++ b/include/cosyvoice.h
|
| 22 |
+
@@ -24,6 +24,14 @@
|
| 23 |
+
|
| 24 |
+
// ----------------------------------------------------------------------------
|
| 25 |
+
// Enumerations
|
| 26 |
+
+// AiTable fixed rank-4/alpha-8 Q/V adapter. Caller verifies the exact base and
|
| 27 |
+
+// factor container before this typed boundary. Status: 0 ok, 1 busy, 2 incompatible,
|
| 28 |
+
+// 3 malformed/not-loaded, 4 allocation failed. Loading never enables the adapter.
|
| 29 |
+
+// Factors: layers 0..23, q then v, A[4,896] then B[out,4], row-major FP32.
|
| 30 |
+
+COSYVOICE_API int cosyvoice_lora_load_f32(struct cosyvoice_context* ctx, const float* factors, size_t count);
|
| 31 |
+
+COSYVOICE_API int cosyvoice_lora_set_enabled(struct cosyvoice_context* ctx, bool enabled);
|
| 32 |
+
+// State: 0 unloaded, 1 disabled, 2 enabled. generation changes only on a mode transition.
|
| 33 |
+
+COSYVOICE_API int cosyvoice_lora_state(struct cosyvoice_context* ctx, uint64_t* generation, size_t* bytes);
|
| 34 |
+
// ----------------------------------------------------------------------------
|
| 35 |
+
|
| 36 |
+
/**
|
| 37 |
+
--- /dev/null
|
| 38 |
+
+++ b/src/cosyvoice-diagnostics.h
|
| 39 |
+
@@ -0,0 +1,28 @@
|
| 40 |
+
+// Content-free observed stages only. No synchronization or audio changes.
|
| 41 |
+
+#pragma once
|
| 42 |
+
+#include <cstdio>
|
| 43 |
+
+#include <chrono>
|
| 44 |
+
+namespace aitable_tts_diag {
|
| 45 |
+
+inline thread_local unsigned remaining = 128;
|
| 46 |
+
+inline void reset() noexcept { remaining = 128; }
|
| 47 |
+
+inline void mark(const char* stage, int count = -1, int worker = -1) noexcept {
|
| 48 |
+
+ if (!remaining) return;
|
| 49 |
+
+ --remaining;
|
| 50 |
+
+ const auto clock_ms = static_cast<unsigned long long>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
|
| 51 |
+
+ char line[192];
|
| 52 |
+
+ if (count >= 0 && worker >= 0)
|
| 53 |
+
+ std::snprintf(line, sizeof(line), "[AITABLE_TTS_STAGE] stage=%s count=%d worker=%d clock_ms=%llu\n", stage, count, worker, clock_ms);
|
| 54 |
+
+ else if (worker >= 0)
|
| 55 |
+
+ std::snprintf(line, sizeof(line), "[AITABLE_TTS_STAGE] stage=%s worker=%d clock_ms=%llu\n", stage, worker, clock_ms);
|
| 56 |
+
+ else
|
| 57 |
+
+ std::snprintf(line, sizeof(line), "[AITABLE_TTS_STAGE] stage=%s clock_ms=%llu\n", stage, clock_ms);
|
| 58 |
+
+ std::fputs(line, stderr);
|
| 59 |
+
+ std::fflush(stderr);
|
| 60 |
+
+}
|
| 61 |
+
+struct scope {
|
| 62 |
+
+ const char* end;
|
| 63 |
+
+ int worker;
|
| 64 |
+
+ scope(const char* begin, const char* end, int worker = -1) noexcept : end(end), worker(worker) { mark(begin, -1, worker); }
|
| 65 |
+
+ ~scope() { mark(end, -1, worker); }
|
| 66 |
+
+};
|
| 67 |
+
+}
|
| 68 |
+
--- a/src/cosyvoice-graph.cpp
|
| 69 |
+
+++ b/src/cosyvoice-graph.cpp
|
| 70 |
+
@@ -244,6 +244,9 @@
|
| 71 |
+
ggml_tensor* Linear::build_cgraph(ggml_context* ctx, ggml_tensor* x) const
|
| 72 |
+
{
|
| 73 |
+
auto out = ggml_mul_mat(ctx, weight, x);
|
| 74 |
+
+ if (lora_enabled && *lora_enabled)
|
| 75 |
+
+ out = ggml_add(ctx, out, ggml_scale(ctx,
|
| 76 |
+
+ ggml_mul_mat(ctx, lora_b, ggml_mul_mat(ctx, lora_a, x)), 2.f));
|
| 77 |
+
if (bias)
|
| 78 |
+
out = ggml_add(ctx, out, bias);
|
| 79 |
+
return out;
|
| 80 |
+
--- a/src/cosyvoice-internal.h
|
| 81 |
+
+++ b/src/cosyvoice-internal.h
|
| 82 |
+
@@ -1,3 +1,4 @@
|
| 83 |
+
+#include <shared_mutex>
|
| 84 |
+
#pragma once
|
| 85 |
+
|
| 86 |
+
#if defined(_WIN32) && !defined(COSYVOICE_STATIC)
|
| 87 |
+
@@ -150,6 +151,9 @@
|
| 88 |
+
struct use_count_guard
|
| 89 |
+
{
|
| 90 |
+
cosyvoice_worker_context* worker;
|
| 91 |
+
+ void* adapter_owner = nullptr;
|
| 92 |
+
+ std::shared_lock<std::shared_mutex> adapter_read;
|
| 93 |
+
+ void protect_adapter(cosyvoice_model* model);
|
| 94 |
+
|
| 95 |
+
use_count_guard(cosyvoice_context* ctx);
|
| 96 |
+
use_count_guard(cosyvoice_model* model);
|
| 97 |
+
--- a/src/cosyvoice-kv-cache.cpp
|
| 98 |
+
+++ b/src/cosyvoice-kv-cache.cpp
|
| 99 |
+
@@ -251,8 +251,8 @@
|
| 100 |
+
{
|
| 101 |
+
size_t nbytes = 0;
|
| 102 |
+
auto object_size = get_offloaded_kv_cache_struct_size(layers);
|
| 103 |
+
- for (auto i = 0; i != layers; ++i)
|
| 104 |
+
- nbytes += advance_ptr(offloaded_cache, object_size)->buffer.capacity();
|
| 105 |
+
+ for (int i = 0; i != n_offloaded_kv_slots; ++i)
|
| 106 |
+
+ nbytes += advance_ptr(offloaded_cache, object_size * i)->buffer.capacity();
|
| 107 |
+
return nbytes;
|
| 108 |
+
}
|
| 109 |
+
else return 0;
|
| 110 |
+
@@ -262,11 +262,13 @@
|
| 111 |
+
{
|
| 112 |
+
if (offloaded_cache)
|
| 113 |
+
{
|
| 114 |
+
- ggml_free(offloaded_cache->ctx);
|
| 115 |
+
auto object_size = get_offloaded_kv_cache_struct_size(layers);
|
| 116 |
+
- for (auto i = 0; i != layers; ++i)
|
| 117 |
+
- {
|
| 118 |
+
- auto cur = advance_ptr(offloaded_cache, object_size);
|
| 119 |
+
+ // Invalidate each allocated offload slot; contexts remain owned until
|
| 120 |
+
+ // destruction and are reused by subsequent prefill/offload operations.
|
| 121 |
+
+ for (int i = 0; i != n_offloaded_kv_slots; ++i)
|
| 122 |
+
+ {
|
| 123 |
+
+ auto cur = advance_ptr(offloaded_cache, object_size * i);
|
| 124 |
+
+ ggml_reset(cur->ctx);
|
| 125 |
+
std::vector<char>().swap(cur->buffer);
|
| 126 |
+
cur->len = 0;
|
| 127 |
+
}
|
| 128 |
+
--- a/src/cosyvoice-llm-job.cpp
|
| 129 |
+
+++ b/src/cosyvoice-llm-job.cpp
|
| 130 |
+
@@ -1,3 +1,4 @@
|
| 131 |
+
+#include "cosyvoice-diagnostics.h"
|
| 132 |
+
#include "cosyvoice-internal.h"
|
| 133 |
+
#include "cosyvoice-model.h"
|
| 134 |
+
|
| 135 |
+
@@ -16,6 +17,7 @@
|
| 136 |
+
bool cosyvoice_model_3::llm_job_ext(const int* text, uint32_t text_len, cosyvoice_prompt_t prompt, uint32_t max_new_tokens, bool* final)
|
| 137 |
+
{
|
| 138 |
+
use_count_guard _guard(this);
|
| 139 |
+
+ aitable_tts_diag::scope diagnostic("llm_begin", "llm_end", get_worker_no());
|
| 140 |
+
const auto& params = shared->params;
|
| 141 |
+
auto& sched = worker->sched;
|
| 142 |
+
const auto& llm = cv3_shared->llm;
|
| 143 |
+
@@ -144,8 +146,23 @@
|
| 144 |
+
constexpr uint32_t max_silent_token_num = 5;
|
| 145 |
+
const auto& silent_tokens = cv3_shared->silent_tokens;
|
| 146 |
+
|
| 147 |
+
+ // Rejected samples keep the current logits/KV unchanged. Bound draws
|
| 148 |
+
+ // on that unchanged state separately from the accepted-token budget.
|
| 149 |
+
+ // Use the current generation limit, widened before adding its existing
|
| 150 |
+
+ // two-draw termination envelope; successful acceptance resets retries.
|
| 151 |
+
+ const uint64_t silent_resample_limit = static_cast<uint64_t>(limit) + 2;
|
| 152 |
+
+ uint64_t silent_resamples = 0;
|
| 153 |
+
for (uint32_t n = llm_get_n_accepted_tokens();; ++n)
|
| 154 |
+
{
|
| 155 |
+
+ if (is_stop_requested())
|
| 156 |
+
+ {
|
| 157 |
+
+ worker->llm_input = nullptr;
|
| 158 |
+
+ cosyvoice_call_ggml_log_callback(GGML_LOG_LEVEL_INFO, "LLM generation stopped by user.\n");
|
| 159 |
+
+ if (params.builtin_sampler_rng_policy == COSYVOICE_BUILTIN_SAMPLER_RNG_POLICY_RESET_PER_SESSION)
|
| 160 |
+
+ reset_builtin_sampler_rng();
|
| 161 |
+
+ return false;
|
| 162 |
+
+ }
|
| 163 |
+
+
|
| 164 |
+
llm_prepare_probs(n > min_len);
|
| 165 |
+
const auto token_id = llm_sample_token();
|
| 166 |
+
if (token_id == -1)
|
| 167 |
+
@@ -161,10 +178,11 @@
|
| 168 |
+
++cur_silent_token_num;
|
| 169 |
+
if (cur_silent_token_num > max_silent_token_num)
|
| 170 |
+
{
|
| 171 |
+
- // Drop excess silent tokens — the token is already in the
|
| 172 |
+
- // KV cache (from llm_decode), so update cur to keep the
|
| 173 |
+
- // model state consistent, but skip llm_accept_token so
|
| 174 |
+
- // it does not appear in the output.
|
| 175 |
+
+ // Re-sample the current logits without accepting this token.
|
| 176 |
+
+ // No decode has consumed this sampled token yet. Cancellation
|
| 177 |
+
+ // and a consecutive rejection budget bound this path.
|
| 178 |
+
+ if (++silent_resamples >= silent_resample_limit)
|
| 179 |
+
+ throw std::runtime_error("LLM consecutive silent resampling limit exceeded.\n");
|
| 180 |
+
cur = speech_emb + token_id * speech_row_size;
|
| 181 |
+
--n;
|
| 182 |
+
continue;
|
| 183 |
+
@@ -173,6 +191,7 @@
|
| 184 |
+
else
|
| 185 |
+
cur_silent_token_num = 0;
|
| 186 |
+
|
| 187 |
+
+ silent_resamples = 0;
|
| 188 |
+
llm_accept_token(token_id);
|
| 189 |
+
|
| 190 |
+
if (is_stop_requested())
|
| 191 |
+
--- /dev/null
|
| 192 |
+
+++ b/src/cosyvoice-lora.cpp
|
| 193 |
+
@@ -0,0 +1,82 @@
|
| 194 |
+
+#include "cosyvoice-model.h"
|
| 195 |
+
+#include <algorithm>
|
| 196 |
+
+#include <cmath>
|
| 197 |
+
+#include <span>
|
| 198 |
+
+#include <cstdio>
|
| 199 |
+
+
|
| 200 |
+
+extern bool aitable_adapter_read_owned(void* owner);
|
| 201 |
+
+namespace {
|
| 202 |
+
+constexpr size_t factor_count = 270336;
|
| 203 |
+
+void invalidate(cosyvoice_model_3* model) {
|
| 204 |
+
+ // All workers are idle under the exclusive adapter gate. No model weights
|
| 205 |
+
+ // or noise state are changed; prefill and decode graphs must see the mode.
|
| 206 |
+
+ for (uint32_t i=0; i<model->shared->params.n_workers; ++i) {
|
| 207 |
+
+ auto& w=model->workers[i];
|
| 208 |
+
+ ggml_backend_synchronize(w.backend.get());
|
| 209 |
+
+ ggml_backend_sched_reset(w.sched.get());
|
| 210 |
+
+ w.gf=nullptr; w.llm_input=nullptr; w.llm_probs=nullptr;
|
| 211 |
+
+ w.prompt_crc32=0; w.llm_kv_cache.cur_len=0;
|
| 212 |
+
+ w.llm_kv_cache.clear_offloaded_cache();
|
| 213 |
+
+ }
|
| 214 |
+
+}
|
| 215 |
+
+}
|
| 216 |
+
+int cosyvoice_lora_load_f32(cosyvoice_context* ctx,const float* data,size_t count) {
|
| 217 |
+
+ auto* model=dynamic_cast<cosyvoice_model_3*>(ctx);
|
| 218 |
+
+ if (!model || !data || count!=factor_count) return 3;
|
| 219 |
+
+ auto& state=*model->cv3_shared;
|
| 220 |
+
+ std::unique_lock gate(model->shared->adapter_mutex,std::try_to_lock);
|
| 221 |
+
+ if (!gate.owns_lock()) return 1;
|
| 222 |
+
+ if (state.lora_buffer || state.llm.layers.size()!=24) return 2;
|
| 223 |
+
+ if (!std::all_of(data,data+count,[](float f){return std::isfinite(f);})) return 3;
|
| 224 |
+
+ for (auto& layer:state.llm.layers) {
|
| 225 |
+
+ auto* q=layer.self_attn.q_proj.weight;auto* v=layer.self_attn.v_proj.weight;
|
| 226 |
+
+ if (!q || !v || q->type!=GGML_TYPE_F16 || v->type!=GGML_TYPE_F16 ||
|
| 227 |
+
+ q->ne[0]!=896 || q->ne[1]!=896 || v->ne[0]!=896 || v->ne[1]!=128) return 2;
|
| 228 |
+
+ }
|
| 229 |
+
+ try {
|
| 230 |
+
+ ggml_context_ptr storage(ggml_init({ggml_tensor_overhead()*100, nullptr, true}));
|
| 231 |
+
+ if (!storage) return 4;
|
| 232 |
+
+ std::vector<std::pair<ggml_tensor*,ggml_tensor*>> tensors;
|
| 233 |
+
+ for (int i=0;i<24;++i) for(int output:{896,128}) {
|
| 234 |
+
+ tensors.emplace_back(ggml_new_tensor_2d(storage.get(),GGML_TYPE_F32,896,4),
|
| 235 |
+
+ ggml_new_tensor_2d(storage.get(),GGML_TYPE_F32,4,output));
|
| 236 |
+
+ }
|
| 237 |
+
+ ggml_backend_buffer_ptr buffer(ggml_backend_alloc_ctx_tensors(storage.get(),model->worker->backend.get()));
|
| 238 |
+
+ if (!buffer) return 4;
|
| 239 |
+
+ size_t offset=0;
|
| 240 |
+
+ for (auto [a,b]:tensors) {
|
| 241 |
+
+ for (auto* tensor:{a,b}) {
|
| 242 |
+
+ auto bytes=ggml_nbytes(tensor);
|
| 243 |
+
+ ggml_backend_tensor_set(tensor,data+offset,0,bytes); offset+=bytes/sizeof(float);
|
| 244 |
+
+ }
|
| 245 |
+
+ }
|
| 246 |
+
+ // Commit only after every tensor is allocated and uploaded.
|
| 247 |
+
+ state.lora_ctx=std::move(storage);state.lora_buffer=std::move(buffer);
|
| 248 |
+
+ size_t index=0;
|
| 249 |
+
+ for(auto& layer:state.llm.layers) for(auto* linear:{&layer.self_attn.q_proj,&layer.self_attn.v_proj}) {
|
| 250 |
+
+ auto [a,b]=tensors[index++];linear->lora_a=a;linear->lora_b=b;
|
| 251 |
+
+ linear->lora_enabled=&state.lora_enabled;
|
| 252 |
+
+ }
|
| 253 |
+
+ return 0;
|
| 254 |
+
+ } catch(const std::bad_alloc&) {return 4;}
|
| 255 |
+
+}
|
| 256 |
+
+int cosyvoice_lora_set_enabled(cosyvoice_context* ctx,bool enabled) {
|
| 257 |
+
+ auto* model=dynamic_cast<cosyvoice_model_3*>(ctx);if(!model)return 2;
|
| 258 |
+
+ std::unique_lock gate(model->shared->adapter_mutex,std::try_to_lock);
|
| 259 |
+
+ if(!gate.owns_lock())return 1;
|
| 260 |
+
+ auto& state=*model->cv3_shared;
|
| 261 |
+
+ if(enabled && !state.lora_buffer)return 3;
|
| 262 |
+
+ if(enabled==state.lora_enabled)return 0;
|
| 263 |
+
+ invalidate(model);
|
| 264 |
+
+ state.lora_enabled=enabled;++state.lora_generation;
|
| 265 |
+
+ return 0;
|
| 266 |
+
+}
|
| 267 |
+
+int cosyvoice_lora_state(cosyvoice_context* ctx,uint64_t* generation,size_t* bytes) {
|
| 268 |
+
+ auto* model=dynamic_cast<cosyvoice_model_3*>(ctx);if(!model)return -1;
|
| 269 |
+
+ std::shared_lock<std::shared_mutex> gate;
|
| 270 |
+
+ if(!aitable_adapter_read_owned(model->shared))gate=std::shared_lock(model->shared->adapter_mutex);
|
| 271 |
+
+ auto& state=*model->cv3_shared;
|
| 272 |
+
+ if(generation)*generation=state.lora_generation;
|
| 273 |
+
+ if(bytes)*bytes=state.lora_buffer?ggml_backend_buffer_get_size(state.lora_buffer.get()):0;
|
| 274 |
+
+ return !state.lora_buffer?0:state.lora_enabled?2:1;
|
| 275 |
+
+}
|
| 276 |
+
--- a/src/cosyvoice-model.cpp
|
| 277 |
+
+++ b/src/cosyvoice-model.cpp
|
| 278 |
+
@@ -1,3 +1,4 @@
|
| 279 |
+
+#include "cosyvoice-diagnostics.h"
|
| 280 |
+
#include "cosyvoice-model.h"
|
| 281 |
+
#include "common.h"
|
| 282 |
+
|
| 283 |
+
@@ -551,19 +552,24 @@
|
| 284 |
+
|
| 285 |
+
void cosyvoice_model::request_stop()
|
| 286 |
+
{
|
| 287 |
+
+ aitable_tts_diag::reset();
|
| 288 |
+
if (worker->use_count.load(std::memory_order_acquire) == 0)
|
| 289 |
+
goto cleanup;
|
| 290 |
+
|
| 291 |
+
worker->stop_flag.store(true, std::memory_order_release);
|
| 292 |
+
{
|
| 293 |
+
std::unique_lock<std::mutex> lock(worker->cv_mutex);
|
| 294 |
+
+ aitable_tts_diag::mark("stop_wait_begin", static_cast<int>(worker->use_count.load(std::memory_order_acquire)), get_worker_no());
|
| 295 |
+
worker->cv.wait(lock, [this]() {
|
| 296 |
+
return worker->use_count.load(std::memory_order_acquire) == 0;
|
| 297 |
+
});
|
| 298 |
+
+ aitable_tts_diag::mark("stop_wait_end", static_cast<int>(worker->use_count.load(std::memory_order_acquire)), get_worker_no());
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
cleanup:
|
| 302 |
+
worker->offset = 0;
|
| 303 |
+
+ worker->flow_cache.clear();
|
| 304 |
+
+ worker->chunk_boundaries.clear();
|
| 305 |
+
worker->stop_flag.store(false, std::memory_order_release);
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
--- a/src/cosyvoice-model.h
|
| 309 |
+
+++ b/src/cosyvoice-model.h
|
| 310 |
+
@@ -30,6 +30,7 @@
|
| 311 |
+
|
| 312 |
+
std::mt19937 noise_rng;
|
| 313 |
+
std::shared_mutex noise_mutex;
|
| 314 |
+
+ std::shared_mutex adapter_mutex;
|
| 315 |
+
std::unique_ptr<float[]> rand_noise;
|
| 316 |
+
uint32_t rand_noise_len;
|
| 317 |
+
cosyvoice_generation_config_t config;
|
| 318 |
+
@@ -153,6 +154,10 @@
|
| 319 |
+
|
| 320 |
+
struct cosyvoice_model_3_shared
|
| 321 |
+
{
|
| 322 |
+
+ ggml_context_ptr lora_ctx;
|
| 323 |
+
+ ggml_backend_buffer_ptr lora_buffer;
|
| 324 |
+
+ bool lora_enabled = false;
|
| 325 |
+
+ uint64_t lora_generation = 0;
|
| 326 |
+
CausalMaskedDiffWithDiT flow;
|
| 327 |
+
CausalHiFTGenerator hift;
|
| 328 |
+
CosyVoice3LM llm;
|
| 329 |
+
@@ -168,6 +173,7 @@
|
| 330 |
+
|
| 331 |
+
ggml_context_ptr ctx1;
|
| 332 |
+
ggml_backend_buffer_ptr token2wav_buffer;
|
| 333 |
+
+ uint32_t committed_speech_samples = 0;
|
| 334 |
+
};
|
| 335 |
+
|
| 336 |
+
struct cosyvoice_model_3 : cosyvoice_model
|
| 337 |
+
--- a/src/cosyvoice-modules.h
|
| 338 |
+
+++ b/src/cosyvoice-modules.h
|
| 339 |
+
@@ -35,6 +35,10 @@
|
| 340 |
+
|
| 341 |
+
struct Linear : BasicModule
|
| 342 |
+
{
|
| 343 |
+
+ // Optional low-rank branch. Disabled means the original graph, not scale zero.
|
| 344 |
+
+ ggml_tensor* lora_a = nullptr;
|
| 345 |
+
+ ggml_tensor* lora_b = nullptr;
|
| 346 |
+
+ const bool* lora_enabled = nullptr;
|
| 347 |
+
ggml_tensor* build_cgraph(ggml_context* ctx, ggml_tensor* x) const;
|
| 348 |
+
};
|
| 349 |
+
|
| 350 |
+
--- /dev/null
|
| 351 |
+
+++ b/src/cosyvoice-streaming-handoff.h
|
| 352 |
+
@@ -0,0 +1,30 @@
|
| 353 |
+
+#pragma once
|
| 354 |
+
+#include <algorithm>
|
| 355 |
+
+#include <cstdint>
|
| 356 |
+
+#include <limits>
|
| 357 |
+
+#include <span>
|
| 358 |
+
+#include <stdexcept>
|
| 359 |
+
+
|
| 360 |
+
+namespace cosyvoice_streaming {
|
| 361 |
+
+// Select only from the currently available prefix. Uncommitted samples are
|
| 362 |
+
+// taken from the next cumulative HiFT result; finalization emits everything.
|
| 363 |
+
+inline uint32_t commit_end(std::span<const float> wave, uint32_t prior, uint32_t rate, bool finalize) {
|
| 364 |
+
+ if (wave.size() > std::numeric_limits<uint32_t>::max() || wave.size() < prior)
|
| 365 |
+
+ throw std::runtime_error("CosyVoice streaming prefix regressed or exceeded sample capacity");
|
| 366 |
+
+ const auto end = static_cast<uint32_t>(wave.size());
|
| 367 |
+
+ if (finalize) return end;
|
| 368 |
+
+ const uint32_t half = std::max(1u,rate/100), min_hold = std::max(1u,rate/50);
|
| 369 |
+
+ const uint32_t max_hold = std::max(1u,rate/4), stride = std::max(1u,rate/1000);
|
| 370 |
+
+ // Short prefixes cannot provide a complete energy window. Preserve the
|
| 371 |
+
+ // existing available output rather than inventing silence or dropping it.
|
| 372 |
+
+ if (end <= max_hold || static_cast<uint64_t>(prior)+half+min_hold > end) return end;
|
| 373 |
+
+ uint32_t selected = end;
|
| 374 |
+
+ double best = std::numeric_limits<double>::infinity();
|
| 375 |
+
+ for (uint32_t j = std::max(prior+half,end-max_hold); j <= end-min_hold; j+=stride) {
|
| 376 |
+
+ double energy=0;
|
| 377 |
+
+ for (uint32_t k=j-half;k<j+half;++k) { const double x=wave[k]; energy+=x*x; }
|
| 378 |
+
+ if (energy<best) { best=energy; selected=j; }
|
| 379 |
+
+ }
|
| 380 |
+
+ return selected;
|
| 381 |
+
+}
|
| 382 |
+
+}
|
| 383 |
+
--- a/src/cosyvoice-token2wav.cpp
|
| 384 |
+
+++ b/src/cosyvoice-token2wav.cpp
|
| 385 |
+
@@ -1,3 +1,5 @@
|
| 386 |
+
+#include "cosyvoice-streaming-handoff.h"
|
| 387 |
+
+#include "cosyvoice-diagnostics.h"
|
| 388 |
+
#include "cosyvoice-internal.h"
|
| 389 |
+
#include "cosyvoice-model.h"
|
| 390 |
+
#include "ggml-cpu-flag.h"
|
| 391 |
+
@@ -223,6 +225,7 @@
|
| 392 |
+
kv_cache->cur_len = 0;
|
| 393 |
+
worker->chunk_boundaries.clear();
|
| 394 |
+
worker->flow_cache.clear();
|
| 395 |
+
+ cv3_worker->committed_speech_samples = 0;
|
| 396 |
+
}
|
| 397 |
+
|
| 398 |
+
ggml_reset(ctx0.get());
|
| 399 |
+
@@ -289,6 +292,7 @@
|
| 400 |
+
else
|
| 401 |
+
full_len = seq_len;
|
| 402 |
+
|
| 403 |
+
+ aitable_tts_diag::mark("flow_begin", -1, get_worker_no());
|
| 404 |
+
float* noise_buffer = shared->noise_callback(COSYVOICE_NOISE_CALLBACK_STAGE_BEFORE_FLOW, noise_req, nullptr, shared->noise_callback_ctx);
|
| 405 |
+
ggml_backend_tensor_set_async(backend.get(), ditctx.x, noise_buffer + noise_req - noise_len, 0, ditctx.x->nb[2]);
|
| 406 |
+
|
| 407 |
+
@@ -357,6 +361,7 @@
|
| 408 |
+
load_kv(0);
|
| 409 |
+
|
| 410 |
+
worker->status = ggml_backend_sched_graph_compute(sched.get(), gf);
|
| 411 |
+
+ aitable_tts_diag::mark("flow_first_step_end", -1, get_worker_no());
|
| 412 |
+
shared->noise_callback(COSYVOICE_NOISE_CALLBACK_STAGE_AFTER_FLOW, noise_req, noise_buffer, shared->noise_callback_ctx);
|
| 413 |
+
if (params.inference_buffer_policy != COSYVOICE_INFERENCE_BUFFER_POLICY_DEDICATED)
|
| 414 |
+
llm_set_kv_cache_len(0);
|
| 415 |
+
@@ -455,6 +460,9 @@
|
| 416 |
+
// Phase 3: Copy flow output to speech_feat (persistent buffer)
|
| 417 |
+
ggml_reset(ctx1.get());
|
| 418 |
+
const auto cache_length = streaming ? static_cast<int64_t>(worker->flow_cache.size() / feat->ne[0]) : 0;
|
| 419 |
+
+ // The cumulative mel history is bounded by this context's existing token capacity.
|
| 420 |
+
+ if (streaming && feat->ne[1] + cache_length > static_cast<int64_t>(params.n_max_seq) * flow.token_mel_ratio)
|
| 421 |
+
+ throw std::runtime_error("CosyVoice streaming mel exceeds context capacity");
|
| 422 |
+
ggml_tensor* speech_feat = ggml_new_tensor_2d(ctx1.get(), feat->type, feat->ne[0], feat->ne[1] + cache_length);
|
| 423 |
+
if (cache_length != 0)
|
| 424 |
+
{
|
| 425 |
+
@@ -478,20 +486,12 @@
|
| 426 |
+
ggml_backend_tensor_copy_async(backend.get(), backend.get(), feat, speech_feat);
|
| 427 |
+
}
|
| 428 |
+
|
| 429 |
+
- if (streaming && !finalize)
|
| 430 |
+
- {
|
| 431 |
+
- const auto overlap = static_cast<int64_t>(shared->hift_overlap);
|
| 432 |
+
- const auto n_feat_frames = feat->ne[1];
|
| 433 |
+
- const auto frames_to_keep = std::min(overlap, n_feat_frames);
|
| 434 |
+
- const auto elements_to_keep = static_cast<size_t>(frames_to_keep * feat->ne[0]);
|
| 435 |
+
-
|
| 436 |
+
- worker->flow_cache.resize(elements_to_keep);
|
| 437 |
+
- if (frames_to_keep > 0)
|
| 438 |
+
- {
|
| 439 |
+
- const auto byte_offset = static_cast<size_t>((n_feat_frames - frames_to_keep) * feat->nb[1]);
|
| 440 |
+
- const auto byte_size = static_cast<size_t>(frames_to_keep * feat->nb[1]);
|
| 441 |
+
- ggml_backend_tensor_get_async(backend.get(), feat, worker->flow_cache.data(), byte_offset, byte_size);
|
| 442 |
+
- }
|
| 443 |
+
+ if (streaming && !finalize) {
|
| 444 |
+
+ // Complete input copies before vector growth can invalidate their source.
|
| 445 |
+
+ ggml_backend_synchronize(backend.get());
|
| 446 |
+
+ worker->flow_cache.resize(ggml_nelements(speech_feat));
|
| 447 |
+
+ ggml_backend_tensor_get_async(backend.get(), speech_feat, worker->flow_cache.data(), 0, speech_feat->nb[2]);
|
| 448 |
+
+ ggml_backend_synchronize(backend.get());
|
| 449 |
+
}
|
| 450 |
+
|
| 451 |
+
if (config[flow.decoder.diffusion_steps - 1].cache_kv)
|
| 452 |
+
@@ -529,13 +529,24 @@
|
| 453 |
+
hift.f0_predictor.condnet_0.output_length(speech_feat->ne[0],
|
| 454 |
+
finalize), true), true), true), true) * hift.scale_factor * noise->ne[0]);
|
| 455 |
+
noise_req = std::max(noise_req, noise_len);
|
| 456 |
+
+ aitable_tts_diag::mark("flow_end", -1, get_worker_no());
|
| 457 |
+
+ aitable_tts_diag::mark("hift_begin", -1, get_worker_no());
|
| 458 |
+
noise_buffer = shared->noise_callback(COSYVOICE_NOISE_CALLBACK_STAGE_BEFORE_HIFT, noise_req, nullptr, shared->noise_callback_ctx);
|
| 459 |
+
ggml_backend_tensor_set_async(backend.get(), noise, noise_buffer + noise_req - noise_len, 0, noise->nb[2]);
|
| 460 |
+
|
| 461 |
+
result->data = reinterpret_cast<float*>(generated_speech->data);
|
| 462 |
+
result->length = static_cast<uint32_t>(generated_speech->ne[0]);
|
| 463 |
+
worker->status = ggml_backend_sched_graph_compute(sched.get(), gf);
|
| 464 |
+
+ aitable_tts_diag::mark("hift_end", -1, get_worker_no());
|
| 465 |
+
shared->noise_callback(COSYVOICE_NOISE_CALLBACK_STAGE_AFTER_HIFT, noise_req, noise_buffer, shared->noise_callback_ctx);
|
| 466 |
+
|
| 467 |
+
+ if (worker->status == GGML_STATUS_SUCCESS && streaming) {
|
| 468 |
+
+ const auto prior = cv3_worker->committed_speech_samples;
|
| 469 |
+
+ const auto end = cosyvoice_streaming::commit_end(std::span<const float>(result->data,result->length),prior,get_sample_rate(),finalize);
|
| 470 |
+
+ result->data += prior;
|
| 471 |
+
+ result->length = end-prior;
|
| 472 |
+
+ cv3_worker->committed_speech_samples = finalize ? 0 : end;
|
| 473 |
+
+ if (finalize) worker->flow_cache.clear();
|
| 474 |
+
+ }
|
| 475 |
+
return worker->status == GGML_STATUS_SUCCESS;
|
| 476 |
+
}
|
| 477 |
+
--- a/src/cosyvoice-tts.cpp
|
| 478 |
+
+++ b/src/cosyvoice-tts.cpp
|
| 479 |
+
@@ -1,4 +1,5 @@
|
| 480 |
+
-#include "cosyvoice-internal.h"
|
| 481 |
+
+#include "cosyvoice-diagnostics.h"
|
| 482 |
+
+#include "cosyvoice-internal.h"
|
| 483 |
+
#include "cosyvoice-text-chunk.h"
|
| 484 |
+
|
| 485 |
+
#include <algorithm>
|
| 486 |
+
@@ -45,6 +46,7 @@
|
| 487 |
+
|
| 488 |
+
static inline bool strip_emoji(std::string_view text, std::string& result)
|
| 489 |
+
{
|
| 490 |
+
+ result.clear();
|
| 491 |
+
result.reserve(text.size());
|
| 492 |
+
bool removed = false;
|
| 493 |
+
bool prev_emoji = false;
|
| 494 |
+
@@ -103,6 +105,8 @@
|
| 495 |
+
|
| 496 |
+
bool tts_job(const char* text, const char* instruction, float speed, cosyvoice_inference_mode mode, cosyvoice_generated_speech_ptr result, cosyvoice_tts_audio_callback_t callback, void* user_data)
|
| 497 |
+
{
|
| 498 |
+
+ aitable_tts_diag::reset();
|
| 499 |
+
+ aitable_tts_diag::scope diagnostic("high_level_enter", "high_level_return");
|
| 500 |
+
instruction_cache.resize(prefix_len);
|
| 501 |
+
if (instruction)
|
| 502 |
+
{
|
| 503 |
+
--- a/src/cosyvoice.cpp
|
| 504 |
+
+++ b/src/cosyvoice.cpp
|
| 505 |
+
@@ -1,3 +1,4 @@
|
| 506 |
+
+#include "cosyvoice-diagnostics.h"
|
| 507 |
+
#include "cosyvoice-internal.h"
|
| 508 |
+
#include "cosyvoice-tokenizer.h"
|
| 509 |
+
#include "cosyvoice-model.h"
|
| 510 |
+
@@ -25,8 +26,24 @@
|
| 511 |
+
return dist(gen);
|
| 512 |
+
}
|
| 513 |
+
|
| 514 |
+
+// Nested LLM/Flow guards run on the same host thread as the outer TTS guard.
|
| 515 |
+
+// Share its reader rather than recursively locking std::shared_mutex.
|
| 516 |
+
+static thread_local std::vector<void*> adapter_readers;
|
| 517 |
+
+bool aitable_adapter_read_owned(void* owner) { return std::find(adapter_readers.begin(),adapter_readers.end(),owner)!=adapter_readers.end(); }
|
| 518 |
+
+void use_count_guard::protect_adapter(cosyvoice_model* model)
|
| 519 |
+
+{
|
| 520 |
+
+ auto owner = static_cast<void*>(model->shared);
|
| 521 |
+
+ if (std::find(adapter_readers.begin(), adapter_readers.end(), owner) == adapter_readers.end())
|
| 522 |
+
+ {
|
| 523 |
+
+ adapter_read = std::shared_lock(model->shared->adapter_mutex);
|
| 524 |
+
+ adapter_readers.push_back(owner);
|
| 525 |
+
+ adapter_owner = owner;
|
| 526 |
+
+ }
|
| 527 |
+
+}
|
| 528 |
+
+
|
| 529 |
+
use_count_guard::use_count_guard(cosyvoice_model* model) : worker(model->worker)
|
| 530 |
+
{
|
| 531 |
+
+ protect_adapter(model);
|
| 532 |
+
worker->use_count.fetch_add(1, std::memory_order_relaxed);
|
| 533 |
+
}
|
| 534 |
+
|
| 535 |
+
@@ -34,18 +51,22 @@
|
| 536 |
+
{
|
| 537 |
+
auto* model = dynamic_cast<cosyvoice_model*>(ctx);
|
| 538 |
+
worker = model->worker;
|
| 539 |
+
+ protect_adapter(model);
|
| 540 |
+
if(worker->use_count.load(std::memory_order_acquire) != 0)
|
| 541 |
+
{
|
| 542 |
+
std::unique_lock<std::mutex> lock(worker->cv_mutex);
|
| 543 |
+
+ aitable_tts_diag::mark("guard_wait_begin", static_cast<int>(worker->use_count.load(std::memory_order_acquire)), model->get_worker_no());
|
| 544 |
+
worker->cv.wait(lock, [this]() {
|
| 545 |
+
return worker->use_count.load(std::memory_order_acquire) == 0;
|
| 546 |
+
});
|
| 547 |
+
+ aitable_tts_diag::mark("guard_wait_end", static_cast<int>(worker->use_count.load(std::memory_order_acquire)), model->get_worker_no());
|
| 548 |
+
}
|
| 549 |
+
worker->use_count.fetch_add(1, std::memory_order_relaxed);
|
| 550 |
+
}
|
| 551 |
+
|
| 552 |
+
use_count_guard::~use_count_guard()
|
| 553 |
+
{
|
| 554 |
+
+ if (adapter_owner) adapter_readers.pop_back();
|
| 555 |
+
if (worker->use_count.fetch_sub(1, std::memory_order_release) == 1)
|
| 556 |
+
worker->cv.notify_all();
|
| 557 |
+
}
|
| 558 |
+
@@ -480,7 +501,7 @@
|
| 559 |
+
goto cleanup;
|
| 560 |
+
|
| 561 |
+
if (result.data && result.length > 0
|
| 562 |
+
- && !callback(result.data, result.length, user_data))
|
| 563 |
+
+ && (aitable_tts_diag::mark("pcm_callback"), !callback(result.data, result.length, user_data)))
|
| 564 |
+
goto cleanup;
|
| 565 |
+
} while (n_tokens != ctx->llm_get_n_accepted_tokens());
|
| 566 |
+
|
| 567 |
+
--- a/tools/server/cosyvoice-server-backend.cpp
|
| 568 |
+
+++ b/tools/server/cosyvoice-server-backend.cpp
|
| 569 |
+
@@ -6,6 +6,9 @@
|
| 570 |
+
#endif
|
| 571 |
+
|
| 572 |
+
#include <cmath>
|
| 573 |
+
+#include <fstream>
|
| 574 |
+
+#include <cstdlib>
|
| 575 |
+
+#include <chrono>
|
| 576 |
+
#include <cstdint>
|
| 577 |
+
#include <ctime>
|
| 578 |
+
#include <limits>
|
| 579 |
+
@@ -19,6 +22,16 @@
|
| 580 |
+
#endif
|
| 581 |
+
|
| 582 |
+
using json = nlohmann::json;
|
| 583 |
+
+static bool prepare_adapter(server_runtime&,cosyvoice_context_t,bool,Response&);
|
| 584 |
+
+// A provider may be discarded before its first callback. Keep slot ownership
|
| 585 |
+
+// until either execution finishes or httplib releases that provider, exactly once.
|
| 586 |
+
+struct adapter_stream_slot {
|
| 587 |
+
+ server_runtime& runtime;uint32_t slot;std::atomic<bool> active{true};
|
| 588 |
+
+ adapter_stream_slot(server_runtime& r,uint32_t s):runtime(r),slot(s){}
|
| 589 |
+
+ void release(){if(active.exchange(false))release_thread_slot(runtime,slot);}
|
| 590 |
+
+ ~adapter_stream_slot(){release();}
|
| 591 |
+
+};
|
| 592 |
+
+
|
| 593 |
+
|
| 594 |
+
struct speech_request
|
| 595 |
+
{
|
| 596 |
+
@@ -29,6 +42,7 @@
|
| 597 |
+
float speed = 1.0f;
|
| 598 |
+
std::string instructions;
|
| 599 |
+
bool has_instructions = false;
|
| 600 |
+
+ bool asmr_adapter = false;
|
| 601 |
+
|
| 602 |
+
bool has_seed = false;
|
| 603 |
+
uint32_t seed = 0;
|
| 604 |
+
@@ -330,6 +344,12 @@
|
| 605 |
+
request->has_chunk_tokens = true;
|
| 606 |
+
}
|
| 607 |
+
|
| 608 |
+
+ if (body.contains("asmr_adapter")) {
|
| 609 |
+
+ if (!body["asmr_adapter"].is_boolean()) {
|
| 610 |
+
+ set_openai_error(res,400,"asmr_adapter must be boolean","invalid_request_error","asmr_adapter","invalid_type");return false;
|
| 611 |
+
+ }
|
| 612 |
+
+ request->asmr_adapter=body["asmr_adapter"].get<bool>();
|
| 613 |
+
+ }
|
| 614 |
+
const auto it_instructions = body.find("instructions");
|
| 615 |
+
const auto it_instruction = body.find("instruction");
|
| 616 |
+
if (it_instructions != body.end())
|
| 617 |
+
@@ -402,6 +422,11 @@
|
| 618 |
+
{ "model", runtime.served_model_name },
|
| 619 |
+
{ "voices", runtime.voice_names }
|
| 620 |
+
};
|
| 621 |
+
+ {
|
| 622 |
+
+ std::lock_guard lock(runtime.slot_mutex);
|
| 623 |
+
+ payload["active_synthesis_slots"]=std::count(runtime.slot_in_use.begin(),runtime.slot_in_use.end(),true);
|
| 624 |
+
+ }
|
| 625 |
+
+ payload["asmr_lora_protocol"]=1;
|
| 626 |
+
res.status = 200;
|
| 627 |
+
res.set_content(payload.dump(), "application/json");
|
| 628 |
+
});
|
| 629 |
+
@@ -476,6 +501,9 @@
|
| 630 |
+
return;
|
| 631 |
+
}
|
| 632 |
+
|
| 633 |
+
+ if (request.asmr_adapter && (!request.has_instructions || request.instructions!="Please say a sentence in a very soft voice.")) {
|
| 634 |
+
+ set_openai_error(res,400,"ASMR requires the bound soft-voice preset","invalid_request_error","instructions","adapter_prompt_mismatch");return;
|
| 635 |
+
+ }
|
| 636 |
+
fill_request_log_context_from_request(&log_ctx, request);
|
| 637 |
+
log_request_details(runtime.log_level, log_ctx);
|
| 638 |
+
|
| 639 |
+
@@ -548,6 +576,9 @@
|
| 640 |
+
return;
|
| 641 |
+
}
|
| 642 |
+
|
| 643 |
+
+ if (!prepare_adapter(runtime,model_ctx,request.asmr_adapter,res)) {
|
| 644 |
+
+ release_thread_slot(runtime,slot);return;
|
| 645 |
+
+ }
|
| 646 |
+
std::string payload;
|
| 647 |
+
std::string encoding_error;
|
| 648 |
+
std::string generation_error;
|
| 649 |
+
@@ -594,6 +625,7 @@
|
| 650 |
+
// IMPORTANT: the provider runs AFTER the route handler returns, so
|
| 651 |
+
// any value captured by reference would be a dangling reference.
|
| 652 |
+
// Capture by value or via raw pointers that outlive the handler.
|
| 653 |
+
+ auto lease=std::make_shared<adapter_stream_slot>(runtime,slot);
|
| 654 |
+
auto& rt = runtime;
|
| 655 |
+
auto* vctx = voice_ctx;
|
| 656 |
+
auto log_ctx_copy = log_ctx;
|
| 657 |
+
@@ -601,7 +633,7 @@
|
| 658 |
+
auto conn_checker = req.is_connection_closed; // copy the socket-checker function
|
| 659 |
+
|
| 660 |
+
res.set_chunked_content_provider(content_type,
|
| 661 |
+
- [&rt, slot, req_copy, vctx, format, wav_header, has_wav, log_ctx_copy, applied_seed, conn_checker]
|
| 662 |
+
+ [&rt, slot, req_copy, vctx, format, wav_header, has_wav, log_ctx_copy, applied_seed, conn_checker, lease]
|
| 663 |
+
(size_t /*offset*/, DataSink& sink) -> bool
|
| 664 |
+
{
|
| 665 |
+
auto model_ctx = get_slot_model_context(rt, slot);
|
| 666 |
+
@@ -613,7 +645,7 @@
|
| 667 |
+
if (!sink.write(wav_header.data(), wav_header.size()))
|
| 668 |
+
{
|
| 669 |
+
mon.join();
|
| 670 |
+
- release_thread_slot(rt, slot);
|
| 671 |
+
+ lease->release();
|
| 672 |
+
sink.done();
|
| 673 |
+
return true;
|
| 674 |
+
}
|
| 675 |
+
@@ -649,7 +681,7 @@
|
| 676 |
+
req_copy.speed, stream_cb, &cb_ctx);
|
| 677 |
+
|
| 678 |
+
mon.join();
|
| 679 |
+
- release_thread_slot(rt, slot);
|
| 680 |
+
+ lease->release();
|
| 681 |
+
sink.done();
|
| 682 |
+
|
| 683 |
+
if (!ok && !aborted)
|
| 684 |
+
@@ -665,7 +697,7 @@
|
| 685 |
+
|
| 686 |
+
return true;
|
| 687 |
+
},
|
| 688 |
+
- [](bool /*success*/) { /* resource releaser — no-op */ });
|
| 689 |
+
+ [lease](bool /*success*/) { lease->release(); });
|
| 690 |
+
|
| 691 |
+
log_request_details(runtime.log_level, log_ctx);
|
| 692 |
+
return;
|
| 693 |
+
@@ -709,6 +741,46 @@
|
| 694 |
+
log_request_done(runtime.log_level, log_ctx, request_log_status::ok, res.status, applied_seed, res.body.size(), "speech");
|
| 695 |
+
}
|
| 696 |
+
});
|
| 697 |
+
+}
|
| 698 |
+
+
|
| 699 |
+
+// The host verifies the exact model/container before starting this owned
|
| 700 |
+
+// subprocess. Only this local startup path may select an adapter file; HTTP
|
| 701 |
+
+// clients select a mode, never a filesystem path or arbitrary weights.
|
| 702 |
+
+static bool prepare_adapter(server_runtime& rt,cosyvoice_context_t ctx,bool enabled,Response& res) {
|
| 703 |
+
+ const auto begin=std::chrono::steady_clock::now();
|
| 704 |
+
+ uint64_t generation=0;size_t bytes=0;
|
| 705 |
+
+ int state=cosyvoice_lora_state(ctx,&generation,&bytes);
|
| 706 |
+
+ if (enabled && rt.concurrency!=1) {
|
| 707 |
+
+ set_openai_error(res,400,"Adapter requires one owned synthesis slot","invalid_request_error","asmr_adapter","adapter_concurrency_unsupported");return false;
|
| 708 |
+
+ }
|
| 709 |
+
+ if(enabled && state==0) {
|
| 710 |
+
+ const char* path=std::getenv("AITABLE_COSY_ASMR_F32");
|
| 711 |
+
+ if(!path || !*path) {
|
| 712 |
+
+ set_openai_error(res,400,"No verified ASMR adapter configured","invalid_request_error","asmr_adapter","adapter_unavailable");return false;
|
| 713 |
+
+ }
|
| 714 |
+
+ std::ifstream file(path,std::ios::binary|std::ios::ate);
|
| 715 |
+
+ constexpr size_t size=270336*sizeof(float);
|
| 716 |
+
+ if(!file || file.tellg()!=size) {
|
| 717 |
+
+ set_openai_error(res,500,"Invalid ASMR factor container","server_error","asmr_adapter","adapter_container_invalid");return false;
|
| 718 |
+
+ }
|
| 719 |
+
+ std::vector<float> data(270336);file.seekg(0);file.read(reinterpret_cast<char*>(data.data()),size);
|
| 720 |
+
+ int status=file?cosyvoice_lora_load_f32(ctx,data.data(),data.size()):3;
|
| 721 |
+
+ if(status) {
|
| 722 |
+
+ set_openai_error(res,500,"ASMR adapter load failed","server_error","asmr_adapter","adapter_load_failed");return false;
|
| 723 |
+
+ }
|
| 724 |
+
+ }
|
| 725 |
+
+ // Do not change ordinary multi-slot servers with no configured adapter.
|
| 726 |
+
+ if(enabled || state!=0) {
|
| 727 |
+
+ if(cosyvoice_lora_set_enabled(ctx,enabled)!=0) {
|
| 728 |
+
+ set_openai_error(res,409,"ASMR adapter is busy or unavailable","server_error","asmr_adapter","adapter_switch_failed");return false;
|
| 729 |
+
+ }
|
| 730 |
+
+ }
|
| 731 |
+
+ state=cosyvoice_lora_state(ctx,&generation,&bytes);
|
| 732 |
+
+ res.set_header("X-AiTable-LoRA-State",state==2?"enabled":state==1?"disabled":"unloaded");
|
| 733 |
+
+ res.set_header("X-AiTable-LoRA-Generation",std::to_string(generation));
|
| 734 |
+
+ res.set_header("X-AiTable-LoRA-Bytes",std::to_string(bytes));
|
| 735 |
+
+ res.set_header("X-AiTable-LoRA-Switch-Microseconds",std::to_string(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-begin).count()));
|
| 736 |
+
+ return true;
|
| 737 |
+
}
|
| 738 |
+
|
| 739 |
+
int cosyvoice_server_backend_run(server_runtime& runtime)
|
apply-cosyvoice-fixes-20261021-01.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Check or apply the version-pinned AiTable CosyVoice native patch. No build/start."""
|
| 2 |
+
import argparse
|
| 3 |
+
import hashlib
|
| 4 |
+
import json
|
| 5 |
+
from pathlib import Path, PurePosixPath
|
| 6 |
+
import shutil
|
| 7 |
+
import subprocess
|
| 8 |
+
import uuid
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def digest(path):
|
| 12 |
+
return hashlib.sha256(path.read_bytes()).hexdigest() if path.is_file() else None
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def main():
|
| 16 |
+
parser = argparse.ArgumentParser(description=__doc__)
|
| 17 |
+
parser.add_argument('--source', required=True, type=Path, help='Separate upstream source checkout; do not target installed runtimes')
|
| 18 |
+
parser.add_argument('--apply', action='store_true', help='Apply after checks; default checks only')
|
| 19 |
+
args = parser.parse_args()
|
| 20 |
+
bundle = Path(__file__).resolve().parent
|
| 21 |
+
manifest = json.loads((bundle / 'fixes-manifest-20261021-01.json').read_text(encoding='utf-8'))
|
| 22 |
+
patch = bundle / 'aitable-native-fixes-20261021-01.patch'
|
| 23 |
+
if digest(patch) != manifest['patch_sha256']:
|
| 24 |
+
raise RuntimeError('Patch checksum mismatch')
|
| 25 |
+
source = args.source.resolve(strict=True)
|
| 26 |
+
if not source.is_dir():
|
| 27 |
+
raise RuntimeError('Source must be a directory')
|
| 28 |
+
targets = {}
|
| 29 |
+
for name, hashes in manifest['files'].items():
|
| 30 |
+
relative = PurePosixPath(name)
|
| 31 |
+
if relative.is_absolute() or '..' in relative.parts or ':' in name or '\\' in name:
|
| 32 |
+
raise RuntimeError('Unsafe manifest path: ' + name)
|
| 33 |
+
target = source.joinpath(*relative.parts)
|
| 34 |
+
if not target.resolve().is_relative_to(source) or target.is_symlink():
|
| 35 |
+
raise RuntimeError('Target escapes the source tree: ' + name)
|
| 36 |
+
if target.exists() and not target.is_file():
|
| 37 |
+
raise RuntimeError('Target is not a regular file: ' + name)
|
| 38 |
+
if digest(target) != hashes['before']:
|
| 39 |
+
raise RuntimeError('Source differs from pinned upstream (or already patched): ' + name)
|
| 40 |
+
targets[name] = target
|
| 41 |
+
git_apply = ['git', '-c', 'core.autocrlf=false', '-c', 'apply.whitespace=nowarn', 'apply']
|
| 42 |
+
subprocess.run([*git_apply, '--check', str(patch)], cwd=source, check=True, timeout=30)
|
| 43 |
+
if not args.apply:
|
| 44 |
+
print('CHECK PASSED: upstream ' + manifest['upstream_commit'] + '; no files modified')
|
| 45 |
+
return
|
| 46 |
+
backup = source.parent / (source.name + '-aitable-backup-' + uuid.uuid4().hex)
|
| 47 |
+
backup.mkdir()
|
| 48 |
+
for name, target in targets.items():
|
| 49 |
+
if target.exists():
|
| 50 |
+
saved = backup / name
|
| 51 |
+
saved.parent.mkdir(parents=True, exist_ok=True)
|
| 52 |
+
shutil.copy2(target, saved)
|
| 53 |
+
(backup / 'restore-manifest.json').write_text(json.dumps({
|
| 54 |
+
'source': str(source), 'files': manifest['files'],
|
| 55 |
+
'restore': 'Stop builds first. Restore saved files; remove only files whose before hash is null. Refuse to overwrite later changes unless verified.'
|
| 56 |
+
}, indent=2), encoding='utf-8')
|
| 57 |
+
print('BACKUP: ' + str(backup), flush=True)
|
| 58 |
+
# Git apply is atomic by default; never pass --reject or allow partial application.
|
| 59 |
+
subprocess.run([*git_apply, str(patch)], cwd=source, check=True, timeout=30)
|
| 60 |
+
for name, target in targets.items():
|
| 61 |
+
if digest(target) != manifest['files'][name]['after']:
|
| 62 |
+
raise RuntimeError('Post-apply checksum mismatch; inspect retained backup: ' + name)
|
| 63 |
+
print('APPLIED AND VERIFIED. No compilation, model download or service start performed.')
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
if __name__ == '__main__':
|
| 67 |
+
main()
|
build-manifest-20261021-01.json
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"release": "20261021-01",
|
| 3 |
+
"repo": "Roland2531/cosyvoice.cpp-AiTable-Vulkan-Windows-x64",
|
| 4 |
+
"upstream": "https://github.com/Lourdle/cosyvoice.cpp",
|
| 5 |
+
"upstream_version": "0.2.0",
|
| 6 |
+
"upstream_commit": "67d45feb2f654bbc66cca25bff78464b3ec08fa1",
|
| 7 |
+
"runtime_core_sha256": "806a6c7a21a82c21c5d5c543586ec7ba48974574dc96991dc8bdc92b5180a277",
|
| 8 |
+
"backend": "Vulkan + CPU; no ROCm backend",
|
| 9 |
+
"patches": [
|
| 10 |
+
{
|
| 11 |
+
"id": "tts-wait-diagnostics-20261018",
|
| 12 |
+
"scope": "observability_only; subsequent streaming patch declared separately"
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"id": "cosyvoice-streaming-handoff-20261018",
|
| 16 |
+
"scope": "CosyVoice3 native streaming: cumulative mel and causal quiet handoff"
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"id": "cosy-asmr-dynamic-20261018",
|
| 20 |
+
"scope": "see release notes"
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"id": "cosyvoice-emoji-buffer-reset-20261019",
|
| 24 |
+
"scope": "clear per-request emoji output string before appending; no model or playback changes"
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
"id": "cosyvoice-silent-resampling-20261021",
|
| 28 |
+
"scope": "bounded consecutive silent rejection; cancellation checked before sampling and after acceptance; existing failure cleanup"
|
| 29 |
+
}
|
| 30 |
+
],
|
| 31 |
+
"files": {
|
| 32 |
+
"avcodec-62.dll": {
|
| 33 |
+
"bytes": 90163200,
|
| 34 |
+
"sha256": "13dae8e27fb521d7fe2f387ebc143a071fd1204d1b70ea21c303d292d2a4821c"
|
| 35 |
+
},
|
| 36 |
+
"avdevice-62.dll": {
|
| 37 |
+
"bytes": 4047872,
|
| 38 |
+
"sha256": "5921b7e8d06cb63db9f41dcdc7b5db85ef81126f516a43b678ef3488dab9fea9"
|
| 39 |
+
},
|
| 40 |
+
"avfilter-11.dll": {
|
| 41 |
+
"bytes": 30822912,
|
| 42 |
+
"sha256": "14210aaa0a6426c3b73a29bb81dfdd925d02f55262da9366f3cd0ee9359eb8f7"
|
| 43 |
+
},
|
| 44 |
+
"avformat-62.dll": {
|
| 45 |
+
"bytes": 22626816,
|
| 46 |
+
"sha256": "de4edb05dd144bbe3232a70052b3a6b849f2f0ffa0e59b0e0cd919a2529c5367"
|
| 47 |
+
},
|
| 48 |
+
"avutil-60.dll": {
|
| 49 |
+
"bytes": 3011584,
|
| 50 |
+
"sha256": "1c8cb40ef03d13aeae7603d1b7c21e0deb105346adbf6140edd15ae16c0b6628"
|
| 51 |
+
},
|
| 52 |
+
"cosyvoice-cli.exe": {
|
| 53 |
+
"bytes": 461824,
|
| 54 |
+
"sha256": "5bc5c3ee0fe74f367c1df1d9e3f0c032bd54d5955e8165d55627e7e5cb2718dd"
|
| 55 |
+
},
|
| 56 |
+
"cosyvoice-server.exe": {
|
| 57 |
+
"bytes": 875008,
|
| 58 |
+
"sha256": "950fa8e5bc047d32ad74b5f2f71e9f0909edc9b0c83aca83134ed6aafeec2505"
|
| 59 |
+
},
|
| 60 |
+
"cosyvoice.dll": {
|
| 61 |
+
"bytes": 1426432,
|
| 62 |
+
"sha256": "806a6c7a21a82c21c5d5c543586ec7ba48974574dc96991dc8bdc92b5180a277"
|
| 63 |
+
},
|
| 64 |
+
"ggml-base.dll": {
|
| 65 |
+
"bytes": 796672,
|
| 66 |
+
"sha256": "df7e8d4f935620e7bd6c4cc346786a7a778d6711cfcb1bdfd10c9db64d43d00f"
|
| 67 |
+
},
|
| 68 |
+
"ggml-cpu-sse42.dll": {
|
| 69 |
+
"bytes": 933376,
|
| 70 |
+
"sha256": "c71727180721f13785ffdd921c029be9f0d64b98e459d1779bfc27448f037bca"
|
| 71 |
+
},
|
| 72 |
+
"ggml-cpu-x64.dll": {
|
| 73 |
+
"bytes": 925184,
|
| 74 |
+
"sha256": "8fc299ed003cb64fec5c09e8bb02c2a9af7937e2087f7d8e8c7cee47738fbc32"
|
| 75 |
+
},
|
| 76 |
+
"ggml-cpu-zen4.dll": {
|
| 77 |
+
"bytes": 1442304,
|
| 78 |
+
"sha256": "e11cb7bd2ac684a35a27a7500a7da31ae43ce1e2a96e1c47009ca3db472ed20b"
|
| 79 |
+
},
|
| 80 |
+
"ggml-rpc.dll": {
|
| 81 |
+
"bytes": 168448,
|
| 82 |
+
"sha256": "746cba70f9a7b873ccb80870b63df2d51a90cf843e57e55f7948da894b166cf2"
|
| 83 |
+
},
|
| 84 |
+
"ggml-vulkan.dll": {
|
| 85 |
+
"bytes": 43318784,
|
| 86 |
+
"sha256": "1eefd2ca8347f0eb5fb29835d4deb596818381537a996cafe5a48b7136fcd082"
|
| 87 |
+
},
|
| 88 |
+
"ggml.dll": {
|
| 89 |
+
"bytes": 79872,
|
| 90 |
+
"sha256": "3113a27c04130a3623b308a6ce782099bf809376a4f4f025a8993b7e257ad30d"
|
| 91 |
+
},
|
| 92 |
+
"icudt78.dll": {
|
| 93 |
+
"bytes": 33110528,
|
| 94 |
+
"sha256": "b937bcec6cbcab5891029d91e2e7a2f803cc2ce258a3546970d0284a528709d5"
|
| 95 |
+
},
|
| 96 |
+
"icuin78.dll": {
|
| 97 |
+
"bytes": 3408896,
|
| 98 |
+
"sha256": "4d5ac61334757813f8ddf5c1ab5e28302ae2392d266450a187968aa66e164e7c"
|
| 99 |
+
},
|
| 100 |
+
"icuio78.dll": {
|
| 101 |
+
"bytes": 62464,
|
| 102 |
+
"sha256": "aafea4f5bd2cfefd734acb12cfccad1eef5f64f31c6854ff4048ca126d4c9005"
|
| 103 |
+
},
|
| 104 |
+
"icutu78.dll": {
|
| 105 |
+
"bytes": 391680,
|
| 106 |
+
"sha256": "0638be559795ecd358431e510e108b5c861c4a7a1f3fe6565732b4ec17e94881"
|
| 107 |
+
},
|
| 108 |
+
"icuuc78.dll": {
|
| 109 |
+
"bytes": 2576384,
|
| 110 |
+
"sha256": "2566e47efc4fe7edc5ea6a333051ae522cd1cbeaf353ece5f3ac97e056e39c9a"
|
| 111 |
+
},
|
| 112 |
+
"libomp.dll": {
|
| 113 |
+
"bytes": 768000,
|
| 114 |
+
"sha256": "a12116ba72d1d6820407cf30be23da04ce79d6bb8a71a5ee71759c5a1faa6f1c"
|
| 115 |
+
},
|
| 116 |
+
"onnxruntime.dll": {
|
| 117 |
+
"bytes": 16462648,
|
| 118 |
+
"sha256": "7e39e2bdbba836d98071ef28620735ba36a47c554cf794585269aecc50fab0da"
|
| 119 |
+
},
|
| 120 |
+
"onnxruntime_providers_shared.dll": {
|
| 121 |
+
"bytes": 21816,
|
| 122 |
+
"sha256": "b9b7ab9e2a8b08ee7ae4a7ac1c8bfd44a741f17ad8d0f764953140a57de0b796"
|
| 123 |
+
},
|
| 124 |
+
"quantize.exe": {
|
| 125 |
+
"bytes": 654848,
|
| 126 |
+
"sha256": "a208042c7dfc9153b66359f017f5e462302534314956114c4d9c7d516ce65375"
|
| 127 |
+
},
|
| 128 |
+
"swresample-6.dll": {
|
| 129 |
+
"bytes": 733696,
|
| 130 |
+
"sha256": "8dda56981dabeff97e0296d336d042e3312f5703ddef5117d5497dc1466c2cc9"
|
| 131 |
+
},
|
| 132 |
+
"swscale-9.dll": {
|
| 133 |
+
"bytes": 12823040,
|
| 134 |
+
"sha256": "1f6ba4fa84bfd5ac371b7d8699bd53c86756bd0f69895dd03bf4a7871ecbccab"
|
| 135 |
+
}
|
| 136 |
+
},
|
| 137 |
+
"models_included": false,
|
| 138 |
+
"private_voices_included": false,
|
| 139 |
+
"source_snapshot_included": false,
|
| 140 |
+
"license_download_sources": {
|
| 141 |
+
"ONNX-Runtime-MIT.txt": "https://raw.githubusercontent.com/microsoft/onnxruntime/main/LICENSE",
|
| 142 |
+
"ONNX-Runtime-ThirdPartyNotices.txt": "https://raw.githubusercontent.com/microsoft/onnxruntime/main/ThirdPartyNotices.txt",
|
| 143 |
+
"Unicode-license.txt": "https://www.unicode.org/license.txt",
|
| 144 |
+
"nlohmann-json-MIT.txt": "https://raw.githubusercontent.com/nlohmann/json/v3.12.0/LICENSE.MIT",
|
| 145 |
+
"KissFFT-BSD.txt": "https://raw.githubusercontent.com/mborgerding/kissfft/master/COPYING",
|
| 146 |
+
"LLVM-libomp.txt": "https://raw.githubusercontent.com/llvm/llvm-project/main/openmp/LICENSE.TXT",
|
| 147 |
+
"PCRE2-LICENCE.md": "https://raw.githubusercontent.com/PCRE2Project/pcre2/master/LICENCE.md",
|
| 148 |
+
"zlib.txt": "https://raw.githubusercontent.com/madler/zlib/master/LICENSE"
|
| 149 |
+
},
|
| 150 |
+
"verification": [
|
| 151 |
+
{
|
| 152 |
+
"exe": "cosyvoice-cli.exe",
|
| 153 |
+
"args": [
|
| 154 |
+
"--help"
|
| 155 |
+
],
|
| 156 |
+
"exit": 0,
|
| 157 |
+
"waited": true
|
| 158 |
+
},
|
| 159 |
+
{
|
| 160 |
+
"exe": "cosyvoice-server.exe",
|
| 161 |
+
"args": [
|
| 162 |
+
"--help"
|
| 163 |
+
],
|
| 164 |
+
"exit": 0,
|
| 165 |
+
"waited": true
|
| 166 |
+
},
|
| 167 |
+
{
|
| 168 |
+
"exe": "quantize.exe",
|
| 169 |
+
"args": [
|
| 170 |
+
"--help"
|
| 171 |
+
],
|
| 172 |
+
"exit": 0,
|
| 173 |
+
"waited": true
|
| 174 |
+
}
|
| 175 |
+
],
|
| 176 |
+
"new_model_inference": false
|
| 177 |
+
}
|
fixes-manifest-20261021-01.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"upstream_commit": "67d45feb2f654bbc66cca25bff78464b3ec08fa1",
|
| 3 |
+
"patch_sha256": "9cc2f8fe88f5704b39c80ea85a1ce67405ef01a8e3e400472c6fd045219db066",
|
| 4 |
+
"files": {
|
| 5 |
+
"CMakeLists.txt": {
|
| 6 |
+
"before": "1885afffb9eb73a9dd969f918ad1c9c8baec59e6b3d7e74c2c3b8b9047990abb",
|
| 7 |
+
"after": "8b39b9dd9c8c0ad84794cd3ba55e954ab61c1d78d98431a82c095dbb21b79c1c"
|
| 8 |
+
},
|
| 9 |
+
"include/cosyvoice.h": {
|
| 10 |
+
"before": "246b3337437a0d7de5f2bc18cba48449e2cf2a3f6050c81a3a90f3068bb1f176",
|
| 11 |
+
"after": "98ed457e186e9b807073bb18dece459477fe7900a7627f0636a7683cfe480681"
|
| 12 |
+
},
|
| 13 |
+
"src/cosyvoice-diagnostics.h": {
|
| 14 |
+
"before": null,
|
| 15 |
+
"after": "7d9b188f1f967aaa2d6fe5d4f0f0d31fea98847fbd99ad488763418dbcbc0922"
|
| 16 |
+
},
|
| 17 |
+
"src/cosyvoice-graph.cpp": {
|
| 18 |
+
"before": "5225031cde4af043e4a6378ed54f018c27bbc656f61535f36ca9e464f5e723bb",
|
| 19 |
+
"after": "96a5d60c52a8d57ac7c0255669f188145722465723bd91da8be52d00c3842e27"
|
| 20 |
+
},
|
| 21 |
+
"src/cosyvoice-internal.h": {
|
| 22 |
+
"before": "be9e76b81a3bc907e12d25c456653f6208cbeb71f9a442054e60be358bce3f3b",
|
| 23 |
+
"after": "942cb468b9c59d326499ed8816cebdabe10c6f929eea20a06fc344018d38ccbe"
|
| 24 |
+
},
|
| 25 |
+
"src/cosyvoice-kv-cache.cpp": {
|
| 26 |
+
"before": "9b649cfd3fa1e218d020605513c321decf05b1f81ae9b0903886339e7fb56914",
|
| 27 |
+
"after": "d1efc94e8b7a22099494e2f83f6a392c089358f2397ddbe259a86c80f6b73f63"
|
| 28 |
+
},
|
| 29 |
+
"src/cosyvoice-llm-job.cpp": {
|
| 30 |
+
"before": "2480ddc925ce1f715d600889943c056aa5fc3afecee98c11ba264aa4af8a1d94",
|
| 31 |
+
"after": "98abc97df7eac40380c0393e0f4d53caa6cbafd97440e135e0e8899035015891"
|
| 32 |
+
},
|
| 33 |
+
"src/cosyvoice-lora.cpp": {
|
| 34 |
+
"before": null,
|
| 35 |
+
"after": "184d469d00d737e45875b6a2589c65adde8e9efdc2effb459a0a5855df339b89"
|
| 36 |
+
},
|
| 37 |
+
"src/cosyvoice-model.cpp": {
|
| 38 |
+
"before": "cd6574ad2ee0ca4ea87e2f95a961c27c6cdee423ac592dfd62e51723886e56eb",
|
| 39 |
+
"after": "aa57c3d534291b1b09d4ab4156eb9822eeb179367707dcd694fee4b2c5133e8f"
|
| 40 |
+
},
|
| 41 |
+
"src/cosyvoice-model.h": {
|
| 42 |
+
"before": "da76f096aa7c4c7b010e00411712bad1285d9815bde6472deac80c7249942dce",
|
| 43 |
+
"after": "552a5d7f4dcb104f32082f3a490d91f538a57183360e3b183bd8c7482b2f07f1"
|
| 44 |
+
},
|
| 45 |
+
"src/cosyvoice-modules.h": {
|
| 46 |
+
"before": "6fe4b0df465d264f0deb337dd1d4cda1b66b3263158c68b6c3b7d56aee7b5760",
|
| 47 |
+
"after": "bc78f7a13ea1e20e88a97374f85493b4d8344a2720afe8a6d300fe58e77e3359"
|
| 48 |
+
},
|
| 49 |
+
"src/cosyvoice-streaming-handoff.h": {
|
| 50 |
+
"before": null,
|
| 51 |
+
"after": "c217f96d7bf81fe7d9a75c1438b53716c30358cd35441f6688fcea2d5e08a597"
|
| 52 |
+
},
|
| 53 |
+
"src/cosyvoice-token2wav.cpp": {
|
| 54 |
+
"before": "6d680401c5b632bd9caaca356df0aad82a8d016e532640315d147e548b98a8f3",
|
| 55 |
+
"after": "4bd02b8817b7e5a9935ebc9db126ee496c149c37a6775e17b71b4bf6da13a2cb"
|
| 56 |
+
},
|
| 57 |
+
"src/cosyvoice-tts.cpp": {
|
| 58 |
+
"before": "aba9680e115b09a16f6c5d9d26319e43768058b2df31e1a98c0f45eb9f2826e4",
|
| 59 |
+
"after": "f2ef5c92214721ce73b14d9be2d45c6db3d16e3d6f8cfdffdbf2a337693f74ff"
|
| 60 |
+
},
|
| 61 |
+
"src/cosyvoice.cpp": {
|
| 62 |
+
"before": "5f497e30aeb5a8a464695a002f3444c4d4c82c7c6456b8eadc3cb585d6ab4131",
|
| 63 |
+
"after": "d4e20eccba1e5a5d00098fb7d84b241d051204d84b8a68c55fb0e4be9b830056"
|
| 64 |
+
},
|
| 65 |
+
"tools/server/cosyvoice-server-backend.cpp": {
|
| 66 |
+
"before": "52f55e5178aff64093da9bc0df8b0fb2d8679ed5ec42df41eb40368863e9e43a",
|
| 67 |
+
"after": "ba95d80666ff8f7eccc62155812fac8f39b7e973de611a1e978717bb1ddde462"
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
}
|