Better error checking in Resampler.
[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 extern "C" {
21 #include "libavutil/channel_layout.h"
22 #include "libavutil/opt.h"
23 }
24 #include "resampler.h"
25 #include "audio_buffers.h"
26 #include "exceptions.h"
27 #include "compose.hpp"
28
29 #include "i18n.h"
30
31 using std::cout;
32 using std::pair;
33 using std::make_pair;
34 using boost::shared_ptr;
35
36 Resampler::Resampler (int in, int out, int channels)
37         : _in_rate (in)
38         , _out_rate (out)
39         , _channels (channels)
40 {
41         _swr_context = swr_alloc ();
42         if (!_swr_context) {
43                 throw StringError (N_("could not allocate resampler contexct"));
44         }
45
46         /* Sample formats */
47         av_opt_set_int (_swr_context, "isf", AV_SAMPLE_FMT_FLTP, 0);
48         av_opt_set_int (_swr_context, "osf", AV_SAMPLE_FMT_FLTP, 0);
49
50         /* Channel counts */
51         av_opt_set_int (_swr_context, "ich", _channels, 0);
52         av_opt_set_int (_swr_context, "och", _channels, 0);
53
54         /* Sample rates */
55         av_opt_set_int (_swr_context, "isr", _in_rate, 0);
56         av_opt_set_int (_swr_context, "osr", _out_rate, 0);
57
58         av_opt_set (_swr_context, "resampler", "soxr", 0);
59
60         int const r = swr_init (_swr_context);
61         if (r) {
62                 char buf[256];
63                 av_strerror (r, buf, sizeof(buf));
64                 throw StringError (String::compose (N_ ("could not initialise sample-rate converter (%1)"), r));
65         }
66 }
67
68 Resampler::~Resampler ()
69 {
70         swr_free (&_swr_context);
71 }
72
73 shared_ptr<const AudioBuffers>
74 Resampler::run (shared_ptr<const AudioBuffers> in)
75 {
76         /* Compute the resampled frames count and add 32 for luck */
77         int const max_resampled_frames = ceil ((double) in->frames() * _out_rate / _in_rate) + 32;
78         shared_ptr<AudioBuffers> resampled (new AudioBuffers (_channels, max_resampled_frames));
79
80         int const resampled_frames = swr_convert (
81                 _swr_context, (uint8_t **) resampled->data(), max_resampled_frames, (uint8_t const **) in->data(), in->frames()
82                 );
83
84         if (resampled_frames < 0) {
85                 char buf[256];
86                 av_strerror (resampled_frames, buf, sizeof(buf));
87                 throw EncodeError (String::compose (_("could not run sample-rate converter for %1 samples (%2) (%3)"), in->frames(), resampled_frames, buf));
88         }
89
90         resampled->set_frames (resampled_frames);
91         return resampled;
92 }
93
94 shared_ptr<const AudioBuffers>
95 Resampler::flush ()
96 {
97         shared_ptr<AudioBuffers> out (new AudioBuffers (_channels, 0));
98         int out_offset = 0;
99         int64_t const pass_size = 256;
100         shared_ptr<AudioBuffers> pass (new AudioBuffers (_channels, 256));
101
102         while (true) {
103                 int const frames = swr_convert (_swr_context, (uint8_t **) pass->data(), pass_size, 0, 0);
104
105                 if (frames < 0) {
106                         throw EncodeError (_("could not run sample-rate converter"));
107                 }
108
109                 if (frames == 0) {
110                         break;
111                 }
112
113                 out->ensure_size (out_offset + frames);
114                 out->copy_from (pass.get(), frames, 0, out_offset);
115                 out_offset += frames;
116                 out->set_frames (out_offset);
117         }
118
119         return out;
120 }