Fill test disk partitions with random noise to expose more bugs.
[dcpomatic.git] / test / ffmpeg_audio_only_test.cc
1 /*
2     Copyright (C) 2016-2021 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 /** @file  test/ffmpeg_audio_only_test.cc
23  *  @brief Test FFmpeg content with audio but no video.
24  *  @ingroup feature
25  */
26
27
28 #include "lib/film.h"
29 #include "lib/ffmpeg_content.h"
30 #include "lib/dcp_content_type.h"
31 #include "lib/player.h"
32 #include "lib/job_manager.h"
33 #include "lib/audio_buffers.h"
34 #include "test.h"
35 #include <dcp/sound_asset.h>
36 #include <dcp/sound_asset_reader.h>
37 #include <sndfile.h>
38 #include <boost/test/unit_test.hpp>
39 #include <iostream>
40
41
42 using std::min;
43 using std::shared_ptr;
44 using std::make_shared;
45 #if BOOST_VERSION >= 106100
46 using namespace boost::placeholders;
47 #endif
48
49
50 static SNDFILE* ref = nullptr;
51 static std::vector<float> ref_buffer;
52
53
54 static void
55 audio (std::shared_ptr<AudioBuffers> audio, int channels)
56 {
57         /* Check that we have a big enough buffer */
58         BOOST_CHECK (audio->frames() * audio->channels() < static_cast<int>(ref_buffer.size()));
59
60         int const N = sf_readf_float (ref, ref_buffer.data(), audio->frames());
61         for (int i = 0; i < N; ++i) {
62                 switch (channels) {
63                 case 1:
64                         BOOST_CHECK_EQUAL (ref_buffer[i], audio->data(2)[i]);
65                         break;
66                 case 2:
67                         BOOST_CHECK_EQUAL (ref_buffer[i*2 + 0], audio->data(0)[i]);
68                         BOOST_CHECK_EQUAL (ref_buffer[i*2 + 1], audio->data(1)[i]);
69                         break;
70                 default:
71                         BOOST_REQUIRE (false);
72                 }
73         }
74 }
75
76
77 /** Test the FFmpeg code with audio-only content */
78 static shared_ptr<Film>
79 test (boost::filesystem::path file)
80 {
81         auto film = new_test_film ("ffmpeg_audio_only_test");
82         film->set_name ("test_film");
83         film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
84         auto c = make_shared<FFmpegContent>(file);
85         film->examine_and_add_content (c);
86         BOOST_REQUIRE (!wait_for_jobs());
87         film->write_metadata ();
88
89         /* See if can make a DCP without any errors */
90         make_and_verify_dcp (film, {dcp::VerificationNote::Code::MISSING_CPL_METADATA});
91         BOOST_CHECK (!JobManager::instance()->errors());
92
93         /* Compare the audio data player reads with what libsndfile reads */
94
95         SF_INFO info;
96         info.format = 0;
97         ref = sf_open (file.string().c_str(), SFM_READ, &info);
98         /* We don't want to test anything that requires resampling */
99         BOOST_REQUIRE_EQUAL (info.samplerate, 48000);
100         ref_buffer.resize(info.samplerate * info.channels);
101
102         auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
103
104         player->Audio.connect (bind (&audio, _1, info.channels));
105         while (!player->pass ()) {}
106
107         sf_close (ref);
108
109         return film;
110 }
111
112
113 BOOST_AUTO_TEST_CASE (ffmpeg_audio_only_test1)
114 {
115         /* S16 */
116         auto film = test ("test/data/staircase.wav");
117
118         /* Compare the audio data in the DCP with what libsndfile reads */
119
120         SF_INFO info;
121         info.format = 0;
122         ref = sf_open ("test/data/staircase.wav", SFM_READ, &info);
123         /* We don't want to test anything that requires resampling */
124         BOOST_REQUIRE_EQUAL (info.samplerate, 48000);
125
126         std::vector<int16_t> buffer(info.channels * 2000);
127
128         dcp::SoundAsset asset (dcp_file(film, "pcm"));
129         auto reader = asset.start_read ();
130         for (int i = 0; i < asset.intrinsic_duration(); ++i) {
131                 auto frame = reader->get_frame(i);
132                 sf_count_t this_time = min (info.frames, sf_count_t(2000));
133                 sf_readf_short (ref, buffer.data(), this_time);
134                 for (int j = 0; j < this_time; ++j) {
135                         BOOST_REQUIRE_EQUAL (frame->get(2, j) >> 8, buffer[j]);
136                 }
137                 info.frames -= this_time;
138         }
139 }
140
141
142 BOOST_AUTO_TEST_CASE (ffmpeg_audio_only_test2)
143 {
144         /* S32 1 channel */
145         auto film = test ("test/data/sine_440.wav");
146
147         /* Compare the audio data in the DCP with what libsndfile reads */
148
149         SF_INFO info;
150         info.format = 0;
151         ref = sf_open ("test/data/sine_440.wav", SFM_READ, &info);
152         /* We don't want to test anything that requires resampling */
153         BOOST_REQUIRE_EQUAL (info.samplerate, 48000);
154
155         std::vector<int32_t> buffer(info.channels * 2000);
156
157         dcp::SoundAsset asset (dcp_file(film, "pcm"));
158         auto reader = asset.start_read ();
159         for (int i = 0; i < asset.intrinsic_duration(); ++i) {
160                 auto frame = reader->get_frame(i);
161                 sf_count_t this_time = min (info.frames, sf_count_t(2000));
162                 sf_readf_int (ref, buffer.data(), this_time);
163                 for (int j = 0; j < this_time; ++j) {
164                         int32_t s = frame->get(2, j);
165                         if (s > (1 << 23)) {
166                                 s -= (1 << 24);
167                         }
168                         BOOST_REQUIRE_MESSAGE (abs(s - (buffer[j] / 256)) <= 1, "failed on asset frame " << i << " sample " << j);
169                 }
170                 info.frames -= this_time;
171         }
172 }
173
174
175 BOOST_AUTO_TEST_CASE (ffmpeg_audio_only_test3)
176 {
177         /* S24 1 channel */
178         auto film = test ("test/data/sine_24_48_440.wav");
179
180         /* Compare the audio data in the DCP with what libsndfile reads */
181
182         SF_INFO info;
183         info.format = 0;
184         ref = sf_open ("test/data/sine_24_48_440.wav", SFM_READ, &info);
185         /* We don't want to test anything that requires resampling */
186         BOOST_REQUIRE_EQUAL (info.samplerate, 48000);
187
188         std::vector<int32_t> buffer(info.channels * 2000);
189
190         dcp::SoundAsset asset (dcp_file(film, "pcm"));
191         auto reader = asset.start_read ();
192         for (int i = 0; i < asset.intrinsic_duration(); ++i) {
193                 auto frame = reader->get_frame(i);
194                 sf_count_t this_time = min (info.frames, sf_count_t(2000));
195                 sf_readf_int (ref, buffer.data(), this_time);
196                 for (int j = 0; j < this_time; ++j) {
197                         int32_t s = frame->get(2, j);
198                         if (s > (1 << 23)) {
199                                 s -= (1 << 24);
200                         }
201                         BOOST_REQUIRE_MESSAGE (abs(s - buffer[j] /256) <= 1, "failed on asset frame " << i << " sample " << j);
202                 }
203                 info.frames -= this_time;
204         }
205 }