Module: wine Branch: master Commit: 3d32315c9e99947fa573837d6510c97c53dd4eee URL: http://source.winehq.org/git/wine.git/?a=commit;h=3d32315c9e99947fa573837d65...
Author: Maarten Lankhorst m.b.lankhorst@gmail.com Date: Wed Aug 15 16:24:07 2007 +0200
winealsa: Add support for playing dsound capture buffer.
---
dlls/winealsa.drv/dscapture.c | 169 ++++++++++++++++++++++++++++++++++++++++- 1 files changed, 166 insertions(+), 3 deletions(-)
diff --git a/dlls/winealsa.drv/dscapture.c b/dlls/winealsa.drv/dscapture.c index 236c3f7..243f24a 100644 --- a/dlls/winealsa.drv/dscapture.c +++ b/dlls/winealsa.drv/dscapture.c @@ -91,6 +91,146 @@ struct IDsCaptureDriverBufferImpl snd_pcm_uframes_t mmap_buflen_frames, mmap_pos; };
+#if 0 +/** Convert the position an application sees into a position ALSA sees */ +static snd_pcm_uframes_t fakepos_to_realpos(const IDsCaptureDriverBufferImpl* This, DWORD fakepos) +{ + snd_pcm_uframes_t realpos; + if (fakepos < This->mmap_ofs_bytes) + realpos = This->mmap_buflen_bytes + fakepos - This->mmap_ofs_bytes; + else realpos = fakepos - This->mmap_ofs_bytes; + return snd_pcm_bytes_to_frames(This->pcm, realpos) % This->mmap_buflen_frames; +} +#endif + +/** Convert the position ALSA sees into a position an application sees */ +static DWORD realpos_to_fakepos(const IDsCaptureDriverBufferImpl* This, snd_pcm_uframes_t realpos) +{ + DWORD realposb = snd_pcm_frames_to_bytes(This->pcm, realpos); + return (realposb + This->mmap_ofs_bytes) % This->mmap_buflen_bytes; +} + +/** Raw copy data, with buffer wrap around */ +static void CopyDataWrap(const IDsCaptureDriverBufferImpl* This, LPBYTE dest, DWORD fromwhere, DWORD copylen, DWORD buflen) +{ + DWORD remainder = buflen - fromwhere; + if (remainder >= copylen) + { + CopyMemory(dest, This->mmap_buffer + fromwhere, copylen); + } + else + { + CopyMemory(dest, This->mmap_buffer + fromwhere, remainder); + copylen -= remainder; + CopyMemory(dest, This->mmap_buffer, copylen); + } +} + +/** Copy data from the mmap buffer to backbuffer, taking into account all wraparounds that may occur */ +static void CopyData(const IDsCaptureDriverBufferImpl* This, snd_pcm_uframes_t fromwhere, snd_pcm_uframes_t len) +{ + DWORD dlen = snd_pcm_frames_to_bytes(This->pcm, len) % This->mmap_buflen_bytes; + + /* Backbuffer */ + DWORD ofs = realpos_to_fakepos(This, fromwhere); + DWORD remainder = This->mmap_buflen_bytes - ofs; + + /* MMAP buffer */ + DWORD realbuflen = snd_pcm_frames_to_bytes(This->pcm, This->mmap_buflen_frames); + DWORD realofs = snd_pcm_frames_to_bytes(This->pcm, fromwhere); + + if (remainder >= dlen) + { + CopyDataWrap(This, This->presented_buffer + ofs, realofs, dlen, realbuflen); + } + else + { + CopyDataWrap(This, This->presented_buffer + ofs, realofs, remainder, realbuflen); + dlen -= remainder; + CopyDataWrap(This, This->presented_buffer, (realofs+remainder)%realbuflen, dlen, realbuflen); + } +} + +/** Fill buffers, for starting and stopping + * Alsa won't start playing until everything is filled up + * This also updates mmap_pos + * + * Returns: Amount of periods in use so snd_pcm_avail_update + * doesn't have to be called up to 4x in GetPosition() + */ +static snd_pcm_uframes_t CommitAll(IDsCaptureDriverBufferImpl *This, DWORD forced) +{ + const snd_pcm_channel_area_t *areas; + snd_pcm_uframes_t used; + const snd_pcm_uframes_t commitahead = This->mmap_buflen_frames; + + used = This->mmap_buflen_frames - snd_pcm_avail_update(This->pcm); + TRACE("%p needs to commit to %lu, used: %lu\n", This, commitahead, used); + if (used < commitahead && (forced || This->play_looping)) + { + snd_pcm_uframes_t done, putin = commitahead - used; + snd_pcm_mmap_begin(This->pcm, &areas, &This->mmap_pos, &putin); + CopyData(This, This->mmap_pos, putin); + done = snd_pcm_mmap_commit(This->pcm, This->mmap_pos, putin); + This->mmap_pos += done; + used += done; + putin = commitahead - used; + + if (This->mmap_pos == This->mmap_buflen_frames && (snd_pcm_sframes_t)putin > 0 && This->play_looping) + { + snd_pcm_mmap_begin(This->pcm, &areas, &This->mmap_pos, &putin); + This->mmap_ofs_bytes += snd_pcm_frames_to_bytes(This->pcm, This->mmap_buflen_frames); + This->mmap_ofs_bytes %= This->mmap_buflen_bytes; + CopyData(This, This->mmap_pos, putin); + done = snd_pcm_mmap_commit(This->pcm, This->mmap_pos, putin); + This->mmap_pos += done; + used += done; + } + } + + if (This->mmap_pos == This->mmap_buflen_frames) + { + This->mmap_ofs_bytes += snd_pcm_frames_to_bytes(This->pcm, This->mmap_buflen_frames); + This->mmap_ofs_bytes %= This->mmap_buflen_bytes; + This->mmap_pos = 0; + } + + return used; +} + +static void CheckXRUN(IDsCaptureDriverBufferImpl* This) +{ + snd_pcm_state_t state = snd_pcm_state(This->pcm); + snd_pcm_sframes_t delay; + int err; + + snd_pcm_hwsync(This->pcm); + snd_pcm_delay(This->pcm, &delay); + if ( state == SND_PCM_STATE_XRUN ) + { + err = snd_pcm_prepare(This->pcm); + CommitAll(This, FALSE); + snd_pcm_start(This->pcm); + WARN("xrun occurred\n"); + if ( err < 0 ) + ERR("recovery from xrun failed, prepare failed: %s\n", snd_strerror(err)); + } + else if ( state == SND_PCM_STATE_SUSPENDED ) + { + int err = snd_pcm_resume(This->pcm); + TRACE("recovery from suspension occurred\n"); + if (err < 0 && err != -EAGAIN){ + err = snd_pcm_prepare(This->pcm); + if (err < 0) + ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err)); + } + } + else if ( state != SND_PCM_STATE_RUNNING) + { + WARN("Unhandled state: %d\n", state); + } +} + /** * Allocate the memory-mapped buffer for direct sound, and set up the * callback. @@ -347,8 +487,7 @@ static HRESULT WINAPI IDsCaptureDriverBufferImpl_SetFormat(PIDSCDRIVERBUFFER ifa static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetPosition(PIDSCDRIVERBUFFER iface, LPDWORD lpdwCappos, LPDWORD lpdwReadpos) { IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface; - - FIXME("stub!\n"); + snd_pcm_uframes_t hw_pptr, hw_wptr;
EnterCriticalSection(&This->pcm_crst);
@@ -359,9 +498,32 @@ static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetPosition(PIDSCDRIVERBUFFER i return DSERR_GENERIC; }
+ if (snd_pcm_state(This->pcm) != SND_PCM_STATE_RUNNING) + { + hw_pptr = This->mmap_pos; + CheckXRUN(This); + } + else + { + /* FIXME: Unused at the moment */ + snd_pcm_uframes_t used = CommitAll(This, FALSE); + + if (This->mmap_pos > used) + hw_pptr = This->mmap_pos - used; + else + hw_pptr = This->mmap_buflen_frames - used + This->mmap_pos; + } + hw_wptr = This->mmap_pos; + + if (lpdwCappos) + *lpdwCappos = realpos_to_fakepos(This, hw_wptr); + if (lpdwReadpos) + *lpdwReadpos = realpos_to_fakepos(This, hw_wptr); + LeaveCriticalSection(&This->pcm_crst);
- return DSERR_GENERIC; + TRACE("hw_pptr=0x%08x, hw_wptr=0x%08x playpos=%d, writepos=%d\n", (unsigned int)hw_pptr, (unsigned int)hw_wptr, lpdwCappos?*lpdwCappos:-1, lpdwReadpos?*lpdwReadpos:-1); + return DS_OK; }
static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetStatus(PIDSCDRIVERBUFFER iface, LPDWORD lpdwStatus) @@ -400,6 +562,7 @@ static HRESULT WINAPI IDsCaptureDriverBufferImpl_Start(PIDSCDRIVERBUFFER iface, /* Not well supported because of the difference in ALSA size and DSOUND's notion of size * what it does right now is fill the buffer once.. ALSA size */ FIXME("Non-looping buffers are not properly supported!\n"); + CommitAll(This, TRUE); /* **** */ LeaveCriticalSection(&This->pcm_crst); return DS_OK;