| 369 | } |
| 370 | |
| 371 | std::vector<float> preprocess_reference_mono( |
| 372 | std::vector<float> mono, |
| 373 | int sample_rate, |
| 374 | const OmniVoiceReferenceAudioOptions & options) { |
| 375 | if (!options.preprocess_prompt) { |
| 376 | return mono; |
| 377 | } |
| 378 | auto mono_pcm16 = engine::audio::float_to_pcm16_clipped( |
| 379 | mono, |
| 380 | engine::audio::Pcm16QuantizeMode::TruncateTowardZero); |
| 381 | if (!options.has_reference_text) { |
| 382 | const double duration = static_cast<double>(mono_pcm16.size()) / static_cast<double>(sample_rate); |
| 383 | if (duration > 20.0) { |
| 384 | auto nonsilent = detect_nonsilent_ranges_ms(mono_pcm16, sample_rate, 100, -40.0F, 10); |
| 385 | if (!nonsilent.empty()) { |
| 386 | const int64_t max_ms = 15000; |
| 387 | const int64_t min_ms = 3000; |
| 388 | int64_t best_split_ms = 0; |
| 389 | for (const auto & range : nonsilent) { |
| 390 | if (range.first > best_split_ms && range.first <= max_ms) { |
| 391 | best_split_ms = range.first; |
| 392 | } |
| 393 | if (range.second > max_ms) { |
| 394 | break; |
| 395 | } |
| 396 | } |
| 397 | if (best_split_ms < min_ms) { |
| 398 | best_split_ms = std::min<int64_t>(max_ms, samples_to_ms(mono_pcm16.size(), sample_rate)); |
| 399 | } |
| 400 | mono_pcm16 = slice_pcm16_ms(mono_pcm16, sample_rate, 0, best_split_ms); |
| 401 | } |
| 402 | } |
| 403 | } |
| 404 | mono_pcm16 = remove_mid_silence_pcm16(mono_pcm16, sample_rate, 200, 200, kSilenceThresholdDb); |
| 405 | mono_pcm16 = trim_edges_pcm16(mono_pcm16, sample_rate, 100, 200, kSilenceThresholdDb); |
| 406 | if (mono_pcm16.empty()) { |
| 407 | throw std::runtime_error( |
| 408 | "OmniVoice reference audio is empty after silence removal; try preprocess_prompt=false"); |
| 409 | } |
| 410 | return engine::audio::pcm16_to_float_unit_range(mono_pcm16); |
| 411 | } |
| 412 | |
| 413 | NormalizedReferenceAudio normalize_reference_audio( |
| 414 | const runtime::AudioBuffer & audio, |
no test coverage detected