• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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