| 369 | runtime::AudioBuffer slice_audio_buffer( |
| 370 | const runtime::AudioBuffer & audio, |
| 371 | const runtime::TimeSpan & span) { |
| 372 | if (audio.sample_rate <= 0) { |
| 373 | throw std::runtime_error("Audio chunker slice requires a positive sample rate"); |
| 374 | } |
| 375 | require_positive(audio.channels, "audio channels"); |
| 376 | if (audio.samples.empty()) { |
| 377 | throw std::runtime_error("Audio chunker slice requires non-empty audio"); |
| 378 | } |
| 379 | if (audio.samples.size() % static_cast<size_t>(audio.channels) != 0) { |
| 380 | throw std::runtime_error("Audio chunker slice input size is not divisible by channel count"); |
| 381 | } |
| 382 | const int64_t frames = static_cast<int64_t>(audio.samples.size() / static_cast<size_t>(audio.channels)); |
| 383 | require_valid_time_span(span, frames, "Audio chunker slice"); |
| 384 | |
| 385 | runtime::AudioBuffer out; |
| 386 | out.sample_rate = audio.sample_rate; |
| 387 | out.channels = audio.channels; |
| 388 | const size_t begin = static_cast<size_t>(span.start_sample * audio.channels); |
| 389 | const size_t end = static_cast<size_t>(span.end_sample * audio.channels); |
| 390 | out.samples.assign( |
| 391 | audio.samples.begin() + static_cast<std::ptrdiff_t>(begin), |
| 392 | audio.samples.begin() + static_cast<std::ptrdiff_t>(end)); |
| 393 | return out; |
| 394 | } |
| 395 | |
| 396 | std::vector<float> make_triangular_overlap_window(int64_t chunk_samples) { |
| 397 | require_positive(chunk_samples, "chunk samples"); |
| 398 | std::vector<float> window(static_cast<size_t>(chunk_samples), 1.0F); |
| 399 | const int64_t rising = chunk_samples / 2; |
| 400 | const int64_t falling = chunk_samples - rising; |
| 401 | for (int64_t i = 0; i < rising; ++i) { |