diff --git a/webrtc/common_audio/blocker.cc b/webrtc/common_audio/blocker.cc index 3b92364076..400db0cae8 100644 --- a/webrtc/common_audio/blocker.cc +++ b/webrtc/common_audio/blocker.cc @@ -83,6 +83,16 @@ void ApplyWindow(const float* window, } } +int gcd(int a, int b) { + int tmp; + while (b) { + tmp = a; + a = b; + b = tmp % b; + } + return a; +} + } // namespace namespace webrtc { @@ -98,7 +108,7 @@ Blocker::Blocker(int chunk_size, block_size_(block_size), num_input_channels_(num_input_channels), num_output_channels_(num_output_channels), - initial_delay_(block_size_), + initial_delay_(block_size_ - gcd(chunk_size, shift_amount)), frame_offset_(0), input_buffer_(chunk_size_ + initial_delay_, num_input_channels_), output_buffer_(chunk_size_ + initial_delay_, num_output_channels_), diff --git a/webrtc/common_audio/blocker.h b/webrtc/common_audio/blocker.h index 1cf95dbc38..68289d57b5 100644 --- a/webrtc/common_audio/blocker.h +++ b/webrtc/common_audio/blocker.h @@ -80,8 +80,6 @@ class Blocker { const int num_output_channels_; // The number of frames of delay to add at the beginning of the first chunk. - // - // TODO(claguna): find a lower cap for this than |block_size_|. const int initial_delay_; // The frame index into the input buffer where the first block should be read diff --git a/webrtc/common_audio/blocker_unittest.cc b/webrtc/common_audio/blocker_unittest.cc index 5009c0eed8..d1bfa6981f 100644 --- a/webrtc/common_audio/blocker_unittest.cc +++ b/webrtc/common_audio/blocker_unittest.cc @@ -15,7 +15,7 @@ namespace { // Callback Function to add 3 to every sample in the signal. -class SimpleBlockerCallback : public webrtc::BlockerCallback { +class PlusThreeBlockerCallback : public webrtc::BlockerCallback { public: virtual void ProcessBlock(const float* const* input, int num_frames, @@ -30,6 +30,22 @@ class SimpleBlockerCallback : public webrtc::BlockerCallback { } }; +// No-op Callback Function. +class CopyBlockerCallback : public webrtc::BlockerCallback { + public: + virtual void ProcessBlock(const float* const* input, + int num_frames, + int num_input_channels, + int num_output_channels, + float* const* output) OVERRIDE { + for (int i = 0; i < num_output_channels; ++i) { + for (int j = 0; j < num_frames; ++j) { + output[i][j] = input[i][j]; + } + } + } +}; + } // namespace namespace webrtc { @@ -75,6 +91,21 @@ class BlockerTest : public ::testing::Test { } } + void ValidateInitialDelay(const float* const* output, + int num_channels, + int num_frames, + int initial_delay) { + for (int i = 0; i < num_channels; ++i) { + for (int j = 0; j < num_frames; ++j) { + if (j < initial_delay) { + EXPECT_FLOAT_EQ(output[i][j], 0.f); + } else { + EXPECT_GT(output[i][j], 0.f); + } + } + } + } + static void CopyTo(float* const* dst, int start_index_dst, int start_index_src, @@ -104,8 +135,8 @@ TEST_F(BlockerTest, TestBlockerMutuallyPrimeChunkandBlockSize) { const ChannelBuffer input_cb(kInput[0], kNumFrames, kNumInputChannels); const float kExpectedOutput[kNumInputChannels][kNumFrames] = { - {6, 6, 12, 12, 20, 20, 20, 20, 20, 20}, - {6, 6, 12, 12, 28, 28, 28, 28, 28, 28}}; + {6, 6, 12, 20, 20, 20, 20, 20, 20, 20}, + {6, 6, 12, 28, 28, 28, 28, 28, 28, 28}}; const ChannelBuffer expected_output_cb( kExpectedOutput[0], kNumFrames, kNumInputChannels); @@ -115,7 +146,7 @@ TEST_F(BlockerTest, TestBlockerMutuallyPrimeChunkandBlockSize) { ChannelBuffer input_chunk_cb(kChunkSize, kNumInputChannels); ChannelBuffer output_chunk_cb(kChunkSize, kNumOutputChannels); - SimpleBlockerCallback callback; + PlusThreeBlockerCallback callback; Blocker blocker(kChunkSize, kBlockSize, kNumInputChannels, @@ -154,11 +185,11 @@ TEST_F(BlockerTest, TestBlockerMutuallyPrimeShiftAndBlockSize) { {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}; const ChannelBuffer input_cb(kInput[0], kNumFrames, kNumInputChannels); - const float kExpectedOutput[kNumInputChannels][kNumFrames] = { - {6, 6, 6, 12, 10, 10, 20, 10, 10, 20, 10, 10}, - {6, 6, 6, 12, 14, 14, 28, 14, 14, 28, 14, 14}}; + const float kExpectedOutput[kNumOutputChannels][kNumFrames] = { + {6, 10, 10, 20, 10, 10, 20, 10, 10, 20, 10, 10}, + {6, 14, 14, 28, 14, 14, 28, 14, 14, 28, 14, 14}}; const ChannelBuffer expected_output_cb( - kExpectedOutput[0], kNumFrames, kNumInputChannels); + kExpectedOutput[0], kNumFrames, kNumOutputChannels); const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f}; @@ -166,7 +197,7 @@ TEST_F(BlockerTest, TestBlockerMutuallyPrimeShiftAndBlockSize) { ChannelBuffer input_chunk_cb(kChunkSize, kNumInputChannels); ChannelBuffer output_chunk_cb(kChunkSize, kNumOutputChannels); - SimpleBlockerCallback callback; + PlusThreeBlockerCallback callback; Blocker blocker(kChunkSize, kBlockSize, kNumInputChannels, @@ -205,11 +236,11 @@ TEST_F(BlockerTest, TestBlockerNoOverlap) { {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}; const ChannelBuffer input_cb(kInput[0], kNumFrames, kNumInputChannels); - const float kExpectedOutput[kNumInputChannels][kNumFrames] = { - {6, 6, 6, 6, 10, 10, 10, 10, 10, 10, 10, 10}, - {6, 6, 6, 6, 14, 14, 14, 14, 14, 14, 14, 14}}; + const float kExpectedOutput[kNumOutputChannels][kNumFrames] = { + {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}}; const ChannelBuffer expected_output_cb( - kExpectedOutput[0], kNumFrames, kNumInputChannels); + kExpectedOutput[0], kNumFrames, kNumOutputChannels); const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f}; @@ -217,7 +248,7 @@ TEST_F(BlockerTest, TestBlockerNoOverlap) { ChannelBuffer input_chunk_cb(kChunkSize, kNumInputChannels); ChannelBuffer output_chunk_cb(kChunkSize, kNumOutputChannels); - SimpleBlockerCallback callback; + PlusThreeBlockerCallback callback; Blocker blocker(kChunkSize, kBlockSize, kNumInputChannels, @@ -242,4 +273,63 @@ TEST_F(BlockerTest, TestBlockerNoOverlap) { kNumFrames); } +TEST_F(BlockerTest, InitialDelaysAreMinimum) { + const int kNumInputChannels = 3; + const int kNumOutputChannels = 2; + const int kNumFrames = 1280; + const int kChunkSize[] = + {80, 80, 80, 80, 80, 80, 160, 160, 160, 160, 160, 160}; + const int kBlockSize[] = + {64, 64, 64, 128, 128, 128, 128, 128, 128, 256, 256, 256}; + const int kShiftAmount[] = + {16, 32, 64, 32, 64, 128, 32, 64, 128, 64, 128, 256}; + const int kInitialDelay[] = + {48, 48, 48, 112, 112, 112, 96, 96, 96, 224, 224, 224}; + + float input[kNumInputChannels][kNumFrames]; + for (int i = 0; i < kNumInputChannels; ++i) { + for (int j = 0; j < kNumFrames; ++j) { + input[i][j] = i + 1; + } + } + const ChannelBuffer input_cb(input[0], kNumFrames, kNumInputChannels); + + ChannelBuffer output_cb(kNumFrames, kNumOutputChannels); + + CopyBlockerCallback callback; + + for (size_t i = 0; i < (sizeof(kChunkSize) / sizeof(*kChunkSize)); ++i) { + scoped_ptr window(new float[kBlockSize[i]]); + for (int j = 0; j < kBlockSize[i]; ++j) { + window[j] = 1.f; + } + + ChannelBuffer input_chunk_cb(kChunkSize[i], kNumInputChannels); + ChannelBuffer output_chunk_cb(kChunkSize[i], kNumOutputChannels); + + Blocker blocker(kChunkSize[i], + kBlockSize[i], + kNumInputChannels, + kNumOutputChannels, + window.get(), + kShiftAmount[i], + &callback); + + RunTest(&blocker, + kChunkSize[i], + kNumFrames, + input_cb.channels(), + input_chunk_cb.channels(), + output_cb.channels(), + output_chunk_cb.channels(), + kNumInputChannels, + kNumOutputChannels); + + ValidateInitialDelay(output_cb.channels(), + kNumOutputChannels, + kNumFrames, + kInitialDelay[i]); + } +} + } // namespace webrtc diff --git a/webrtc/common_audio/lapped_transform_unittest.cc b/webrtc/common_audio/lapped_transform_unittest.cc index 905238229f..4b521fad58 100644 --- a/webrtc/common_audio/lapped_transform_unittest.cc +++ b/webrtc/common_audio/lapped_transform_unittest.cc @@ -53,12 +53,7 @@ class FftCheckerCallback : public webrtc::LappedTransform::Callback { float full_length = (frames - 1) * 2; ++block_num_; - if (block_num_ == 1) { - for (int i = 0; i < frames; ++i) { - ASSERT_NEAR(in_block[0][i].real(), 0.0f, 1e-5f); - ASSERT_NEAR(in_block[0][i].imag(), 0.0f, 1e-5f); - } - } else { + if (block_num_ > 0) { ASSERT_NEAR(in_block[0][0].real(), full_length, 1e-5f); ASSERT_NEAR(in_block[0][0].imag(), 0.0f, 1e-5f); for (int i = 1; i < frames; ++i) { @@ -114,7 +109,7 @@ TEST(LappedTransformTest, Windowless) { for (int i = 0; i < kChannels; ++i) { for (int j = 0; j < kChunkLength; ++j) { - ASSERT_NEAR(out_chunk[i][j], (j < kBlockLength) ? 0.0f : 2.0f, 1e-5f); + ASSERT_NEAR(out_chunk[i][j], 2.0f, 1e-5f); } } @@ -145,7 +140,9 @@ TEST(LappedTransformTest, IdentityProcessor) { trans.ProcessChunk(&in_chunk, &out_chunk); for (int i = 0; i < kChunkLength; ++i) { - ASSERT_NEAR(out_chunk[i], (i < kBlockLength) ? 0.0f : 2.0f, 1e-5f); + ASSERT_NEAR(out_chunk[i], + (i < kBlockLength - kShiftAmount) ? 0.0f : 2.0f, + 1e-5f); } ASSERT_EQ(kChunkLength / kShiftAmount, noop.block_num());