Maarten Lankhorst : winealsa: Add support for playing dsound capture buffer .

Alexandre Julliard julliard at wine.codeweavers.com
Thu Aug 16 05:38:29 CDT 2007


Module: wine
Branch: master
Commit: 3d32315c9e99947fa573837d6510c97c53dd4eee
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=3d32315c9e99947fa573837d6510c97c53dd4eee

Author: Maarten Lankhorst <m.b.lankhorst at 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;




More information about the wine-cvs mailing list