Use libsamplerate for resampling instead of FFmpeg / libsoxr.
authorCarl Hetherington <cth@carlh.net>
Fri, 4 Sep 2015 20:51:54 +0000 (21:51 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 4 Sep 2015 23:25:08 +0000 (00:25 +0100)
libsoxr was crashing on Windows and seems somewhat
unmaintained.

ChangeLog
src/lib/resampler.cc
src/lib/resampler.h
src/lib/wscript
src/tools/wscript
wscript

index aefa39a917ada76dd971d9abcde6ce8669d25976..6583113a61eea9bd34bb214d26c54e7d2915d5b1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
 2015-09-04  Carl Hetherington  <cth@carlh.net>
 
+       * Use libsamplerate for resampling instead
+       of FFmpeg / libsoxr.
+
        * Fix audio mapping view changing size
        on first click.
 
index 78e1da114976dc0a0faef01b868e8a9de7659a15..b12b549bf1f4618ccaacfdc4c401f7fd54adfd36 100644 (file)
 
 */
 
-extern "C" {
-#include "libavutil/channel_layout.h"
-#include "libavutil/opt.h"
-}
 #include "resampler.h"
 #include "audio_buffers.h"
 #include "exceptions.h"
 #include "compose.hpp"
+#include "dcpomatic_assert.h"
+#include <samplerate.h>
 
 #include "i18n.h"
 
@@ -38,56 +36,84 @@ Resampler::Resampler (int in, int out, int channels)
        , _out_rate (out)
        , _channels (channels)
 {
-       _swr_context = swr_alloc ();
-       if (!_swr_context) {
-               throw StringError (N_("could not allocate resampler contexct"));
-       }
-
-       /* Sample formats */
-       av_opt_set_int (_swr_context, "isf", AV_SAMPLE_FMT_FLTP, 0);
-       av_opt_set_int (_swr_context, "osf", AV_SAMPLE_FMT_FLTP, 0);
-
-       /* Channel counts */
-       av_opt_set_int (_swr_context, "ich", _channels, 0);
-       av_opt_set_int (_swr_context, "och", _channels, 0);
-
-       /* Sample rates */
-       av_opt_set_int (_swr_context, "isr", _in_rate, 0);
-       av_opt_set_int (_swr_context, "osr", _out_rate, 0);
-
-       av_opt_set (_swr_context, "resampler", "soxr", 0);
-
-       int const r = swr_init (_swr_context);
-       if (r) {
-               char buf[256];
-               av_strerror (r, buf, sizeof(buf));
-               throw StringError (String::compose (N_ ("could not initialise sample-rate converter (%1)"), r));
+       int error;
+       _src = src_new (SRC_SINC_BEST_QUALITY, _channels, &error);
+       if (!_src) {
+               throw StringError (String::compose (N_("could not create sample-rate converter (%1)"), error));
        }
 }
 
 Resampler::~Resampler ()
 {
-       swr_free (&_swr_context);
+       src_delete (_src);
 }
 
 shared_ptr<const AudioBuffers>
 Resampler::run (shared_ptr<const AudioBuffers> in)
 {
-       /* Compute the resampled frames count and add 32 for luck */
-       int const max_resampled_frames = ceil ((double) in->frames() * _out_rate / _in_rate) + 32;
-       shared_ptr<AudioBuffers> resampled (new AudioBuffers (_channels, max_resampled_frames));
-
-       int const resampled_frames = swr_convert (
-               _swr_context, (uint8_t **) resampled->data(), max_resampled_frames, (uint8_t const **) in->data(), in->frames()
-               );
-
-       if (resampled_frames < 0) {
-               char buf[256];
-               av_strerror (resampled_frames, buf, sizeof(buf));
-               throw EncodeError (String::compose (_("could not run sample-rate converter for %1 samples (%2) (%3)"), in->frames(), resampled_frames, buf));
+       int in_frames = in->frames ();
+       int in_offset = 0;
+       int out_offset = 0;
+       shared_ptr<AudioBuffers> resampled (new AudioBuffers (_channels, 0));
+
+       while (in_frames > 0) {
+
+               /* Compute the resampled frames count and add 32 for luck */
+               int const max_resampled_frames = ceil ((double) in_frames * _out_rate / _in_rate) + 32;
+
+               SRC_DATA data;
+               data.data_in = new float[in_frames * _channels];
+
+               {
+                       float** p = in->data ();
+                       float* q = data.data_in;
+                       for (int i = 0; i < in_frames; ++i) {
+                               for (int j = 0; j < _channels; ++j) {
+                                       *q++ = p[j][in_offset + i];
+                               }
+                       }
+               }
+
+               data.input_frames = in_frames;
+
+               data.data_out = new float[max_resampled_frames * _channels];
+               data.output_frames = max_resampled_frames;
+
+               data.end_of_input = 0;
+               data.src_ratio = double (_out_rate) / _in_rate;
+
+               int const r = src_process (_src, &data);
+               if (r) {
+                       delete[] data.data_in;
+                       delete[] data.data_out;
+                       throw EncodeError (String::compose (N_("could not run sample-rate converter (%1)"), src_strerror (r)));
+               }
+
+               if (data.output_frames_gen == 0) {
+                       break;
+               }
+
+               resampled->ensure_size (out_offset + data.output_frames_gen);
+               resampled->set_frames (out_offset + data.output_frames_gen);
+
+               {
+                       float* p = data.data_out;
+                       float** q = resampled->data ();
+                       for (int i = 0; i < data.output_frames_gen; ++i) {
+                               for (int j = 0; j < _channels; ++j) {
+                                       q[j][out_offset + i] = *p++;
+                               }
+                       }
+               }
+
+               in_frames -= data.input_frames_used;
+               in_offset += data.input_frames_used;
+               out_offset += data.output_frames_gen;
+
+               delete[] data.data_in;
+               delete[] data.data_out;
        }
 
-       resampled->set_frames (resampled_frames);
        return resampled;
 }
 
@@ -96,25 +122,36 @@ Resampler::flush ()
 {
        shared_ptr<AudioBuffers> out (new AudioBuffers (_channels, 0));
        int out_offset = 0;
-       int64_t const pass_size = 256;
-       shared_ptr<AudioBuffers> pass (new AudioBuffers (_channels, 256));
+       int64_t const output_size = 65536;
 
-       while (true) {
-               int const frames = swr_convert (_swr_context, (uint8_t **) pass->data(), pass_size, 0, 0);
+       float dummy[1];
+       float buffer[output_size];
 
-               if (frames < 0) {
-                       throw EncodeError (_("could not run sample-rate converter"));
-               }
+       SRC_DATA data;
+       data.data_in = dummy;
+       data.input_frames = 0;
+       data.data_out = buffer;
+       data.output_frames = output_size;
+       data.end_of_input = 1;
+       data.src_ratio = double (_out_rate) / _in_rate;
 
-               if (frames == 0) {
-                       break;
-               }
+       int const r = src_process (_src, &data);
+       if (r) {
+               throw EncodeError (String::compose (N_("could not run sample-rate converter (%1)"), src_strerror (r)));
+       }
+
+       out->ensure_size (out_offset + data.output_frames_gen);
 
-               out->ensure_size (out_offset + frames);
-               out->copy_from (pass.get(), frames, 0, out_offset);
-               out_offset += frames;
-               out->set_frames (out_offset);
+       float* p = data.data_out;
+       float** q = out->data ();
+       for (int i = 0; i < data.output_frames_gen; ++i) {
+               for (int j = 0; j < _channels; ++j) {
+                       q[j][out_offset + i] = *p++;
+               }
        }
 
+       out_offset += data.output_frames_gen;
+       out->set_frames (out_offset);
+
        return out;
 }
index 36b991ddf5190a140a933aa8780bf06c4ed5a544..5a69d09834e81bc483c350f26fc5ca5553263341 100644 (file)
@@ -18,9 +18,7 @@
 */
 
 #include "types.h"
-extern "C" {
-#include <libswresample/swresample.h>
-}
+#include <samplerate.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/utility.hpp>
 
@@ -36,7 +34,7 @@ public:
        boost::shared_ptr<const AudioBuffers> flush ();
 
 private:
-       SwrContext* _swr_context;
+       SRC_STATE* _src;
        int _in_rate;
        int _out_rate;
        int _channels;
index 68005f50b556333d3781071b89700cf33917d562..f0646910b13bdf744b721bee6f4f74582cdebc49 100644 (file)
@@ -140,7 +140,7 @@ def build(bld):
     obj.uselib = """
                  AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE
                  BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2 BOOST_REGEX
-                 SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XML++
+                 SNDFILE SAMPLERATE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XML++
                  CURL ZIP FONTCONFIG PANGOMM CAIROMM XMLSEC SUB
                  """
 
index c6e4b3236b8b5147b2beb66b0498dc2665c43978..33a631e6e2679c4d735c2400d298ab48d0b7e719 100644 (file)
@@ -28,7 +28,7 @@ def configure(conf):
 
 def build(bld):
     uselib =  'BOOST_THREAD BOOST_DATETIME OPENJPEG DCP XMLSEC CXML XMLPP AVFORMAT AVFILTER AVCODEC '
-    uselib += 'AVUTIL SWSCALE POSTPROC CURL BOOST_FILESYSTEM SSH ZIP CAIROMM FONTCONFIG PANGOMM SUB MAGICK SNDFILE BOOST_REGEX '
+    uselib += 'AVUTIL SWSCALE POSTPROC CURL BOOST_FILESYSTEM SSH ZIP CAIROMM FONTCONFIG PANGOMM SUB MAGICK SNDFILE SAMPLERATE BOOST_REGEX '
 
     if bld.env.TARGET_WINDOWS:
         uselib += 'WINSOCK2'
diff --git a/wscript b/wscript
index d77fadb8e9591904b01ca17089b40af1ed1cb15f..18409fe01eab5c1019524858aa36b989c3788c2b 100644 (file)
--- a/wscript
+++ b/wscript
@@ -163,6 +163,9 @@ def configure(conf):
     # libsndfile
     conf.check_cfg(package='sndfile', args='--cflags --libs', uselib_store='SNDFILE', mandatory=True)
 
+    # libsamplerate
+    conf.check_cfg(package='samplerate', args='--cflags --libs', uselib_store='SAMPLERATE', mandatory=True)
+
     # glib
     conf.check_cfg(package='glib-2.0', args='--cflags --libs', uselib_store='GLIB', mandatory=True)