Use libsamplerate for resampling instead of FFmpeg / libsoxr.
[dcpomatic.git] / src / lib / resampler.cc
1 /*
2     Copyright (C) 2013-2015 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include "resampler.h"
21 #include "audio_buffers.h"
22 #include "exceptions.h"
23 #include "compose.hpp"
24 #include "dcpomatic_assert.h"
25 #include <samplerate.h>
26
27 #include "i18n.h"
28
29 using std::cout;
30 using std::pair;
31 using std::make_pair;
32 using boost::shared_ptr;
33
34 Resampler::Resampler (int in, int out, int channels)
35         : _in_rate (in)
36         , _out_rate (out)
37         , _channels (channels)
38 {
39         int error;
40         _src = src_new (SRC_SINC_BEST_QUALITY, _channels, &error);
41         if (!_src) {
42                 throw StringError (String::compose (N_("could not create sample-rate converter (%1)"), error));
43         }
44 }
45
46 Resampler::~Resampler ()
47 {
48         src_delete (_src);
49 }
50
51 shared_ptr<const AudioBuffers>
52 Resampler::run (shared_ptr<const AudioBuffers> in)
53 {
54         int in_frames = in->frames ();
55         int in_offset = 0;
56         int out_offset = 0;
57         shared_ptr<AudioBuffers> resampled (new AudioBuffers (_channels, 0));
58
59         while (in_frames > 0) {
60
61                 /* Compute the resampled frames count and add 32 for luck */
62                 int const max_resampled_frames = ceil ((double) in_frames * _out_rate / _in_rate) + 32;
63
64                 SRC_DATA data;
65                 data.data_in = new float[in_frames * _channels];
66
67                 {
68                         float** p = in->data ();
69                         float* q = data.data_in;
70                         for (int i = 0; i < in_frames; ++i) {
71                                 for (int j = 0; j < _channels; ++j) {
72                                         *q++ = p[j][in_offset + i];
73                                 }
74                         }
75                 }
76
77                 data.input_frames = in_frames;
78
79                 data.data_out = new float[max_resampled_frames * _channels];
80                 data.output_frames = max_resampled_frames;
81
82                 data.end_of_input = 0;
83                 data.src_ratio = double (_out_rate) / _in_rate;
84
85                 int const r = src_process (_src, &data);
86                 if (r) {
87                         delete[] data.data_in;
88                         delete[] data.data_out;
89                         throw EncodeError (String::compose (N_("could not run sample-rate converter (%1)"), src_strerror (r)));
90                 }
91
92                 if (data.output_frames_gen == 0) {
93                         break;
94                 }
95
96                 resampled->ensure_size (out_offset + data.output_frames_gen);
97                 resampled->set_frames (out_offset + data.output_frames_gen);
98
99                 {
100                         float* p = data.data_out;
101                         float** q = resampled->data ();
102                         for (int i = 0; i < data.output_frames_gen; ++i) {
103                                 for (int j = 0; j < _channels; ++j) {
104                                         q[j][out_offset + i] = *p++;
105                                 }
106                         }
107                 }
108
109                 in_frames -= data.input_frames_used;
110                 in_offset += data.input_frames_used;
111                 out_offset += data.output_frames_gen;
112
113                 delete[] data.data_in;
114                 delete[] data.data_out;
115         }
116
117         return resampled;
118 }
119
120 shared_ptr<const AudioBuffers>
121 Resampler::flush ()
122 {
123         shared_ptr<AudioBuffers> out (new AudioBuffers (_channels, 0));
124         int out_offset = 0;
125         int64_t const output_size = 65536;
126
127         float dummy[1];
128         float buffer[output_size];
129
130         SRC_DATA data;
131         data.data_in = dummy;
132         data.input_frames = 0;
133         data.data_out = buffer;
134         data.output_frames = output_size;
135         data.end_of_input = 1;
136         data.src_ratio = double (_out_rate) / _in_rate;
137
138         int const r = src_process (_src, &data);
139         if (r) {
140                 throw EncodeError (String::compose (N_("could not run sample-rate converter (%1)"), src_strerror (r)));
141         }
142
143         out->ensure_size (out_offset + data.output_frames_gen);
144
145         float* p = data.data_out;
146         float** q = out->data ();
147         for (int i = 0; i < data.output_frames_gen; ++i) {
148                 for (int j = 0; j < _channels; ++j) {
149                         q[j][out_offset + i] = *p++;
150                 }
151         }
152
153         out_offset += data.output_frames_gen;
154         out->set_frames (out_offset);
155
156         return out;
157 }