From 1adf0356d8bc7a44cc7eab020107d0f3a357840a Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Sat, 12 Sep 2026 11:51:09 -0400 Subject: [PATCH 1/6] Add native Bark TTS model family --- CMakeLists.txt | 34 +++ README.md | 1 + docs/models/bark_tts.md | 27 +++ include/engine/models/bark_tts/assets.h | 22 ++ include/engine/models/bark_tts/codec.h | 26 +++ include/engine/models/bark_tts/generator.h | 31 +++ include/engine/models/bark_tts/session.h | 34 +++ include/engine/models/bark_tts/tokenizer.h | 20 ++ include/engine/models/bark_tts/transformer.h | 40 ++++ include/engine/models/bark_tts/types.h | 43 ++++ model_specs/bark_tts.json | 78 +++++++ src/models/bark_tts/assets.cpp | 71 ++++++ src/models/bark_tts/codec.cpp | 186 +++++++++++++++ src/models/bark_tts/generator.cpp | 154 ++++++++++++ src/models/bark_tts/session.cpp | 70 ++++++ src/models/bark_tts/tokenizer.cpp | 94 ++++++++ src/models/bark_tts/transformer.cpp | 234 +++++++++++++++++++ tests/bark_tts/bark_codec_parity.cpp | 40 ++++ tests/bark_tts/bark_transformer_parity.cpp | 51 ++++ tests/unittests/test_bark_tokenizer.cpp | 20 ++ tools/community_models/convert_bark.py | 144 ++++++++++++ 21 files changed, 1420 insertions(+) create mode 100644 docs/models/bark_tts.md create mode 100644 include/engine/models/bark_tts/assets.h create mode 100644 include/engine/models/bark_tts/codec.h create mode 100644 include/engine/models/bark_tts/generator.h create mode 100644 include/engine/models/bark_tts/session.h create mode 100644 include/engine/models/bark_tts/tokenizer.h create mode 100644 include/engine/models/bark_tts/transformer.h create mode 100644 include/engine/models/bark_tts/types.h create mode 100644 model_specs/bark_tts.json create mode 100644 src/models/bark_tts/assets.cpp create mode 100644 src/models/bark_tts/codec.cpp create mode 100644 src/models/bark_tts/generator.cpp create mode 100644 src/models/bark_tts/session.cpp create mode 100644 src/models/bark_tts/tokenizer.cpp create mode 100644 src/models/bark_tts/transformer.cpp create mode 100644 tests/bark_tts/bark_codec_parity.cpp create mode 100644 tests/bark_tts/bark_transformer_parity.cpp create mode 100644 tests/unittests/test_bark_tokenizer.cpp create mode 100644 tools/community_models/convert_bark.py diff --git a/CMakeLists.txt b/CMakeLists.txt index b0cedea3..0f069a85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1055,6 +1055,26 @@ audiocpp_add_model(magpie_tts engine::models::magpie_tts::make_magpie_tts_loader ) +audiocpp_add_model(bark_tts + SOURCES + src/models/bark_tts/assets.cpp + src/models/bark_tts/codec.cpp + src/models/bark_tts/generator.cpp + src/models/bark_tts/session.cpp + src/models/bark_tts/tokenizer.cpp + src/models/bark_tts/transformer.cpp + INCLUDES + engine/models/bark_tts/assets.h + engine/models/bark_tts/codec.h + engine/models/bark_tts/generator.h + engine/models/bark_tts/session.h + engine/models/bark_tts/tokenizer.h + engine/models/bark_tts/transformer.h + engine/models/bark_tts/types.h + LOADERS + engine::models::bark_tts::make_bark_tts_loader +) + audiocpp_add_model(confucius4_tts SOURCES src/models/confucius4_tts/assets.cpp @@ -2279,6 +2299,9 @@ if (ENGINE_BUILD_TESTS OR ENGINE_BUILD_EXTENDED_TESTS OR ENGINE_BUILD_MODEL_TEST add_engine_unittest(hf_tokenizer_vocab_bounds_test tests/unittests/test_hf_tokenizer_vocab_bounds.cpp) add_test(NAME hf_tokenizer_vocab_bounds_test COMMAND hf_tokenizer_vocab_bounds_test) + add_engine_unittest(bark_tokenizer_test tests/unittests/test_bark_tokenizer.cpp) + add_test(NAME bark_tokenizer_test COMMAND bark_tokenizer_test) + add_engine_unittest(safetensors_offsets_test tests/unittests/test_safetensors_offsets.cpp) add_test(NAME safetensors_offsets_test COMMAND safetensors_offsets_test) @@ -2716,6 +2739,17 @@ if (ENGINE_BUILD_TESTS OR ENGINE_BUILD_EXTENDED_TESTS OR ENGINE_BUILD_MODEL_TEST ) target_link_libraries(miocodec_wavlm_parity PRIVATE engine_runtime ggml) + add_executable(bark_codec_parity EXCLUDE_FROM_ALL tests/bark_tts/bark_codec_parity.cpp) + target_link_libraries(bark_codec_parity PRIVATE engine_runtime ggml) + if (ENGINE_ENABLE_OPENMP) + target_link_libraries(bark_codec_parity PRIVATE OpenMP::OpenMP_CXX) + endif() + add_executable(bark_transformer_parity EXCLUDE_FROM_ALL tests/bark_tts/bark_transformer_parity.cpp) + target_link_libraries(bark_transformer_parity PRIVATE engine_runtime ggml) + if (ENGINE_ENABLE_OPENMP) + target_link_libraries(bark_transformer_parity PRIVATE OpenMP::OpenMP_CXX) + endif() + add_engine_unittest(dots_tts_vocoder_parity tests/dots_tts/dots_tts_vocoder_parity.cpp) # Needs the GGUF and a PyTorch reference dump, so it is driven by hand diff --git a/README.md b/README.md index e6b80999..b2abd1b6 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ Runtime tags summarize the supported loading paths. GGUF package precision varie | **confucius4_tts** | Clone | zh, en, ja, ko, de, fr, es, id, it, th, pt, ru, ms, vi | Confucius4-TTS multilingual voice cloning | GGUF F32, Stream | | **cosyvoice3** | TTS, Clone | zh, en, ja, ko, de, es, fr, it, ru, yue | Fun-CosyVoice3 zero-shot, cross-lingual, and instruction-conditioned TTS | GGUF F32/Q8 | | **dots_tts** | TTS, Clone, Edit, Ctrl | multilingual | DotTTS SOAR, MeanFlow, and Edit | GGUF 16/Q8, Stream | +| **bark_tts** | TTS | 13 languages | Bark Small, 261 speaker presets | GGUF 16/Q8 | | **dramabox** | TTS, Clone | en | DramaBox expressive TTS and voice cloning | GGUF Q8 | | **fish_audio** | TTS, Clone, Ctrl | auto, en, zh | Fish Audio S2 Pro | GGUF 16/Q8 | | **firered_audio** | ASR, TTS, Clone, Design, Ctrl | zh, en | FireRedAudio multimodal speech/audio model with ASR, understanding, cloning, design, and edit paths | GGUF original/Q8 | diff --git a/docs/models/bark_tts.md b/docs/models/bark_tts.md new file mode 100644 index 00000000..58113e3e --- /dev/null +++ b/docs/models/bark_tts.md @@ -0,0 +1,27 @@ +# Bark Small + +Bark is a multilingual, expressive text-to-audio model. The audio.cpp package +contains the complete Bark Small semantic, coarse, fine, and EnCodec pipeline, +plus all 261 upstream speaker histories. + +```sh +python3 tools/model_manager_v2.py install bark_small_q8_0 + +audiocpp_cli --task tts --family bark_tts \ + --model Bark-Small-GGUF/bark-small-q8_0.gguf \ + --text "Hello from Bark. [laughs] This model can be quite expressive." \ + --option history_prompt=v2/en_speaker_6 --output bark.wav +``` + +Use `--option history_prompt=` to select another packaged history. Presets +follow upstream names such as `v2/de_speaker_3`, `v2/ja_speaker_0`, or +`announcer`. `temperature`, `top_k`, `max_tokens`, and `seed` are available as +request options. Bark supports non-speech cues written in brackets, although +the result remains probabilistic. + +The Q8 package quantizes transformer matrix weights. Embeddings, normalization +parameters, EnCodec codebooks, recurrent weights, and convolution weights are +kept at 16-bit precision to protect synthesis quality. + +Source model: [suno/bark-small](https://huggingface.co/suno/bark-small), pinned +to revision `1dbd7a128513b8ae4a4e2130fed57b7ac9da5bcd`. Bark is MIT licensed. diff --git a/include/engine/models/bark_tts/assets.h b/include/engine/models/bark_tts/assets.h new file mode 100644 index 00000000..c3905480 --- /dev/null +++ b/include/engine/models/bark_tts/assets.h @@ -0,0 +1,22 @@ +#pragma once + +#include "engine/framework/assets/resource_bundle.h" +#include "engine/framework/assets/tensor_source.h" +#include "engine/models/bark_tts/types.h" + +#include +#include +#include + +namespace engine::models::bark_tts { + +struct BarkAssets { + engine::assets::ResourceBundle resources; + std::shared_ptr weights; + BarkConfig config; + std::unordered_map presets; +}; + +std::shared_ptr load_bark_assets(const std::filesystem::path & model_path); + +} // namespace engine::models::bark_tts diff --git a/include/engine/models/bark_tts/codec.h b/include/engine/models/bark_tts/codec.h new file mode 100644 index 00000000..be524112 --- /dev/null +++ b/include/engine/models/bark_tts/codec.h @@ -0,0 +1,26 @@ +#pragma once + +#include "engine/framework/assets/tensor_source.h" + +#include +#include +#include + +namespace engine::core { class ExecutionContext; } +namespace engine::models::bark_tts { + +struct BarkAssets; + +class BarkCodecDecoder { +public: + BarkCodecDecoder(std::shared_ptr assets, + engine::core::ExecutionContext & execution, + engine::assets::TensorStorageType storage_type); + ~BarkCodecDecoder(); + std::vector decode(const std::vector> & codes) const; +private: + struct Impl; + std::unique_ptr impl_; +}; + +} // namespace engine::models::bark_tts diff --git a/include/engine/models/bark_tts/generator.h b/include/engine/models/bark_tts/generator.h new file mode 100644 index 00000000..e0e9f7ab --- /dev/null +++ b/include/engine/models/bark_tts/generator.h @@ -0,0 +1,31 @@ +#pragma once + +#include "engine/models/bark_tts/codec.h" +#include "engine/models/bark_tts/tokenizer.h" +#include "engine/models/bark_tts/transformer.h" +#include "engine/models/bark_tts/types.h" + +#include +#include +#include + +namespace engine::core { class ExecutionContext; } +namespace engine::models::bark_tts { + +class BarkGenerator { +public: + BarkGenerator(std::shared_ptr assets, + engine::core::ExecutionContext & execution, + engine::assets::TensorStorageType transformer_storage, + engine::assets::TensorStorageType codec_storage); + std::vector synthesize(const std::string & text, const BarkGenerationOptions & options) const; +private: + std::shared_ptr assets_; + BarkTokenizer tokenizer_; + BarkTransformer semantic_; + BarkTransformer coarse_; + BarkTransformer fine_; + BarkCodecDecoder codec_; +}; + +} // namespace engine::models::bark_tts diff --git a/include/engine/models/bark_tts/session.h b/include/engine/models/bark_tts/session.h new file mode 100644 index 00000000..0c8a10d3 --- /dev/null +++ b/include/engine/models/bark_tts/session.h @@ -0,0 +1,34 @@ +#pragma once + +#include "engine/framework/model_spec/metadata.h" +#include "engine/framework/runtime/session_base.h" +#include "engine/models/bark_tts/assets.h" + +#include + +namespace engine::core { class ExecutionContext; } +namespace engine::models::bark_tts { +class BarkGenerator; +std::shared_ptr make_bark_tts_loader(); + +class BarkSession final : public engine::runtime::RuntimeSessionBase, + public engine::runtime::IOfflineVoiceTaskSession { +public: + BarkSession(engine::runtime::TaskSpec task, engine::runtime::SessionOptions options, + std::shared_ptr assets, + std::shared_ptr contract); + ~BarkSession() override; + std::string family() const override; + engine::runtime::VoiceTaskKind task_kind() const override; + engine::runtime::RunMode run_mode() const override; + void prepare(const engine::runtime::SessionPreparationRequest & request) override; + engine::runtime::TaskResult run(const engine::runtime::TaskRequest & request) override; +private: + engine::runtime::TaskSpec task_; + engine::runtime::SessionOptions options_; + std::shared_ptr assets_; + std::shared_ptr contract_; + std::unique_ptr execution_; + std::unique_ptr generator_; +}; +} // namespace engine::models::bark_tts diff --git a/include/engine/models/bark_tts/tokenizer.h b/include/engine/models/bark_tts/tokenizer.h new file mode 100644 index 00000000..0b7efd10 --- /dev/null +++ b/include/engine/models/bark_tts/tokenizer.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include + +namespace engine::models::bark_tts { + +class BarkTokenizer { +public: + explicit BarkTokenizer(const std::string & tokenizer_json); + std::vector encode(const std::string & text) const; + +private: + std::unordered_map vocab_; + int32_t unknown_ = 100; +}; + +} // namespace engine::models::bark_tts diff --git a/include/engine/models/bark_tts/transformer.h b/include/engine/models/bark_tts/transformer.h new file mode 100644 index 00000000..19cece7a --- /dev/null +++ b/include/engine/models/bark_tts/transformer.h @@ -0,0 +1,40 @@ +#pragma once + +#include "engine/framework/assets/tensor_source.h" +#include "engine/models/bark_tts/assets.h" + +#include +#include +#include +#include +#include + +namespace engine::core { class ExecutionContext; } + +namespace engine::models::bark_tts { + +class BarkTransformer { +public: + BarkTransformer(std::shared_ptr assets, + engine::core::ExecutionContext & execution, + std::string prefix, + BarkTransformerConfig config, + bool causal, + engine::assets::TensorStorageType storage_type); + ~BarkTransformer(); + + // For causal models, embedding_channels may contain one or more token rows + // whose embeddings are summed. Returns logits for the final position. + std::vector causal_logits(const std::vector> & embedding_channels) const; + + // Fine Bark sums codebook embeddings 0..codebook_idx and returns one logit + // row per sequence position. + std::vector fine_logits(const std::vector> & codes, + int codebook_idx) const; + +private: + struct Impl; + std::unique_ptr impl_; +}; + +} // namespace engine::models::bark_tts diff --git a/include/engine/models/bark_tts/types.h b/include/engine/models/bark_tts/types.h new file mode 100644 index 00000000..a83d65a9 --- /dev/null +++ b/include/engine/models/bark_tts/types.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include + +namespace engine::models::bark_tts { + +struct BarkTransformerConfig { + int64_t hidden = 768; + int64_t layers = 12; + int64_t heads = 12; + int64_t block_size = 1024; + int64_t input_vocab = 0; + int64_t output_vocab = 0; + bool bias = false; +}; + +struct BarkConfig { + BarkTransformerConfig semantic; + BarkTransformerConfig coarse; + BarkTransformerConfig fine; + int64_t codebook_size = 1024; + int64_t codebook_dim = 128; + int64_t sample_rate = 24000; +}; + +struct BarkSpeakerPreset { + std::vector semantic; + std::vector> coarse; + std::vector> fine; +}; + +struct BarkGenerationOptions { + std::string voice_id = "v2/en_speaker_6"; + float temperature = 0.7F; + int top_k = 50; + int64_t max_tokens = 768; + uint64_t seed = 0; +}; + +} // namespace engine::models::bark_tts diff --git a/model_specs/bark_tts.json b/model_specs/bark_tts.json new file mode 100644 index 00000000..21bb7149 --- /dev/null +++ b/model_specs/bark_tts.json @@ -0,0 +1,78 @@ +{ + "schema_version": 1, + "family": "bark_tts", + "display_name": "Bark Small", + "description": "Suno Bark is a multilingual text-to-audio model with semantic, coarse-acoustic, and fine-acoustic generation followed by a 24 kHz EnCodec decoder. This package includes Bark Small and its downloaded built-in speaker presets.", + "category": "tts", + "status": "supported", + "tasks": ["tts"], + "modes": ["offline"], + "languages": ["de", "en", "es", "fr", "hi", "it", "ja", "ko", "pl", "pt", "ru", "tr", "zh"], + "runtime": {"tags": ["gguf"]}, + "capabilities": {"tts": ["built_in_voices"]}, + "options": { + "request": [ + {"name": "history_prompt", "type": "string", "description": "Packaged Bark speaker history, for example v2/en_speaker_6.", "required": false, "default": "v2/en_speaker_6"}, + {"name": "temperature", "type": "float", "description": "Semantic and coarse token sampling temperature.", "required": false, "min": 0.01, "default": 0.7}, + {"name": "top_k", "type": "int", "description": "Semantic and coarse token top-k sampling limit.", "required": false, "min": 1, "default": 50}, + {"name": "max_tokens", "type": "int", "description": "Maximum generated semantic token count.", "required": false, "min": 1, "default": 768}, + {"name": "seed", "type": "int", "description": "Sampling seed.", "required": false, "min": 0, "default": 0} + ], + "session": [ + {"name": "weight_type", "type": "enum", "description": "Transformer matmul storage type.", "preset": "weight_type_full", "required": false, "default": "native"}, + {"name": "codec_weight_type", "type": "enum", "description": "EnCodec decoder storage type; native is recommended for audio quality.", "preset": "weight_type_full", "required": false, "default": "native"}, + {"name": "graph_arena_mb", "type": "int", "description": "Reusable graph arena size in MiB.", "required": false, "min": 64, "default": 768} + ], + "load": [] + }, + "package_defaults": { + "download": {"kind": "huggingface_snapshot", "repo": "audio-cpp/audio.cpp-gguf", "revision": "main", "gated": false} + }, + "packages": [ + { + "id": "bark_small_f16", + "display_name": "Bark Small F16 GGUF", + "default": false, + "format": "gguf", + "precision": "f16", + "target_directory": "Bark-Small-GGUF", + "files": ["Bark-Small-GGUF/bark-small-f16.gguf"], + "strip_prefix": "Bark-Small-GGUF" + }, + { + "id": "bark_small_q8_0", + "display_name": "Bark Small Q8_0 GGUF", + "default": true, + "format": "gguf", + "precision": "q8_0", + "target_directory": "Bark-Small-GGUF", + "files": ["Bark-Small-GGUF/bark-small-q8_0.gguf"], + "strip_prefix": "Bark-Small-GGUF" + } + ], + "dependencies": [], + "ui": { + "recommended_package": "bark_small_q8_0", + "tags": ["TTS", "GGUF"], + "docs": ["docs/models/bark_tts.md", "docs/tts.md", "docs/gguf.md"] + }, + "sources": [ + { + "format": "gguf", + "roots": {"model": ".", "weights": "$gguf"}, + "files": { + "config_json": "model:config.json", + "generation_config_json": "model:generation_config.json", + "tokenizer_json": "model:tokenizer.json", + "speaker_presets_json": "model:speaker_presets.json" + }, + "tensors": {"weights": {"source": "weights:", "prefix": "bark"}} + } + ], + "provenance": { + "model": "suno/bark-small", + "model_revision": "1dbd7a128513b8ae4a4e2130fed57b7ac9da5bcd", + "license": "MIT", + "upstream": "https://github.com/suno-ai/bark" + } +} diff --git a/src/models/bark_tts/assets.cpp b/src/models/bark_tts/assets.cpp new file mode 100644 index 00000000..79bf8213 --- /dev/null +++ b/src/models/bark_tts/assets.cpp @@ -0,0 +1,71 @@ +#include "engine/models/bark_tts/assets.h" + +#include "engine/framework/io/json.h" +#include "engine/framework/model_spec/package.h" + +#include + +namespace engine::models::bark_tts { +namespace { + +BarkTransformerConfig transformer_config(const engine::io::json::Value & value) { + BarkTransformerConfig out; + out.hidden = engine::io::json::require_i64(value, "hidden_size"); + out.layers = engine::io::json::require_i64(value, "num_layers"); + out.heads = engine::io::json::require_i64(value, "num_heads"); + out.block_size = engine::io::json::require_i64(value, "block_size"); + out.input_vocab = engine::io::json::require_i64(value, "input_vocab_size"); + out.output_vocab = engine::io::json::require_i64(value, "output_vocab_size"); + out.bias = engine::io::json::optional_bool(value, "bias", false); + return out; +} + +std::vector> matrix_i32(const engine::io::json::Value & value) { + std::vector> out; + for (const auto & row : value.as_array()) out.push_back(engine::io::json::number_array_as(row)); + return out; +} + +void validate(const BarkAssets & assets) { + if (assets.config.semantic.hidden != 768 || assets.config.coarse.hidden != 768 || + assets.config.fine.hidden != 768 || assets.config.semantic.heads != 12 || + assets.config.sample_rate != 24000 || assets.config.codebook_size != 1024) { + throw std::runtime_error("unsupported Bark architecture; this runtime currently targets suno/bark-small"); + } + engine::assets::require_tensor_shape(*assets.weights, "semantic.input_embeds_layer.weight", + {assets.config.semantic.input_vocab, assets.config.semantic.hidden}); + engine::assets::require_tensor_shape(*assets.weights, "coarse_acoustics.input_embeds_layer.weight", + {assets.config.coarse.input_vocab, assets.config.coarse.hidden}); + engine::assets::require_tensor_shape(*assets.weights, "fine_acoustics.input_embeds_layers.0.weight", + {assets.config.fine.input_vocab, assets.config.fine.hidden}); + engine::assets::require_tensor_shape(*assets.weights, "codec_model.quantizer.layers.0.codebook.embed", + {assets.config.codebook_size, assets.config.codebook_dim}); +} + +} // namespace + +std::shared_ptr load_bark_assets(const std::filesystem::path & model_path) { + auto assets = std::make_shared(); + assets->resources = engine::model_spec::load_resource_bundle_for_family(model_path, "bark_tts"); + const auto config = assets->resources.parse_json("config_json"); + assets->config.semantic = transformer_config(config.require("semantic_config")); + assets->config.coarse = transformer_config(config.require("coarse_acoustics_config")); + assets->config.fine = transformer_config(config.require("fine_acoustics_config")); + const auto & codec = config.require("codec_config"); + assets->config.codebook_size = engine::io::json::require_i64(codec, "codebook_size"); + assets->config.codebook_dim = engine::io::json::require_i64(codec, "codebook_dim"); + assets->config.sample_rate = engine::io::json::require_i64(codec, "sampling_rate"); + assets->weights = assets->resources.open_tensor_source("weights"); + const auto preset_root = assets->resources.parse_json("speaker_presets_json").require("presets"); + for (const auto & [name, value] : preset_root.as_object()) { + BarkSpeakerPreset preset; + preset.semantic = engine::io::json::number_array_as(value.require("semantic")); + preset.coarse = matrix_i32(value.require("coarse")); + preset.fine = matrix_i32(value.require("fine")); + assets->presets.emplace(name, std::move(preset)); + } + validate(*assets); + return assets; +} + +} // namespace engine::models::bark_tts diff --git a/src/models/bark_tts/codec.cpp b/src/models/bark_tts/codec.cpp new file mode 100644 index 00000000..fd3c797c --- /dev/null +++ b/src/models/bark_tts/codec.cpp @@ -0,0 +1,186 @@ +#include "engine/models/bark_tts/codec.h" + +#include "engine/framework/core/backend.h" +#include "engine/framework/core/backend_weight_store.h" +#include "engine/framework/core/execution_context.h" +#include "engine/framework/modules/activation_modules.h" +#include "engine/framework/modules/conv_modules.h" +#include "engine/framework/modules/lookup_modules.h" +#include "engine/framework/modules/primitive_modules.h" +#include "engine/framework/modules/recurrent_modules.h" +#include "engine/framework/modules/structural_modules.h" +#include "engine/framework/modules/weight_binding.h" +#include "engine/models/bark_tts/assets.h" + +#include +#include + +#include +#include + +namespace engine::models::bark_tts { +namespace { + +struct Conv { engine::modules::Conv1dWeights weights; int64_t in, out, kernel, dilation; }; +struct Up { engine::modules::ConvTranspose1dWeights weights; int64_t in, out, kernel, stride; }; +struct Residual { Conv first; Conv second; Conv shortcut; }; +struct CodecWeights { + std::shared_ptr store; + std::vector codebooks; + Conv initial; + engine::modules::LSTMStackWeights lstm; + std::array ups; + std::array residuals; + Conv final; +}; +struct ContextDelete { void operator()(ggml_context * p) const noexcept { if (p) ggml_free(p); } }; + +Conv load_conv(engine::core::BackendWeightStore & store, const engine::assets::TensorSource & source, + const std::string & prefix, int64_t in, int64_t out, int64_t kernel, int64_t dilation, + engine::assets::TensorStorageType storage) { + Conv result{{}, in, out, kernel, dilation}; + result.weights = engine::modules::binding::conv1d_from_source(store, source, prefix, storage, + out, in, kernel, true); + return result; +} + +Up load_up(engine::core::BackendWeightStore & store, const engine::assets::TensorSource & source, + const std::string & prefix, int64_t in, int64_t out, int64_t kernel, int64_t stride, + engine::assets::TensorStorageType storage) { + Up result{{}, in, out, kernel, stride}; + result.weights = engine::modules::binding::conv_transpose1d_from_source(store, source, prefix, storage, + in, out, kernel, true); + return result; +} + +std::shared_ptr load(const BarkAssets & assets, ggml_backend_t backend, engine::core::BackendType backend_type, + engine::assets::TensorStorageType storage) { + auto out = std::make_shared(); + out->store = std::make_shared(backend, backend_type, + "bark.codec.weights", 384ULL * 1024ULL * 1024ULL); + const auto & source = *assets.weights; + for (int i = 0; i < 8; ++i) out->codebooks.push_back(out->store->load_tensor(source, + "codec_model.quantizer.layers." + std::to_string(i) + ".codebook.embed", storage, {1024, 128})); + out->initial = load_conv(*out->store, source, "codec_model.decoder.layers.0.conv", 128, 512, 7, 1, storage); + for (int i = 0; i < 2; ++i) { + const std::string p = "codec_model.decoder.layers.1.lstm."; + engine::modules::LSTMCellWeights cell; + cell.weight_ih = out->store->load_tensor(source, p + "weight_ih_l" + std::to_string(i), storage, {2048, 512}); + cell.weight_hh = out->store->load_tensor(source, p + "weight_hh_l" + std::to_string(i), storage, {2048, 512}); + cell.bias_ih = out->store->load_tensor(source, p + "bias_ih_l" + std::to_string(i), engine::assets::TensorStorageType::F32, {2048}); + cell.bias_hh = out->store->load_tensor(source, p + "bias_hh_l" + std::to_string(i), engine::assets::TensorStorageType::F32, {2048}); + out->lstm.layers.push_back(std::move(cell)); + } + const int layer[] = {3, 6, 9, 12}; + const int in[] = {512, 256, 128, 64}; + const int channel[] = {256, 128, 64, 32}; + const int ratio[] = {8, 5, 4, 2}; + const int residual_layer[] = {4, 7, 10, 13}; + for (int i = 0; i < 4; ++i) { + const auto base = "codec_model.decoder.layers." + std::to_string(layer[i]) + ".conv"; + out->ups[static_cast(i)] = load_up(*out->store, source, base, in[i], channel[i], ratio[i] * 2, ratio[i], storage); + const auto r = "codec_model.decoder.layers." + std::to_string(residual_layer[i]); + out->residuals[static_cast(i)] = { + load_conv(*out->store, source, r + ".block.1.conv", channel[i], channel[i] / 2, 3, 1, storage), + load_conv(*out->store, source, r + ".block.3.conv", channel[i] / 2, channel[i], 1, 1, storage), + load_conv(*out->store, source, r + ".shortcut.conv", channel[i], channel[i], 1, 1, storage), + }; + } + out->final = load_conv(*out->store, source, "codec_model.decoder.layers.15.conv", 32, 1, 7, 1, storage); + out->store->upload(); + return out; +} + +engine::core::TensorValue causal_conv(engine::core::ModuleBuildContext & ctx, + const engine::core::TensorValue & input, const Conv & conv) { + const int64_t effective = (conv.kernel - 1) * conv.dilation + 1; + const auto contiguous = engine::core::ensure_backend_addressable_layout(ctx, input); + auto padded = engine::modules::ReflectPad1dModule({effective - 1, 0}).build(ctx, contiguous); + return engine::modules::Conv1dModule({conv.in, conv.out, conv.kernel, 1, 0, + static_cast(conv.dilation), true}).build(ctx, padded, conv.weights); +} + +engine::core::TensorValue upsample(engine::core::ModuleBuildContext & ctx, + const engine::core::TensorValue & input, const Up & up) { + auto full = engine::modules::ConvTranspose1dModule({up.in, up.out, up.kernel, + static_cast(up.stride), 0, 1, true}).build(ctx, input, up.weights); + const int64_t frames = full.shape.dims[2] - (up.kernel - up.stride); + return engine::modules::SliceModule({2, 0, frames}).build(ctx, full); +} + +engine::core::TensorValue elu(engine::core::ModuleBuildContext & ctx, const engine::core::TensorValue & input) { + return engine::modules::EluModule{}.build(ctx, input); +} + +engine::core::TensorValue residual(engine::core::ModuleBuildContext & ctx, + const engine::core::TensorValue & input, const Residual & weights) { + auto hidden = causal_conv(ctx, elu(ctx, input), weights.first); + hidden = causal_conv(ctx, elu(ctx, hidden), weights.second); + return engine::modules::AddModule{}.build(ctx, causal_conv(ctx, input, weights.shortcut), hidden); +} + +} // namespace + +struct BarkCodecDecoder::Impl { + std::shared_ptr assets; + engine::core::ExecutionContext & execution; + std::shared_ptr weights; +}; + +BarkCodecDecoder::BarkCodecDecoder(std::shared_ptr assets, + engine::core::ExecutionContext & execution, + engine::assets::TensorStorageType storage) + : impl_(std::make_unique(Impl{assets, execution, load(*assets, execution.backend(), execution.backend_type(), storage)})) {} +BarkCodecDecoder::~BarkCodecDecoder() = default; + +std::vector BarkCodecDecoder::decode(const std::vector> & codes) const { + if (codes.size() != 8 || codes.front().empty()) throw std::runtime_error("Bark codec requires 8 non-empty codebooks"); + const int64_t frames = static_cast(codes.front().size()); + for (const auto & row : codes) if (static_cast(row.size()) != frames) + throw std::runtime_error("Bark codec codebook lengths differ"); + std::unique_ptr context(ggml_init({256ULL * 1024ULL * 1024ULL, nullptr, true})); + engine::core::ModuleBuildContext build{context.get(), "bark.codec", impl_->execution.backend_type()}; + engine::core::TensorValue quantized; + std::vector inputs; + for (size_t i = 0; i < 8; ++i) { + auto * raw = ggml_new_tensor_1d(context.get(), GGML_TYPE_I32, frames); + inputs.push_back(raw); + auto ids = engine::core::wrap_tensor(raw, engine::core::TensorShape::from_dims({frames}), GGML_TYPE_I32); + auto value = engine::modules::EmbeddingModule({1024, 128}).build(build, ids, impl_->weights->codebooks[i]); + value = engine::core::reshape_tensor(build, value, engine::core::TensorShape::from_dims({1, frames, 128})); + value = engine::modules::TransposeModule({{0, 2, 1, 3}, 3}).build(build, value); + quantized = quantized.valid() ? engine::modules::AddModule{}.build(build, quantized, value) : value; + } + auto hidden = causal_conv(build, quantized, impl_->weights->initial); + hidden = engine::modules::TransposeModule({{0, 2, 1, 3}, 3}).build(build, hidden); + hidden = engine::core::ensure_backend_addressable_layout(build, hidden); + hidden = engine::core::reshape_tensor(build, hidden, engine::core::TensorShape::from_dims({frames, 512})); + const auto lstm_residual = hidden; + for (const auto & cell : impl_->weights->lstm.layers) { + auto zero = engine::core::wrap_tensor(ggml_scale(context.get(), engine::modules::SliceModule({0, 0, 1}).build(build, hidden).tensor, 0.0F), + engine::core::TensorShape::from_dims({1, 512}), GGML_TYPE_F32); + hidden = engine::modules::LSTMSequenceModule({512, 512, false}).build(build, hidden, zero, zero, {cell}).sequence; + } + hidden = engine::modules::AddModule{}.build(build, hidden, lstm_residual); + hidden = engine::core::reshape_tensor(build, hidden, engine::core::TensorShape::from_dims({1, frames, 512})); + hidden = engine::modules::TransposeModule({{0, 2, 1, 3}, 3}).build(build, hidden); + for (size_t i = 0; i < 4; ++i) hidden = residual(build, upsample(build, elu(build, hidden), impl_->weights->ups[i]), impl_->weights->residuals[i]); + auto waveform = causal_conv(build, elu(build, hidden), impl_->weights->final); + ggml_set_output(waveform.tensor); + auto * graph = ggml_new_graph_custom(context.get(), 65536, false); + ggml_build_forward_expand(graph, waveform.tensor); + auto * buffer = ggml_backend_alloc_ctx_tensors(context.get(), impl_->execution.backend()); + if (!buffer) throw std::runtime_error("failed to allocate Bark codec graph"); + for (size_t i = 0; i < 8; ++i) ggml_backend_tensor_set(inputs[i], codes[i].data(), 0, codes[i].size() * sizeof(int32_t)); + engine::core::set_backend_threads(impl_->execution.backend(), impl_->execution.config().threads); + const auto status = engine::core::compute_backend_graph(impl_->execution.backend(), graph); + ggml_backend_synchronize(impl_->execution.backend()); + if (status != GGML_STATUS_SUCCESS) throw std::runtime_error("Bark codec graph compute failed"); + std::vector out(static_cast(waveform.shape.dims[2])); + ggml_backend_tensor_get(waveform.tensor, out.data(), 0, out.size() * sizeof(float)); + engine::core::release_backend_graph_resources(impl_->execution.backend(), graph); + ggml_backend_buffer_free(buffer); + return out; +} + +} // namespace engine::models::bark_tts diff --git a/src/models/bark_tts/generator.cpp b/src/models/bark_tts/generator.cpp new file mode 100644 index 00000000..1e2f70c0 --- /dev/null +++ b/src/models/bark_tts/generator.cpp @@ -0,0 +1,154 @@ +#include "engine/models/bark_tts/generator.h" + +#include +#include +#include +#include +#include +#include + +namespace engine::models::bark_tts { +namespace { + +int32_t sample(std::vector logits, int begin, int end, int top_k, float temperature, std::mt19937_64 & rng) { + if (begin < 0 || end > static_cast(logits.size()) || begin >= end) throw std::runtime_error("invalid Bark sampling range"); + std::vector indices(static_cast(end - begin)); + std::iota(indices.begin(), indices.end(), begin); + const int keep = std::min(top_k, static_cast(indices.size())); + std::partial_sort(indices.begin(), indices.begin() + keep, indices.end(), [&](int32_t a, int32_t b) { return logits[a] > logits[b]; }); + indices.resize(static_cast(keep)); + const float maximum = logits[indices.front()] / temperature; + std::vector weights; + weights.reserve(indices.size()); + for (int32_t id : indices) weights.push_back(std::exp(static_cast(logits[id] / temperature - maximum))); + return indices[std::discrete_distribution(weights.begin(), weights.end())(rng)]; +} + +std::vector semantic_tokens(BarkTransformer & model, const BarkTokenizer & tokenizer, + const BarkSpeakerPreset & preset, const std::string & text, + const BarkGenerationOptions & options, std::mt19937_64 & rng) { + auto text_ids = tokenizer.encode(text); + if (text_ids.size() > 256) text_ids.resize(256); + for (auto & id : text_ids) id += 10048; + text_ids.resize(256, 129595); + std::vector history; + const size_t history_start = preset.semantic.size() > 256 ? preset.semantic.size() - 256 : 0; + history.assign(preset.semantic.begin() + static_cast(history_start), preset.semantic.end()); + history.resize(256, 10000); + text_ids.push_back(129599); + history.push_back(10000); + std::vector generated; + for (int64_t step = 0; step < options.max_tokens && text_ids.size() < 1024; ++step) { + auto logits = model.causal_logits({text_ids, history}); + const int32_t id = sample(std::move(logits), 0, 10001, options.top_k, options.temperature, rng); + if (id == 10000) break; + generated.push_back(id); + text_ids.push_back(id); + history.push_back(-1); + } + if (generated.empty()) throw std::runtime_error("Bark semantic model generated no speech tokens"); + return generated; +} + +std::vector coarse_tokens(BarkTransformer & model, const BarkSpeakerPreset & preset, + const std::vector & semantic, const BarkGenerationOptions & options, + std::mt19937_64 & rng) { + constexpr double ratio = 75.0 / 49.9 * 2.0; + std::vector sem_history = preset.semantic; + std::vector coarse_history; + if (preset.coarse.size() != 2) throw std::runtime_error("Bark preset coarse history must have two codebooks"); + for (size_t frame = 0; frame < preset.coarse[0].size(); ++frame) + for (int book = 0; book < 2; ++book) coarse_history.push_back(preset.coarse[book][frame] + book * 1024 + 10000); + const int max_sem_history = static_cast(std::floor(630.0 / ratio)); + int sem_count = std::min({max_sem_history, static_cast(sem_history.size() / 2 * 2), + static_cast(std::floor(coarse_history.size() / ratio))}); + int coarse_count = static_cast(std::round(sem_count * ratio)); + if (sem_count > 0) sem_history.erase(sem_history.begin(), sem_history.end() - sem_count); + if (coarse_count > 0) coarse_history.erase(coarse_history.begin(), coarse_history.end() - coarse_count); + if (coarse_history.size() >= 2) coarse_history.resize(coarse_history.size() - 2); + const size_t original_history = coarse_history.size(); + std::vector all_semantic = sem_history; + all_semantic.insert(all_semantic.end(), semantic.begin(), semantic.end()); + const int generated_length = static_cast(std::round(std::floor(semantic.size() * ratio / 2.0) * 2.0)); + for (int total = 0; total < generated_length;) { + const int semantic_index = sem_count + static_cast(std::round(total / ratio)); + const int begin = std::max(0, semantic_index - max_sem_history); + std::vector input(all_semantic.begin() + begin, all_semantic.end()); + if (input.size() > 256) input.resize(256); + input.resize(256, 12048); + input.push_back(12050); + const size_t take = std::min(630, coarse_history.size()); + input.insert(input.end(), coarse_history.end() - static_cast(take), coarse_history.end()); + const int window = std::min(60, generated_length - total); + for (int i = 0; i < window; ++i) { + auto logits = model.causal_logits({input}); + const int book = total % 2; + const int32_t id = sample(std::move(logits), 10000 + book * 1024, + 10000 + (book + 1) * 1024, options.top_k, options.temperature, rng); + input.push_back(id); + coarse_history.push_back(id); + ++total; + } + } + return {coarse_history.begin() + static_cast(original_history), coarse_history.end()}; +} + +std::vector> fine_tokens(BarkTransformer & model, const BarkSpeakerPreset & preset, + const std::vector & coarse) { + const int64_t frames = static_cast(coarse.size() / 2); + std::vector> values(8); + for (int64_t frame = 0; frame < frames; ++frame) for (int book = 0; book < 2; ++book) + values[book].push_back((coarse[static_cast(frame * 2 + book)] - 10000) % 1024); + const int64_t history = std::min(512, preset.fine.empty() ? 0 : preset.fine.front().size()); + std::vector> input(8); + for (int book = 0; book < 8; ++book) { + if (history) input[book].insert(input[book].end(), preset.fine[book].end() - history, preset.fine[book].end()); + if (book < 2) input[book].insert(input[book].end(), values[book].begin(), values[book].end()); + else input[book].resize(static_cast(history + frames), 1024); + } + const int64_t padded = std::max(1024, history + frames); + for (auto & row : input) row.resize(static_cast(padded), 1024); + const int loops = std::max(0, static_cast(std::ceil((frames - (1024 - history)) / 512.0))) + 1; + for (int outer = 0; outer < loops; ++outer) { + const int64_t start = std::min(outer * 512, padded - 1024); + const int64_t fill = std::min(history + outer * 512, padded - 512); + const int64_t relative = fill - start; + std::vector> window(8); + for (int book = 0; book < 8; ++book) window[book].assign(input[book].begin() + start, input[book].begin() + start + 1024); + for (int book = 2; book < 8; ++book) { + const auto logits = model.fine_logits(window, book); + for (int64_t position = relative; position < 1024; ++position) { + const auto begin = logits.begin() + position * 1056; + window[book][static_cast(position)] = static_cast(std::distance(begin, + std::max_element(begin, begin + 1024))); + } + } + for (int book = 2; book < 8; ++book) + std::copy(window[book].begin() + relative, window[book].end(), input[book].begin() + fill); + } + for (int book = 0; book < 8; ++book) + values[book].assign(input[book].begin() + history, input[book].begin() + history + frames); + return values; +} + +} // namespace + +BarkGenerator::BarkGenerator(std::shared_ptr assets, engine::core::ExecutionContext & execution, + engine::assets::TensorStorageType transformer_storage, + engine::assets::TensorStorageType codec_storage) + : assets_(std::move(assets)), tokenizer_(assets_->resources.read_text("tokenizer_json")), + semantic_(assets_, execution, "semantic", assets_->config.semantic, true, transformer_storage), + coarse_(assets_, execution, "coarse_acoustics", assets_->config.coarse, true, transformer_storage), + fine_(assets_, execution, "fine_acoustics", assets_->config.fine, false, transformer_storage), + codec_(assets_, execution, codec_storage) {} + +std::vector BarkGenerator::synthesize(const std::string & text, const BarkGenerationOptions & options) const { + const auto found = assets_->presets.find(options.voice_id); + if (found == assets_->presets.end()) throw std::runtime_error("unknown Bark voice_id: " + options.voice_id); + std::mt19937_64 rng(options.seed); + auto semantic = semantic_tokens(const_cast(semantic_), tokenizer_, found->second, text, options, rng); + auto coarse = coarse_tokens(const_cast(coarse_), found->second, semantic, options, rng); + return codec_.decode(fine_tokens(const_cast(fine_), found->second, coarse)); +} + +} // namespace engine::models::bark_tts diff --git a/src/models/bark_tts/session.cpp b/src/models/bark_tts/session.cpp new file mode 100644 index 00000000..87bbd4ff --- /dev/null +++ b/src/models/bark_tts/session.cpp @@ -0,0 +1,70 @@ +#include "engine/models/bark_tts/session.h" + +#include "engine/framework/runtime/options.h" +#include "engine/framework/runtime/spec_backed_model.h" +#include "engine/models/bark_tts/generator.h" + +#include +#include + +namespace engine::models::bark_tts { +namespace { +constexpr const char * kFamily = "bark_tts"; + +engine::assets::TensorStorageType storage(const engine::runtime::SessionOptions & options, const char * name) { + return engine::runtime::parse_tensor_storage_option(options.options, name, + engine::assets::TensorStorageType::Native, {engine::assets::TensorStorageType::Native, + engine::assets::TensorStorageType::F32, engine::assets::TensorStorageType::F16, + engine::assets::TensorStorageType::BF16, engine::assets::TensorStorageType::Q8_0}); +} + +std::unique_ptr create(const engine::runtime::TaskSpec & task, + const engine::runtime::SessionOptions & options, std::shared_ptr assets, + std::shared_ptr contract) { + return std::make_unique(task, options, std::move(assets), std::move(contract)); +} +} // namespace + +BarkSession::BarkSession(engine::runtime::TaskSpec task, engine::runtime::SessionOptions options, + std::shared_ptr assets, + std::shared_ptr contract) + : RuntimeSessionBase(options), task_(task), options_(std::move(options)), assets_(std::move(assets)), + contract_(std::move(contract)) { + if (!assets_ || !contract_) throw std::runtime_error("Bark session requires assets and model contract"); + engine::runtime::validate_spec_backed_session_options(options_, *contract_, kFamily, "Bark"); + if (task_.task != engine::runtime::VoiceTaskKind::Tts || task_.mode != engine::runtime::RunMode::Offline) + throw std::runtime_error("Bark supports offline TTS"); + execution_ = std::make_unique(options_.backend); + generator_ = std::make_unique(assets_, *execution_, storage(options_, "bark_tts.weight_type"), + storage(options_, "bark_tts.codec_weight_type")); +} +BarkSession::~BarkSession() = default; +std::string BarkSession::family() const { return kFamily; } +engine::runtime::VoiceTaskKind BarkSession::task_kind() const { return task_.task; } +engine::runtime::RunMode BarkSession::run_mode() const { return task_.mode; } +void BarkSession::prepare(const engine::runtime::SessionPreparationRequest & request) { + engine::runtime::validate_spec_backed_request_options(request.options, *contract_, "Bark"); mark_prepared(); +} +engine::runtime::TaskResult BarkSession::run(const engine::runtime::TaskRequest & request) { + require_prepared("Bark run"); + engine::runtime::validate_spec_backed_request_options(request.options, *contract_, "Bark"); + if (!request.text_input.has_value() || request.text_input->text.empty()) throw std::runtime_error("Bark requires text_input"); + BarkGenerationOptions generation; + generation.voice_id = engine::runtime::find_option(request.options, {"history_prompt"}).value_or(generation.voice_id); + generation.temperature = engine::runtime::parse_float_option(request.options, {"temperature"}).value_or(generation.temperature); + generation.top_k = engine::runtime::parse_int_option(request.options, {"top_k"}).value_or(generation.top_k); + generation.max_tokens = engine::runtime::parse_i64_option(request.options, {"max_tokens"}).value_or(generation.max_tokens); + generation.seed = engine::runtime::parse_u64_option(request.options, {"seed"}).value_or(generation.seed); + if (!(generation.temperature > 0.0F) || !std::isfinite(generation.temperature) || generation.top_k <= 0 || generation.max_tokens <= 0) + throw std::runtime_error("invalid Bark generation options"); + engine::runtime::TaskResult result; + result.audio_output = engine::runtime::AudioBuffer{24000, 1, generator_->synthesize(request.text_input->text, generation)}; + return result; +} + +std::shared_ptr make_bark_tts_loader() { + engine::runtime::SpecBackedVoiceModelConfig config; + config.family = kFamily; config.load_assets = load_bark_assets; config.create_session = create; + return engine::runtime::make_spec_backed_voice_loader(std::move(config)); +} +} // namespace engine::models::bark_tts diff --git a/src/models/bark_tts/tokenizer.cpp b/src/models/bark_tts/tokenizer.cpp new file mode 100644 index 00000000..8dd3e1e6 --- /dev/null +++ b/src/models/bark_tts/tokenizer.cpp @@ -0,0 +1,94 @@ +#include "engine/models/bark_tts/tokenizer.h" + +#include "engine/framework/io/json.h" + +#include +#include + +namespace engine::models::bark_tts { +namespace { + +struct Character { uint32_t codepoint; std::string bytes; }; + +std::vector characters(const std::string & text) { + std::vector out; + for (size_t pos = 0; pos < text.size();) { + const auto first = static_cast(text[pos]); + size_t width = 1; + uint32_t value = first; + if ((first & 0xe0U) == 0xc0U) { width = 2; value = first & 0x1fU; } + else if ((first & 0xf0U) == 0xe0U) { width = 3; value = first & 0x0fU; } + else if ((first & 0xf8U) == 0xf0U) { width = 4; value = first & 0x07U; } + if (pos + width > text.size()) throw std::runtime_error("Bark text contains truncated UTF-8"); + for (size_t i = 1; i < width; ++i) { + const auto next = static_cast(text[pos + i]); + if ((next & 0xc0U) != 0x80U) throw std::runtime_error("Bark text contains invalid UTF-8"); + value = (value << 6U) | (next & 0x3fU); + } + out.push_back({value, text.substr(pos, width)}); + pos += width; + } + return out; +} + +bool is_cjk(uint32_t cp) { + return (cp >= 0x3400 && cp <= 0x4dbf) || (cp >= 0x4e00 && cp <= 0x9fff) || + (cp >= 0xf900 && cp <= 0xfaff) || (cp >= 0x20000 && cp <= 0x2fa1f); +} + +bool is_punctuation(uint32_t cp) { + if (cp < 128) return std::ispunct(static_cast(cp)) != 0; + return (cp >= 0x2000 && cp <= 0x206f) || (cp >= 0x3000 && cp <= 0x303f); +} + +} // namespace + +BarkTokenizer::BarkTokenizer(const std::string & tokenizer_json) { + const auto root = engine::io::json::parse(tokenizer_json); + const auto & values = root.require("model").require("vocab").as_object(); + vocab_.reserve(values.size()); + for (const auto & [token, id] : values) vocab_.emplace(token, static_cast(id.as_i64())); + if (const auto it = vocab_.find("[UNK]"); it != vocab_.end()) unknown_ = it->second; +} + +std::vector BarkTokenizer::encode(const std::string & text) const { + std::vector words; + std::string current; + for (const auto & ch : characters(text)) { + if (ch.codepoint <= 0x20 || ch.codepoint == 0x7f) { + if (!current.empty()) { words.push_back(std::move(current)); current.clear(); } + } else if (is_cjk(ch.codepoint) || is_punctuation(ch.codepoint)) { + if (!current.empty()) { words.push_back(std::move(current)); current.clear(); } + words.push_back(ch.bytes); + } else { + current += ch.bytes; + } + } + if (!current.empty()) words.push_back(std::move(current)); + + std::vector ids{101}; + for (const auto & word : words) { + const auto chars = characters(word); + if (chars.size() > 100) { ids.push_back(unknown_); continue; } + size_t start = 0; + std::vector pieces; + while (start < chars.size()) { + size_t end = chars.size(); + bool found = false; + while (end > start) { + std::string piece = start == 0 ? "" : "##"; + for (size_t i = start; i < end; ++i) piece += chars[i].bytes; + if (const auto it = vocab_.find(piece); it != vocab_.end()) { + pieces.push_back(it->second); start = end; found = true; break; + } + --end; + } + if (!found) { pieces.assign(1, unknown_); break; } + } + ids.insert(ids.end(), pieces.begin(), pieces.end()); + } + ids.push_back(102); + return ids; +} + +} // namespace engine::models::bark_tts diff --git a/src/models/bark_tts/transformer.cpp b/src/models/bark_tts/transformer.cpp new file mode 100644 index 00000000..df52e5fd --- /dev/null +++ b/src/models/bark_tts/transformer.cpp @@ -0,0 +1,234 @@ +#include "engine/models/bark_tts/transformer.h" + +#include "engine/framework/core/backend.h" +#include "engine/framework/core/backend_weight_store.h" +#include "engine/framework/core/constant_tensor_cache.h" +#include "engine/framework/core/execution_context.h" +#include "engine/framework/modules/attention/feed_forward.h" +#include "engine/framework/modules/attention/self_attention.h" +#include "engine/framework/modules/linear_module.h" +#include "engine/framework/modules/lookup_modules.h" +#include "engine/framework/modules/norm_modules.h" +#include "engine/framework/modules/primitive_modules.h" +#include "engine/framework/modules/structural_modules.h" +#include "engine/framework/modules/weight_binding.h" + +#include +#include + +#include + +namespace engine::models::bark_tts { +namespace { + +namespace binding = engine::modules::binding; + +struct LayerWeights { + engine::modules::NormWeights norm1; + engine::modules::AttentionWeights attention; + engine::modules::NormWeights norm2; + engine::modules::FeedForwardWeights mlp; +}; + +struct Weights { + std::shared_ptr store; + std::vector embeddings; + engine::core::TensorValue positions; + std::vector layers; + engine::modules::NormWeights final_norm; + std::vector heads; +}; + +struct ContextDelete { void operator()(ggml_context * p) const noexcept { if (p) ggml_free(p); } }; + +engine::modules::NormWeights load_norm(engine::core::BackendWeightStore & store, + const engine::assets::TensorSource & source, + const std::string & prefix, int64_t hidden, bool bias) { + engine::modules::NormWeights out; + out.weight = store.load_tensor(source, prefix + ".weight", engine::assets::TensorStorageType::F32, {hidden}); + if (bias) out.bias = store.load_tensor(source, prefix + ".bias", engine::assets::TensorStorageType::F32, {hidden}); + return out; +} + +std::shared_ptr load_weights(const BarkAssets & assets, + ggml_backend_t backend, + engine::core::BackendType backend_type, + const std::string & prefix, + const BarkTransformerConfig & config, + bool causal, + engine::assets::TensorStorageType storage) { + const auto & source = *assets.weights; + auto out = std::make_shared(); + out->store = std::make_shared(backend, backend_type, + "bark.transformer.weights", 768ULL * 1024ULL * 1024ULL); + const int embeddings = causal ? 1 : 8; + for (int i = 0; i < embeddings; ++i) { + const std::string name = causal ? prefix + ".input_embeds_layer.weight" : + prefix + ".input_embeds_layers." + std::to_string(i) + ".weight"; + out->embeddings.push_back(out->store->load_tensor(source, name, storage, {config.input_vocab, config.hidden})); + } + out->positions = out->store->load_tensor(source, prefix + ".position_embeds_layer.weight", storage, + {config.block_size, config.hidden}); + for (int64_t i = 0; i < config.layers; ++i) { + const std::string p = prefix + ".layers." + std::to_string(i); + LayerWeights layer; + layer.norm1 = load_norm(*out->store, source, p + ".layernorm_1", config.hidden, !causal || config.bias); + layer.norm2 = load_norm(*out->store, source, p + ".layernorm_2", config.hidden, !causal || config.bias); + layer.attention.qkv_weight = out->store->load_tensor(source, p + ".attn.att_proj.weight", storage, + {3 * config.hidden, config.hidden}); + if (config.bias) { + layer.attention.qkv_bias = out->store->load_tensor(source, p + ".attn.att_proj.bias", + engine::assets::TensorStorageType::F32, {3 * config.hidden}); + } + layer.attention.out_weight = out->store->load_tensor(source, p + ".attn.out_proj.weight", storage, + {config.hidden, config.hidden}); + if (config.bias) { + layer.attention.out_bias = out->store->load_tensor(source, p + ".attn.out_proj.bias", + engine::assets::TensorStorageType::F32, {config.hidden}); + } + layer.mlp.fc1_weight = out->store->load_tensor(source, p + ".mlp.in_proj.weight", storage, + {4 * config.hidden, config.hidden}); + layer.mlp.fc2_weight = out->store->load_tensor(source, p + ".mlp.out_proj.weight", storage, + {config.hidden, 4 * config.hidden}); + if (config.bias) { + layer.mlp.fc1_bias = out->store->load_tensor(source, p + ".mlp.in_proj.bias", + engine::assets::TensorStorageType::F32, {4 * config.hidden}); + layer.mlp.fc2_bias = out->store->load_tensor(source, p + ".mlp.out_proj.bias", + engine::assets::TensorStorageType::F32, {config.hidden}); + } + out->layers.push_back(std::move(layer)); + } + out->final_norm = load_norm(*out->store, source, prefix + ".layernorm_final", config.hidden, !causal || config.bias); + const int heads = causal ? 1 : 7; + for (int i = 0; i < heads; ++i) { + const std::string name = causal ? prefix + ".lm_head.weight" : prefix + ".lm_heads." + std::to_string(i) + ".weight"; + out->heads.push_back(out->store->load_tensor(source, name, storage, {config.output_vocab, config.hidden})); + } + out->store->upload(); + return out; +} + +engine::core::TensorValue block(engine::core::ModuleBuildContext & ctx, + const engine::core::TensorValue & input, + const LayerWeights & weights, + const BarkTransformerConfig & config, + bool causal) { + auto n1 = engine::modules::LayerNormModule({config.hidden, 1.0e-5F, true, !causal || config.bias}) + .build(ctx, input, weights.norm1); + auto attention = engine::modules::SelfAttentionModule({ + config.hidden, config.heads, config.bias, GGML_PREC_F32, GGML_PREC_F32, + engine::modules::AttentionPrefixCacheLayout::SequenceHeads, true, causal, false, 0, 0, 0, false, + }).build(ctx, n1, weights.attention); + auto hidden = engine::modules::AddModule{}.build(ctx, input, attention); + auto n2 = engine::modules::LayerNormModule({config.hidden, 1.0e-5F, true, !causal || config.bias}) + .build(ctx, hidden, weights.norm2); + auto ff = engine::modules::FeedForwardGeluModule({config.hidden, 4 * config.hidden, + config.bias, engine::modules::GeluApproximation::ExactErf, GGML_PREC_F32}).build(ctx, n2, weights.mlp); + return engine::modules::AddModule{}.build(ctx, hidden, ff); +} + +} // namespace + +struct BarkTransformer::Impl { + std::shared_ptr assets; + engine::core::ExecutionContext & execution; + std::string prefix; + BarkTransformerConfig config; + bool causal; + std::shared_ptr weights; + + std::vector run(const std::vector> & channels, int head, bool last_only) const { + if (channels.empty() || channels.front().empty()) throw std::runtime_error("Bark transformer input is empty"); + const int64_t sequence = static_cast(channels.front().size()); + if (sequence > config.block_size) throw std::runtime_error("Bark transformer input exceeds block size"); + for (const auto & row : channels) if (static_cast(row.size()) != sequence) + throw std::runtime_error("Bark embedding channel lengths differ"); + if (head < 0 || static_cast(head) >= weights->heads.size()) throw std::runtime_error("Bark LM head index is invalid"); + std::unique_ptr context(ggml_init({128ULL * 1024ULL * 1024ULL, nullptr, true})); + if (!context) throw std::runtime_error("failed to create Bark graph context"); + engine::core::ModuleBuildContext build{context.get(), "bark.transformer", execution.backend_type()}; + engine::core::TensorValue hidden; + std::vector inputs; + std::vector masks; + std::vector> safe_channels = channels; + std::vector> mask_values(channels.size(), std::vector(static_cast(sequence), 1.0F)); + for (size_t channel = 0; channel < channels.size(); ++channel) { + for (int64_t step = 0; step < sequence; ++step) { + if (safe_channels[channel][static_cast(step)] < 0) { + safe_channels[channel][static_cast(step)] = 0; + mask_values[channel][static_cast(step)] = 0.0F; + } + } + auto * raw = ggml_new_tensor_1d(context.get(), GGML_TYPE_I32, sequence); + inputs.push_back(raw); + auto ids = engine::core::wrap_tensor(raw, engine::core::TensorShape::from_dims({sequence}), GGML_TYPE_I32); + auto embedded = engine::modules::EmbeddingModule({config.input_vocab, config.hidden}) + .build(build, ids, weights->embeddings.at(causal ? 0 : channel)); + embedded = engine::core::reshape_tensor(build, embedded, + engine::core::TensorShape::from_dims({1, sequence, config.hidden})); + auto * mask_raw = ggml_new_tensor_2d(context.get(), GGML_TYPE_F32, 1, sequence); + masks.push_back(mask_raw); + auto mask = engine::core::wrap_tensor(mask_raw, + engine::core::TensorShape::from_dims({1, sequence, 1}), GGML_TYPE_F32); + embedded = engine::core::wrap_tensor(ggml_mul(context.get(), embedded.tensor, mask.tensor), + embedded.shape, GGML_TYPE_F32); + hidden = hidden.valid() ? engine::modules::AddModule{}.build(build, hidden, embedded) : embedded; + } + std::vector positions(static_cast(sequence)); + for (int32_t i = 0; i < sequence; ++i) positions[static_cast(i)] = i; + auto * pos_raw = ggml_new_tensor_1d(context.get(), GGML_TYPE_I32, sequence); + auto pos_ids = engine::core::wrap_tensor(pos_raw, engine::core::TensorShape::from_dims({sequence}), GGML_TYPE_I32); + auto pos = engine::modules::EmbeddingModule({config.block_size, config.hidden}).build(build, pos_ids, weights->positions); + pos = engine::core::reshape_tensor(build, pos, engine::core::TensorShape::from_dims({1, sequence, config.hidden})); + hidden = engine::modules::AddModule{}.build(build, hidden, pos); + for (const auto & layer : weights->layers) hidden = block(build, hidden, layer, config, causal); + hidden = engine::modules::LayerNormModule({config.hidden, 1.0e-5F, true, !causal || config.bias}) + .build(build, hidden, weights->final_norm); + if (last_only) hidden = engine::modules::SliceModule({1, sequence - 1, 1}).build(build, hidden); + auto logits = engine::modules::LinearModule({config.hidden, config.output_vocab, false, GGML_PREC_F32}) + .build(build, hidden, {weights->heads[static_cast(head)], std::nullopt}); + ggml_set_output(logits.tensor); + auto * graph = ggml_new_graph_custom(context.get(), 65536, false); + ggml_build_forward_expand(graph, logits.tensor); + auto * buffer = ggml_backend_alloc_ctx_tensors(context.get(), execution.backend()); + if (!buffer) throw std::runtime_error("failed to allocate Bark graph tensors"); + for (size_t i = 0; i < inputs.size(); ++i) { + ggml_backend_tensor_set(inputs[i], safe_channels[i].data(), 0, safe_channels[i].size() * sizeof(int32_t)); + ggml_backend_tensor_set(masks[i], mask_values[i].data(), 0, mask_values[i].size() * sizeof(float)); + } + ggml_backend_tensor_set(pos_raw, positions.data(), 0, positions.size() * sizeof(int32_t)); + engine::core::set_backend_threads(execution.backend(), execution.config().threads); + const auto status = engine::core::compute_backend_graph(execution.backend(), graph); + ggml_backend_synchronize(execution.backend()); + if (status != GGML_STATUS_SUCCESS) throw std::runtime_error("Bark transformer graph compute failed"); + std::vector out(static_cast((last_only ? 1 : sequence) * config.output_vocab)); + ggml_backend_tensor_get(logits.tensor, out.data(), 0, out.size() * sizeof(float)); + engine::core::release_backend_graph_resources(execution.backend(), graph); + ggml_backend_buffer_free(buffer); + return out; + } +}; + +BarkTransformer::BarkTransformer(std::shared_ptr assets, + engine::core::ExecutionContext & execution, + std::string prefix, + BarkTransformerConfig config, + bool causal, + engine::assets::TensorStorageType storage) + : impl_(std::make_unique(Impl{assets, execution, std::move(prefix), config, causal, nullptr})) { + impl_->weights = load_weights(*impl_->assets, execution.backend(), execution.backend_type(), impl_->prefix, config, causal, storage); +} + +BarkTransformer::~BarkTransformer() = default; + +std::vector BarkTransformer::causal_logits(const std::vector> & channels) const { + if (!impl_->causal) throw std::runtime_error("fine Bark transformer cannot run causal logits"); + return impl_->run(channels, 0, true); +} + +std::vector BarkTransformer::fine_logits(const std::vector> & codes, int codebook_idx) const { + if (impl_->causal || codebook_idx < 1 || codebook_idx > 7) throw std::runtime_error("invalid Bark fine codebook"); + return impl_->run(std::vector>(codes.begin(), codes.begin() + codebook_idx + 1), codebook_idx - 1, false); +} + +} // namespace engine::models::bark_tts diff --git a/tests/bark_tts/bark_codec_parity.cpp b/tests/bark_tts/bark_codec_parity.cpp new file mode 100644 index 00000000..ef7cfbb9 --- /dev/null +++ b/tests/bark_tts/bark_codec_parity.cpp @@ -0,0 +1,40 @@ +#include "engine/framework/core/backend.h" +#include "engine/framework/core/execution_context.h" +#include "engine/framework/model_spec/package.h" +#include "engine/models/bark_tts/assets.h" +#include "engine/models/bark_tts/codec.h" + +#include +#include +#include +#include + +int main(int argc, char ** argv) try { + if (argc != 5) { + std::cerr << "usage: bark_codec_parity \n"; + return 2; + } + const int frames = std::stoi(argv[3]); + engine::model_spec::ScopedSpecOverride spec{std::filesystem::path(argv[2]), std::filesystem::path(argv[1])}; + auto assets = engine::models::bark_tts::load_bark_assets(argv[1]); + const auto found = assets->presets.find("v2/en_speaker_6"); + if (found == assets->presets.end()) throw std::runtime_error("missing parity preset"); + std::vector> codes(8); + for (int book = 0; book < 8; ++book) { + codes[book].assign(found->second.fine[book].begin(), + found->second.fine[book].begin() + std::min(frames, found->second.fine[book].size())); + } + engine::core::BackendConfig config; + config.type = engine::core::BackendType::Cpu; + config.threads = 8; + engine::core::ExecutionContext execution(config); + engine::models::bark_tts::BarkCodecDecoder decoder(assets, execution, engine::assets::TensorStorageType::Native); + const auto waveform = decoder.decode(codes); + std::ofstream output(argv[4], std::ios::binary); + output.write(reinterpret_cast(waveform.data()), static_cast(waveform.size() * sizeof(float))); + std::cout << "samples=" << waveform.size() << "\n"; + return 0; +} catch (const std::exception & error) { + std::cerr << error.what() << "\n"; + return 1; +} diff --git a/tests/bark_tts/bark_transformer_parity.cpp b/tests/bark_tts/bark_transformer_parity.cpp new file mode 100644 index 00000000..ae44c5e2 --- /dev/null +++ b/tests/bark_tts/bark_transformer_parity.cpp @@ -0,0 +1,51 @@ +#include "engine/framework/core/backend.h" +#include "engine/framework/core/execution_context.h" +#include "engine/framework/model_spec/package.h" +#include "engine/models/bark_tts/assets.h" +#include "engine/models/bark_tts/tokenizer.h" +#include "engine/models/bark_tts/transformer.h" + +#include +#include +#include + +int main(int argc, char ** argv) try { + if (argc != 4) { + std::cerr << "usage: bark_transformer_parity \n"; + return 2; + } + engine::model_spec::ScopedSpecOverride spec{std::filesystem::path(argv[2]), std::filesystem::path(argv[1])}; + auto assets = engine::models::bark_tts::load_bark_assets(argv[1]); + const auto & preset = assets->presets.at("v2/en_speaker_6"); + engine::models::bark_tts::BarkTokenizer tokenizer(assets->resources.read_text("tokenizer_json")); + auto text = tokenizer.encode("Hello, Bark!"); + for (auto & id : text) id += 10048; + text.resize(256, 129595); + std::vector history(preset.semantic.end() - 256, preset.semantic.end()); + text.push_back(129599); + history.push_back(10000); + engine::core::BackendConfig config; + config.type = engine::core::BackendType::Cpu; + config.threads = 8; + engine::core::ExecutionContext execution(config); + engine::models::bark_tts::BarkTransformer transformer(assets, execution, "semantic", assets->config.semantic, + true, engine::assets::TensorStorageType::Native); + const auto logits = transformer.causal_logits({text, history}); + std::ofstream output(argv[3], std::ios::binary); + output.write(reinterpret_cast(logits.data()), static_cast(logits.size() * sizeof(float))); + output.close(); + std::vector> fine_codes(8); + for (int book = 0; book < 8; ++book) + fine_codes[book].assign(preset.fine[book].begin(), preset.fine[book].begin() + 64); + engine::models::bark_tts::BarkTransformer fine(assets, execution, "fine_acoustics", assets->config.fine, + false, engine::assets::TensorStorageType::Native); + const auto fine_logits = fine.fine_logits(fine_codes, 2); + std::ofstream fine_output(std::string(argv[3]) + ".fine", std::ios::binary); + fine_output.write(reinterpret_cast(fine_logits.data()), + static_cast(fine_logits.size() * sizeof(float))); + std::cout << "semantic_logits=" << logits.size() << " fine_logits=" << fine_logits.size() << "\n"; + return 0; +} catch (const std::exception & error) { + std::cerr << error.what() << "\n"; + return 1; +} diff --git a/tests/unittests/test_bark_tokenizer.cpp b/tests/unittests/test_bark_tokenizer.cpp new file mode 100644 index 00000000..e246cd9c --- /dev/null +++ b/tests/unittests/test_bark_tokenizer.cpp @@ -0,0 +1,20 @@ +#include "engine/models/bark_tts/tokenizer.h" + +#include +#include + +int main() { + const std::string json = R"({"model":{"vocab":{"[UNK]":100,"[CLS]":101,"[SEP]":102,"Hello":31178,",":117,"Bar":20698,"##k":10174,"!":106,"你":2262,"好":3240}}})"; + engine::models::bark_tts::BarkTokenizer tokenizer(json); + const std::vector expected{101, 31178, 117, 20698, 10174, 106, 102}; + if (tokenizer.encode("Hello, Bark!") != expected) { + std::cerr << "Bark WordPiece tokenization mismatch\n"; + return 1; + } + const std::vector chinese{101, 2262, 3240, 102}; + if (tokenizer.encode("你好") != chinese) { + std::cerr << "Bark Chinese-character splitting mismatch\n"; + return 1; + } + return 0; +} diff --git a/tools/community_models/convert_bark.py b/tools/community_models/convert_bark.py new file mode 100644 index 00000000..f2539a17 --- /dev/null +++ b/tools/community_models/convert_bark.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +"""Convert Hugging Face ``suno/bark-small`` into an audio.cpp GGUF package. + +The generated package contains Bark's semantic, coarse, and fine transformers, +the eight EnCodec codebooks used by Bark, the EnCodec decoder, the tokenizer, +and every downloaded speaker preset. Weight-normalized EnCodec convolutions +are materialized before export so the native runtime does not need PyTorch. +""" + +from __future__ import annotations + +import argparse +import json +import shutil +import subprocess +import tempfile +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] +SPEC = REPO_ROOT / "model_specs" / "bark_tts.json" + + +def _load_checkpoint(path: Path): + try: + import torch + except ImportError as exc: + raise SystemExit("PyTorch is required: uv run --with torch --with safetensors " + __file__) from exc + checkpoint = torch.load(path, map_location="cpu", weights_only=True) + return checkpoint.get("model", checkpoint) + + +def _materialize_weight_norm(state: dict, prefix: str): + import torch + + g_key, v_key = prefix + ".weight_g", prefix + ".weight_v" + if g_key not in state or v_key not in state: + return None + g, v = state[g_key].float(), state[v_key].float() + # PyTorch weight_norm(..., dim=0): one magnitude per output channel. + dims = tuple(range(1, v.ndim)) + return (v * (g / torch.linalg.vector_norm(v, dim=dims, keepdim=True))).contiguous() + + +def _export_tensors(source: Path, output: Path) -> None: + from safetensors.torch import save_file + + state = _load_checkpoint(source / "pytorch_model.bin") + exported = {} + for name, tensor in state.items(): + if name.endswith(".attn.bias") or name.startswith("codec_model.encoder."): + continue + if name.startswith("codec_model.quantizer.layers."): + parts = name.split(".") + if int(parts[3]) >= 8 or not name.endswith(".codebook.embed"): + continue + if name.endswith(".weight_g") or name.endswith(".weight_v"): + prefix = name.rsplit(".", 1)[0] + weight_name = prefix + ".weight" + if weight_name not in exported: + exported[weight_name] = _materialize_weight_norm(state, prefix) + continue + # Fine-model LM heads are tied to embeddings in the PyTorch object; + # safetensors requires each exported name to own its storage. + exported[name] = tensor.detach().clone().contiguous() + save_file(exported, str(output)) + + +def _collect_presets(source: Path, output: Path) -> int: + import numpy as np + + preset_root = source / "speaker_embeddings" + records = {} + for semantic in sorted(preset_root.rglob("*_semantic_prompt.npy")): + stem = semantic.name.removesuffix("_semantic_prompt.npy") + relative = semantic.parent.relative_to(preset_root) + key = str(relative / stem) + coarse = semantic.with_name(stem + "_coarse_prompt.npy") + fine = semantic.with_name(stem + "_fine_prompt.npy") + if not coarse.is_file() or not fine.is_file(): + raise RuntimeError(f"incomplete Bark preset: {key}") + records[key] = { + "semantic": np.load(semantic).astype("int32").tolist(), + "coarse": np.load(coarse).astype("int32").tolist(), + "fine": np.load(fine).astype("int32").tolist(), + } + output.write_text(json.dumps({"version": 1, "presets": records}, separators=(",", ":"))) + return len(records) + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--source", type=Path, required=True, help="downloaded suno/bark-small snapshot") + parser.add_argument("--converter", type=Path, required=True, help="built audiocpp_gguf executable") + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--type", default="q8_0", choices=["orig", "f16", "bf16", "q8_0"]) + parser.add_argument("--overwrite", action="store_true") + args = parser.parse_args() + source, output = args.source.resolve(), args.output.resolve() + required = ["pytorch_model.bin", "config.json", "generation_config.json", "tokenizer.json"] + missing = [name for name in required if not (source / name).is_file()] + if missing: + raise SystemExit(f"Bark source is missing: {', '.join(missing)}") + if not args.converter.is_file(): + raise SystemExit(f"converter not found: {args.converter}") + output.parent.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory(prefix="audiocpp-bark-") as temp_string: + temp = Path(temp_string) + tensors = temp / "bark.safetensors" + presets = temp / "speaker_presets.json" + _export_tensors(source, tensors) + preset_count = _collect_presets(source, presets) + for name in ("config.json", "generation_config.json", "tokenizer.json", "tokenizer_config.json"): + candidate = source / name + if candidate.is_file(): + shutil.copy2(candidate, temp / name) + command = [ + str(args.converter), "--input", f"bark={tensors}", "--root", str(temp), + "--family", "bark_tts", "--model-spec", str(SPEC), "--type", args.type, + "--output", str(output), + ] + if args.type == "q8_0": + # Bark is sensitive to quantizing its lookup tables and waveform + # decoder. Keep these in F16 and quantize only dense transformer + # matrices; this is also materially smaller than keeping them F32. + for pattern in ( + "codec_model.*", "semantic.input_embeds_layer.*", "semantic.position_embeds_layer.*", + "semantic.layernorm_final.*", "semantic.lm_head.*", + "coarse_acoustics.input_embeds_layer.*", "coarse_acoustics.position_embeds_layer.*", + "coarse_acoustics.layernorm_final.*", "coarse_acoustics.lm_head.*", + "fine_acoustics.input_embeds_layers.*", + "fine_acoustics.position_embeds_layer.*", "fine_acoustics.layernorm_final.*", + "fine_acoustics.lm_heads.*", + ): + command.extend(["--keep-type", pattern + "=f16"]) + if args.overwrite: + command.append("--overwrite") + print("+", " ".join(command), flush=True) + subprocess.run(command, check=True) + print(f"wrote {output} with {preset_count} speaker presets") + + +if __name__ == "__main__": + main() From 5004f715a170c63f4c3e0265ac354e5799fc9c2a Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Sat, 12 Sep 2026 11:52:41 -0400 Subject: [PATCH 2/6] Add Bark generated demo clip --- demos/bark-small-q8-demo.mp4 | Bin 0 -> 7738 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/bark-small-q8-demo.mp4 diff --git a/demos/bark-small-q8-demo.mp4 b/demos/bark-small-q8-demo.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..d2fa7a704f9cc86805aa6daab49d48aafdfc3632 GIT binary patch literal 7738 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 literal 0 HcmV?d00001 From dd36986399b2b1ac796aa4aecdda11a5ef20cd21 Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Sat, 12 Sep 2026 12:23:21 -0400 Subject: [PATCH 3/6] Fix Bark semantic history sequence layout --- src/models/bark_tts/generator.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/models/bark_tts/generator.cpp b/src/models/bark_tts/generator.cpp index 1e2f70c0..41b43bf4 100644 --- a/src/models/bark_tts/generator.cpp +++ b/src/models/bark_tts/generator.cpp @@ -35,16 +35,20 @@ std::vector semantic_tokens(BarkTransformer & model, const BarkTokenize const size_t history_start = preset.semantic.size() > 256 ? preset.semantic.size() - 256 : 0; history.assign(preset.semantic.begin() + static_cast(history_start), preset.semantic.end()); history.resize(256, 10000); + // Bark's text model consumes one concatenated sequence: + // [256 text tokens][256 semantic-history tokens][SEMANTIC_INFER]. + // These are sequence positions, not two embedding channels. Passing + // them as separate channels sums embeddings at each position and + // produces invalid semantic audio. text_ids.push_back(129599); - history.push_back(10000); + text_ids.insert(text_ids.end(), history.begin(), history.end()); std::vector generated; for (int64_t step = 0; step < options.max_tokens && text_ids.size() < 1024; ++step) { - auto logits = model.causal_logits({text_ids, history}); + auto logits = model.causal_logits({text_ids}); const int32_t id = sample(std::move(logits), 0, 10001, options.top_k, options.temperature, rng); if (id == 10000) break; generated.push_back(id); text_ids.push_back(id); - history.push_back(-1); } if (generated.empty()) throw std::runtime_error("Bark semantic model generated no speech tokens"); return generated; @@ -58,7 +62,8 @@ std::vector coarse_tokens(BarkTransformer & model, const BarkSpeakerPre std::vector coarse_history; if (preset.coarse.size() != 2) throw std::runtime_error("Bark preset coarse history must have two codebooks"); for (size_t frame = 0; frame < preset.coarse[0].size(); ++frame) - for (int book = 0; book < 2; ++book) coarse_history.push_back(preset.coarse[book][frame] + book * 1024 + 10000); + for (int book = 0; book < 2; ++book) + coarse_history.push_back(preset.coarse[book][frame] + book * 1024 + 10000); const int max_sem_history = static_cast(std::floor(630.0 / ratio)); int sem_count = std::min({max_sem_history, static_cast(sem_history.size() / 2 * 2), static_cast(std::floor(coarse_history.size() / ratio))}); From 1b3d806e70d49ba6acea5b542d93c92c278663fa Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Sat, 12 Sep 2026 13:08:15 -0400 Subject: [PATCH 4/6] Add Bark full-sentence demo after semantic fix --- demos/bark-small-fixed-full-sentence.mp4 | Bin 0 -> 11559 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/bark-small-fixed-full-sentence.mp4 diff --git a/demos/bark-small-fixed-full-sentence.mp4 b/demos/bark-small-fixed-full-sentence.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..1bf74997f33ba55b8d95c2c5508f9d496926d065 GIT binary patch literal 11559 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 literal 0 HcmV?d00001 From 89bd98fe1dc39a9d0befe9f07788ee14a1fac051 Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Sat, 12 Sep 2026 13:14:19 -0400 Subject: [PATCH 5/6] Fix Bark coarse semantic context window --- src/models/bark_tts/generator.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/models/bark_tts/generator.cpp b/src/models/bark_tts/generator.cpp index 41b43bf4..914cb2dd 100644 --- a/src/models/bark_tts/generator.cpp +++ b/src/models/bark_tts/generator.cpp @@ -77,9 +77,14 @@ std::vector coarse_tokens(BarkTransformer & model, const BarkSpeakerPre const int generated_length = static_cast(std::round(std::floor(semantic.size() * ratio / 2.0) * 2.0)); for (int total = 0; total < generated_length;) { const int semantic_index = sem_count + static_cast(std::round(total / ratio)); - const int begin = std::max(0, semantic_index - max_sem_history); - std::vector input(all_semantic.begin() + begin, all_semantic.end()); - if (input.size() > 256) input.resize(256); + // The coarse model must see semantic context ending at the current + // frame. Including the remainder of all_semantic here shifts the + // conditioning window to the beginning of the utterance and yields + // unrelated/noisy audio. + const int end = std::min(semantic_index, static_cast(all_semantic.size())); + const int begin = std::max(0, end - max_sem_history); + std::vector input(all_semantic.begin() + begin, all_semantic.begin() + end); + if (input.size() > 256) input.erase(input.begin(), input.end() - 256); input.resize(256, 12048); input.push_back(12050); const size_t take = std::min(630, coarse_history.size()); From c91743062573be1958d323f983ee6d17ff42c2dc Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Sat, 12 Sep 2026 18:07:39 -0400 Subject: [PATCH 6/6] Fix Bark generation and quality validation --- demos/bark-small-fixed-full-sentence.mp4 | Bin 11559 -> 31964 bytes demos/bark-small-q8-demo.mp4 | Bin 7738 -> 0 bytes docs/models/bark_tts.md | 11 ++++--- model_specs/bark_tts.json | 6 ++-- src/models/bark_tts/generator.cpp | 28 ++++++++--------- src/models/bark_tts/tokenizer.cpp | 5 +-- tests/bark_tts/bark_transformer_parity.cpp | 35 ++++++++++++++++++++- tests/unittests/test_bark_tokenizer.cpp | 4 +-- tools/community_models/convert_bark.py | 18 +++++------ 9 files changed, 69 insertions(+), 38 deletions(-) delete mode 100644 demos/bark-small-q8-demo.mp4 diff --git a/demos/bark-small-fixed-full-sentence.mp4 b/demos/bark-small-fixed-full-sentence.mp4 index 1bf74997f33ba55b8d95c2c5508f9d496926d065..5102f9469fe04500aaa8e7fa6a0a4953dd53e1c1 100644 GIT binary patch literal 31964 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 M{>j;(KUVht4-JP&jsO4v delta 11036 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 PC2;ym1Mw5wea-(5SkRk? diff --git a/demos/bark-small-q8-demo.mp4 b/demos/bark-small-q8-demo.mp4 deleted file mode 100644 index d2fa7a704f9cc86805aa6daab49d48aafdfc3632..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7738 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 diff --git a/docs/models/bark_tts.md b/docs/models/bark_tts.md index 58113e3e..ca2c2b09 100644 --- a/docs/models/bark_tts.md +++ b/docs/models/bark_tts.md @@ -5,10 +5,10 @@ contains the complete Bark Small semantic, coarse, fine, and EnCodec pipeline, plus all 261 upstream speaker histories. ```sh -python3 tools/model_manager_v2.py install bark_small_q8_0 +python3 tools/model_manager_v2.py install bark_small_f16 audiocpp_cli --task tts --family bark_tts \ - --model Bark-Small-GGUF/bark-small-q8_0.gguf \ + --model Bark-Small-GGUF/bark-small-f16.gguf \ --text "Hello from Bark. [laughs] This model can be quite expressive." \ --option history_prompt=v2/en_speaker_6 --output bark.wav ``` @@ -19,9 +19,10 @@ follow upstream names such as `v2/de_speaker_3`, `v2/ja_speaker_0`, or request options. Bark supports non-speech cues written in brackets, although the result remains probabilistic. -The Q8 package quantizes transformer matrix weights. Embeddings, normalization -parameters, EnCodec codebooks, recurrent weights, and convolution weights are -kept at 16-bit precision to protect synthesis quality. +F16 is the recommended package. The hybrid Q8 package keeps both autoregressive +semantic/coarse transformers and the complete EnCodec path in F16, and only +quantizes the non-causal fine transformer's dense matrices. Quantizing Bark's +autoregressive stages causes token errors to compound and is not quality-safe. Source model: [suno/bark-small](https://huggingface.co/suno/bark-small), pinned to revision `1dbd7a128513b8ae4a4e2130fed57b7ac9da5bcd`. Bark is MIT licensed. diff --git a/model_specs/bark_tts.json b/model_specs/bark_tts.json index 21bb7149..8c3c5d84 100644 --- a/model_specs/bark_tts.json +++ b/model_specs/bark_tts.json @@ -32,7 +32,7 @@ { "id": "bark_small_f16", "display_name": "Bark Small F16 GGUF", - "default": false, + "default": true, "format": "gguf", "precision": "f16", "target_directory": "Bark-Small-GGUF", @@ -42,7 +42,7 @@ { "id": "bark_small_q8_0", "display_name": "Bark Small Q8_0 GGUF", - "default": true, + "default": false, "format": "gguf", "precision": "q8_0", "target_directory": "Bark-Small-GGUF", @@ -52,7 +52,7 @@ ], "dependencies": [], "ui": { - "recommended_package": "bark_small_q8_0", + "recommended_package": "bark_small_f16", "tags": ["TTS", "GGUF"], "docs": ["docs/models/bark_tts.md", "docs/tts.md", "docs/gguf.md"] }, diff --git a/src/models/bark_tts/generator.cpp b/src/models/bark_tts/generator.cpp index 914cb2dd..2a43c057 100644 --- a/src/models/bark_tts/generator.cpp +++ b/src/models/bark_tts/generator.cpp @@ -35,20 +35,20 @@ std::vector semantic_tokens(BarkTransformer & model, const BarkTokenize const size_t history_start = preset.semantic.size() > 256 ? preset.semantic.size() - 256 : 0; history.assign(preset.semantic.begin() + static_cast(history_start), preset.semantic.end()); history.resize(256, 10000); - // Bark's text model consumes one concatenated sequence: - // [256 text tokens][256 semantic-history tokens][SEMANTIC_INFER]. - // These are sequence positions, not two embedding channels. Passing - // them as separate channels sums embeddings at each position and - // produces invalid semantic audio. + // Bark's merge_context path sums the first 256 text embeddings with the + // 256 semantic-history embeddings, then appends SEMANTIC_INFER without a + // history embedding at that position. Two channels reproduce that + // merged 257-position sequence; negative ids are masked by the runtime. text_ids.push_back(129599); - text_ids.insert(text_ids.end(), history.begin(), history.end()); + history.push_back(-1); std::vector generated; for (int64_t step = 0; step < options.max_tokens && text_ids.size() < 1024; ++step) { - auto logits = model.causal_logits({text_ids}); + auto logits = model.causal_logits({text_ids, history}); const int32_t id = sample(std::move(logits), 0, 10001, options.top_k, options.temperature, rng); if (id == 10000) break; generated.push_back(id); text_ids.push_back(id); + history.push_back(-1); } if (generated.empty()) throw std::runtime_error("Bark semantic model generated no speech tokens"); return generated; @@ -77,14 +77,12 @@ std::vector coarse_tokens(BarkTransformer & model, const BarkSpeakerPre const int generated_length = static_cast(std::round(std::floor(semantic.size() * ratio / 2.0) * 2.0)); for (int total = 0; total < generated_length;) { const int semantic_index = sem_count + static_cast(std::round(total / ratio)); - // The coarse model must see semantic context ending at the current - // frame. Including the remainder of all_semantic here shifts the - // conditioning window to the beginning of the utterance and yields - // unrelated/noisy audio. - const int end = std::min(semantic_index, static_cast(all_semantic.size())); - const int begin = std::max(0, end - max_sem_history); - std::vector input(all_semantic.begin() + begin, all_semantic.begin() + end); - if (input.size() > 256) input.erase(input.begin(), input.end() - 256); + const int begin = std::max(0, semantic_index - max_sem_history); + // This deliberately takes the suffix beginning at semantic_index's + // history boundary and then its first 256 values. It matches Bark's + // reference x_semantic[:, max(0, semantic_idx-max_history):][:, :256]. + std::vector input(all_semantic.begin() + begin, all_semantic.end()); + if (input.size() > 256) input.resize(256); input.resize(256, 12048); input.push_back(12050); const size_t take = std::min(630, coarse_history.size()); diff --git a/src/models/bark_tts/tokenizer.cpp b/src/models/bark_tts/tokenizer.cpp index 8dd3e1e6..6b0273a5 100644 --- a/src/models/bark_tts/tokenizer.cpp +++ b/src/models/bark_tts/tokenizer.cpp @@ -66,7 +66,9 @@ std::vector BarkTokenizer::encode(const std::string & text) const { } if (!current.empty()) words.push_back(std::move(current)); - std::vector ids{101}; + // Bark calls BertTokenizer.encode(..., add_special_tokens=False). + // [CLS]/[SEP] are not part of the semantic text conditioning sequence. + std::vector ids; for (const auto & word : words) { const auto chars = characters(word); if (chars.size() > 100) { ids.push_back(unknown_); continue; } @@ -87,7 +89,6 @@ std::vector BarkTokenizer::encode(const std::string & text) const { } ids.insert(ids.end(), pieces.begin(), pieces.end()); } - ids.push_back(102); return ids; } diff --git a/tests/bark_tts/bark_transformer_parity.cpp b/tests/bark_tts/bark_transformer_parity.cpp index ae44c5e2..ca0ed69a 100644 --- a/tests/bark_tts/bark_transformer_parity.cpp +++ b/tests/bark_tts/bark_transformer_parity.cpp @@ -8,6 +8,7 @@ #include #include #include +#include int main(int argc, char ** argv) try { if (argc != 4) { @@ -23,7 +24,7 @@ int main(int argc, char ** argv) try { text.resize(256, 129595); std::vector history(preset.semantic.end() - 256, preset.semantic.end()); text.push_back(129599); - history.push_back(10000); + history.push_back(-1); engine::core::BackendConfig config; config.type = engine::core::BackendType::Cpu; config.threads = 8; @@ -34,6 +35,38 @@ int main(int argc, char ** argv) try { std::ofstream output(argv[3], std::ios::binary); output.write(reinterpret_cast(logits.data()), static_cast(logits.size() * sizeof(float))); output.close(); + text.push_back(147); + history.push_back(-1); + const auto logits2 = transformer.causal_logits({text, history}); + std::ofstream output2(std::string(argv[3]) + ".semantic2", std::ios::binary); + output2.write(reinterpret_cast(logits2.data()), + static_cast(logits2.size() * sizeof(float))); + constexpr double ratio = 75.0 / 49.9 * 2.0; + std::vector semantic_seed{147, 302, 2089, 330, 602, 206, 218, 2009}; + std::vector sem_history = preset.semantic; + std::vector coarse_history; + for (size_t frame = 0; frame < preset.coarse[0].size(); ++frame) + for (int book = 0; book < 2; ++book) + coarse_history.push_back(preset.coarse[book][frame] + book * 1024 + 10000); + const int max_sem_history = static_cast(std::floor(630.0 / ratio)); + const int sem_count = std::min({max_sem_history, static_cast(sem_history.size() / 2 * 2), + static_cast(std::floor(coarse_history.size() / ratio))}); + const int coarse_count = static_cast(std::round(sem_count * ratio)); + sem_history.erase(sem_history.begin(), sem_history.end() - sem_count); + coarse_history.erase(coarse_history.begin(), coarse_history.end() - coarse_count); + coarse_history.resize(coarse_history.size() - 2); + sem_history.insert(sem_history.end(), semantic_seed.begin(), semantic_seed.end()); + if (sem_history.size() > 256) sem_history.resize(256); + sem_history.resize(256, 12048); + sem_history.push_back(12050); + const size_t take = std::min(630, coarse_history.size()); + sem_history.insert(sem_history.end(), coarse_history.end() - static_cast(take), coarse_history.end()); + engine::models::bark_tts::BarkTransformer coarse(assets, execution, "coarse_acoustics", assets->config.coarse, + true, engine::assets::TensorStorageType::Native); + const auto coarse_logits = coarse.causal_logits({sem_history}); + std::ofstream coarse_output(std::string(argv[3]) + ".coarse", std::ios::binary); + coarse_output.write(reinterpret_cast(coarse_logits.data()), + static_cast(coarse_logits.size() * sizeof(float))); std::vector> fine_codes(8); for (int book = 0; book < 8; ++book) fine_codes[book].assign(preset.fine[book].begin(), preset.fine[book].begin() + 64); diff --git a/tests/unittests/test_bark_tokenizer.cpp b/tests/unittests/test_bark_tokenizer.cpp index e246cd9c..684fac28 100644 --- a/tests/unittests/test_bark_tokenizer.cpp +++ b/tests/unittests/test_bark_tokenizer.cpp @@ -6,12 +6,12 @@ int main() { const std::string json = R"({"model":{"vocab":{"[UNK]":100,"[CLS]":101,"[SEP]":102,"Hello":31178,",":117,"Bar":20698,"##k":10174,"!":106,"你":2262,"好":3240}}})"; engine::models::bark_tts::BarkTokenizer tokenizer(json); - const std::vector expected{101, 31178, 117, 20698, 10174, 106, 102}; + const std::vector expected{31178, 117, 20698, 10174, 106}; if (tokenizer.encode("Hello, Bark!") != expected) { std::cerr << "Bark WordPiece tokenization mismatch\n"; return 1; } - const std::vector chinese{101, 2262, 3240, 102}; + const std::vector chinese{2262, 3240}; if (tokenizer.encode("你好") != chinese) { std::cerr << "Bark Chinese-character splitting mismatch\n"; return 1; diff --git a/tools/community_models/convert_bark.py b/tools/community_models/convert_bark.py index f2539a17..2a3fad13 100644 --- a/tools/community_models/convert_bark.py +++ b/tools/community_models/convert_bark.py @@ -120,17 +120,15 @@ def main() -> None: "--output", str(output), ] if args.type == "q8_0": - # Bark is sensitive to quantizing its lookup tables and waveform - # decoder. Keep these in F16 and quantize only dense transformer - # matrices; this is also materially smaller than keeping them F32. + # Autoregressive errors compound across Bark's semantic and coarse + # stages. Keep both complete causal transformers, plus the waveform + # decoder and fine lookup/output tensors, in F16. Only the fine + # transformer's dense matrices are safe to quantize to Q8_0. for pattern in ( - "codec_model.*", "semantic.input_embeds_layer.*", "semantic.position_embeds_layer.*", - "semantic.layernorm_final.*", "semantic.lm_head.*", - "coarse_acoustics.input_embeds_layer.*", "coarse_acoustics.position_embeds_layer.*", - "coarse_acoustics.layernorm_final.*", "coarse_acoustics.lm_head.*", - "fine_acoustics.input_embeds_layers.*", - "fine_acoustics.position_embeds_layer.*", "fine_acoustics.layernorm_final.*", - "fine_acoustics.lm_heads.*", + "bark/codec_model.*", "bark/semantic.*", "bark/coarse_acoustics.*", + "bark/fine_acoustics.input_embeds_layers.*", + "bark/fine_acoustics.position_embeds_layer.*", "bark/fine_acoustics.layernorm_final.*", + "bark/fine_acoustics.lm_heads.*", ): command.extend(["--keep-type", pattern + "=f16"]) if args.overwrite: