Ejemplo n.º 1
0
TEST(cubeb, resampler_drain)
{
  cubeb_stream_params output_params;
  int target_rate;

  output_params.rate = 44100;
  output_params.channels = 1;
  output_params.format = CUBEB_SAMPLE_FLOAT32NE;
  target_rate = 48000;

  cubeb_resampler * resampler =
    cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params, target_rate,
                           test_drain_data_cb, nullptr,
                           CUBEB_RESAMPLER_QUALITY_VOIP);

  const long out_frames = 128;
  float out_buffer[out_frames];
  long got;

  do {
    got = cubeb_resampler_fill(resampler, nullptr, nullptr,
                               out_buffer, out_frames);
  } while (got == out_frames);

  /* 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);
}
Ejemplo n.º 2
0
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);
  }
}
Ejemplo n.º 3
0
TEST(cubeb, resampler_output_only_noop)
{
  cubeb_stream_params output_params;
  int target_rate;

  output_params.rate = 44100;
  output_params.channels = 1;
  output_params.format = CUBEB_SAMPLE_FLOAT32NE;
  target_rate = output_params.rate;

  cubeb_resampler * resampler =
    cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params, target_rate,
                           test_output_only_noop_data_cb, nullptr,
                           CUBEB_RESAMPLER_QUALITY_VOIP);

  const long out_frames = 128;
  float out_buffer[out_frames];
  long got;

  got = cubeb_resampler_fill(resampler, nullptr, nullptr,
                             out_buffer, out_frames);

  ASSERT_EQ(got, out_frames);

  cubeb_resampler_destroy(resampler);
}
Ejemplo n.º 4
0
static void
cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, float ** bufs_out, jack_nframes_t nframes)
{
  float * out_interleaved_buffer = nullptr;

  short * inptr = (in != NULL) ? *in : nullptr;
  float * outptr = (bufs_out != NULL) ? *bufs_out : nullptr;

  long needed_frames = (bufs_out != NULL) ? nframes : 0;
  long done_frames = 0;
  long input_frames_count = (in != NULL) ? nframes : 0;

  done_frames = cubeb_resampler_fill(stream->resampler,
                                     inptr,
                                     &input_frames_count,
                                     (bufs_out != NULL) ? stream->context->out_resampled_interleaved_buffer_s16ne : NULL,
                                     needed_frames);

  s16ne_to_float(stream->context->out_resampled_interleaved_buffer_float, stream->context->out_resampled_interleaved_buffer_s16ne, done_frames * stream->out_params.channels);

  out_interleaved_buffer = stream->context->out_resampled_interleaved_buffer_float;

  if (outptr) {
    // convert interleaved output buffers to contiguous buffers
    for (unsigned int c = 0; c < stream->out_params.channels; c++) {
      float* buffer = bufs_out[c];
      for (long f = 0; f < done_frames; f++) {
        buffer[f] = out_interleaved_buffer[(f * stream->out_params.channels) + c] * stream->volume;
      }
      if (done_frames < needed_frames) {
        // draining
        for (long f = done_frames; f < needed_frames; f++) {
          buffer[f] = 0.f;
        }
      }
      if (done_frames == 0) {
        // stop, but first zero out the existing buffer
        for (long f = 0; f < needed_frames; f++) {
          buffer[f] = 0.f;
        }
      }
    }
  }

  if (done_frames >= 0 && done_frames < needed_frames) {
    // set drained
    stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
    // stop stream
    cbjack_stream_stop(stream);
  }
  if (done_frames > 0 && done_frames <= needed_frames) {
    // advance stream position
    stream->position += done_frames * stream->ratio;
  }
  if (done_frames < 0 || done_frames > needed_frames) {
    // stream error
    stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_ERROR);
  }
}
Ejemplo n.º 5
0
static void
bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
{
  cubeb_stream * stm = user_ptr;
  assert(stm);
  SLBufferQueueState state;
  SLresult res;

  res = (*stm->bufq)->GetState(stm->bufq, &state);
  assert(res == SL_RESULT_SUCCESS);

  if (state.count > 1)
    return;

  SLuint32 i;
  for (i = state.count; i < NBUFS; i++) {
    uint8_t *buf = stm->queuebuf[stm->queuebuf_idx];
    long written = 0;
    pthread_mutex_lock(&stm->mutex);
    int draining = stm->draining;
    pthread_mutex_unlock(&stm->mutex);

    if (!draining) {
      written = cubeb_resampler_fill(stm->resampler,
                                     NULL, NULL,
                                     buf, stm->queuebuf_len / stm->framesize);
      if (written < 0 || written * stm->framesize > stm->queuebuf_len) {
        (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PAUSED);
        return;
      }
    }

    // Keep sending silent data even in draining mode to prevent the audio
    // back-end from being stopped automatically by OpenSL/ES.
    memset(buf + written * stm->framesize, 0, stm->queuebuf_len - written * stm->framesize);
    res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->queuebuf_len);
    assert(res == SL_RESULT_SUCCESS);
    stm->queuebuf_idx = (stm->queuebuf_idx + 1) % NBUFS;
    if (written > 0) {
      pthread_mutex_lock(&stm->mutex);
      stm->written += written;
      pthread_mutex_unlock(&stm->mutex);
    }

    if (!draining && written * stm->framesize < stm->queuebuf_len) {
      pthread_mutex_lock(&stm->mutex);
      int64_t written_duration = INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
      stm->draining = 1;
      pthread_mutex_unlock(&stm->mutex);
      // Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf
      // to make sure all the data has been processed.
      (*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
      return;
    }
  }
}
Ejemplo n.º 6
0
static void
bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
{
  cubeb_stream * stm = user_ptr;
  SLBufferQueueState state;
  SLresult res;

  res = (*stm->bufq)->GetState(stm->bufq, &state);
  assert(res == SL_RESULT_SUCCESS);

  if (stm->draining) {
    if (!state.count) {
      stm->draining = 0;
      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
    }
    return;
  }

  if (state.count > 1)
    return;

  SLuint32 i;
  for (i = state.count; i < NBUFS; i++) {
    void *buf = stm->queuebuf[stm->queuebuf_idx];
    long written = cubeb_resampler_fill(stm->resampler, buf,
                                        stm->queuebuf_len / stm->framesize);
    if (written == CUBEB_ERROR) {
      (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_STOPPED);
      return;
    }

    if (written) {
      res = (*stm->bufq)->Enqueue(stm->bufq, buf, written * stm->framesize);
      assert(res == SL_RESULT_SUCCESS);
      stm->queuebuf_idx = (stm->queuebuf_idx + 1) % NBUFS;
    } else if (!i) {
      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
      return;
    }

    if ((written * stm->framesize) < stm->queuebuf_len) {
      stm->draining = 1;
      return;
    }
  }
}
Ejemplo n.º 7
0
void test_resampler_duplex(uint32_t input_channels, uint32_t output_channels,
                           uint32_t input_rate, uint32_t output_rate,
                           uint32_t target_rate, float chunk_duration)
{
  cubeb_stream_params input_params;
  cubeb_stream_params output_params;
  osc_state state;

  input_params.format = output_params.format = cubeb_format<T>();
  state.input_channels = input_params.channels = input_channels;
  state.output_channels = output_params.channels = output_channels;
  input_params.rate = input_rate;
  state.output_rate = output_params.rate = output_rate;
  state.target_rate = target_rate;
  long got;

  cubeb_resampler * resampler =
    cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params, target_rate,
                           data_cb_resampler, (void*)&state, CUBEB_RESAMPLER_QUALITY_VOIP);

  long latency = cubeb_resampler_latency(resampler);

  const uint32_t duration_s = 2;
  int32_t duration_frames = duration_s * target_rate;
  uint32_t input_array_frame_count = ceil(chunk_duration * input_rate / 1000) + ceilf(static_cast<float>(input_rate) / target_rate) * 2;
  uint32_t output_array_frame_count = chunk_duration * output_rate / 1000;
  auto_array<float> input_buffer(input_channels * input_array_frame_count);
  auto_array<float> output_buffer(output_channels * output_array_frame_count);
  auto_array<float> expected_resampled_input(input_channels * duration_frames);
  auto_array<float> expected_resampled_output(output_channels * output_rate * duration_s);

  state.max_output_phase_index = duration_s * target_rate;

  expected_resampled_input.push_silence(input_channels * duration_frames);
  expected_resampled_output.push_silence(output_channels * output_rate * duration_s);

  /* expected output is a 440Hz sine wave at 16kHz */
  fill_with_sine(expected_resampled_input.data() + latency,
                 target_rate, input_channels, duration_frames - latency, 0);
  /* expected output is a 440Hz sine wave at 32kHz */
  fill_with_sine(expected_resampled_output.data() + latency,
                 output_rate, output_channels, output_rate * duration_s - latency, 0);

  while (state.output_phase_index != state.max_output_phase_index) {
    uint32_t leftover_samples = input_buffer.length() * input_channels;
    input_buffer.reserve(input_array_frame_count);
    state.input_phase_index = fill_with_sine(input_buffer.data() + leftover_samples,
                                             input_rate,
                                             input_channels,
                                             input_array_frame_count - leftover_samples,
                                             state.input_phase_index);
    long input_consumed = input_array_frame_count;
    input_buffer.set_length(input_array_frame_count);

    got = cubeb_resampler_fill(resampler,
                               input_buffer.data(), &input_consumed,
                               output_buffer.data(), output_array_frame_count);

    /* handle leftover input */
    if (input_array_frame_count != static_cast<uint32_t>(input_consumed)) {
      input_buffer.pop(nullptr, input_consumed * input_channels);
    } else {
      input_buffer.clear();
    }

    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)));

  cubeb_resampler_destroy(resampler);
}
Ejemplo n.º 8
0
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;

  // prebuffer_frames will hold the frames used by the resampler.
  ASSERT_EQ(prebuffer_frames, BUF_BASE_SIZE);
  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);
  }
}