1 /*
2  *  Copyright (c) 2013 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 "modules/audio_processing/transient/wpd_tree.h"
12 
13 #include <memory>
14 #include <string>
15 
16 #include "modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h"
17 #include "modules/audio_processing/transient/file_utils.h"
18 #include "rtc_base/strings/string_builder.h"
19 #include "rtc_base/system/file_wrapper.h"
20 #include "test/gtest.h"
21 #include "test/testsupport/file_utils.h"
22 
23 namespace webrtc {
24 
TEST(WPDTreeTest,Construction)25 TEST(WPDTreeTest, Construction) {
26   const size_t kTestBufferSize = 100;
27   const int kLevels = 5;
28   const int kExpectedNumberOfNodes = (1 << (kLevels + 1)) - 1;
29 
30   float test_buffer[kTestBufferSize];
31   memset(test_buffer, 0.f, kTestBufferSize * sizeof(*test_buffer));
32   float test_coefficients[] = {1.f, 2.f, 3.f, 4.f, 5.f};
33   const size_t kTestCoefficientsLength =
34       sizeof(test_coefficients) / sizeof(test_coefficients[0]);
35   WPDTree tree(kTestBufferSize, test_coefficients, test_coefficients,
36                kTestCoefficientsLength, kLevels);
37   ASSERT_EQ(kExpectedNumberOfNodes, tree.num_nodes());
38   // Checks for NodeAt(level, index).
39   int nodes_at_level = 0;
40   for (int level = 0; level <= kLevels; ++level) {
41     nodes_at_level = 1 << level;
42     for (int i = 0; i < nodes_at_level; ++i) {
43       ASSERT_TRUE(NULL != tree.NodeAt(level, i));
44     }
45     // Out of bounds.
46     EXPECT_EQ(NULL, tree.NodeAt(level, -1));
47     EXPECT_EQ(NULL, tree.NodeAt(level, -12));
48     EXPECT_EQ(NULL, tree.NodeAt(level, nodes_at_level));
49     EXPECT_EQ(NULL, tree.NodeAt(level, nodes_at_level + 5));
50   }
51   // Out of bounds.
52   EXPECT_EQ(NULL, tree.NodeAt(-1, 0));
53   EXPECT_EQ(NULL, tree.NodeAt(-12, 0));
54   EXPECT_EQ(NULL, tree.NodeAt(kLevels + 1, 0));
55   EXPECT_EQ(NULL, tree.NodeAt(kLevels + 5, 0));
56   // Checks for Update().
57   EXPECT_EQ(0, tree.Update(test_buffer, kTestBufferSize));
58   EXPECT_EQ(-1, tree.Update(NULL, kTestBufferSize));
59   EXPECT_EQ(-1, tree.Update(test_buffer, kTestBufferSize - 1));
60 }
61 
62 // This test is for the correctness of the tree.
63 // Checks the results from the Matlab equivalent, it is done comparing the
64 // results that are stored in the output files from Matlab.
65 // It also writes the results in its own set of files in the out directory.
66 // Matlab and output files contain all the results in double precision (Little
67 // endian) appended.
68 #if defined(WEBRTC_IOS)
TEST(WPDTreeTest,DISABLED_CorrectnessBasedOnMatlabFiles)69 TEST(WPDTreeTest, DISABLED_CorrectnessBasedOnMatlabFiles) {
70 #else
71 TEST(WPDTreeTest, CorrectnessBasedOnMatlabFiles) {
72 #endif
73   // 10 ms at 16000 Hz.
74   const size_t kTestBufferSize = 160;
75   const int kLevels = 3;
76   const int kLeaves = 1 << kLevels;
77   const size_t kLeavesSamples = kTestBufferSize >> kLevels;
78   // Create tree with Discrete Meyer Wavelet Coefficients.
79   WPDTree tree(kTestBufferSize, kDaubechies8HighPassCoefficients,
80                kDaubechies8LowPassCoefficients, kDaubechies8CoefficientsLength,
81                kLevels);
82   // Allocate and open all matlab and out files.
83   FileWrapper matlab_files_data[kLeaves];
84   FileWrapper out_files_data[kLeaves];
85 
86   for (int i = 0; i < kLeaves; ++i) {
87     // Matlab files.
88     rtc::StringBuilder matlab_stream;
89     matlab_stream << "audio_processing/transient/wpd" << i;
90     std::string matlab_string = test::ResourcePath(matlab_stream.str(), "dat");
91     matlab_files_data[i] = FileWrapper::OpenReadOnly(matlab_string.c_str());
92 
93     bool file_opened = matlab_files_data[i].is_open();
94     ASSERT_TRUE(file_opened) << "File could not be opened.\n" << matlab_string;
95 
96     // Out files.
97     rtc::StringBuilder out_stream;
98     out_stream << test::OutputPath() << "wpd_" << i << ".out";
99     std::string out_string = out_stream.str();
100 
101     out_files_data[i] = FileWrapper::OpenWriteOnly(out_string.c_str());
102 
103     file_opened = out_files_data[i].is_open();
104     ASSERT_TRUE(file_opened) << "File could not be opened.\n" << out_string;
105   }
106 
107   // Prepare the test file.
108   std::string test_file_name = test::ResourcePath(
109       "audio_processing/transient/ajm-macbook-1-spke16m", "pcm");
110 
111   FileWrapper test_file = FileWrapper::OpenReadOnly(test_file_name.c_str());
112 
113   bool file_opened = test_file.is_open();
114   ASSERT_TRUE(file_opened) << "File could not be opened.\n" << test_file_name;
115 
116   float test_buffer[kTestBufferSize];
117 
118   // Only the first frames of the audio file are tested. The matlab files also
119   // only contains information about the first frames.
120   const size_t kMaxFramesToTest = 100;
121   const float kTolerance = 0.03f;
122 
123   size_t frames_read = 0;
124 
125   // Read first buffer from the PCM test file.
126   size_t file_samples_read =
127       ReadInt16FromFileToFloatBuffer(&test_file, kTestBufferSize, test_buffer);
128   while (file_samples_read > 0 && frames_read < kMaxFramesToTest) {
129     ++frames_read;
130 
131     if (file_samples_read < kTestBufferSize) {
132       // Pad the rest of the buffer with zeros.
133       for (size_t i = file_samples_read; i < kTestBufferSize; ++i) {
134         test_buffer[i] = 0.0;
135       }
136     }
137     tree.Update(test_buffer, kTestBufferSize);
138     double matlab_buffer[kTestBufferSize];
139 
140     // Compare results with data from the matlab test files.
141     for (int i = 0; i < kLeaves; ++i) {
142       // Compare data values
143       size_t matlab_samples_read = ReadDoubleBufferFromFile(
144           &matlab_files_data[i], kLeavesSamples, matlab_buffer);
145 
146       ASSERT_EQ(kLeavesSamples, matlab_samples_read)
147           << "Matlab test files are malformed.\n"
148              "File: 3_"
149           << i;
150       // Get output data from the corresponding node
151       const float* node_data = tree.NodeAt(kLevels, i)->data();
152       // Compare with matlab files.
153       for (size_t j = 0; j < kLeavesSamples; ++j) {
154         EXPECT_NEAR(matlab_buffer[j], node_data[j], kTolerance)
155             << "\nLeaf: " << i << "\nSample: " << j
156             << "\nFrame: " << frames_read - 1;
157       }
158 
159       // Write results to out files.
160       WriteFloatBufferToFile(&out_files_data[i], kLeavesSamples, node_data);
161     }
162 
163     // Read next buffer from the PCM test file.
164     file_samples_read = ReadInt16FromFileToFloatBuffer(
165         &test_file, kTestBufferSize, test_buffer);
166   }
167 
168   // Close all matlab and out files.
169   for (int i = 0; i < kLeaves; ++i) {
170     matlab_files_data[i].Close();
171     out_files_data[i].Close();
172   }
173 
174   test_file.Close();
175 }
176 
177 }  // namespace webrtc
178