wav_file_unittest.cc (11220B)
1 /* 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "common_audio/wav_file.h" 12 13 #include <cmath> 14 #include <cstddef> 15 #include <cstdint> 16 #include <cstdio> 17 #include <cstring> 18 #include <limits> 19 #include <numbers> 20 #include <string> 21 22 #include "common_audio/wav_header.h" 23 #include "rtc_base/checks.h" 24 #include "rtc_base/logging.h" 25 #include "test/gtest.h" 26 #include "test/testsupport/file_utils.h" 27 28 // WavWriterTest.CPP flaky on Mac. See webrtc:9247. 29 #if defined(WEBRTC_MAC) 30 #define MAYBE_CPP DISABLED_CPP 31 #define MAYBE_CPPReset DISABLED_CPPReset 32 #else 33 #define MAYBE_CPP CPP 34 #define MAYBE_CPPReset CPPReset 35 #endif 36 37 namespace webrtc { 38 39 namespace { 40 const char* SampleFormatToStr(WavFile::SampleFormat format) { 41 switch (format) { 42 case WavFile::SampleFormat::kInt16: 43 return "int16"; 44 case WavFile::SampleFormat::kFloat: 45 return "float"; 46 } 47 RTC_CHECK_NOTREACHED(); 48 } 49 } // namespace 50 51 static const float kSamples[] = {0.0, 10.0, 4e4, -1e9}; 52 53 // Write a tiny WAV file with the C++ interface and verify the result. 54 TEST(WavWriterTest, MAYBE_CPP) { 55 const std::string outfile = 56 test::OutputPathWithRandomDirectory() + "wavtest1.wav"; 57 static const size_t kNumSamples = 3; 58 { 59 WavWriter w(outfile, 14099, 1); 60 EXPECT_EQ(14099, w.sample_rate()); 61 EXPECT_EQ(1u, w.num_channels()); 62 EXPECT_EQ(0u, w.num_samples()); 63 w.WriteSamples(kSamples, kNumSamples); 64 EXPECT_EQ(kNumSamples, w.num_samples()); 65 } 66 // Write some extra "metadata" to the file that should be silently ignored 67 // by WavReader. We don't use WavWriter directly for this because it doesn't 68 // support metadata. 69 static const uint8_t kMetadata[] = {101, 202}; 70 { 71 FILE* f = fopen(outfile.c_str(), "ab"); 72 ASSERT_TRUE(f); 73 ASSERT_EQ(1u, fwrite(kMetadata, sizeof(kMetadata), 1, f)); 74 fclose(f); 75 } 76 static const uint8_t kExpectedContents[] = { 77 // clang-format off 78 // clang formatting doesn't respect inline comments. 79 'R', 'I', 'F', 'F', 80 42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8 81 'W', 'A', 'V', 'E', 82 'f', 'm', 't', ' ', 83 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 84 1, 0, // format: PCM (1) 85 1, 0, // channels: 1 86 0x13, 0x37, 0, 0, // sample rate: 14099 87 0x26, 0x6e, 0, 0, // byte rate: 2 * 14099 88 2, 0, // block align: NumChannels * BytesPerSample 89 16, 0, // bits per sample: 2 * 8 90 'd', 'a', 't', 'a', 91 6, 0, 0, 0, // size of payload: 6 92 0, 0, // first sample: 0.0 93 10, 0, // second sample: 10.0 94 0xff, 0x7f, // third sample: 4e4 (saturated) 95 kMetadata[0], kMetadata[1], 96 // clang-format on 97 }; 98 static const size_t kContentSize = 99 kPcmWavHeaderSize + kNumSamples * sizeof(int16_t) + sizeof(kMetadata); 100 static_assert(sizeof(kExpectedContents) == kContentSize, "content size"); 101 EXPECT_EQ(kContentSize, test::GetFileSize(outfile)); 102 FILE* f = fopen(outfile.c_str(), "rb"); 103 ASSERT_TRUE(f); 104 uint8_t contents[kContentSize]; 105 ASSERT_EQ(1u, fread(contents, kContentSize, 1, f)); 106 EXPECT_EQ(0, fclose(f)); 107 EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize)); 108 109 { 110 WavReader r(outfile); 111 EXPECT_EQ(14099, r.sample_rate()); 112 EXPECT_EQ(1u, r.num_channels()); 113 EXPECT_EQ(kNumSamples, r.num_samples()); 114 static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0}; 115 float samples[kNumSamples]; 116 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples)); 117 EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples))); 118 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples)); 119 } 120 } 121 122 // Write a larger WAV file. You can listen to this file to sanity-check it. 123 TEST(WavWriterTest, LargeFile) { 124 constexpr int kSampleRate = 8000; 125 constexpr size_t kNumChannels = 2; 126 constexpr size_t kNumSamples = 3 * kSampleRate * kNumChannels; 127 for (WavFile::SampleFormat wav_format : 128 {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) { 129 for (WavFile::SampleFormat write_format : 130 {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) { 131 for (WavFile::SampleFormat read_format : 132 {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) { 133 std::string outdir = test::OutputPathWithRandomDirectory(); 134 std::string outfile = outdir + "wavtest3.wav"; 135 float samples[kNumSamples]; 136 for (size_t i = 0; i < kNumSamples; i += kNumChannels) { 137 // A nice periodic beeping sound. 138 static const double kToneHz = 440; 139 const double t = 140 static_cast<double>(i) / (kNumChannels * kSampleRate); 141 const double x = std::numeric_limits<int16_t>::max() * 142 std::sin(t * kToneHz * 2 * std::numbers::pi); 143 samples[i] = std::pow(std::sin(t * 2 * 2 * std::numbers::pi), 10) * x; 144 samples[i + 1] = 145 std::pow(std::cos(t * 2 * 2 * std::numbers::pi), 10) * x; 146 // See https://issues.webrtc.org/issues/379973428 147 RTC_CHECK(std::isfinite(samples[i])); 148 RTC_CHECK(std::isfinite(samples[i + 1])); 149 } 150 { 151 WavWriter w(outfile, kSampleRate, kNumChannels, wav_format); 152 EXPECT_EQ(kSampleRate, w.sample_rate()); 153 EXPECT_EQ(kNumChannels, w.num_channels()); 154 EXPECT_EQ(0u, w.num_samples()); 155 if (write_format == WavFile::SampleFormat::kFloat) { 156 int16_t truncated_samples[kNumSamples]; 157 for (size_t k = 0; k < kNumSamples; ++k) { 158 truncated_samples[k] = static_cast<int16_t>(samples[k]); 159 } 160 w.WriteSamples(truncated_samples, kNumSamples); 161 } else { 162 w.WriteSamples(samples, kNumSamples); 163 } 164 EXPECT_EQ(kNumSamples, w.num_samples()); 165 } 166 if (wav_format == WavFile::SampleFormat::kFloat) { 167 EXPECT_EQ(sizeof(float) * kNumSamples + kIeeeFloatWavHeaderSize, 168 test::GetFileSize(outfile)); 169 } else { 170 EXPECT_EQ(sizeof(int16_t) * kNumSamples + kPcmWavHeaderSize, 171 test::GetFileSize(outfile)); 172 } 173 174 { 175 WavReader r(outfile); 176 EXPECT_EQ(kSampleRate, r.sample_rate()); 177 EXPECT_EQ(kNumChannels, r.num_channels()); 178 EXPECT_EQ(kNumSamples, r.num_samples()); 179 180 if (read_format == WavFile::SampleFormat::kFloat) { 181 float read_samples[kNumSamples]; 182 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, read_samples)); 183 for (size_t i = 0; i < kNumSamples; ++i) { 184 EXPECT_NEAR(samples[i], read_samples[i], 1); 185 if (!std::isfinite(samples[i])) { 186 // See https://issues.webrtc.org/issues/379973428 187 RTC_LOG(LS_ERROR) 188 << "samples[" << i << "] is not finite. " 189 << "wav_format=" << SampleFormatToStr(wav_format) 190 << ", write_format=" << SampleFormatToStr(write_format) 191 << ", read_format=" << SampleFormatToStr(read_format); 192 } 193 } 194 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, read_samples)); 195 } else { 196 int16_t read_samples[kNumSamples]; 197 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, read_samples)); 198 for (size_t i = 0; i < kNumSamples; ++i) { 199 EXPECT_NEAR(samples[i], static_cast<float>(read_samples[i]), 1); 200 if (!std::isfinite(samples[i])) { 201 // See https://issues.webrtc.org/issues/379973428 202 RTC_LOG(LS_ERROR) 203 << "samples[" << i << "] is not finite. " 204 << "wav_format=" << SampleFormatToStr(wav_format) 205 << ", write_format=" << SampleFormatToStr(write_format) 206 << ", read_format=" << SampleFormatToStr(read_format); 207 } 208 } 209 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, read_samples)); 210 } 211 } 212 RTC_CHECK(test::RemoveFile(outfile)); 213 RTC_CHECK(test::RemoveDir(outdir)); 214 } 215 } 216 } 217 } 218 219 // Write a tiny WAV file with the C++ interface then read-reset-read. 220 TEST(WavReaderTest, MAYBE_CPPReset) { 221 const std::string outfile = 222 test::OutputPathWithRandomDirectory() + "wavtest4.wav"; 223 static const size_t kNumSamples = 3; 224 { 225 WavWriter w(outfile, 14099, 1); 226 EXPECT_EQ(14099, w.sample_rate()); 227 EXPECT_EQ(1u, w.num_channels()); 228 EXPECT_EQ(0u, w.num_samples()); 229 w.WriteSamples(kSamples, kNumSamples); 230 EXPECT_EQ(kNumSamples, w.num_samples()); 231 } 232 // Write some extra "metadata" to the file that should be silently ignored 233 // by WavReader. We don't use WavWriter directly for this because it doesn't 234 // support metadata. 235 static const uint8_t kMetadata[] = {101, 202}; 236 { 237 FILE* f = fopen(outfile.c_str(), "ab"); 238 ASSERT_TRUE(f); 239 ASSERT_EQ(1u, fwrite(kMetadata, sizeof(kMetadata), 1, f)); 240 fclose(f); 241 } 242 static const uint8_t kExpectedContents[] = { 243 // clang-format off 244 // clang formatting doesn't respect inline comments. 245 'R', 'I', 'F', 'F', 246 42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8 247 'W', 'A', 'V', 'E', 248 'f', 'm', 't', ' ', 249 16, 0, 0, 0, // size of fmt block - 8: 24 - 8 250 1, 0, // format: PCM (1) 251 1, 0, // channels: 1 252 0x13, 0x37, 0, 0, // sample rate: 14099 253 0x26, 0x6e, 0, 0, // byte rate: 2 * 14099 254 2, 0, // block align: NumChannels * BytesPerSample 255 16, 0, // bits per sample: 2 * 8 256 'd', 'a', 't', 'a', 257 6, 0, 0, 0, // size of payload: 6 258 0, 0, // first sample: 0.0 259 10, 0, // second sample: 10.0 260 0xff, 0x7f, // third sample: 4e4 (saturated) 261 kMetadata[0], kMetadata[1], 262 // clang-format on 263 }; 264 static const size_t kContentSize = 265 kPcmWavHeaderSize + kNumSamples * sizeof(int16_t) + sizeof(kMetadata); 266 static_assert(sizeof(kExpectedContents) == kContentSize, "content size"); 267 EXPECT_EQ(kContentSize, test::GetFileSize(outfile)); 268 FILE* f = fopen(outfile.c_str(), "rb"); 269 ASSERT_TRUE(f); 270 uint8_t contents[kContentSize]; 271 ASSERT_EQ(1u, fread(contents, kContentSize, 1, f)); 272 EXPECT_EQ(0, fclose(f)); 273 EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize)); 274 275 { 276 WavReader r(outfile); 277 EXPECT_EQ(14099, r.sample_rate()); 278 EXPECT_EQ(1u, r.num_channels()); 279 EXPECT_EQ(kNumSamples, r.num_samples()); 280 static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0}; 281 float samples[kNumSamples]; 282 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples)); 283 EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples))); 284 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples)); 285 286 r.Reset(); 287 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples)); 288 EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples))); 289 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples)); 290 } 291 } 292 293 } // namespace webrtc