foxygit / doom Log in
commit de318a6f06dd24136a3d538a27ecef43a1bce1e5
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Thu Mar 13 18:33:59 2008 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Thu Mar 13 18:33:59 2008 +0000

    Apply SRC patch from David Flater.

    Subversion-branch: /trunk/chocolate-doom
    Subversion-revision: 1105
---
 src/i_sdlsound.c | 93 +++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 68 insertions(+), 25 deletions(-)

diff --git a/src/i_sdlsound.c b/src/i_sdlsound.c
index 6764d19e..a250cc36 100644
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@ -57,8 +57,8 @@ static int channels_playing[NUM_CHANNELS];
 static int mixer_freq;
 static Uint16 mixer_format;
 static int mixer_channels;
-static void (*ExpandSoundData)(byte *data, int samplerate, int length,
-                               Mix_Chunk *destination) = NULL;
+static uint32_t (*ExpandSoundData)(byte *data, int samplerate, int length,
+                                   Mix_Chunk *destination) = NULL;

 int use_libsamplerate = 0;

@@ -97,15 +97,16 @@ static void ReleaseSoundOnChannel(int channel)
 //   unsigned 8 bits --> signed 16 bits
 //   mono --> stereo
 //   samplerate --> mixer_freq
+// Returns number of clipped samples.
 // DWF 2008-02-10 with cleanups by Simon Howard.

-static void ExpandSoundData_SRC(byte *data,
-                                int samplerate,
-                                int length,
-                                Mix_Chunk *destination)
+static uint32_t ExpandSoundData_SRC(byte *data,
+                                    int samplerate,
+                                    int length,
+                                    Mix_Chunk *destination)
 {
     SRC_DATA src_data;
-    uint32_t i, abuf_index=0;
+    uint32_t i, abuf_index=0, clipped=0;
     int retn;
     int16_t *expanded;

@@ -123,12 +124,15 @@ static void ExpandSoundData_SRC(byte *data,

     for (i=0; i<length; ++i)
     {
+        // Unclear whether 128 should be interpreted as "zero" or whether a
+        // symmetrical range should be assumed.  The following assumes a
+        // symmetrical range.
         src_data.data_in[i] = data[i] / 127.5 - 1;
     }

     // Do the sound conversion

-    retn = src_simple(&src_data, SRC_SINC_FASTEST, 1);
+    retn = src_simple(&src_data, SRC_SINC_BEST_QUALITY, 1);
     assert(retn == 0);

     // Convert the result back into 16-bit integers.
@@ -140,21 +144,48 @@ static void ExpandSoundData_SRC(byte *data,

     for (i=0; i<src_data.output_frames_gen; ++i)
     {
-        // libsamplerate does not limit itself to the -1.0 .. 1.0 range
-        // on output, so some slack is required to avoid overflows or
-        // clipping.  The amount of slack is a fudge factor.
-
-        float cvtval = src_data.data_out[i] * 20000;
-        cvtval += (cvtval < 0 ? -0.5 : 0.5);
+        // libsamplerate does not limit itself to the -1.0 .. 1.0 range on
+        // output, so a multiplier less than INT16_MAX (32767) is required
+        // to avoid overflows or clipping.  However, the smaller the
+        // multiplier, the quieter the sound effects get, and the more you
+        // have to turn down the music to keep it in balance.
+
+        // 22265 is the largest multiplier that can be used to resample all
+        // of the Vanilla DOOM sound effects to 48 kHz without clipping
+        // using SRC_SINC_BEST_QUALITY.  It is close enough (only slightly
+        // too conservative) for SRC_SINC_MEDIUM_QUALITY and
+        // SRC_SINC_FASTEST.  PWADs with interestingly different sound
+        // effects or target rates other than 48 kHz might still result in
+        // clipping--I don't know if there's a limit to it.
+
+        // As the number of clipped samples increases, the signal is
+        // gradually overtaken by noise, with the loudest parts going first.
+        // However, a moderate amount of clipping is often tolerated in the
+        // quest for the loudest possible sound overall.  The results of
+        // using INT16_MAX as the multiplier are not all that bad, but
+        // artifacts are noticeable during the loudest parts.
+
+        float   cvtval_f = src_data.data_out[i] * 22265;
+        int32_t cvtval_i = cvtval_f + (cvtval_f < 0 ? -0.5 : 0.5);
+
+        // Asymmetrical sound worries me, so we won't use -32768.
+        if (cvtval_i < -INT16_MAX) {
+          cvtval_i = -INT16_MAX;
+          ++clipped;
+        } else if (cvtval_i > INT16_MAX) {
+          cvtval_i = INT16_MAX;
+          ++clipped;
+        }

         // Left and right channels

-        expanded[abuf_index++] = cvtval;
-        expanded[abuf_index++] = cvtval;
+        expanded[abuf_index++] = cvtval_i;
+        expanded[abuf_index++] = cvtval_i;
     }

     free(src_data.data_in);
     free(src_data.data_out);
+    return clipped;
 }

 #endif
@@ -188,12 +219,13 @@ static boolean ConvertibleRatio(int freq1, int freq2)
     }
 }

-// Generic sound expansion function for any sample rate
+// Generic sound expansion function for any sample rate.
+// Returns number of clipped samples (always 0).

-static void ExpandSoundData_SDL(byte *data,
-                                int samplerate,
-                                int length,
-                                Mix_Chunk *destination)
+static uint32_t ExpandSoundData_SDL(byte *data,
+                                    int samplerate,
+                                    int length,
+                                    Mix_Chunk *destination)
 {
     SDL_AudioCVT convertor;
     uint32_t expanded_length;
@@ -284,6 +316,8 @@ static void ExpandSoundData_SDL(byte *data,
         }
 #endif /* #ifdef LOW_PASS_FILTER */
     }
+
+    return 0;
 }

 // Load and convert a sound effect
@@ -294,6 +328,7 @@ static boolean CacheSFX(int sound)
     int lumpnum;
     unsigned int lumplen;
     int samplerate;
+    int clipped;
     unsigned int length;
     byte *data;

@@ -332,10 +367,18 @@ static boolean CacheSFX(int sound)

     sound_chunks[sound].allocated = 1;
     sound_chunks[sound].volume = MIX_MAX_VOLUME;
-    ExpandSoundData(data + 8,
-                    samplerate,
-                    length,
-                    &sound_chunks[sound]);
+
+    clipped = ExpandSoundData(data + 8,
+                              samplerate,
+                              length,
+                              &sound_chunks[sound]);
+
+    if (clipped)
+    {
+        fprintf(stderr, "Sound %d: clipped %u samples (%0.2f %%)\n",
+                        sound, clipped,
+                        400.0 * clipped / sound_chunks[sound].alen);
+    }

     // don't need the original lump any more