[PATCH 3/3] xaudio2: Support looping buffers

Andrew Eikum aeikum at codeweavers.com
Fri Sep 11 10:55:58 CDT 2015


Also shorten up the test runtime.
---
 dlls/xaudio2_7/tests/xaudio2.c | 265 ++++++++++++++++++++++++++++++++++++++++-
 dlls/xaudio2_7/xaudio_dll.c    | 132 ++++++++++++++++++--
 2 files changed, 382 insertions(+), 15 deletions(-)

diff --git a/dlls/xaudio2_7/tests/xaudio2.c b/dlls/xaudio2_7/tests/xaudio2.c
index 0538f80..aabb70e 100644
--- a/dlls/xaudio2_7/tests/xaudio2.c
+++ b/dlls/xaudio2_7/tests/xaudio2.c
@@ -209,9 +209,9 @@ static void test_simple_streaming(IXAudio2 *xa)
     ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
 
     memset(&buf, 0, sizeof(buf));
-    buf.AudioBytes = 44100 * fmt.nBlockAlign;
+    buf.AudioBytes = 22050 * fmt.nBlockAlign;
     buf.pAudioData = HeapAlloc(GetProcessHeap(), 0, buf.AudioBytes);
-    fill_buf((float*)buf.pAudioData, &fmt, 440, 44100);
+    fill_buf((float*)buf.pAudioData, &fmt, 440, 22050);
 
     hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
     ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
@@ -224,9 +224,9 @@ static void test_simple_streaming(IXAudio2 *xa)
     ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
 
     memset(&buf2, 0, sizeof(buf2));
-    buf2.AudioBytes = 44100 * fmt.nBlockAlign;
+    buf2.AudioBytes = 22050 * fmt.nBlockAlign;
     buf2.pAudioData = HeapAlloc(GetProcessHeap(), 0, buf2.AudioBytes);
-    fill_buf((float*)buf2.pAudioData, &fmt, 220, 44100);
+    fill_buf((float*)buf2.pAudioData, &fmt, 220, 22050);
 
     hr = IXAudio2SourceVoice_SubmitSourceBuffer(src2, &buf2, NULL);
     ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
@@ -264,12 +264,12 @@ static void test_simple_streaming(IXAudio2 *xa)
             IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
         else
             IXAudio2SourceVoice_GetState(src, &state, 0);
-        if(state.SamplesPlayed >= 44100)
+        if(state.SamplesPlayed >= 22050)
             break;
         Sleep(100);
     }
 
-    ok(state.SamplesPlayed == 44100, "Got wrong samples played\n");
+    ok(state.SamplesPlayed == 22050, "Got wrong samples played\n");
 
     HeapFree(GetProcessHeap(), 0, (void*)buf.pAudioData);
     HeapFree(GetProcessHeap(), 0, (void*)buf2.pAudioData);
