winecoreaudio.drv: Rewrite capture mode.

This commit is contained in:
Andrew Eikum 2013-08-21 10:16:05 -05:00 committed by Alexandre Julliard
parent fdfff7cf60
commit 927324ce8f
1 changed files with 104 additions and 98 deletions

View File

@ -61,8 +61,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(coreaudio);
#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
#define CAPTURE_BUFFERS 5
static const REFERENCE_TIME DefaultPeriod = 200000;
static const REFERENCE_TIME MinimumPeriod = 100000;
@ -134,11 +132,12 @@ struct ACImpl {
AudioQueueRef aqueue;
AudioObjectPropertyScope scope;
HANDLE timer;
UINT32 period_ms, bufsize_frames, inbuf_frames;
UINT32 period_ms, bufsize_frames, inbuf_frames, read_offs_bytes, period_frames;
UINT64 last_time, written_frames;
AudioQueueBufferRef public_buffer;
UINT32 getbuf_last;
int playing;
BYTE *tmp_buffer, *capture_buf;
Float64 highest_sampletime, next_sampletime;
@ -199,7 +198,6 @@ static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
static struct list g_sessions = LIST_INIT(g_sessions);
static HRESULT AudioCaptureClient_GetNextPacket(ACImpl *This, UINT32 *frames);
static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
static HRESULT ca_setvol(ACImpl *This, UINT32 index);
@ -739,14 +737,53 @@ static UINT64 get_current_aqbuffer_position(ACImpl *This, int mode)
static void avail_update(ACImpl *This)
{
AQBuffer *buf, *next;
OSStatus sc;
LIST_FOR_EACH_ENTRY_SAFE(buf, next, &This->queued_buffers, AQBuffer, entry){
if(buf->used)
break;
if(This->dataflow == eCapture)
This->inbuf_frames += buf->buf->mAudioDataByteSize / This->fmt->nBlockAlign;
list_remove(&buf->entry);
list_add_tail(&This->avail_buffers, &buf->entry);
if(This->dataflow == eCapture){
DWORD bufsize_bytes = This->bufsize_frames * This->fmt->nBlockAlign;
DWORD inbuf_bytes = This->inbuf_frames * This->fmt->nBlockAlign;
LIST_FOR_EACH_ENTRY_SAFE(buf, next, &This->queued_buffers, AQBuffer, entry){
DWORD buffer_bytes = buf->buf->mAudioDataByteSize, to_copy_bytes;
if(buf->used)
break;
to_copy_bytes = bufsize_bytes - (This->read_offs_bytes + inbuf_bytes) % bufsize_bytes;
if(buffer_bytes <= to_copy_bytes){
memcpy(This->capture_buf + (This->read_offs_bytes + inbuf_bytes) % bufsize_bytes,
buf->buf->mAudioData, buffer_bytes);
}else{
memcpy(This->capture_buf + (This->read_offs_bytes + inbuf_bytes) % bufsize_bytes,
buf->buf->mAudioData, to_copy_bytes);
memcpy(This->capture_buf, ((char *)buf->buf->mAudioData) + to_copy_bytes,
buffer_bytes - to_copy_bytes);
}
if(inbuf_bytes + buffer_bytes > bufsize_bytes){
This->read_offs_bytes += inbuf_bytes + buffer_bytes;
This->read_offs_bytes %= bufsize_bytes;
inbuf_bytes = bufsize_bytes;
}else
inbuf_bytes += buffer_bytes;
buf->used = TRUE;
list_remove(&buf->entry);
list_add_tail(&This->queued_buffers, &buf->entry);
sc = AudioQueueEnqueueBuffer(This->aqueue, buf->buf, 0, NULL);
if(sc != noErr)
WARN("EnqueueBuffer gave: %lx\n", sc);
}
This->inbuf_frames = inbuf_bytes / This->fmt->nBlockAlign;
}else{
LIST_FOR_EACH_ENTRY_SAFE(buf, next, &This->queued_buffers, AQBuffer, entry){
if(buf->used)
break;
list_remove(&buf->entry);
list_add_tail(&This->avail_buffers, &buf->entry);
}
}
}
@ -819,6 +856,8 @@ static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
LeaveCriticalSection(&g_sessions_lock);
}
HeapFree(GetProcessHeap(), 0, This->vols);
HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
HeapFree(GetProcessHeap(), 0, This->capture_buf);
CoTaskMemFree(This->fmt);
IMMDevice_Release(This->parent);
IUnknown_Release(This->pUnkFTMarshal);
@ -1191,18 +1230,21 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
}
This->period_ms = period / 10000;
This->period_frames = MulDiv(period, This->fmt->nSamplesPerSec, 10000000);
This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
if(This->dataflow == eCapture){
int i;
UInt32 bsize = ceil((This->bufsize_frames / (double)CAPTURE_BUFFERS) *
This->fmt->nBlockAlign);
for(i = 0; i < CAPTURE_BUFFERS; ++i){
int i, nbuffs = (This->bufsize_frames / This->period_frames) + 1;
This->capture_buf = HeapAlloc(GetProcessHeap(), 0, This->bufsize_frames * This->fmt->nBlockAlign);
for(i = 0; i < nbuffs; ++i){
AQBuffer *buf;
buf = HeapAlloc(GetProcessHeap(), 0, sizeof(AQBuffer));
if(!buf){
HeapFree(GetProcessHeap(), 0, This->capture_buf);
AudioQueueDispose(This->aqueue, 1);
This->aqueue = NULL;
CoTaskMemFree(This->fmt);
@ -1211,8 +1253,9 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
return E_OUTOFMEMORY;
}
sc = AudioQueueAllocateBuffer(This->aqueue, bsize, &buf->buf);
sc = AudioQueueAllocateBuffer(This->aqueue, This->period_frames * This->fmt->nBlockAlign, &buf->buf);
if(sc != noErr){
HeapFree(GetProcessHeap(), 0, This->capture_buf);
AudioQueueDispose(This->aqueue, 1);
This->aqueue = NULL;
CoTaskMemFree(This->fmt);
@ -1235,6 +1278,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
if(!This->vols){
HeapFree(GetProcessHeap(), 0, This->capture_buf);
AudioQueueDispose(This->aqueue, 1);
This->aqueue = NULL;
CoTaskMemFree(This->fmt);
@ -1259,6 +1303,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
This->aqueue = NULL;
CoTaskMemFree(This->fmt);
This->fmt = NULL;
HeapFree(GetProcessHeap(), 0, This->capture_buf);
HeapFree(GetProcessHeap(), 0, This->vols);
This->vols = NULL;
OSSpinLockUnlock(&This->lock);
@ -1417,8 +1462,11 @@ static HRESULT AudioClient_GetCurrentPadding_nolock(ACImpl *This,
UINT64 bufpos;
bufpos = get_current_aqbuffer_position(This, BUFPOS_RELATIVE);
*numpad = This->inbuf_frames - bufpos;
}else
}else{
*numpad = This->inbuf_frames;
if(*numpad < This->period_frames)
*numpad = 0;
}
return S_OK;
}
@ -1663,10 +1711,8 @@ static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
return E_OUTOFMEMORY;
}
if(This->dataflow == eCapture){
UINT32 frames; /* enqueue packets */
AudioCaptureClient_GetNextPacket(This, &frames);
}
/* enqueue buffers */
avail_update(This);
This->playing = StateInTransition;
@ -1756,7 +1802,6 @@ static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
ACImpl *This = impl_from_IAudioClient(iface);
OSStatus sc;
QueuedBufInfo *bufinfo, *bufinfo2;
AQBuffer *buf;
TRACE("(%p)\n", This);
@ -1792,14 +1837,9 @@ static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
return osstatus_to_hresult(sc);
}
/* AQReset is synchronous */
list_move_tail(&This->avail_buffers, &This->queued_buffers);
if(This->dataflow == eRender){
This->written_frames = 0;
}else{
LIST_FOR_EACH_ENTRY(buf, &This->avail_buffers, AQBuffer, entry)
buf->buf->mAudioDataByteSize = 0;
This->written_frames += This->inbuf_frames;
}
This->inbuf_frames = 0;
@ -2205,49 +2245,12 @@ static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
return IAudioClient_Release(&This->IAudioClient_iface);
}
static HRESULT AudioCaptureClient_GetNextPacket(ACImpl *This, UINT32 *frames)
{
AQBuffer *buf;
OSStatus sc;
avail_update(This); /* once, not inside loop */
for(;;){
if(!This->public_buffer){
struct list *head = list_head(&This->avail_buffers);
if(!head){
*frames = 0;
return S_OK;
}
buf = LIST_ENTRY(head, AQBuffer, entry);
This->public_buffer = buf->buf;
list_remove(&buf->entry);
}else
buf = This->public_buffer->mUserData;
*frames = This->public_buffer->mAudioDataByteSize / This->fmt->nBlockAlign;
if(*frames)
return S_OK;
WARN("empty packet\n");
buf->used = TRUE;
sc = AudioQueueEnqueueBuffer(This->aqueue, This->public_buffer, 0, NULL);
if(sc != noErr){
ERR("Unable to enqueue buffer: %lx\n", sc);
/* Release will free This->public_buffer */
return AUDCLNT_E_DEVICE_INVALIDATED;
}else
list_add_tail(&This->queued_buffers, &buf->entry);
This->public_buffer = NULL;
}
}
static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
UINT64 *qpcpos)
{
ACImpl *This = impl_from_IAudioCaptureClient(iface);
HRESULT hr;
DWORD chunk_bytes;
TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
devpos, qpcpos);
@ -2262,36 +2265,46 @@ static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
return AUDCLNT_E_OUT_OF_ORDER;
}
hr = AudioCaptureClient_GetNextPacket(This, frames);
if(FAILED(hr)){
avail_update(This);
if(This->inbuf_frames < This->period_frames){
*frames = 0;
OSSpinLockUnlock(&This->lock);
return hr;
return AUDCLNT_S_BUFFER_EMPTY;
}
if((This->getbuf_last = *frames)){
*flags = 0;
*data = This->public_buffer->mAudioData;
*flags = 0;
if(devpos)
*devpos = This->written_frames;
if(qpcpos){ /* fixme: qpc of recording time */
LARGE_INTEGER stamp, freq;
QueryPerformanceCounter(&stamp);
QueryPerformanceFrequency(&freq);
*qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
}
chunk_bytes = This->bufsize_frames * This->fmt->nBlockAlign - This->read_offs_bytes;
if(chunk_bytes < This->period_frames * This->fmt->nBlockAlign){
if(!This->tmp_buffer)
This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, This->period_frames * This->fmt->nBlockAlign);
*data = This->tmp_buffer;
memcpy(*data, This->capture_buf + This->read_offs_bytes, chunk_bytes);
memcpy((*data) + chunk_bytes, This->capture_buf, This->period_frames * This->fmt->nBlockAlign - chunk_bytes);
}else
*data = This->capture_buf + This->read_offs_bytes;
This->getbuf_last = *frames = This->period_frames;
if(devpos)
*devpos = This->written_frames;
if(qpcpos){ /* fixme: qpc of recording time */
LARGE_INTEGER stamp, freq;
QueryPerformanceCounter(&stamp);
QueryPerformanceFrequency(&freq);
*qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
}
OSSpinLockUnlock(&This->lock);
return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
return S_OK;
}
static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
IAudioCaptureClient *iface, UINT32 done)
{
ACImpl *This = impl_from_IAudioCaptureClient(iface);
AQBuffer *buf;
OSStatus sc;
TRACE("(%p)->(%u)\n", This, done);
@ -2315,21 +2328,10 @@ static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
This->written_frames += done;
This->inbuf_frames -= done;
This->read_offs_bytes += done * This->fmt->nBlockAlign;
This->read_offs_bytes %= This->bufsize_frames * This->fmt->nBlockAlign;
This->getbuf_last = 0;
buf = This->public_buffer->mUserData;
buf->used = TRUE;
sc = AudioQueueEnqueueBuffer(This->aqueue, This->public_buffer, 0, NULL);
if(sc != noErr){
OSSpinLockUnlock(&This->lock);
/* fixme: can't zero public_buffer or we lose memory, but then
* GetBuffer will see that packet again and again. */
ERR("Unable to enqueue buffer: %lx\n", sc);
return AUDCLNT_E_DEVICE_INVALIDATED;
}else
list_add_tail(&This->queued_buffers, &buf->entry);
This->public_buffer = NULL;
OSSpinLockUnlock(&This->lock);
return S_OK;
@ -2339,7 +2341,6 @@ static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
IAudioCaptureClient *iface, UINT32 *frames)
{
ACImpl *This = impl_from_IAudioCaptureClient(iface);
HRESULT hr;
TRACE("(%p)->(%p)\n", This, frames);
@ -2348,11 +2349,16 @@ static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
OSSpinLockLock(&This->lock);
hr = AudioCaptureClient_GetNextPacket(This, frames);
avail_update(This);
if(This->inbuf_frames >= This->period_frames)
*frames = This->period_frames;
else
*frames = 0;
OSSpinLockUnlock(&This->lock);
return hr;
return S_OK;
}
static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =