Fix audio fading.
authorCarl Hetherington <cth@carlh.net>
Sun, 13 Feb 2022 23:06:49 +0000 (00:06 +0100)
committerCarl Hetherington <cth@carlh.net>
Sun, 13 Feb 2022 23:06:49 +0000 (00:06 +0100)
The ::fade() method needs to know the stream it is working with,
rather than assuming its content has only one stream.

src/lib/audio_content.cc
src/lib/audio_content.h
src/lib/player.cc
test/audio_content_test.cc

index 101ed8f1ed6974603ff1418e38d8a30a76ee724b..ad8d7df0fdbb731a0a9844a00d70808d53066e87 100644 (file)
@@ -443,7 +443,7 @@ AudioContent::set_fade_out (ContentTime t)
 
 
 vector<float>
-AudioContent::fade (Frame frame, Frame length, int frame_rate) const
+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);
@@ -451,7 +451,7 @@ AudioContent::fade (Frame frame, Frame length, int frame_rate) const
        /* 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);
+       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))) &&
index d736fc38f6f9f074a8662fa6618e8d29b54255ee..064cc42f052013b6d51967b55c157027959a691c 100644 (file)
@@ -114,7 +114,7 @@ public:
         *  @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 (Frame frame, Frame length, int frame_rate) const;
+       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);
 
index 91daeed32d13c670efa180b7c11232981b384365..4a4093323ae1efcaf205eb7902ecd8d3a36913cb 100644 (file)
@@ -1051,7 +1051,7 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
 
        /* Gain and fade */
 
-       auto const fade_coeffs = content->fade (content_audio.frame, content_audio.audio->frames(), rfr);
+       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()) {
index 1f586a59a18c6b621a24cb80222f59e606230067..92bb4bcf052504151958d94ee89c8e9b3fe8c5d7 100644 (file)
@@ -31,7 +31,7 @@ BOOST_AUTO_TEST_CASE (audio_content_fade_empty_region)
        auto content = content_factory("test/data/impulse_train.wav").front();
        auto film = new_test_film2("audio_content_fade_empty_region", { content });
 
-       BOOST_CHECK (content->audio->fade(0, 0, 48000).empty());
+       BOOST_CHECK (content->audio->fade(content->audio->stream(), 0, 0, 48000).empty());
 }
 
 
@@ -40,9 +40,11 @@ BOOST_AUTO_TEST_CASE (audio_content_fade_no_fade)
        auto content = content_factory("test/data/impulse_train.wav").front();
        auto film = new_test_film2("audio_content_fade_no_fade", { content });
 
-       BOOST_CHECK (content->audio->fade(0, 2000, 48000).empty());
-       BOOST_CHECK (content->audio->fade(9999, 451, 48000).empty());
-       BOOST_CHECK (content->audio->fade(content->audio->stream()->length() + 100, 8000, 48000).empty());
+       auto const stream = content->audio->stream();
+
+       BOOST_CHECK (content->audio->fade(stream, 0, 2000, 48000).empty());
+       BOOST_CHECK (content->audio->fade(stream, 9999, 451, 48000).empty());
+       BOOST_CHECK (content->audio->fade(stream, stream->length() + 100, 8000, 48000).empty());
 }
 
 
@@ -51,12 +53,14 @@ BOOST_AUTO_TEST_CASE (audio_content_fade_unfaded_part)
        auto content = content_factory("test/data/impulse_train.wav").front();
        auto film = new_test_film2("audio_content_fade_unfaded_part", { content });
 
+       auto const stream = content->audio->stream();
+
        content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
        content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
 
-       BOOST_CHECK (content->audio->fade(2000, 50, 48000).empty());
-       BOOST_CHECK (content->audio->fade(12000, 99, 48000).empty());
-       BOOST_CHECK (content->audio->fade(content->audio->stream()->length() - 2051, 50, 48000).empty());
+       BOOST_CHECK (content->audio->fade(stream, 2000, 50, 48000).empty());
+       BOOST_CHECK (content->audio->fade(stream, 12000, 99, 48000).empty());
+       BOOST_CHECK (content->audio->fade(stream, stream->length() - 2051, 50, 48000).empty());
 }
 
 
