| 251 | } |
| 252 | |
| 253 | VibeVoiceTokenizerEncoderWeights load_encoder( |
| 254 | core::BackendWeightStore & store, |
| 255 | const assets::TensorSource & source, |
| 256 | const std::string & prefix, |
| 257 | const VibeVoiceTokenizerConfig & config, |
| 258 | assets::TensorStorageType weight_storage_type) { |
| 259 | validate_tokenizer_config(config, false); |
| 260 | auto depths = parse_depths(config.encoder_depths, "encoder_depths"); |
| 261 | auto ratios = config.encoder_ratios; |
| 262 | std::reverse(ratios.begin(), ratios.end()); |
| 263 | if (depths.size() != ratios.size() + 1) { |
| 264 | throw std::runtime_error("VibeVoice tokenizer encoder depths/ratios mismatch"); |
| 265 | } |
| 266 | |
| 267 | VibeVoiceTokenizerEncoderWeights weights; |
| 268 | weights.downsample_layers.reserve(depths.size()); |
| 269 | weights.downsample_layers.push_back(load_conv1d( |
| 270 | store, |
| 271 | source, |
| 272 | prefix + ".downsample_layers.0.0.conv.conv", |
| 273 | config.channels, |
| 274 | config.encoder_n_filters, |
| 275 | kTokenizerKernelSize, |
| 276 | weight_storage_type)); |
| 277 | for (size_t i = 0; i < ratios.size(); ++i) { |
| 278 | const int64_t in_ch = config.encoder_n_filters * (int64_t{1} << static_cast<int64_t>(i)); |
| 279 | const int64_t out_ch = config.encoder_n_filters * (int64_t{1} << static_cast<int64_t>(i + 1)); |
| 280 | weights.downsample_layers.push_back(load_conv1d( |
| 281 | store, |
| 282 | source, |
| 283 | prefix + ".downsample_layers." + std::to_string(i + 1) + ".0.conv.conv", |
| 284 | in_ch, |
| 285 | out_ch, |
| 286 | ratios[i] * 2, |
| 287 | weight_storage_type)); |
| 288 | } |
| 289 | |
| 290 | weights.stages.reserve(depths.size()); |
| 291 | for (size_t stage = 0; stage < depths.size(); ++stage) { |
| 292 | const int64_t channels = config.encoder_n_filters * (int64_t{1} << static_cast<int64_t>(stage)); |
| 293 | std::vector<VibeVoiceTokenizerBlockWeights> blocks; |
| 294 | blocks.reserve(static_cast<size_t>(depths[stage])); |
| 295 | for (int64_t block = 0; block < depths[stage]; ++block) { |
| 296 | blocks.push_back(load_block( |
| 297 | store, |
| 298 | source, |
| 299 | prefix + ".stages." + std::to_string(stage) + "." + std::to_string(block), |
| 300 | channels, |
| 301 | weight_storage_type)); |
| 302 | } |
| 303 | weights.stages.push_back(std::move(blocks)); |
| 304 | } |
| 305 | const int64_t final_channels = config.encoder_n_filters * (int64_t{1} << static_cast<int64_t>(depths.size() - 1)); |
| 306 | if (config.disable_last_norm) { |
| 307 | weights.norm = std::nullopt; |
| 308 | } else { |
| 309 | weights.norm = source.require_f32_tensor(prefix + ".norm.weight", {final_channels}); |
| 310 | } |
no test coverage detected