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