Hand-apply d849d411cff28ef5453085791d0b4d7cd73bd070 from master; replace all assert...
[dcpomatic.git] / src / lib / audio_content.cc
1 /*
2     Copyright (C) 2013-2014 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 "audio_content.h"
21 #include "analyse_audio_job.h"
22 #include "job_manager.h"
23 #include "film.h"
24 #include "exceptions.h"
25 #include "config.h"
26 #include "frame_rate_change.h"
27 #include "audio_processor.h"
28 #include <libcxml/cxml.h>
29 #include <dcp/raw_convert.h>
30
31 #include "i18n.h"
32
33 using std::string;
34 using std::cout;
35 using std::vector;
36 using std::stringstream;
37 using std::fixed;
38 using std::setprecision;
39 using boost::shared_ptr;
40 using boost::dynamic_pointer_cast;
41 using dcp::raw_convert;
42
43 int const AudioContentProperty::AUDIO_CHANNELS = 200;
44 int const AudioContentProperty::AUDIO_LENGTH = 201;
45 int const AudioContentProperty::AUDIO_FRAME_RATE = 202;
46 int const AudioContentProperty::AUDIO_GAIN = 203;
47 int const AudioContentProperty::AUDIO_DELAY = 204;
48 int const AudioContentProperty::AUDIO_MAPPING = 205;
49 int const AudioContentProperty::AUDIO_PROCESSOR = 206;
50
51 AudioContent::AudioContent (shared_ptr<const Film> f)
52         : Content (f)
53         , _audio_gain (0)
54         , _audio_delay (Config::instance()->default_audio_delay ())
55         , _audio_processor (0)
56 {
57
58 }
59
60 AudioContent::AudioContent (shared_ptr<const Film> f, DCPTime s)
61         : Content (f, s)
62         , _audio_gain (0)
63         , _audio_delay (Config::instance()->default_audio_delay ())
64         , _audio_processor (0)
65 {
66
67 }
68
69 AudioContent::AudioContent (shared_ptr<const Film> f, boost::filesystem::path p)
70         : Content (f, p)
71         , _audio_gain (0)
72         , _audio_delay (Config::instance()->default_audio_delay ())
73         , _audio_processor (0)
74 {
75
76 }
77
78 AudioContent::AudioContent (shared_ptr<const Film> f, cxml::ConstNodePtr node)
79         : Content (f, node)
80         , _audio_processor (0)
81 {
82         _audio_gain = node->number_child<float> ("AudioGain");
83         _audio_delay = node->number_child<int> ("AudioDelay");
84         if (node->optional_string_child ("AudioProcessor")) {
85                 _audio_processor = AudioProcessor::from_id (node->string_child ("AudioProcessor"));
86         }
87 }
88
89 AudioContent::AudioContent (shared_ptr<const Film> f, vector<shared_ptr<Content> > c)
90         : Content (f, c)
91 {
92         shared_ptr<AudioContent> ref = dynamic_pointer_cast<AudioContent> (c[0]);
93         DCPOMATIC_ASSERT (ref);
94         
95         for (size_t i = 0; i < c.size(); ++i) {
96                 shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (c[i]);
97
98                 if (ac->audio_gain() != ref->audio_gain()) {
99                         throw JoinError (_("Content to be joined must have the same audio gain."));
100                 }
101
102                 if (ac->audio_delay() != ref->audio_delay()) {
103                         throw JoinError (_("Content to be joined must have the same audio delay."));
104                 }
105         }
106
107         _audio_gain = ref->audio_gain ();
108         _audio_delay = ref->audio_delay ();
109         _audio_processor = ref->audio_processor ();
110 }
111
112 void
113 AudioContent::as_xml (xmlpp::Node* node) const
114 {
115         boost::mutex::scoped_lock lm (_mutex);
116         node->add_child("AudioGain")->add_child_text (raw_convert<string> (_audio_gain));
117         node->add_child("AudioDelay")->add_child_text (raw_convert<string> (_audio_delay));
118         if (_audio_processor) {
119                 node->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
120         }
121 }
122
123
124 void
125 AudioContent::set_audio_gain (double g)
126 {
127         {
128                 boost::mutex::scoped_lock lm (_mutex);
129                 _audio_gain = g;
130         }
131         
132         signal_changed (AudioContentProperty::AUDIO_GAIN);
133 }
134
135 void
136 AudioContent::set_audio_delay (int d)
137 {
138         {
139                 boost::mutex::scoped_lock lm (_mutex);
140                 _audio_delay = d;
141         }
142         
143         signal_changed (AudioContentProperty::AUDIO_DELAY);
144 }
145
146 void
147 AudioContent::set_audio_processor (AudioProcessor const * p)
148 {
149         {
150                 boost::mutex::scoped_lock lm (_mutex);
151                 _audio_processor = p;
152         }
153
154         /* The channel count might have changed, so reset the mapping */
155         AudioMapping m (processed_audio_channels ());
156         m.make_default ();
157         set_audio_mapping (m);
158
159         signal_changed (AudioContentProperty::AUDIO_PROCESSOR);
160 }
161
162 boost::signals2::connection
163 AudioContent::analyse_audio (boost::function<void()> finished)
164 {
165         shared_ptr<const Film> film = _film.lock ();
166         DCPOMATIC_ASSERT (film);
167         
168         shared_ptr<AnalyseAudioJob> job (new AnalyseAudioJob (film, dynamic_pointer_cast<AudioContent> (shared_from_this())));
169         boost::signals2::connection c = job->Finished.connect (finished);
170         JobManager::instance()->add (job);
171
172         return c;
173 }
174
175 boost::filesystem::path
176 AudioContent::audio_analysis_path () const
177 {
178         shared_ptr<const Film> film = _film.lock ();
179         if (!film) {
180                 return boost::filesystem::path ();
181         }
182
183         boost::filesystem::path p = film->audio_analysis_dir ();
184         p /= digest().get_value_or ("X") + "_" + audio_mapping().digest();
185         return p;
186 }
187
188 string
189 AudioContent::technical_summary () const
190 {
191         return String::compose (
192                 "audio: channels %1, length %2, content rate %3, resampled rate %4",
193                 audio_channels(),
194                 audio_length().seconds(),
195                 audio_frame_rate(),
196                 resampled_audio_frame_rate()
197                 );
198 }
199
200 void
201 AudioContent::set_audio_mapping (AudioMapping)
202 {
203         signal_changed (AudioContentProperty::AUDIO_MAPPING);
204 }
205
206 /** @return the frame rate that this content should be resampled to in order
207  *  that it is in sync with the active video content at its start time.
208  */
209 int
210 AudioContent::resampled_audio_frame_rate () const
211 {
212         shared_ptr<const Film> film = _film.lock ();
213         DCPOMATIC_ASSERT (film);
214         
215         /* Resample to a DCI-approved sample rate */
216         double t = dcp_audio_frame_rate (audio_frame_rate ());
217
218         FrameRateChange frc = film->active_frame_rate_change (position ());
219
220         /* Compensate if the DCP is being run at a different frame rate
221            to the source; that is, if the video is run such that it will
222            look different in the DCP compared to the source (slower or faster).
223         */
224
225         if (frc.change_speed) {
226                 t /= frc.speed_up;
227         }
228
229         return rint (t);
230 }
231
232 int
233 AudioContent::processed_audio_channels () const
234 {
235         if (!audio_processor ()) {
236                 return audio_channels ();
237         }
238
239         return audio_processor()->out_channels (audio_channels ());
240 }
241
242 string
243 AudioContent::processing_description () const
244 {
245         stringstream d;
246         
247         if (audio_frame_rate() != resampled_audio_frame_rate ()) {
248                 stringstream from;
249                 from << fixed << setprecision(3) << (audio_frame_rate() / 1000.0);
250                 stringstream to;
251                 to << fixed << setprecision(3) << (resampled_audio_frame_rate() / 1000.0);
252
253                 d << String::compose (_("Audio will be resampled from %1kHz to %2kHz."), from.str(), to.str());
254         } else {
255                 d << _("Audio will not be resampled.");
256         }
257
258         return d.str ();
259 }
260