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; }
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 *) ¬ify) != 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; }