2 Copyright (C) 2022 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
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.
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.
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/>.
22 #include "lib/audio_content.h"
23 #include "lib/dcp_content.h"
24 #include "lib/content_factory.h"
26 #include "lib/maths_util.h"
27 #include "lib/video_content.h"
29 #include <dcp/sound_asset.h>
30 #include <dcp/sound_asset_reader.h>
31 #include <boost/test/unit_test.hpp>
34 BOOST_AUTO_TEST_CASE (audio_content_fade_empty_region)
36 auto content = content_factory("test/data/impulse_train.wav");
37 auto film = new_test_film2("audio_content_fade_empty_region", content);
39 BOOST_CHECK(content[0]->audio->fade(content[0]->audio->stream(), 0, 0, 48000).empty());
43 BOOST_AUTO_TEST_CASE (audio_content_fade_no_fade)
45 auto content = content_factory("test/data/impulse_train.wav");
46 auto film = new_test_film2("audio_content_fade_no_fade", content);
48 auto const stream = content[0]->audio->stream();
50 BOOST_CHECK(content[0]->audio->fade(stream, 0, 2000, 48000).empty());
51 BOOST_CHECK(content[0]->audio->fade(stream, 9999, 451, 48000).empty());
52 BOOST_CHECK(content[0]->audio->fade(stream, stream->length() + 100, 8000, 48000).empty());
56 BOOST_AUTO_TEST_CASE (audio_content_fade_unfaded_part)
58 auto content = content_factory("test/data/impulse_train.wav")[0];
59 auto film = new_test_film2("audio_content_fade_unfaded_part", { content });
61 auto const stream = content->audio->stream();
63 content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
64 content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
66 BOOST_CHECK (content->audio->fade(stream, 2000, 50, 48000).empty());
67 BOOST_CHECK (content->audio->fade(stream, 12000, 99, 48000).empty());
68 BOOST_CHECK (content->audio->fade(stream, stream->length() - 2051, 50, 48000).empty());
72 BOOST_AUTO_TEST_CASE (audio_content_within_the_fade_in)
74 auto content = content_factory("test/data/impulse_train.wav")[0];
75 auto film = new_test_film2("audio_content_within_the_fade_in", { content });
77 content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
79 auto const f1 = content->audio->fade(content->audio->stream(), 0, 2000, 48000);
80 BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
81 for (auto i = 0; i < 2000; ++i) {
82 BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_in_curve(static_cast<float>(i) / 2000), 0.01);
87 BOOST_AUTO_TEST_CASE (audio_content_within_the_fade_out)
89 auto content = content_factory("test/data/impulse_train.wav")[0];
90 auto film = new_test_film2("audio_content_within_the_fade_out", { content });
92 auto const stream = content->audio->stream();
94 content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
95 content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
97 auto const f1 = content->audio->fade(stream, stream->length() - 2000, 2000, 48000);
98 BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
99 for (auto i = 0; i < 2000; ++i) {
100 BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_out_curve(static_cast<float>(i) / 2000), 0.01);
105 BOOST_AUTO_TEST_CASE (audio_content_overlapping_the_fade_in)
107 auto content = content_factory("test/data/impulse_train.wav")[0];
108 auto film = new_test_film2("audio_content_overlapping_the_fade_in", { content });
110 content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
111 content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
113 auto const f1 = content->audio->fade(content->audio->stream(), 1500, 2000, 48000);
114 BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
115 for (auto i = 0; i < 500; ++i) {
116 BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_in_curve(static_cast<float>(i + 1500) / 2000), 0.01);
118 for (auto i = 500; i < 2000; ++i) {
119 BOOST_REQUIRE_CLOSE (f1[i], 1.0f, 0.01);
124 BOOST_AUTO_TEST_CASE (audio_content_overlapping_the_fade_out)
126 auto content = content_factory("test/data/impulse_train.wav")[0];
127 auto film = new_test_film2("audio_content_overlapping_the_fade_out", { content });
129 auto const stream = content->audio->stream();
131 content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
132 content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(4000, 48000));
134 auto const f1 = content->audio->fade(stream, stream->length() - 4100, 2000, 48000);
135 BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
136 for (auto i = 0; i < 100; ++i) {
137 BOOST_REQUIRE_CLOSE (f1[i], 1.0f, 0.01);
139 for (auto i = 100; i < 2000; ++i) {
140 BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_out_curve(static_cast<float>(i - 100) / 4000), 0.01);
145 BOOST_AUTO_TEST_CASE (audio_content_fade_in_and_out)
147 auto content = content_factory("test/data/impulse_train.wav")[0];
148 auto film = new_test_film2("audio_content_fade_in_and_out", { content });
150 auto const stream = content->audio->stream();
151 auto const length = stream->length();
153 content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(length, 48000));
154 content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(length, 48000));
156 auto const f1 = content->audio->fade(stream, 0, 10000, 48000);
157 BOOST_REQUIRE_EQUAL (f1.size(), 10000U);
158 for (auto i = 0; i < 10000; ++i) {
159 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);
164 BOOST_AUTO_TEST_CASE (audio_content_fade_in_with_trim)
166 auto content = content_factory("test/data/impulse_train.wav")[0];
167 auto film = new_test_film2("audio_content_fade_in_with_trim", { content });
169 auto const stream = content->audio->stream();
171 content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
172 content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(1000, 48000));
173 content->set_trim_start(film, dcpomatic::ContentTime::from_frames(5200, 48000));
176 auto const f1 = content->audio->fade(stream, 0, 2000, 48000);
177 BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
178 for (auto i = 0; i < 2000; ++i) {
179 BOOST_REQUIRE_CLOSE (f1[i], 0.0f, 0.01);
183 auto const f2 = content->audio->fade(stream, 5200, 2000, 48000);
184 BOOST_REQUIRE_EQUAL (f2.size(), 2000U);
185 for (auto i = 0; i < 2000; ++i) {
186 BOOST_REQUIRE_CLOSE (f2[i], logarithmic_fade_in_curve(static_cast<float>(i) / 2000), 0.01);
191 BOOST_AUTO_TEST_CASE (audio_content_fade_out_with_trim)
193 auto content = content_factory("test/data/impulse_train.wav")[0];
194 auto film = new_test_film2("audio_content_fade_out_with_trim", { content });
196 auto const stream = content->audio->stream();
197 auto const length = stream->length();
199 content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
200 content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(1000, 48000));
201 content->set_trim_start(film, dcpomatic::ContentTime::from_frames(5200, 48000));
202 content->set_trim_end(dcpomatic::ContentTime::from_frames(9000, 48000));
205 auto const f1 = content->audio->fade(stream, length - 6000, 2000, 48000);
206 BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
207 for (auto i = 0; i < 2000; ++i) {
208 BOOST_REQUIRE_CLOSE (f1[i], 0.0f, 0.01);
212 auto const f2 = content->audio->fade(stream, length - 9000 - 1000, 1000, 48000);
213 BOOST_REQUIRE_EQUAL (f2.size(), 1000U);
214 for (auto i = 0; i < 1000; ++i) {
215 BOOST_REQUIRE_CLOSE (f2[i], logarithmic_fade_out_curve(static_cast<float>(i) / 1000), 0.01);
220 BOOST_AUTO_TEST_CASE (audio_content_fade_out_with_trim_at_44k1)
223 auto content = content_factory("test/data/white.wav")[0];
224 auto film = new_test_film2("audio_content_fade_out_with_trim_at_44k1", { content });
226 auto const stream = content->audio->stream();
228 /* /----- 3.5s ------|-Fade-|-Trim-\
230 * \-----------------|------|------/
233 content->audio->set_fade_out(dcpomatic::ContentTime::from_seconds(1));
234 content->set_trim_end(dcpomatic::ContentTime::from_seconds(0.5));
237 auto const f1 = content->audio->fade(stream, std::round(48000 * 4.75), 200, 48000);
238 BOOST_REQUIRE_EQUAL (f1.size(), 200U);
239 for (auto i = 0; i < 200; ++i) {
240 BOOST_REQUIRE_CLOSE (f1[i], 0.0f, 0.01);
244 auto const f2 = content->audio->fade(stream, std::round(48000 * 3.5 + 200), 7000, 48000);
245 BOOST_REQUIRE_EQUAL (f2.size(), 7000U);
246 for (auto i = 0; i < 7000; ++i) {
247 BOOST_REQUIRE_CLOSE (f2[i], logarithmic_fade_out_curve(static_cast<float>(i + 200) / 48000), 0.01);
253 BOOST_AUTO_TEST_CASE (audio_content_fades_same_as_video)
255 auto content = content_factory("test/data/staircase.mov")[0];
256 auto film = new_test_film2("audio_content_fades_same_as_video", { content });
258 content->audio->set_use_same_fades_as_video(true);
259 content->video->set_fade_in(9);
260 content->video->set_fade_out(81);
262 BOOST_CHECK(content->audio->fade_in() == dcpomatic::ContentTime::from_frames(9 * 48000 / 24, 48000));
263 BOOST_CHECK(content->audio->fade_out() == dcpomatic::ContentTime::from_frames(81 * 48000 / 24, 48000));
268 BOOST_AUTO_TEST_CASE(fade_out_works_with_dcp_content)
270 auto dcp = std::make_shared<DCPContent>(TestPaths::private_data() / "JourneyToJah_TLR-1_F_EN-DE-FR_CH_51_2K_LOK_20140225_DGL_SMPTE_OV");
271 auto film = new_test_film2("fade_out_works_with_dcp_content", { dcp });
272 dcp->audio->set_fade_out(dcpomatic::ContentTime::from_seconds(15));
273 make_and_verify_dcp(film);
276 dcp::SoundAsset sound(find_file(film->dir(film->dcp_name()), "pcm_"));
277 auto reader = sound.start_read();
278 for (auto i = 0; i < sound.intrinsic_duration(); ++i) {
279 auto frame = reader->get_frame(i);
280 for (auto j = 0; j < frame->channels(); ++j) {
281 for (auto k = 0; k < frame->samples(); ++k) {
282 max = std::max(max, frame->get(j, k));
287 BOOST_CHECK(max > 2000);