@@ -373,6 +373,45 @@ static const IXAudio2VoiceCallbackVtbl vcb_buf_vtbl = {
 
 static IXAudio2VoiceCallback vcb_buf = { &vcb_buf_vtbl };
 
+static int nloopends = 0;
+
+static void WINAPI loop_buf_OnStreamEnd(IXAudio2VoiceCallback *This)
+{
+}
+
+static void WINAPI loop_buf_OnBufferStart(IXAudio2VoiceCallback *This,
+        void *pBufferContext)
+{
+}
+
+static void WINAPI loop_buf_OnBufferEnd(IXAudio2VoiceCallback *This,
+        void *pBufferContext)
+{
+}
+
+static void WINAPI loop_buf_OnLoopEnd(IXAudio2VoiceCallback *This,
+        void *pBufferContext)
+{
+    ++nloopends;
+}
+
+static void WINAPI loop_buf_OnVoiceError(IXAudio2VoiceCallback *This,
+        void *pBuffercontext, HRESULT Error)
+{
+}
+
+static const IXAudio2VoiceCallbackVtbl loop_buf_vtbl = {
+    vcb_buf_OnVoiceProcessingPassStart,
+    vcb_buf_OnVoiceProcessingPassEnd,
+    loop_buf_OnStreamEnd,
+    loop_buf_OnBufferStart,
+    loop_buf_OnBufferEnd,
+    loop_buf_OnLoopEnd,
+    loop_buf_OnVoiceError
+};
+
+static IXAudio2VoiceCallback loop_buf = { &loop_buf_vtbl };
+
 static void test_buffer_callbacks(IXAudio2 *xa)
 {
     HRESULT hr;
@@ -450,6 +489,218 @@ static void test_buffer_callbacks(IXAudio2 *xa)
     IXAudio2MasteringVoice_DestroyVoice(master);
 }
 
+static UINT32 play_to_completion(IXAudio2SourceVoice *src, UINT32 max_samples)
+{
+    XAUDIO2_VOICE_STATE state;
+    HRESULT hr;
+
+    nloopends = 0;
+
+    hr = IXAudio2SourceVoice_Start(src, 0, XAUDIO2_COMMIT_NOW);
+    ok(hr == S_OK, "Start failed: %08x\n", hr);
+
+    while(1){
+        if(xaudio27)
+            IXAudio27SourceVoice_GetState((IXAudio27SourceVoice*)src, &state);
+        else
+            IXAudio2SourceVoice_GetState(src, &state, 0);
+        if(state.BuffersQueued == 0)
+            break;
+        if(state.SamplesPlayed >= max_samples){
+            if(xaudio27)
+                IXAudio27SourceVoice_ExitLoop((IXAudio27SourceVoice*)src, XAUDIO2_COMMIT_NOW);
+            else
+                IXAudio2SourceVoice_ExitLoop(src, XAUDIO2_COMMIT_NOW);
+        }
+        Sleep(100);
+    }
+
+    hr = IXAudio2SourceVoice_Stop(src, 0, XAUDIO2_COMMIT_NOW);
+    ok(hr == S_OK, "Start failed: %08x\n", hr);
+
+    return state.SamplesPlayed;
+}
+
+static void test_looping(IXAudio2 *xa)
+{
+    HRESULT hr;
+    IXAudio2MasteringVoice *master;
+    IXAudio2SourceVoice *src;
+    WAVEFORMATEX fmt;
+    XAUDIO2_BUFFER buf;
+    UINT32 played, running_total = 0;
+
+    XA2CALL_0V(StopEngine);
+
+    if(xaudio27)
+        hr = IXAudio27_CreateMasteringVoice((IXAudio27*)xa, &master, 2, 44100, 0, 0, NULL);
+    else
+        hr = IXAudio2_CreateMasteringVoice(xa, &master, 2, 44100, 0, NULL, NULL, AudioCategory_GameEffects);
+    ok(hr == S_OK, "CreateMasteringVoice failed: %08x\n", hr);
+
+
+    fmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+    fmt.nChannels = 2;
+    fmt.nSamplesPerSec = 44100;
+    fmt.wBitsPerSample = 32;
+    fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
+    fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
+    fmt.cbSize = 0;
+
+    XA2CALL(CreateSourceVoice, &src, &fmt, 0, 1.f, &loop_buf, NULL, NULL);
+    ok(hr == S_OK, "CreateSourceVoice failed: %08x\n", hr);
+
+    memset(&buf, 0, sizeof(buf));
+
+    buf.AudioBytes = 44100 * fmt.nBlockAlign;
+    buf.pAudioData = HeapAlloc(GetProcessHeap(), 0, buf.AudioBytes);
+    fill_buf((float*)buf.pAudioData, &fmt, 440, 44100);
+
+    XA2CALL_0(StartEngine);
+    ok(hr == S_OK, "StartEngine failed: %08x\n", hr);
+
+    /* play from middle to end */
+    buf.PlayBegin = 22050;
+    buf.PlayLength = 0;
+    buf.LoopBegin = 0;
+    buf.LoopLength = 0;
+    buf.LoopCount = 0;
+
+    hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
+    ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
+
+    played = play_to_completion(src, -1);
+    ok(played - running_total == 22050, "Got wrong samples played: %u\n", played - running_total);
+    running_total = played;
+    ok(nloopends == 0, "Got wrong OnLoopEnd calls: %u\n", nloopends);
+
+    /* play 4410 samples from middle */
+    buf.PlayBegin = 22050;
+    buf.PlayLength = 4410;
+    buf.LoopBegin = 0;
+    buf.LoopLength = 0;
+    buf.LoopCount = 0;
+
+    hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
+    ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
+
+    played = play_to_completion(src, -1);
+    ok(played - running_total == 4410, "Got wrong samples played: %u\n", played - running_total);
+    running_total = played;
+    ok(nloopends == 0, "Got wrong OnLoopEnd calls: %u\n", nloopends);
+
+    /* loop 4410 samples in middle */
+    buf.PlayBegin = 0;
+    buf.PlayLength = 0;
+    buf.LoopBegin = 22050;
+    buf.LoopLength = 4410;
+    buf.LoopCount = 1;
+
+    hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
+    ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
+
+    played = play_to_completion(src, -1);
+    ok(played - running_total == 44100 + 4410, "Got wrong samples played: %u\n", played - running_total);
+    running_total = played;
+    ok(nloopends == 1, "Got wrong OnLoopEnd calls: %u\n", nloopends);
+
+    /* play last half, then loop the whole buffer */
+    buf.PlayBegin = 22050;
+    buf.PlayLength = 0;
+    buf.LoopBegin = 0;
+    buf.LoopLength = 0;
+    buf.LoopCount = 1;
+
+    hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
+    ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
+
+    played = play_to_completion(src, -1);
+    ok(played - running_total == 22050 + 44100, "Got wrong samples played: %u\n", played - running_total);
+    running_total = played;
+    ok(nloopends == 1, "Got wrong OnLoopEnd calls: %u\n", nloopends);
+
+    /* play short segment from middle, loop to the beginning, and end at PlayEnd */
+    buf.PlayBegin = 22050;
+    buf.PlayLength = 4410;
+    buf.LoopBegin = 0;
+    buf.LoopLength = 0;
+    buf.LoopCount = 1;
+
+    hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
+    ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
+
+    played = play_to_completion(src, -1);
+    ok(played - running_total == 4410 + (22050 + 4410), "Got wrong samples played: %u\n", played - running_total);
+    running_total = played;
+    ok(nloopends == 1, "Got wrong OnLoopEnd calls: %u\n", nloopends);
+
+    /* invalid: LoopEnd must be <= PlayEnd
+     * xaudio27: play until LoopEnd, loop to beginning, play until PlayEnd */
+    buf.PlayBegin = 22050;
+    buf.PlayLength = 4410;
+    buf.LoopBegin = 0;
+    buf.LoopLength = 22050 + 4410 * 2;
+    buf.LoopCount = 1;
+
+    hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
+    if(xaudio27){
+        ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
+
+        played = play_to_completion(src, -1);
+        ok(played - running_total == 4410 + (22050 + 4410 * 2), "Got wrong samples played: %u\n", played - running_total);
+        running_total = played;
+        ok(nloopends == 1, "Got wrong OnLoopEnd calls: %u\n", nloopends);
+    }else
+        ok(hr == XAUDIO2_E_INVALID_CALL, "SubmitSourceBuffer should have failed: %08x\n", hr);
+
+    /* invalid: LoopEnd must be within play range
+     * xaudio27: plays only play range */
+    buf.PlayBegin = 22050;
+    buf.PlayLength = 0; /* == until end of buffer */
+    buf.LoopBegin = 0;
+    buf.LoopLength = 22050;
+    buf.LoopCount = 1;
+
+    hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
+    if(xaudio27){
+        ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
+
+        played = play_to_completion(src, -1);
+        ok(played - running_total == 22050, "Got wrong samples played: %u\n", played - running_total);
+        running_total = played;
+        ok(nloopends == 0, "Got wrong OnLoopEnd calls: %u\n", nloopends);
+    }else
+        ok(hr == XAUDIO2_E_INVALID_CALL, "SubmitSourceBuffer should have failed: %08x\n", hr);
+
+    /* invalid: LoopBegin must be before PlayEnd
+     * xaudio27: crashes */
+    if(!xaudio27){
+        buf.PlayBegin = 0;
+        buf.PlayLength = 4410;
+        buf.LoopBegin = 22050;
+        buf.LoopLength = 4410;
+        buf.LoopCount = 1;
+
+        hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
+        ok(hr == XAUDIO2_E_INVALID_CALL, "SubmitSourceBuffer should have failed: %08x\n", hr);
+    }
+
+    /* infinite looping buffer */
+    buf.PlayBegin = 22050;
+    buf.PlayLength = 0;
+    buf.LoopBegin = 0;
+    buf.LoopLength = 0;
+    buf.LoopCount = 255;
+
+    hr = IXAudio2SourceVoice_SubmitSourceBuffer(src, &buf, NULL);
+    ok(hr == S_OK, "SubmitSourceBuffer failed: %08x\n", hr);
+
+    played = play_to_completion(src, running_total + 88200);
+    ok(played - running_total == 22050 + 44100 * 2, "Got wrong samples played: %u\n", played - running_total);
+    ok(nloopends == (played - running_total) / 88200 + 1, "Got wrong OnLoopEnd calls: %u\n", nloopends);
+    running_total = played;
+}
+
 static UINT32 test_DeviceDetails(IXAudio27 *xa)
 {
     HRESULT hr;
@@ -519,6 +770,7 @@ START_TEST(xaudio2)
             test_DeviceDetails(xa27);
             test_simple_streaming((IXAudio2*)xa27);
             test_buffer_callbacks((IXAudio2*)xa27);
+            test_looping((IXAudio2*)xa27);
         }else
             skip("No audio devices available\n");
 
@@ -537,6 +789,7 @@ START_TEST(xaudio2)
         if(has_devices){
             test_simple_streaming(xa);
             test_buffer_callbacks(xa);
+            test_looping(xa);
         }else
             skip("No audio devices available\n");
 
diff --git a/dlls/xaudio2_7/xaudio_dll.c b/dlls/xaudio2_7/xaudio_dll.c
index e68f1b6..ccbcd21 100644
--- a/dlls/xaudio2_7/xaudio_dll.c
+++ b/dlls/xaudio2_7/xaudio_dll.c
@@ -80,6 +80,9 @@ static void dump_fmt(const WAVEFORMATEX *fmt)
         TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
         TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
         TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
+    }else if(fmt->wFormatTag == WAVE_FORMAT_ADPCM){
+        ADPCMWAVEFORMAT *fmtadpcm = (void*)fmt;
+        TRACE("wSamplesPerBlock: %u\n", fmtadpcm->wSamplesPerBlock);
     }
 }
 
