blob: 9b03c3df5fb3f35b1f499ca85dc5b5a013c3e0a2 [file] [log] [blame]
kwiberg@webrtc.org877083c2014-08-20 07:42:461/*
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// MSVC++ requires this to be set before any other includes to get M_PI.
12#define _USE_MATH_DEFINES
13
Jonas Olssona4d87372019-07-05 17:08:3314#include "common_audio/wav_file.h"
15
kwiberg@webrtc.org877083c2014-08-20 07:42:4616#include <cmath>
17#include <limits>
18
Mirko Bonadei92ea95e2017-09-15 04:47:3119#include "common_audio/wav_header.h"
Björn Tereliusc1814322024-11-20 13:30:1720#include "rtc_base/logging.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3121#include "test/gtest.h"
Steve Anton10542f22019-01-11 17:11:0022#include "test/testsupport/file_utils.h"
kwiberg@webrtc.org877083c2014-08-20 07:42:4623
Niels Möller825aad12019-06-27 08:52:1424// WavWriterTest.CPP flaky on Mac. See webrtc:9247.
Alessio Bazzicad5ef6ff2018-05-07 14:33:3625#if defined(WEBRTC_MAC)
26#define MAYBE_CPP DISABLED_CPP
Artem Titov153056b2019-04-16 14:49:3227#define MAYBE_CPPReset DISABLED_CPPReset
Alessio Bazzicad5ef6ff2018-05-07 14:33:3628#else
29#define MAYBE_CPP CPP
Artem Titov153056b2019-04-16 14:49:3230#define MAYBE_CPPReset CPPReset
Alessio Bazzicad5ef6ff2018-05-07 14:33:3631#endif
32
andrew@webrtc.org048c5022014-12-16 20:17:2133namespace webrtc {
34
Björn Tereliusc1814322024-11-20 13:30:1735namespace {
36const char* SampleFormatToStr(WavFile::SampleFormat format) {
37 switch (format) {
38 case WavFile::SampleFormat::kInt16:
39 return "int16";
40 case WavFile::SampleFormat::kFloat:
41 return "float";
42 }
43 RTC_CHECK_NOTREACHED();
44}
45} // namespace
46
kwiberg@webrtc.org877083c2014-08-20 07:42:4647static const float kSamples[] = {0.0, 10.0, 4e4, -1e9};
48
49// Write a tiny WAV file with the C++ interface and verify the result.
Alessio Bazzicad5ef6ff2018-05-07 14:33:3650TEST(WavWriterTest, MAYBE_CPP) {
Dor Hen5ba4f2a2024-02-08 10:47:5451 const std::string outfile =
Dor Hen4efc8302024-02-14 08:07:5652 test::OutputPathWithRandomDirectory() + "wavtest1.wav";
pkasting25702cb2016-01-08 21:50:2753 static const size_t kNumSamples = 3;
kwiberg@webrtc.org877083c2014-08-20 07:42:4654 {
andrew@webrtc.org048c5022014-12-16 20:17:2155 WavWriter w(outfile, 14099, 1);
kwiberg@webrtc.org584cd8d2014-08-25 06:26:0456 EXPECT_EQ(14099, w.sample_rate());
Peter Kasting69558702016-01-13 00:26:3557 EXPECT_EQ(1u, w.num_channels());
kwiberg@webrtc.org584cd8d2014-08-25 06:26:0458 EXPECT_EQ(0u, w.num_samples());
kwiberg@webrtc.org877083c2014-08-20 07:42:4659 w.WriteSamples(kSamples, kNumSamples);
kwiberg@webrtc.org584cd8d2014-08-25 06:26:0460 EXPECT_EQ(kNumSamples, w.num_samples());
kwiberg@webrtc.org877083c2014-08-20 07:42:4661 }
andrew@webrtc.org048c5022014-12-16 20:17:2162 // Write some extra "metadata" to the file that should be silently ignored
63 // by WavReader. We don't use WavWriter directly for this because it doesn't
64 // support metadata.
65 static const uint8_t kMetadata[] = {101, 202};
66 {
67 FILE* f = fopen(outfile.c_str(), "ab");
68 ASSERT_TRUE(f);
69 ASSERT_EQ(1u, fwrite(kMetadata, sizeof(kMetadata), 1, f));
70 fclose(f);
71 }
kwiberg@webrtc.org877083c2014-08-20 07:42:4672 static const uint8_t kExpectedContents[] = {
Yves Gerey665174f2018-06-19 13:03:0573 // clang-format off
74 // clang formatting doesn't respect inline comments.
kwiberg@webrtc.org877083c2014-08-20 07:42:4675 'R', 'I', 'F', 'F',
76 42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8
77 'W', 'A', 'V', 'E',
78 'f', 'm', 't', ' ',
79 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
80 1, 0, // format: PCM (1)
81 1, 0, // channels: 1
82 0x13, 0x37, 0, 0, // sample rate: 14099
83 0x26, 0x6e, 0, 0, // byte rate: 2 * 14099
84 2, 0, // block align: NumChannels * BytesPerSample
85 16, 0, // bits per sample: 2 * 8
86 'd', 'a', 't', 'a',
87 6, 0, 0, 0, // size of payload: 6
88 0, 0, // first sample: 0.0
89 10, 0, // second sample: 10.0
90 0xff, 0x7f, // third sample: 4e4 (saturated)
andrew@webrtc.org048c5022014-12-16 20:17:2191 kMetadata[0], kMetadata[1],
Yves Gerey665174f2018-06-19 13:03:0592 // clang-format on
kwiberg@webrtc.org877083c2014-08-20 07:42:4693 };
pkasting25702cb2016-01-08 21:50:2794 static const size_t kContentSize =
Per Åhgren5dca3f12020-01-28 08:08:1195 kPcmWavHeaderSize + kNumSamples * sizeof(int16_t) + sizeof(kMetadata);
kwiberg@webrtc.org2ebfac52015-01-14 10:51:5496 static_assert(sizeof(kExpectedContents) == kContentSize, "content size");
pkasting25702cb2016-01-08 21:50:2797 EXPECT_EQ(kContentSize, test::GetFileSize(outfile));
kwiberg@webrtc.org877083c2014-08-20 07:42:4698 FILE* f = fopen(outfile.c_str(), "rb");
99 ASSERT_TRUE(f);
100 uint8_t contents[kContentSize];
101 ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
102 EXPECT_EQ(0, fclose(f));
103 EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
andrew@webrtc.orga3ed7132014-10-31 21:51:03104
105 {
andrew@webrtc.org048c5022014-12-16 20:17:21106 WavReader r(outfile);
andrew@webrtc.orga3ed7132014-10-31 21:51:03107 EXPECT_EQ(14099, r.sample_rate());
Peter Kasting69558702016-01-13 00:26:35108 EXPECT_EQ(1u, r.num_channels());
andrew@webrtc.orga3ed7132014-10-31 21:51:03109 EXPECT_EQ(kNumSamples, r.num_samples());
110 static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0};
111 float samples[kNumSamples];
112 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
113 EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
114 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
115 }
kwiberg@webrtc.org877083c2014-08-20 07:42:46116}
117
kwiberg@webrtc.org877083c2014-08-20 07:42:46118// Write a larger WAV file. You can listen to this file to sanity-check it.
119TEST(WavWriterTest, LargeFile) {
Per Åhgren5dca3f12020-01-28 08:08:11120 constexpr int kSampleRate = 8000;
121 constexpr size_t kNumChannels = 2;
122 constexpr size_t kNumSamples = 3 * kSampleRate * kNumChannels;
123 for (WavFile::SampleFormat wav_format :
124 {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) {
125 for (WavFile::SampleFormat write_format :
126 {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) {
127 for (WavFile::SampleFormat read_format :
128 {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) {
Dor Hen4efc8302024-02-14 08:07:56129 std::string outfile =
130 test::OutputPathWithRandomDirectory() + "wavtest3.wav";
Per Åhgren5dca3f12020-01-28 08:08:11131 float samples[kNumSamples];
132 for (size_t i = 0; i < kNumSamples; i += kNumChannels) {
133 // A nice periodic beeping sound.
134 static const double kToneHz = 440;
135 const double t =
136 static_cast<double>(i) / (kNumChannels * kSampleRate);
137 const double x = std::numeric_limits<int16_t>::max() *
138 std::sin(t * kToneHz * 2 * M_PI);
139 samples[i] = std::pow(std::sin(t * 2 * 2 * M_PI), 10) * x;
140 samples[i + 1] = std::pow(std::cos(t * 2 * 2 * M_PI), 10) * x;
Björn Tereliusc1814322024-11-20 13:30:17141 // See https://issues.webrtc.org/issues/379973428
142 RTC_CHECK(isfinite(samples[i]));
143 RTC_CHECK(isfinite(samples[i + 1]));
Per Åhgren5dca3f12020-01-28 08:08:11144 }
145 {
146 WavWriter w(outfile, kSampleRate, kNumChannels, wav_format);
147 EXPECT_EQ(kSampleRate, w.sample_rate());
148 EXPECT_EQ(kNumChannels, w.num_channels());
149 EXPECT_EQ(0u, w.num_samples());
150 if (write_format == WavFile::SampleFormat::kFloat) {
151 float truncated_samples[kNumSamples];
152 for (size_t k = 0; k < kNumSamples; ++k) {
153 truncated_samples[k] = static_cast<int16_t>(samples[k]);
154 }
155 w.WriteSamples(truncated_samples, kNumSamples);
156 } else {
157 w.WriteSamples(samples, kNumSamples);
158 }
159 EXPECT_EQ(kNumSamples, w.num_samples());
160 }
161 if (wav_format == WavFile::SampleFormat::kFloat) {
162 EXPECT_EQ(sizeof(float) * kNumSamples + kIeeeFloatWavHeaderSize,
163 test::GetFileSize(outfile));
164 } else {
165 EXPECT_EQ(sizeof(int16_t) * kNumSamples + kPcmWavHeaderSize,
166 test::GetFileSize(outfile));
167 }
andrew@webrtc.orga3ed7132014-10-31 21:51:03168
Per Åhgren5dca3f12020-01-28 08:08:11169 {
170 WavReader r(outfile);
171 EXPECT_EQ(kSampleRate, r.sample_rate());
172 EXPECT_EQ(kNumChannels, r.num_channels());
173 EXPECT_EQ(kNumSamples, r.num_samples());
andrew@webrtc.orga3ed7132014-10-31 21:51:03174
Per Åhgren5dca3f12020-01-28 08:08:11175 if (read_format == WavFile::SampleFormat::kFloat) {
176 float read_samples[kNumSamples];
177 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, read_samples));
178 for (size_t i = 0; i < kNumSamples; ++i) {
179 EXPECT_NEAR(samples[i], read_samples[i], 1);
Björn Tereliusc1814322024-11-20 13:30:17180 if (!isfinite(samples[i])) {
181 // See https://issues.webrtc.org/issues/379973428
182 RTC_LOG(LS_ERROR)
183 << "samples[" << i << "] is not finite. "
184 << "wav_format=" << SampleFormatToStr(wav_format)
185 << ", write_format=" << SampleFormatToStr(write_format)
186 << ", read_format=" << SampleFormatToStr(read_format);
187 }
Per Åhgren5dca3f12020-01-28 08:08:11188 }
189 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, read_samples));
190 } else {
191 int16_t read_samples[kNumSamples];
192 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, read_samples));
193 for (size_t i = 0; i < kNumSamples; ++i) {
194 EXPECT_NEAR(samples[i], static_cast<float>(read_samples[i]), 1);
Björn Tereliusc1814322024-11-20 13:30:17195 if (!isfinite(samples[i])) {
196 // See https://issues.webrtc.org/issues/379973428
197 RTC_LOG(LS_ERROR)
198 << "samples[" << i << "] is not finite. "
199 << "wav_format=" << SampleFormatToStr(wav_format)
200 << ", write_format=" << SampleFormatToStr(write_format)
201 << ", read_format=" << SampleFormatToStr(read_format);
202 }
Per Åhgren5dca3f12020-01-28 08:08:11203 }
204 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, read_samples));
205 }
206 }
207 }
208 }
andrew@webrtc.orga3ed7132014-10-31 21:51:03209 }
kwiberg@webrtc.org877083c2014-08-20 07:42:46210}
andrew@webrtc.org048c5022014-12-16 20:17:21211
Artem Titov153056b2019-04-16 14:49:32212// Write a tiny WAV file with the C++ interface then read-reset-read.
213TEST(WavReaderTest, MAYBE_CPPReset) {
Dor Hen4efc8302024-02-14 08:07:56214 const std::string outfile =
215 test::OutputPathWithRandomDirectory() + "wavtest4.wav";
Artem Titov153056b2019-04-16 14:49:32216 static const size_t kNumSamples = 3;
217 {
218 WavWriter w(outfile, 14099, 1);
219 EXPECT_EQ(14099, w.sample_rate());
220 EXPECT_EQ(1u, w.num_channels());
221 EXPECT_EQ(0u, w.num_samples());
222 w.WriteSamples(kSamples, kNumSamples);
223 EXPECT_EQ(kNumSamples, w.num_samples());
224 }
225 // Write some extra "metadata" to the file that should be silently ignored
226 // by WavReader. We don't use WavWriter directly for this because it doesn't
227 // support metadata.
228 static const uint8_t kMetadata[] = {101, 202};
229 {
230 FILE* f = fopen(outfile.c_str(), "ab");
231 ASSERT_TRUE(f);
232 ASSERT_EQ(1u, fwrite(kMetadata, sizeof(kMetadata), 1, f));
233 fclose(f);
234 }
235 static const uint8_t kExpectedContents[] = {
236 // clang-format off
237 // clang formatting doesn't respect inline comments.
238 'R', 'I', 'F', 'F',
239 42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8
240 'W', 'A', 'V', 'E',
241 'f', 'm', 't', ' ',
242 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
243 1, 0, // format: PCM (1)
244 1, 0, // channels: 1
245 0x13, 0x37, 0, 0, // sample rate: 14099
246 0x26, 0x6e, 0, 0, // byte rate: 2 * 14099
247 2, 0, // block align: NumChannels * BytesPerSample
248 16, 0, // bits per sample: 2 * 8
249 'd', 'a', 't', 'a',
250 6, 0, 0, 0, // size of payload: 6
251 0, 0, // first sample: 0.0
252 10, 0, // second sample: 10.0
253 0xff, 0x7f, // third sample: 4e4 (saturated)
254 kMetadata[0], kMetadata[1],
255 // clang-format on
256 };
257 static const size_t kContentSize =
Per Åhgren5dca3f12020-01-28 08:08:11258 kPcmWavHeaderSize + kNumSamples * sizeof(int16_t) + sizeof(kMetadata);
Artem Titov153056b2019-04-16 14:49:32259 static_assert(sizeof(kExpectedContents) == kContentSize, "content size");
260 EXPECT_EQ(kContentSize, test::GetFileSize(outfile));
261 FILE* f = fopen(outfile.c_str(), "rb");
262 ASSERT_TRUE(f);
263 uint8_t contents[kContentSize];
264 ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
265 EXPECT_EQ(0, fclose(f));
266 EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
267
268 {
269 WavReader r(outfile);
270 EXPECT_EQ(14099, r.sample_rate());
271 EXPECT_EQ(1u, r.num_channels());
272 EXPECT_EQ(kNumSamples, r.num_samples());
273 static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0};
274 float samples[kNumSamples];
275 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
276 EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
277 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
278
279 r.Reset();
280 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
281 EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
282 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
283 }
284}
285
andrew@webrtc.org048c5022014-12-16 20:17:21286} // namespace webrtc