@@ -28,6 +28,12 @@ const uint32_t TONE_FREQUENCY = 440;
2828const double OUTPUT_AMPLITUDE = 0.25 ;
2929const int32_t NUM_FRAMES_TO_OUTPUT =
3030 SAMPLE_FREQUENCY / 20 ; /* play ~50ms of samples */
31+ /* data_cb_playback() loops the tone long enough for delayed loopback capture,
32+ * then goes silent before render stop. The tests keep capturing after render
33+ * stop to consume queued virtual-device data. */
34+ const int32_t CONTINUOUS_TONE_FRAMES = SAMPLE_FREQUENCY / 2 ; /* ~500ms */
35+ const unsigned int LOOPBACK_PLAY_MS = 600 ;
36+ const unsigned int LOOPBACK_DRAIN_MS = 600 ;
3137
3238template <typename T>
3339T
@@ -266,11 +272,11 @@ data_cb_playback(cubeb_stream * stream, void * user, const void * inputbuffer,
266272 }
267273
268274 std::lock_guard<std::mutex> lock (u->user_state_mutex );
269- /* generate our test tone on the fly */
275+ /* Generate the tone long enough for loopback capture streams whose first
276+ * buffer can arrive late; find_phase() searches wherever it lands. */
270277 for (int i = 0 ; i < nframes; i++) {
271278 double tone = 0.0 ;
272- if (u->position + i < NUM_FRAMES_TO_OUTPUT ) {
273- /* generate sine wave */
279+ if (u->position + i < CONTINUOUS_TONE_FRAMES ) {
274280 tone =
275281 sin (2 * M_PI * (i + u->position ) * TONE_FREQUENCY / SAMPLE_FREQUENCY );
276282 tone *= OUTPUT_AMPLITUDE ;
@@ -452,11 +458,26 @@ run_loopback_separate_streams_test(bool is_float)
452458 std::unique_ptr<cubeb_stream, decltype (&cubeb_stream_destroy)>
453459 cleanup_output_stream_at_exit (output_stream, cubeb_stream_destroy);
454460
455- cubeb_stream_start (input_stream);
461+ // Start the render stream before the loopback capture stream, matching
462+ // the order cubeb_wasapi.cpp uses internally for a single duplex stream.
463+ // Some virtual audio drivers (e.g. VB-CABLE) only start relaying audio to
464+ // their loopback/capture side once a render client is active, and never
465+ // catch up if the capture side starts first.
456466 cubeb_stream_start (output_stream);
457- delay (300 );
467+ cubeb_stream_start (input_stream);
468+ // Give the capture side extra margin to receive its first buffer under
469+ // slow/instrumented builds (e.g. ASAN), on top of the continuous tone above.
470+ delay (LOOPBACK_PLAY_MS );
458471 cubeb_stream_stop (output_stream);
472+ // Keep capturing after render stops, so a virtual loopback device can
473+ // deliver any queued render data to this test instead of the next one.
474+ delay (LOOPBACK_DRAIN_MS );
459475 cubeb_stream_stop (input_stream);
476+ // Destroy the streams (and the context) right away instead of waiting
477+ // for the RAII cleanups below to do it when this function returns.
478+ cleanup_output_stream_at_exit.reset ();
479+ cleanup_input_stream_at_exit.reset ();
480+ cleanup_cubeb_at_exit.reset ();
460481
461482 /* access after stop should not happen, but lock just in case and to appease
462483 * sanitization tools */
@@ -641,11 +662,26 @@ run_loopback_device_selection_test(bool is_float)
641662 std::unique_ptr<cubeb_stream, decltype (&cubeb_stream_destroy)>
642663 cleanup_output_stream_at_exit (output_stream, cubeb_stream_destroy);
643664
644- cubeb_stream_start (input_stream);
665+ // Start the render stream before the loopback capture stream, matching
666+ // the order cubeb_wasapi.cpp uses internally for a single duplex stream.
667+ // Some virtual audio drivers (e.g. VB-CABLE) only start relaying audio to
668+ // their loopback/capture side once a render client is active, and never
669+ // catch up if the capture side starts first.
645670 cubeb_stream_start (output_stream);
646- delay (300 );
671+ cubeb_stream_start (input_stream);
672+ // Give the capture side extra margin to receive its first buffer under
673+ // slow/instrumented builds (e.g. ASAN), on top of the continuous tone above.
674+ delay (LOOPBACK_PLAY_MS );
647675 cubeb_stream_stop (output_stream);
676+ // Keep capturing after render stops, so a virtual loopback device can
677+ // deliver any queued render data to this test instead of the next one.
678+ delay (LOOPBACK_DRAIN_MS );
648679 cubeb_stream_stop (input_stream);
680+ // Destroy the streams (and the context) right away instead of waiting
681+ // for the RAII cleanups below to do it when this function returns.
682+ cleanup_output_stream_at_exit.reset ();
683+ cleanup_input_stream_at_exit.reset ();
684+ cleanup_cubeb_at_exit.reset ();
649685
650686 /* access after stop should not happen, but lock just in case and to appease
651687 * sanitization tools */
0 commit comments