[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