foxygit / doom Log in
commit 4f16d8163cedb8b42ac01512c32a54023708b0ec
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Mon Feb 11 22:59:51 2008 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Mon Feb 11 22:59:51 2008 +0000

    Add support for sample rate conversion using libsamplerate (thanks to
    David Flater for this patch).

    Subversion-branch: /trunk/chocolate-doom
    Subversion-revision: 1076
---
 configure.in     |   3 ++
 src/i_sdlsound.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 src/m_misc.c     |   5 +++
 3 files changed, 116 insertions(+), 15 deletions(-)

diff --git a/configure.in b/configure.in
index 07efe457..322c4957 100644
--- a/configure.in
+++ b/configure.in
@@ -43,6 +43,9 @@ AC_CHECK_LIB(SDL_net,SDLNet_UDP_Send,[
     exit -1
 ] ,$SDL_LIBS $SDLNET_LIBS)

+# DWF 2008-02-10:  FIXME
+AC_CHECK_LIB(samplerate, src_new)
+
 AC_CHECK_TOOL(WINDRES, windres, )

 AM_CONDITIONAL(HAVE_WINDRES, test "$WINDRES" != "")
diff --git a/src/i_sdlsound.c b/src/i_sdlsound.c
index e10cb5ee..5c68d00f 100644
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@ -3,6 +3,7 @@
 //
 // Copyright(C) 1993-1996 Id Software, Inc.
 // Copyright(C) 2005 Simon Howard
+// Copyright(C) 2008 David Flater
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -25,11 +26,18 @@
 //-----------------------------------------------------------------------------


+#include "config.h"
+
 #include <stdio.h>
 #include <stdlib.h>
+#include <assert.h>
 #include "SDL.h"
 #include "SDL_mixer.h"

+#ifdef HAVE_LIBSAMPLERATE
+#include <samplerate.h>
+#endif
+
 #include "deh_main.h"
 #include "s_sound.h"
 #include "m_argv.h"
@@ -48,6 +56,10 @@ 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;
+
+int use_libsamplerate = 0;

 // When a sound stops, check if it is still playing.  If it is not,
 // we can mark the sound data as CACHE to be freed back for other
@@ -78,6 +90,72 @@ static void ReleaseSoundOnChannel(int channel)
     Z_ChangeTag(sound_chunks[id].abuf, PU_CACHE);
 }

