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