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

Krzysztof Nikiel knik00 at gmail.com
Fri Feb 11 03:19:47 CST 2011


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..7738dd4
--- /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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-patches/attachments/20110211/ecbb9b13/attachment-0001.htm>


More information about the wine-patches mailing list