Bug 1337328 - Uplift cubeb to revision 927877. r?achronop draft
authorPaul Adenot <paul@paul.cx>
Tue, 07 Feb 2017 12:13:30 +0100
changeset 479840 41dc4e72ce41b632e727bb9ebcc118750181bb63
parent 479358 d81f216e7c037bc8a5ca0c7f6f37ecb44271881b
child 544805 bb1a0bb455639763e88b2691454adc03b62500b7
push id44383
push userpaul@paul.cx
push dateTue, 07 Feb 2017 11:14:36 +0000
reviewersachronop
bugs1337328, 927877
milestone54.0a1
Bug 1337328 - Uplift cubeb to revision 927877. r?achronop MozReview-Commit-ID: 1VxCdWZQtR4
media/libcubeb/README_MOZILLA
media/libcubeb/gtest/common.h
media/libcubeb/gtest/test_audio.cpp
media/libcubeb/gtest/test_duplex.cpp
media/libcubeb/gtest/test_record.cpp
media/libcubeb/gtest/test_resampler.cpp
media/libcubeb/src/cubeb-internal.h
media/libcubeb/src/cubeb.c
media/libcubeb/src/cubeb_assert.h
media/libcubeb/src/cubeb_pulse.c
media/libcubeb/src/cubeb_resampler.cpp
media/libcubeb/src/cubeb_resampler_internal.h
media/libcubeb/src/cubeb_sndio.c
media/libcubeb/src/cubeb_wasapi.cpp
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb 
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was d96e35f02dbb9a093e5bfdff4f2948b7a6e9d3f9.
+The git commit ID used was 927877c3204d6b8467b6dc782ca2aa740d240d41.
--- a/media/libcubeb/gtest/common.h
+++ b/media/libcubeb/gtest/common.h
@@ -11,16 +11,19 @@
 #ifndef WIN32_LEAN_AND_MEAN
 #define WIN32_LEAN_AND_MEAN
 #endif
 #include <windows.h>
 #else
 #include <unistd.h>
 #endif
 
+#include <cstdarg>
+#include "cubeb/cubeb.h"
+
 template<typename T, size_t N>
 constexpr size_t
 ARRAY_LENGTH(T(&)[N])
 {
   return N;
 }
 
 void delay(unsigned int ms)
@@ -91,9 +94,17 @@ int has_available_input_device(cubeb * c
     fprintf(stderr, "there are input devices, but they are not "
         "available, skipping\n");
     return 0;
   }
 
   return 1;
 }
 
+void print_log(const char * msg, ...)
+{
+  va_list args;
+  va_start(args, msg);
+  vprintf(msg, args);
+  va_end(args);
+}
+
 #endif /* TEST_COMMON */
--- a/media/libcubeb/gtest/test_audio.cpp
+++ b/media/libcubeb/gtest/test_audio.cpp
@@ -12,214 +12,201 @@
 #define _XOPEN_SOURCE 600
 #endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
 #include <string.h>
 #include "cubeb/cubeb.h"
 #include "common.h"
+#include <string>
+
+using namespace std;
 
 #define MAX_NUM_CHANNELS 32
 
 #if !defined(M_PI)
 #define M_PI 3.14159265358979323846
 #endif
 
 #define VOLUME 0.2
 
 float get_frequency(int channel_index)
 {
   return 220.0f * (channel_index+1);
 }
 
+template<typename T> T ConvertSample(double input);
+template<> float ConvertSample(double input) { return input; }
+template<> short ConvertSample(double input) { return short(input * 32767.0f); }
+
 /* store the phase of the generated waveform */
-typedef struct {
+struct synth_state {
+  synth_state(int num_channels_, float sample_rate_)
+    : num_channels(num_channels_),
+    sample_rate(sample_rate_)
+  {
+    for(int i=0;i < MAX_NUM_CHANNELS;++i)
+      phase[i] = 0.0f;
+  }
+
+  template<typename T>
+    void run(T* audiobuffer, long nframes)
+    {
+      for(int c=0;c < num_channels;++c) {
+        float freq = get_frequency(c);
+        float phase_inc = 2.0 * M_PI * freq / sample_rate;
+        for(long n=0;n < nframes;++n) {
+          audiobuffer[n*num_channels+c] = ConvertSample<T>(sin(phase[c]) * VOLUME);
+          phase[c] += phase_inc;
+        }
+      }
+    }
+
+private:
   int num_channels;
   float phase[MAX_NUM_CHANNELS];
   float sample_rate;
-} synth_state;
-
-synth_state* synth_create(int num_channels, float sample_rate)
-{
-  synth_state* synth = (synth_state *) malloc(sizeof(synth_state));
-  if (!synth)
-    return NULL;
-  for(int i=0;i < MAX_NUM_CHANNELS;++i)
-    synth->phase[i] = 0.0f;
-  synth->num_channels = num_channels;
-  synth->sample_rate = sample_rate;
-  return synth;
-}
+};
 
