Example #1
0
TEST(cubeb, drain)
{
  int r;
  cubeb * ctx;
  cubeb_stream * stream;
  cubeb_stream_params params;
  uint64_t position;

  delay_callback = 0;
  total_frames_written = 0;

  r = common_init(&ctx, "test_sanity");
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_NE(ctx, nullptr);

  params.format = STREAM_FORMAT;
  params.rate = STREAM_RATE;
  params.channels = STREAM_CHANNELS;
  params.layout = STREAM_LAYOUT;

  r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
                        test_drain_data_callback, test_drain_state_callback, &dummy);
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_NE(stream, nullptr);

  r = cubeb_stream_start(stream);
  ASSERT_EQ(r, CUBEB_OK);

  delay(500);

  do_drain = 1;

  for (;;) {
    r = cubeb_stream_get_position(stream, &position);
    ASSERT_EQ(r, CUBEB_OK);
    if (got_drain) {
      break;
    } else {
      ASSERT_LE(position, total_frames_written.load());
    }
    delay(500);
  }

  r = cubeb_stream_get_position(stream, &position);
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_TRUE(got_drain);

  // Really, we should be able to rely on position reaching our final written frame, but
  // for now let's make sure it doesn't continue beyond that point.
  //ASSERT_LE(position, total_frames_written.load());

  cubeb_stream_destroy(stream);
  cubeb_destroy(ctx);

  got_drain = 0;
  do_drain = 0;
}
Example #2
0
static void
test_drain(void)
{
  int r;
  cubeb * ctx;
  cubeb_stream * stream;
  cubeb_stream_params params;
  uint64_t position;

  BEGIN_TEST;

  total_frames_written = 0;

  r = cubeb_init(&ctx, "test_sanity");
  assert(r == 0 && ctx);

  params.format = STREAM_FORMAT;
  params.rate = STREAM_RATE;
  params.channels = STREAM_CHANNELS;

  r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
                        test_drain_data_callback, test_drain_state_callback, &dummy);
  assert(r == 0 && stream);

  r = cubeb_stream_start(stream);
  assert(r == 0);

  delay(500);

  do_drain = 1;

  for (;;) {
    r = cubeb_stream_get_position(stream, &position);
    assert(r == 0);
    if (got_drain) {
      break;
    } else {
      assert(position <= total_frames_written);
    }
    delay(500);
  }

  r = cubeb_stream_get_position(stream, &position);
  assert(r == 0);
  assert(got_drain);

  // Really, we should be able to rely on position reaching our final written frame, but
  // for now let's make sure it doesn't continue beyond that point.
  //assert(position <= total_frames_written);

  cubeb_stream_destroy(stream);
  cubeb_destroy(ctx);

  END_TEST;
}
int64_t
BufferedAudioStream::GetPositionInFramesUnlocked()
{
  mMonitor.AssertCurrentThreadOwns();

  if (!mCubebStream || mState == ERRORED) {
    return -1;
  }

  uint64_t position = 0;
  {
    MonitorAutoUnlock mon(mMonitor);
    if (cubeb_stream_get_position(mCubebStream, &position) != CUBEB_OK) {
      return -1;
    }
  }

  // Adjust the reported position by the number of silent frames written
  // during stream underruns.
  uint64_t adjustedPosition = 0;
  if (position >= mLostFrames) {
    adjustedPosition = position - mLostFrames;
  }
  return std::min<uint64_t>(adjustedPosition, INT64_MAX);
}
Example #4
0
static void
test_basic_stream_operations(void)
{
  int r;
  cubeb * ctx;
  cubeb_stream * stream;
  cubeb_stream_params params;
  uint64_t position;

  BEGIN_TEST

  r = cubeb_init(&ctx, "test_sanity");
  assert(r == 0 && ctx);

  params.format = STREAM_FORMAT;
  params.rate = STREAM_RATE;
  params.channels = STREAM_CHANNELS;

  r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
                        test_data_callback, test_state_callback, &dummy);
  assert(r == 0 && stream);

  /* position and volume before stream has started */
  r = cubeb_stream_get_position(stream, &position);
  assert(r == 0 && position == 0);

  r = cubeb_stream_start(stream);
  assert(r == 0);

  /* position and volume after while stream running */
  r = cubeb_stream_get_position(stream, &position);
  assert(r == 0);

  r = cubeb_stream_stop(stream);
  assert(r == 0);

  /* position and volume after stream has stopped */
  r = cubeb_stream_get_position(stream, &position);
  assert(r == 0);

  cubeb_stream_destroy(stream);
  cubeb_destroy(ctx);

  END_TEST
}
Example #5
0
TEST(cubeb, basic_stream_operations)
{
  int r;
  cubeb * ctx;
  cubeb_stream * stream;
  cubeb_stream_params params;
  uint64_t position;

  r = common_init(&ctx, "test_sanity");
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_NE(ctx, nullptr);

  params.format = STREAM_FORMAT;
  params.rate = STREAM_RATE;
  params.channels = STREAM_CHANNELS;
  params.layout = STREAM_LAYOUT;

  r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
                        test_data_callback, test_state_callback, &dummy);
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_NE(stream, nullptr);

  /* position and volume before stream has started */
  r = cubeb_stream_get_position(stream, &position);
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_EQ(position, 0u);

  r = cubeb_stream_start(stream);
  ASSERT_EQ(r, CUBEB_OK);

  /* position and volume after while stream running */
  r = cubeb_stream_get_position(stream, &position);
  ASSERT_EQ(r, CUBEB_OK);

  r = cubeb_stream_stop(stream);
  ASSERT_EQ(r, CUBEB_OK);

  /* position and volume after stream has stopped */
  r = cubeb_stream_get_position(stream, &position);
  ASSERT_EQ(r, CUBEB_OK);

  cubeb_stream_destroy(stream);
  cubeb_destroy(ctx);
}
Example #6
0
GraphTime
AudioCallbackDriver::GetCurrentTime()
{
  uint64_t position = 0;

  if (cubeb_stream_get_position(mAudioStream, &position) != CUBEB_OK) {
    NS_WARNING("Could not get current time from cubeb.");
  }

  return mSampleRate * position;
}
Example #7
0
int64_t
AudioStream::GetPositionInFramesUnlocked()
{
  mMonitor.AssertCurrentThreadOwns();

  if (!mCubebStream || mState == ERRORED) {
    return -1;
  }

  uint64_t position = 0;
  {
    MonitorAutoUnlock mon(mMonitor);
    if (cubeb_stream_get_position(mCubebStream, &position) != CUBEB_OK) {
      return -1;
    }
  }

  // Adjust the reported position by the number of silent frames written
  // during stream underruns.
  // Since frames sent to DataCallback is not consumed by the backend immediately,
  // it will be an over adjustment if we return |position - mLostFramesPast - mLostFramesLast|.
  // On the other hand, we need to keep the whole history of frames sent to DataCallback
  // in order to adjust position correctly which will require more storage.
  // We choose a simple way to store the history where |mWrittenFramesPast| and
  // |mLostFramesPast| are the sum of frames from 1th to |N-1|th callbacks, and
  // |mWrittenFramesLast| and |mLostFramesLast| represent the frames sent in last callback.
  // When |position| lies in
  // [mWrittenFramesPast+mLostFramesPast, mWrittenFramesPast+mLostFramesPast+mWrittenFramesLast+mLostFramesLast],
  // we will be able to adjust position precisely which should be the major case.
  // If |position| falls in [0, mWrittenFramesPast+mLostFramesPast), there will be an
  // error in the adjustment. However that is fine as long as we can ensure the
  // adjusted position is mono-increasing to avoid audio clock going backward.
  uint64_t adjustedPosition = 0;
  if (position <= mWrittenFramesPast) {
    adjustedPosition = position;
  } else if (position <= mWrittenFramesPast + mLostFramesPast) {
    adjustedPosition = mWrittenFramesPast;
  } else if (position <= mWrittenFramesPast + mLostFramesPast + mWrittenFramesLast) {
    adjustedPosition = position - mLostFramesPast;
  } else {
    adjustedPosition = mWrittenFramesPast + mWrittenFramesLast;
  }
  return std::min<uint64_t>(adjustedPosition, INT64_MAX);
}
Example #8
0
int64_t
AudioStream::GetPositionInFramesUnlocked()
{
    mMonitor.AssertCurrentThreadOwns();

    if (!mCubebStream || mState == ERRORED) {
        return -1;
    }

    uint64_t position = 0;
    {
        MonitorAutoUnlock mon(mMonitor);
        if (cubeb_stream_get_position(mCubebStream.get(), &position) != CUBEB_OK) {
            return -1;
        }
    }

    return std::min<uint64_t>(position, INT64_MAX);
}
Example #9
0
int64_t
AudioStream::GetPositionInFramesUnlocked()
{
    mMonitor.AssertCurrentThreadOwns();

    if (!mCubebStream || mState == ERRORED) {
        return -1;
    }

    uint64_t position = 0;
    {
        MonitorAutoUnlock mon(mMonitor);
        if (cubeb_stream_get_position(mCubebStream.get(), &position) != CUBEB_OK) {
            return -1;
        }
    }

    MOZ_ASSERT(position >= mLastGoodPosition, "cubeb position shouldn't go backward");
    // This error handling/recovery keeps us in good shape in release build.
    if (position >= mLastGoodPosition) {
        mLastGoodPosition = position;
    }
    return std::min<uint64_t>(mLastGoodPosition, INT64_MAX);
}
Example #10
0
static void
test_drain(void)
{
  int r;
  cubeb * ctx;
  cubeb_stream * stream;
  cubeb_stream_params params;
  uint64_t position;

  BEGIN_TEST

  total_frames_written = 0;

  r = cubeb_init(&ctx, "test_sanity");
  assert(r == 0 && ctx);

  params.format = STREAM_FORMAT;
  params.rate = STREAM_RATE;
  params.channels = STREAM_CHANNELS;

  r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
                        test_drain_data_callback, test_drain_state_callback, &dummy);
  assert(r == 0 && stream);

  r = cubeb_stream_start(stream);
  assert(r == 0);

  delay(500);

  do_drain = 1;

  for (;;) {
    r = cubeb_stream_get_position(stream, &position);
    assert(r == 0);
    if (got_drain) {
      break;
    } else {
      uint32_t i, skip = 0;
      /* Latency passed to cubeb_stream_init is not really honored on OSX,
         win32/winmm and android, skip this test. */
      const char * backend_id = cubeb_get_backend_id(ctx);
      const char * latency_not_honored_bakends[] = {
        "audiounit",
        "winmm",
        "audiotrack",
        "opensl"
      };

      for (i = 0; i < ARRAY_LENGTH(latency_not_honored_bakends); i++) {
        if (!strcmp(backend_id, latency_not_honored_bakends[i])) {
          skip = 1;
        }
      }
      if (!skip) {
        /* Position should roughly be equal to the number of written frames. We
         * need to take the latency into account. */
        int latency = (STREAM_LATENCY * STREAM_RATE) / 1000;
        assert(position + latency <= total_frames_written);
      }
    }
    delay(500);
  }

  r = cubeb_stream_get_position(stream, &position);
  assert(r == 0);
  assert(got_drain);

  // Disabled due to failures in the ALSA backend.
  //assert(position == total_frames_written);

  cubeb_stream_destroy(stream);
  cubeb_destroy(ctx);

  END_TEST
}
Example #11
0
static void
test_stream_position(void)
{
  int i;
  int r;
  cubeb * ctx;
  cubeb_stream * stream;
  cubeb_stream_params params;
  uint64_t position, last_position;

  BEGIN_TEST

  total_frames_written = 0;

  r = cubeb_init(&ctx, "test_sanity");
  assert(r == 0 && ctx);

  params.format = STREAM_FORMAT;
  params.rate = STREAM_RATE;
  params.channels = STREAM_CHANNELS;

  r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
                        test_data_callback, test_state_callback, &dummy);
  assert(r == 0 && stream);

  /* stream position should not advance before starting playback */
  r = cubeb_stream_get_position(stream, &position);
  assert(r == 0 && position == 0);

  delay(500);

  r = cubeb_stream_get_position(stream, &position);
  assert(r == 0 && position == 0);

  /* stream position should advance during playback */
  r = cubeb_stream_start(stream);
  assert(r == 0);

  /* XXX let start happen */
  delay(500);

  /* stream should have prefilled */
  assert(total_frames_written > 0);

  r = cubeb_stream_get_position(stream, &position);
  assert(r == 0);
  last_position = position;

  delay(500);

  r = cubeb_stream_get_position(stream, &position);
  assert(r == 0);
  assert(position >= last_position);
  last_position = position;

  /* stream position should not exceed total frames written */
  for (i = 0; i < 5; ++i) {
    r = cubeb_stream_get_position(stream, &position);
    assert(r == 0);
    assert(position >= last_position);
    assert(position <= total_frames_written);
    last_position = position;
    delay(500);
  }

  assert(last_position != 0);

  /* stream position should not advance after stopping playback */
  r = cubeb_stream_stop(stream);
  assert(r == 0);

  /* XXX allow stream to settle */
  delay(500);

  r = cubeb_stream_get_position(stream, &position);
  assert(r == 0);
  last_position = position;

  delay(500);

  r = cubeb_stream_get_position(stream, &position);
  assert(r == 0);
  assert(position == last_position);

  cubeb_stream_destroy(stream);
  cubeb_destroy(ctx);

  END_TEST
}
Example #12
0
TEST(cubeb, stream_position)
{
  size_t i;
  int r;
  cubeb * ctx;
  cubeb_stream * stream;
  cubeb_stream_params params;
  uint64_t position, last_position;

  total_frames_written = 0;

  r = common_init(&ctx, "test_sanity");
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_NE(ctx, nullptr);

  params.format = STREAM_FORMAT;
  params.rate = STREAM_RATE;
  params.channels = STREAM_CHANNELS;
  params.layout = STREAM_LAYOUT;

  r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
                        test_data_callback, test_state_callback, &dummy);
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_NE(stream, nullptr);

  /* stream position should not advance before starting playback */
  r = cubeb_stream_get_position(stream, &position);
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_EQ(position, 0u);

  delay(500);

  r = cubeb_stream_get_position(stream, &position);
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_EQ(position, 0u);

  /* stream position should advance during playback */
  r = cubeb_stream_start(stream);
  ASSERT_EQ(r, CUBEB_OK);

  /* XXX let start happen */
  delay(500);

  /* stream should have prefilled */
  ASSERT_TRUE(total_frames_written.load() > 0);

  r = cubeb_stream_get_position(stream, &position);
  ASSERT_EQ(r, CUBEB_OK);
  last_position = position;

  delay(500);

  r = cubeb_stream_get_position(stream, &position);
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_GE(position, last_position);
  last_position = position;

  /* stream position should not exceed total frames written */
  for (i = 0; i < 5; ++i) {
    r = cubeb_stream_get_position(stream, &position);
    ASSERT_EQ(r, CUBEB_OK);
    ASSERT_GE(position, last_position);
    ASSERT_LE(position, total_frames_written.load());
    last_position = position;
    delay(500);
  }

  /* test that the position is valid even when starting and
   * stopping the stream.  */
  for (i = 0; i < 5; ++i) {
    r = cubeb_stream_stop(stream);
    ASSERT_EQ(r, CUBEB_OK);
    r = cubeb_stream_get_position(stream, &position);
    ASSERT_EQ(r, CUBEB_OK);
    ASSERT_TRUE(last_position < position);
    last_position = position;
    delay(500);
    r = cubeb_stream_start(stream);
    ASSERT_EQ(r, CUBEB_OK);
    delay(500);
  }

  ASSERT_NE(last_position, 0u);

  /* stream position should not advance after stopping playback */
  r = cubeb_stream_stop(stream);
  ASSERT_EQ(r, CUBEB_OK);

  /* XXX allow stream to settle */
  delay(500);

  r = cubeb_stream_get_position(stream, &position);
  ASSERT_EQ(r, CUBEB_OK);
  last_position = position;

  delay(500);

  r = cubeb_stream_get_position(stream, &position);
  ASSERT_EQ(r, CUBEB_OK);
  ASSERT_EQ(position, last_position);

  cubeb_stream_destroy(stream);
  cubeb_destroy(ctx);
}
static int
directsound_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
                        cubeb_stream_params stream_params, unsigned int latency,
                        cubeb_data_callback data_callback,
                        cubeb_state_callback state_callback,
                        void * user_ptr)
{
  struct cubeb_list_node * node;

  assert(context);
  *stream = NULL;

  /*
    create primary buffer
  */
  DSBUFFERDESC bd;
  bd.dwSize = sizeof(DSBUFFERDESC);
  bd.dwFlags = DSBCAPS_PRIMARYBUFFER;
  bd.dwBufferBytes = 0;
  bd.dwReserved = 0;
  bd.lpwfxFormat = NULL;
  bd.guid3DAlgorithm = DS3DALG_DEFAULT;

  LPDIRECTSOUNDBUFFER primary;
  if (FAILED(context->dsound->CreateSoundBuffer(&bd, &primary, NULL))) {
    return 1;
  }

  WAVEFORMATEXTENSIBLE wfx;
  wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  wfx.Format.nChannels = stream_params.channels;
  wfx.Format.nSamplesPerSec = stream_params.rate;
  wfx.Format.cbSize = sizeof(wfx) - sizeof(wfx.Format);

  /* XXX fix channel mappings */
  wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;

  switch (stream_params.format) {
  case CUBEB_SAMPLE_S16LE:
    wfx.Format.wBitsPerSample = 16;
    wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
    break;
  case CUBEB_SAMPLE_FLOAT32LE:
    wfx.Format.wBitsPerSample = 32;
    wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
    break;
  default:
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
  wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
  wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;

  if (FAILED(primary->SetFormat((LPWAVEFORMATEX) &wfx))) {
    /* XXX free primary */
    return CUBEB_ERROR;
  }

  primary->Release();

  cubeb_stream * stm = (cubeb_stream *) calloc(1, sizeof(*stm));
  assert(stm);

  stm->context = context;

  stm->params = stream_params;

  stm->data_callback = data_callback;
  stm->state_callback = state_callback;
  stm->user_ptr = user_ptr;

  InitializeCriticalSection(&stm->lock);

  /*
    create secondary buffer
  */
  bd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY;
  bd.dwBufferBytes = (DWORD) (wfx.Format.nSamplesPerSec / 1000.0 * latency * bytes_per_frame(stream_params));
  if (bd.dwBufferBytes % bytes_per_frame(stream_params) != 0) {
    bd.dwBufferBytes += bytes_per_frame(stream_params) - (bd.dwBufferBytes % bytes_per_frame(stream_params));
  }
  bd.lpwfxFormat = (LPWAVEFORMATEX) &wfx;
  if (FAILED(context->dsound->CreateSoundBuffer(&bd, &stm->buffer, NULL))) {
    return CUBEB_ERROR;
  }

  stm->buffer_size = bd.dwBufferBytes;

  LPDIRECTSOUNDNOTIFY notify;
  if (stm->buffer->QueryInterface(IID_IDirectSoundNotify, (LPVOID *) &notify) != DS_OK) {
    /* XXX free resources */
    return CUBEB_ERROR;
  }

  DSBPOSITIONNOTIFY note[3];
  for (int i = 0; i < 3; ++i) {
    note[i].dwOffset = (stm->buffer_size / 4) * i;
    note[i].hEventNotify = context->streams_event;
  }
  if (notify->SetNotificationPositions(3, note) != DS_OK) {
    /* XXX free resources */
    return CUBEB_ERROR;
  }

  notify->Release();

  refill_stream(stm, 1);
  /* XXX remove this, just a test that double refill does not overwrite existing data */
  refill_stream(stm, 0);
  uint64_t pos;
  cubeb_stream_get_position(stm, &pos);

  stm->node = (struct cubeb_list_node *) calloc(1, sizeof(*node));
  stm->node->stream = stm;

  EnterCriticalSection(&context->lock);
  if (!context->streams) {
    context->streams = stm->node;
  } else {
    node = context->streams;
    while (node->next) {
      node = node->next;
    }
    node->next = stm->node;
    stm->node->prev = node;
  }
  LeaveCriticalSection(&context->lock);

  SetEvent(context->streams_event);

  *stream = stm;

  return CUBEB_OK;
}