+#ifdef HAVE_LIBSAMPLERATE
+
+// libsamplerate-based generic sound expansion function for any sample rate
+//   unsigned 8 bits --> signed 16 bits
+//   mono --> stereo
+//   samplerate --> mixer_freq
+// Rewritten by DWF 2008-02-10:
+
+static void ExpandSoundData_SRC(byte *data,
+                                int samplerate,
+                                int length,
+                                Mix_Chunk *destination)
+{
+    SRC_DATA src_data;
+    uint32_t i, abuf_index=0;
+    int retn;
+    int16_t *expanded;
+
+    src_data.input_frames = length;
+    src_data.data_in = malloc(length * sizeof(float));
+    src_data.src_ratio = (double)mixer_freq / samplerate;
+    src_data.output_frames = src_data.src_ratio * length + samplerate;
+    src_data.data_out = malloc(src_data.output_frames * sizeof(float));
+
+    assert(src_data.data_in != NULL && src_data.data_out != NULL);
+
+    // Convert input data to floats
+
+    for (i=0; i<length; ++i)
+    {
+        src_data.data_in[i] = data[i] / 127.5 - 1;
+    }
+
+    // Do the sound conversion
+
+    retn = src_simple(&src_data, SRC_SINC_MEDIUM_QUALITY, 1);
+    assert(retn == 0);
+
+    // Convert the result back into 16-bit integers.
+
+    destination->alen = src_data.output_frames_gen * 4;
+    destination->abuf = Z_Malloc(destination->alen, PU_STATIC,
+                                 &destination->abuf);
+    expanded = (int16_t *) destination->abuf;
+
+    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);
+
+        // Left and right channels
+
+        expanded[abuf_index++] = cvtval;
+        expanded[abuf_index++] = cvtval;
+    }
+
+    free(src_data.data_in);
+    free(src_data.data_out);
+}
+
+#endif
+
 static boolean ConvertibleRatio(int freq1, int freq2)
 {
     int ratio;
@@ -109,12 +187,27 @@ static boolean ConvertibleRatio(int freq1, int freq2)

 // Generic sound expansion function for any sample rate

-static void ExpandSoundData(byte *data,
-                            int samplerate,
-                            int length,
-                            Mix_Chunk *destination)
+static void ExpandSoundData_SDL(byte *data,
+                                int samplerate,
+                                int length,
+                                Mix_Chunk *destination)
 {
     SDL_AudioCVT convertor;
+    uint32_t expanded_length;
+
+    // Calculate the length of the expanded version of the sample.
+
+    expanded_length = (uint32_t) ((((uint64_t) length) * mixer_freq) / samplerate);
+
+    // Double up twice: 8 -> 16 bit and mono -> stereo
+
+    expanded_length *= 4;
+
+    destination->alen = expanded_length;
+    destination->abuf
+        = Z_Malloc(expanded_length, PU_STATIC, &destination->abuf);
+
+    // If we can, use the standard / optimised SDL conversion routines.

     if (samplerate <= mixer_freq
      && ConvertibleRatio(samplerate, mixer_freq)
@@ -172,7 +265,6 @@ static boolean CacheSFX(int sound)
     unsigned int lumplen;
     int samplerate;
     unsigned int length;
-    unsigned int expanded_length;
     byte *data;

     // need to load the sound
@@ -205,19 +297,11 @@ static boolean CacheSFX(int sound)
     }

     // Sample rate conversion
-
-    expanded_length = (uint32_t) ((((uint64_t) length) * mixer_freq) / samplerate);
-
-    // Double up twice: 8 -> 16 bit and mono -> stereo
-
-    expanded_length *= 4;
+    // DWF 2008-02-10:  sound_chunks[sound].alen and abuf are determined
+    // by ExpandSoundData.

     sound_chunks[sound].allocated = 1;
-    sound_chunks[sound].alen = expanded_length;
-    sound_chunks[sound].abuf
-        = Z_Malloc(expanded_length, PU_STATIC, &sound_chunks[sound].abuf);
     sound_chunks[sound].volume = MIX_MAX_VOLUME;
-
     ExpandSoundData(data + 8,
                     samplerate,
                     length,
@@ -416,6 +500,15 @@ static boolean I_SDL_InitSound()
         return false;
     }

+    ExpandSoundData = ExpandSoundData_SDL;
+
+#ifdef HAVE_LIBSAMPLERATE
+    if (use_libsamplerate)
+    {
+        ExpandSoundData = ExpandSoundData_SRC;
+    }
+#endif
+
     Mix_QuerySpec(&mixer_freq, &mixer_format, &mixer_channels);

     Mix_AllocateChannels(NUM_CHANNELS);
diff --git a/src/m_misc.c b/src/m_misc.c
index d3f531b0..d1b9c567 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -299,6 +299,10 @@ extern int snd_musicdevice;
 extern int snd_sfxdevice;
 extern int snd_samplerate;

+// controls whether to use libsamplerate for sample rate conversions
+
+extern int use_libsamplerate;
+
 // dos specific options: these are unused but should be maintained
 // so that the config file can be shared between chocolate
 // doom and doom.exe
@@ -444,6 +448,7 @@ static default_t extra_defaults_list[] =
     {"mouseb_backward",             &mousebbackward, DEFAULT_INT, 0, 0},

     {"dclick_use",                  &dclick_use, DEFAULT_INT, 0, 0},
+    {"use_libsamplerate",           &use_libsamplerate, DEFAULT_INT, 0, 0},
 };

 static default_collection_t extra_defaults =