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#include "webrtc/common_audio/lapped_transform.h" 12 13#include <algorithm> 14#include <cmath> 15#include <cstring> 16 17#include "testing/gtest/include/gtest/gtest.h" 18 19using std::complex; 20 21namespace { 22 23class NoopCallback : public webrtc::LappedTransform::Callback { 24 public: 25 NoopCallback() : block_num_(0) {} 26 27 virtual void ProcessAudioBlock(const complex<float>* const* in_block, 28 size_t in_channels, 29 size_t frames, 30 size_t out_channels, 31 complex<float>* const* out_block) { 32 RTC_CHECK_EQ(in_channels, out_channels); 33 for (size_t i = 0; i < out_channels; ++i) { 34 memcpy(out_block[i], in_block[i], sizeof(**in_block) * frames); 35 } 36 ++block_num_; 37 } 38 39 size_t block_num() { 40 return block_num_; 41 } 42 43 private: 44 size_t block_num_; 45}; 46 47class FftCheckerCallback : public webrtc::LappedTransform::Callback { 48 public: 49 FftCheckerCallback() : block_num_(0) {} 50 51 virtual void ProcessAudioBlock(const complex<float>* const* in_block, 52 size_t in_channels, 53 size_t frames, 54 size_t out_channels, 55 complex<float>* const* out_block) { 56 RTC_CHECK_EQ(in_channels, out_channels); 57 58 size_t full_length = (frames - 1) * 2; 59 ++block_num_; 60 61 if (block_num_ > 0) { 62 ASSERT_NEAR(in_block[0][0].real(), static_cast<float>(full_length), 63 1e-5f); 64 ASSERT_NEAR(in_block[0][0].imag(), 0.0f, 1e-5f); 65 for (size_t i = 1; i < frames; ++i) { 66 ASSERT_NEAR(in_block[0][i].real(), 0.0f, 1e-5f); 67 ASSERT_NEAR(in_block[0][i].imag(), 0.0f, 1e-5f); 68 } 69 } 70 } 71 72 size_t block_num() { 73 return block_num_; 74 } 75 76 private: 77 size_t block_num_; 78}; 79 80void SetFloatArray(float value, int rows, int cols, float* const* array) { 81 for (int i = 0; i < rows; ++i) { 82 for (int j = 0; j < cols; ++j) { 83 array[i][j] = value; 84 } 85 } 86} 87 88} // namespace 89 90namespace webrtc { 91 92TEST(LappedTransformTest, Windowless) { 93 const size_t kChannels = 3; 94 const size_t kChunkLength = 512; 95 const size_t kBlockLength = 64; 96 const size_t kShiftAmount = 64; 97 NoopCallback noop; 98 99 // Rectangular window. 100 float window[kBlockLength]; 101 std::fill(window, &window[kBlockLength], 1.0f); 102 103 LappedTransform trans(kChannels, kChannels, kChunkLength, window, 104 kBlockLength, kShiftAmount, &noop); 105 float in_buffer[kChannels][kChunkLength]; 106 float* in_chunk[kChannels]; 107 float out_buffer[kChannels][kChunkLength]; 108 float* out_chunk[kChannels]; 109 110 in_chunk[0] = in_buffer[0]; 111 in_chunk[1] = in_buffer[1]; 112 in_chunk[2] = in_buffer[2]; 113 out_chunk[0] = out_buffer[0]; 114 out_chunk[1] = out_buffer[1]; 115 out_chunk[2] = out_buffer[2]; 116 SetFloatArray(2.0f, kChannels, kChunkLength, in_chunk); 117 SetFloatArray(-1.0f, kChannels, kChunkLength, out_chunk); 118 119 trans.ProcessChunk(in_chunk, out_chunk); 120 121 for (size_t i = 0; i < kChannels; ++i) { 122 for (size_t j = 0; j < kChunkLength; ++j) { 123 ASSERT_NEAR(out_chunk[i][j], 2.0f, 1e-5f); 124 } 125 } 126 127 ASSERT_EQ(kChunkLength / kBlockLength, noop.block_num()); 128} 129 130TEST(LappedTransformTest, IdentityProcessor) { 131 const size_t kChunkLength = 512; 132 const size_t kBlockLength = 64; 133 const size_t kShiftAmount = 32; 134 NoopCallback noop; 135 136 // Identity window for |overlap = block_size / 2|. 137 float window[kBlockLength]; 138 std::fill(window, &window[kBlockLength], std::sqrt(0.5f)); 139 140 LappedTransform trans(1, 1, kChunkLength, window, kBlockLength, kShiftAmount, 141 &noop); 142 float in_buffer[kChunkLength]; 143 float* in_chunk = in_buffer; 144 float out_buffer[kChunkLength]; 145 float* out_chunk = out_buffer; 146 147 SetFloatArray(2.0f, 1, kChunkLength, &in_chunk); 148 SetFloatArray(-1.0f, 1, kChunkLength, &out_chunk); 149 150 trans.ProcessChunk(&in_chunk, &out_chunk); 151 152 for (size_t i = 0; i < kChunkLength; ++i) { 153 ASSERT_NEAR(out_chunk[i], 154 (i < kBlockLength - kShiftAmount) ? 0.0f : 2.0f, 155 1e-5f); 156 } 157 158 ASSERT_EQ(kChunkLength / kShiftAmount, noop.block_num()); 159} 160 161TEST(LappedTransformTest, Callbacks) { 162 const size_t kChunkLength = 512; 163 const size_t kBlockLength = 64; 164 FftCheckerCallback call; 165 166 // Rectangular window. 167 float window[kBlockLength]; 168 std::fill(window, &window[kBlockLength], 1.0f); 169 170 LappedTransform trans(1, 1, kChunkLength, window, kBlockLength, 171 kBlockLength, &call); 172 float in_buffer[kChunkLength]; 173 float* in_chunk = in_buffer; 174 float out_buffer[kChunkLength]; 175 float* out_chunk = out_buffer; 176 177 SetFloatArray(1.0f, 1, kChunkLength, &in_chunk); 178 SetFloatArray(-1.0f, 1, kChunkLength, &out_chunk); 179 180 trans.ProcessChunk(&in_chunk, &out_chunk); 181 182 ASSERT_EQ(kChunkLength / kBlockLength, call.block_num()); 183} 184 185TEST(LappedTransformTest, chunk_length) { 186 const size_t kBlockLength = 64; 187 FftCheckerCallback call; 188 const float window[kBlockLength] = {}; 189 190 // Make sure that chunk_length returns the same value passed to the 191 // LappedTransform constructor. 192 { 193 const size_t kExpectedChunkLength = 512; 194 const LappedTransform trans(1, 1, kExpectedChunkLength, window, 195 kBlockLength, kBlockLength, &call); 196 197 EXPECT_EQ(kExpectedChunkLength, trans.chunk_length()); 198 } 199 { 200 const size_t kExpectedChunkLength = 160; 201 const LappedTransform trans(1, 1, kExpectedChunkLength, window, 202 kBlockLength, kBlockLength, &call); 203 204 EXPECT_EQ(kExpectedChunkLength, trans.chunk_length()); 205 } 206} 207 208} // namespace webrtc 209