unsigned __stdcall
directsound_buffer_refill_thread(void * user_ptr)
{
  int shutdown = 0;
  cubeb * ctx = (cubeb *) user_ptr;
  assert(ctx);

  while (!shutdown) {
    DWORD rv = WaitForSingleObject(ctx->streams_event, INFINITE);
    assert(rv == WAIT_OBJECT_0);
    EnterCriticalSection(&ctx->lock);
    struct cubeb_list_node * node = ctx->streams;
    while (node) {
      EnterCriticalSection(&node->stream->lock);
      if (node->stream->draining) {
	node->stream->state_callback(node->stream, node->stream->user_ptr, CUBEB_STATE_DRAINED);
	node->stream->active = 0;
	HRESULT rv = node->stream->buffer->Stop();
	assert(rv == DS_OK);
	DWORD play, write;
	rv = node->stream->buffer->GetCurrentPosition(&play, &write);
	assert(rv == DS_OK);
	fprintf(stderr, "stm %p drained (p=%u w=%u)\n", node->stream, play, write);
      }
      if (node->stream->active) {
	refill_stream(node->stream, 0);
      }
      LeaveCriticalSection(&node->stream->lock);
      node = node->next;
    }
    shutdown = ctx->shutdown;
    LeaveCriticalSection(&ctx->lock);
  }
  return 0;
}
Example #2
0
static ssize_t
read_stream (EntryStream *stream, void *out, size_t size)
{
	if (refill_stream (stream, size)) {
		memcpy (out, stream->pos, size);
		stream->pos += size;
		return size;
	}
	return 0;
}
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;
}