diff options
| author | Carl Hetherington <cth@carlh.net> | 2022-01-23 22:21:29 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2022-04-04 23:09:12 +0200 |
| commit | 0e164ad80f0ceff9d643f3b466690d013c3be19d (patch) | |
| tree | c656a136c12ff3c5d9bf8f7331610162de0c0592 /src/lib | |
| parent | 8eb951b71fa90e54c8da64e54cf5ddf6bf0809cf (diff) | |
Add fade in/out option to the content audio tab (#1026).
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/audio_content.cc | 60 | ||||
| -rw-r--r-- | src/lib/audio_content.h | 24 | ||||
| -rw-r--r-- | src/lib/maths_util.cc | 17 | ||||
| -rw-r--r-- | src/lib/maths_util.h | 15 | ||||
| -rw-r--r-- | src/lib/player.cc | 28 | ||||
| -rw-r--r-- | src/lib/video_content.cc | 1 |
6 files changed, 139 insertions, 6 deletions
diff --git a/src/lib/audio_content.cc b/src/lib/audio_content.cc index 32e713ff2..ad8d7df0f 100644 --- a/src/lib/audio_content.cc +++ b/src/lib/audio_content.cc @@ -53,6 +53,8 @@ using namespace dcpomatic; int const AudioContentProperty::STREAMS = 200; int const AudioContentProperty::GAIN = 201; int const AudioContentProperty::DELAY = 202; +int const AudioContentProperty::FADE_IN = 203; +int const AudioContentProperty::FADE_OUT = 204; AudioContent::AudioContent (Content* parent) @@ -90,6 +92,8 @@ AudioContent::AudioContent (Content* parent, cxml::ConstNodePtr node) { _gain = node->number_child<double> ("AudioGain"); _delay = node->number_child<int> ("AudioDelay"); + _fade_in = ContentTime(node->optional_number_child<ContentTime::Type>("AudioFadeIn").get_value_or(0)); + _fade_out = ContentTime(node->optional_number_child<ContentTime::Type>("AudioFadeOut").get_value_or(0)); /* Backwards compatibility */ auto r = node->optional_number_child<double>("AudioVideoFrameRate"); @@ -127,6 +131,8 @@ AudioContent::as_xml (xmlpp::Node* node) const boost::mutex::scoped_lock lm (_mutex); node->add_child("AudioGain")->add_child_text(raw_convert<string>(_gain)); node->add_child("AudioDelay")->add_child_text(raw_convert<string>(_delay)); + node->add_child("AudioFadeIn")->add_child_text(raw_convert<string>(_fade_in.get())); + node->add_child("AudioFadeOut")->add_child_text(raw_convert<string>(_fade_out.get())); } @@ -420,3 +426,57 @@ AudioContent::modify_trim_start (ContentTime& trim) const /* XXX: we're in trouble if streams have different rates */ trim = trim.round (_streams.front()->frame_rate()); } + + +void +AudioContent::set_fade_in (ContentTime t) +{ + maybe_set (_fade_in, t, AudioContentProperty::FADE_IN); +} + + +void +AudioContent::set_fade_out (ContentTime t) +{ + maybe_set (_fade_out, t, AudioContentProperty::FADE_OUT); +} + + +vector<float> +AudioContent::fade (AudioStreamPtr stream, Frame frame, Frame length, int frame_rate) const +{ + auto const in = fade_in().frames_round(frame_rate); + auto const out = fade_out().frames_round(frame_rate); + + /* Where the start trim ends, at frame_rate */ + auto const trim_start = _parent->trim_start().frames_round(frame_rate); + /* Where the end trim starts within the whole length of the content, at frame_rate */ + auto const trim_end = ContentTime(ContentTime::from_frames(stream->length(), stream->frame_rate()) - _parent->trim_end()).frames_round(frame_rate); + + if ( + (in == 0 || (frame >= (trim_start + in))) && + (out == 0 || ((frame + length) < (trim_end - out))) + ) { + /* This section starts after the fade in and ends before the fade out */ + return {}; + } + + /* Start position relative to the start of the fade in */ + auto in_start = frame - trim_start; + /* Start position relative to the start of the fade out */ + auto out_start = frame - (trim_end - out); + + vector<float> coeffs(length); + for (auto coeff = 0; coeff < length; ++coeff) { + coeffs[coeff] = 1.0; + if (in) { + coeffs[coeff] *= logarithmic_fade_in_curve(static_cast<float>(in_start + coeff) / in); + } + if (out) { + coeffs[coeff] *= logarithmic_fade_out_curve(static_cast<float>(out_start + coeff) / out); + } + } + + return coeffs; +} + diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h index 4dc45f114..ba998a5ad 100644 --- a/src/lib/audio_content.h +++ b/src/lib/audio_content.h @@ -42,6 +42,8 @@ public: static int const STREAMS; static int const GAIN; static int const DELAY; + static int const FADE_IN; + static int const FADE_OUT; }; @@ -76,6 +78,19 @@ public: return _delay; } + dcpomatic::ContentTime fade_in () const { + boost::mutex::scoped_lock lm (_mutex); + return _fade_in; + } + + dcpomatic::ContentTime fade_out () const { + boost::mutex::scoped_lock lm (_mutex); + return _fade_out; + } + + void set_fade_in (dcpomatic::ContentTime time); + void set_fade_out (dcpomatic::ContentTime time); + std::string processing_description (std::shared_ptr<const Film> film) const; std::vector<AudioStreamPtr> streams () const { @@ -93,6 +108,13 @@ public: void modify_position (std::shared_ptr<const Film> film, dcpomatic::DCPTime& pos) const; void modify_trim_start (dcpomatic::ContentTime& pos) const; + /** @param frame frame within the whole (untrimmed) content. + * @param frame_rate The frame rate of the audio (it may have been resampled). + * @return a fade coefficient for @ref length samples starting at an offset @frame within + * the content, or an empty vector if the given section has no fade. + */ + std::vector<float> fade (AudioStreamPtr stream, Frame frame, Frame length, int frame_rate) const; + static std::shared_ptr<AudioContent> from_xml (Content* parent, cxml::ConstNodePtr, int version); private: @@ -101,6 +123,8 @@ private: double _gain = 0; /** Delay to apply to audio (positive moves audio later) in milliseconds */ int _delay = 0; + dcpomatic::ContentTime _fade_in; + dcpomatic::ContentTime _fade_out; std::vector<AudioStreamPtr> _streams; }; diff --git a/src/lib/maths_util.cc b/src/lib/maths_util.cc index 35e3879c4..76681afb6 100644 --- a/src/lib/maths_util.cc +++ b/src/lib/maths_util.cc @@ -19,6 +19,7 @@ */ +#include "maths_util.h" #include <cmath> @@ -35,3 +36,19 @@ linear_to_db (double linear) return 20 * log10(linear); } + +float +logarithmic_fade_in_curve (float t) +{ + auto const c = clamp(t, 0.0f, 1.0f); + return std::exp(2 * (c - 1)) * c; +} + + +float +logarithmic_fade_out_curve (float t) +{ + auto const c = clamp(t, 0.0f, 1.0f); + return std::exp(-2 * c) * (1 - c); +} + diff --git a/src/lib/maths_util.h b/src/lib/maths_util.h index 8adefbbc4..24c4b547f 100644 --- a/src/lib/maths_util.h +++ b/src/lib/maths_util.h @@ -30,6 +30,21 @@ extern double db_to_linear (double db); extern double linear_to_db (double linear); +/** @return linear gain according to a logarithmic curve, for fading in. + * t < 0: linear gain of 0 + * 0 >= t >= 1: logarithmic fade in curve + * t > 1: linear gain of 1 + */ +extern float logarithmic_fade_in_curve (float t); + + +/** @return linear gain according to a logarithmic curve, for fading out. + * t > 1: linear gain of 0 + * 0 >= t >= 1: logarithmic fade out curve + * t < 0: linear gain of 1 + */ +extern float logarithmic_fade_out_curve (float t); + template <class T> T clamp (T val, T minimum, T maximum) diff --git a/src/lib/player.cc b/src/lib/player.cc index abd051bed..12a53bc63 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -1049,12 +1049,28 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a DCPOMATIC_ASSERT (content_audio.audio->frames() > 0); - /* Gain */ - - if (content->gain() != 0) { - auto gain = make_shared<AudioBuffers>(content_audio.audio); - gain->apply_gain (content->gain()); - content_audio.audio = gain; + /* Gain and fade */ + + auto const fade_coeffs = content->fade (stream, content_audio.frame, content_audio.audio->frames(), rfr); + if (content->gain() != 0 || !fade_coeffs.empty()) { + auto gain_buffers = make_shared<AudioBuffers>(content_audio.audio); + if (!fade_coeffs.empty()) { + /* Apply both fade and gain */ + DCPOMATIC_ASSERT (fade_coeffs.size() == static_cast<size_t>(gain_buffers->frames())); + auto const channels = gain_buffers->channels(); + auto const frames = fade_coeffs.size(); + auto data = gain_buffers->data(); + auto const gain = db_to_linear (content->gain()); + for (auto channel = 0; channel < channels; ++channel) { + for (auto frame = 0U; frame < frames; ++frame) { + data[channel][frame] *= gain * fade_coeffs[frame]; + } + } + } else { + /* Just apply gain */ + gain_buffers->apply_gain (content->gain()); + } + content_audio.audio = gain_buffers; } /* Remap */ diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc index 9ff35ffdf..853204575 100644 --- a/src/lib/video_content.cc +++ b/src/lib/video_content.cc @@ -173,6 +173,7 @@ VideoContent::VideoContent (Content* parent, cxml::ConstNodePtr node, int versio _yuv = node->optional_bool_child("YUV").get_value_or (true); if (version >= 32) { + /* These should be VideoFadeIn and VideoFadeOut but we'll leave them like this until 2.18.x */ _fade_in = node->number_child<Frame> ("FadeIn"); _fade_out = node->number_child<Frame> ("FadeOut"); } else { |
