7083de714dcf411946c65598cdaf07d23e52ee32
[dcpomatic.git] / src / lib / audio_content.cc
1 /*
2     Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "audio_content.h"
23 #include "compose.hpp"
24 #include "config.h"
25 #include "constants.h"
26 #include "exceptions.h"
27 #include "film.h"
28 #include "frame_rate_change.h"
29 #include "maths_util.h"
30 #include "video_content.h"
31 #include <dcp/raw_convert.h>
32 #include <libcxml/cxml.h>
33 #include <libxml++/libxml++.h>
34 #include <iostream>
35
36 #include "i18n.h"
37
38
39 using std::cout;
40 using std::dynamic_pointer_cast;
41 using std::fixed;
42 using std::list;
43 using std::make_shared;
44 using std::pair;
45 using std::setprecision;
46 using std::shared_ptr;
47 using std::string;
48 using std::vector;
49 using boost::optional;
50 using dcp::raw_convert;
51 using namespace dcpomatic;
52
53
54 /** Something stream-related has changed */
55 int const AudioContentProperty::STREAMS = 200;
56 int const AudioContentProperty::GAIN = 201;
57 int const AudioContentProperty::DELAY = 202;
58 int const AudioContentProperty::FADE_IN = 203;
59 int const AudioContentProperty::FADE_OUT = 204;
60 int const AudioContentProperty::USE_SAME_FADES_AS_VIDEO = 205;
61
62
63 AudioContent::AudioContent (Content* parent)
64         : ContentPart (parent)
65         , _delay (Config::instance()->default_audio_delay())
66 {
67
68 }
69
70
71 shared_ptr<AudioContent>
72 AudioContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version)
73 {
74         if (version < 34) {
75                 /* With old metadata FFmpeg content has the audio-related tags even with no
76                    audio streams, so check for that.
77                 */
78                 if (node->string_child("Type") == "FFmpeg" && node->node_children("AudioStream").empty()) {
79                         return {};
80                 }
81
82                 /* Otherwise we can drop through to the newer logic */
83         }
84
85         if (!node->optional_number_child<double> ("AudioGain")) {
86                 return {};
87         }
88
89         return make_shared<AudioContent>(parent, node);
90 }
91
92
93 AudioContent::AudioContent (Content* parent, cxml::ConstNodePtr node)
94         : ContentPart (parent)
95 {
96         _gain = node->number_child<double> ("AudioGain");
97         _delay = node->number_child<int> ("AudioDelay");
98         _fade_in = ContentTime(node->optional_number_child<ContentTime::Type>("AudioFadeIn").get_value_or(0));
99         _fade_out = ContentTime(node->optional_number_child<ContentTime::Type>("AudioFadeOut").get_value_or(0));
100         _use_same_fades_as_video = node->optional_bool_child("AudioUseSameFadesAsVideo").get_value_or(false);
101 }
102
103
104 AudioContent::AudioContent (Content* parent, vector<shared_ptr<Content>> c)
105         : ContentPart (parent)
106 {
107         auto ref = c[0]->audio;
108         DCPOMATIC_ASSERT (ref);
109
110         for (size_t i = 1; i < c.size(); ++i) {
111                 if (c[i]->audio->gain() != ref->gain()) {
112                         throw JoinError (_("Content to be joined must have the same audio gain."));
113                 }
114
115                 if (c[i]->audio->delay() != ref->delay()) {
116                         throw JoinError (_("Content to be joined must have the same audio delay."));
117                 }
118         }
119
120         _gain = ref->gain ();
121         _delay = ref->delay ();
122         _streams = ref->streams ();
123 }
124
125
126 void
127 AudioContent::as_xml (xmlpp::Node* node) const
128 {
129         boost::mutex::scoped_lock lm (_mutex);
130         node->add_child("AudioGain")->add_child_text(raw_convert<string>(_gain));
131         node->add_child("AudioDelay")->add_child_text(raw_convert<string>(_delay));
132         node->add_child("AudioFadeIn")->add_child_text(raw_convert<string>(_fade_in.get()));
133         node->add_child("AudioFadeOut")->add_child_text(raw_convert<string>(_fade_out.get()));
134         node->add_child("AudioUseSameFadesAsVideo")->add_child_text(_use_same_fades_as_video ? "1" : "0");
135 }
136
137
138 void
139 AudioContent::set_gain (double g)
140 {
141         maybe_set (_gain, g, AudioContentProperty::GAIN);
142 }
143
144
145 void
146 AudioContent::set_delay (int d)
147 {
148         maybe_set (_delay, d, AudioContentProperty::DELAY);
149 }
150
151
152 string
153 AudioContent::technical_summary () const
154 {
155         string s = "audio: ";
156         for (auto i: streams()) {
157                 s += String::compose ("stream channels %1 rate %2 ", i->channels(), i->frame_rate());
158         }
159
160         return s;
161 }
162
163
164 void
165 AudioContent::set_mapping (AudioMapping mapping)
166 {
167         ContentChangeSignaller cc (_parent, AudioContentProperty::STREAMS);
168
169         int c = 0;
170         for (auto i: streams()) {
171                 AudioMapping stream_mapping (i->channels(), MAX_DCP_AUDIO_CHANNELS);
172                 for (int j = 0; j < i->channels(); ++j) {
173                         for (int k = 0; k < MAX_DCP_AUDIO_CHANNELS; ++k) {
174                                 stream_mapping.set (j, k, mapping.get(c, k));
175                         }
176                         ++c;
177                 }
178                 i->set_mapping (stream_mapping);
179         }
180 }
181
182
183 AudioMapping
184 AudioContent::mapping () const
185 {
186         int channels = 0;
187         for (auto i: streams()) {
188                 channels += i->channels ();
189         }
190
191         AudioMapping merged (channels, MAX_DCP_AUDIO_CHANNELS);
192         merged.make_zero ();
193
194         int c = 0;
195         int s = 0;
196         for (auto i: streams()) {
197                 auto mapping = i->mapping ();
198                 for (int j = 0; j < mapping.input_channels(); ++j) {
199                         for (int k = 0; k < MAX_DCP_AUDIO_CHANNELS; ++k) {
200                                 if (k < mapping.output_channels()) {
201                                         merged.set (c, k, mapping.get(j, k));
202                                 }
203                         }
204                         ++c;
205                 }
206                 ++s;
207         }
208
209         return merged;
210 }
211
212
213 /** @return the frame rate that this content should be resampled to in order
214  *  that it is in sync with the active video content at its start time.
215  */
216 int
217 AudioContent::resampled_frame_rate (shared_ptr<const Film> film) const
218 {
219         double t = film->audio_frame_rate ();
220
221         FrameRateChange frc (film, _parent);
222
223         /* Compensate if the DCP is being run at a different frame rate
224            to the source; that is, if the video is run such that it will
225            look different in the DCP compared to the source (slower or faster).
226         */
227
228         if (frc.change_speed) {
229                 t /= frc.speed_up;
230         }
231
232         return lrint (t);
233 }
234
235 string
236 AudioContent::processing_description (shared_ptr<const Film> film) const
237 {
238         if (streams().empty()) {
239                 return "";
240         }
241
242         /* Possible answers are:
243            1. all audio will be resampled from x to y.
244            2. all audio will be resampled to y (from a variety of rates)
245            3. some audio will be resampled to y (from a variety of rates)
246            4. nothing will be resampled.
247         */
248
249         bool not_resampled = false;
250         bool resampled = false;
251         bool same = true;
252
253         optional<int> common_frame_rate;
254         for (auto i: streams()) {
255                 if (i->frame_rate() != resampled_frame_rate(film)) {
256                         resampled = true;
257                 } else {
258                         not_resampled = true;
259                 }
260
261                 if (common_frame_rate && common_frame_rate != i->frame_rate ()) {
262                         same = false;
263                 }
264                 common_frame_rate = i->frame_rate ();
265         }
266
267         if (not_resampled && !resampled) {
268                 return _("Audio will not be resampled");
269         }
270
271         if (not_resampled && resampled) {
272                 return String::compose (_("Some audio will be resampled to %1Hz"), resampled_frame_rate(film));
273         }
274
275         if (!not_resampled && resampled) {
276                 if (same) {
277                         return String::compose (_("Audio will be resampled from %1Hz to %2Hz"), common_frame_rate.get(), resampled_frame_rate(film));
278                 } else {
279                         return String::compose (_("Audio will be resampled to %1Hz"), resampled_frame_rate(film));
280                 }
281         }
282
283         return "";
284 }
285
286
287 /** @return User-visible names of each of our audio channels */
288 vector<NamedChannel>
289 AudioContent::channel_names () const
290 {
291         vector<NamedChannel> n;
292
293         int index = 0;
294         int stream = 1;
295         for (auto i: streams()) {
296                 for (int j = 0; j < i->channels(); ++j) {
297                         n.push_back (NamedChannel(String::compose ("%1:%2", stream, j + 1), index++));
298                 }
299                 ++stream;
300         }
301
302         return n;
303 }
304
305
306 void
307 AudioContent::add_properties (shared_ptr<const Film> film, list<UserProperty>& p) const
308 {
309         shared_ptr<const AudioStream> stream;
310         if (streams().size() == 1) {
311                 stream = streams().front();
312         }
313
314         if (stream) {
315                 p.push_back (UserProperty(UserProperty::AUDIO, _("Channels"), stream->channels()));
316                 p.push_back (UserProperty(UserProperty::AUDIO, _("Content sample rate"), stream->frame_rate(), _("Hz")));
317                 if (auto bits = stream->bit_depth()) {
318                         p.push_back(UserProperty(UserProperty::AUDIO, _("Content bit depth"), *bits, _("bits")));
319                 }
320         }
321
322         FrameRateChange const frc (_parent->active_video_frame_rate(film), film->video_frame_rate());
323         ContentTime const c (_parent->full_length(film), frc);
324
325         p.push_back (
326                 UserProperty (UserProperty::LENGTH, _("Full length in video frames at content rate"), c.frames_round(frc.source))
327                 );
328
329         if (stream) {
330                 p.push_back (
331                         UserProperty (
332                                 UserProperty::LENGTH,
333                                 _("Full length in audio samples at content rate"),
334                                 c.frames_round (stream->frame_rate ())
335                                 )
336                         );
337         }
338
339         p.push_back (UserProperty(UserProperty::AUDIO, _("DCP sample rate"), resampled_frame_rate(film), _("Hz")));
340         p.push_back (UserProperty(UserProperty::LENGTH, _("Full length in video frames at DCP rate"), c.frames_round (frc.dcp)));
341
342         if (stream) {
343                 p.push_back (
344                         UserProperty (
345                                 UserProperty::LENGTH,
346                                 _("Full length in audio samples at DCP rate"),
347                                 c.frames_round(resampled_frame_rate(film))
348                                 )
349                         );
350         }
351 }
352
353
354 void
355 AudioContent::set_streams (vector<AudioStreamPtr> streams)
356 {
357         ContentChangeSignaller cc (_parent, AudioContentProperty::STREAMS);
358
359         {
360                 boost::mutex::scoped_lock lm (_mutex);
361                 _streams = streams;
362         }
363 }
364
365
366 AudioStreamPtr
367 AudioContent::stream () const
368 {
369         boost::mutex::scoped_lock lm (_mutex);
370         DCPOMATIC_ASSERT (_streams.size() == 1);
371         return _streams.front ();
372 }
373
374
375 void
376 AudioContent::add_stream (AudioStreamPtr stream)
377 {
378         ContentChangeSignaller cc (_parent, AudioContentProperty::STREAMS);
379
380         {
381                 boost::mutex::scoped_lock lm (_mutex);
382                 _streams.push_back (stream);
383         }
384 }
385
386
387 void
388 AudioContent::set_stream (AudioStreamPtr stream)
389 {
390         ContentChangeSignaller cc (_parent, AudioContentProperty::STREAMS);
391
392         {
393                 boost::mutex::scoped_lock lm (_mutex);
394                 _streams.clear ();
395                 _streams.push_back (stream);
396         }
397 }
398
399
400 void
401 AudioContent::take_settings_from (shared_ptr<const AudioContent> c)
402 {
403         set_gain (c->_gain);
404         set_delay (c->_delay);
405         set_fade_in (c->fade_in());
406         set_fade_out (c->fade_out());
407
408         size_t i = 0;
409         size_t j = 0;
410
411         while (i < _streams.size() && j < c->_streams.size()) {
412                 auto mapping = _streams[i]->mapping();
413                 mapping.take_from(c->_streams[j]->mapping());
414                 _streams[i]->set_mapping(mapping);
415                 ++i;
416                 ++j;
417         }
418 }
419
420
421 void
422 AudioContent::modify_position (shared_ptr<const Film> film, DCPTime& pos) const
423 {
424         pos = pos.round (film->audio_frame_rate());
425 }
426
427
428 void
429 AudioContent::modify_trim_start(shared_ptr<const Film> film, ContentTime& trim) const
430 {
431         /* When this trim is used it the audio will have been resampled, and using the
432          * DCP rate here reduces the chance of rounding errors causing audio glitches
433          * due to errors in placement of audio frames (#2373).
434          */
435         trim = trim.round(film ? film->audio_frame_rate() : 48000);
436 }
437
438
439 ContentTime
440 AudioContent::fade_in () const
441 {
442         boost::mutex::scoped_lock lm (_mutex);
443         if (_use_same_fades_as_video && _parent->video) {
444                 return dcpomatic::ContentTime::from_frames(_parent->video->fade_in(), _parent->video_frame_rate().get_value_or(24));
445         }
446
447         return _fade_in;
448 }
449
450
451 ContentTime
452 AudioContent::fade_out () const
453 {
454         boost::mutex::scoped_lock lm (_mutex);
455         if (_use_same_fades_as_video && _parent->video) {
456                 return dcpomatic::ContentTime::from_frames(_parent->video->fade_out(), _parent->video_frame_rate().get_value_or(24));
457         }
458
459         return _fade_out;
460 }
461
462
463 void
464 AudioContent::set_fade_in (ContentTime t)
465 {
466         maybe_set (_fade_in, t, AudioContentProperty::FADE_IN);
467 }
468
469
470 void
471 AudioContent::set_fade_out (ContentTime t)
472 {
473         maybe_set (_fade_out, t, AudioContentProperty::FADE_OUT);
474 }
475
476
477 void
478 AudioContent::set_use_same_fades_as_video (bool s)
479 {
480         maybe_set (_use_same_fades_as_video, s, AudioContentProperty::USE_SAME_FADES_AS_VIDEO);
481 }
482
483
484 vector<float>
485 AudioContent::fade (AudioStreamPtr stream, Frame frame, Frame length, int frame_rate) const
486 {
487         auto const in = fade_in().frames_round(frame_rate);
488         auto const out = fade_out().frames_round(frame_rate);
489
490         /* Where the start trim ends, at frame_rate */
491         auto const trim_start = _parent->trim_start().frames_round(frame_rate);
492         /* Where the end trim starts within the whole length of the content, at frame_rate */
493         auto const trim_end = ContentTime(ContentTime::from_frames(stream->length(), stream->frame_rate()) - _parent->trim_end()).frames_round(frame_rate);
494
495         if (
496                 (in == 0  || (frame >= (trim_start + in))) &&
497                 (out == 0 || ((frame + length) < (trim_end - out)))
498            ) {
499                 /* This section starts after the fade in and ends before the fade out */
500                 return {};
501         }
502
503         /* Start position relative to the start of the fade in */
504         auto in_start = frame - trim_start;
505         /* Start position relative to the start of the fade out */
506         auto out_start = frame - (trim_end - out);
507
508         vector<float> coeffs(length);
509         for (auto coeff = 0; coeff < length; ++coeff) {
510                 coeffs[coeff] = 1.0;
511                 if (in) {
512                         coeffs[coeff] *= logarithmic_fade_in_curve(static_cast<float>(in_start + coeff) / in);
513                 }
514                 if (out) {
515                         coeffs[coeff] *= logarithmic_fade_out_curve(static_cast<float>(out_start + coeff) / out);
516                 }
517         }
518
519         return coeffs;
520 }
521