| 506 | const minitts::app::StreamEventSink & sink) { |
| 507 | // build_request_from_cli fills audio_input with the declared format and no samples, which is |
| 508 | // also what prepare() consumes as its audio contract. |
| 509 | if (!request.audio_input.has_value()) { |
| 510 | throw std::runtime_error("streaming from stdin requires an audio format contract"); |
| 511 | } |
| 512 | const auto sample_format = minitts::app::parse_pcm_sample_format( |
| 513 | minitts::cli::find_arg(argc, argv, "--input-format").value_or("s16le")); |
| 514 | const minitts::app::AudioStreamFormat format{ |
| 515 | request.audio_input->sample_rate, |
| 516 | request.audio_input->channels, |
| 517 | }; |
| 518 | // A headerless stream cannot be checked against a header, so report how it was interpreted. |
| 519 | std::cout << "audio_input=stdin format=" << minitts::app::to_string(sample_format) |
| 520 | << " rate=" << format.sample_rate |
| 521 | << " channels=" << format.channels << "\n" |
| 522 | << std::flush; |
| 523 | const auto stream = minitts::app::make_stdin_pcm_stream(format, sample_format); |
| 524 | return minitts::app::run_streaming_task(streaming, request, sink, stream); |
| 525 | } |
| 526 | |
| 527 | void run_streaming( |
| 528 | int argc, |
| 529 | char ** argv, |
| 530 | engine::runtime::IStreamingVoiceTaskSession & streaming, |
| 531 | engine::runtime::IVoiceTaskSession & session, |
| 532 | engine::runtime::TaskRequest & request) { |
| 533 | const auto out_dir = minitts::cli::optional_path_arg(argc, argv, "--out-dir"); |
| 534 | minitts::cli::PartialTextRenderer partial_renderer(stdout_is_terminal()); |
| 535 | const minitts::app::StreamEventSink sink = |
| 536 | [&](const engine::runtime::StreamEvent & event) { |
| 537 | if (event.partial_text.has_value()) { |
| 538 | // Flushed so a live source's partials appear as they are produced rather than |
| 539 | // when the stdio buffer happens to fill. |
| 540 | std::cout << partial_renderer.render(event.partial_text->text) << std::flush; |
| 541 | } |
| 542 | engine::runtime::TaskResult event_result; |
| 543 | event_result.audio_output = event.audio_output; |
| 544 | event_result.named_audio_outputs = event.named_audio_outputs; |
| 545 | event_result.speaker_turns = event.speaker_turns; |
| 546 | event_result.word_timestamps = event.word_timestamps; |
| 547 | event_result.output_artifacts = event.output_artifacts; |
| 548 | minitts::app::emit_task_result( |
| 549 | event_result, |
| 550 | std::nullopt, |
| 551 | out_dir, |
| 552 | out_dir, |
| 553 | std::nullopt, |
| 554 | std::nullopt, |
| 555 | std::nullopt); |
| 556 | for (const auto & activity : event.voice_activity) { |
| 557 | std::cout << "event="; |
| 558 | switch (activity.kind) { |
| 559 | case engine::runtime::VoiceActivityEvent::Kind::SpeechStart: |
| 560 | std::cout << "speech_start"; |
| 561 | break; |
| 562 | case engine::runtime::VoiceActivityEvent::Kind::SpeechEnd: |
| 563 | std::cout << "speech_end"; |
| 564 | break; |
| 565 | case engine::runtime::VoiceActivityEvent::Kind::SpeechSegment: |
nothing calls this directly
no test coverage detected