Bump libsub for SSA Margin{L,R} support (#2811).
[dcpomatic.git] / test / audio_content_test.cc
1 /*
2     Copyright (C) 2022 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 "lib/audio_content.h"
23 #include "lib/dcp_content.h"
24 #include "lib/content_factory.h"
25 #include "lib/film.h"
26 #include "lib/maths_util.h"
27 #include "lib/video_content.h"
28 #include "test.h"
29 #include <dcp/sound_asset.h>
30 #include <dcp/sound_asset_reader.h>
31 #include <boost/test/unit_test.hpp>
32
33
34 BOOST_AUTO_TEST_CASE (audio_content_fade_empty_region)
35 {
36         auto content = content_factory("test/data/impulse_train.wav");
37         auto film = new_test_film2("audio_content_fade_empty_region", content);
38
39         BOOST_CHECK(content[0]->audio->fade(content[0]->audio->stream(), 0, 0, 48000).empty());
40 }
41
42
43 BOOST_AUTO_TEST_CASE (audio_content_fade_no_fade)
44 {
45         auto content = content_factory("test/data/impulse_train.wav");
46         auto film = new_test_film2("audio_content_fade_no_fade", content);
47
48         auto const stream = content[0]->audio->stream();
49
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());
53 }
54
55
56 BOOST_AUTO_TEST_CASE (audio_content_fade_unfaded_part)
57 {
58         auto content = content_factory("test/data/impulse_train.wav")[0];
59         auto film = new_test_film2("audio_content_fade_unfaded_part", { content });
60
61         auto const stream = content->audio->stream();
62
63         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
64         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
65
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());
69 }
70
71
72 BOOST_AUTO_TEST_CASE (audio_content_within_the_fade_in)
73 {
74         auto content = content_factory("test/data/impulse_train.wav")[0];
75         auto film = new_test_film2("audio_content_within_the_fade_in", { content });
76
77         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
78
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);
83         }
84 }
85
86
87 BOOST_AUTO_TEST_CASE (audio_content_within_the_fade_out)
88 {
89         auto content = content_factory("test/data/impulse_train.wav")[0];
90         auto film = new_test_film2("audio_content_within_the_fade_out", { content });
91
92         auto const stream = content->audio->stream();
93
94         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
95         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
96
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);
101         }
102 }
103
104
105 BOOST_AUTO_TEST_CASE (audio_content_overlapping_the_fade_in)
106 {
107         auto content = content_factory("test/data/impulse_train.wav")[0];
108         auto film = new_test_film2("audio_content_overlapping_the_fade_in", { content });
109
110         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
111         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
112
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);
117         }
118         for (auto i = 500; i < 2000; ++i) {
119                 BOOST_REQUIRE_CLOSE (f1[i], 1.0f, 0.01);
120         }
121 }
122
123
124 BOOST_AUTO_TEST_CASE (audio_content_overlapping_the_fade_out)
125 {
126         auto content = content_factory("test/data/impulse_train.wav")[0];
127         auto film = new_test_film2("audio_content_overlapping_the_fade_out", { content });
128
129         auto const stream = content->audio->stream();
130
131         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
132         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(4000, 48000));
133
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);
138         }
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);
141         }
142 }
143
144
145 BOOST_AUTO_TEST_CASE (audio_content_fade_in_and_out)
146 {
147         auto content = content_factory("test/data/impulse_train.wav")[0];
148         auto film = new_test_film2("audio_content_fade_in_and_out", { content });
149
150         auto const stream = content->audio->stream();
151         auto const length = stream->length();
152
153         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(length, 48000));
154         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(length, 48000));
155
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);
160         }
161 }
162
163
164 BOOST_AUTO_TEST_CASE (audio_content_fade_in_with_trim)
165 {
166         auto content = content_factory("test/data/impulse_train.wav")[0];
167         auto film = new_test_film2("audio_content_fade_in_with_trim", { content });
168
169         auto const stream = content->audio->stream();
170
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));
174
175         /* In the trim */
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);
180         }
181
182         /* In the fade */
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);
187         }
188 }
189
190
191 BOOST_AUTO_TEST_CASE (audio_content_fade_out_with_trim)
192 {
193         auto content = content_factory("test/data/impulse_train.wav")[0];
194         auto film = new_test_film2("audio_content_fade_out_with_trim", { content });
195
196         auto const stream = content->audio->stream();
197         auto const length = stream->length();
198
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));
203
204         /* In the trim */
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);
209         }
210
211         /* In the fade */
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);
216         }
217 }
218
219
220 BOOST_AUTO_TEST_CASE (audio_content_fade_out_with_trim_at_44k1)
221 {
222         /* 5s at 44.1kHz */
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 });
225
226         auto const stream = content->audio->stream();
227
228         /* /----- 3.5s ------|-Fade-|-Trim-\
229          * |                 |  1s  | 0.5s |
230          * \-----------------|------|------/
231          */
232
233         content->audio->set_fade_out(dcpomatic::ContentTime::from_seconds(1));
234         content->set_trim_end(dcpomatic::ContentTime::from_seconds(0.5));
235
236         /* In the trim */
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);
241         }
242
243         /* In the fade */
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);
248         }
249
250 }
251
252
253 BOOST_AUTO_TEST_CASE (audio_content_fades_same_as_video)
254 {
255         auto content = content_factory("test/data/staircase.mov")[0];
256         auto film = new_test_film2("audio_content_fades_same_as_video", { content });
257
258         content->audio->set_use_same_fades_as_video(true);
259         content->video->set_fade_in(9);
260         content->video->set_fade_out(81);
261
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));
264 }
265
266
267
268 BOOST_AUTO_TEST_CASE(fade_out_works_with_dcp_content)
269 {
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);
274
275         int32_t max = 0;
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));
283                         }
284                 }
285         }
286
287         BOOST_CHECK(max > 2000);
288 }
289