blob: 71e4fedf941a00e16c9e14383b7a5cca0c3b315b [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"
20#include "test/gtest.h"
Steve Anton10542f22019-01-11 17:11:0021#include "test/testsupport/file_utils.h"
kwiberg@webrtc.org877083c2014-08-20 07:42:4622
Niels Möller825aad12019-06-27 08:52:1423// WavWriterTest.CPP flaky on Mac. See webrtc:9247.
Alessio Bazzicad5ef6ff2018-05-07 14:33:3624#if defined(WEBRTC_MAC)
25#define MAYBE_CPP DISABLED_CPP
Artem Titov153056b2019-04-16 14:49:3226#define MAYBE_CPPReset DISABLED_CPPReset
Alessio Bazzicad5ef6ff2018-05-07 14:33:3627#else
28#define MAYBE_CPP CPP
Artem Titov153056b2019-04-16 14:49:3229#define MAYBE_CPPReset CPPReset
Alessio Bazzicad5ef6ff2018-05-07 14:33:3630#endif
31
andrew@webrtc.org048c5022014-12-16 20:17:2132namespace webrtc {
33
kwiberg@webrtc.org877083c2014-08-20 07:42:4634static const float kSamples[] = {0.0, 10.0, 4e4, -1e9};
35
36// Write a tiny WAV file with the C++ interface and verify the result.
Alessio Bazzicad5ef6ff2018-05-07 14:33:3637TEST(WavWriterTest, MAYBE_CPP) {
Dor Hen5ba4f2a2024-02-08 10:47:5438 const std::string outfile =
Dor Hen4efc8302024-02-14 08:07:5639 test::OutputPathWithRandomDirectory() + "wavtest1.wav";
pkasting25702cb2016-01-08 21:50:2740 static const size_t kNumSamples = 3;
kwiberg@webrtc.org877083c2014-08-20 07:42:4641 {
andrew@webrtc.org048c5022014-12-16 20:17:2142 WavWriter w(outfile, 14099, 1);
kwiberg@webrtc.org584cd8d2014-08-25 06:26:0443 EXPECT_EQ(14099, w.sample_rate());
Peter Kasting69558702016-01-13 00:26:3544 EXPECT_EQ(1u, w.num_channels());
kwiberg@webrtc.org584cd8d2014-08-25 06:26:0445 EXPECT_EQ(0u, w.num_samples());
kwiberg@webrtc.org877083c2014-08-20 07:42:4646 w.WriteSamples(kSamples, kNumSamples);
kwiberg@webrtc.org584cd8d2014-08-25 06:26:0447 EXPECT_EQ(kNumSamples, w.num_samples());
kwiberg@webrtc.org877083c2014-08-20 07:42:4648 }
andrew@webrtc.org048c5022014-12-16 20:17:2149 // Write some extra "metadata" to the file that should be silently ignored
50 // by WavReader. We don't use WavWriter directly for this because it doesn't
51 // support metadata.
52 static const uint8_t kMetadata[] = {101, 202};
53 {
54 FILE* f = fopen(outfile.c_str(), "ab");
55 ASSERT_TRUE(f);
56 ASSERT_EQ(1u, fwrite(kMetadata, sizeof(kMetadata), 1, f));
57 fclose(f);
58 }
kwiberg@webrtc.org877083c2014-08-20 07:42:4659 static const uint8_t kExpectedContents[] = {
Yves Gerey665174f2018-06-19 13:03:0560 // clang-format off
61 // clang formatting doesn't respect inline comments.
kwiberg@webrtc.org877083c2014-08-20 07:42:4662 'R', 'I', 'F', 'F',
63 42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8
64 'W', 'A', 'V', 'E',
65 'f', 'm', 't', ' ',
66 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
67 1, 0, // format: PCM (1)
68 1, 0, // channels: 1
69 0x13, 0x37, 0, 0, // sample rate: 14099
70 0x26, 0x6e, 0, 0, // byte rate: 2 * 14099
71 2, 0, // block align: NumChannels * BytesPerSample
72 16, 0, // bits per sample: 2 * 8
73 'd', 'a', 't', 'a',
74 6, 0, 0, 0, // size of payload: 6
75 0, 0, // first sample: 0.0
76 10, 0, // second sample: 10.0
77 0xff, 0x7f, // third sample: 4e4 (saturated)
andrew@webrtc.org048c5022014-12-16 20:17:2178 kMetadata[0], kMetadata[1],
Yves Gerey665174f2018-06-19 13:03:0579 // clang-format on
kwiberg@webrtc.org877083c2014-08-20 07:42:4680 };
pkasting25702cb2016-01-08 21:50:2781 static const size_t kContentSize =
Per Åhgren5dca3f12020-01-28 08:08:1182 kPcmWavHeaderSize + kNumSamples * sizeof(int16_t) + sizeof(kMetadata);
kwiberg@webrtc.org2ebfac52015-01-14 10:51:5483 static_assert(sizeof(kExpectedContents) == kContentSize, "content size");
pkasting25702cb2016-01-08 21:50:2784 EXPECT_EQ(kContentSize, test::GetFileSize(outfile));
kwiberg@webrtc.org877083c2014-08-20 07:42:4685 FILE* f = fopen(outfile.c_str(), "rb");
86 ASSERT_TRUE(f);
87 uint8_t contents[kContentSize];
88 ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
89 EXPECT_EQ(0, fclose(f));
90 EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
andrew@webrtc.orga3ed7132014-10-31 21:51:0391
92 {
andrew@webrtc.org048c5022014-12-16 20:17:2193 WavReader r(outfile);
andrew@webrtc.orga3ed7132014-10-31 21:51:0394 EXPECT_EQ(14099, r.sample_rate());
Peter Kasting69558702016-01-13 00:26:3595 EXPECT_EQ(1u, r.num_channels());
andrew@webrtc.orga3ed7132014-10-31 21:51:0396 EXPECT_EQ(kNumSamples, r.num_samples());
97 static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0};
98 float samples[kNumSamples];
99 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
100 EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
101 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
102 }
kwiberg@webrtc.org877083c2014-08-20 07:42:46103}
104
kwiberg@webrtc.org877083c2014-08-20 07:42:46105// Write a larger WAV file. You can listen to this file to sanity-check it.
106TEST(WavWriterTest, LargeFile) {
Per Åhgren5dca3f12020-01-28 08:08:11107 constexpr int kSampleRate = 8000;
108 constexpr size_t kNumChannels = 2;
109 constexpr size_t kNumSamples = 3 * kSampleRate * kNumChannels;
110 for (WavFile::SampleFormat wav_format :
111 {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) {
112 for (WavFile::SampleFormat write_format :
113 {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) {
114 for (WavFile::SampleFormat read_format :
115 {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) {
Dor Hen4efc8302024-02-14 08:07:56116 std::string outfile =
117 test::OutputPathWithRandomDirectory() + "wavtest3.wav";
Per Åhgren5dca3f12020-01-28 08:08:11118 float samples[kNumSamples];
119 for (size_t i = 0; i < kNumSamples; i += kNumChannels) {
120 // A nice periodic beeping sound.
121 static const double kToneHz = 440;
122 const double t =
123 static_cast<double>(i) / (kNumChannels * kSampleRate);
124 const double x = std::numeric_limits<int16_t>::max() *
125 std::sin(t * kToneHz * 2 * M_PI);
126 samples[i] = std::pow(std::sin(t * 2 * 2 * M_PI), 10) * x;
127 samples[i + 1] = std::pow(std::cos(t * 2 * 2 * M_PI), 10) * x;
128 }
129 {
130 WavWriter w(outfile, kSampleRate, kNumChannels, wav_format);
131 EXPECT_EQ(kSampleRate, w.sample_rate());
132 EXPECT_EQ(kNumChannels, w.num_channels());
133 EXPECT_EQ(0u, w.num_samples());
134 if (write_format == WavFile::SampleFormat::kFloat) {
135 float truncated_samples[kNumSamples];
136 for (size_t k = 0; k < kNumSamples; ++k) {
137 truncated_samples[k] = static_cast<int16_t>(samples[k]);
138 }
139 w.WriteSamples(truncated_samples, kNumSamples);
140 } else {
141 w.WriteSamples(samples, kNumSamples);
142 }
143 EXPECT_EQ(kNumSamples, w.num_samples());
144 }
145 if (wav_format == WavFile::SampleFormat::kFloat) {
146 EXPECT_EQ(sizeof(float) * kNumSamples + kIeeeFloatWavHeaderSize,
147 test::GetFileSize(outfile));
148 } else {
149 EXPECT_EQ(sizeof(int16_t) * kNumSamples + kPcmWavHeaderSize,
150 test::GetFileSize(outfile));
151 }
andrew@webrtc.orga3ed7132014-10-31 21:51:03152
Per Åhgren5dca3f12020-01-28 08:08:11153 {
154 WavReader r(outfile);
155 EXPECT_EQ(kSampleRate, r.sample_rate());
156 EXPECT_EQ(kNumChannels, r.num_channels());
157 EXPECT_EQ(kNumSamples, r.num_samples());
andrew@webrtc.orga3ed7132014-10-31 21:51:03158
Per Åhgren5dca3f12020-01-28 08:08:11159 if (read_format == WavFile::SampleFormat::kFloat) {
160 float read_samples[kNumSamples];
161 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, read_samples));
162 for (size_t i = 0; i < kNumSamples; ++i) {
163 EXPECT_NEAR(samples[i], read_samples[i], 1);
164 }
165 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, read_samples));
166 } else {
167 int16_t read_samples[kNumSamples];
168 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, read_samples));
169 for (size_t i = 0; i < kNumSamples; ++i) {
170 EXPECT_NEAR(samples[i], static_cast<float>(read_samples[i]), 1);
171 }
172 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, read_samples));
173 }
174 }
175 }
176 }
andrew@webrtc.orga3ed7132014-10-31 21:51:03177 }
kwiberg@webrtc.org877083c2014-08-20 07:42:46178}
andrew@webrtc.org048c5022014-12-16 20:17:21179
Artem Titov153056b2019-04-16 14:49:32180// Write a tiny WAV file with the C++ interface then read-reset-read.
181TEST(WavReaderTest, MAYBE_CPPReset) {
Dor Hen4efc8302024-02-14 08:07:56182 const std::string outfile =
183 test::OutputPathWithRandomDirectory() + "wavtest4.wav";
Artem Titov153056b2019-04-16 14:49:32184 static const size_t kNumSamples = 3;
185 {
186 WavWriter w(outfile, 14099, 1);
187 EXPECT_EQ(14099, w.sample_rate());
188 EXPECT_EQ(1u, w.num_channels());
189 EXPECT_EQ(0u, w.num_samples());
190 w.WriteSamples(kSamples, kNumSamples);
191 EXPECT_EQ(kNumSamples, w.num_samples());
192 }
193 // Write some extra "metadata" to the file that should be silently ignored
194 // by WavReader. We don't use WavWriter directly for this because it doesn't
195 // support metadata.
196 static const uint8_t kMetadata[] = {101, 202};
197 {
198 FILE* f = fopen(outfile.c_str(), "ab");
199 ASSERT_TRUE(f);
200 ASSERT_EQ(1u, fwrite(kMetadata, sizeof(kMetadata), 1, f));
201 fclose(f);
202 }
203 static const uint8_t kExpectedContents[] = {
204 // clang-format off
205 // clang formatting doesn't respect inline comments.
206 'R', 'I', 'F', 'F',
207 42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8
208 'W', 'A', 'V', 'E',
209 'f', 'm', 't', ' ',
210 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
211 1, 0, // format: PCM (1)
212 1, 0, // channels: 1
213 0x13, 0x37, 0, 0, // sample rate: 14099
214 0x26, 0x6e, 0, 0, // byte rate: 2 * 14099
215 2, 0, // block align: NumChannels * BytesPerSample
216 16, 0, // bits per sample: 2 * 8
217 'd', 'a', 't', 'a',
218 6, 0, 0, 0, // size of payload: 6
219 0, 0, // first sample: 0.0
220 10, 0, // second sample: 10.0
221 0xff, 0x7f, // third sample: 4e4 (saturated)
222 kMetadata[0], kMetadata[1],
223 // clang-format on
224 };
225 static const size_t kContentSize =
Per Åhgren5dca3f12020-01-28 08:08:11226 kPcmWavHeaderSize + kNumSamples * sizeof(int16_t) + sizeof(kMetadata);
Artem Titov153056b2019-04-16 14:49:32227 static_assert(sizeof(kExpectedContents) == kContentSize, "content size");
228 EXPECT_EQ(kContentSize, test::GetFileSize(outfile));
229 FILE* f = fopen(outfile.c_str(), "rb");
230 ASSERT_TRUE(f);
231 uint8_t contents[kContentSize];
232 ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
233 EXPECT_EQ(0, fclose(f));
234 EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
235
236 {
237 WavReader r(outfile);
238 EXPECT_EQ(14099, r.sample_rate());
239 EXPECT_EQ(1u, r.num_channels());
240 EXPECT_EQ(kNumSamples, r.num_samples());
241 static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0};
242 float samples[kNumSamples];
243 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
244 EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
245 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
246
247 r.Reset();
248 EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
249 EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
250 EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
251 }
252}
253
andrew@webrtc.org048c5022014-12-16 20:17:21254} // namespace webrtc