X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fresampler.cc;h=c18bad3ac9eccfcb7b1b9c5ea6fc286207b94d89;hb=1858190cff2f960f3d1f0a5cc02c69da86088f5b;hp=00121384dce7617ab59f0ee85ea0373a7ab0c440;hpb=8aeb741ccbe2edb528e98a431bf55459a6836a9b;p=dcpomatic.git diff --git a/src/lib/resampler.cc b/src/lib/resampler.cc index 00121384d..c18bad3ac 100644 --- a/src/lib/resampler.cc +++ b/src/lib/resampler.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington + Copyright (C) 2013-2015 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,13 +17,13 @@ */ -extern "C" { -#include "libavutil/channel_layout.h" -} #include "resampler.h" #include "audio_buffers.h" #include "exceptions.h" #include "compose.hpp" +#include "dcpomatic_assert.h" +#include +#include #include "i18n.h" @@ -32,83 +32,132 @@ using std::pair; using std::make_pair; using boost::shared_ptr; -Resampler::Resampler (int in, int out, int channels) +/** @param in Input sampling rate (Hz) + * @param out Output sampling rate (Hz) + * @param channels Number of channels. + * @param fast true to be fast rather than good. + */ +Resampler::Resampler (int in, int out, int channels, bool fast) : _in_rate (in) , _out_rate (out) , _channels (channels) { - /* We will be using planar float data when we call the - resampler. As far as I can see, the audio channel - layout is not necessary for our purposes; it seems - only to be used get the number of channels and - decide if rematrixing is needed. It won't be, since - input and output layouts are the same. - */ - - _swr_context = swr_alloc_set_opts ( - 0, - av_get_default_channel_layout (_channels), - AV_SAMPLE_FMT_FLTP, - _out_rate, - av_get_default_channel_layout (_channels), - AV_SAMPLE_FMT_FLTP, - _in_rate, - 0, 0 - ); - - swr_init (_swr_context); + int error; + _src = src_new (fast ? SRC_LINEAR : 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 Resampler::run (shared_ptr 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 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 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; -} +} shared_ptr Resampler::flush () { shared_ptr out (new AudioBuffers (_channels, 0)); int out_offset = 0; - int64_t const pass_size = 256; - shared_ptr pass (new AudioBuffers (_channels, 256)); - - while (1) { - int const frames = swr_convert (_swr_context, (uint8_t **) pass->data(), pass_size, 0, 0); - - if (frames < 0) { - throw EncodeError (_("could not run sample-rate converter")); - } - - if (frames == 0) { - break; - } + int64_t const output_size = 65536; + + float dummy[1]; + float buffer[output_size]; + + 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; + + 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 + frames); - out->copy_from (pass.get(), frames, 0, out_offset); - out_offset += frames; - out->set_frames (out_offset); + out->ensure_size (out_offset + data.output_frames_gen); + + 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; }