[PATCH 01/13] dsound: New sample rate converter core functions. (resend)

Krzysztof Nikiel knik00 at gmail.com
Fri Feb 11 05:34:51 CST 2011


It looks like it may be too big to be accepted. Anyway, I will try to
resend it with 4 space indent for new files.
Hopefully these patches won't get wrapped this time.


Description:
High quality sample rate converter.
New code seems simplier and faster despite the big sound quality
improvement.

This patchset only makes sense when applying all 13 parts.

Fixes bug:
http://bugs.winehq.org/show_bug.cgi?id=14717



---
 dlls/dsound/resample.c |  381 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 381 insertions(+), 0 deletions(-)
 create mode 100644 dlls/dsound/resample.c

diff --git a/dlls/dsound/resample.c b/dlls/dsound/resample.c
new file mode 100644
index 0000000..3b713f6
--- /dev/null
+++ b/dlls/dsound/resample.c
@@ -0,0 +1,381 @@
+/* DirectSound
+ *
+ * Resample core
+ * Copyright 2010, 2011 Krzysztof Nikiel
+ *
+ * Initially based on resample:
+ *    http://www-ccrma.stanford.edu/~jos/resample/
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include <math.h>
+
+#define NONAMELESSSTRUCT
+#define NONAMELESSUNION
+#include "windef.h"
+#include "winbase.h"
+#include "mmsystem.h"
+#include "winternl.h"
+#include "wine/debug.h"
+#include "dsound.h"
+#include "dsdriver.h"
+#include "dsound_private.h"
+
+#include "firtab.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(dsound);
+
+
+#define MAXCHAN 16
+
+#define CLIPSAMPLE(x,max) if(x>max)x=max;if(x<-max)x=-max;
+#define SAMPLE24(p) (*((BYTE*)p)|*((BYTE*)p+1)<<8|*((CHAR*)p+2)<<16)
+#ifdef WORDS_BIGENDIAN
+#define SAMPLE16(p) (*((BYTE*)p)|*((CHAR*)p+1)<<8)
+#define PUT16(p,s) *((BYTE*)p)=s;*((BYTE*)p+1)=(s>>8)
+#else
+#define SAMPLE16(p) (*((SHORT*)p))
+#define PUT16(p,s) *((SHORT*)p)=s
+#endif
+
+
+typedef double sample_t;
+
+static fir_t *g_fir[] = {
+    &firfast,
+    &firgood,
+    &firbest,
+    &firslow,
+#if RESAMPLE_INSANE
+    &firinsane,
+#endif
+    NULL
+};
+
+
+static inline sample_t getsample(LPBYTE buf, INT bps)
+{
+    sample_t tmp;
+
+    switch (bps)
+    {
+    case 1:
+        tmp = ((sample_t) (*((BYTE *) buf)) - 128.0);
+        tmp *= 256.0;
+        break;
+    case 2:
+        tmp = SAMPLE16(buf);
+        break;
+    case 3:
+        tmp = SAMPLE24(buf);
+        tmp *= (1.0 / 256.0);
+        break;
+    case 4:
+        tmp = *((INT *) buf);
+        tmp *= (1.0 / 65536.0);
+        break;
+    }
+    return tmp;
+}
+
+static inline void putsample(LPBYTE buf, INT bps, sample_t smp)
+{
+    INT ismp;
+
+    switch (bps)
+    {
+    case 1:
+        ismp = lrint(smp);
+        ismp /= 0x100;
+        ismp += (INT) * ((BYTE *) buf) - 0x80;
+        CLIPSAMPLE(ismp, 0x7f);
+        *((BYTE *) buf) = ismp + 0x80;
+        break;
+    case 2:
+        ismp = lrint(smp);
+        ismp += SAMPLE16(buf);
+        CLIPSAMPLE(ismp, 0x7fff);
+        PUT16(buf, ismp);
+        break;
+    case 3:
+        ismp = lrint(smp * 256.0);
+        ismp += SAMPLE24(buf);
+        CLIPSAMPLE(ismp, 0x7fffff);
+        *((BYTE *) buf) = ismp & 0xff;
+        ismp >>= 8;
+        *((BYTE *) buf + 1) = ismp & 0xff;
+        ismp >>= 8;
+        *((BYTE *) buf + 2) = ismp & 0xff;
+        break;
+    case 4:
+        CLIPSAMPLE(smp, (double) 0x7fff);
+        ismp = lrint(smp * 65536.0);
+        *((INT *) buf) += ismp;
+        break;
+    }
+}
+
+
+/*
+ * Takes buffer data starting from current position and mixes new samples
+ * with primary buffer, starting at writepos.
+ * At most mixlen bytes can be mixed.
+ * 'writepos' and 'mixlen' should be block aligned.
+ */
+DWORD DSOUND_PullBuffer(IDirectSoundBufferImpl * dsb, DWORD writepos,
+                        DWORD mixlen)
+{
+    INT iAdvance, oAdvance, iBPS, oBPS;
+    sample_t rsum[MAXCHAN], smp[MAXCHAN];
+    BYTE *bufptr;
+    DWORD outlen;
+    INT iChans, oChans;
+    INT chan;
+    INT firstep;
+    DOUBLE amp[3];
+    fir_t *fir = g_fir[dsb->quality];
+
+    iAdvance = dsb->pwfx->nBlockAlign;
+    oAdvance = dsb->device->pwfx->nBlockAlign;
+    iBPS = dsb->pwfx->wBitsPerSample >> 3;
+    oBPS = dsb->device->pwfx->wBitsPerSample >> 3;
+    outlen = 0;
+    iChans = dsb->pwfx->nChannels;
+    oChans = dsb->device->pwfx->nChannels;
+
+    if (iChans >= MAXCHAN)
+        iChans = MAXCHAN - 1;
+    if (oChans >= MAXCHAN)
+        oChans = MAXCHAN - 1;
+
+    firstep = dsb->firstep;
+
+    amp[0] = (DOUBLE) dsb->volpan.dwTotalLeftAmpFactor
+        * ((DOUBLE) firstep / 65536.0);
+    if (oChans > 1)
+    {
+        amp[1] = (DOUBLE) dsb->volpan.dwTotalRightAmpFactor
+            * ((DOUBLE) firstep / 65536.0);
+        if (oChans > 2)
+            amp[2] = (DOUBLE) dsb->volpan.dwVolAmpFactor
+                * ((DOUBLE) firstep / 65536.0);
+    }
+
+    while (1)
+    {
+        double firpos, firfrac;
+        int firpos_i;
+        double ifir;
+
+        outlen += oAdvance;
+        if (outlen > mixlen)
+        {
+            outlen -= oAdvance;
+            break;
+        }
+
+        if ((dsb->inpos + iAdvance) > dsb->buflen)
+        {
+            TRACE("Buffer not sample aligned (%p,%d,%d)\n",
+                  dsb, dsb->buflen, iAdvance);
+            dsb->inpos = 0;
+        }
+
+        bufptr = dsb->buffer->memory + dsb->inpos;
+
+        if (dsb->freq != dsb->outfreq)
+        {
+            firpos = (double) fir->size - 1
+                - ((double) firstep * dsb->infrac * dsb->outfreq_1);
+
+            firfrac = firpos - floor(firpos);
+            firpos_i = firpos;
+
+            for (chan = 0; chan < iChans; chan++)
+                rsum[chan] = 0.0;
+
+            /* right wing */
+            while (firpos_i >= 0)
+            {
+                for (chan = iChans - 1; chan >= 0; chan--)
+                {
+                    bufptr -= iBPS;
+                    if (bufptr < dsb->buffer->memory)
+                        bufptr += dsb->buflen;
+
+                    smp[chan] = getsample(bufptr, iBPS);
+                }
+
+                ifir = (1.0 - firfrac) * fir->wing[firpos_i]
+                    + firfrac * fir->wing[firpos_i + 1];
+
+                for (chan = 0; chan < iChans; chan++)
+                    rsum[chan] += ifir * smp[chan];
+
+                firpos_i -= firstep;
+            }
+
+            /* left wing */
+            firpos_i = -firpos_i;
+            if (firfrac > 0.0)
+            {
+                firfrac = 1.0 - firfrac;
+                firpos_i--;
+            }
+
+            while (firpos_i < (fir->size - 1))
+            {
+                for (chan = iChans - 1; chan >= 0; chan--)
+                {
+                    bufptr -= iBPS;
+                    if (bufptr < dsb->buffer->memory)
+                        bufptr += dsb->buflen;
+
+                    smp[chan] = getsample(bufptr, iBPS);
+                }
+
+                ifir = (1.0 - firfrac) * fir->wing[firpos_i]
+                    + firfrac * fir->wing[firpos_i + 1];
+
+                for (chan = 0; chan < iChans; chan++)
+                    rsum[chan] += ifir * smp[chan];
+
+                firpos_i += firstep;
+            }
+        }
+        else
+        {
+            for (chan = iChans - 1; chan >= 0; chan--)
+            {
+                bufptr -= iBPS;
+                if (bufptr < dsb->buffer->memory)
+                    bufptr += dsb->buflen;
+
+                rsum[chan] = getsample(bufptr, iBPS);
+            }
+        }
+
+
+        if ((writepos + oAdvance) > dsb->device->buflen)
+        {
+            TRACE("Device buffer not sample aligned (%p,%d,%d)\n",
+                  dsb, dsb->device->buflen, oAdvance);
+            writepos = 0;
+        }
+
+        bufptr = dsb->device->buffer + writepos;
+
+        for (chan = 0; chan < oChans; chan++)
+        {
+            if (chan >= iChans)
+            {
+                if (iChans > 1)
+                    break;
+                if (chan > 1)
+                    break;
+
+                /* convert mono input to stereo output */
+                rsum[chan] = rsum[0];
+            }
+
+            putsample(bufptr, oBPS, rsum[chan] * amp[(chan < 2) ? chan : 2]);
+
+            bufptr += oBPS;
+        }
+
+        /* advance pointers */
+        dsb->infrac += dsb->freq;
+        while (dsb->infrac >= dsb->outfreq)
+        {
+            dsb->infrac -= dsb->outfreq;
+            dsb->inpos += iAdvance;
+            if (dsb->inpos >= dsb->buflen)
+            {
+                if (dsb->playflags & DSBPLAY_LOOPING)
+                    dsb->inpos -= dsb->buflen;
+                else
+                {
+                    dsb->inpos = dsb->infrac = 0;
+                    dsb->state = STATE_STOPPED;
+                }
+            }
+        }
+
+        writepos += oAdvance;
+        if (writepos >= dsb->device->buflen)
+            writepos -= dsb->device->buflen;
+    }
+
+    return outlen;
+}
+
+
+/**
+ * Should be called when one of the following things occur:
+ * - Primary buffer format is changed
+ * - This buffer format (frequency) is changed
+ */
+void DSOUND_RecalcFormat(IDirectSoundBufferImpl * dsb)
+{
+    int cnt;
+
+    for (cnt = 0; cnt <= ds_resample_quality; cnt++)
+    {
+        if (!g_fir[cnt])
+            break;
+    }
+    dsb->quality = cnt - 1;
+
+    dsb->nAvgBytesPerSec = dsb->freq * dsb->pwfx->nBlockAlign;
+
+    dsb->outfreq = dsb->device->pwfx->nSamplesPerSec;
+    dsb->outfreq_1 = 1.0 / dsb->outfreq;
+
+    dsb->firstep = g_fir[dsb->quality]->step;
+    if (dsb->outfreq < dsb->freq)
+    {
+        /* move transition band below output nuquist */
+        dsb->firstep = (9 * dsb->firstep * dsb->outfreq) / (10 * dsb->freq);
+        /*
+         * If firstep==0 the resample loop will hang.
+         * Maybe it would be a better idea to return some error if downsample
+         * ratio is too high.
+         */
+        if (dsb->firstep < 1)
+            dsb->firstep = 1;
+    }
+    if (dsb->freq == dsb->outfreq)
+    {
+        dsb->firstep = 1;
+        dsb->firdelay = 1;
+    }
+    else
+        dsb->firdelay = (2 * g_fir[dsb->quality]->size / dsb->firstep) + 1;
+
+    dsb->firdelay *= dsb->pwfx->nBlockAlign;
+
+    dsb->infrac = 0;
+    dsb->inpos = dsb->firdelay;
+
+    TRACE("resample quality: %d\n", dsb->quality);
+    TRACE("resample firstep %d\n", dsb->firstep);
+    TRACE("resample firdelay %d\n", dsb->firdelay);
+    TRACE("resample output: %d Hz, %d channels, %d bits\n",
+          dsb->outfreq, dsb->device->pwfx->nChannels,
+          dsb->device->pwfx->wBitsPerSample);
+}
-- 
1.7.2.3



More information about the wine-patches mailing list