-void synth_destroy(synth_state* synth)
-{
-  free(synth);
-}
-
-void synth_run_float(synth_state* synth, float* audiobuffer, long nframes)
-{
-  for(int c=0;c < synth->num_channels;++c) {
-    float freq = get_frequency(c);
-    float phase_inc = 2.0 * M_PI * freq / synth->sample_rate;
-    for(long n=0;n < nframes;++n) {
-      audiobuffer[n*synth->num_channels+c] = sin(synth->phase[c]) * VOLUME;
-      synth->phase[c] += phase_inc;
-    }
-  }
-}
-
-long data_cb_float(cubeb_stream * /*stream*/, void * user, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
+template<typename T>
+long data_cb(cubeb_stream * /*stream*/, void * user, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
 {
   synth_state *synth = (synth_state *)user;
-  synth_run_float(synth, (float*)outputbuffer, nframes);
+  synth->run((T*)outputbuffer, nframes);
   return nframes;
 }
 
-void synth_run_16bit(synth_state* synth, short* audiobuffer, long nframes)
+struct CubebCleaner
 {
-  for(int c=0;c < synth->num_channels;++c) {
-    float freq = get_frequency(c);
-    float phase_inc = 2.0 * M_PI * freq / synth->sample_rate;
-    for(long n=0;n < nframes;++n) {
-      audiobuffer[n*synth->num_channels+c] = sin(synth->phase[c]) * VOLUME * 32767.0f;
-      synth->phase[c] += phase_inc;
-    }
-  }
-}
+  CubebCleaner(cubeb* ctx_) : ctx(ctx_) {}
+  ~CubebCleaner() { cubeb_destroy(ctx); }
+  cubeb* ctx;
+};
 
-long data_cb_short(cubeb_stream * /*stream*/, void * user, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
+struct CubebStreamCleaner
 {
-  synth_state *synth = (synth_state *)user;
-  synth_run_16bit(synth, (short*)outputbuffer, nframes);
-  return nframes;
-}
+  CubebStreamCleaner(cubeb_stream* ctx_) : ctx(ctx_) {}
+  ~CubebStreamCleaner() { cubeb_stream_destroy(ctx); }
+  cubeb_stream* ctx;
+};
 
 void state_cb_audio(cubeb_stream * /*stream*/, void * /*user*/, cubeb_state /*state*/)
 {
 }
 
 /* Our android backends don't support float, only int16. */
-int supports_float32(const char* backend_id)
+int supports_float32(string backend_id)
 {
-  return (strcmp(backend_id, "opensl") != 0 &&
-          strcmp(backend_id, "audiotrack") != 0);
+  return backend_id != "opensl"
+    && backend_id != "audiotrack";
 }
 
 /* The WASAPI backend only supports float. */
-int supports_int16(const char* backend_id)
+int supports_int16(string backend_id)
 {
-  return strcmp(backend_id, "wasapi") != 0;
+  return backend_id != "wasapi";
 }
 
 /* Some backends don't have code to deal with more than mono or stereo. */
-int supports_channel_count(const char* backend_id, int nchannels)
+int supports_channel_count(string backend_id, int nchannels)
 {
   return nchannels <= 2 ||
-    (strcmp(backend_id, "opensl") != 0 && strcmp(backend_id, "audiotrack") != 0);
+    (backend_id != "opensl" && backend_id != "audiotrack");
 }
 
 int run_test(int num_channels, layout_info layout, int sampling_rate, int is_float)
 {
   int r = CUBEB_OK;
 
   cubeb *ctx = NULL;
-  synth_state* synth = NULL;
-  cubeb_stream *stream = NULL;
-  const char * backend_id = NULL;
 
   r = cubeb_init(&ctx, "Cubeb audio test: channels");
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
-    goto cleanup;
+    return r;
   }
+  CubebCleaner cleanup_cubeb_at_exit(ctx);
 
-  backend_id = cubeb_get_backend_id(ctx);
+  const char * backend_id = cubeb_get_backend_id(ctx);
 
   if ((is_float && !supports_float32(backend_id)) ||
       (!is_float && !supports_int16(backend_id)) ||
       !supports_channel_count(backend_id, num_channels)) {
     /* don't treat this as a test failure. */
-    goto cleanup;
+    return CUBEB_OK;
   }
 
   fprintf(stderr, "Testing %d channel(s), layout: %s, %d Hz, %s (%s)\n", num_channels, layout.name, sampling_rate, is_float ? "float" : "short", cubeb_get_backend_id(ctx));
 
   cubeb_stream_params params;
   params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16NE;
   params.rate = sampling_rate;
   params.channels = num_channels;
   params.layout = layout.layout;
 
-  synth = synth_create(params.channels, params.rate);
-  if (synth == NULL) {
-    fprintf(stderr, "Out of memory\n");
-    goto cleanup;
+  synth_state synth(params.channels, params.rate);
+
+  cubeb_stream *stream = NULL;
+  r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
+      4096, is_float ? &data_cb<float> : &data_cb<short>, state_cb_audio, &synth);
+  if (r != CUBEB_OK) {
+    fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
+    return r;
   }
 
-  r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
-                        4096, is_float ? data_cb_float : data_cb_short, state_cb_audio, synth);
-  if (r != CUBEB_OK) {
-    fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
-    goto cleanup;
-  }
+  CubebStreamCleaner cleanup_stream_at_exit(stream);
 
   cubeb_stream_start(stream);
   delay(200);
   cubeb_stream_stop(stream);
 
-cleanup:
-  cubeb_stream_destroy(stream);
-  cubeb_destroy(ctx);
-  synth_destroy(synth);
-
   return r;
 }
 
 int run_panning_volume_test(int is_float)
 {
   int r = CUBEB_OK;
 
   cubeb *ctx = NULL;
-  synth_state* synth = NULL;
-  cubeb_stream *stream = NULL;
-  const char * backend_id = NULL;
 
   r = cubeb_init(&ctx, "Cubeb audio test");
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
-    goto cleanup;
+    return r;
   }
-  backend_id = cubeb_get_backend_id(ctx);
+
+  CubebCleaner cleanup_cubeb_at_exit(ctx);
+
+  const char * backend_id = cubeb_get_backend_id(ctx);
 
   if ((is_float && !supports_float32(backend_id)) ||
       (!is_float && !supports_int16(backend_id))) {
     /* don't treat this as a test failure. */
-    goto cleanup;
+    return CUBEB_OK;
   }
 
   cubeb_stream_params params;
   params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16NE;
   params.rate = 44100;
   params.channels = 2;
   params.layout = CUBEB_LAYOUT_STEREO;
 
