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 // MSVC++ requires this to be set before any other includes to get M_PI.
12 #define _USE_MATH_DEFINES
13 
14 #include "common_audio/wav_file.h"
15 
16 #include <cmath>
17 #include <limits>
18 
19 #include "common_audio/wav_header.h"
20 #include "test/gtest.h"
21 #include "test/testsupport/file_utils.h"
22 
23 // WavWriterTest.CPP flaky on Mac. See webrtc:9247.
24 #if defined(WEBRTC_MAC)
25 #define MAYBE_CPP DISABLED_CPP
26 #define MAYBE_CPPReset DISABLED_CPPReset
27 #else
28 #define MAYBE_CPP CPP
29 #define MAYBE_CPPReset CPPReset
30 #endif
31 
32 namespace webrtc {
33 
34 static const float kSamples[] = {0.0, 10.0, 4e4, -1e9};
35 
36 // Write a tiny WAV file with the C++ interface and verify the result.
TEST(WavWriterTest,MAYBE_CPP)37 TEST(WavWriterTest, MAYBE_CPP) {
38   const std::string outfile = test::OutputPath() + "wavtest1.wav";
39   static const size_t kNumSamples = 3;
40   {
41     WavWriter w(outfile, 14099, 1);
42     EXPECT_EQ(14099, w.sample_rate());
43     EXPECT_EQ(1u, w.num_channels());
44     EXPECT_EQ(0u, w.num_samples());
45     w.WriteSamples(kSamples, kNumSamples);
46     EXPECT_EQ(kNumSamples, w.num_samples());
47   }
48   // Write some extra "metadata" to the file that should be silently ignored
49   // by WavReader. We don't use WavWriter directly for this because it doesn't
50   // support metadata.
51   static const uint8_t kMetadata[] = {101, 202};
52   {
53     FILE* f = fopen(outfile.c_str(), "ab");
54     ASSERT_TRUE(f);
55     ASSERT_EQ(1u, fwrite(kMetadata, sizeof(kMetadata), 1, f));
56     fclose(f);
57   }
58   static const uint8_t kExpectedContents[] = {
59       // clang-format off
60       // clang formatting doesn't respect inline comments.
61     'R', 'I', 'F', 'F',
62     42, 0, 0, 0,  // size of whole file - 8: 6 + 44 - 8
63     'W', 'A', 'V', 'E',
64     'f', 'm', 't', ' ',
65     16, 0, 0, 0,  // size of fmt block - 8: 24 - 8
66     1, 0,  // format: PCM (1)
67     1, 0,  // channels: 1
68     0x13, 0x37, 0, 0,  // sample rate: 14099
69     0x26, 0x6e, 0, 0,  // byte rate: 2 * 14099
70     2, 0,  // block align: NumChannels * BytesPerSample
71     16, 0,  // bits per sample: 2 * 8
72     'd', 'a', 't', 'a',
73     6, 0, 0, 0,  // size of payload: 6
74     0, 0,  // first sample: 0.0
75     10, 0,  // second sample: 10.0
76     0xff, 0x7f,  // third sample: 4e4 (saturated)
77     kMetadata[0], kMetadata[1],
78       // clang-format on
79   };
80   static const size_t kContentSize =
81       kPcmWavHeaderSize + kNumSamples * sizeof(int16_t) + sizeof(kMetadata);
82   static_assert(sizeof(kExpectedContents) == kContentSize, "content size");
83   EXPECT_EQ(kContentSize, test::GetFileSize(outfile));
84   FILE* f = fopen(outfile.c_str(), "rb");
85   ASSERT_TRUE(f);
86   uint8_t contents[kContentSize];
87   ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
88   EXPECT_EQ(0, fclose(f));
89   EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
90 
91   {
92     WavReader r(outfile);
93     EXPECT_EQ(14099, r.sample_rate());
94     EXPECT_EQ(1u, r.num_channels());
95     EXPECT_EQ(kNumSamples, r.num_samples());
96     static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0};
97     float samples[kNumSamples];
98     EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
99     EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
100     EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
101   }
102 }
103 
104 // Write a larger WAV file. You can listen to this file to sanity-check it.
TEST(WavWriterTest,LargeFile)105 TEST(WavWriterTest, LargeFile) {
106   constexpr int kSampleRate = 8000;
107   constexpr size_t kNumChannels = 2;
108   constexpr size_t kNumSamples = 3 * kSampleRate * kNumChannels;
109   for (WavFile::SampleFormat wav_format :
110        {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) {
111     for (WavFile::SampleFormat write_format :
112          {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) {
113       for (WavFile::SampleFormat read_format :
114            {WavFile::SampleFormat::kInt16, WavFile::SampleFormat::kFloat}) {
115         std::string outfile = test::OutputPath() + "wavtest3.wav";
116         float samples[kNumSamples];
117         for (size_t i = 0; i < kNumSamples; i += kNumChannels) {
118           // A nice periodic beeping sound.
119           static const double kToneHz = 440;
120           const double t =
121               static_cast<double>(i) / (kNumChannels * kSampleRate);
122           const double x = std::numeric_limits<int16_t>::max() *
123                            std::sin(t * kToneHz * 2 * M_PI);
124           samples[i] = std::pow(std::sin(t * 2 * 2 * M_PI), 10) * x;
125           samples[i + 1] = std::pow(std::cos(t * 2 * 2 * M_PI), 10) * x;
126         }
127         {
128           WavWriter w(outfile, kSampleRate, kNumChannels, wav_format);
129           EXPECT_EQ(kSampleRate, w.sample_rate());
130           EXPECT_EQ(kNumChannels, w.num_channels());
131           EXPECT_EQ(0u, w.num_samples());
132           if (write_format == WavFile::SampleFormat::kFloat) {
133             float truncated_samples[kNumSamples];
134             for (size_t k = 0; k < kNumSamples; ++k) {
135               truncated_samples[k] = static_cast<int16_t>(samples[k]);
136             }
137             w.WriteSamples(truncated_samples, kNumSamples);
138           } else {
139             w.WriteSamples(samples, kNumSamples);
140           }
141           EXPECT_EQ(kNumSamples, w.num_samples());
142         }
143         if (wav_format == WavFile::SampleFormat::kFloat) {
144           EXPECT_EQ(sizeof(float) * kNumSamples + kIeeeFloatWavHeaderSize,
145                     test::GetFileSize(outfile));
146         } else {
147           EXPECT_EQ(sizeof(int16_t) * kNumSamples + kPcmWavHeaderSize,
148                     test::GetFileSize(outfile));
149         }
150 
151         {
152           WavReader r(outfile);
153           EXPECT_EQ(kSampleRate, r.sample_rate());
154           EXPECT_EQ(kNumChannels, r.num_channels());
155           EXPECT_EQ(kNumSamples, r.num_samples());
156 
157           if (read_format == WavFile::SampleFormat::kFloat) {
158             float read_samples[kNumSamples];
159             EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, read_samples));
160             for (size_t i = 0; i < kNumSamples; ++i) {
161               EXPECT_NEAR(samples[i], read_samples[i], 1);
162             }
163             EXPECT_EQ(0u, r.ReadSamples(kNumSamples, read_samples));
164           } else {
165             int16_t read_samples[kNumSamples];
166             EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, read_samples));
167             for (size_t i = 0; i < kNumSamples; ++i) {
168               EXPECT_NEAR(samples[i], static_cast<float>(read_samples[i]), 1);
169             }
170             EXPECT_EQ(0u, r.ReadSamples(kNumSamples, read_samples));
171           }
172         }
173       }
174     }
175   }
176 }
177 
178 // Write a tiny WAV file with the C++ interface then read-reset-read.
TEST(WavReaderTest,MAYBE_CPPReset)179 TEST(WavReaderTest, MAYBE_CPPReset) {
180   const std::string outfile = test::OutputPath() + "wavtest4.wav";
181   static const size_t kNumSamples = 3;
182   {
183     WavWriter w(outfile, 14099, 1);
184     EXPECT_EQ(14099, w.sample_rate());
185     EXPECT_EQ(1u, w.num_channels());
186     EXPECT_EQ(0u, w.num_samples());
187     w.WriteSamples(kSamples, kNumSamples);
188     EXPECT_EQ(kNumSamples, w.num_samples());
189   }
190   // Write some extra "metadata" to the file that should be silently ignored
191   // by WavReader. We don't use WavWriter directly for this because it doesn't
192   // support metadata.
193   static const uint8_t kMetadata[] = {101, 202};
194   {
195     FILE* f = fopen(outfile.c_str(), "ab");
196     ASSERT_TRUE(f);
197     ASSERT_EQ(1u, fwrite(kMetadata, sizeof(kMetadata), 1, f));
198     fclose(f);
199   }
200   static const uint8_t kExpectedContents[] = {
201       // clang-format off
202       // clang formatting doesn't respect inline comments.
203     'R', 'I', 'F', 'F',
204     42, 0, 0, 0,  // size of whole file - 8: 6 + 44 - 8
205     'W', 'A', 'V', 'E',
206     'f', 'm', 't', ' ',
207     16, 0, 0, 0,  // size of fmt block - 8: 24 - 8
208     1, 0,  // format: PCM (1)
209     1, 0,  // channels: 1
210     0x13, 0x37, 0, 0,  // sample rate: 14099
211     0x26, 0x6e, 0, 0,  // byte rate: 2 * 14099
212     2, 0,  // block align: NumChannels * BytesPerSample
213     16, 0,  // bits per sample: 2 * 8
214     'd', 'a', 't', 'a',
215     6, 0, 0, 0,  // size of payload: 6
216     0, 0,  // first sample: 0.0
217     10, 0,  // second sample: 10.0
218     0xff, 0x7f,  // third sample: 4e4 (saturated)
219     kMetadata[0], kMetadata[1],
220       // clang-format on
221   };
222   static const size_t kContentSize =
223       kPcmWavHeaderSize + kNumSamples * sizeof(int16_t) + sizeof(kMetadata);
224   static_assert(sizeof(kExpectedContents) == kContentSize, "content size");
225   EXPECT_EQ(kContentSize, test::GetFileSize(outfile));
226   FILE* f = fopen(outfile.c_str(), "rb");
227   ASSERT_TRUE(f);
228   uint8_t contents[kContentSize];
229   ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
230   EXPECT_EQ(0, fclose(f));
231   EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
232 
233   {
234     WavReader r(outfile);
235     EXPECT_EQ(14099, r.sample_rate());
236     EXPECT_EQ(1u, r.num_channels());
237     EXPECT_EQ(kNumSamples, r.num_samples());
238     static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0};
239     float samples[kNumSamples];
240     EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
241     EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
242     EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
243 
244     r.Reset();
245     EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
246     EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
247     EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
248   }
249 }
250 
251 }  // namespace webrtc
252