GPL boilerplate.
[dcpomatic.git] / src / lib / external_audio_decoder.cc
1 /*
2     Copyright (C) 2012 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 <sndfile.h>
21 #include "external_audio_decoder.h"
22 #include "film.h"
23 #include "exceptions.h"
24
25 using std::vector;
26 using std::string;
27 using std::min;
28 using boost::shared_ptr;
29
30 ExternalAudioDecoder::ExternalAudioDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j)
31         : Decoder (f, o, j)
32         , AudioDecoder (f, o, j)
33 {
34
35 }
36
37 bool
38 ExternalAudioDecoder::pass ()
39 {
40         vector<string> const files = _film->external_audio ();
41
42         int N = 0;
43         for (size_t i = 0; i < files.size(); ++i) {
44                 if (!files[i].empty()) {
45                         N = i + 1;
46                 }
47         }
48
49         if (N == 0) {
50                 return true;
51         }
52
53         bool first = true;
54         sf_count_t frames = 0;
55         
56         vector<SNDFILE*> sndfiles;
57         for (vector<string>::const_iterator i = files.begin(); i != files.end(); ++i) {
58                 if (i->empty ()) {
59                         sndfiles.push_back (0);
60                 } else {
61                         SF_INFO info;
62                         SNDFILE* s = sf_open (i->c_str(), SFM_READ, &info);
63                         if (!s) {
64                                 throw DecodeError ("could not open external audio file for reading");
65                         }
66
67                         if (info.channels != 1) {
68                                 throw DecodeError ("external audio files must be mono");
69                         }
70                         
71                         sndfiles.push_back (s);
72
73                         if (first) {
74                                 /* XXX: nasty magic value */
75                                 AudioStream st ("DVDOMATIC-EXTERNAL", -1, info.samplerate, av_get_default_channel_layout (info.channels));
76                                 _audio_streams.push_back (st);
77                                 _audio_stream = st;
78                                 frames = info.frames;
79                                 first = false;
80                         } else {
81                                 if (info.frames != frames) {
82                                         throw DecodeError ("external audio files have differing lengths");
83                                 }
84                         }
85                 }
86         }
87
88         sf_count_t const block = 65536;
89
90         shared_ptr<AudioBuffers> audio (new AudioBuffers (_audio_stream.get().channels(), block));
91         while (frames > 0) {
92                 sf_count_t const this_time = min (block, frames);
93                 for (size_t i = 0; i < sndfiles.size(); ++i) {
94                         if (!sndfiles[i]) {
95                                 audio->make_silent (i);
96                         } else {
97                                 sf_read_float (sndfiles[i], audio->data(i), block);
98                         }
99                 }
100
101                 Audio (audio);
102                 frames -= this_time;
103         }
104         
105         return true;
106 }