| 320 | } |
| 321 | |
| 322 | VibeVoiceTokenizerDecoderWeights load_decoder( |
| 323 | core::BackendWeightStore & store, |
| 324 | const assets::TensorSource & source, |
| 325 | const std::string & prefix, |
| 326 | const VibeVoiceTokenizerConfig & config, |
| 327 | assets::TensorStorageType weight_storage_type) { |
| 328 | validate_tokenizer_config(config, true); |
| 329 | auto depths = decoder_depths(config); |
| 330 | const auto & ratios = config.decoder_ratios; |
| 331 | if (ratios.empty() || depths.size() != ratios.size() + 1) { |
| 332 | throw std::runtime_error("VibeVoice tokenizer decoder depths/ratios mismatch"); |
| 333 | } |
| 334 | |
| 335 | VibeVoiceTokenizerDecoderWeights weights; |
| 336 | const int64_t top_channels = config.decoder_n_filters * (int64_t{1} << static_cast<int64_t>(depths.size() - 1)); |
| 337 | weights.stem = load_conv1d( |
| 338 | store, |
| 339 | source, |
| 340 | prefix + ".upsample_layers.0.0.conv.conv", |
| 341 | config.vae_dim, |
| 342 | top_channels, |
| 343 | kTokenizerKernelSize, |
| 344 | weight_storage_type); |
| 345 | weights.upsample_layers.reserve(ratios.size()); |
| 346 | for (size_t i = 0; i < ratios.size(); ++i) { |
| 347 | const int64_t in_ch = config.decoder_n_filters * (int64_t{1} << static_cast<int64_t>(depths.size() - 1 - i)); |
| 348 | const int64_t out_ch = config.decoder_n_filters * (int64_t{1} << static_cast<int64_t>(depths.size() - 2 - i)); |
| 349 | weights.upsample_layers.push_back(load_conv_transpose1d( |
| 350 | store, |
| 351 | source, |
| 352 | prefix + ".upsample_layers." + std::to_string(i + 1) + ".0.convtr.convtr", |
| 353 | in_ch, |
| 354 | out_ch, |
| 355 | ratios[i] * 2, |
| 356 | weight_storage_type)); |
| 357 | } |
| 358 | |
| 359 | weights.stages.reserve(depths.size()); |
| 360 | for (size_t stage = 0; stage < depths.size(); ++stage) { |
| 361 | const int64_t channels = config.decoder_n_filters * (int64_t{1} << static_cast<int64_t>(depths.size() - 1 - stage)); |
| 362 | std::vector<VibeVoiceTokenizerBlockWeights> blocks; |
| 363 | blocks.reserve(static_cast<size_t>(depths[stage])); |
| 364 | for (int64_t block = 0; block < depths[stage]; ++block) { |
| 365 | blocks.push_back(load_block( |
| 366 | store, |
| 367 | source, |
| 368 | prefix + ".stages." + std::to_string(stage) + "." + std::to_string(block), |
| 369 | channels, |
| 370 | weight_storage_type)); |
| 371 | } |
| 372 | weights.stages.push_back(std::move(blocks)); |
| 373 | } |
| 374 | if (config.disable_last_norm) { |
| 375 | weights.norm = std::nullopt; |
| 376 | } else { |
| 377 | weights.norm = source.require_f32_tensor(prefix + ".norm.weight", {config.decoder_n_filters}); |
| 378 | } |
| 379 | weights.head = load_conv1d( |
no test coverage detected