mmdevapi: Add support for openal disconnected extension.

This commit is contained in:
Maarten Lankhorst 2010-05-04 13:30:40 +02:00 committed by Alexandre Julliard
parent 9da2bbfab7
commit 38517694bb
1 changed files with 142 additions and 30 deletions

View File

@ -65,14 +65,14 @@ typedef struct ACImpl {
BOOL init, running; BOOL init, running;
CRITICAL_SECTION *crst; CRITICAL_SECTION *crst;
HANDLE handle; HANDLE handle;
DWORD locked, flags, bufsize, pad, padpartial, ofs, psize; DWORD locked, flags, bufsize, pad, padpartial, ofs, psize, candisconnect;
BYTE *buffer; BYTE *buffer;
WAVEFORMATEX *pwfx; WAVEFORMATEX *pwfx;
ALuint source; ALuint source;
INT64 frameswritten; INT64 frameswritten;
REFERENCE_TIME laststamp; REFERENCE_TIME laststamp;
HANDLE timer_id; HANDLE timer_id;
ALCdevice *capdev; ALCdevice *dev;
ALint format; ALint format;
ACRender *render; ACRender *render;
@ -132,6 +132,15 @@ static void AudioSimpleVolume_Destroy(ASVolume *This);
static HRESULT AudioClock_Create(ACImpl *parent, AClock **ppv); static HRESULT AudioClock_Create(ACImpl *parent, AClock **ppv);
static void AudioClock_Destroy(AClock *This); static void AudioClock_Destroy(AClock *This);
static int valid_dev(ACImpl *This)
{
if (!This->dev)
return 0;
if (This->parent->flow == eRender && This->dev != This->parent->device)
return 0;
return 1;
}
static int get_format_PCM(WAVEFORMATEX *format) static int get_format_PCM(WAVEFORMATEX *format)
{ {
if (format->nChannels > 2) { if (format->nChannels > 2) {
@ -315,7 +324,9 @@ static void AudioClient_Destroy(ACImpl *This)
AudioSimpleVolume_Destroy(This->svolume); AudioSimpleVolume_Destroy(This->svolume);
if (This->clock) if (This->clock)
AudioClock_Destroy(This->clock); AudioClock_Destroy(This->clock);
if (This->parent->flow == eRender && This->init) { if (!valid_dev(This))
TRACE("Not destroying device since none exists\n");
else if (This->parent->flow == eRender) {
setALContext(This->parent->ctx); setALContext(This->parent->ctx);
IAudioClient_Stop((IAudioClient*)This); IAudioClient_Stop((IAudioClient*)This);
IAudioClient_Reset((IAudioClient*)This); IAudioClient_Reset((IAudioClient*)This);
@ -323,8 +334,8 @@ static void AudioClient_Destroy(ACImpl *This)
getALError(); getALError();
popALContext(); popALContext();
} }
if (This->capdev) else if (This->parent->flow == eCapture)
palcCaptureCloseDevice(This->capdev); palcCaptureCloseDevice(This->dev);
HeapFree(GetProcessHeap(), 0, This->pwfx); HeapFree(GetProcessHeap(), 0, This->pwfx);
HeapFree(GetProcessHeap(), 0, This->buffer); HeapFree(GetProcessHeap(), 0, This->buffer);
HeapFree(GetProcessHeap(), 0, This); HeapFree(GetProcessHeap(), 0, This);
@ -360,13 +371,13 @@ static HRESULT AC_OpenRenderAL(ACImpl *This)
cur->device = palcOpenDevice(alname); cur->device = palcOpenDevice(alname);
if (!cur->device) { if (!cur->device) {
ALCenum err = palcGetError(NULL); ALCenum err = palcGetError(NULL);
FIXME("Could not open device %s: 0x%04x\n", alname, err); WARN("Could not open device %s: 0x%04x\n", alname, err);
return AUDCLNT_E_DEVICE_IN_USE; return AUDCLNT_E_DEVICE_IN_USE;
} }
cur->ctx = palcCreateContext(cur->device, NULL); cur->ctx = palcCreateContext(cur->device, NULL);
if (!cur->ctx) { if (!cur->ctx) {
ALCenum err = palcGetError(cur->device); ALCenum err = palcGetError(cur->device);
FIXME("Could not create context: 0x%04x\n", err); ERR("Could not create context: 0x%04x\n", err);
return AUDCLNT_E_SERVICE_NOT_RUNNING; return AUDCLNT_E_SERVICE_NOT_RUNNING;
} }
if (!cur->device) if (!cur->device)
@ -383,14 +394,14 @@ static HRESULT AC_OpenCaptureAL(ACImpl *This)
size = This->bufsize; size = This->bufsize;
alname[sizeof(alname)-1] = 0; alname[sizeof(alname)-1] = 0;
if (This->capdev) { if (This->dev) {
FIXME("Attempting to open device while already open\n"); FIXME("Attempting to open device while already open\n");
return S_OK; return S_OK;
} }
WideCharToMultiByte(CP_UNIXCP, 0, This->parent->alname, -1, WideCharToMultiByte(CP_UNIXCP, 0, This->parent->alname, -1,
alname, sizeof(alname)/sizeof(*alname)-1, NULL, NULL); alname, sizeof(alname)/sizeof(*alname)-1, NULL, NULL);
This->capdev = palcCaptureOpenDevice(alname, freq, This->format, size); This->dev = palcCaptureOpenDevice(alname, freq, This->format, size);
if (!This->capdev) { if (!This->dev) {
ALCenum err = palcGetError(NULL); ALCenum err = palcGetError(NULL);
FIXME("Could not open device %s with buf size %u: 0x%04x\n", FIXME("Could not open device %s with buf size %u: 0x%04x\n",
alname, This->bufsize, err); alname, This->bufsize, err);
@ -522,6 +533,7 @@ static HRESULT WINAPI AC_Initialize(IAudioClient *iface, AUDCLNT_SHAREMODE mode,
ALuint buf = 0, towrite; ALuint buf = 0, towrite;
hr = AC_OpenRenderAL(This); hr = AC_OpenRenderAL(This);
This->dev = This->parent->device;
if (FAILED(hr)) if (FAILED(hr))
goto out; goto out;
@ -552,6 +564,7 @@ static HRESULT WINAPI AC_Initialize(IAudioClient *iface, AUDCLNT_SHAREMODE mode,
if (FAILED(hr)) if (FAILED(hr))
goto out; goto out;
This->candisconnect = palcIsExtensionPresent(This->dev, "ALC_EXT_disconnect");
This->buffer = HeapAlloc(GetProcessHeap(), 0, bufsize); This->buffer = HeapAlloc(GetProcessHeap(), 0, bufsize);
if (!This->buffer) { if (!This->buffer) {
hr = E_OUTOFMEMORY; hr = E_OUTOFMEMORY;
@ -594,6 +607,74 @@ static HRESULT WINAPI AC_GetStreamLatency(IAudioClient *iface, REFERENCE_TIME *l
return S_OK; return S_OK;
} }
static int disconnected(ACImpl *This)
{
if (!This->candisconnect)
return 0;
if (This->parent->flow == eRender) {
if (This->parent->device) {
ALCint con = 1;
palcGetIntegerv(This->parent->device, ALC_CONNECTED, 1, &con);
palcGetError(This->parent->device);
if (!con) {
palcCloseDevice(This->parent->device);
This->parent->device = NULL;
This->parent->ctx = NULL;
This->dev = NULL;
}
}
if (!This->parent->device && FAILED(AC_OpenRenderAL(This))) {
This->pad -= This->padpartial;
This->padpartial = 0;
return 1;
}
if (This->parent->device != This->dev) {
WARN("Emptying buffer after newly reconnected!\n");
This->pad -= This->padpartial;
This->padpartial = 0;
This->dev = This->parent->device;
setALContext(This->parent->ctx);
palGenSources(1, &This->source);
palSourcei(This->source, AL_LOOPING, AL_FALSE);
getALError();
if (This->render && !This->locked && This->pad) {
UINT pad = This->pad;
BYTE *data;
This->pad = 0;
/* Probably will cause sound glitches, who cares? */
IAudioRenderClient_GetBuffer((IAudioRenderClient *)This->render, pad, &data);
IAudioRenderClient_ReleaseBuffer((IAudioRenderClient *)This->render, pad, 0);
}
popALContext();
}
} else {
if (This->dev) {
ALCint con = 1;
palcGetIntegerv(This->dev, ALC_CONNECTED, 1, &con);
palcGetError(This->dev);
if (!con) {
palcCaptureCloseDevice(This->dev);
This->dev = NULL;
}
}
if (!This->dev) {
if (FAILED(AC_OpenCaptureAL(This)))
return 1;
WARN("Emptying buffer after newly reconnected!\n");
This->pad = This->ofs = 0;
if (This->running)
palcCaptureStart(This->dev);
}
}
return 0;
}
static HRESULT WINAPI AC_GetCurrentPadding(IAudioClient *iface, UINT32 *numpad) static HRESULT WINAPI AC_GetCurrentPadding(IAudioClient *iface, UINT32 *numpad)
{ {
ACImpl *This = (ACImpl*)iface; ACImpl *This = (ACImpl*)iface;
@ -605,7 +686,32 @@ static HRESULT WINAPI AC_GetCurrentPadding(IAudioClient *iface, UINT32 *numpad)
if (!numpad) if (!numpad)
return E_POINTER; return E_POINTER;
EnterCriticalSection(This->crst); EnterCriticalSection(This->crst);
if (This->parent->flow == eRender) { if (disconnected(This)) {
REFERENCE_TIME time = gettime(), period;
WARN("No device found, faking increment\n");
IAudioClient_GetDevicePeriod(iface, &period, NULL);
while (This->running && time - This->laststamp >= period) {
This->laststamp += period;
if (This->parent->flow == eCapture) {
This->pad += This->psize;
if (This->pad > This->bufsize)
This->pad = This->bufsize;
} else {
if (This->pad <= This->psize) {
This->pad = 0;
break;
} else
This->pad -= This->psize;
}
}
if (This->parent->flow == eCapture)
*numpad = This->pad >= This->psize ? This->psize : 0;
else
*numpad = This->pad;
} else if (This->parent->flow == eRender) {
UINT64 played = 0; UINT64 played = 0;
ALint state, padpart; ALint state, padpart;
setALContext(This->parent->ctx); setALContext(This->parent->ctx);
@ -637,7 +743,7 @@ static HRESULT WINAPI AC_GetCurrentPadding(IAudioClient *iface, UINT32 *numpad)
} else { } else {
DWORD block = This->pwfx->nBlockAlign; DWORD block = This->pwfx->nBlockAlign;
DWORD psize = This->psize / block; DWORD psize = This->psize / block;
palcGetIntegerv(This->capdev, ALC_CAPTURE_SAMPLES, 1, &avail); palcGetIntegerv(This->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
if (avail) { if (avail) {
DWORD ofs = This->ofs + This->pad; DWORD ofs = This->ofs + This->pad;
BYTE *buf1; BYTE *buf1;
@ -648,11 +754,11 @@ static HRESULT WINAPI AC_GetCurrentPadding(IAudioClient *iface, UINT32 *numpad)
SetEvent(This->handle); SetEvent(This->handle);
if (ofs + avail <= This->bufsize) if (ofs + avail <= This->bufsize)
palcCaptureSamples(This->capdev, buf1, avail); palcCaptureSamples(This->dev, buf1, avail);
else { else {
DWORD part1 = This->bufsize - ofs; DWORD part1 = This->bufsize - ofs;
palcCaptureSamples(This->capdev, buf1, part1); palcCaptureSamples(This->dev, buf1, part1);
palcCaptureSamples(This->capdev, This->buffer, avail - part1); palcCaptureSamples(This->dev, This->buffer, avail - part1);
} }
This->pad += avail; This->pad += avail;
This->frameswritten += avail; This->frameswritten += avail;
@ -722,7 +828,7 @@ static HRESULT WINAPI AC_IsFormatSupported(IAudioClient *iface, AUDCLNT_SHAREMOD
case 8: mask = X7DOT1; break; case 8: mask = X7DOT1; break;
default: default:
TRACE("Unsupported channel count %i\n", pwfx->nChannels); TRACE("Unsupported channel count %i\n", pwfx->nChannels);
return AUDCLNT_E_UNSUPPORTED_FORMAT;; return AUDCLNT_E_UNSUPPORTED_FORMAT;
} }
tmp = CoTaskMemAlloc(size); tmp = CoTaskMemAlloc(size);
if (outpwfx) if (outpwfx)
@ -812,14 +918,16 @@ static HRESULT WINAPI AC_Start(IAudioClient *iface)
hr = AUDCLNT_E_NOT_STOPPED; hr = AUDCLNT_E_NOT_STOPPED;
goto out; goto out;
} }
if (This->parent->flow == eRender) { if (!valid_dev(This))
WARN("No valid device\n");
else if (This->parent->flow == eRender) {
setALContext(This->parent->ctx); setALContext(This->parent->ctx);
palSourcePlay(This->source); palSourcePlay(This->source);
getALError(); getALError();
popALContext(); popALContext();
} }
else else
palcCaptureStart(This->capdev); palcCaptureStart(This->dev);
AC_GetDevicePeriod(iface, &refresh, NULL); AC_GetDevicePeriod(iface, &refresh, NULL);
if (!This->timer_id && This->handle) if (!This->timer_id && This->handle)
@ -848,10 +956,11 @@ static HRESULT WINAPI AC_Stop(IAudioClient *iface)
if (!This->running) if (!This->running)
return S_FALSE; return S_FALSE;
EnterCriticalSection(This->crst); EnterCriticalSection(This->crst);
if (This->parent->flow == eRender) { if (!valid_dev(This))
WARN("No valid device\n");
else if (This->parent->flow == eRender) {
ALint state; ALint state;
setALContext(This->parent->ctx); setALContext(This->parent->ctx);
This->running = FALSE;
palSourcePause(This->source); palSourcePause(This->source);
while (1) { while (1) {
state = AL_STOPPED; state = AL_STOPPED;
@ -864,7 +973,8 @@ static HRESULT WINAPI AC_Stop(IAudioClient *iface)
popALContext(); popALContext();
} }
else else
palcCaptureStop(This->capdev); palcCaptureStop(This->dev);
This->running = FALSE;
timer_id = This->timer_id; timer_id = This->timer_id;
This->timer_id = 0; This->timer_id = 0;
LeaveCriticalSection(This->crst); LeaveCriticalSection(This->crst);
@ -887,7 +997,9 @@ static HRESULT WINAPI AC_Reset(IAudioClient *iface)
hr = AUDCLNT_E_BUFFER_OPERATION_PENDING; hr = AUDCLNT_E_BUFFER_OPERATION_PENDING;
goto out; goto out;
} }
if (This->parent->flow == eRender) { if (!valid_dev(This))
WARN("No valid device\n");
else if (This->parent->flow == eRender) {
ALuint buf; ALuint buf;
ALint n = 0; ALint n = 0;
setALContext(This->parent->ctx); setALContext(This->parent->ctx);
@ -901,9 +1013,9 @@ static HRESULT WINAPI AC_Reset(IAudioClient *iface)
popALContext(); popALContext();
} else { } else {
ALint avail = 0; ALint avail = 0;
palcGetIntegerv(This->capdev, ALC_CAPTURE_SAMPLES, 1, &avail); palcGetIntegerv(This->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
if (avail) if (avail)
palcCaptureSamples(This->capdev, This->buffer, avail); palcCaptureSamples(This->dev, This->buffer, avail);
} }
This->pad = This->padpartial = 0; This->pad = This->padpartial = 0;
This->ofs = 0; This->ofs = 0;
@ -1093,14 +1205,13 @@ static HRESULT WINAPI ACR_ReleaseBuffer(IAudioRenderClient *iface, UINT32 writte
DWORD framesize = This->parent->pwfx->nBlockAlign; DWORD framesize = This->parent->pwfx->nBlockAlign;
DWORD ofs = This->parent->ofs; DWORD ofs = This->parent->ofs;
DWORD bufsize = This->parent->bufsize; DWORD bufsize = This->parent->bufsize;
DWORD locked = This->parent->locked;
DWORD freq = This->parent->pwfx->nSamplesPerSec; DWORD freq = This->parent->pwfx->nSamplesPerSec;
DWORD bpp = This->parent->pwfx->wBitsPerSample; DWORD bpp = This->parent->pwfx->wBitsPerSample;
ALuint albuf; ALuint albuf;
TRACE("(%p)->(%u,%x)\n", This, written, flags); TRACE("(%p)->(%u,%x)\n", This, written, flags);
if (locked < written) if (This->parent->locked < written)
return AUDCLNT_E_INVALID_SIZE; return AUDCLNT_E_INVALID_SIZE;
if (flags & ~AUDCLNT_BUFFERFLAGS_SILENT) if (flags & ~AUDCLNT_BUFFERFLAGS_SILENT)
@ -1117,22 +1228,24 @@ static HRESULT WINAPI ACR_ReleaseBuffer(IAudioRenderClient *iface, UINT32 writte
return AUDCLNT_E_OUT_OF_ORDER; return AUDCLNT_E_OUT_OF_ORDER;
EnterCriticalSection(This->parent->crst); EnterCriticalSection(This->parent->crst);
setALContext(This->parent->parent->ctx);
This->parent->ofs += written; This->parent->ofs += written;
This->parent->ofs %= bufsize; This->parent->ofs %= bufsize;
This->parent->pad += written; This->parent->pad += written;
This->parent->frameswritten += written; This->parent->frameswritten += written;
This->parent->locked = 0;
ofs *= framesize; ofs *= framesize;
written *= framesize; written *= framesize;
bufsize *= framesize; bufsize *= framesize;
locked *= framesize;
if (flags & AUDCLNT_BUFFERFLAGS_SILENT) if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
memset(buf+ofs, bpp != 8 ? 0 : 128, written); memset(buf+ofs, bpp != 8 ? 0 : 128, written);
TRACE("buf: %p, ofs: %x, written %u, freq %u\n", buf, ofs, written, freq); TRACE("buf: %p, ofs: %x, written %u, freq %u\n", buf, ofs, written, freq);
if (!valid_dev(This->parent))
goto out;
setALContext(This->parent->parent->ctx);
palGenBuffers(1, &albuf); palGenBuffers(1, &albuf);
palBufferData(albuf, This->parent->format, buf+ofs, written, freq); palBufferData(albuf, This->parent->format, buf+ofs, written, freq);
palSourceQueueBuffers(This->parent->source, 1, &albuf); palSourceQueueBuffers(This->parent->source, 1, &albuf);
@ -1181,10 +1294,9 @@ static HRESULT WINAPI ACR_ReleaseBuffer(IAudioRenderClient *iface, UINT32 writte
} }
getALError(); getALError();
} }
This->parent->locked = 0;
getALError(); getALError();
popALContext(); popALContext();
out:
LeaveCriticalSection(This->parent->crst); LeaveCriticalSection(This->parent->crst);
return S_OK; return S_OK;