JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_jmfext_media_protocol_wasapi_WASAPI_IAudioRenderClient_1Write (JNIEnv *env, jclass clazz, jlong thiz, jbyteArray data, jint offset, jint length, jint srcSampleSize, jint srcChannels, jint dstSampleSize, jint dstChannels) { jint srcFrameSize; UINT32 numFramesRequested; HRESULT hr; IAudioRenderClient *iAudioRenderClient = (IAudioRenderClient *) (intptr_t) thiz; BYTE *pData; jint written; srcFrameSize = srcSampleSize * srcChannels; numFramesRequested = length / srcFrameSize; hr = IAudioRenderClient_GetBuffer( iAudioRenderClient, numFramesRequested, &pData); if (SUCCEEDED(hr)) { jbyte *data_; UINT32 numFramesWritten; data_ = (*env)->GetPrimitiveArrayCritical(env, data, NULL); if (data_) { numFramesWritten = WASAPI_audiocopy( data_ + offset, srcSampleSize, srcChannels, pData, dstSampleSize, dstChannels, numFramesRequested); (*env)->ReleasePrimitiveArrayCritical(env, data, data_, JNI_ABORT); } else numFramesWritten = 0; /* An OutOfMemoryError has been thrown. */ hr = IAudioRenderClient_ReleaseBuffer( iAudioRenderClient, numFramesWritten, /* dwFlags */ 0); written = numFramesWritten * srcFrameSize; if (FAILED(hr)) WASAPI_throwNewHResultException(env, hr, __func__, __LINE__); } else { written = 0; WASAPI_throwNewHResultException(env, hr, __func__, __LINE__); } return written; }
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; }
static GstFlowReturn gst_wasapi_sink_render (GstBaseSink * sink, GstBuffer * buffer) { GstWasapiSink *self = GST_WASAPI_SINK (sink); GstFlowReturn ret = GST_FLOW_OK; HRESULT hr; gint16 *src = (gint16 *) GST_BUFFER_DATA (buffer); gint16 *dst = NULL; guint nsamples = GST_BUFFER_SIZE (buffer) / sizeof (gint16); guint i; WaitForSingleObject (self->event_handle, INFINITE); hr = IAudioRenderClient_GetBuffer (self->render_client, nsamples, (BYTE **) & dst); if (hr != S_OK) { GST_ELEMENT_ERROR (self, RESOURCE, WRITE, (NULL), ("IAudioRenderClient::GetBuffer () failed: %s", gst_wasapi_util_hresult_to_string (hr))); ret = GST_FLOW_ERROR; goto beach; } for (i = 0; i < nsamples; i++) { dst[0] = *src; dst[1] = *src; src++; dst += 2; } hr = IAudioRenderClient_ReleaseBuffer (self->render_client, nsamples, 0); if (hr != S_OK) { GST_ERROR_OBJECT (self, "IAudioRenderClient::ReleaseBuffer () failed: %s", gst_wasapi_util_hresult_to_string (hr)); ret = GST_FLOW_ERROR; goto beach; } beach: return ret; }
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 gint gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length) { GstWasapiSink *self = GST_WASAPI_SINK (asink); HRESULT hr; gint16 *dst = NULL; guint nsamples; nsamples = length / self->info.bpf; WaitForSingleObject (self->event_handle, INFINITE); hr = IAudioRenderClient_GetBuffer (self->render_client, nsamples, (BYTE **) & dst); if (hr != S_OK) { GST_ELEMENT_ERROR (self, RESOURCE, WRITE, (NULL), ("IAudioRenderClient::GetBuffer () failed: %s", gst_wasapi_util_hresult_to_string (hr))); length = 0; goto beach; } memcpy (dst, data, length); hr = IAudioRenderClient_ReleaseBuffer (self->render_client, nsamples, 0); if (hr != S_OK) { GST_ERROR_OBJECT (self, "IAudioRenderClient::ReleaseBuffer () failed: %s", gst_wasapi_util_hresult_to_string (hr)); length = 0; goto beach; } beach: return length; }
static void test_clock(void) { HRESULT hr; IAudioClient *ac; IAudioClock *acl; IAudioRenderClient *arc; UINT64 freq, pos, pcpos, last; BYTE *data; WAVEFORMATEX *pwfx; 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_GetService(ac, &IID_IAudioClock, (void**)&acl); ok(hr == S_OK, "GetService(IAudioClock) failed: %08x\n", hr); hr = IAudioClock_GetFrequency(acl, &freq); ok(hr == S_OK, "GetFrequency failed: %08x\n", hr); hr = IAudioClock_GetPosition(acl, NULL, NULL); ok(hr == E_POINTER, "GetPosition wrong error: %08x\n", hr); pcpos = 0; hr = IAudioClock_GetPosition(acl, &pos, &pcpos); ok(hr == S_OK, "GetPosition failed: %08x\n", hr); ok(pos == 0, "GetPosition returned non-zero pos before being started\n"); ok(pcpos != 0, "GetPosition returned zero pcpos\n"); last = pos; hr = IAudioClient_GetService(ac, &IID_IAudioRenderClient, (void**)&arc); ok(hr == S_OK, "GetService(IAudioRenderClient) failed: %08x\n", hr); hr = IAudioRenderClient_GetBuffer(arc, pwfx->nSamplesPerSec / 2., &data); ok(hr == S_OK, "GetBuffer failed: %08x\n", hr); hr = IAudioRenderClient_ReleaseBuffer(arc, pwfx->nSamplesPerSec / 2., AUDCLNT_BUFFERFLAGS_SILENT); ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr); hr = IAudioClock_GetPosition(acl, &pos, NULL); ok(hr == S_OK, "GetPosition failed: %08x\n", hr); ok(pos == 0, "GetPosition returned non-zero pos before being started\n"); hr = IAudioClient_Start(ac); ok(hr == S_OK, "Start failed: %08x\n", hr); Sleep(100); hr = IAudioClock_GetPosition(acl, &pos, NULL); ok(hr == S_OK, "GetPosition failed: %08x\n", hr); ok(pos > 0, "Position should have been further along...\n"); last = pos; hr = IAudioClient_Stop(ac); ok(hr == S_OK, "Stop failed: %08x\n", hr); hr = IAudioClock_GetPosition(acl, &pos, NULL); ok(hr == S_OK, "GetPosition failed: %08x\n", hr); ok(pos >= last, "Position should have been further along...\n"); last = pos; hr = IAudioClient_Start(ac); ok(hr == S_OK, "Start failed: %08x\n", hr); Sleep(100); hr = IAudioClient_Stop(ac); ok(hr == S_OK, "Stop failed: %08x\n", hr); hr = IAudioClock_GetPosition(acl, &pos, NULL); ok(hr == S_OK, "GetPosition failed: %08x\n", hr); ok(pos >= last, "Position should have been further along...\n"); last = pos; hr = IAudioClock_GetPosition(acl, &pos, NULL); ok(hr == S_OK, "GetPosition failed: %08x\n", hr); ok(pos == last, "Position should have been further along...\n"); hr = IAudioClient_Reset(ac); ok(hr == S_OK, "Reset failed: %08x\n", hr); hr = IAudioClock_GetPosition(acl, &pos, NULL); ok(hr == S_OK, "GetPosition failed: %08x\n", hr); ok(pos == 0, "GetPosition returned non-zero pos after Reset\n"); last = pos; hr = IAudioRenderClient_GetBuffer(arc, pwfx->nSamplesPerSec / 2., &data); ok(hr == S_OK, "GetBuffer failed: %08x\n", hr); hr = IAudioRenderClient_ReleaseBuffer(arc, pwfx->nSamplesPerSec / 2., AUDCLNT_BUFFERFLAGS_SILENT); ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr); hr = IAudioClock_GetPosition(acl, &pos, NULL); ok(hr == S_OK, "GetPosition failed: %08x\n", hr); ok(pos == 0, "GetPosition returned non-zero pos after Reset\n"); last = pos; hr = IAudioClient_Start(ac); ok(hr == S_OK, "Start failed: %08x\n", hr); Sleep(100); hr = IAudioClock_GetPosition(acl, &pos, NULL); ok(hr == S_OK, "GetPosition failed: %08x\n", hr); ok(pos > last, "Position should have been further along...\n"); hr = IAudioClient_Stop(ac); ok(hr == S_OK, "Stop failed: %08x\n", hr); hr = IAudioClock_GetPosition(acl, &pos, NULL); ok(hr == S_OK, "GetPosition failed: %08x\n", hr); ok(pos >= last, "Position should have been further along...\n"); IAudioClock_Release(acl); IAudioClient_Release(ac); }
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); }
static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force) { DWORD prebuf_frames, prebuf_bytes, read_offs_bytes; BYTE *buffer; HRESULT hr; TRACE("(%p)\n", device); read_offs_bytes = (device->playing_offs_bytes + device->in_mmdev_bytes) % device->buflen; TRACE("read_offs_bytes = %u, playing_offs_bytes = %u, in_mmdev_bytes: %u, prebuf = %u\n", read_offs_bytes, device->playing_offs_bytes, device->in_mmdev_bytes, device->prebuf); if (!force) { if(device->mixpos < device->playing_offs_bytes) prebuf_bytes = device->mixpos + device->buflen - device->playing_offs_bytes; else prebuf_bytes = device->mixpos - device->playing_offs_bytes; } else /* buffer the maximum amount of frags */ prebuf_bytes = device->prebuf * device->fraglen; /* limit to the queue we have left */ if(device->in_mmdev_bytes + prebuf_bytes > device->prebuf * device->fraglen) prebuf_bytes = device->prebuf * device->fraglen - device->in_mmdev_bytes; TRACE("prebuf_bytes = %u\n", prebuf_bytes); if(!prebuf_bytes) return; if(prebuf_bytes + read_offs_bytes > device->buflen){ DWORD chunk_bytes = device->buflen - read_offs_bytes; prebuf_frames = chunk_bytes / device->pwfx->nBlockAlign; prebuf_bytes -= chunk_bytes; }else{ prebuf_frames = prebuf_bytes / device->pwfx->nBlockAlign; prebuf_bytes = 0; } hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer); if(FAILED(hr)){ WARN("GetBuffer failed: %08x\n", hr); return; } memcpy(buffer, device->buffer + read_offs_bytes, prebuf_frames * device->pwfx->nBlockAlign); hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0); if(FAILED(hr)){ WARN("ReleaseBuffer failed: %08x\n", hr); return; } device->in_mmdev_bytes += prebuf_frames * device->pwfx->nBlockAlign; /* check if anything wrapped */ if(prebuf_bytes > 0){ prebuf_frames = prebuf_bytes / device->pwfx->nBlockAlign; hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer); if(FAILED(hr)){ WARN("GetBuffer failed: %08x\n", hr); return; } memcpy(buffer, device->buffer, prebuf_frames * device->pwfx->nBlockAlign); hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0); if(FAILED(hr)){ WARN("ReleaseBuffer failed: %08x\n", hr); return; } device->in_mmdev_bytes += prebuf_frames * device->pwfx->nBlockAlign; } TRACE("in_mmdev_bytes now = %i\n", device->in_mmdev_bytes); }
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 void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force) { DWORD prebuf_frames, buf_offs_bytes, wave_fragpos; int prebuf_frags; BYTE *buffer; HRESULT hr; TRACE("(%p)\n", device); /* calculate the current wave frag position */ wave_fragpos = (device->pwplay + device->pwqueue) % device->helfrags; /* calculate the current wave write position */ buf_offs_bytes = wave_fragpos * device->fraglen; TRACE("wave_fragpos = %i, buf_offs_bytes = %i, pwqueue = %i, prebuf = %i\n", wave_fragpos, buf_offs_bytes, device->pwqueue, device->prebuf); if (!force) { /* check remaining prebuffered frags */ prebuf_frags = device->mixpos / device->fraglen; if (prebuf_frags == device->helfrags) --prebuf_frags; TRACE("wave_fragpos = %d, mixpos_frags = %d\n", wave_fragpos, prebuf_frags); if (prebuf_frags < wave_fragpos) prebuf_frags += device->helfrags; prebuf_frags -= wave_fragpos; TRACE("wanted prebuf_frags = %d\n", prebuf_frags); } else /* buffer the maximum amount of frags */ prebuf_frags = device->prebuf; /* limit to the queue we have left */ if ((prebuf_frags + device->pwqueue) > device->prebuf) prebuf_frags = device->prebuf - device->pwqueue; TRACE("prebuf_frags = %i\n", prebuf_frags); if(!prebuf_frags) return; /* adjust queue */ device->pwqueue += prebuf_frags; prebuf_frames = ((prebuf_frags + wave_fragpos > device->helfrags) ? (device->helfrags - wave_fragpos) : (prebuf_frags)) * device->fraglen / device->pwfx->nBlockAlign; hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer); if(FAILED(hr)){ WARN("GetBuffer failed: %08x\n", hr); return; } memcpy(buffer, device->buffer + buf_offs_bytes, prebuf_frames * device->pwfx->nBlockAlign); hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0); if(FAILED(hr)){ WARN("ReleaseBuffer failed: %08x\n", hr); return; } /* check if anything wrapped */ prebuf_frags = prebuf_frags + wave_fragpos - device->helfrags; if(prebuf_frags > 0){ prebuf_frames = prebuf_frags * device->fraglen / device->pwfx->nBlockAlign; hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer); if(FAILED(hr)){ WARN("GetBuffer failed: %08x\n", hr); return; } memcpy(buffer, device->buffer, prebuf_frames * device->pwfx->nBlockAlign); hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0); if(FAILED(hr)){ WARN("ReleaseBuffer failed: %08x\n", hr); return; } } TRACE("queue now = %i\n", device->pwqueue); }
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; }