Add fade in/out option to the content audio tab (#1026).
[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/content_factory.h"
24 #include "lib/maths_util.h"
25 #include "test.h"
26 #include <boost/test/unit_test.hpp>
27
28
29 BOOST_AUTO_TEST_CASE (audio_content_fade_empty_region)
30 {
31         auto content = content_factory("test/data/impulse_train.wav").front();
32         auto film = new_test_film2("audio_content_fade_empty_region", { content });
33
34         BOOST_CHECK (content->audio->fade(content->audio->stream(), 0, 0, 48000).empty());
35 }
36
37
38 BOOST_AUTO_TEST_CASE (audio_content_fade_no_fade)
39 {
40         auto content = content_factory("test/data/impulse_train.wav").front();
41         auto film = new_test_film2("audio_content_fade_no_fade", { content });
42
43         auto const stream = content->audio->stream();
44
45         BOOST_CHECK (content->audio->fade(stream, 0, 2000, 48000).empty());
46         BOOST_CHECK (content->audio->fade(stream, 9999, 451, 48000).empty());
47         BOOST_CHECK (content->audio->fade(stream, stream->length() + 100, 8000, 48000).empty());
48 }
49
50
51 BOOST_AUTO_TEST_CASE (audio_content_fade_unfaded_part)
52 {
53         auto content = content_factory("test/data/impulse_train.wav").front();
54         auto film = new_test_film2("audio_content_fade_unfaded_part", { content });
55
56         auto const stream = content->audio->stream();
57
58         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
59         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
60
61         BOOST_CHECK (content->audio->fade(stream, 2000, 50, 48000).empty());
62         BOOST_CHECK (content->audio->fade(stream, 12000, 99, 48000).empty());
63         BOOST_CHECK (content->audio->fade(stream, stream->length() - 2051, 50, 48000).empty());
64 }
65
66
67 BOOST_AUTO_TEST_CASE (audio_content_within_the_fade_in)
68 {
69         auto content = content_factory("test/data/impulse_train.wav").front();
70         auto film = new_test_film2("audio_content_within_the_fade_in", { content });
71
72         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
73
74         auto const f1 = content->audio->fade(content->audio->stream(), 0, 2000, 48000);
75         BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
76         for (auto i = 0; i < 2000; ++i) {
77                 BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_in_curve(static_cast<float>(i) / 2000), 0.01);
78         }
79 }
80
81
82 BOOST_AUTO_TEST_CASE (audio_content_within_the_fade_out)
83 {
84         auto content = content_factory("test/data/impulse_train.wav").front();
85         auto film = new_test_film2("audio_content_within_the_fade_out", { content });
86
87         auto const stream = content->audio->stream();
88
89         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
90         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
91
92         auto const f1 = content->audio->fade(stream, stream->length() - 2000, 2000, 48000);
93         BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
94         for (auto i = 0; i < 2000; ++i) {
95                 BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_out_curve(static_cast<float>(i) / 2000), 0.01);
96         }
97 }
98
99
100 BOOST_AUTO_TEST_CASE (audio_content_overlapping_the_fade_in)
101 {
102         auto content = content_factory("test/data/impulse_train.wav").front();
103         auto film = new_test_film2("audio_content_overlapping_the_fade_in", { content });
104
105         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
106         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(2000, 48000));
107
108         auto const f1 = content->audio->fade(content->audio->stream(), 1500, 2000, 48000);
109         BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
110         for (auto i = 0; i < 500; ++i) {
111                 BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_in_curve(static_cast<float>(i + 1500) / 2000), 0.01);
112         }
113         for (auto i = 500; i < 2000; ++i) {
114                 BOOST_REQUIRE_CLOSE (f1[i], 1.0f, 0.01);
115         }
116 }
117
118
119 BOOST_AUTO_TEST_CASE (audio_content_overlapping_the_fade_out)
120 {
121         auto content = content_factory("test/data/impulse_train.wav").front();
122         auto film = new_test_film2("audio_content_overlapping_the_fade_out", { content });
123
124         auto const stream = content->audio->stream();
125
126         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
127         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(4000, 48000));
128
129         auto const f1 = content->audio->fade(stream, stream->length() - 4100, 2000, 48000);
130         BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
131         for (auto i = 0; i < 100; ++i) {
132                 BOOST_REQUIRE_CLOSE (f1[i], 1.0f, 0.01);
133         }
134         for (auto i = 100; i < 2000; ++i) {
135                 BOOST_REQUIRE_CLOSE (f1[i], logarithmic_fade_out_curve(static_cast<float>(i - 100) / 4000), 0.01);
136         }
137 }
138
139
140 BOOST_AUTO_TEST_CASE (audio_content_fade_in_and_out)
141 {
142         auto content = content_factory("test/data/impulse_train.wav").front();
143         auto film = new_test_film2("audio_content_fade_in_and_out", { content });
144
145         auto const stream = content->audio->stream();
146         auto const length = stream->length();
147
148         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(length, 48000));
149         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(length, 48000));
150
151         auto const f1 = content->audio->fade(stream, 0, 10000, 48000);
152         BOOST_REQUIRE_EQUAL (f1.size(), 10000U);
153         for (auto i = 0; i < 10000; ++i) {
154                 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);
155         }
156 }
157
158
159 BOOST_AUTO_TEST_CASE (audio_content_fade_in_with_trim)
160 {
161         auto content = content_factory("test/data/impulse_train.wav").front();
162         auto film = new_test_film2("audio_content_fade_in_with_trim", { content });
163
164         auto const stream = content->audio->stream();
165
166         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
167         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(1000, 48000));
168         content->set_trim_start(dcpomatic::ContentTime::from_frames(5200, 48000));
169
170         /* In the trim */
171         auto const f1 = content->audio->fade(stream, 0, 2000, 48000);
172         BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
173         for (auto i = 0; i < 2000; ++i) {
174                 BOOST_REQUIRE_CLOSE (f1[i], 0.0f, 0.01);
175         }
176
177         /* In the fade */
178         auto const f2 = content->audio->fade(stream, 5200, 2000, 48000);
179         BOOST_REQUIRE_EQUAL (f2.size(), 2000U);
180         for (auto i = 0; i < 2000; ++i) {
181                 BOOST_REQUIRE_CLOSE (f2[i], logarithmic_fade_in_curve(static_cast<float>(i) / 2000), 0.01);
182         }
183 }
184
185
186 BOOST_AUTO_TEST_CASE (audio_content_fade_out_with_trim)
187 {
188         auto content = content_factory("test/data/impulse_train.wav").front();
189         auto film = new_test_film2("audio_content_fade_out_with_trim", { content });
190
191         auto const stream = content->audio->stream();
192         auto const length = stream->length();
193
194         content->audio->set_fade_in(dcpomatic::ContentTime::from_frames(2000, 48000));
195         content->audio->set_fade_out(dcpomatic::ContentTime::from_frames(1000, 48000));
196         content->set_trim_start(dcpomatic::ContentTime::from_frames(5200, 48000));
197         content->set_trim_end(dcpomatic::ContentTime::from_frames(9000, 48000));
198
199         /* In the trim */
200         auto const f1 = content->audio->fade(stream, length - 6000, 2000, 48000);
201         BOOST_REQUIRE_EQUAL (f1.size(), 2000U);
202         for (auto i = 0; i < 2000; ++i) {
203                 BOOST_REQUIRE_CLOSE (f1[i], 0.0f, 0.01);
204         }
205
206         /* In the fade */
207         auto const f2 = content->audio->fade(stream, length - 9000 - 1000, 1000, 48000);
208         BOOST_REQUIRE_EQUAL (f2.size(), 1000U);
209         for (auto i = 0; i < 1000; ++i) {
210                 BOOST_REQUIRE_CLOSE (f2[i], logarithmic_fade_out_curve(static_cast<float>(i) / 1000), 0.01);
211         }
212 }
213
214
215 BOOST_AUTO_TEST_CASE (audio_content_fade_out_with_trim_at_44k1)
216 {
217         /* 5s at 44.1kHz */
218         auto content = content_factory("test/data/white.wav").front();
219         auto film = new_test_film2("audio_content_fade_out_with_trim_at_44k1", { content });
220
221         auto const stream = content->audio->stream();
222
223         /* /----- 3.5s ------|-Fade-|-Trim-\
224          * |                 |  1s  | 0.5s |
225          * \-----------------|------|------/
226          */
227
228         content->audio->set_fade_out(dcpomatic::ContentTime::from_seconds(1));
229         content->set_trim_end(dcpomatic::ContentTime::from_seconds(0.5));
230
231         /* In the trim */
232         auto const f1 = content->audio->fade(stream, std::round(48000 * 4.75), 200, 48000);
233         BOOST_REQUIRE_EQUAL (f1.size(), 200U);
234         for (auto i = 0; i < 200; ++i) {
235                 BOOST_REQUIRE_CLOSE (f1[i], 0.0f, 0.01);
236         }
237
238         /* In the fade */
239         auto const f2 = content->audio->fade(stream, std::round(48000 * 3.5 + 200), 7000, 48000);
240         BOOST_REQUIRE_EQUAL (f2.size(), 7000U);
241         for (auto i = 0; i < 7000; ++i) {
242                 BOOST_REQUIRE_CLOSE (f2[i], logarithmic_fade_out_curve(static_cast<float>(i + 200) / 48000), 0.01);
243         }
244
245 }
246