@@ -67,7 +71,7 @@ BOOST_AUTO_TEST_CASE (audio_content_within_the_fade_in)
 
        content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
 
-       auto const f1 = content->audio->fade(0, 2000, 48000);
+       auto const f1 = content->audio->fade(content->audio->stream(), 0, 2000, 48000);
        BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
        for (auto i = 0; i < 2000; ++i) {
                BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_in_curve(static_cast<float>(i) / 2000), 0.01);
@@ -80,10 +84,12 @@ BOOST_AUTO_TEST_CASE (audio_content_within_the_fade_out)
        auto content = content_factory("test/data/impulse_train.wav").front();
        auto film = new_test_film2("audio_content_within_the_fade_out", { content });
 
+       auto const stream = content->audio->stream();
+
        content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
        content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
 
-       auto const f1 = content->audio->fade(content->audio->stream()->length() - 2000, 2000, 48000);
+       auto const f1 = content->audio->fade(stream, stream->length() - 2000, 2000, 48000);
        BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
        for (auto i = 0; i < 2000; ++i) {
                BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_out_curve(static_cast<float>(i) / 2000), 0.01);
@@ -99,7 +105,7 @@ BOOST_AUTO_TEST_CASE (audio_content_overlapping_the_fade_in)
        content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
        content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
 
-       auto const f1 = content->audio->fade(1500, 2000, 48000);
+       auto const f1 = content->audio->fade(content->audio->stream(), 1500, 2000, 48000);
        BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
        for (auto i = 0; i < 500; ++i) {
                BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_in_curve(static_cast<float>(i + 1500) / 2000), 0.01);
@@ -115,10 +121,12 @@ BOOST_AUTO_TEST_CASE (audio_content_overlapping_the_fade_out)
        auto content = content_factory("test/data/impulse_train.wav").front();
        auto film = new_test_film2("audio_content_overlapping_the_fade_out", { content });
 
+       auto const stream = content->audio->stream();
+
        content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
        content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(4000, 48000));
 
-       auto const f1 = content->audio->fade(content->audio->stream()->length() - 4100, 2000, 48000);
+       auto const f1 = content->audio->fade(stream, stream->length() - 4100, 2000, 48000);
        BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
        for (auto i = 0; i < 100; ++i) {
                BOOST_REQUIRE_CLOSE (f1[i], 1.0f, 0.01);
@@ -134,12 +142,13 @@ BOOST_AUTO_TEST_CASE (audio_content_fade_in_and_out)
        auto content = content_factory("test/data/impulse_train.wav").front();
        auto film = new_test_film2("audio_content_fade_in_and_out", { content });
 
-       auto const length = content->audio->stream()->length();
+       auto const stream = content->audio->stream();
+       auto const length = stream->length();
 
        content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(length, 48000));
        content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(length, 48000));
 
-       auto const f1 = content->audio->fade(0, 10000, 48000);
+       auto const f1 = content->audio->fade(stream, 0, 10000, 48000);
        BOOST_REQUIRE_EQUAL (f1.size(), 10000U);
        for (auto i = 0; i < 10000; ++i) {
                BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_in_curve(static_cast<float>(i) / length) * logarithmic_fade_out_curve(static_cast<float>(i) / length), 0.01);
@@ -152,19 +161,21 @@ BOOST_AUTO_TEST_CASE (audio_content_fade_in_with_trim)
        auto content = content_factory("test/data/impulse_train.wav").front();
        auto film = new_test_film2("audio_content_fade_in_with_trim", { content });
 
+       auto const stream = content->audio->stream();
+
        content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
        content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(1000, 48000));
        content->set_trim_start(dcpomatic::ContentTime::from_frames(5200, 48000));
 
        /* In the trim */
