Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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);
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 5
0
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;
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
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);
}
Ejemplo n.º 8
0
Archivo: mixer.c Proyecto: behda/wine
/**
 * 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));
	/* **** */
}
Ejemplo n.º 9
0
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);
}
Ejemplo n.º 10
0
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;
}
Ejemplo n.º 11
0
Archivo: capture.c Proyecto: hejin/wine
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);
}