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