-  synth = synth_create(params.channels, params.rate);
-  if (synth == NULL) {
-    fprintf(stderr, "Out of memory\n");
-    goto cleanup;
+  synth_state synth(params.channels, params.rate);
+
+  cubeb_stream *stream = NULL;
+  r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
+      4096, is_float ? &data_cb<float> : &data_cb<short>,
+      state_cb_audio, &synth);
+  if (r != CUBEB_OK) {
+    fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
+    return r;
   }
 
-  r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
-                        4096, is_float ? data_cb_float : data_cb_short,
-                        state_cb_audio, synth);
-  if (r != CUBEB_OK) {
-    fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
-    goto cleanup;
-  }
+  CubebStreamCleaner cleanup_stream_at_exit(stream);
 
   fprintf(stderr, "Testing: volume\n");
   for(int i=0;i <= 4; ++i)
   {
     fprintf(stderr, "Volume: %d%%\n", i*25);
 
     cubeb_stream_set_volume(stream, i/4.0f);
     cubeb_stream_start(stream);
@@ -235,21 +222,16 @@ int run_panning_volume_test(int is_float
 
     cubeb_stream_set_panning(stream, i/4.0f);
     cubeb_stream_start(stream);
     delay(400);
     cubeb_stream_stop(stream);
     delay(100);
   }
 
-cleanup:
-  cubeb_stream_destroy(stream);
-  cubeb_destroy(ctx);
-  synth_destroy(synth);
-
   return r;
 }
 
 TEST(cubeb, run_panning_volume_test_short)
 {
   ASSERT_EQ(run_panning_volume_test(0), CUBEB_OK);
 }
 
@@ -270,21 +252,21 @@ TEST(cubeb, run_channel_rate_test)
 
   int freq_values[] = {
     16000,
     24000,
     44100,
     48000,
   };
 
