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