Adding SmoothingFilter to audio network adaptor.
BUG=webrtc:6303 Review-Url: https://codereview.webrtc.org/2339523002 Cr-Commit-Position: refs/heads/master@{#14313}
This commit is contained in:
@ -710,6 +710,8 @@ source_set("audio_network_adaptor") {
|
||||
"audio_network_adaptor/dtx_controller.cc",
|
||||
"audio_network_adaptor/dtx_controller.h",
|
||||
"audio_network_adaptor/include/audio_network_adaptor.h",
|
||||
"audio_network_adaptor/smoothing_filter.cc",
|
||||
"audio_network_adaptor/smoothing_filter.h",
|
||||
]
|
||||
configs += [ "../..:common_config" ]
|
||||
public_configs = [ "../..:common_inherited_config" ]
|
||||
|
@ -24,7 +24,9 @@
|
||||
'controller_manager.h',
|
||||
'dtx_controller.h',
|
||||
'dtx_controller.cc',
|
||||
'include/audio_network_adaptor.h'
|
||||
'include/audio_network_adaptor.h',
|
||||
'smoothing_filter.h',
|
||||
'smoothing_filter.cc',
|
||||
], # source
|
||||
},
|
||||
], # targets
|
||||
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
SmoothingFilterImpl::SmoothingFilterImpl(int time_constant_ms,
|
||||
const Clock* clock)
|
||||
: time_constant_ms_(time_constant_ms),
|
||||
clock_(clock),
|
||||
first_sample_received_(false),
|
||||
initialized_(false),
|
||||
first_sample_time_ms_(0),
|
||||
last_sample_time_ms_(0),
|
||||
filter_(0.0) {}
|
||||
|
||||
void SmoothingFilterImpl::AddSample(float sample) {
|
||||
if (!first_sample_received_) {
|
||||
last_sample_time_ms_ = first_sample_time_ms_ = clock_->TimeInMilliseconds();
|
||||
first_sample_received_ = true;
|
||||
RTC_DCHECK_EQ(rtc::ExpFilter::kValueUndefined, filter_.filtered());
|
||||
|
||||
// Since this is first sample, any value for argument 1 should work.
|
||||
filter_.Apply(0.0f, sample);
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
if (!initialized_) {
|
||||
float duration = now_ms - first_sample_time_ms_;
|
||||
if (duration < static_cast<int64_t>(time_constant_ms_)) {
|
||||
filter_.UpdateBase(exp(1.0f / duration));
|
||||
} else {
|
||||
initialized_ = true;
|
||||
filter_.UpdateBase(exp(1.0f / time_constant_ms_));
|
||||
}
|
||||
}
|
||||
|
||||
// The filter will do the following:
|
||||
// float alpha = pow(base, last_update_time_ms_ - now_ms);
|
||||
// filtered_ = alpha * filtered_ + (1 - alpha) * sample;
|
||||
filter_.Apply(static_cast<float>(last_sample_time_ms_ - now_ms), sample);
|
||||
last_sample_time_ms_ = now_ms;
|
||||
}
|
||||
|
||||
rtc::Optional<float> SmoothingFilterImpl::GetAverage() const {
|
||||
float value = filter_.filtered();
|
||||
return value == rtc::ExpFilter::kValueUndefined ? rtc::Optional<float>()
|
||||
: rtc::Optional<float>(value);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_SMOOTHING_FILTER_H_
|
||||
#define WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_SMOOTHING_FILTER_H_
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/base/exp_filter.h"
|
||||
#include "webrtc/base/optional.h"
|
||||
#include "webrtc/system_wrappers/include/clock.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class SmoothingFilter {
|
||||
public:
|
||||
virtual ~SmoothingFilter() = default;
|
||||
virtual void AddSample(float sample) = 0;
|
||||
virtual rtc::Optional<float> GetAverage() const = 0;
|
||||
};
|
||||
|
||||
// SmoothingFilterImpl applies an exponential filter
|
||||
// alpha = exp(-sample_interval / time_constant);
|
||||
// y[t] = alpha * y[t-1] + (1 - alpha) * sample;
|
||||
class SmoothingFilterImpl final : public SmoothingFilter {
|
||||
public:
|
||||
// |time_constant_ms| is the time constant for the exponential filter.
|
||||
SmoothingFilterImpl(int time_constant_ms, const Clock* clock);
|
||||
|
||||
void AddSample(float sample) override;
|
||||
rtc::Optional<float> GetAverage() const override;
|
||||
|
||||
private:
|
||||
const int time_constant_ms_;
|
||||
const Clock* const clock_;
|
||||
|
||||
bool first_sample_received_;
|
||||
bool initialized_;
|
||||
int64_t first_sample_time_ms_;
|
||||
int64_t last_sample_time_ms_;
|
||||
rtc::ExpFilter filter_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(SmoothingFilterImpl);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_SMOOTHING_FILTER_H_
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "webrtc/modules/audio_coding/audio_network_adaptor/smoothing_filter.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kTimeConstantMs = 1000;
|
||||
constexpr float kMaxAbsError = 0.0001f;
|
||||
constexpr int64_t kClockInitialTime = 123456;
|
||||
|
||||
struct SmoothingFilterStates {
|
||||
std::unique_ptr<SimulatedClock> simulated_clock;
|
||||
std::unique_ptr<SmoothingFilter> smoothing_filter;
|
||||
};
|
||||
|
||||
SmoothingFilterStates CreateSmoothingFilter() {
|
||||
SmoothingFilterStates states;
|
||||
states.simulated_clock.reset(new SimulatedClock(kClockInitialTime));
|
||||
states.smoothing_filter.reset(
|
||||
new SmoothingFilterImpl(kTimeConstantMs, states.simulated_clock.get()));
|
||||
return states;
|
||||
}
|
||||
|
||||
void CheckOutput(SmoothingFilterStates* states,
|
||||
int advance_time_ms,
|
||||
float sample,
|
||||
float expected_ouput) {
|
||||
states->simulated_clock->AdvanceTimeMilliseconds(advance_time_ms);
|
||||
states->smoothing_filter->AddSample(sample);
|
||||
auto output = states->smoothing_filter->GetAverage();
|
||||
EXPECT_TRUE(output);
|
||||
EXPECT_NEAR(expected_ouput, *output, kMaxAbsError);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(SmoothingFilterTest, NoOutputWhenNoSampleAdded) {
|
||||
auto states = CreateSmoothingFilter();
|
||||
EXPECT_FALSE(states.smoothing_filter->GetAverage());
|
||||
}
|
||||
|
||||
// Python script to calculate the reference values used in this test.
|
||||
// import math
|
||||
//
|
||||
// class ExpFilter:
|
||||
// alpha = 0.0
|
||||
// old_value = 0.0
|
||||
// def calc(self, new_value):
|
||||
// self.old_value = self.old_value * self.alpha
|
||||
// + (1.0 - self.alpha) * new_value
|
||||
// return self.old_value
|
||||
//
|
||||
// delta_t = 100.0
|
||||
// filter = ExpFilter()
|
||||
// total_t = 100.0
|
||||
// filter.alpha = math.exp(-delta_t/ total_t)
|
||||
// print filter.calc(1.0)
|
||||
// total_t = 200.0
|
||||
// filter.alpha = math.exp(-delta_t/ total_t)
|
||||
// print filter.calc(0.0)
|
||||
// total_t = 300.0
|
||||
// filter.alpha = math.exp(-delta_t/ total_t)
|
||||
// print filter.calc(1.0)
|
||||
TEST(SmoothingFilterTest, CheckBehaviorBeforeInitialized) {
|
||||
// Adding three samples, all added before |kTimeConstantMs| is reached.
|
||||
constexpr int kTimeIntervalMs = 100;
|
||||
auto states = CreateSmoothingFilter();
|
||||
states.smoothing_filter->AddSample(0.0);
|
||||
CheckOutput(&states, kTimeIntervalMs, 1.0, 0.63212f);
|
||||
CheckOutput(&states, kTimeIntervalMs, 0.0, 0.38340f);
|
||||
CheckOutput(&states, kTimeIntervalMs, 1.0, 0.55818f);
|
||||
}
|
||||
|
||||
// Python script to calculate the reference value used in this test.
|
||||
// (after defining ExpFilter as for CheckBehaviorBeforeInitialized)
|
||||
// time_constant_ms = 1000.0
|
||||
// filter = ExpFilter()
|
||||
// delta_t = 1100.0
|
||||
// filter.alpha = math.exp(-delta_t/ time_constant_ms)
|
||||
// print filter.calc(1.0)
|
||||
// delta_t = 100.0
|
||||
// filter.alpha = math.exp(-delta_t/ time_constant_ms)
|
||||
// print filter.calc(0.0)
|
||||
// print filter.calc(1.0)
|
||||
TEST(SmoothingFilterTest, CheckBehaviorAfterInitialized) {
|
||||
constexpr int kTimeIntervalMs = 100;
|
||||
auto states = CreateSmoothingFilter();
|
||||
states.smoothing_filter->AddSample(0.0);
|
||||
states.simulated_clock->AdvanceTimeMilliseconds(kTimeConstantMs);
|
||||
CheckOutput(&states, kTimeIntervalMs, 1.0, 0.66713f);
|
||||
CheckOutput(&states, kTimeIntervalMs, 0.0, 0.60364f);
|
||||
CheckOutput(&states, kTimeIntervalMs, 1.0, 0.64136f);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
Reference in New Issue
Block a user