-  for(unsigned int j = 0; j < ARRAY_LENGTH(channel_values); ++j) {
-    for(unsigned int i = 0; i < ARRAY_LENGTH(freq_values); ++i) {
-      ASSERT_TRUE(channel_values[j] < MAX_NUM_CHANNELS);
+  for(auto channels : channel_values) {
+    for(auto freq : freq_values) {
+      ASSERT_TRUE(channels < MAX_NUM_CHANNELS);
       fprintf(stderr, "--------------------------\n");
-      for (unsigned int k = 0 ; k < ARRAY_LENGTH(layout_infos); ++k ) {
-        if (layout_infos[k].channels == channel_values[j]) {
-          ASSERT_EQ(run_test(channel_values[j], layout_infos[k], freq_values[i], 0), CUBEB_OK);
-          ASSERT_EQ(run_test(channel_values[j], layout_infos[k], freq_values[i], 1), CUBEB_OK);
+      for (auto layout : layout_infos) {
+        if (layout.channels == channels) {
+          ASSERT_EQ(run_test(channels, layout, freq, 0), CUBEB_OK);
+          ASSERT_EQ(run_test(channels, layout, freq, 1), CUBEB_OK);
         }
       }
     }
   }
 }
--- a/media/libcubeb/gtest/test_duplex.cpp
+++ b/media/libcubeb/gtest/test_duplex.cpp
@@ -13,57 +13,47 @@
 #endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
 #include "cubeb/cubeb.h"
 #include "common.h"
 
 #define SAMPLE_FREQUENCY 48000
-#if (defined(_WIN32) || defined(__WIN32__))
 #define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
-#define SILENT_SAMPLE 0.0f
-#else
-#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
-#define SILENT_SAMPLE 0
-#endif
 
 struct user_state_duplex
 {
-  bool seen_noise;
+  bool seen_audio;
 };
 
 long data_cb_duplex(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
 {
   user_state_duplex * u = reinterpret_cast<user_state_duplex*>(user);
-#if (defined(_WIN32) || defined(__WIN32__))
   float *ib = (float *)inputbuffer;
   float *ob = (float *)outputbuffer;
-#else
-  short *ib = (short *)inputbuffer;
-  short *ob = (short *)outputbuffer;
-#endif
-  bool seen_noise = false;
+  bool seen_audio = true;
 
   if (stream == NULL || inputbuffer == NULL || outputbuffer == NULL) {
     return CUBEB_ERROR;
   }
 
   // Loop back: upmix the single input channel to the two output channels,
   // checking if there is noise in the process.
   long output_index = 0;
   for (long i = 0; i < nframes; i++) {
-    if (ib[i] != SILENT_SAMPLE) {
-      seen_noise = true;
+    if (ib[i] <= -1.0 && ib[i] >= 1.0) {
+      seen_audio = false;
+      break;
     }
     ob[output_index] = ob[output_index + 1] = ib[i];
     output_index += 2;
   }
 
-  u->seen_noise |= seen_noise;
+  u->seen_audio |= seen_audio;
 
   return nframes;
 }
 
 void state_cb_duplex(cubeb_stream * stream, void * /*user*/, cubeb_state state)
 {
   if (stream == NULL)
     return;
@@ -131,10 +121,10 @@ TEST(cubeb, duplex)
 
   cubeb_stream_start(stream);
   delay(500);
   cubeb_stream_stop(stream);
 
   cubeb_stream_destroy(stream);
   cubeb_destroy(ctx);
 
-  ASSERT_TRUE(stream_state.seen_noise);
+  ASSERT_TRUE(stream_state.seen_audio);
 }
--- a/media/libcubeb/gtest/test_record.cpp
+++ b/media/libcubeb/gtest/test_record.cpp
@@ -12,48 +12,41 @@
 #endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
 #include "cubeb/cubeb.h"
 #include "common.h"
 
 #define SAMPLE_FREQUENCY 48000
-#if (defined(_WIN32) || defined(__WIN32__))
 #define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
-#else
-#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
-#endif
 
 struct user_state_record
 {
-  bool seen_noise;
+  bool seen_audio;
 };
 
 long data_cb_record(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
 {
   user_state_record * u = reinterpret_cast<user_state_record*>(user);
-#if STREAM_FORMAT != CUBEB_SAMPLE_FLOAT32LE
-  short *b = (short *)inputbuffer;
-#else
   float *b = (float *)inputbuffer;
-#endif
 
   if (stream == NULL  || inputbuffer == NULL || outputbuffer != NULL) {
     return CUBEB_ERROR;
   }
 
-  bool seen_noise = false;
+  bool seen_audio = true;
   for (long i = 0; i < nframes; i++) {
-    if (b[i] != 0.0) {
-      seen_noise = true;
+    if (b[i] <= -1.0 && b[i] >= 1.0) {
+      seen_audio = false;
+      break;
     }
   }
 
-  u->seen_noise |= seen_noise;
+  u->seen_audio |= seen_audio;
 
   return nframes;
 }
 
 void state_cb_record(cubeb_stream * stream, void * /*user*/, cubeb_state state)
 {
   if (stream == NULL)
     return;
@@ -69,16 +62,19 @@ void state_cb_record(cubeb_stream * stre
     printf("unknown stream state %d\n", state);
   }
 
   return;
 }
 
 TEST(cubeb, record)
 {
+  if (cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr /*print_log*/) != CUBEB_OK) {
+    printf("Set log callback failed\n");
+  }
   cubeb *ctx;
   cubeb_stream *stream;
   cubeb_stream_params params;
   int r;
   user_state_record stream_state = { false };
 
   r = cubeb_init(&ctx, "Cubeb record example");
   if (r != CUBEB_OK) {
@@ -106,10 +102,15 @@ TEST(cubeb, record)
 
   cubeb_stream_start(stream);
   delay(500);
   cubeb_stream_stop(stream);
 
   cubeb_stream_destroy(stream);
   cubeb_destroy(ctx);
 
-  ASSERT_TRUE(stream_state.seen_noise);
+#ifdef __linux__
+  // user callback does not arrive in Linux, silence the error
+  printf("Check is disabled in Linux\n");
+#else
+  ASSERT_TRUE(stream_state.seen_audio);
+#endif
 }
--- a/media/libcubeb/gtest/test_resampler.cpp
+++ b/media/libcubeb/gtest/test_resampler.cpp
@@ -3,16 +3,17 @@
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 #ifndef NOMINMAX
 #define NOMINMAX
 #endif // NOMINMAX
 #include "gtest/gtest.h"
+#include "common.h"
 #include "cubeb_resampler_internal.h"
 #include <stdio.h>
 #include <algorithm>
 #include <iostream>
 
 /* Windows cmath USE_MATH_DEFINE thing... */
 const float PI = 3.14159265359f;
 
@@ -386,18 +387,21 @@ void test_resampler_duplex(uint32_t inpu
     state.output.push(output_buffer.data(), got * state.output_channels);
   }
 
   dump("input_expected.raw", expected_resampled_input.data(), expected_resampled_input.length());
   dump("output_expected.raw", expected_resampled_output.data(), expected_resampled_output.length());
   dump("input.raw", state.input.data(), state.input.length());
   dump("output.raw", state.output.data(), state.output.length());
 
-  ASSERT_TRUE(array_fuzzy_equal(state.input, expected_resampled_input, epsilon<T>(input_rate/target_rate)));
-  ASSERT_TRUE(array_fuzzy_equal(state.output, expected_resampled_output, epsilon<T>(output_rate/target_rate)));
+ // This is disabled because the latency estimation in the resampler code is
+ // slightly off so we can generate expected vectors.
+ // See https://github.com/kinetiknz/cubeb/issues/93
+ // ASSERT_TRUE(array_fuzzy_equal(state.input, expected_resampled_input, epsilon<T>(input_rate/target_rate)));
+ // ASSERT_TRUE(array_fuzzy_equal(state.output, expected_resampled_output, epsilon<T>(output_rate/target_rate)));
 
   cubeb_resampler_destroy(resampler);
 }
 
 #define array_size(x) (sizeof(x) / sizeof(x[0]))
 
 TEST(cubeb, resampler_one_way)
 {
@@ -411,19 +415,16 @@ TEST(cubeb, resampler_one_way)
           test_resampler_one_way<float>(channels, sample_rates[source_rate],
                                         sample_rates[dest_rate], chunk_duration);
         }
       }
     }
   }
 }
 
-// This is disabled because the latency estimation in the resampler code is
-// slightly off so we can generate expected vectors.
-// See https://github.com/kinetiknz/cubeb/issues/93
 TEST(cubeb, DISABLED_resampler_duplex)
 {
   for (uint32_t input_channels = 1; input_channels <= max_channels; input_channels++) {
     for (uint32_t output_channels = 1; output_channels <= max_channels; output_channels++) {
       for (uint32_t source_rate_input = 0; source_rate_input < array_size(sample_rates); source_rate_input++) {
         for (uint32_t source_rate_output = 0; source_rate_output < array_size(sample_rates); source_rate_output++) {
           for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates); dest_rate++) {
             for (uint32_t chunk_duration = min_chunks; chunk_duration < max_chunks; chunk_duration+=chunk_increment) {
@@ -531,8 +532,219 @@ TEST(cubeb, resampler_drain)
 
   /* If the above is not an infinite loop, the drain was a success, just mark
    * this test as such. */
   ASSERT_TRUE(true);
 
   cubeb_resampler_destroy(resampler);
 }
 
+// gtest does not support using ASSERT_EQ and friend in a function that returns
+// a value.
+void check_output(const void * input_buffer, void * output_buffer, long frame_count)
+{
+  ASSERT_EQ(input_buffer, nullptr);
+  ASSERT_EQ(frame_count, 256);
+  ASSERT_TRUE(!!output_buffer);
+}
+
+long cb_passthrough_resampler_output(cubeb_stream * /*stm*/, void * /*user_ptr*/,
+                                     const void * input_buffer,
+                                     void * output_buffer, long frame_count)
+{
+  check_output(input_buffer, output_buffer, frame_count);
+  return frame_count;
+}
+
+TEST(cubeb, resampler_passthrough_output_only)
+{
+  // Test that the passthrough resampler works when there is only an output stream.
+  cubeb_stream_params output_params;
+
+  const size_t output_channels = 2;
+  output_params.channels = output_channels;
+  output_params.rate = 44100;
+  output_params.format = CUBEB_SAMPLE_FLOAT32NE;
+  int target_rate = output_params.rate;
+
+  cubeb_resampler * resampler =
+    cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params,
+                           target_rate, cb_passthrough_resampler_output, nullptr,
+                           CUBEB_RESAMPLER_QUALITY_VOIP);
+
+  float output_buffer[output_channels * 256];
+
+  long got;
+  for (uint32_t i = 0; i < 30; i++) {
+    got = cubeb_resampler_fill(resampler, nullptr, nullptr, output_buffer, 256);
+    ASSERT_EQ(got, 256);
+  }
+}
+
+// gtest does not support using ASSERT_EQ and friend in a function that returns
+// a value.
+void check_input(const void * input_buffer, void * output_buffer, long frame_count)
+{
+  ASSERT_EQ(output_buffer, nullptr);
+  ASSERT_EQ(frame_count, 256);
+  ASSERT_TRUE(!!input_buffer);
+}
+
+long cb_passthrough_resampler_input(cubeb_stream * /*stm*/, void * /*user_ptr*/,
+                                    const void * input_buffer,
+                                    void * output_buffer, long frame_count)
+{
+  check_input(input_buffer, output_buffer, frame_count);
+  return frame_count;
+}
+
+TEST(cubeb, resampler_passthrough_input_only)
+{
+  // Test that the passthrough resampler works when there is only an output stream.
+  cubeb_stream_params input_params;
+
+  const size_t input_channels = 2;
+  input_params.channels = input_channels;
+  input_params.rate = 44100;
+  input_params.format = CUBEB_SAMPLE_FLOAT32NE;
+  int target_rate = input_params.rate;
+
+  cubeb_resampler * resampler =
+    cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, nullptr,
+                           target_rate, cb_passthrough_resampler_input, nullptr,
+                           CUBEB_RESAMPLER_QUALITY_VOIP);
+
+  float input_buffer[input_channels * 256];
+
+  long got;
+  for (uint32_t i = 0; i < 30; i++) {
+    long int frames = 256;
+    got = cubeb_resampler_fill(resampler, input_buffer, &frames, nullptr, 0);
+    ASSERT_EQ(got, 256);
+  }
+}
+
+template<typename T>
+long seq(T* array, int stride, long start, long count)
+{
+  for(int i = 0; i < count; i++) {
+    for (int j = 0; j < stride; j++) {
+      array[i + j] = static_cast<T>(start + i);
+    }
+  }
+  return start + count;
+}
+
+template<typename T>
+void is_seq(T * array, int stride, long count, long expected_start)
+{
+  uint32_t output_index = 0;
+  for (long i = 0; i < count; i++) {
+    for (int j = 0; j < stride; j++) {
+      ASSERT_EQ(array[output_index + j], expected_start + i);
+    }
+    output_index += stride;
+  }
+}
+
+// gtest does not support using ASSERT_EQ and friend in a function that returns
+// a value.
+template<typename T>
+void check_duplex(const T * input_buffer,
+                  T * output_buffer, long frame_count)
+{
+  ASSERT_EQ(frame_count, 256);
+  // Silence scan-build warning.
+  ASSERT_TRUE(!!output_buffer); assert(output_buffer);
+  ASSERT_TRUE(!!input_buffer); assert(input_buffer);
+
+  int output_index = 0;
+  for (int i = 0; i < frame_count; i++) {
+    // output is two channels, input is one channel, we upmix.
+    output_buffer[output_index] = output_buffer[output_index+1] = input_buffer[i];
+    output_index += 2;
+  }
+}
+
+long cb_passthrough_resampler_duplex(cubeb_stream * /*stm*/, void * /*user_ptr*/,
+                                     const void * input_buffer,
+                                     void * output_buffer, long frame_count)
+{
+  check_duplex<float>(static_cast<const float*>(input_buffer), static_cast<float*>(output_buffer), frame_count);
+  return frame_count;
+}
+
+
+TEST(cubeb, resampler_passthrough_duplex_callback_reordering)
+{
+  // Test that when pre-buffering on resampler creation, we can survive an input
+  // callback being delayed.
+
+  cubeb_stream_params input_params;
+  cubeb_stream_params output_params;
+
+  const int input_channels = 1;
+  const int output_channels = 2;
+
+  input_params.channels = input_channels;
+  input_params.rate = 44100;
+  input_params.format = CUBEB_SAMPLE_FLOAT32NE;
+
+  output_params.channels = output_channels;
+  output_params.rate = input_params.rate;
+  output_params.format = CUBEB_SAMPLE_FLOAT32NE;
+
+  int target_rate = input_params.rate;
+
+  cubeb_resampler * resampler =
+    cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params,
+                           target_rate, cb_passthrough_resampler_duplex, nullptr,
+                           CUBEB_RESAMPLER_QUALITY_VOIP);
+
+  const long BUF_BASE_SIZE = 256;
+  float input_buffer_prebuffer[input_channels * BUF_BASE_SIZE * 2];
+  float input_buffer_glitch[input_channels * BUF_BASE_SIZE * 2];
+  float input_buffer_normal[input_channels * BUF_BASE_SIZE];
+  float output_buffer[output_channels * BUF_BASE_SIZE];
+
+  long seq_idx = 0;
+  long output_seq_idx = 0;
+
+  long prebuffer_frames = ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels;
+  seq_idx = seq(input_buffer_prebuffer, input_channels, seq_idx,
+                prebuffer_frames);
+
+  long got = cubeb_resampler_fill(resampler, input_buffer_prebuffer, &prebuffer_frames,
+                                  output_buffer, BUF_BASE_SIZE);
+
+  output_seq_idx += BUF_BASE_SIZE;
+
+  ASSERT_EQ(prebuffer_frames, static_cast<long>(ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels));
+  ASSERT_EQ(got, BUF_BASE_SIZE);
+
+  for (uint32_t i = 0; i < 300; i++) {
+    long int frames = BUF_BASE_SIZE;
+    // Simulate that sometimes, we don't have the input callback on time
+    if (i != 0 && (i % 100) == 0) {
+      long zero = 0;
+      got = cubeb_resampler_fill(resampler, input_buffer_normal /* unused here */,
+                                 &zero, output_buffer, BUF_BASE_SIZE);
+      is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
+      output_seq_idx += BUF_BASE_SIZE;
+    } else if (i != 0 && (i % 100) == 1) {
+      // if this is the case, the on the next iteration, we'll have twice the
+      // amount of input frames
+      seq_idx = seq(input_buffer_glitch, input_channels, seq_idx, BUF_BASE_SIZE * 2);
+      frames = 2 * BUF_BASE_SIZE;
+      got = cubeb_resampler_fill(resampler, input_buffer_glitch, &frames, output_buffer, BUF_BASE_SIZE);
+      is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
+      output_seq_idx += BUF_BASE_SIZE;
+    } else {
+       // normal case
+      seq_idx = seq(input_buffer_normal, input_channels, seq_idx, BUF_BASE_SIZE);
+      long normal_input_frame_count = 256;
+      got = cubeb_resampler_fill(resampler, input_buffer_normal, &normal_input_frame_count, output_buffer, BUF_BASE_SIZE);
+      is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
+      output_seq_idx += BUF_BASE_SIZE;
+    }
+    ASSERT_EQ(got, BUF_BASE_SIZE);
+  }
+}
--- a/media/libcubeb/src/cubeb-internal.h
+++ b/media/libcubeb/src/cubeb-internal.h
@@ -4,16 +4,17 @@
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 #if !defined(CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5)
 #define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
 
 #include "cubeb/cubeb.h"
 #include "cubeb_log.h"
+#include "cubeb_assert.h"
 #include <stdio.h>
 #include <string.h>
 
 #ifdef __clang__
 #ifndef CLANG_ANALYZER_NORETURN
 #if __has_feature(attribute_analyzer_noreturn)
 #define CLANG_ANALYZER_NORETURN __attribute__((analyzer_noreturn))
 #else
@@ -23,19 +24,16 @@
 #else // __clang__
 #define CLANG_ANALYZER_NORETURN
 #endif
 
 #if defined(__cplusplus)
 extern "C" {
 #endif
 
-/* Crash the caller.  */
-void cubeb_crash() CLANG_ANALYZER_NORETURN;
-
 #if defined(__cplusplus)
 }
 #endif
 
 typedef struct {
   char const * name;
   unsigned int const channels;
   cubeb_channel_layout const layout;
@@ -80,16 +78,9 @@ struct cubeb_ops {
   int (* stream_register_device_changed_callback)(cubeb_stream * stream,
                                                   cubeb_device_changed_callback device_changed_callback);
   int (* register_device_collection_changed)(cubeb * context,
                                              cubeb_device_type devtype,
                                              cubeb_device_collection_changed_callback callback,
                                              void * user_ptr);
 };
 
-#define XASSERT(expr) do {                                                     \
-    if (!(expr)) {                                                             \
-      fprintf(stderr, "%s:%d - fatal error: %s\n", __FILE__, __LINE__, #expr); \
-      cubeb_crash();                                                           \
-    }                                                                          \
-  } while (0)
-
 #endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */
--- a/media/libcubeb/src/cubeb.c
+++ b/media/libcubeb/src/cubeb.c
@@ -103,22 +103,22 @@ validate_latency(int latency)
   }
   return CUBEB_OK;
 }
 
 int
 cubeb_init(cubeb ** context, char const * context_name)
 {
   int (* init[])(cubeb **, char const *) = {
+#if defined(USE_PULSE)
+    pulse_init,
+#endif
 #if defined(USE_JACK)
     jack_init,
 #endif
-#if defined(USE_PULSE)
-    pulse_init,
-#endif
 #if defined(USE_ALSA)
     alsa_init,
 #endif
 #if defined(USE_AUDIOUNIT)
     audiounit_init,
 #endif
 #if defined(USE_WASAPI)
     wasapi_init,
@@ -565,14 +565,8 @@ int cubeb_set_log_callback(cubeb_log_lev
   }
 
   g_log_callback = log_callback;
   g_log_level = log_level;
 
   return CUBEB_OK;
 }
 
-void
-cubeb_crash()
-{
-  abort();
-  *((volatile int *) NULL) = 0;
-}
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb_assert.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CUBEB_ASSERT
+#define CUBEB_ASSERT
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <mozilla/Assertions.h>
+
+/* Forward fatal asserts to MOZ_ASSERT when built inside Gecko. */
+#define XASSERT(expr) MOZ_ASSERT(expr)
+
+#endif
--- a/media/libcubeb/src/cubeb_pulse.c
+++ b/media/libcubeb/src/cubeb_pulse.c
@@ -1235,17 +1235,17 @@ pulse_get_state_from_sink_port(pa_sink_p
 #if PA_CHECK_VERSION(2, 0, 0)
     if (info->available == PA_PORT_AVAILABLE_NO)
       return CUBEB_DEVICE_STATE_UNPLUGGED;
     else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */
 #endif
       return CUBEB_DEVICE_STATE_ENABLED;
   }
 
-  return CUBEB_DEVICE_STATE_DISABLED;
+  return CUBEB_DEVICE_STATE_ENABLED;
 }
 
 static void
 pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
                    int eol, void * user_data)
 {
   pulse_dev_list_data * list_data = user_data;
   cubeb_device_info * devinfo;
@@ -1265,17 +1265,18 @@ pulse_sink_info_cb(pa_context * context,
   if (prop)
     devinfo->group_id = strdup(prop);
   prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name");
   if (prop)
     devinfo->vendor_name = strdup(prop);
 
   devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT;
   devinfo->state = pulse_get_state_from_sink_port(info->active_port);
-  devinfo->preferred = strcmp(info->name, list_data->default_sink_name) == 0;
+  devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0) ?
+				 CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
 
   devinfo->format = CUBEB_DEVICE_FMT_ALL;
   devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
   devinfo->max_channels = info->channel_map.channels;
   devinfo->min_rate = 1;
   devinfo->max_rate = PA_RATE_MAX;
   devinfo->default_rate = info->sample_spec.rate;
 
@@ -1295,17 +1296,17 @@ pulse_get_state_from_source_port(pa_sour
 #if PA_CHECK_VERSION(2, 0, 0)
     if (info->available == PA_PORT_AVAILABLE_NO)
       return CUBEB_DEVICE_STATE_UNPLUGGED;
     else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */
 #endif
       return CUBEB_DEVICE_STATE_ENABLED;
   }
 
-  return CUBEB_DEVICE_STATE_DISABLED;
+  return CUBEB_DEVICE_STATE_ENABLED;
 }
 
 static void
 pulse_source_info_cb(pa_context * context, const pa_source_info * info,
     int eol, void * user_data)
 {
   pulse_dev_list_data * list_data = user_data;
   cubeb_device_info * devinfo;
@@ -1325,17 +1326,18 @@ pulse_source_info_cb(pa_context * contex
   if (prop)
     devinfo->group_id = strdup(prop);
   prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name");
   if (prop)
     devinfo->vendor_name = strdup(prop);
 
   devinfo->type = CUBEB_DEVICE_TYPE_INPUT;
   devinfo->state = pulse_get_state_from_source_port(info->active_port);
-  devinfo->preferred = strcmp(info->name, list_data->default_source_name) == 0;
+  devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0) ?
+				   CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
 
   devinfo->format = CUBEB_DEVICE_FMT_ALL;
   devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
   devinfo->max_channels = info->channel_map.channels;
   devinfo->min_rate = 1;
   devinfo->max_rate = PA_RATE_MAX;
   devinfo->default_rate = info->sample_spec.rate;
 
--- a/media/libcubeb/src/cubeb_resampler.cpp
+++ b/media/libcubeb/src/cubeb_resampler.cpp
@@ -30,39 +30,56 @@ to_speex_quality(cubeb_resampler_quality
   case CUBEB_RESAMPLER_QUALITY_DESKTOP:
     return SPEEX_RESAMPLER_QUALITY_DESKTOP;
   default:
     assert(false);
     return 0XFFFFFFFF;
   }
 }
 
-long noop_resampler::fill(void * input_buffer, long * input_frames_count,
-                          void * output_buffer, long output_frames)
+template<typename T>
+passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
+                                                cubeb_data_callback cb,
+                                                void * ptr,
+                                                uint32_t input_channels)
+  : processor(input_channels)
+  , stream(s)
+  , data_callback(cb)
+  , user_ptr(ptr)
+{
+}
+
+template<typename T>
+long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
+                                    void * output_buffer, long output_frames)
 {
   if (input_buffer) {
     assert(input_frames_count);
   }
   assert((input_buffer && output_buffer &&
-         *input_frames_count >= output_frames) ||
-         (!input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
-         (!output_buffer && output_frames == 0));
+         *input_frames_count + static_cast<int>(samples_to_frames(internal_input_buffer.length())) >= output_frames) ||
+         (output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
+         (input_buffer && !output_buffer && output_frames == 0));
 
-  if (output_buffer == nullptr) {
-    assert(input_buffer);
-    output_frames = *input_frames_count;
+  if (input_buffer) {
+    if (!output_buffer) {
+      output_frames = *input_frames_count;
+    }
+    internal_input_buffer.push(static_cast<T*>(input_buffer),
+                               frames_to_samples(*input_frames_count));
   }
 
-  if (input_buffer && *input_frames_count != output_frames) {
-    assert(*input_frames_count > output_frames);
-    *input_frames_count = output_frames;
+  long rv = data_callback(stream, user_ptr, internal_input_buffer.data(),
+                          output_buffer, output_frames);
+
+  if (input_buffer) {
+    internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
   }
 
-  return data_callback(stream, user_ptr,
-                       input_buffer, output_buffer, output_frames);
+  return rv;
 }
 
 template<typename T, typename InputProcessor, typename OutputProcessor>
 cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
   ::cubeb_resampler_speex(InputProcessor * input_processor,
                           OutputProcessor * output_processor,
                           cubeb_stream * s,
                           cubeb_data_callback cb,
--- a/media/libcubeb/src/cubeb_resampler_internal.h
+++ b/media/libcubeb/src/cubeb_resampler_internal.h
@@ -43,41 +43,16 @@ int to_speex_quality(cubeb_resampler_qua
 
 struct cubeb_resampler {
   virtual long fill(void * input_buffer, long * input_frames_count,
                     void * output_buffer, long frames_needed) = 0;
   virtual long latency() = 0;
   virtual ~cubeb_resampler() {}
 };
 
-class noop_resampler : public cubeb_resampler {
-public:
-  noop_resampler(cubeb_stream * s,
-                 cubeb_data_callback cb,
-                 void * ptr)
-    : stream(s)
-    , data_callback(cb)
-    , user_ptr(ptr)
-  {
-  }
-
-  virtual long fill(void * input_buffer, long * input_frames_count,
-                    void * output_buffer, long output_frames);
-
-  virtual long latency()
-  {
-    return 0;
-  }
-
-private:
-  cubeb_stream * const stream;
-  const cubeb_data_callback data_callback;
-  void * const user_ptr;
-};
-
 /** Base class for processors. This is just used to share methods for now. */
 class processor {
 public:
   explicit processor(uint32_t channels)
     : channels(channels)
   {}
 protected:
   size_t frames_to_samples(size_t frames)
@@ -88,16 +63,42 @@ protected:
   {
     assert(!(samples % channels));
     return samples / channels;
   }
   /** The number of channel of the audio buffers to be resampled. */
   const uint32_t channels;
 };
 
+template<typename T>
+class passthrough_resampler : public cubeb_resampler
+                            , public processor {
+public:
+  passthrough_resampler(cubeb_stream * s,
+                        cubeb_data_callback cb,
+                        void * ptr,
+                        uint32_t input_channels);
+
+  virtual long fill(void * input_buffer, long * input_frames_count,
+                    void * output_buffer, long output_frames);
+
+  virtual long latency()
+  {
+    return 0;
+  }
+
+private:
+  cubeb_stream * const stream;
+  const cubeb_data_callback data_callback;
+  void * const user_ptr;
+  /* This allows to buffer some input to account for the fact that we buffer
+   * some inputs. */
+  auto_array<T> internal_input_buffer;
+};
+
 /** Bidirectional resampler, can resample an input and an output stream, or just
  * an input stream or output stream. In this case a delay is inserted in the
  * opposite direction to keep the streams synchronized. */
 template<typename T, typename InputProcessing, typename OutputProcessing>
 class cubeb_resampler_speex : public cubeb_resampler {
 public:
   cubeb_resampler_speex(InputProcessing * input_processor,
                         OutputProcessing * output_processor,
@@ -475,17 +476,19 @@ cubeb_resampler_create_internal(cubeb_st
 
   /* All the streams we have have a sample rate that matches the target
      sample rate, use a no-op resampler, that simply forwards the buffers to the
      callback. */
   if (((input_params && input_params->rate == target_rate) &&
       (output_params && output_params->rate == target_rate)) ||
       (input_params && !output_params && (input_params->rate == target_rate)) ||
       (output_params && !input_params && (output_params->rate == target_rate))) {
-    return new noop_resampler(stream, callback, user_ptr);
+    return new passthrough_resampler<T>(stream, callback,
+                                        user_ptr,
+                                        input_params ? input_params->channels : 0);
   }
 
   /* Determine if we need to resampler one or both directions, and create the
      resamplers. */
   if (output_params && (output_params->rate != target_rate)) {
     output_resampler.reset(
         new cubeb_resampler_speex_one_way<T>(output_params->channels,
                                              target_rate,
--- a/media/libcubeb/src/cubeb_sndio.c
+++ b/media/libcubeb/src/cubeb_sndio.c
@@ -240,17 +240,17 @@ sndio_stream_init(cubeb * context,
   sio_onmove(s->hdl, sndio_onmove, s);
   s->active = 0;
   s->nfr = rpar.round;
   s->bpf = rpar.bps * rpar.pchan;
   s->pchan = rpar.pchan;
   s->data_cb = data_callback;
   s->state_cb = state_callback;
   s->arg = user_ptr;
-  s->mtx = PTHREAD_MUTEX_INITIALIZER;
+  s->mtx = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
   s->rdpos = s->wrpos = 0;
   if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE) {
     s->conv = 1;
     size = rpar.round * rpar.pchan * sizeof(float);
   } else {
     s->conv = 0;
     size = rpar.round * rpar.pchan * rpar.bps;
   }
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -742,25 +742,16 @@ refill_callback_duplex(cubeb_stream * st
   }
 
   /* This can only happen when debugging, and having breakpoints set in the
    * callback in a way that it makes the stream underrun. */
   if (output_frames == 0) {
     return true;
   }
 
-  // When WASAPI has not filled the input buffer yet, send silence.
-  double output_duration = double(output_frames) / stm->output_mix_params.rate;
-  double input_duration = double(stm->linear_input_buffer.length() / stm->input_stream_params.channels) / stm->input_mix_params.rate;
-  if (input_duration < output_duration) {
-    size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate));
-    LOG("padding silence: out=%f in=%f pad=%u", output_duration, input_duration, padding);
-    stm->linear_input_buffer.push_front_silence(padding * stm->input_stream_params.channels);
-  }
-
   LOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
        stm->linear_input_buffer.length(), output_frames);
 
   refill(stm,
          stm->linear_input_buffer.data(),
          stm->linear_input_buffer.length(),
          output_buffer,
          output_frames);
@@ -1646,16 +1637,24 @@ int setup_wasapi_stream(cubeb_stream * s
                                       stm->input_device.get(),
                                       eCapture,
                                       __uuidof(IAudioCaptureClient),
                                       stm->input_client,
                                       &stm->input_buffer_frame_count,
                                       stm->input_available_event,
                                       stm->capture_client,
                                       &stm->input_mix_params);
+
+    // We initializing an input stream, buffer ahead two buffers worth of silence.
+    // This delays the input side slightly, but allow to not glitch when no input
+    // is available when calling into the resampler to call the callback: the input
+    // refill event will be set shortly after to compensate for this lack of data.
+    stm->linear_input_buffer.push_silence(stm->input_buffer_frame_count *
+                                          stm->input_stream_params.channels * 2);
+
     if (rv != CUBEB_OK) {
       LOG("Failure to open the input side.");
       return rv;
     }
   }
 
   if (has_output(stm)) {
     LOG("(%p) Setup render: device=%p", stm, stm->output_device.get());