static void thread_resume(struct ao *ao) { struct wasapi_state *state = ao->priv; HRESULT hr; MP_DBG(state, "Thread Resume\n"); UINT32 padding = 0; hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding); if (hr != S_OK) { MP_ERR(state, "IAudioClient_GetCurrentPadding returned %s\n", mp_HRESULT_to_str(hr)); } // Fill the buffer before starting, but only if there is no audio queued to // play. This prevents overfilling the buffer, which leads to problems in // exclusive mode if (padding < (UINT32) state->bufferFrameCount) thread_feed(ao); // start feeding next wakeup if something else hasn't been requested int expected = WASAPI_THREAD_RESUME; atomic_compare_exchange_strong(&state->thread_state, &expected, WASAPI_THREAD_FEED); hr = IAudioClient_Start(state->pAudioClient); if (hr != S_OK) { MP_ERR(state, "IAudioClient_Start returned %s\n", mp_HRESULT_to_str(hr)); } return; }
static void test_uninitialized(IAudioClient *ac) { HRESULT hr; UINT32 num; REFERENCE_TIME t1; HANDLE handle = CreateEventW(NULL, FALSE, FALSE, NULL); IUnknown *unk; hr = IAudioClient_GetBufferSize(ac, &num); ok(hr == AUDCLNT_E_NOT_INITIALIZED, "Uninitialized GetBufferSize call returns %08x\n", hr); hr = IAudioClient_GetStreamLatency(ac, &t1); ok(hr == AUDCLNT_E_NOT_INITIALIZED, "Uninitialized GetStreamLatency call returns %08x\n", hr); hr = IAudioClient_GetCurrentPadding(ac, &num); ok(hr == AUDCLNT_E_NOT_INITIALIZED, "Uninitialized GetCurrentPadding call returns %08x\n", hr); hr = IAudioClient_Start(ac); ok(hr == AUDCLNT_E_NOT_INITIALIZED, "Uninitialized Start call returns %08x\n", hr); hr = IAudioClient_Stop(ac); ok(hr == AUDCLNT_E_NOT_INITIALIZED, "Uninitialized Stop call returns %08x\n", hr); hr = IAudioClient_Reset(ac); ok(hr == AUDCLNT_E_NOT_INITIALIZED, "Uninitialized Reset call returns %08x\n", hr); hr = IAudioClient_SetEventHandle(ac, handle); ok(hr == AUDCLNT_E_NOT_INITIALIZED, "Uninitialized SetEventHandle call returns %08x\n", hr); hr = IAudioClient_GetService(ac, &IID_IAudioStreamVolume, (void**)&unk); ok(hr == AUDCLNT_E_NOT_INITIALIZED, "Uninitialized GetService call returns %08x\n", hr); CloseHandle(handle); }
static void thread_feed(struct ao *ao) { struct wasapi_state *state = ao->priv; HRESULT hr; UINT32 frame_count = state->bufferFrameCount; if (state->share_mode == AUDCLNT_SHAREMODE_SHARED) { UINT32 padding = 0; hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding); EXIT_ON_ERROR(hr); frame_count -= padding; MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n", frame_count, padding); } double delay_us; hr = get_device_delay(state, &delay_us); EXIT_ON_ERROR(hr); // add the buffer delay delay_us += frame_count * 1e6 / state->format.Format.nSamplesPerSec; BYTE *pData; hr = IAudioRenderClient_GetBuffer(state->pRenderClient, frame_count, &pData); EXIT_ON_ERROR(hr); BYTE *data[1] = {pData}; ao_read_data(ao, (void **)data, frame_count, mp_time_us() + (int64_t)llrint(delay_us)); // note, we can't use ao_read_data return value here since we already // commited to frame_count above in the GetBuffer call hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient, frame_count, 0); EXIT_ON_ERROR(hr); atomic_fetch_add(&state->sample_count, frame_count); return; exit_label: MP_ERR(state, "Error feeding audio: %s\n", mp_HRESULT_to_str(hr)); MP_VERBOSE(ao, "Requesting ao reload\n"); ao_request_reload(ao); return; }
JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_jmfext_media_protocol_wasapi_WASAPI_IAudioClient_1GetCurrentPadding (JNIEnv *env, jclass clazz, jlong thiz) { HRESULT hr; UINT32 numPaddingFrames; hr = IAudioClient_GetCurrentPadding( (IAudioClient *) (intptr_t) thiz, &numPaddingFrames); if (FAILED(hr)) { numPaddingFrames = 0; WASAPI_throwNewHResultException(env, hr, __func__, __LINE__); } return (jint) numPaddingFrames; }
static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(IDirectSoundBuffer *iface, DWORD *playpos, DWORD *writepos) { HRESULT hres = DS_OK; UINT32 pad = 0; UINT32 mixpos; IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface); DirectSoundDevice *device = This->device; TRACE("(%p,%p,%p)\n", iface, playpos, writepos); /* **** */ EnterCriticalSection(&(device->mixlock)); if (device->client) hres = IAudioClient_GetCurrentPadding(device->client, &pad); if (hres != DS_OK) { WARN("IAudioClient_GetCurrentPadding failed\n"); LeaveCriticalSection(&(device->mixlock)); return hres; } mixpos = (device->playpos + pad * device->pwfx->nBlockAlign) % device->buflen; if (playpos) *playpos = mixpos; if (writepos) { *writepos = mixpos; if (!device->stopped) { /* apply the documented 10ms lead to writepos */ *writepos += device->writelead; *writepos %= device->buflen; } } LeaveCriticalSection(&(device->mixlock)); /* **** */ TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount()); return DS_OK; }
static void thread_feed(struct ao *ao) { struct wasapi_state *state = (struct wasapi_state *)ao->priv; HRESULT hr; UINT32 frame_count = state->bufferFrameCount; if (state->share_mode == AUDCLNT_SHAREMODE_SHARED) { UINT32 padding = 0; hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding); EXIT_ON_ERROR(hr); frame_count -= padding; } BYTE *pData; hr = IAudioRenderClient_GetBuffer(state->pRenderClient, frame_count, &pData); EXIT_ON_ERROR(hr); BYTE *data[1] = {pData}; ao_read_data(ao, (void**)data, frame_count, (int64_t) ( mp_time_us() + get_device_delay(state) * 1e6 + frame_count * 1e6 / state->format.Format.nSamplesPerSec)); hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient, frame_count, 0); EXIT_ON_ERROR(hr); atomic_fetch_add(&state->sample_count, frame_count); return; exit_label: MP_ERR(state, "thread_feed fails with %"PRIx32"!\n", (uint32_t)hr); return; }
static void test_padding(void) { HRESULT hr; IAudioClient *ac; IAudioRenderClient *arc; WAVEFORMATEX *pwfx; REFERENCE_TIME minp, defp; BYTE *buf; UINT32 psize, pad, written; hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void**)&ac); ok(hr == S_OK, "Activation failed with %08x\n", hr); if(hr != S_OK) return; hr = IAudioClient_GetMixFormat(ac, &pwfx); ok(hr == S_OK, "GetMixFormat failed: %08x\n", hr); if(hr != S_OK) return; hr = IAudioClient_Initialize(ac, AUDCLNT_SHAREMODE_SHARED, 0, 5000000, 0, pwfx, NULL); ok(hr == S_OK, "Initialize failed: %08x\n", hr); hr = IAudioClient_GetDevicePeriod(ac, &defp, &minp); ok(hr == S_OK, "GetDevicePeriod failed: %08x\n", hr); ok(defp != 0, "Default period is 0\n"); ok(minp != 0, "Minimum period is 0\n"); ok(minp <= defp, "Mininum period is greater than default period\n"); hr = IAudioClient_GetService(ac, &IID_IAudioRenderClient, (void**)&arc); ok(hr == S_OK, "GetService failed: %08x\n", hr); psize = (defp / 10000000.) * pwfx->nSamplesPerSec * pwfx->nBlockAlign; written = 0; hr = IAudioClient_GetCurrentPadding(ac, &pad); ok(hr == S_OK, "GetCurrentPadding failed: %08x\n", hr); ok(pad == written, "GetCurrentPadding returned %u, should be %u\n", pad, written); hr = IAudioRenderClient_GetBuffer(arc, psize, &buf); ok(hr == S_OK, "GetBuffer failed: %08x\n", hr); ok(buf != NULL, "NULL buffer returned\n"); hr = IAudioRenderClient_ReleaseBuffer(arc, psize, AUDCLNT_BUFFERFLAGS_SILENT); ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr); written += psize; hr = IAudioClient_GetCurrentPadding(ac, &pad); ok(hr == S_OK, "GetCurrentPadding failed: %08x\n", hr); ok(pad == written, "GetCurrentPadding returned %u, should be %u\n", pad, written); psize = (minp / 10000000.) * pwfx->nSamplesPerSec * pwfx->nBlockAlign; hr = IAudioRenderClient_GetBuffer(arc, psize, &buf); ok(hr == S_OK, "GetBuffer failed: %08x\n", hr); ok(buf != NULL, "NULL buffer returned\n"); hr = IAudioRenderClient_ReleaseBuffer(arc, psize, AUDCLNT_BUFFERFLAGS_SILENT); ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr); written += psize; hr = IAudioClient_GetCurrentPadding(ac, &pad); ok(hr == S_OK, "GetCurrentPadding failed: %08x\n", hr); ok(pad == written, "GetCurrentPadding returned %u, should be %u\n", pad, written); /* overfull buffer. requested 1/2s buffer size, so try * to get a 1/2s buffer, which should fail */ psize = pwfx->nSamplesPerSec / 2.; hr = IAudioRenderClient_GetBuffer(arc, psize, &buf); ok(hr == AUDCLNT_E_BUFFER_TOO_LARGE, "GetBuffer gave wrong error: %08x\n", hr); hr = IAudioRenderClient_ReleaseBuffer(arc, psize, 0); ok(hr == AUDCLNT_E_OUT_OF_ORDER, "ReleaseBuffer gave wrong error: %08x\n", hr); hr = IAudioClient_GetCurrentPadding(ac, &pad); ok(hr == S_OK, "GetCurrentPadding failed: %08x\n", hr); ok(pad == written, "GetCurrentPadding returned %u, should be %u\n", pad, written); CoTaskMemFree(pwfx); IAudioRenderClient_Release(arc); IAudioClient_Release(ac); }
/** * Perform mixing for a Direct Sound device. That is, go through all the * secondary buffers (the sound bites currently playing) and mix them in * to the primary buffer (the device buffer). * * The mixing procedure goes: * * secondary->buffer (secondary format) * =[Resample]=> device->tmp_buffer (float format) * =[Volume]=> device->tmp_buffer (float format) * =[Mix]=> device->mix_buffer (float format) * =[Reformat]=> device->buffer (device format) */ static void DSOUND_PerformMix(DirectSoundDevice *device) { UINT32 pad, to_mix_frags, to_mix_bytes; HRESULT hr; TRACE("(%p)\n", device); /* **** */ EnterCriticalSection(&device->mixlock); hr = IAudioClient_GetCurrentPadding(device->client, &pad); if(FAILED(hr)){ WARN("GetCurrentPadding failed: %08x\n", hr); LeaveCriticalSection(&device->mixlock); return; } to_mix_frags = device->prebuf - (pad * device->pwfx->nBlockAlign + device->fraglen - 1) / device->fraglen; to_mix_bytes = to_mix_frags * device->fraglen; if(device->in_mmdev_bytes > 0){ DWORD delta_bytes = min(to_mix_bytes, device->in_mmdev_bytes); device->in_mmdev_bytes -= delta_bytes; device->playing_offs_bytes += delta_bytes; device->playing_offs_bytes %= device->buflen; } if (device->priolevel != DSSCL_WRITEPRIMARY) { BOOL recover = FALSE, all_stopped = FALSE; DWORD playpos, writepos, writelead, maxq, prebuff_max, prebuff_left, size1, size2; LPVOID buf1, buf2; int nfiller; /* the sound of silence */ nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0; /* get the position in the primary buffer */ if (DSOUND_PrimaryGetPosition(device, &playpos, &writepos) != 0){ LeaveCriticalSection(&(device->mixlock)); return; } TRACE("primary playpos=%d, writepos=%d, clrpos=%d, mixpos=%d, buflen=%d\n", playpos,writepos,device->playpos,device->mixpos,device->buflen); assert(device->playpos < device->buflen); /* calc maximum prebuff */ prebuff_max = (device->prebuf * device->fraglen); /* check how close we are to an underrun. It occurs when the writepos overtakes the mixpos */ prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos); writelead = DSOUND_BufPtrDiff(device->buflen, writepos, playpos); /* check for underrun. underrun occurs when the write position passes the mix position * also wipe out just-played sound data */ if((prebuff_left > prebuff_max) || (device->state == STATE_STOPPED) || (device->state == STATE_STARTING)){ if (device->state == STATE_STOPPING || device->state == STATE_PLAYING) WARN("Probable buffer underrun\n"); else TRACE("Buffer starting or buffer underrun\n"); /* recover mixing for all buffers */ recover = TRUE; /* reset mix position to write position */ device->mixpos = writepos; ZeroMemory(device->buffer, device->buflen); } else if (playpos < device->playpos) { buf1 = device->buffer + device->playpos; buf2 = device->buffer; size1 = device->buflen - device->playpos; size2 = playpos; FillMemory(buf1, size1, nfiller); if (playpos && (!buf2 || !size2)) FIXME("%d: (%d, %d)=>(%d, %d) There should be an additional buffer here!!\n", __LINE__, device->playpos, device->mixpos, playpos, writepos); FillMemory(buf2, size2, nfiller); } else { buf1 = device->buffer + device->playpos; buf2 = NULL; size1 = playpos - device->playpos; size2 = 0; FillMemory(buf1, size1, nfiller); } device->playpos = playpos; /* find the maximum we can prebuffer from current write position */ maxq = (writelead < prebuff_max) ? (prebuff_max - writelead) : 0; TRACE("prebuff_left = %d, prebuff_max = %dx%d=%d, writelead=%d\n", prebuff_left, device->prebuf, device->fraglen, prebuff_max, writelead); ZeroMemory(device->mix_buffer, device->mix_buffer_len); /* do the mixing */ DSOUND_MixToPrimary(device, writepos, maxq, recover, &all_stopped); if (maxq + writepos > device->buflen) { DWORD todo = device->buflen - writepos; DWORD offs_float = (todo / device->pwfx->nBlockAlign) * device->pwfx->nChannels; device->normfunction(device->mix_buffer, device->buffer + writepos, todo); device->normfunction(device->mix_buffer + offs_float, device->buffer, maxq - todo); } else device->normfunction(device->mix_buffer, device->buffer + writepos, maxq); /* update the mix position, taking wrap-around into account */ device->mixpos = writepos + maxq; device->mixpos %= device->buflen; /* update prebuff left */ prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos); /* check if have a whole fragment */ if (prebuff_left >= device->fraglen){ /* update the wave queue */ DSOUND_WaveQueue(device, FALSE); /* buffers are full. start playing if applicable */ if(device->state == STATE_STARTING){ TRACE("started primary buffer\n"); if(DSOUND_PrimaryPlay(device) != DS_OK){ WARN("DSOUND_PrimaryPlay failed\n"); } else{ /* we are playing now */ device->state = STATE_PLAYING; } } /* buffers are full. start stopping if applicable */ if(device->state == STATE_STOPPED){ TRACE("restarting primary buffer\n"); if(DSOUND_PrimaryPlay(device) != DS_OK){ WARN("DSOUND_PrimaryPlay failed\n"); } else{ /* start stopping again. as soon as there is no more data, it will stop */ device->state = STATE_STOPPING; } } } /* if device was stopping, its for sure stopped when all buffers have stopped */ else if((all_stopped == TRUE) && (device->state == STATE_STOPPING)){ TRACE("All buffers have stopped. Stopping primary buffer\n"); device->state = STATE_STOPPED; /* stop the primary buffer now */ DSOUND_PrimaryStop(device); } } else if (device->state != STATE_STOPPED) { DSOUND_WaveQueue(device, TRUE); /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */ if (device->state == STATE_STARTING) { if (DSOUND_PrimaryPlay(device) != DS_OK) WARN("DSOUND_PrimaryPlay failed\n"); else device->state = STATE_PLAYING; } else if (device->state == STATE_STOPPING) { if (DSOUND_PrimaryStop(device) != DS_OK) WARN("DSOUND_PrimaryStop failed\n"); else device->state = STATE_STOPPED; } } LeaveCriticalSection(&(device->mixlock)); /* **** */ }
static void Play(audio_output_t *aout, block_t *block) { aout_sys_t *sys = aout->sys; HRESULT hr; Enter(); if (likely(sys->clock != NULL)) { UINT64 pos, qpcpos; IAudioClock_GetPosition(sys->clock, &pos, &qpcpos); qpcpos = (qpcpos + 5) / 10; /* 100ns -> 1µs */ /* NOTE: this assumes mdate() uses QPC() (which it currently does). */ aout_TimeReport(aout, qpcpos); } for (;;) { UINT32 frames; hr = IAudioClient_GetCurrentPadding(sys->client, &frames); if (FAILED(hr)) { msg_Err(aout, "cannot get current padding (error 0x%lx)", hr); break; } assert(frames <= sys->frames); frames = sys->frames - frames; if (frames > block->i_nb_samples) frames = block->i_nb_samples; BYTE *dst; hr = IAudioRenderClient_GetBuffer(sys->render, frames, &dst); if (FAILED(hr)) { msg_Err(aout, "cannot get buffer (error 0x%lx)", hr); break; } const size_t copy = frames * (size_t)aout->format.i_bytes_per_frame; memcpy(dst, block->p_buffer, copy); hr = IAudioRenderClient_ReleaseBuffer(sys->render, frames, 0); if (FAILED(hr)) { msg_Err(aout, "cannot release buffer (error 0x%lx)", hr); break; } IAudioClient_Start(sys->client); block->p_buffer += copy; block->i_buffer -= copy; block->i_nb_samples -= frames; if (block->i_nb_samples == 0) break; /* done */ /* Out of buffer space, sleep */ msleep(AOUT_MIN_PREPARE_TIME + block->i_nb_samples * CLOCK_FREQ / aout->format.i_rate); } Leave(); block_Release(block); }
static ALuint MMDevApiProc(ALvoid *ptr) { ALCdevice *device = ptr; MMDevApiData *data = device->ExtraData; UINT32 buffer_len, written; ALuint update_size, len; BYTE *buffer; HRESULT hr; hr = CoInitialize(NULL); if(FAILED(hr)) { ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr); aluHandleDisconnect(device); return 0; } hr = IAudioClient_GetBufferSize(data->client, &buffer_len); if(FAILED(hr)) { ERR("Failed to get audio buffer size: 0x%08lx\n", hr); aluHandleDisconnect(device); CoUninitialize(); return 0; } SetRTPriority(); update_size = device->UpdateSize; while(!data->killNow) { hr = IAudioClient_GetCurrentPadding(data->client, &written); if(FAILED(hr)) { ERR("Failed to get padding: 0x%08lx\n", hr); aluHandleDisconnect(device); break; } len = buffer_len - written; if(len < update_size) { DWORD res; res = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE); if(res != WAIT_OBJECT_0) ERR("WaitForSingleObjectEx error: 0x%lx\n", res); continue; } len -= len%update_size; hr = IAudioRenderClient_GetBuffer(data->render, len, &buffer); if(SUCCEEDED(hr)) { aluMixData(device, buffer, len); hr = IAudioRenderClient_ReleaseBuffer(data->render, len, 0); } if(FAILED(hr)) { ERR("Failed to buffer data: 0x%08lx\n", hr); aluHandleDisconnect(device); break; } } CoUninitialize(); return 0; }
static void test_capture(IAudioClient *ac, HANDLE handle, WAVEFORMATEX *wfx) { IAudioCaptureClient *acc; HRESULT hr; UINT32 frames, next, pad, sum = 0; BYTE *data; DWORD flags; UINT64 pos, qpc; REFERENCE_TIME period; hr = IAudioClient_GetService(ac, &IID_IAudioCaptureClient, (void**)&acc); ok(hr == S_OK, "IAudioClient_GetService(IID_IAudioCaptureClient) returns %08x\n", hr); if (hr != S_OK) return; frames = 0xabadcafe; data = (void*)0xdeadf00d; flags = 0xabadcafe; pos = qpc = 0xdeadbeef; hr = IAudioCaptureClient_GetBuffer(acc, &data, &frames, &flags, &pos, &qpc); ok(hr == AUDCLNT_S_BUFFER_EMPTY, "Initial IAudioCaptureClient_GetBuffer returns %08x\n", hr); /* should be empty right after start. Otherwise consume one packet */ if(hr == S_OK){ hr = IAudioCaptureClient_ReleaseBuffer(acc, frames); ok(hr == S_OK, "Releasing buffer returns %08x\n", hr); sum += frames; frames = 0xabadcafe; data = (void*)0xdeadf00d; flags = 0xabadcafe; pos = qpc = 0xdeadbeef; hr = IAudioCaptureClient_GetBuffer(acc, &data, &frames, &flags, &pos, &qpc); ok(hr == AUDCLNT_S_BUFFER_EMPTY, "Initial IAudioCaptureClient_GetBuffer returns %08x\n", hr); } if(hr == AUDCLNT_S_BUFFER_EMPTY){ ok(!frames, "frames changed to %u\n", frames); ok(data == (void*)0xdeadf00d, "data changed to %p\n", data); ok(flags == 0xabadcafe, "flags changed to %x\n", flags); ok(pos == 0xdeadbeef, "position changed to %u\n", (UINT)pos); ok(qpc == 0xdeadbeef, "timer changed to %u\n", (UINT)qpc); /* GetNextPacketSize yields 0 if no data is yet available * it is not constantly period_size * SamplesPerSec */ hr = IAudioCaptureClient_GetNextPacketSize(acc, &next); ok(hr == S_OK, "IAudioCaptureClient_GetNextPacketSize returns %08x\n", hr); ok(!next, "GetNextPacketSize %u\n", next); } hr = IAudioCaptureClient_ReleaseBuffer(acc, frames); ok(hr == S_OK, "Releasing buffer returns %08x\n", hr); sum += frames; ok(ResetEvent(handle), "ResetEvent\n"); hr = IAudioCaptureClient_GetNextPacketSize(acc, &next); ok(hr == S_OK, "IAudioCaptureClient_GetNextPacketSize returns %08x\n", hr); hr = IAudioClient_GetCurrentPadding(ac, &pad); ok(hr == S_OK, "GetCurrentPadding call returns %08x\n", hr); ok(next == pad, "GetNextPacketSize %u vs. GCP %u\n", next, pad); /* later GCP will grow, while GNPS is 0 or period size */ hr = IAudioCaptureClient_GetNextPacketSize(acc, NULL); ok(hr == E_POINTER, "IAudioCaptureClient_GetNextPacketSize(NULL) returns %08x\n", hr); data = (void*)0xdeadf00d; frames = 0xdeadbeef; flags = 0xabadcafe; hr = IAudioCaptureClient_GetBuffer(acc, &data, NULL, NULL, NULL, NULL); ok(hr == E_POINTER, "IAudioCaptureClient_GetBuffer(data, NULL, NULL) returns %08x\n", hr); hr = IAudioCaptureClient_GetBuffer(acc, NULL, &frames, NULL, NULL, NULL); ok(hr == E_POINTER, "IAudioCaptureClient_GetBuffer(NULL, &frames, NULL) returns %08x\n", hr); hr = IAudioCaptureClient_GetBuffer(acc, NULL, NULL, &flags, NULL, NULL); ok(hr == E_POINTER, "IAudioCaptureClient_GetBuffer(NULL, NULL, &flags) returns %08x\n", hr); hr = IAudioCaptureClient_GetBuffer(acc, &data, &frames, NULL, NULL, NULL); ok(hr == E_POINTER, "IAudioCaptureClient_GetBuffer(&ata, &frames, NULL) returns %08x\n", hr); ok((DWORD_PTR)data == 0xdeadf00d, "data is reset to %p\n", data); ok(frames == 0xdeadbeef, "frames is reset to %08x\n", frames); ok(flags == 0xabadcafe, "flags is reset to %08x\n", flags); hr = IAudioClient_GetDevicePeriod(ac, &period, NULL); ok(hr == S_OK, "GetDevicePeriod failed: %08x\n", hr); period = MulDiv(period, wfx->nSamplesPerSec, 10000000); /* as in render.c */ ok(WaitForSingleObject(handle, 1000) == WAIT_OBJECT_0, "Waiting on event handle failed!\n"); data = (void*)0xdeadf00d; hr = IAudioCaptureClient_GetBuffer(acc, &data, &frames, &flags, &pos, &qpc); ok(hr == S_OK || hr == AUDCLNT_S_BUFFER_EMPTY, "Valid IAudioCaptureClient_GetBuffer returns %08x\n", hr); if (hr == S_OK){ ok(frames, "Amount of frames locked is 0!\n"); /* broken: some w7 machines return pad == 0 and DATA_DISCONTINUITY here, * AUDCLNT_S_BUFFER_EMPTY above, yet pos == 1-2 * period rather than 0 */ ok(pos == sum || broken(pos == period || pos == 2*period), "Position %u expected %u\n", (UINT)pos, sum); sum = pos; }else if (hr == AUDCLNT_S_BUFFER_EMPTY){ ok(!frames, "Amount of frames locked with empty buffer is %u!\n", frames); ok(data == (void*)0xdeadf00d, "No data changed to %p\n", data); } trace("Wait'ed position %d pad %u flags %x, amount of frames locked: %u\n", hr==S_OK ? (UINT)pos : -1, pad, flags, frames); hr = IAudioCaptureClient_GetNextPacketSize(acc, &next); ok(hr == S_OK, "IAudioCaptureClient_GetNextPacketSize returns %08x\n", hr); ok(next == frames, "GetNextPacketSize %u vs. GetBuffer %u\n", next, frames); hr = IAudioCaptureClient_ReleaseBuffer(acc, frames); ok(hr == S_OK, "Releasing buffer returns %08x\n", hr); hr = IAudioCaptureClient_ReleaseBuffer(acc, 0); ok(hr == S_OK, "Releasing 0 returns %08x\n", hr); hr = IAudioCaptureClient_GetNextPacketSize(acc, &next); ok(hr == S_OK, "IAudioCaptureClient_GetNextPacketSize returns %08x\n", hr); if (frames) { hr = IAudioCaptureClient_ReleaseBuffer(acc, frames); ok(hr == AUDCLNT_E_OUT_OF_ORDER, "Releasing buffer twice returns %08x\n", hr); sum += frames; } Sleep(350); /* for sure there's data now */ hr = IAudioClient_GetCurrentPadding(ac, &pad); ok(hr == S_OK, "GetCurrentPadding call returns %08x\n", hr); /** GetNextPacketSize * returns either 0 or one period worth of frames * whereas GetCurrentPadding grows when input is not consumed. */ hr = IAudioCaptureClient_GetNextPacketSize(acc, &next); ok(hr == S_OK, "IAudioCaptureClient_GetNextPacketSize returns %08x\n", hr); ok(next < pad, "GetNextPacketSize %u vs. GCP %u\n", next, pad); hr = IAudioCaptureClient_GetBuffer(acc, &data, &frames, &flags, &pos, &qpc); ok(hr == S_OK, "Valid IAudioCaptureClient_GetBuffer returns %08x\n", hr); ok(next == frames, "GetNextPacketSize %u vs. GetBuffer %u\n", next, frames); if(hr == S_OK){ UINT32 frames2 = frames; UINT64 pos2, qpc2; ok(frames, "Amount of frames locked is 0!\n"); ok(pos == sum, "Position %u expected %u\n", (UINT)pos, sum); hr = IAudioCaptureClient_ReleaseBuffer(acc, 0); ok(hr == S_OK, "Releasing 0 returns %08x\n", hr); /* GCP did not decrement, no data consumed */ hr = IAudioClient_GetCurrentPadding(ac, &frames); ok(hr == S_OK, "GetCurrentPadding call returns %08x\n", hr); ok(frames == pad || frames == pad + next /* concurrent feeder */, "GCP %u past ReleaseBuffer(0) initially %u\n", frames, pad); /* should re-get the same data */ hr = IAudioCaptureClient_GetBuffer(acc, &data, &frames, &flags, &pos2, &qpc2); ok(hr == S_OK, "Valid IAudioCaptureClient_GetBuffer returns %08x\n", hr); ok(frames2 == frames, "GetBuffer after ReleaseBuffer(0) %u/%u\n", frames2, frames); ok(pos2 == pos, "Position after ReleaseBuffer(0) %u/%u\n", (UINT)pos2, (UINT)pos); todo_wine ok(qpc2 == qpc, "HPC after ReleaseBuffer(0) %u vs. %u\n", (UINT)qpc2, (UINT)qpc); } /* trace after the GCP test because log output to MS-DOS console disturbs timing */ trace("Sleep.1 position %d pad %u flags %x, amount of frames locked: %u\n", hr==S_OK ? (UINT)pos : -1, pad, flags, frames); if(hr == S_OK){ UINT32 frames2 = 0xabadcafe; BYTE *data2 = (void*)0xdeadf00d; flags = 0xabadcafe; ok(pos == sum, "Position %u expected %u\n", (UINT)pos, sum); pos = qpc = 0xdeadbeef; hr = IAudioCaptureClient_GetBuffer(acc, &data2, &frames2, &flags, &pos, &qpc); ok(hr == AUDCLNT_E_OUT_OF_ORDER, "Out of order IAudioCaptureClient_GetBuffer returns %08x\n", hr); ok(frames2 == 0xabadcafe, "Out of order frames changed to %x\n", frames2); ok(data2 == (void*)0xdeadf00d, "Out of order data changed to %p\n", data2); ok(flags == 0xabadcafe, "Out of order flags changed to %x\n", flags); ok(pos == 0xdeadbeef, "Out of order position changed to %x\n", (UINT)pos); ok(qpc == 0xdeadbeef, "Out of order timer changed to %x\n", (UINT)qpc); hr = IAudioCaptureClient_ReleaseBuffer(acc, frames+1); ok(hr == AUDCLNT_E_INVALID_SIZE, "Releasing buffer+1 returns %08x\n", hr); hr = IAudioCaptureClient_ReleaseBuffer(acc, 1); ok(hr == AUDCLNT_E_INVALID_SIZE, "Releasing 1 returns %08x\n", hr); hr = IAudioClient_Reset(ac); ok(hr == AUDCLNT_E_NOT_STOPPED, "Reset failed: %08x\n", hr); } hr = IAudioCaptureClient_ReleaseBuffer(acc, frames); ok(hr == S_OK, "Releasing buffer returns %08x\n", hr); if (frames) { sum += frames; hr = IAudioCaptureClient_ReleaseBuffer(acc, frames); ok(hr == AUDCLNT_E_OUT_OF_ORDER, "Releasing buffer twice returns %08x\n", hr); } frames = period; ok(next == frames, "GetNextPacketSize %u vs. GetDevicePeriod %u\n", next, frames); /* GetBufferSize is not a multiple of the period size! */ hr = IAudioClient_GetBufferSize(ac, &next); ok(hr == S_OK, "GetBufferSize failed: %08x\n", hr); trace("GetBufferSize %u period size %u\n", next, frames); Sleep(400); /* overrun */ hr = IAudioClient_GetCurrentPadding(ac, &pad); ok(hr == S_OK, "GetCurrentPadding call returns %08x\n", hr); hr = IAudioCaptureClient_GetBuffer(acc, &data, &frames, &flags, &pos, &qpc); ok(hr == S_OK, "Valid IAudioCaptureClient_GetBuffer returns %08x\n", hr); trace("Overrun position %d pad %u flags %x, amount of frames locked: %u\n", hr==S_OK ? (UINT)pos : -1, pad, flags, frames); if(hr == S_OK){ /* The discontinuity is reported here, but is this an old or new packet? */ todo_wine ok(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, "expect DISCONTINUITY %x\n", flags); ok(pad == next, "GCP %u vs. BufferSize %u\n", (UINT32)pad, next); /* Native's position is one period further than what we read. * Perhaps that's precisely the meaning of DATA_DISCONTINUITY: * signal when the position jump left a gap. */ todo_wine ok(pos == sum + frames, "Position %u gap %d\n", (UINT)pos, (UINT)pos - sum); if(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) sum = pos; } hr = IAudioCaptureClient_ReleaseBuffer(acc, frames); ok(hr == S_OK, "Releasing buffer returns %08x\n", hr); sum += frames; hr = IAudioClient_GetCurrentPadding(ac, &pad); ok(hr == S_OK, "GetCurrentPadding call returns %08x\n", hr); hr = IAudioCaptureClient_GetBuffer(acc, &data, &frames, &flags, &pos, &qpc); ok(hr == S_OK, "Valid IAudioCaptureClient_GetBuffer returns %08x\n", hr); trace("Cont'ed position %d pad %u flags %x, amount of frames locked: %u\n", hr==S_OK ? (UINT)pos : -1, pad, flags, frames); if(hr == S_OK){ ok(pos == sum, "Position %u expected %u\n", (UINT)pos, sum); ok(!flags, "flags %u\n", flags); hr = IAudioCaptureClient_ReleaseBuffer(acc, frames); ok(hr == S_OK, "Releasing buffer returns %08x\n", hr); sum += frames; } hr = IAudioClient_Stop(ac); ok(hr == S_OK, "Stop on a started stream returns %08x\n", hr); hr = IAudioClient_Start(ac); ok(hr == S_OK, "Start on a stopped stream returns %08x\n", hr); hr = IAudioCaptureClient_GetBuffer(acc, &data, &frames, &flags, &pos, &qpc); ok(hr == S_OK, "Valid IAudioCaptureClient_GetBuffer returns %08x\n", hr); hr = IAudioClient_GetCurrentPadding(ac, &pad); ok(hr == S_OK, "GetCurrentPadding call returns %08x\n", hr); trace("Restart position %d pad %u flags %x, amount of frames locked: %u\n", hr==S_OK ? (UINT)pos : -1, pad, flags, frames); ok(pad > sum, "restarted GCP %u\n", pad); /* GCP is still near buffer size */ if(frames){ ok(pos == sum, "Position %u expected %u\n", (UINT)pos, sum); ok(!flags, "flags %u\n", flags); hr = IAudioCaptureClient_ReleaseBuffer(acc, frames); ok(hr == S_OK, "Releasing buffer returns %08x\n", hr); sum += frames; } hr = IAudioClient_Stop(ac); ok(hr == S_OK, "Stop on a started stream returns %08x\n", hr); hr = IAudioClient_Reset(ac); ok(hr == S_OK, "Reset on a stopped stream returns %08x\n", hr); sum += pad - frames; hr = IAudioClient_Start(ac); ok(hr == S_OK, "Start on a stopped stream returns %08x\n", hr); hr = IAudioClient_GetCurrentPadding(ac, &pad); ok(hr == S_OK, "GetCurrentPadding call returns %08x\n", hr); flags = 0xabadcafe; hr = IAudioCaptureClient_GetBuffer(acc, &data, &frames, &flags, &pos, &qpc); ok(hr == AUDCLNT_S_BUFFER_EMPTY || /*PulseAudio*/hr == S_OK, "Initial IAudioCaptureClient_GetBuffer returns %08x\n", hr); trace("Reset position %d pad %u flags %x, amount of frames locked: %u\n", hr==S_OK ? (UINT)pos : -1, pad, flags, frames); if(hr == S_OK){ /* Only PulseAudio goes here; despite snd_pcm_drop it manages * to fill GetBufferSize with a single snd_pcm_read */ trace("Test marked todo: only PulseAudio gets here\n"); todo_wine ok(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, "expect DISCONTINUITY %x\n", flags); /* Reset zeroes padding, not the position */ ok(pos >= sum, "Position %u last %u\n", (UINT)pos, sum); /*sum = pos; check after next GetBuffer */ hr = IAudioCaptureClient_ReleaseBuffer(acc, frames); ok(hr == S_OK, "Releasing buffer returns %08x\n", hr); sum += frames; } else if(hr == AUDCLNT_S_BUFFER_EMPTY){ ok(!pad, "resetted GCP %u\n", pad); Sleep(180); } hr = IAudioClient_GetCurrentPadding(ac, &pad); ok(hr == S_OK, "GetCurrentPadding call returns %08x\n", hr); hr = IAudioCaptureClient_GetBuffer(acc, &data, &frames, &flags, &pos, &qpc); ok(hr == S_OK, "Valid IAudioCaptureClient_GetBuffer returns %08x\n", hr); trace("Running position %d pad %u flags %x, amount of frames locked: %u\n", hr==S_OK ? (UINT)pos : -1, pad, flags, frames); if(hr == S_OK){ /* Some w7 machines signal DATA_DISCONTINUITY here following the * previous AUDCLNT_S_BUFFER_EMPTY, others not. What logic? */ ok(pos >= sum, "Position %u gap %d\n", (UINT)pos, (UINT)pos - sum); IAudioCaptureClient_ReleaseBuffer(acc, frames); } IAudioCaptureClient_Release(acc); }