-       auto const f1 = content->audio->fade(0, 2000, 48000);
+       auto const f1 = content->audio->fade(stream, 0, 2000, 48000);
        BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
        for (auto i = 0; i < 2000; ++i) {
                BOOST_REQUIRE_CLOSE (f1[i], 0.0f, 0.01);
        }
 
        /* In the fade */
-       auto const f2 = content->audio->fade(5200, 2000, 48000);
+       auto const f2 = content->audio->fade(stream, 5200, 2000, 48000);
        BOOST_REQUIRE_EQUAL (f2.size(), 2000U);
        for (auto i = 0; i < 2000; ++i) {
                BOOST_REQUIRE_CLOSE (f2[i], logarithmic_fade_in_curve(static_cast<float>(i) / 2000), 0.01);
@@ -177,7 +188,8 @@ BOOST_AUTO_TEST_CASE (audio_content_fade_out_with_trim)
        auto content = content_factory("test/data/impulse_train.wav").front();
        auto film = new_test_film2("audio_content_fade_out_with_trim", { content });
 
-       auto const length = content->audio->stream()->length();
+       auto const stream = content->audio->stream();
+       auto const length = stream->length();
 
        content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
        content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(1000, 48000));
@@ -185,14 +197,14 @@ BOOST_AUTO_TEST_CASE (audio_content_fade_out_with_trim)
        content->set_trim_end(dcpomatic::ContentTime::from_frames(9000, 48000));
 
        /* In the trim */
-       auto const f1 = content->audio->fade(length - 6000, 2000, 48000);
+       auto const f1 = content->audio->fade(stream, length - 6000, 2000, 48000);
        BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
        for (auto i = 0; i < 2000; ++i) {
                BOOST_REQUIRE_CLOSE (f1[i], 0.0f, 0.01);
        }
 
        /* In the fade */
-       auto const f2 = content->audio->fade(length - 9000 - 1000, 1000, 48000);
+       auto const f2 = content->audio->fade(stream, length - 9000 - 1000, 1000, 48000);
        BOOST_REQUIRE_EQUAL (f2.size(), 1000U);
        for (auto i = 0; i < 1000; ++i) {
                BOOST_REQUIRE_CLOSE (f2[i], logarithmic_fade_out_curve(static_cast<float>(i) / 1000), 0.01);
@@ -206,6 +218,8 @@ BOOST_AUTO_TEST_CASE (audio_content_fade_out_with_trim_at_44k1)
        auto content = content_factory("test/data/white.wav").front();
        auto film = new_test_film2("audio_content_fade_out_with_trim_at_44k1", { content });
 
+       auto const stream = content->audio->stream();
+
        /* /----- 3.5s ------|-Fade-|-Trim-\
         * |                 |  1s  | 0.5s |
         * \-----------------|------|------/
@@ -215,14 +229,14 @@ BOOST_AUTO_TEST_CASE (audio_content_fade_out_with_trim_at_44k1)
        content->set_trim_end(dcpomatic::ContentTime::from_seconds(0.5));
 
        /* In the trim */
-       auto const f1 = content->audio->fade(std::round(48000 * 4.75), 200, 48000);
+       auto const f1 = content->audio->fade(stream, std::round(48000 * 4.75), 200, 48000);
        BOOST_REQUIRE_EQUAL (f1.size(), 200U);
        for (auto i = 0; i < 200; ++i) {
                BOOST_REQUIRE_CLOSE (f1[i], 0.0f, 0.01);
        }
 
        /* In the fade */
-       auto const f2 = content->audio->fade(std::round(48000 * 3.5 + 200), 7000, 48000);
+       auto const f2 = content->audio->fade(stream, std::round(48000 * 3.5 + 200), 7000, 48000);
        BOOST_REQUIRE_EQUAL (f2.size(), 7000U);
        for (auto i = 0; i < 7000; ++i) {
                BOOST_REQUIRE_CLOSE (f2[i], logarithmic_fade_out_curve(static_cast<float>(i + 200) / 48000), 0.01);