X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=blobdiff_plain;f=src%2Flib%2Fresampler.cc;h=322c00c136779f009ad1163119bce5496a191dad;hp=00121384dce7617ab59f0ee85ea0373a7ab0c440;hb=a5be11a965c2c38442e4e069874e7e21b5b43a5c;hpb=2c0478d2b33906845b9d910668b12fe3e8f03a7c diff --git a/src/lib/resampler.cc b/src/lib/resampler.cc index 00121384d..322c00c13 100644 --- a/src/lib/resampler.cc +++ b/src/lib/resampler.cc @@ -1,114 +1,199 @@ /* - Copyright (C) 2013 Carl Hetherington + Copyright (C) 2013-2015 Carl Hetherington - This program is free software; you can redistribute it and/or modify + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, + DCP-o-matic is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with DCP-o-matic. If not, see . */ -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 #include "i18n.h" using std::cout; using std::pair; using std::make_pair; +using std::runtime_error; using boost::shared_ptr; +/** @param in Input sampling rate (Hz) + * @param out Output sampling rate (Hz) + * @param channels Number of channels. + */ Resampler::Resampler (int in, int out, int channels) : _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 (SRC_SINC_BEST_QUALITY, _channels, &error); + if (!_src) { + throw runtime_error (String::compose (N_("could not create sample-rate converter (%1)"), error)); + } } Resampler::~Resampler () { - swr_free (&_swr_context); + if (_src) { + src_delete (_src); + } +} + +void +Resampler::set_fast () +{ + src_delete (_src); + _src = 0; + + int error; + _src = src_new (SRC_LINEAR, _channels, &error); + if (!_src) { + throw runtime_error (String::compose (N_("could not create sample-rate converter (%1)"), error)); + } } 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; + float* in_buffer = new float[in_frames * _channels]; + + { + float** p = in->data (); + float* q = in_buffer; + for (int i = 0; i < in_frames; ++i) { + for (int j = 0; j < _channels; ++j) { + *q++ = p[j][in_offset + i]; + } + } + } + + data.data_in = in_buffer; + 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) [processing %2 to %3, %4 channels]"), + src_strerror (r), + in_frames, + max_resampled_frames, + _channels + ) + ); + } + + if (data.output_frames_gen == 0) { + delete[] data.data_in; + delete[] data.data_out; + 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 = new float[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) { + delete[] buffer; + 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); + + delete[] buffer; return out; } + +void +Resampler::reset () +{ + src_reset (_src); +}