Add speexdsp support
8 files changed, 171 insertions(+), 19 deletions(-)

M build/run-platform-tests.sh
M speex/COPYING => ext/speex/COPYING
M speex/resample.c => ext/speex/resample.c
M speex/speex_resampler.h => ext/speex/speex_resampler.h
M src/BQResampler.cpp
M src/Resampler.cpp
A => src/common.cpp
A => src/common.h
M build/run-platform-tests.sh +1 -1
@@ 35,7 35,7 @@ run() {
     shift
     echo -n "Running \"$@\"..."
     if "$@" > "$tmpfile" 2>&1 ; then
-	if [ -z "$successtext" ] || fgrep -q "$successtext" "$tmpfile" ; then
+	if [ -z "$successtext" ] || grep -f -q "$successtext" "$tmpfile" ; then
 	    echo " OK"
 	    return 0
 	else

          
M speex/COPYING => ext/speex/COPYING +0 -0

        
M speex/resample.c => ext/speex/resample.c +0 -0

        
M speex/speex_resampler.h => ext/speex/speex_resampler.h +0 -0

        
M src/BQResampler.cpp +1 -0
@@ 34,6 34,7 @@ 
 */
 
 #include "BQResampler.h"
+#include "common.h"
 
 #include <cmath>
 

          
M src/Resampler.cpp +42 -18
@@ 34,6 34,7 @@ 
 */
 
 #include "bqresample/Resampler.h"
+#include "common.h"
 
 #include <cstdlib>
 #include <cmath>

          
@@ 62,7 63,11 @@ 
 #endif
 
 #ifdef USE_SPEEX
-#include "../speex/speex_resampler.h"
+#include "../ext/speex/speex_resampler.h"
+#else
+#ifdef HAVE_LIBSPEEXDSP
+#include <speex/speex_resampler.h>
+#endif
 #endif
 
 #ifdef USE_BQRESAMPLER

          
@@ 71,6 76,7 @@ 
 
 #ifndef HAVE_IPP
 #ifndef HAVE_LIBSAMPLERATE
+#ifndef HAVE_LIBSPEEXDSP
 #ifndef USE_SPEEX
 #ifndef USE_BQRESAMPLER
 #error No resampler implementation selected!

          
@@ 78,6 84,7 @@ 
 #endif
 #endif
 #endif
+#endif
 
 using namespace std;
 

          
@@ 921,7 928,7 @@ D_BQResampler::reset()
 
 #endif /* USE_BQRESAMPLER */
 
-#ifdef USE_SPEEX
+#if defined(USE_SPEEX) || defined(HAVE_LIBSPEEXDSP)
     
 class D_Speex : public Resampler::Impl
 {

          
@@ 1029,19 1036,23 @@ D_Speex::setRatio(double ratio)
     // Speex wants a ratio of two unsigned integers, not a single
     // float.  Let's do that.
 
-    unsigned int big = 272408136U; 
-    unsigned int denom = 1, num = 1;
+    int max_denom = 48000;
+    if (ratio > 1.0) {
+        max_denom = int(ceil(48000 / ratio));
+    }
+
+    int inum, idenom;
+    pickNearestRational(ratio, max_denom, inum, idenom);
 
-    if (ratio < 1.f) {
-        denom = big;
-        double dnum = double(big) * double(ratio);
-        num = (unsigned int)dnum;
-    } else if (ratio > 1.f) {
-        num = big;
-        double ddenom = double(big) / double(ratio);
-        denom = (unsigned int)ddenom;
+    if (inum < 0 || idenom < 0) {
+        cerr << "Resampler::setRatio: Internal error: "
+             << "numerator or denominator < 0 ("
+             << inum << "/" << idenom << ")" << endl;
+        return;
     }
     
+    unsigned int num = inum, denom = idenom;
+    
     if (m_debugLevel > 1) {
         cerr << "D_Speex: Desired ratio " << ratio << ", requesting ratio "
              << num << "/" << denom << " = " << float(double(num)/double(denom))

          
@@ 1055,8 1066,12 @@ D_Speex::setRatio(double ratio)
         (m_resampler, denom, num, fromRate, toRate);
 
     if (err) {
-        cerr << "Resampler::Resampler: failed to set rate on Speex resampler" 
-             << endl;
+        cerr << "Resampler::Resampler: failed to set rate on Speex resampler"
+             << " (with ratio = " << ratio << " [ratio-1 = " << ratio - 1.0
+             << "], denom = " << denom
+             << ", num = " << num << ", fromRate = " << fromRate
+             << ", toRate = " << toRate << ", err = " << err
+             << ")" << endl;
 #ifndef NO_EXCEPTIONS
         throw Resampler::ImplementationError;
 #endif

          
@@ 1219,8 1234,11 @@ Resampler::Resampler(Resampler::Paramete
 #ifdef USE_SPEEX
         m_method = 2;
 #endif
+#ifdef HAVE_LIBSPEEXDSP
+        m_method = 2;
+#endif
 #ifdef USE_BQRESAMPLER
-        m_method = 4;
+        m_method = 3;
 #endif
 #ifdef HAVE_LIBSAMPLERATE
         m_method = 1;

          
@@ 1235,11 1253,14 @@ Resampler::Resampler(Resampler::Paramete
         m_method = 2;
 #endif
 #ifdef USE_BQRESAMPLER
-        m_method = 4;
+        m_method = 3;
 #endif
 #ifdef HAVE_LIBSAMPLERATE
         m_method = 1;
 #endif
+#ifdef HAVE_LIBSPEEXDSP
+        m_method = 2;
+#endif
         break;
 
     case Resampler::Fastest:

          
@@ 1250,11 1271,14 @@ Resampler::Resampler(Resampler::Paramete
         m_method = 2;
 #endif
 #ifdef USE_BQRESAMPLER
-        m_method = 4;
+        m_method = 3;
 #endif
 #ifdef HAVE_LIBSAMPLERATE
         m_method = 1;
 #endif
+#ifdef HAVE_LIBSPEEXDSP
+        m_method = 2;
+#endif
         break;
     }
 

          
@@ 1289,7 1313,7 @@ Resampler::Resampler(Resampler::Paramete
         break;
 
     case 2:
-#ifdef USE_SPEEX
+#if defined(USE_SPEEX) || defined(HAVE_LIBSPEEXDSP)
         d = new Resamplers::D_Speex
             (params.quality, params.ratioChange,
              channels,

          
A => src/common.cpp +82 -0
@@ 0,0 1,82 @@ 
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqresample
+
+    A small library wrapping various audio sample rate conversion
+    implementations in C++.
+
+    Copyright 2007-2023 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#include <cmath>
+
+namespace breakfastquay {
+
+void pickNearestRational(double ratio, int max_denom, int &num, int &denom)
+{
+    // Farey algorithm, see
+    // https://www.johndcook.com/blog/2010/10/20/best-rational-approximation/
+    double a = 0.0, b = 1.0, c = 1.0, d = 0.0;
+    double pa = a, pb = b, pc = c, pd = d;
+    double eps = 1e-9;
+    while (b <= max_denom && d <= max_denom) {
+        double mediant = (a + c) / (b + d);
+        if (fabs(ratio - mediant) < eps) {
+            if (b + d <= max_denom) {
+                num = a + c;
+                denom = b + d;
+                return;
+            } else if (d > b) {
+                num = c;
+                denom = d;
+                return;
+            } else {
+                num = a;
+                denom = b;
+                return;
+            }
+        }
+        if (ratio > mediant) {
+            pa = a; pb = b;
+            a += c; b += d;
+        } else {
+            pc = c; pd = d;
+            c += a; d += b;
+        }
+    }
+    if (fabs(ratio - (pc / pd)) < fabs(ratio - (pa / pb))) {
+        num = pc;
+        denom = pd;
+    } else {
+        num = pa;
+        denom = pb;
+    }
+}
+
+}
+

          
A => src/common.h +45 -0
@@ 0,0 1,45 @@ 
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqresample
+
+    A small library wrapping various audio sample rate conversion
+    implementations in C++.
+
+    Copyright 2007-2023 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQ_BQRESAMPLE_COMMON_H
+#define BQ_BQRESAMPLE_COMMON_H
+
+namespace breakfastquay {
+
+void pickNearestRational(double ratio, int maxDenom, int &num, int &denom);
+
+}
+
+#endif