1 /*
2 * Copyright (c) 2023, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 3-Clause Clear License
5 * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6 * License was not distributed with this source code in the LICENSE file, you
7 * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8 * Alliance for Open Media Patent License 1.0 was not distributed with this
9 * source code in the PATENTS file, you can obtain it at
10 * www.aomedia.org/license/patent.
11 */
12 #include "iamf/cli/wav_reader.h"
13
14 #include <cstddef>
15 #include <cstdint>
16 #include <cstring>
17 #include <filesystem>
18 #include <fstream>
19 #include <ios>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 // [internal] Placeholder for get runfiles header.
25 #include "absl/status/status_matchers.h"
26 #include "absl/strings/string_view.h"
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29 #include "iamf/cli/tests/cli_test_utils.h"
30
31 namespace iamf_tools {
32 namespace {
33
34 using ::absl_testing::IsOk;
35
36 constexpr size_t kArbitraryNumSamplesPerFrame = 1;
37 constexpr absl::string_view kAdmBwfWithOneStereoAndOneMonoObject(
38 "RIFF"
39 "\xf5\x00\x00\x00" // Size of `RIFF` chunk (the whole file).
40 "WAVE"
41 "fmt "
42 "\x10\x00\x00\x00" // Size of the `fmt ` chunk.
43 "\x01\x00" // Format tag.
44 "\x03\x00" // Number of channels.
45 "\x80\xbb\x00\x00" // Sample per second
46 "\x00\x65\x04\x00" // Bytes per second = [number_of_channels *
47 // ceil(bits_per_sample / 8) * sample_per_second].
48 "\x06\x00" // Block align = [number of channels * bits per sample]
49 "\x10\x00" // Bits per sample.
50 "data"
51 "\x0c\x00\x00\x00" // Size of `data` chunk.
52 "\x01\x23" // Sample[0] for object[0] L.
53 "\x45\x67" // Sample[0] for object[0] R.
54 "\xaa\xbb" // Sample[0] for object[1] M.
55 "\x89\xab" // Sample[1] for object[0] L.
56 "\xcd\xef" // Sample[1] for object[0] R.
57 "\xcc\xdd" // Sample[1] for object[1] M.
58 "axml"
59 "\xbd\x00\x00\x00" // Size of `axml` chunk.
60 "<topLevel>"
61 "<audioObject>"
62 "<audioTrackUIDRef>L</audioTrackUIDRef>"
63 "<audioTrackUIDRef>R</audioTrackUIDRef>"
64 "</audioObject>"
65 "<audioObject>"
66 "<audioTrackUIDRef>M</audioTrackUIDRef>"
67 "</audioObject>"
68 "</topLevel>",
69 253);
70
CreateFileFromStringView(absl::string_view file_contents,absl::string_view file_path_suffix,std::string & file_name)71 void CreateFileFromStringView(absl::string_view file_contents,
72 absl::string_view file_path_suffix,
73 std::string& file_name) {
74 file_name = GetAndCleanupOutputFileName(file_path_suffix);
75 std::ofstream file_stream(file_name, std::ios::binary | std::ios::out);
76 file_stream << file_contents;
77 file_stream.close();
78 ASSERT_TRUE(std::filesystem::exists(file_name));
79 }
80
TEST(CreateFromFile,SucceedsOnValidAdmFile)81 TEST(CreateFromFile, SucceedsOnValidAdmFile) {
82 std::string adm_file_name;
83 CreateFileFromStringView(kAdmBwfWithOneStereoAndOneMonoObject, ".adm",
84 adm_file_name);
85
86 EXPECT_THAT(
87 WavReader::CreateFromFile(adm_file_name, kArbitraryNumSamplesPerFrame),
88 IsOk());
89 }
90
TEST(CreateFromFile,SucceedsOnValidWavFile)91 TEST(CreateFromFile, SucceedsOnValidWavFile) {
92 const auto input_wav_file =
93 (std::filesystem::current_path() / std::string("iamf/cli/testdata/") /
94 "stereo_8_samples_48khz_s16le.wav")
95 .string();
96 ASSERT_TRUE(std::filesystem::exists(input_wav_file));
97
98 EXPECT_THAT(
99 WavReader::CreateFromFile(input_wav_file, kArbitraryNumSamplesPerFrame),
100 IsOk());
101 }
102
TEST(CreateFromFile,FailsWhenNumSamplesPerFrameIsZero)103 TEST(CreateFromFile, FailsWhenNumSamplesPerFrameIsZero) {
104 const size_t kInvalidNumSamplesPerFrame = 0;
105 const auto input_wav_file =
106 (std::filesystem::current_path() / std::string("iamf/cli/testdata/") /
107 "stereo_8_samples_48khz_s16le.wav")
108 .string();
109 ASSERT_TRUE(std::filesystem::exists(input_wav_file));
110
111 EXPECT_FALSE(
112 WavReader::CreateFromFile(input_wav_file, kInvalidNumSamplesPerFrame)
113 .ok());
114 }
115
TEST(CreateFromFile,FailsOnMissingFile)116 TEST(CreateFromFile, FailsOnMissingFile) {
117 const std::string non_existent_file(GetAndCleanupOutputFileName(".wav"));
118 ASSERT_FALSE(std::filesystem::exists(non_existent_file));
119
120 EXPECT_FALSE(
121 WavReader::CreateFromFile(non_existent_file, kArbitraryNumSamplesPerFrame)
122 .ok());
123 }
124
TEST(CreateFromFile,FailsOnNonWavFile)125 TEST(CreateFromFile, FailsOnNonWavFile) {
126 const std::string non_wav_file(GetAndCleanupOutputFileName(".txt"));
127 std::ofstream(non_wav_file, std::ios::binary | std::ios::out)
128 << "This is not a wav file.";
129 ASSERT_TRUE(std::filesystem::exists(non_wav_file));
130
131 EXPECT_FALSE(
132 WavReader::CreateFromFile(non_wav_file, kArbitraryNumSamplesPerFrame)
133 .ok());
134 }
135
InitAndValidate(const std::filesystem::path & filename,const size_t num_samples_per_frame)136 WavReader InitAndValidate(const std::filesystem::path& filename,
137 const size_t num_samples_per_frame) {
138 const auto input_wav_file = (std::filesystem::current_path() /
139 std::string("iamf/cli/testdata/") / filename)
140 .string();
141 auto wav_reader =
142 WavReader::CreateFromFile(input_wav_file, num_samples_per_frame);
143 EXPECT_THAT(wav_reader, IsOk());
144
145 // Validate `wav_reader` sees the expected properties from the wav header.
146 EXPECT_EQ(wav_reader->num_samples_per_frame_, num_samples_per_frame);
147 return std::move(*wav_reader);
148 }
149
TEST(WavReader,GetNumChannelsMatchesWavFile)150 TEST(WavReader, GetNumChannelsMatchesWavFile) {
151 EXPECT_EQ(InitAndValidate("stereo_8_samples_48khz_s16le.wav",
152 kArbitraryNumSamplesPerFrame)
153 .num_channels(),
154 2);
155 EXPECT_EQ(InitAndValidate("stereo_8_samples_48khz_s24le.wav",
156 kArbitraryNumSamplesPerFrame)
157 .num_channels(),
158 2);
159 EXPECT_EQ(InitAndValidate("sine_1000_16khz_512ms_s32le.wav",
160 kArbitraryNumSamplesPerFrame)
161 .num_channels(),
162 1);
163 }
164
TEST(WavReader,GetNumChannelsMatchesAdmFile)165 TEST(WavReader, GetNumChannelsMatchesAdmFile) {
166 auto adm_file_name = GetAndCleanupOutputFileName(".adm");
167 CreateFileFromStringView(kAdmBwfWithOneStereoAndOneMonoObject, ".adm",
168 adm_file_name);
169
170 EXPECT_EQ(InitAndValidate(adm_file_name, kArbitraryNumSamplesPerFrame)
171 .num_channels(),
172 3);
173 }
174
TEST(WavReader,GetSampleRateHzMatchesWavFile)175 TEST(WavReader, GetSampleRateHzMatchesWavFile) {
176 const size_t kNumSamplesPerFrame = 8;
177
178 EXPECT_EQ(
179 InitAndValidate("stereo_8_samples_48khz_s16le.wav", kNumSamplesPerFrame)
180 .sample_rate_hz(),
181 48000);
182 EXPECT_EQ(
183 InitAndValidate("stereo_8_samples_48khz_s24le.wav", kNumSamplesPerFrame)
184 .sample_rate_hz(),
185 48000);
186 EXPECT_EQ(
187 InitAndValidate("sine_1000_16khz_512ms_s32le.wav", kNumSamplesPerFrame)
188 .sample_rate_hz(),
189 16000);
190 }
191
TEST(WavReader,GetSampleRateHzMatchesAdmFile)192 TEST(WavReader, GetSampleRateHzMatchesAdmFile) {
193 auto adm_file_name = GetAndCleanupOutputFileName(".adm");
194 CreateFileFromStringView(kAdmBwfWithOneStereoAndOneMonoObject, ".adm",
195 adm_file_name);
196
197 EXPECT_EQ(InitAndValidate(adm_file_name, kArbitraryNumSamplesPerFrame)
198 .sample_rate_hz(),
199 48000);
200 }
201
TEST(WavReader,GetBitDepthMatchesWavFile)202 TEST(WavReader, GetBitDepthMatchesWavFile) {
203 const size_t kNumSamplesPerFrame = 8;
204
205 EXPECT_EQ(
206 InitAndValidate("stereo_8_samples_48khz_s16le.wav", kNumSamplesPerFrame)
207 .bit_depth(),
208 16);
209 EXPECT_EQ(
210 InitAndValidate("stereo_8_samples_48khz_s24le.wav", kNumSamplesPerFrame)
211 .bit_depth(),
212 24);
213 EXPECT_EQ(
214 InitAndValidate("sine_1000_16khz_512ms_s32le.wav", kNumSamplesPerFrame)
215 .bit_depth(),
216 32);
217 }
218
TEST(WavReader,GetBitDepthMatchesAdmFile)219 TEST(WavReader, GetBitDepthMatchesAdmFile) {
220 auto adm_file_name = GetAndCleanupOutputFileName(".adm");
221 CreateFileFromStringView(kAdmBwfWithOneStereoAndOneMonoObject, ".adm",
222 adm_file_name);
223
224 EXPECT_EQ(
225 InitAndValidate(adm_file_name, kArbitraryNumSamplesPerFrame).bit_depth(),
226 16);
227 }
228
TEST(WavReader,GetNumRemainingSamplesUpdatesWithRead)229 TEST(WavReader, GetNumRemainingSamplesUpdatesWithRead) {
230 // Read four samples x two channels per frame.
231 const size_t kNumSamplesPerFrame = 4;
232 auto wav_reader =
233 InitAndValidate("stereo_8_samples_48khz_s16le.wav", kNumSamplesPerFrame);
234 EXPECT_EQ(wav_reader.remaining_samples(), 16);
235 wav_reader.ReadFrame();
236 EXPECT_EQ(wav_reader.remaining_samples(), 8);
237 wav_reader.ReadFrame();
238 EXPECT_EQ(wav_reader.remaining_samples(), 0);
239 }
240
TEST(WavReader,GetNumRemainingSamplesMatchesUpdatesWithReadForAdm)241 TEST(WavReader, GetNumRemainingSamplesMatchesUpdatesWithReadForAdm) {
242 // Read one sample x three channels per frame.
243 const size_t kNumSamplesPerFrame = 1;
244
245 auto adm_file_name = GetAndCleanupOutputFileName(".adm");
246 CreateFileFromStringView(kAdmBwfWithOneStereoAndOneMonoObject, ".adm",
247 adm_file_name);
248
249 auto wav_reader = InitAndValidate(adm_file_name, kNumSamplesPerFrame);
250
251 EXPECT_EQ(wav_reader.remaining_samples(), 6);
252 wav_reader.ReadFrame();
253 EXPECT_EQ(wav_reader.remaining_samples(), 3);
254 wav_reader.ReadFrame();
255 EXPECT_EQ(wav_reader.remaining_samples(), 0);
256 }
257
TEST(WavReader,OneFrame16BitLittleEndian)258 TEST(WavReader, OneFrame16BitLittleEndian) {
259 const size_t kNumSamplesPerFrame = 8;
260 auto wav_reader =
261 InitAndValidate("stereo_8_samples_48khz_s16le.wav", kNumSamplesPerFrame);
262
263 // Read one frame. The result of n-bit samples are stored in the upper `n`
264 // bits.
265 EXPECT_EQ(wav_reader.ReadFrame(), 16);
266 std::vector<std::vector<int32_t>> expected_frame = {
267 {0x00010000, static_cast<int32_t>(0xffff0000)},
268 {0x00020000, static_cast<int32_t>(0xfffe0000)},
269 {0x00030000, static_cast<int32_t>(0xfffd0000)},
270 {0x00040000, static_cast<int32_t>(0xfffc0000)},
271 {0x00050000, static_cast<int32_t>(0xfffb0000)},
272 {0x00060000, static_cast<int32_t>(0xfffa0000)},
273 {0x00070000, static_cast<int32_t>(0xfff90000)},
274 {0x00080000, static_cast<int32_t>(0xfff80000)},
275 };
276 EXPECT_EQ(wav_reader.buffers_, expected_frame);
277 }
278
TEST(WavReader,TwoFrames16BitLittleEndian)279 TEST(WavReader, TwoFrames16BitLittleEndian) {
280 const size_t kNumSamplesPerFrame = 4;
281 auto wav_reader =
282 InitAndValidate("stereo_8_samples_48khz_s16le.wav", kNumSamplesPerFrame);
283
284 EXPECT_EQ(wav_reader.ReadFrame(), 8);
285 std::vector<std::vector<int32_t>> expected_frame = {
286 {0x00010000, static_cast<int32_t>(0xffff0000)},
287 {0x00020000, static_cast<int32_t>(0xfffe0000)},
288 {0x00030000, static_cast<int32_t>(0xfffd0000)},
289 {0x00040000, static_cast<int32_t>(0xfffc0000)},
290 };
291 EXPECT_EQ(wav_reader.buffers_, expected_frame);
292
293 expected_frame = {{0x00050000, static_cast<int32_t>(0xfffb0000)},
294 {0x00060000, static_cast<int32_t>(0xfffa0000)},
295 {0x00070000, static_cast<int32_t>(0xfff90000)},
296 {0x00080000, static_cast<int32_t>(0xfff80000)}};
297 wav_reader.ReadFrame();
298 EXPECT_EQ(wav_reader.buffers_, expected_frame);
299 }
300
TEST(WavReader,OneFrame24BitLittleEndian)301 TEST(WavReader, OneFrame24BitLittleEndian) {
302 const size_t kNumSamplesPerFrame = 2;
303 auto wav_reader =
304 InitAndValidate("stereo_8_samples_48khz_s24le.wav", kNumSamplesPerFrame);
305
306 EXPECT_EQ(wav_reader.ReadFrame(), 4);
307 std::vector<std::vector<int32_t>> expected_frame = {
308 {0x00000100, static_cast<int32_t>(0xffffff00)},
309 {0x00000200, static_cast<int32_t>(0xfffffe00)},
310 };
311
312 EXPECT_EQ(wav_reader.buffers_, expected_frame);
313 }
314
TEST(WavReader,OneFrame32BitLittleEndian)315 TEST(WavReader, OneFrame32BitLittleEndian) {
316 const size_t kNumSamplesPerFrame = 8;
317
318 auto wav_reader =
319 InitAndValidate("sine_1000_16khz_512ms_s32le.wav", kNumSamplesPerFrame);
320
321 EXPECT_EQ(wav_reader.ReadFrame(), 8);
322 std::vector<std::vector<int32_t>> expected_frame = {
323 {0}, {82180641}, {151850024}, {198401618},
324 {214748364}, {198401618}, {151850024}, {82180641},
325 };
326 EXPECT_EQ(wav_reader.buffers_, expected_frame);
327 }
328
TEST(WavReader,OneFrameAdm)329 TEST(WavReader, OneFrameAdm) {
330 const size_t kNumSamplesPerFrame = 1;
331
332 auto adm_file_name = GetAndCleanupOutputFileName(".adm");
333 CreateFileFromStringView(kAdmBwfWithOneStereoAndOneMonoObject, ".adm",
334 adm_file_name);
335
336 auto wav_reader = InitAndValidate(adm_file_name, kNumSamplesPerFrame);
337
338 // Read one frame. The result of n-bit samples are stored in the upper `n`
339 // bits.
340 EXPECT_EQ(wav_reader.ReadFrame(), 3);
341 const std::vector<std::vector<int32_t>> kExpectedFrame = {
342 {0x23010000, 0x67450000, static_cast<int32_t>(0xbbaa0000)}};
343 EXPECT_EQ(wav_reader.buffers_, kExpectedFrame);
344 }
345
TEST(WavReader,IsSafeToCallReadFrameAfterMove)346 TEST(WavReader, IsSafeToCallReadFrameAfterMove) {
347 const size_t kNumSamplesPerFrame = 1;
348 auto wav_reader =
349 InitAndValidate("stereo_8_samples_48khz_s16le.wav", kNumSamplesPerFrame);
350 auto wav_reader_moved = std::move(wav_reader);
351
352 EXPECT_EQ(wav_reader_moved.ReadFrame(), 2);
353 const std::vector<std::vector<int32_t>> kExpectedFrame = {
354 {0x00010000, static_cast<int32_t>(0xffff0000)}};
355 EXPECT_EQ(wav_reader_moved.buffers_, kExpectedFrame);
356 }
357
358 template <typename T>
GetRawBytes(const T & object)359 std::vector<uint8_t> GetRawBytes(const T& object) {
360 std::vector<uint8_t> raw_object(sizeof(object));
361 std::memcpy(raw_object.data(), static_cast<const void*>(&object),
362 raw_object.size());
363 return raw_object;
364 }
365
TEST(WavReader,IsByteEquivalentAfterMoving)366 TEST(WavReader, IsByteEquivalentAfterMoving) {
367 const size_t kNumSamplesPerFrame = 1;
368 auto wav_reader =
369 InitAndValidate("stereo_8_samples_48khz_s16le.wav", kNumSamplesPerFrame);
370 const auto raw_wav_reader_before_move = GetRawBytes(wav_reader);
371
372 const auto wav_reader_moved = std::move(wav_reader);
373 const auto raw_wav_reader_after_move = GetRawBytes(wav_reader_moved);
374
375 EXPECT_EQ(raw_wav_reader_before_move, raw_wav_reader_after_move);
376 }
377
378 } // namespace
379 } // namespace iamf_tools
380