Example #1
0
static void
cubeb_refill_stream(cubeb_stream * stm)
{
  WAVEHDR * hdr;
  long got;
  long wanted;
  MMRESULT r;

  EnterCriticalSection(&stm->lock);
  stm->free_buffers += 1;
  assert(stm->free_buffers > 0 && stm->free_buffers <= NBUFS);

  if (stm->draining) {
    LeaveCriticalSection(&stm->lock);
    if (stm->free_buffers == NBUFS) {
      stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
    }
    SetEvent(stm->event);
    return;
  }

  if (stm->shutdown) {
    LeaveCriticalSection(&stm->lock);
    SetEvent(stm->event);
    return;
  }

  hdr = cubeb_get_next_buffer(stm);

  wanted = (DWORD) stm->buffer_size / bytes_per_frame(stm->params);

  /* It is assumed that the caller is holding this lock.  It must be dropped
     during the callback to avoid deadlocks. */
  LeaveCriticalSection(&stm->lock);
  got = stm->data_callback(stm, stm->user_ptr, hdr->lpData, wanted);
  EnterCriticalSection(&stm->lock);
  if (got < 0) {
    LeaveCriticalSection(&stm->lock);
    /* XXX handle this case */
    assert(0);
    return;
  } else if (got < wanted) {
    stm->draining = 1;
  }

  assert(hdr->dwFlags & WHDR_PREPARED);

  hdr->dwBufferLength = got * bytes_per_frame(stm->params);
  assert(hdr->dwBufferLength <= stm->buffer_size);

  r = waveOutWrite(stm->waveout, hdr, sizeof(*hdr));
  if (r != MMSYSERR_NOERROR) {
    LeaveCriticalSection(&stm->lock);
    stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
    return;
  }

  LeaveCriticalSection(&stm->lock);
}
Example #2
0
static unsigned __stdcall
cubeb_buffer_thread(void * user_ptr)
{
  cubeb * ctx = (cubeb *) user_ptr;
  assert(ctx);

  for (;;) {
    DWORD rv;
    struct cubeb_stream_item * item;

    rv = WaitForSingleObject(ctx->event, INFINITE);
    assert(rv == WAIT_OBJECT_0);

    item = (struct cubeb_stream_item *) InterlockedPopEntrySList(ctx->work);
    while (item) {
      cubeb_stream * stm = item->stream;

      EnterCriticalSection(&stm->lock);
      stm->free_buffers += 1;
      assert(stm->free_buffers > 0 && stm->free_buffers <= NBUFS);

      if (stm->draining || stm->shutdown) {
        if (stm->free_buffers == NBUFS) {
          if (stm->draining) {
            stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
          }
          SetEvent(stm->event);
        }
      } else {
        cubeb_submit_buffer(stm, cubeb_get_next_buffer(stm));
      }
      LeaveCriticalSection(&stm->lock);

      _aligned_free(item);

      item = (struct cubeb_stream_item *) InterlockedPopEntrySList(ctx->work);
    }

    if (ctx->shutdown) {
      break;
    }
  }

  return 0;
}
Example #3
0
int
cubeb_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)
{
  MMRESULT r;
  WAVEFORMATEXTENSIBLE wfx;
  cubeb_stream * stm;
  int i;
  size_t bufsz;

  if (stream_params.rate < 1 || stream_params.rate > 192000 ||
      stream_params.channels < 1 || stream_params.channels > 32 ||
      latency < 1 || latency > 2000) {
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  memset(&wfx, 0, sizeof(wfx));
  if (stream_params.channels > 2) {
    wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
    wfx.Format.cbSize = sizeof(wfx) - sizeof(wfx.Format);
  } else {
    wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
    if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE) {
      wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    }
    wfx.Format.cbSize = 0;
  }
  wfx.Format.nChannels = stream_params.channels;
  wfx.Format.nSamplesPerSec = stream_params.rate;

  /* 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 = 0;
  wfx.Samples.wSamplesPerBlock = 0;
  wfx.Samples.wReserved = 0;

  stm = 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;

  bufsz = (size_t) (stm->params.rate / 1000.0 * latency * bytes_per_frame(stm->params) / NBUFS);
  if (bufsz % bytes_per_frame(stm->params) != 0) {
    bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params));
  }
  assert(bufsz % bytes_per_frame(stm->params) == 0);

  for (i = 0; i < NBUFS; ++i) {
    stm->buffers[i].lpData = calloc(1, bufsz);
    assert(stm->buffers[i].lpData);
    stm->buffers[i].dwBufferLength = bufsz;
    stm->buffers[i].dwFlags = 0;
  }

  InitializeCriticalSection(&stm->lock);

  stm->event = CreateEvent(NULL, FALSE, FALSE, NULL);
  if (!stm->event) {
    cubeb_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  stm->free_buffers = NBUFS;

  /* cubeb_buffer_callback will be called during waveOutOpen, so all
     other initialization must be complete before calling it. */
  r = waveOutOpen(&stm->waveout, WAVE_MAPPER, &wfx.Format,
                  (DWORD_PTR) cubeb_buffer_callback, (DWORD_PTR) stm,
                  CALLBACK_FUNCTION);
  if (r != MMSYSERR_NOERROR) {
    cubeb_stream_destroy(stm);
    return CUBEB_ERROR;
  }
  assert(r == MMSYSERR_NOERROR);

  r = waveOutPause(stm->waveout);
  assert(r == MMSYSERR_NOERROR);

  for (i = 0; i < NBUFS; ++i) {
    WAVEHDR * hdr = cubeb_get_next_buffer(stm);

    r = waveOutPrepareHeader(stm->waveout, hdr, sizeof(*hdr));
    assert(r == MMSYSERR_NOERROR);

    cubeb_submit_buffer(stm, hdr);
  }

  *stream = stm;

  return CUBEB_OK;
}