[PATCH variant 2] dsound: Add a linear resampler, use it for games

Alexander E. Patrakov patrakov at gmail.com
Sat May 19 10:12:48 CDT 2012


This patch implements a linear resampler and uses it if the existing
high-quality resampler would be inappropriate due to the high CPU
usage. Please read
http://www.winehq.org/pipermail/wine-patches/2012-May/114356.html for
the rest of information about the problem and the two proposed
patches.

-- 
Alexander E. Patrakov
-------------- next part --------------
From 2908b4827982ccabf230d5640a47d8d0b9f09312 Mon Sep 17 00:00:00 2001
From: "Alexander E. Patrakov" <patrakov at gmail.com>
Date: Sat, 12 May 2012 22:54:18 +0600
Subject: [PATCH] dsound: Add a linear resampler, use it for games

---
 dlls/dsound/dsound_main.c    |    5 +++
 dlls/dsound/dsound_private.h |    1 +
 dlls/dsound/mixer.c          |   57 ++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c
index 76b71c9..7c7cfcf 100644
--- a/dlls/dsound/dsound_main.c
+++ b/dlls/dsound/dsound_main.c
@@ -95,6 +95,7 @@ int ds_hel_buflen = 32768 * 2;
 int ds_snd_queue_max = 10;
 int ds_default_sample_rate = 44100;
 int ds_default_bits_per_sample = 16;
+int ds_hq_buffers_max = 4;
 static HINSTANCE instance;
 
 /*
@@ -157,6 +158,9 @@ void setup_dsound_options(void)
     if (!get_config_key( hkey, appkey, "DefaultBitsPerSample", buffer, MAX_PATH ))
         ds_default_bits_per_sample = atoi(buffer);
 
+    if (!get_config_key( hkey, appkey, "HQBuffersMax", buffer, MAX_PATH ))
+        ds_hq_buffers_max = atoi(buffer);
+
     if (appkey) RegCloseKey( appkey );
     if (hkey) RegCloseKey( hkey );
 
@@ -164,6 +168,7 @@ void setup_dsound_options(void)
     TRACE("ds_snd_queue_max = %d\n", ds_snd_queue_max);
     TRACE("ds_default_sample_rate = %d\n", ds_default_sample_rate);
     TRACE("ds_default_bits_per_sample = %d\n", ds_default_bits_per_sample);
+    TRACE("ds_hq_buffers_max = %d\n", ds_hq_buffers_max);
 }
 
 static const char * get_device_id(LPCGUID pGuid)
diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h
index 45c8c5d..7204376 100644
--- a/dlls/dsound/dsound_private.h
+++ b/dlls/dsound/dsound_private.h
@@ -35,6 +35,7 @@ extern int ds_snd_queue_max DECLSPEC_HIDDEN;
 extern int ds_snd_shadow_maxsize DECLSPEC_HIDDEN;
 extern int ds_default_sample_rate DECLSPEC_HIDDEN;
 extern int ds_default_bits_per_sample DECLSPEC_HIDDEN;
+extern int ds_hq_buffers_max DECLSPEC_HIDDEN;
 
 /*****************************************************************************
  * Predeclare the interface implementation structures
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index 09b69b4..e23daae 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -246,7 +246,50 @@ static UINT cp_fields_noresample(IDirectSoundBufferImpl *dsb, UINT count)
     return count;
 }
 
-static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, float *freqAcc)
+static UINT cp_fields_resample_lq(IDirectSoundBufferImpl *dsb, UINT count, float *freqAcc)
+{
+    UINT i, channel;
+    UINT istride = dsb->pwfx->nBlockAlign;
+    UINT ostride = dsb->device->pwfx->nChannels * sizeof(float);
+    UINT channels = dsb->mix_channels;
+
+    float freqAdjust = dsb->freqAdjust;
+    float freqAcc_start = *freqAcc;
+    float freqAcc_end = freqAcc_start + count * freqAdjust;
+    UINT max_ipos = freqAcc_end;
+
+    freqAcc_end -= (int)freqAcc_end;
+
+    for(i = 0; i < count; ++i) {
+        float cur_freqAcc = freqAcc_start + i * freqAdjust;
+	float cur_freqAcc2;
+        UINT ipos = cur_freqAcc;
+        UINT idx = dsb->sec_mixpos + ipos * istride;
+        cur_freqAcc -= (int)cur_freqAcc;
+        cur_freqAcc2 = 1.0 - cur_freqAcc;
+        for (channel = 0; channel < channels; channel++) {
+            /**
+             * Generally we cannot cache the result of get_current_sample().
+             * Consider the case of resampling from 192000 Hz to 44100 Hz -
+             * none of the values will get reused for the next value of i.
+             * OTOH, for resampling from 44100 Hz to 192000 Hz both values
+             * will likely be reused.
+             *
+             * So far, this possibility of saving calls to
+             * get_current_sample() is ignored.
+             */
+            float s1 = get_current_sample(dsb, idx, channel);
+            float s2 = get_current_sample(dsb, idx + istride, channel);
+            float result = s1 * cur_freqAcc2 + s2 * cur_freqAcc;
+            dsb->put(dsb, i * ostride, channel, result);
+        }
+    }
+
+    *freqAcc = freqAcc_end;
+    return max_ipos;
+}
+
+static UINT cp_fields_resample_hq(IDirectSoundBufferImpl *dsb, UINT count, float *freqAcc)
 {
     UINT i, channel;
     UINT istride = dsb->pwfx->nBlockAlign;
@@ -320,8 +363,18 @@ static void cp_fields(IDirectSoundBufferImpl *dsb, UINT count, float *freqAcc)
 
     if (dsb->freqAdjust == 1.0)
         adv = cp_fields_noresample(dsb, count); /* *freqAcc is unmodified */
+    /**
+     * HACK: distinguish music players from games by the number of
+     * secondary buffers. Music players usually have only one buffer,
+     * (or maybe two if crossfading, give them four by default just to
+     * be sure) while games have many. Sound quality is important for
+     * players (bug 14717), but the high-quality resampler is too slow
+     * for some games (bug 30639).
+     */
+    else if (dsb->device->nrofbuffers > ds_hq_buffers_max)
+        adv = cp_fields_resample_lq(dsb, count, freqAcc);
     else
-        adv = cp_fields_resample(dsb, count, freqAcc);
+        adv = cp_fields_resample_hq(dsb, count, freqAcc);
 
     ipos = dsb->sec_mixpos + adv * dsb->pwfx->nBlockAlign;
     if (ipos >= dsb->buflen) {
-- 
1.7.8.6


More information about the wine-patches mailing list