@@ -127,7 +130,7 @@ HRESULT WINAPI DllUnregisterServer(void)
 typedef struct _XA2Buffer {
     XAUDIO2_BUFFER xa2buffer;
     DWORD offs_bytes;
-    UINT32 latest_al_buf;
+    UINT32 latest_al_buf, looped, loop_end_bytes, play_end_bytes, cur_end_bytes;
 } XA2Buffer;
 
 typedef struct _IXAudio2Impl IXAudio2Impl;
@@ -625,7 +628,67 @@ static HRESULT WINAPI XA2SRC_SubmitSourceBuffer(IXAudio2SourceVoice *iface,
      * but pBuffer itself may be reused immediately */
     memcpy(&buf->xa2buffer, pBuffer, sizeof(*pBuffer));
 
-    buf->offs_bytes = 0;
+    /* convert samples offsets to bytes */
+    if(This->fmt->wFormatTag == WAVE_FORMAT_ADPCM){
+        /* ADPCM gives us a number of samples per block, so round down to
+         * nearest block and convert to bytes */
+        buf->xa2buffer.PlayBegin = buf->xa2buffer.PlayBegin / ((ADPCMWAVEFORMAT*)This->fmt)->wSamplesPerBlock * This->fmt->nBlockAlign;
+        buf->xa2buffer.PlayLength = buf->xa2buffer.PlayLength / ((ADPCMWAVEFORMAT*)This->fmt)->wSamplesPerBlock * This->fmt->nBlockAlign;
+        buf->xa2buffer.LoopBegin = buf->xa2buffer.LoopBegin / ((ADPCMWAVEFORMAT*)This->fmt)->wSamplesPerBlock * This->fmt->nBlockAlign;
+        buf->xa2buffer.LoopLength = buf->xa2buffer.LoopLength / ((ADPCMWAVEFORMAT*)This->fmt)->wSamplesPerBlock * This->fmt->nBlockAlign;
+    }else{
+        buf->xa2buffer.PlayBegin *= This->fmt->nBlockAlign;
+        buf->xa2buffer.PlayLength *= This->fmt->nBlockAlign;
+        buf->xa2buffer.LoopBegin *= This->fmt->nBlockAlign;
+        buf->xa2buffer.LoopLength *= This->fmt->nBlockAlign;
+    }
+
+    if(buf->xa2buffer.PlayLength == 0)
+        /* set to end of buffer */
+        buf->xa2buffer.PlayLength = buf->xa2buffer.AudioBytes - buf->xa2buffer.PlayBegin;
+
+    buf->play_end_bytes = buf->xa2buffer.PlayBegin + buf->xa2buffer.PlayLength;
+
+    if(buf->xa2buffer.LoopCount){
+        if(buf->xa2buffer.LoopLength == 0)
+            /* set to end of play range */
+            buf->xa2buffer.LoopLength = buf->play_end_bytes - buf->xa2buffer.LoopBegin;
+
+        if(buf->xa2buffer.LoopBegin >= buf->play_end_bytes){
+            /* this actually crashes on native xaudio 2.7 */
+            LeaveCriticalSection(&This->lock);
+            return XAUDIO2_E_INVALID_CALL;
+        }
+
+        buf->loop_end_bytes = buf->xa2buffer.LoopBegin + buf->xa2buffer.LoopLength;
+
+        /* xaudio 2.7 allows some invalid looping setups, but later versions
+         * return an error */
+        if(This->xa2->version > 27){
+            if(buf->loop_end_bytes > buf->play_end_bytes){
+                LeaveCriticalSection(&This->lock);
+                return XAUDIO2_E_INVALID_CALL;
+            }
+
+            if(buf->loop_end_bytes <= buf->xa2buffer.PlayBegin){
+                LeaveCriticalSection(&This->lock);
+                return XAUDIO2_E_INVALID_CALL;
+            }
+        }else{
+            if(buf->loop_end_bytes <= buf->xa2buffer.PlayBegin){
+                buf->xa2buffer.LoopCount = 0;
+                buf->loop_end_bytes = buf->play_end_bytes;
+            }
+        }
+    }else{
+        buf->xa2buffer.LoopLength = buf->xa2buffer.PlayLength;
+        buf->xa2buffer.LoopBegin = buf->xa2buffer.PlayBegin;
+        buf->loop_end_bytes = buf->play_end_bytes;
+    }
+
+    buf->offs_bytes = buf->xa2buffer.PlayBegin;
+    buf->cur_end_bytes = buf->loop_end_bytes;
+
     buf->latest_al_buf = -1;
 
     ++This->nbufs;
@@ -692,7 +755,15 @@ static HRESULT WINAPI XA2SRC_Discontinuity(IXAudio2SourceVoice *iface)
 static HRESULT WINAPI XA2SRC_ExitLoop(IXAudio2SourceVoice *iface, UINT32 OperationSet)
 {
     XA2SourceImpl *This = impl_from_IXAudio2SourceVoice(iface);
+
     TRACE("%p, 0x%x\n", This, OperationSet);
+
+    EnterCriticalSection(&This->lock);
+
+    This->buffers[This->cur_buf].looped = XAUDIO2_LOOP_INFINITE;
+
+    LeaveCriticalSection(&This->lock);
+
     return S_OK;
 }
 
@@ -2908,12 +2979,12 @@ static BOOL xa2buffer_queue_period(XA2SourceImpl *src, XA2Buffer *buf, ALuint al
     UINT32 submit_bytes;
     const BYTE *submit_buf = NULL;
 
-    if(buf->offs_bytes >= buf->xa2buffer.AudioBytes){
+    if(buf->offs_bytes >= buf->cur_end_bytes){
         WARN("Shouldn't happen: Trying to push frames from a spent buffer?\n");
         return FALSE;
     }
 
-    submit_bytes = min(src->xa2->period_frames * src->submit_blocksize, buf->xa2buffer.AudioBytes - buf->offs_bytes);
+    submit_bytes = min(src->xa2->period_frames * src->submit_blocksize, buf->cur_end_bytes - buf->offs_bytes);
     submit_buf = buf->xa2buffer.pAudioData + buf->offs_bytes;
     buf->offs_bytes += submit_bytes;
 
@@ -2929,9 +3000,32 @@ static BOOL xa2buffer_queue_period(XA2SourceImpl *src, XA2Buffer *buf, ALuint al
 
     TRACE("queueing %u bytes, now %u in AL\n", submit_bytes, src->in_al_bytes);
 
-    return buf->offs_bytes < buf->xa2buffer.AudioBytes;
+    return buf->offs_bytes < buf->cur_end_bytes;
 }
 
+/* Looping:
+ *
+ * The looped section of a buffer is a subset of the play area which is looped
+ * LoopCount times.
+ *
+ *       v PlayBegin
+ *       vvvvvvvvvvvvvvvvvv PlayLength
+ *                        v (PlayEnd)
+ * [-----PPPLLLLLLLLPPPPPPP------]
+ *          ^ LoopBegin
+ *          ^^^^^^^^ LoopLength
+ *                 ^ (LoopEnd)
+ *
+ * In the simple case, playback will start at PlayBegin. At LoopEnd, playback
+ * will move to LoopBegin and repeat that loop LoopCount times. Then, playback
+ * will cease at LoopEnd.
+ *
+ * If PlayLength is zero, then PlayEnd is the end of the buffer.
+ *
+ * If LoopLength is zero, then LoopEnd is PlayEnd.
+ *
+ * For corner cases and version differences, see tests.
+ */
 static void update_source_state(XA2SourceImpl *src)
 {
     int i;
@@ -2985,15 +3079,35 @@ static void update_source_state(XA2SourceImpl *src)
         TRACE("%p: going to queue a period from buffer %u\n", src, src->cur_buf);
 
         /* starting from an empty buffer */
-        if(src->cb && src->cur_buf == src->first_buf && src->buffers[src->cur_buf].offs_bytes == 0)
+        if(src->cb && src->cur_buf == src->first_buf && src->buffers[src->cur_buf].offs_bytes == 0 && !src->buffers[src->cur_buf].looped)
             IXAudio2VoiceCallback_OnBufferStart(src->cb,
                     src->buffers[src->first_buf].xa2buffer.pContext);
 
         if(!xa2buffer_queue_period(src, &src->buffers[src->cur_buf],
                     src->al_bufs[(src->first_al_buf + src->al_bufs_used) % XAUDIO2_MAX_QUEUED_BUFFERS])){
-            /* buffer is spent, move on */
-            src->cur_buf++;
-            src->cur_buf %= XAUDIO2_MAX_QUEUED_BUFFERS;
+            XA2Buffer *cur = &src->buffers[src->cur_buf];
+
+            if(cur->looped < cur->xa2buffer.LoopCount){
+                if(cur->xa2buffer.LoopCount != XAUDIO2_LOOP_INFINITE)
+                    ++cur->looped;
+                else
+                    cur->looped = 1; /* indicate that we are executing a loop */
+
+                cur->offs_bytes = cur->xa2buffer.LoopBegin;
+                if(cur->looped == cur->xa2buffer.LoopCount)
+                    cur->cur_end_bytes = cur->play_end_bytes;
+                else
+                    cur->cur_end_bytes = cur->loop_end_bytes;
+
+                if(src->cb)
+                    IXAudio2VoiceCallback_OnLoopEnd(src->cb,
+                            src->buffers[src->cur_buf].xa2buffer.pContext);
+
+            }else{
+                /* buffer is spent, move on */
+                src->cur_buf++;
+                src->cur_buf %= XAUDIO2_MAX_QUEUED_BUFFERS;
+            }
         }
     }
 }
-- 
2.5.1




More information about the wine-patches mailing list