Skip to content

Commit 343a6a6

Browse files
justinjohn0306claude
andcommitted
Add MOSS-TTS-Local Phase 5: voice cloning + session/CLI wiring
Wire the MOSS-TTS-Local session end to end: the text processor builds the generation prefix, the generator emits RVQ codes, and the codec decoder renders 48 kHz stereo. Add voice cloning via the MOSS-Audio-Tokenizer-v2 encoder (audio -> RLFQ codes), the structural mirror of the decoder; extract the shared ProjectedTransformer machinery into codec_transformer.h so the encoder and decoder share one implementation. The processor's clone prefix embeds the reference speaker's codes under "- Reference(s):"; the session resamples and loudness-normalizes a --voice-ref clip, encodes it, and seeds generation. Parity-verified against the transformers reference: generation loop 96/96 codes over 8 frames, encoder 300/300 codes, clone input_ids 100x13 exact, decoder cosine 1.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5847d38 commit 343a6a6

16 files changed

Lines changed: 1380 additions & 324 deletions

File tree

CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,13 @@ add_library(engine_runtime STATIC
209209
src/models/moss_tts_local/assets.cpp
210210
src/models/moss_tts_local/backbone.cpp
211211
src/models/moss_tts_local/codec_decoder.cpp
212+
src/models/moss_tts_local/codec_encoder.cpp
212213
src/models/moss_tts_local/codec_quantizer.cpp
213214
src/models/moss_tts_local/depth_transformer.cpp
214215
src/models/moss_tts_local/generator.cpp
215216
src/models/moss_tts_local/loader.cpp
216217
src/models/moss_tts_local/processor.cpp
218+
src/models/moss_tts_local/session.cpp
217219
src/models/voxcpm2/assets.cpp
218220
src/models/voxcpm2/audiovae.cpp
219221
src/models/voxcpm2/generator.cpp
@@ -468,6 +470,15 @@ if (ENGINE_ENABLE_OPENMP)
468470
target_link_libraries(codec_decode_parity PRIVATE OpenMP::OpenMP_CXX)
469471
endif()
470472
473+
add_executable(codec_encode_parity
474+
tests/moss_tts_local/codec_encode_parity.cpp
475+
)
476+
477+
target_link_libraries(codec_encode_parity PRIVATE engine_runtime ggml)
478+
if (ENGINE_ENABLE_OPENMP)
479+
target_link_libraries(codec_encode_parity PRIVATE OpenMP::OpenMP_CXX)
480+
endif()
481+
471482
if (ENGINE_BUILD_WARMBENCH)
472483
function(add_engine_warmbench target_name source_file)
473484
add_executable(${target_name}

include/engine/models/moss_tts_local/assets.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,9 @@ MossTTSLocalAssetPaths resolve_moss_tts_local_assets(const std::filesystem::path
7575
MossTTSLocalConfig load_moss_tts_local_config(const std::filesystem::path & model_path);
7676
std::shared_ptr<const MossTTSLocalAssets> load_moss_tts_local_assets(const std::filesystem::path & model_path);
7777

78+
// Locates the MOSS-Audio-Tokenizer-v2 codec weights: prefers a local audio_tokenizer/
79+
// directory next to the model, otherwise resolves the Hugging Face hub cache snapshot for
80+
// the repo id recorded in config (audio_tokenizer_name_or_path).
81+
std::filesystem::path resolve_moss_codec_dir(const MossTTSLocalAssets & assets);
82+
7883
} // namespace engine::models::moss_tts_local
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
3+
#include "engine/framework/core/execution_context.h"
4+
5+
#include <cstdint>
6+
#include <filesystem>
7+
#include <memory>
8+
#include <vector>
9+
10+
namespace engine::models::moss_tts_local {
11+
12+
// MOSS-Audio-Tokenizer-v2 encoder: turns a reference waveform into RLFQ codes
13+
// for zero-shot voice cloning. It is the structural mirror of MossCodecDecoder --
14+
// stereo is interleaved into one stream, patched down and run through a stack of
15+
// causal Transformer blocks (interleaved RoPE, LayerScale, GELU MLP), then the
16+
// RLFQ quantizer selects the nearest codes. Produces the same [num_quantizers,
17+
// frames] code matrix the generator consumes.
18+
class MossCodecEncoder {
19+
public:
20+
MossCodecEncoder(
21+
const std::filesystem::path & codec_dir,
22+
core::ExecutionContext & execution_context,
23+
int64_t num_quantizers,
24+
size_t weight_context_bytes,
25+
size_t graph_arena_bytes);
26+
~MossCodecEncoder();
27+
28+
MossCodecEncoder(const MossCodecEncoder &) = delete;
29+
MossCodecEncoder & operator=(const MossCodecEncoder &) = delete;
30+
31+
// Encodes a waveform given as {left, right} channels (each with the same
32+
// per-channel sample count, 48 kHz) into [num_quantizers][frames] codes.
33+
std::vector<std::vector<int32_t>> encode(const std::vector<std::vector<float>> & channels) const;
34+
35+
private:
36+
struct Impl;
37+
std::unique_ptr<Impl> impl_;
38+
};
39+
40+
} // namespace engine::models::moss_tts_local

include/engine/models/moss_tts_local/codec_quantizer.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,21 @@ class MossCodecDequantizer {
2222

2323
std::vector<float> decode(const std::vector<std::vector<int32_t>> & codes) const;
2424

25+
// Quantizes the encoder latent into codes: the inverse of decode(). `hidden`
26+
// is [frames, code_dim] feature-last (row-major: frame * code_dim + channel),
27+
// matching the codec encoder's output. Mirrors the RLFQ forward pass
28+
// (input_proj -> per-quantizer in_proj -> L2-normalized nearest code ->
29+
// residual subtraction) and returns the [num_quantizers][frames] code matrix.
30+
std::vector<std::vector<int32_t>> encode(const std::vector<float> & hidden, int64_t frames) const;
31+
2532
private:
2633
struct Codebook {
27-
std::vector<float> table; // [codebook_size, codebook_dim] row-major
28-
std::vector<float> out_weight; // [rvq_dim, codebook_dim] row-major
29-
std::vector<float> out_bias; // [rvq_dim]
34+
std::vector<float> table; // [codebook_size, codebook_dim] row-major
35+
std::vector<float> table_normalized; // [codebook_size, codebook_dim], L2-normalized rows (encode)
36+
std::vector<float> out_weight; // [rvq_dim, codebook_dim] row-major
37+
std::vector<float> out_bias; // [rvq_dim]
38+
std::vector<float> in_weight; // [codebook_dim, rvq_dim] row-major (encode)
39+
std::vector<float> in_bias; // [codebook_dim] (encode)
3040
};
3141

3242
int64_t codebook_size_ = 0;
@@ -37,6 +47,8 @@ class MossCodecDequantizer {
3747
std::vector<Codebook> codebooks_;
3848
std::vector<float> output_weight_; // [code_dim, rvq_dim] row-major
3949
std::vector<float> output_bias_; // [code_dim]
50+
std::vector<float> input_weight_; // [rvq_dim, code_dim] row-major (encode)
51+
std::vector<float> input_bias_; // [rvq_dim] (encode)
4052
};
4153

4254
} // namespace engine::models::moss_tts_local

0 commit comments

Comments
 (0)