Add RMS computation for the RTP level indicator.
- Compute RMS over a packet's worth of audio to be sent in Channel, rather than the captured audio in TransmitMixer.
- We now use the entire packet rather than the last 10 ms frame.
- Restore functionality to LevelEstimator.
- Fix a bug in the splitting filter.
- Fix a number of bugs in process_test related to a poorly named
AudioFrame member.
- Update the unittest protobuf and float reference output.
- Add audioproc unittests.
- Reenable voe_extended_tests, and add a real function test.
- Use correct minimum level of 127.
TEST=audioproc_unittest, audioproc, voe_extended_test, voe_auto_test
Review URL: http://webrtc-codereview.appspot.com/279003
git-svn-id: http://webrtc.googlecode.com/svn/trunk@950 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/src/modules/audio_conference_mixer/source/audio_frame_manipulator.cc b/src/modules/audio_conference_mixer/source/audio_frame_manipulator.cc
index 3ae48b2..8716454 100644
--- a/src/modules/audio_conference_mixer/source/audio_frame_manipulator.cc
+++ b/src/modules/audio_conference_mixer/source/audio_frame_manipulator.cc
@@ -49,6 +49,7 @@
for(int position = 0; position < audioFrame._payloadDataLengthInSamples;
position++)
{
+ // TODO(andrew): this can easily overflow.
audioFrame._energy += audioFrame._payloadData[position] *
audioFrame._payloadData[position];
}
diff --git a/src/modules/audio_processing/audio_buffer.cc b/src/modules/audio_processing/audio_buffer.cc
index f7c55b4..a7fb04d 100644
--- a/src/modules/audio_processing/audio_buffer.cc
+++ b/src/modules/audio_processing/audio_buffer.cc
@@ -10,6 +10,8 @@
#include "audio_buffer.h"
+#include "signal_processing_library.h"
+
namespace webrtc {
namespace {
@@ -19,18 +21,14 @@
kSamplesPer32kHzChannel = 320
};
-void StereoToMono(const WebRtc_Word16* left, const WebRtc_Word16* right,
- WebRtc_Word16* out, int samples_per_channel) {
- WebRtc_Word32 data_int32 = 0;
+void StereoToMono(const int16_t* left, const int16_t* right,
+ int16_t* out, int samples_per_channel) {
+ assert(left != NULL && right != NULL && out != NULL);
for (int i = 0; i < samples_per_channel; i++) {
- data_int32 = (left[i] + right[i]) >> 1;
- if (data_int32 > 32767) {
- data_int32 = 32767;
- } else if (data_int32 < -32768) {
- data_int32 = -32768;
- }
+ int32_t data32 = (static_cast<int32_t>(left[i]) +
+ static_cast<int32_t>(right[i])) >> 1;
- out[i] = static_cast<WebRtc_Word16>(data_int32);
+ out[i] = WebRtcSpl_SatW32ToW16(data32);
}
}
} // namespace
@@ -40,7 +38,7 @@
memset(data, 0, sizeof(data));
}
- WebRtc_Word16 data[kSamplesPer32kHzChannel];
+ int16_t data[kSamplesPer32kHzChannel];
};
struct SplitAudioChannel {
@@ -53,8 +51,8 @@
memset(synthesis_filter_state2, 0, sizeof(synthesis_filter_state2));
}
- WebRtc_Word16 low_pass_data[kSamplesPer16kHzChannel];
- WebRtc_Word16 high_pass_data[kSamplesPer16kHzChannel];
+ int16_t low_pass_data[kSamplesPer16kHzChannel];
+ int16_t high_pass_data[kSamplesPer16kHzChannel];
WebRtc_Word32 analysis_filter_state1[6];
WebRtc_Word32 analysis_filter_state2[6];
@@ -69,46 +67,34 @@
num_channels_(0),
num_mixed_channels_(0),
num_mixed_low_pass_channels_(0),
+ data_was_mixed_(false),
samples_per_channel_(samples_per_channel),
samples_per_split_channel_(samples_per_channel),
reference_copied_(false),
activity_(AudioFrame::kVadUnknown),
+ is_muted_(false),
data_(NULL),
channels_(NULL),
split_channels_(NULL),
+ mixed_channels_(NULL),
mixed_low_pass_channels_(NULL),
low_pass_reference_channels_(NULL) {
if (max_num_channels_ > 1) {
- channels_ = new AudioChannel[max_num_channels_];
- mixed_low_pass_channels_ = new AudioChannel[max_num_channels_];
+ channels_.reset(new AudioChannel[max_num_channels_]);
+ mixed_channels_.reset(new AudioChannel[max_num_channels_]);
+ mixed_low_pass_channels_.reset(new AudioChannel[max_num_channels_]);
}
- low_pass_reference_channels_ = new AudioChannel[max_num_channels_];
+ low_pass_reference_channels_.reset(new AudioChannel[max_num_channels_]);
if (samples_per_channel_ == kSamplesPer32kHzChannel) {
- split_channels_ = new SplitAudioChannel[max_num_channels_];
+ split_channels_.reset(new SplitAudioChannel[max_num_channels_]);
samples_per_split_channel_ = kSamplesPer16kHzChannel;
}
}
-AudioBuffer::~AudioBuffer() {
- if (channels_ != NULL) {
- delete [] channels_;
- }
+AudioBuffer::~AudioBuffer() {}
- if (mixed_low_pass_channels_ != NULL) {
- delete [] mixed_low_pass_channels_;
- }
-
- if (low_pass_reference_channels_ != NULL) {
- delete [] low_pass_reference_channels_;
- }
-
- if (split_channels_ != NULL) {
- delete [] split_channels_;
- }
-}
-
-WebRtc_Word16* AudioBuffer::data(int channel) const {
+int16_t* AudioBuffer::data(int channel) const {
assert(channel >= 0 && channel < num_channels_);
if (data_ != NULL) {
return data_;
@@ -117,31 +103,37 @@
return channels_[channel].data;
}
-WebRtc_Word16* AudioBuffer::low_pass_split_data(int channel) const {
+int16_t* AudioBuffer::low_pass_split_data(int channel) const {
assert(channel >= 0 && channel < num_channels_);
- if (split_channels_ == NULL) {
+ if (split_channels_.get() == NULL) {
return data(channel);
}
return split_channels_[channel].low_pass_data;
}
-WebRtc_Word16* AudioBuffer::high_pass_split_data(int channel) const {
+int16_t* AudioBuffer::high_pass_split_data(int channel) const {
assert(channel >= 0 && channel < num_channels_);
- if (split_channels_ == NULL) {
+ if (split_channels_.get() == NULL) {
return NULL;
}
return split_channels_[channel].high_pass_data;
}
-WebRtc_Word16* AudioBuffer::mixed_low_pass_data(int channel) const {
+int16_t* AudioBuffer::mixed_data(int channel) const {
+ assert(channel >= 0 && channel < num_mixed_channels_);
+
+ return mixed_channels_[channel].data;
+}
+
+int16_t* AudioBuffer::mixed_low_pass_data(int channel) const {
assert(channel >= 0 && channel < num_mixed_low_pass_channels_);
return mixed_low_pass_channels_[channel].data;
}
-WebRtc_Word16* AudioBuffer::low_pass_reference(int channel) const {
+int16_t* AudioBuffer::low_pass_reference(int channel) const {
assert(channel >= 0 && channel < num_channels_);
if (!reference_copied_) {
return NULL;
@@ -174,10 +166,14 @@
activity_ = activity;
}
-AudioFrame::VADActivity AudioBuffer::activity() {
+AudioFrame::VADActivity AudioBuffer::activity() const {
return activity_;
}
+bool AudioBuffer::is_muted() const {
+ return is_muted_;
+}
+
int AudioBuffer::num_channels() const {
return num_channels_;
}
@@ -196,10 +192,15 @@
assert(frame->_payloadDataLengthInSamples == samples_per_channel_);
num_channels_ = frame->_audioChannel;
+ data_was_mixed_ = false;
num_mixed_channels_ = 0;
num_mixed_low_pass_channels_ = 0;
reference_copied_ = false;
activity_ = frame->_vadActivity;
+ is_muted_ = false;
+ if (frame->_energy == 0) {
+ is_muted_ = true;
+ }
if (num_channels_ == 1) {
// We can get away with a pointer assignment in this case.
@@ -207,9 +208,9 @@
return;
}
- WebRtc_Word16* interleaved = frame->_payloadData;
+ int16_t* interleaved = frame->_payloadData;
for (int i = 0; i < num_channels_; i++) {
- WebRtc_Word16* deinterleaved = channels_[i].data;
+ int16_t* deinterleaved = channels_[i].data;
int interleaved_idx = i;
for (int j = 0; j < samples_per_channel_; j++) {
deinterleaved[j] = interleaved[interleaved_idx];
@@ -218,16 +219,20 @@
}
}
-void AudioBuffer::InterleaveTo(AudioFrame* frame) const {
+void AudioBuffer::InterleaveTo(AudioFrame* frame, bool data_changed) const {
assert(frame->_audioChannel == num_channels_);
assert(frame->_payloadDataLengthInSamples == samples_per_channel_);
frame->_vadActivity = activity_;
+ if (!data_changed) {
+ return;
+ }
+
if (num_channels_ == 1) {
- if (num_mixed_channels_ == 1) {
+ if (data_was_mixed_) {
memcpy(frame->_payloadData,
channels_[0].data,
- sizeof(WebRtc_Word16) * samples_per_channel_);
+ sizeof(int16_t) * samples_per_channel_);
} else {
// These should point to the same buffer in this case.
assert(data_ == frame->_payloadData);
@@ -236,9 +241,9 @@
return;
}
- WebRtc_Word16* interleaved = frame->_payloadData;
+ int16_t* interleaved = frame->_payloadData;
for (int i = 0; i < num_channels_; i++) {
- WebRtc_Word16* deinterleaved = channels_[i].data;
+ int16_t* deinterleaved = channels_[i].data;
int interleaved_idx = i;
for (int j = 0; j < samples_per_channel_; j++) {
interleaved[interleaved_idx] = deinterleaved[j];
@@ -261,6 +266,19 @@
samples_per_channel_);
num_channels_ = num_mixed_channels;
+ data_was_mixed_ = true;
+}
+
+void AudioBuffer::CopyAndMix(int num_mixed_channels) {
+ // We currently only support the stereo to mono case.
+ assert(num_channels_ == 2);
+ assert(num_mixed_channels == 1);
+
+ StereoToMono(channels_[0].data,
+ channels_[1].data,
+ mixed_channels_[0].data,
+ samples_per_channel_);
+
num_mixed_channels_ = num_mixed_channels;
}
@@ -282,7 +300,7 @@
for (int i = 0; i < num_channels_; i++) {
memcpy(low_pass_reference_channels_[i].data,
low_pass_split_data(i),
- sizeof(WebRtc_Word16) * samples_per_split_channel_);
+ sizeof(int16_t) * samples_per_split_channel_);
}
}
} // namespace webrtc
diff --git a/src/modules/audio_processing/audio_buffer.h b/src/modules/audio_processing/audio_buffer.h
index 1bdd3c7..87d6972 100644
--- a/src/modules/audio_processing/audio_buffer.h
+++ b/src/modules/audio_processing/audio_buffer.h
@@ -12,6 +12,7 @@
#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_BUFFER_H_
#include "module_common_types.h"
+#include "scoped_ptr.h"
#include "typedefs.h"
namespace webrtc {
@@ -28,23 +29,30 @@
int samples_per_channel() const;
int samples_per_split_channel() const;
- WebRtc_Word16* data(int channel) const;
- WebRtc_Word16* low_pass_split_data(int channel) const;
- WebRtc_Word16* high_pass_split_data(int channel) const;
- WebRtc_Word16* mixed_low_pass_data(int channel) const;
- WebRtc_Word16* low_pass_reference(int channel) const;
+ int16_t* data(int channel) const;
+ int16_t* low_pass_split_data(int channel) const;
+ int16_t* high_pass_split_data(int channel) const;
+ int16_t* mixed_data(int channel) const;
+ int16_t* mixed_low_pass_data(int channel) const;
+ int16_t* low_pass_reference(int channel) const;
- WebRtc_Word32* analysis_filter_state1(int channel) const;
- WebRtc_Word32* analysis_filter_state2(int channel) const;
- WebRtc_Word32* synthesis_filter_state1(int channel) const;
- WebRtc_Word32* synthesis_filter_state2(int channel) const;
+ int32_t* analysis_filter_state1(int channel) const;
+ int32_t* analysis_filter_state2(int channel) const;
+ int32_t* synthesis_filter_state1(int channel) const;
+ int32_t* synthesis_filter_state2(int channel) const;
void set_activity(AudioFrame::VADActivity activity);
- AudioFrame::VADActivity activity();
+ AudioFrame::VADActivity activity() const;
+
+ bool is_muted() const;
void DeinterleaveFrom(AudioFrame* audioFrame);
void InterleaveTo(AudioFrame* audioFrame) const;
+ // If |data_changed| is false, only the non-audio data members will be copied
+ // to |frame|.
+ void InterleaveTo(AudioFrame* frame, bool data_changed) const;
void Mix(int num_mixed_channels);
+ void CopyAndMix(int num_mixed_channels);
void CopyAndMixLowPass(int num_mixed_channels);
void CopyLowPassToReference();
@@ -53,18 +61,21 @@
int num_channels_;
int num_mixed_channels_;
int num_mixed_low_pass_channels_;
+ // Whether the original data was replaced with mixed data.
+ bool data_was_mixed_;
const int samples_per_channel_;
int samples_per_split_channel_;
bool reference_copied_;
AudioFrame::VADActivity activity_;
+ bool is_muted_;
- WebRtc_Word16* data_;
- // TODO(andrew): use vectors here.
- AudioChannel* channels_;
- SplitAudioChannel* split_channels_;
+ int16_t* data_;
+ scoped_array<AudioChannel> channels_;
+ scoped_array<SplitAudioChannel> split_channels_;
+ scoped_array<AudioChannel> mixed_channels_;
// TODO(andrew): improve this, we don't need the full 32 kHz space here.
- AudioChannel* mixed_low_pass_channels_;
- AudioChannel* low_pass_reference_channels_;
+ scoped_array<AudioChannel> mixed_low_pass_channels_;
+ scoped_array<AudioChannel> low_pass_reference_channels_;
};
} // namespace webrtc
diff --git a/src/modules/audio_processing/audio_processing_impl.cc b/src/modules/audio_processing/audio_processing_impl.cc
index da8dcdb..4828ba8 100644
--- a/src/modules/audio_processing/audio_processing_impl.cc
+++ b/src/modules/audio_processing/audio_processing_impl.cc
@@ -271,7 +271,7 @@
if (debug_file_->Open()) {
event_msg_->set_type(audioproc::Event::STREAM);
audioproc::Stream* msg = event_msg_->mutable_stream();
- const size_t data_size = sizeof(WebRtc_Word16) *
+ const size_t data_size = sizeof(int16_t) *
frame->_payloadDataLengthInSamples *
frame->_audioChannel;
msg->set_input_data(frame->_payloadData, data_size);
@@ -285,12 +285,12 @@
// TODO(ajm): experiment with mixing and AEC placement.
if (num_output_channels_ < num_input_channels_) {
capture_audio_->Mix(num_output_channels_);
-
frame->_audioChannel = num_output_channels_;
}
- if (sample_rate_hz_ == kSampleRate32kHz) {
- for (int i = 0; i < num_input_channels_; i++) {
+ bool data_changed = stream_data_changed();
+ if (analysis_needed(data_changed)) {
+ for (int i = 0; i < num_output_channels_; i++) {
// Split into a low and high band.
SplittingFilterAnalysis(capture_audio_->data(i),
capture_audio_->low_pass_split_data(i),
@@ -340,12 +340,7 @@
return err;
}
- //err = level_estimator_->ProcessCaptureAudio(capture_audio_);
- //if (err != kNoError) {
- // return err;
- //}
-
- if (sample_rate_hz_ == kSampleRate32kHz) {
+ if (synthesis_needed(data_changed)) {
for (int i = 0; i < num_output_channels_; i++) {
// Recombine low and high bands.
SplittingFilterSynthesis(capture_audio_->low_pass_split_data(i),
@@ -356,11 +351,17 @@
}
}
- capture_audio_->InterleaveTo(frame);
+ // The level estimator operates on the recombined data.
+ err = level_estimator_->ProcessStream(capture_audio_);
+ if (err != kNoError) {
+ return err;
+ }
+
+ capture_audio_->InterleaveTo(frame, data_changed);
if (debug_file_->Open()) {
audioproc::Stream* msg = event_msg_->mutable_stream();
- const size_t data_size = sizeof(WebRtc_Word16) *
+ const size_t data_size = sizeof(int16_t) *
frame->_payloadDataLengthInSamples *
frame->_audioChannel;
msg->set_output_data(frame->_payloadData, data_size);
@@ -396,7 +397,7 @@
if (debug_file_->Open()) {
event_msg_->set_type(audioproc::Event::REVERSE_STREAM);
audioproc::ReverseStream* msg = event_msg_->mutable_reverse_stream();
- const size_t data_size = sizeof(WebRtc_Word16) *
+ const size_t data_size = sizeof(int16_t) *
frame->_payloadDataLengthInSamples *
frame->_audioChannel;
msg->set_data(frame->_payloadData, data_size);
@@ -436,11 +437,6 @@
return err;
}
- //err = level_estimator_->AnalyzeReverseStream(render_audio_);
- //if (err != kNoError) {
- // return err;
- //}
-
was_stream_delay_set_ = false;
return err; // TODO(ajm): this is for returning warnings; necessary?
}
@@ -648,4 +644,44 @@
return kNoError;
}
+
+bool AudioProcessingImpl::stream_data_changed() const {
+ int enabled_count = 0;
+ std::list<ProcessingComponent*>::const_iterator it;
+ for (it = component_list_.begin(); it != component_list_.end(); it++) {
+ if ((*it)->is_component_enabled()) {
+ enabled_count++;
+ }
+ }
+
+ // Data is unchanged if no components are enabled, or if only level_estimator_
+ // or voice_detection_ is enabled.
+ if (enabled_count == 0) {
+ return false;
+ } else if (enabled_count == 1) {
+ if (level_estimator_->is_enabled() || voice_detection_->is_enabled()) {
+ return false;
+ }
+ } else if (enabled_count == 2) {
+ if (level_estimator_->is_enabled() && voice_detection_->is_enabled()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AudioProcessingImpl::synthesis_needed(bool stream_data_changed) const {
+ return (stream_data_changed && sample_rate_hz_ == kSampleRate32kHz);
+}
+
+bool AudioProcessingImpl::analysis_needed(bool stream_data_changed) const {
+ if (!stream_data_changed && !voice_detection_->is_enabled()) {
+ // Only level_estimator_ is enabled.
+ return false;
+ } else if (sample_rate_hz_ == kSampleRate32kHz) {
+ // Something besides level_estimator_ is enabled, and we have super-wb.
+ return true;
+ }
+ return false;
+}
} // namespace webrtc
diff --git a/src/modules/audio_processing/audio_processing_impl.h b/src/modules/audio_processing/audio_processing_impl.h
index fc35937..61bc904 100644
--- a/src/modules/audio_processing/audio_processing_impl.h
+++ b/src/modules/audio_processing/audio_processing_impl.h
@@ -81,6 +81,9 @@
private:
int WriteMessageToDebugFile();
int WriteInitMessage();
+ bool stream_data_changed() const;
+ bool synthesis_needed(bool stream_data_changed) const;
+ bool analysis_needed(bool stream_data_changed) const;
int id_;
diff --git a/src/modules/audio_processing/interface/audio_processing.h b/src/modules/audio_processing/interface/audio_processing.h
index 87d539f..e263696 100644
--- a/src/modules/audio_processing/interface/audio_processing.h
+++ b/src/modules/audio_processing/interface/audio_processing.h
@@ -496,27 +496,23 @@
};
// An estimation component used to retrieve level metrics.
-// NOTE: currently unavailable. All methods return errors.
class LevelEstimator {
public:
virtual int Enable(bool enable) = 0;
virtual bool is_enabled() const = 0;
- // The metrics are reported in dBFs calculated as:
- // Level = 10log_10(P_s / P_max) [dBFs], where
- // P_s is the signal power and P_max is the maximum possible (or peak)
- // power. With 16-bit signals, P_max = (2^15)^2.
- struct Metrics {
- AudioProcessing::Statistic signal; // Overall signal level.
- AudioProcessing::Statistic speech; // Speech level.
- AudioProcessing::Statistic noise; // Noise level.
- };
-
- virtual int GetMetrics(Metrics* metrics, Metrics* reverse_metrics) = 0;
-
- //virtual int enable_noise_warning(bool enable) = 0;
- //bool is_noise_warning_enabled() const = 0;
- //virtual bool stream_has_high_noise() const = 0;
+ // Returns the root mean square (RMS) level in dBFs (decibels from digital
+ // full-scale), or alternately dBov. It is computed over all primary stream
+ // frames since the last call to RMS(). The returned value is positive but
+ // should be interpreted as negative. It is constrained to [0, 127].
+ //
+ // The computation follows:
+ // http://tools.ietf.org/html/draft-ietf-avtext-client-to-mixer-audio-level-05
+ // with the intent that it can provide the RTP audio level indication.
+ //
+ // Frames passed to ProcessStream() with an |_energy| of zero are considered
+ // to have been muted. The RMS of the frame will be interpreted as -127.
+ virtual int RMS() = 0;
protected:
virtual ~LevelEstimator() {};
diff --git a/src/modules/audio_processing/level_estimator_impl.cc b/src/modules/audio_processing/level_estimator_impl.cc
index 799a962..f127d4a 100644
--- a/src/modules/audio_processing/level_estimator_impl.cc
+++ b/src/modules/audio_processing/level_estimator_impl.cc
@@ -10,73 +10,78 @@
#include "level_estimator_impl.h"
-#include <cassert>
-#include <cstring>
-
-#include "critical_section_wrapper.h"
+#include <assert.h>
+#include <math.h>
+#include <string.h>
#include "audio_processing_impl.h"
#include "audio_buffer.h"
-
-// TODO(ajm): implement the underlying level estimator component.
+#include "critical_section_wrapper.h"
namespace webrtc {
-
-typedef void Handle;
-
namespace {
-/*int EstimateLevel(AudioBuffer* audio, Handle* my_handle) {
- assert(audio->samples_per_split_channel() <= 160);
- WebRtc_Word16* mixed_data = audio->low_pass_split_data(0);
- if (audio->num_channels() > 1) {
- audio->CopyAndMixLowPass(1);
- mixed_data = audio->mixed_low_pass_data(0);
+const double kMaxSquaredLevel = 32768.0 * 32768.0;
+
+class Level {
+ public:
+ static const int kMinLevel = 127;
+
+ Level()
+ : sum_square_(0.0),
+ sample_count_(0) {}
+ ~Level() {}
+
+ void Init() {
+ sum_square_ = 0.0;
+ sample_count_ = 0;
}
- int err = UpdateLvlEst(my_handle,
- mixed_data,
- audio->samples_per_split_channel());
- if (err != AudioProcessing::kNoError) {
- return GetHandleError(my_handle);
+ void Process(int16_t* data, int length) {
+ assert(data != NULL);
+ assert(length > 0);
+ sum_square_ += SumSquare(data, length);
+ sample_count_ += length;
}
- return AudioProcessing::kNoError;
-}
-
-int GetMetricsLocal(Handle* my_handle, LevelEstimator::Metrics* metrics) {
- level_t levels;
- memset(&levels, 0, sizeof(levels));
-
- int err = ExportLevels(my_handle, &levels, 2);
- if (err != AudioProcessing::kNoError) {
- return err;
+ void ProcessMuted(int length) {
+ assert(length > 0);
+ sample_count_ += length;
}
- metrics->signal.instant = levels.instant;
- metrics->signal.average = levels.average;
- metrics->signal.maximum = levels.max;
- metrics->signal.minimum = levels.min;
- err = ExportLevels(my_handle, &levels, 1);
- if (err != AudioProcessing::kNoError) {
- return err;
+ int RMS() {
+ if (sample_count_ == 0 || sum_square_ == 0.0) {
+ Init();
+ return kMinLevel;
+ }
+
+ // Normalize by the max level.
+ double rms = sum_square_ / (sample_count_ * kMaxSquaredLevel);
+ // 20log_10(x^0.5) = 10log_10(x)
+ rms = 10 * log10(rms);
+ if (rms > 0)
+ rms = 0;
+ else if (rms < -kMinLevel)
+ rms = -kMinLevel;
+
+ rms = -rms;
+ Init();
+ return static_cast<int>(rms + 0.5);
}
- metrics->speech.instant = levels.instant;
- metrics->speech.average = levels.average;
- metrics->speech.maximum = levels.max;
- metrics->speech.minimum = levels.min;
- err = ExportLevels(my_handle, &levels, 0);
- if (err != AudioProcessing::kNoError) {
- return err;
+ private:
+ static double SumSquare(int16_t* data, int length) {
+ double sum_square = 0.0;
+ for (int i = 0; i < length; ++i) {
+ double data_d = static_cast<double>(data[i]);
+ sum_square += data_d * data_d;
+ }
+ return sum_square;
}
- metrics->noise.instant = levels.instant;
- metrics->noise.average = levels.average;
- metrics->noise.maximum = levels.max;
- metrics->noise.minimum = levels.min;
- return AudioProcessing::kNoError;
-}*/
+ double sum_square_;
+ int sample_count_;
+};
} // namespace
LevelEstimatorImpl::LevelEstimatorImpl(const AudioProcessingImpl* apm)
@@ -85,52 +90,44 @@
LevelEstimatorImpl::~LevelEstimatorImpl() {}
-int LevelEstimatorImpl::AnalyzeReverseStream(AudioBuffer* /*audio*/) {
- return apm_->kUnsupportedComponentError;
- /*if (!is_component_enabled()) {
+int LevelEstimatorImpl::ProcessStream(AudioBuffer* audio) {
+ if (!is_component_enabled()) {
return apm_->kNoError;
}
- return EstimateLevel(audio, static_cast<Handle*>(handle(1)));*/
-}
-
-int LevelEstimatorImpl::ProcessCaptureAudio(AudioBuffer* /*audio*/) {
- return apm_->kUnsupportedComponentError;
- /*if (!is_component_enabled()) {
+ Level* level = static_cast<Level*>(handle(0));
+ if (audio->is_muted()) {
+ level->ProcessMuted(audio->samples_per_channel());
return apm_->kNoError;
}
- return EstimateLevel(audio, static_cast<Handle*>(handle(0)));*/
+ int16_t* mixed_data = audio->data(0);
+ if (audio->num_channels() > 1) {
+ audio->CopyAndMix(1);
+ mixed_data = audio->mixed_data(0);
+ }
+
+ level->Process(mixed_data, audio->samples_per_channel());
+
+ return apm_->kNoError;
}
-int LevelEstimatorImpl::Enable(bool /*enable*/) {
+int LevelEstimatorImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(*apm_->crit());
- return apm_->kUnsupportedComponentError;
- //return EnableComponent(enable);
+ return EnableComponent(enable);
}
bool LevelEstimatorImpl::is_enabled() const {
return is_component_enabled();
}
-int LevelEstimatorImpl::GetMetrics(LevelEstimator::Metrics* /*metrics*/,
- LevelEstimator::Metrics* /*reverse_metrics*/) {
- return apm_->kUnsupportedComponentError;
- /*if (!is_component_enabled()) {
+int LevelEstimatorImpl::RMS() {
+ if (!is_component_enabled()) {
return apm_->kNotEnabledError;
}
- int err = GetMetricsLocal(static_cast<Handle*>(handle(0)), metrics);
- if (err != apm_->kNoError) {
- return err;
- }
-
- err = GetMetricsLocal(static_cast<Handle*>(handle(1)), reverse_metrics);
- if (err != apm_->kNoError) {
- return err;
- }
-
- return apm_->kNoError;*/
+ Level* level = static_cast<Level*>(handle(0));
+ return level->RMS();
}
int LevelEstimatorImpl::get_version(char* version,
@@ -141,37 +138,30 @@
}
void* LevelEstimatorImpl::CreateHandle() const {
- Handle* handle = NULL;
- /*if (CreateLvlEst(&handle) != apm_->kNoError) {
- handle = NULL;
- } else {
- assert(handle != NULL);
- }*/
-
- return handle;
+ return new Level;
}
-int LevelEstimatorImpl::DestroyHandle(void* /*handle*/) const {
- return apm_->kUnsupportedComponentError;
- //return FreeLvlEst(static_cast<Handle*>(handle));
+int LevelEstimatorImpl::DestroyHandle(void* handle) const {
+ assert(handle != NULL);
+ Level* level = static_cast<Level*>(handle);
+ delete level;
+ return apm_->kNoError;
}
-int LevelEstimatorImpl::InitializeHandle(void* /*handle*/) const {
- return apm_->kUnsupportedComponentError;
- /*const double kIntervalSeconds = 1.5;
- return InitLvlEst(static_cast<Handle*>(handle),
- apm_->sample_rate_hz(),
- kIntervalSeconds);*/
+int LevelEstimatorImpl::InitializeHandle(void* handle) const {
+ assert(handle != NULL);
+ Level* level = static_cast<Level*>(handle);
+ level->Init();
+
+ return apm_->kNoError;
}
int LevelEstimatorImpl::ConfigureHandle(void* /*handle*/) const {
- return apm_->kUnsupportedComponentError;
- //return apm_->kNoError;
+ return apm_->kNoError;
}
int LevelEstimatorImpl::num_handles_required() const {
- return apm_->kUnsupportedComponentError;
- //return 2;
+ return 1;
}
int LevelEstimatorImpl::GetHandleError(void* handle) const {
diff --git a/src/modules/audio_processing/level_estimator_impl.h b/src/modules/audio_processing/level_estimator_impl.h
index 1515722..c9b7e02 100644
--- a/src/modules/audio_processing/level_estimator_impl.h
+++ b/src/modules/audio_processing/level_estimator_impl.h
@@ -24,8 +24,7 @@
explicit LevelEstimatorImpl(const AudioProcessingImpl* apm);
virtual ~LevelEstimatorImpl();
- int AnalyzeReverseStream(AudioBuffer* audio);
- int ProcessCaptureAudio(AudioBuffer* audio);
+ int ProcessStream(AudioBuffer* audio);
// LevelEstimator implementation.
virtual bool is_enabled() const;
@@ -36,7 +35,7 @@
private:
// LevelEstimator implementation.
virtual int Enable(bool enable);
- virtual int GetMetrics(Metrics* metrics, Metrics* reverse_metrics);
+ virtual int RMS();
// ProcessingComponent implementation.
virtual void* CreateHandle() const;
diff --git a/src/modules/audio_processing/processing_component.h b/src/modules/audio_processing/processing_component.h
index 3d8a02b..3af0c4d 100644
--- a/src/modules/audio_processing/processing_component.h
+++ b/src/modules/audio_processing/processing_component.h
@@ -18,16 +18,6 @@
namespace webrtc {
class AudioProcessingImpl;
-/*template <class T>
-class ComponentHandle {
- public:
- ComponentHandle();
- virtual ~ComponentHandle();
-
- virtual int Create() = 0;
- virtual T* ptr() const = 0;
-};*/
-
class ProcessingComponent {
public:
explicit ProcessingComponent(const AudioProcessingImpl* apm);
@@ -37,10 +27,11 @@
virtual int Destroy();
virtual int get_version(char* version, int version_len_bytes) const = 0;
+ bool is_component_enabled() const;
+
protected:
virtual int Configure();
int EnableComponent(bool enable);
- bool is_component_enabled() const;
void* handle(int index) const;
int num_handles() const;
diff --git a/src/modules/audio_processing/test/process_test.cc b/src/modules/audio_processing/test/process_test.cc
index aede7b7..f88f920 100644
--- a/src/modules/audio_processing/test/process_test.cc
+++ b/src/modules/audio_processing/test/process_test.cc
@@ -117,6 +117,8 @@
printf(" --ns_very_high\n");
printf("\n -vad Voice activity detection\n");
printf(" --vad_out_file FILE\n");
+ printf("\n Level metrics (enabled by default)\n");
+ printf(" --no_level_metrics\n");
printf("\n");
printf("Modifiers:\n");
printf(" --noasm Disable SSE optimization.\n");
@@ -171,6 +173,7 @@
int extra_delay_ms = 0;
//bool interleaved = true;
+ ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(true));
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-pb") == 0) {
i++;
@@ -250,6 +253,9 @@
ASSERT_EQ(apm->kNoError,
apm->echo_cancellation()->enable_delay_logging(false));
+ } else if (strcmp(argv[i], "--no_level_metrics") == 0) {
+ ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(false));
+
} else if (strcmp(argv[i], "-aecm") == 0) {
ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
@@ -454,16 +460,16 @@
ASSERT_TRUE(NULL != out_file) << "Unable to open output audio file "
<< out_filename;
- int near_size_samples = 0;
+ int near_size_bytes = 0;
if (pb_file) {
struct stat st;
stat(pb_filename, &st);
// Crude estimate, but should be good enough.
- near_size_samples = st.st_size / 3 / sizeof(int16_t);
+ near_size_bytes = st.st_size / 3;
} else {
struct stat st;
stat(near_filename, &st);
- near_size_samples = st.st_size / sizeof(int16_t);
+ near_size_bytes = st.st_size;
}
if (apm->voice_detection()->is_enabled()) {
@@ -500,14 +506,11 @@
size_t read_count = 0;
int reverse_count = 0;
int primary_count = 0;
- int near_read_samples = 0;
+ int near_read_bytes = 0;
TickInterval acc_ticks;
AudioFrame far_frame;
- far_frame._frequencyInHz = sample_rate_hz;
-
AudioFrame near_frame;
- near_frame._frequencyInHz = sample_rate_hz;
int delay_ms = 0;
int drift_samples = 0;
@@ -556,14 +559,19 @@
samples_per_channel = msg.sample_rate() / 100;
far_frame._frequencyInHz = msg.sample_rate();
- far_frame._payloadDataLengthInSamples =
- msg.num_reverse_channels() * samples_per_channel;
+ far_frame._payloadDataLengthInSamples = samples_per_channel;
+ far_frame._audioChannel = msg.num_reverse_channels();
near_frame._frequencyInHz = msg.sample_rate();
+ near_frame._payloadDataLengthInSamples = samples_per_channel;
if (verbose) {
printf("Init at frame: %d (primary), %d (reverse)\n",
primary_count, reverse_count);
printf(" Sample rate: %d Hz\n", sample_rate_hz);
+ printf(" Primary channels: %d (in), %d (out)\n",
+ msg.num_input_channels(),
+ msg.num_output_channels());
+ printf(" Reverse channels: %d \n", msg.num_reverse_channels());
}
} else if (event_msg.type() == Event::REVERSE_STREAM) {
@@ -572,8 +580,8 @@
reverse_count++;
ASSERT_TRUE(msg.has_data());
- ASSERT_EQ(sizeof(int16_t) * far_frame._payloadDataLengthInSamples,
- msg.data().size());
+ ASSERT_EQ(sizeof(int16_t) * samples_per_channel *
+ far_frame._audioChannel, msg.data().size());
memcpy(far_frame._payloadData, msg.data().data(), msg.data().size());
if (perf_testing) {
@@ -600,21 +608,20 @@
const Stream msg = event_msg.stream();
primary_count++;
+ // ProcessStream could have changed this for the output frame.
near_frame._audioChannel = apm->num_input_channels();
- near_frame._payloadDataLengthInSamples =
- apm->num_input_channels() * samples_per_channel;
ASSERT_TRUE(msg.has_input_data());
- ASSERT_EQ(sizeof(int16_t) * near_frame._payloadDataLengthInSamples,
- msg.input_data().size());
+ ASSERT_EQ(sizeof(int16_t) * samples_per_channel *
+ near_frame._audioChannel, msg.input_data().size());
memcpy(near_frame._payloadData,
msg.input_data().data(),
msg.input_data().size());
- near_read_samples += near_frame._payloadDataLengthInSamples;
+ near_read_bytes += msg.input_data().size();
if (progress && primary_count % 100 == 0) {
printf("%.0f%% complete\r",
- (near_read_samples * 100.0) / near_size_samples);
+ (near_read_bytes * 100.0) / near_size_bytes);
fflush(stdout);
}
@@ -635,6 +642,7 @@
}
ASSERT_TRUE(err == apm->kNoError ||
err == apm->kBadStreamParameterWarning);
+ ASSERT_TRUE(near_frame._audioChannel == apm->num_output_channels());
capture_level = apm->gain_control()->stream_analog_level();
@@ -663,11 +671,11 @@
}
}
- ASSERT_EQ(near_frame._payloadDataLengthInSamples,
- fwrite(near_frame._payloadData,
- sizeof(int16_t),
- near_frame._payloadDataLengthInSamples,
- out_file));
+ size_t size = samples_per_channel * near_frame._audioChannel;
+ ASSERT_EQ(size, fwrite(near_frame._payloadData,
+ sizeof(int16_t),
+ size,
+ out_file));
}
}
@@ -704,6 +712,12 @@
}
}
+ far_frame._frequencyInHz = sample_rate_hz;
+ far_frame._payloadDataLengthInSamples = samples_per_channel;
+ far_frame._audioChannel = num_render_channels;
+ near_frame._frequencyInHz = sample_rate_hz;
+ near_frame._payloadDataLengthInSamples = samples_per_channel;
+
if (event == kInitializeEvent || event == kResetEventDeprecated) {
ASSERT_EQ(1u,
fread(&sample_rate_hz, sizeof(sample_rate_hz), 1, event_file));
@@ -723,7 +737,10 @@
device_sample_rate_hz));
far_frame._frequencyInHz = sample_rate_hz;
+ far_frame._payloadDataLengthInSamples = samples_per_channel;
+ far_frame._audioChannel = num_render_channels;
near_frame._frequencyInHz = sample_rate_hz;
+ near_frame._payloadDataLengthInSamples = samples_per_channel;
if (verbose) {
printf("Init at frame: %d (primary), %d (reverse)\n",
@@ -733,26 +750,23 @@
} else if (event == kRenderEvent) {
reverse_count++;
- far_frame._audioChannel = num_render_channels;
- far_frame._payloadDataLengthInSamples =
- num_render_channels * samples_per_channel;
+ size_t size = samples_per_channel * num_render_channels;
read_count = fread(far_frame._payloadData,
- sizeof(WebRtc_Word16),
- far_frame._payloadDataLengthInSamples,
+ sizeof(int16_t),
+ size,
far_file);
if (simulating) {
- if (read_count != far_frame._payloadDataLengthInSamples) {
+ if (read_count != size) {
// Read an equal amount from the near file to avoid errors due to
// not reaching end-of-file.
- EXPECT_EQ(0, fseek(near_file, read_count * sizeof(WebRtc_Word16),
+ EXPECT_EQ(0, fseek(near_file, read_count * sizeof(int16_t),
SEEK_CUR));
break; // This is expected.
}
} else {
- ASSERT_EQ(read_count,
- far_frame._payloadDataLengthInSamples);
+ ASSERT_EQ(size, read_count);
}
if (perf_testing) {
@@ -777,30 +791,28 @@
} else if (event == kCaptureEvent) {
primary_count++;
near_frame._audioChannel = num_capture_input_channels;
- near_frame._payloadDataLengthInSamples =
- num_capture_input_channels * samples_per_channel;
+ size_t size = samples_per_channel * num_capture_input_channels;
read_count = fread(near_frame._payloadData,
- sizeof(WebRtc_Word16),
- near_frame._payloadDataLengthInSamples,
+ sizeof(int16_t),
+ size,
near_file);
- near_read_samples += read_count;
+ near_read_bytes += read_count * sizeof(int16_t);
if (progress && primary_count % 100 == 0) {
printf("%.0f%% complete\r",
- (near_read_samples * 100.0) / near_size_samples);
+ (near_read_bytes * 100.0) / near_size_bytes);
fflush(stdout);
}
if (simulating) {
- if (read_count != near_frame._payloadDataLengthInSamples) {
+ if (read_count != size) {
break; // This is expected.
}
delay_ms = 0;
drift_samples = 0;
} else {
- ASSERT_EQ(read_count,
- near_frame._payloadDataLengthInSamples);
+ ASSERT_EQ(size, read_count);
// TODO(ajm): sizeof(delay_ms) for current files?
ASSERT_EQ(1u,
@@ -829,6 +841,7 @@
}
ASSERT_TRUE(err == apm->kNoError ||
err == apm->kBadStreamParameterWarning);
+ ASSERT_TRUE(near_frame._audioChannel == apm->num_output_channels());
capture_level = apm->gain_control()->stream_analog_level();
@@ -857,11 +870,11 @@
}
}
- ASSERT_EQ(near_frame._payloadDataLengthInSamples,
- fwrite(near_frame._payloadData,
- sizeof(WebRtc_Word16),
- near_frame._payloadDataLengthInSamples,
- out_file));
+ size = samples_per_channel * near_frame._audioChannel;
+ ASSERT_EQ(size, fwrite(near_frame._payloadData,
+ sizeof(int16_t),
+ size,
+ out_file));
}
else {
FAIL() << "Event " << event << " is unrecognized";
@@ -887,6 +900,10 @@
printf("\nProcessed frames: %d (primary), %d (reverse)\n",
primary_count, reverse_count);
+ if (apm->level_estimator()->is_enabled()) {
+ printf("\n--Level metrics--\n");
+ printf("RMS: %d dBFS\n", -apm->level_estimator()->RMS());
+ }
if (apm->echo_cancellation()->are_metrics_enabled()) {
EchoCancellation::Metrics metrics;
apm->echo_cancellation()->GetMetrics(&metrics);
diff --git a/src/modules/audio_processing/test/unit_test.cc b/src/modules/audio_processing/test/unit_test.cc
index 54f9251..0d8b5ec 100644
--- a/src/modules/audio_processing/test/unit_test.cc
+++ b/src/modules/audio_processing/test/unit_test.cc
@@ -45,26 +45,25 @@
// be set to true with the command-line switch --write_output_data.
bool write_output_data = false;
-class ApmEnvironment : public ::testing::Environment {
- public:
- virtual void SetUp() {
- Trace::CreateTrace();
- ASSERT_EQ(0, Trace::SetTraceFile("apm_trace.txt"));
- }
-
- virtual void TearDown() {
- Trace::ReturnTrace();
- }
-};
-
class ApmTest : public ::testing::Test {
protected:
ApmTest();
virtual void SetUp();
virtual void TearDown();
+
+ static void SetUpTestCase() {
+ Trace::CreateTrace();
+ std::string trace_filename = webrtc::test::OutputPath() +
+ "audioproc_trace.txt";
+ ASSERT_EQ(0, Trace::SetTraceFile(trace_filename.c_str()));
+ }
+
+ static void TearDownTestCase() {
+ Trace::ReturnTrace();
+ }
// Path to where the resource files to be used for this test are located.
- const std::string kResourcePath;
- const std::string kOutputFileName;
+ const std::string resource_path;
+ const std::string output_filename;
webrtc::AudioProcessing* apm_;
webrtc::AudioFrame* frame_;
webrtc::AudioFrame* revframe_;
@@ -73,12 +72,12 @@
};
ApmTest::ApmTest()
- : kResourcePath(webrtc::test::ProjectRootPath() +
+ : resource_path(webrtc::test::ProjectRootPath() +
"test/data/audio_processing/"),
#if defined(WEBRTC_APM_UNIT_TEST_FIXED_PROFILE)
- kOutputFileName(kResourcePath + "output_data_fixed.pb"),
+ output_filename(resource_path + "output_data_fixed.pb"),
#elif defined(WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE)
- kOutputFileName(kResourcePath + "output_data_float.pb"),
+ output_filename(resource_path + "output_data_float.pb"),
#endif
apm_(NULL),
frame_(NULL),
@@ -104,11 +103,11 @@
revframe_->_audioChannel = 2;
revframe_->_frequencyInHz = 32000;
- std::string input_filename = kResourcePath + "aec_far.pcm";
+ std::string input_filename = resource_path + "aec_far.pcm";
far_file_ = fopen(input_filename.c_str(), "rb");
ASSERT_TRUE(far_file_ != NULL) << "Could not open input file " <<
input_filename << "\n";
- input_filename = kResourcePath + "aec_near.pcm";
+ input_filename = resource_path + "aec_near.pcm";
near_file_ = fopen(input_filename.c_str(), "rb");
ASSERT_TRUE(near_file_ != NULL) << "Could not open input file " <<
input_filename << "\n";
@@ -141,13 +140,13 @@
apm_ = NULL;
}
-void MixStereoToMono(const WebRtc_Word16* stereo,
- WebRtc_Word16* mono,
- int num_samples) {
- for (int i = 0; i < num_samples; i++) {
- int int32 = (static_cast<int>(stereo[i * 2]) +
- static_cast<int>(stereo[i * 2 + 1])) >> 1;
- mono[i] = static_cast<WebRtc_Word16>(int32);
+void MixStereoToMono(const int16_t* stereo,
+ int16_t* mono,
+ int samples_per_channel) {
+ for (int i = 0; i < samples_per_channel; i++) {
+ int32_t int32 = (static_cast<int32_t>(stereo[i * 2]) +
+ static_cast<int32_t>(stereo[i * 2 + 1])) >> 1;
+ mono[i] = static_cast<int16_t>(int32);
}
}
@@ -161,9 +160,16 @@
return a > 0 ? a : -a;
}
-WebRtc_Word16 MaxAudioFrame(const AudioFrame& frame) {
+void SetFrameTo(AudioFrame* frame, int16_t value) {
+ for (int i = 0; i < frame->_payloadDataLengthInSamples * frame->_audioChannel;
+ ++i) {
+ frame->_payloadData[i] = value;
+ }
+}
+
+int16_t MaxAudioFrame(const AudioFrame& frame) {
const int length = frame._payloadDataLengthInSamples * frame._audioChannel;
- WebRtc_Word16 max = AbsValue(frame._payloadData[0]);
+ int16_t max = AbsValue(frame._payloadData[0]);
for (int i = 1; i < length; i++) {
max = MaxValue(max, AbsValue(frame._payloadData[i]));
}
@@ -171,6 +177,23 @@
return max;
}
+bool FrameDataAreEqual(const AudioFrame& frame1, const AudioFrame& frame2) {
+ if (frame1._payloadDataLengthInSamples !=
+ frame2._payloadDataLengthInSamples) {
+ return false;
+ }
+ if (frame1._audioChannel !=
+ frame2._audioChannel) {
+ return false;
+ }
+ if (memcmp(frame1._payloadData, frame2._payloadData,
+ frame1._payloadDataLengthInSamples * frame1._audioChannel *
+ sizeof(int16_t))) {
+ return false;
+ }
+ return true;
+}
+
void TestStats(const AudioProcessing::Statistic& test,
const webrtc::audioproc::Test::Statistic& reference) {
EXPECT_EQ(reference.instant(), test.instant);
@@ -421,251 +444,6 @@
}
}
-TEST_F(ApmTest, Process) {
- GOOGLE_PROTOBUF_VERIFY_VERSION;
- webrtc::audioproc::OutputData output_data;
-
- if (!write_output_data) {
- ReadMessageLiteFromFile(kOutputFileName, &output_data);
- } else {
- // We don't have a file; add the required tests to the protobuf.
- // TODO(ajm): vary the output channels as well?
- const int channels[] = {1, 2};
- const size_t channels_size = sizeof(channels) / sizeof(*channels);
-#if defined(WEBRTC_APM_UNIT_TEST_FIXED_PROFILE)
- // AECM doesn't support super-wb.
- const int sample_rates[] = {8000, 16000};
-#elif defined(WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE)
- const int sample_rates[] = {8000, 16000, 32000};
-#endif
- const size_t sample_rates_size = sizeof(sample_rates) / sizeof(*sample_rates);
- for (size_t i = 0; i < channels_size; i++) {
- for (size_t j = 0; j < channels_size; j++) {
- for (size_t k = 0; k < sample_rates_size; k++) {
- webrtc::audioproc::Test* test = output_data.add_test();
- test->set_num_reverse_channels(channels[i]);
- test->set_num_input_channels(channels[j]);
- test->set_num_output_channels(channels[j]);
- test->set_sample_rate(sample_rates[k]);
- }
- }
- }
- }
-
-#if defined(WEBRTC_APM_UNIT_TEST_FIXED_PROFILE)
- EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(16000));
- EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true));
-
- EXPECT_EQ(apm_->kNoError,
- apm_->gain_control()->set_mode(GainControl::kAdaptiveDigital));
- EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
-#elif defined(WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE)
- EXPECT_EQ(apm_->kNoError,
- apm_->echo_cancellation()->enable_drift_compensation(true));
- EXPECT_EQ(apm_->kNoError,
- apm_->echo_cancellation()->enable_metrics(true));
- EXPECT_EQ(apm_->kNoError,
- apm_->echo_cancellation()->enable_delay_logging(true));
- EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true));
-
- EXPECT_EQ(apm_->kNoError,
- apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog));
- EXPECT_EQ(apm_->kNoError,
- apm_->gain_control()->set_analog_level_limits(0, 255));
- EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
-#endif
-
- EXPECT_EQ(apm_->kNoError,
- apm_->high_pass_filter()->Enable(true));
-
- //EXPECT_EQ(apm_->kNoError,
- // apm_->level_estimator()->Enable(true));
-
- EXPECT_EQ(apm_->kNoError,
- apm_->noise_suppression()->Enable(true));
-
- EXPECT_EQ(apm_->kNoError,
- apm_->voice_detection()->Enable(true));
-
- for (int i = 0; i < output_data.test_size(); i++) {
- printf("Running test %d of %d...\n", i + 1, output_data.test_size());
-
- webrtc::audioproc::Test* test = output_data.mutable_test(i);
- const int num_samples = test->sample_rate() / 100;
- revframe_->_payloadDataLengthInSamples = num_samples;
- revframe_->_audioChannel = test->num_reverse_channels();
- revframe_->_frequencyInHz = test->sample_rate();
- frame_->_payloadDataLengthInSamples = num_samples;
- frame_->_audioChannel = test->num_input_channels();
- frame_->_frequencyInHz = test->sample_rate();
-
- EXPECT_EQ(apm_->kNoError, apm_->Initialize());
- ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(test->sample_rate()));
- ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(frame_->_audioChannel,
- frame_->_audioChannel));
- ASSERT_EQ(apm_->kNoError,
- apm_->set_num_reverse_channels(revframe_->_audioChannel));
-
- int frame_count = 0;
- int has_echo_count = 0;
- int has_voice_count = 0;
- int is_saturated_count = 0;
- int analog_level = 127;
- int analog_level_average = 0;
- int max_output_average = 0;
-
- while (1) {
- WebRtc_Word16 temp_data[640];
-
- // Read far-end frame
- size_t read_count = fread(temp_data,
- sizeof(WebRtc_Word16),
- num_samples * 2,
- far_file_);
- if (read_count != static_cast<size_t>(num_samples * 2)) {
- // Check that the file really ended.
- ASSERT_NE(0, feof(far_file_));
- break; // This is expected.
- }
-
- if (revframe_->_audioChannel == 1) {
- MixStereoToMono(temp_data, revframe_->_payloadData,
- revframe_->_payloadDataLengthInSamples);
- } else {
- memcpy(revframe_->_payloadData,
- &temp_data[0],
- sizeof(WebRtc_Word16) * read_count);
- }
-
- EXPECT_EQ(apm_->kNoError,
- apm_->AnalyzeReverseStream(revframe_));
-
- EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0));
- EXPECT_EQ(apm_->kNoError,
- apm_->echo_cancellation()->set_stream_drift_samples(0));
- EXPECT_EQ(apm_->kNoError,
- apm_->gain_control()->set_stream_analog_level(analog_level));
-
- // Read near-end frame
- read_count = fread(temp_data,
- sizeof(WebRtc_Word16),
- num_samples * 2,
- near_file_);
- if (read_count != static_cast<size_t>(num_samples * 2)) {
- // Check that the file really ended.
- ASSERT_NE(0, feof(near_file_));
- break; // This is expected.
- }
-
- if (frame_->_audioChannel == 1) {
- MixStereoToMono(temp_data, frame_->_payloadData, num_samples);
- } else {
- memcpy(frame_->_payloadData,
- &temp_data[0],
- sizeof(WebRtc_Word16) * read_count);
- }
- frame_->_vadActivity = AudioFrame::kVadUnknown;
-
- EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
-
- max_output_average += MaxAudioFrame(*frame_);
-
- if (apm_->echo_cancellation()->stream_has_echo()) {
- has_echo_count++;
- }
-
- analog_level = apm_->gain_control()->stream_analog_level();
- analog_level_average += analog_level;
- if (apm_->gain_control()->stream_is_saturated()) {
- is_saturated_count++;
- }
- if (apm_->voice_detection()->stream_has_voice()) {
- has_voice_count++;
- EXPECT_EQ(AudioFrame::kVadActive, frame_->_vadActivity);
- } else {
- EXPECT_EQ(AudioFrame::kVadPassive, frame_->_vadActivity);
- }
-
- frame_count++;
- }
- max_output_average /= frame_count;
- analog_level_average /= frame_count;
-
- //LevelEstimator::Metrics far_metrics;
- //LevelEstimator::Metrics near_metrics;
- //EXPECT_EQ(apm_->kNoError,
- // apm_->level_estimator()->GetMetrics(&near_metrics,
-
-#if defined(WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE)
- EchoCancellation::Metrics echo_metrics;
- EXPECT_EQ(apm_->kNoError,
- apm_->echo_cancellation()->GetMetrics(&echo_metrics));
- int median = 0;
- int std = 0;
- EXPECT_EQ(apm_->kNoError,
- apm_->echo_cancellation()->GetDelayMetrics(&median, &std));
-#endif
-
- if (!write_output_data) {
- EXPECT_EQ(test->has_echo_count(), has_echo_count);
- EXPECT_EQ(test->has_voice_count(), has_voice_count);
- EXPECT_EQ(test->is_saturated_count(), is_saturated_count);
-
- EXPECT_EQ(test->analog_level_average(), analog_level_average);
- EXPECT_EQ(test->max_output_average(), max_output_average);
-
-#if defined(WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE)
- webrtc::audioproc::Test::EchoMetrics reference =
- test->echo_metrics();
- TestStats(echo_metrics.residual_echo_return_loss,
- reference.residual_echo_return_loss());
- TestStats(echo_metrics.echo_return_loss,
- reference.echo_return_loss());
- TestStats(echo_metrics.echo_return_loss_enhancement,
- reference.echo_return_loss_enhancement());
- TestStats(echo_metrics.a_nlp,
- reference.a_nlp());
-
- webrtc::audioproc::Test::DelayMetrics reference_delay =
- test->delay_metrics();
- EXPECT_EQ(median, reference_delay.median());
- EXPECT_EQ(std, reference_delay.std());
-#endif
- } else {
- test->set_has_echo_count(has_echo_count);
- test->set_has_voice_count(has_voice_count);
- test->set_is_saturated_count(is_saturated_count);
-
- test->set_analog_level_average(analog_level_average);
- test->set_max_output_average(max_output_average);
-
-#if defined(WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE)
- webrtc::audioproc::Test::EchoMetrics* message =
- test->mutable_echo_metrics();
- WriteStatsMessage(echo_metrics.residual_echo_return_loss,
- message->mutable_residual_echo_return_loss());
- WriteStatsMessage(echo_metrics.echo_return_loss,
- message->mutable_echo_return_loss());
- WriteStatsMessage(echo_metrics.echo_return_loss_enhancement,
- message->mutable_echo_return_loss_enhancement());
- WriteStatsMessage(echo_metrics.a_nlp,
- message->mutable_a_nlp());
-
- webrtc::audioproc::Test::DelayMetrics* message_delay =
- test->mutable_delay_metrics();
- message_delay->set_median(median);
- message_delay->set_std(std);
-#endif
- }
-
- rewind(far_file_);
- rewind(near_file_);
- }
-
- if (write_output_data) {
- WriteMessageLiteToFile(kOutputFileName, output_data);
- }
-}
TEST_F(ApmTest, EchoCancellation) {
EXPECT_EQ(apm_->kNoError,
@@ -948,13 +726,78 @@
}
TEST_F(ApmTest, LevelEstimator) {
- // Turing Level estimator on/off
- EXPECT_EQ(apm_->kUnsupportedComponentError,
- apm_->level_estimator()->Enable(true));
+ // Turning level estimator on/off
+ EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(false));
EXPECT_FALSE(apm_->level_estimator()->is_enabled());
- EXPECT_EQ(apm_->kUnsupportedComponentError,
- apm_->level_estimator()->Enable(false));
- EXPECT_FALSE(apm_->level_estimator()->is_enabled());
+
+ EXPECT_EQ(apm_->kNotEnabledError, apm_->level_estimator()->RMS());
+
+ EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true));
+ EXPECT_TRUE(apm_->level_estimator()->is_enabled());
+
+ // Run this test in wideband; in super-wb, the splitting filter distorts the
+ // audio enough to cause deviation from the expectation for small values.
+ EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(16000));
+ frame_->_payloadDataLengthInSamples = 160;
+ frame_->_audioChannel = 2;
+ frame_->_frequencyInHz = 16000;
+
+ // Min value if no frames have been processed.
+ EXPECT_EQ(127, apm_->level_estimator()->RMS());
+
+ // Min value on zero frames.
+ SetFrameTo(frame_, 0);
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(127, apm_->level_estimator()->RMS());
+
+ // Try a few RMS values.
+ // (These also test that the value resets after retrieving it.)
+ SetFrameTo(frame_, 32767);
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(0, apm_->level_estimator()->RMS());
+
+ SetFrameTo(frame_, 30000);
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(1, apm_->level_estimator()->RMS());
+
+ SetFrameTo(frame_, 10000);
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(10, apm_->level_estimator()->RMS());
+
+ SetFrameTo(frame_, 10);
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(70, apm_->level_estimator()->RMS());
+
+ // Min value if _energy == 0.
+ SetFrameTo(frame_, 10000);
+ uint32_t energy = frame_->_energy; // Save default to restore below.
+ frame_->_energy = 0;
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(127, apm_->level_estimator()->RMS());
+ frame_->_energy = energy;
+
+ // Verify reset after enable/disable.
+ SetFrameTo(frame_, 32767);
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(false));
+ EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true));
+ SetFrameTo(frame_, 1);
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(90, apm_->level_estimator()->RMS());
+
+ // Verify reset after initialize.
+ SetFrameTo(frame_, 32767);
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->Initialize());
+ SetFrameTo(frame_, 1);
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(90, apm_->level_estimator()->RMS());
}
TEST_F(ApmTest, VoiceDetection) {
@@ -1028,12 +871,325 @@
// TODO(bjornv): Add tests for streamed voice; stream_has_voice()
}
+
+TEST_F(ApmTest, SplittingFilter) {
+ // Verify the filter is not active through undistorted audio when:
+ // 1. No components are enabled...
+ SetFrameTo(frame_, 1000);
+ AudioFrame frame_copy = *frame_;
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy));
+
+ // 2. Only the level estimator is enabled...
+ SetFrameTo(frame_, 1000);
+ frame_copy = *frame_;
+ EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy));
+ EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(false));
+
+ // 3. Only VAD is enabled...
+ SetFrameTo(frame_, 1000);
+ frame_copy = *frame_;
+ EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy));
+ EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(false));
+
+ // 4. Both VAD and the level estimator are enabled...
+ SetFrameTo(frame_, 1000);
+ frame_copy = *frame_;
+ EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true));
+ EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy));
+ EXPECT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(false));
+ EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(false));
+
+ // 5. Not using super-wb.
+ EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(16000));
+ frame_->_payloadDataLengthInSamples = 160;
+ frame_->_audioChannel = 2;
+ frame_->_frequencyInHz = 16000;
+ // Enable AEC, which would require the filter in super-wb. We rely on the
+ // first few frames of data being unaffected by the AEC.
+ // TODO(andrew): This test, and the one below, rely rather tenuously on the
+ // behavior of the AEC. Think of something more robust.
+ EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true));
+ SetFrameTo(frame_, 1000);
+ frame_copy = *frame_;
+ EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0));
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_cancellation()->set_stream_drift_samples(0));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0));
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_cancellation()->set_stream_drift_samples(0));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy));
+
+ // Check the test is valid. We should have distortion from the filter
+ // when AEC is enabled (which won't affect the audio).
+ EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(32000));
+ frame_->_payloadDataLengthInSamples = 320;
+ frame_->_audioChannel = 2;
+ frame_->_frequencyInHz = 32000;
+ SetFrameTo(frame_, 1000);
+ frame_copy = *frame_;
+ EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0));
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_cancellation()->set_stream_drift_samples(0));
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+ EXPECT_FALSE(FrameDataAreEqual(*frame_, frame_copy));
+}
+
+TEST_F(ApmTest, Process) {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+ webrtc::audioproc::OutputData output_data;
+
+ if (!write_output_data) {
+ ReadMessageLiteFromFile(output_filename, &output_data);
+ } else {
+ // We don't have a file; add the required tests to the protobuf.
+ // TODO(ajm): vary the output channels as well?
+ const int channels[] = {1, 2};
+ const size_t channels_size = sizeof(channels) / sizeof(*channels);
+#if defined(WEBRTC_APM_UNIT_TEST_FIXED_PROFILE)
+ // AECM doesn't support super-wb.
+ const int sample_rates[] = {8000, 16000};
+#elif defined(WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE)
+ const int sample_rates[] = {8000, 16000, 32000};
+#endif
+ const size_t sample_rates_size = sizeof(sample_rates) / sizeof(*sample_rates);
+ for (size_t i = 0; i < channels_size; i++) {
+ for (size_t j = 0; j < channels_size; j++) {
+ for (size_t k = 0; k < sample_rates_size; k++) {
+ webrtc::audioproc::Test* test = output_data.add_test();
+ test->set_num_reverse_channels(channels[i]);
+ test->set_num_input_channels(channels[j]);
+ test->set_num_output_channels(channels[j]);
+ test->set_sample_rate(sample_rates[k]);
+ }
+ }
+ }
+ }
+
+#if defined(WEBRTC_APM_UNIT_TEST_FIXED_PROFILE)
+ EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(16000));
+ EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true));
+
+ EXPECT_EQ(apm_->kNoError,
+ apm_->gain_control()->set_mode(GainControl::kAdaptiveDigital));
+ EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
+#elif defined(WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE)
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_cancellation()->enable_drift_compensation(true));
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_cancellation()->enable_metrics(true));
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_cancellation()->enable_delay_logging(true));
+ EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true));
+
+ EXPECT_EQ(apm_->kNoError,
+ apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog));
+ EXPECT_EQ(apm_->kNoError,
+ apm_->gain_control()->set_analog_level_limits(0, 255));
+ EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
+#endif
+
+ EXPECT_EQ(apm_->kNoError,
+ apm_->high_pass_filter()->Enable(true));
+
+ EXPECT_EQ(apm_->kNoError,
+ apm_->level_estimator()->Enable(true));
+
+ EXPECT_EQ(apm_->kNoError,
+ apm_->noise_suppression()->Enable(true));
+
+ EXPECT_EQ(apm_->kNoError,
+ apm_->voice_detection()->Enable(true));
+
+ for (int i = 0; i < output_data.test_size(); i++) {
+ printf("Running test %d of %d...\n", i + 1, output_data.test_size());
+
+ webrtc::audioproc::Test* test = output_data.mutable_test(i);
+ const int samples_per_channel = test->sample_rate() / 100;
+ revframe_->_payloadDataLengthInSamples = samples_per_channel;
+ revframe_->_audioChannel = test->num_reverse_channels();
+ revframe_->_frequencyInHz = test->sample_rate();
+ frame_->_payloadDataLengthInSamples = samples_per_channel;
+ frame_->_audioChannel = test->num_input_channels();
+ frame_->_frequencyInHz = test->sample_rate();
+
+ EXPECT_EQ(apm_->kNoError, apm_->Initialize());
+ ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(test->sample_rate()));
+ ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(frame_->_audioChannel,
+ frame_->_audioChannel));
+ ASSERT_EQ(apm_->kNoError,
+ apm_->set_num_reverse_channels(revframe_->_audioChannel));
+
+ int frame_count = 0;
+ int has_echo_count = 0;
+ int has_voice_count = 0;
+ int is_saturated_count = 0;
+ int analog_level = 127;
+ int analog_level_average = 0;
+ int max_output_average = 0;
+
+ while (1) {
+ // Read far-end frame
+ const size_t frame_size = samples_per_channel * 2;
+ size_t read_count = fread(revframe_->_payloadData,
+ sizeof(int16_t),
+ frame_size,
+ far_file_);
+ if (read_count != frame_size) {
+ // Check that the file really ended.
+ ASSERT_NE(0, feof(far_file_));
+ break; // This is expected.
+ }
+
+ if (revframe_->_audioChannel == 1) {
+ MixStereoToMono(revframe_->_payloadData, revframe_->_payloadData,
+ samples_per_channel);
+ }
+
+ EXPECT_EQ(apm_->kNoError, apm_->AnalyzeReverseStream(revframe_));
+
+ EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0));
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_cancellation()->set_stream_drift_samples(0));
+ EXPECT_EQ(apm_->kNoError,
+ apm_->gain_control()->set_stream_analog_level(analog_level));
+
+ // Read near-end frame
+ read_count = fread(frame_->_payloadData,
+ sizeof(int16_t),
+ frame_size,
+ near_file_);
+ if (read_count != frame_size) {
+ // Check that the file really ended.
+ ASSERT_NE(0, feof(near_file_));
+ break; // This is expected.
+ }
+
+ if (frame_->_audioChannel == 1) {
+ MixStereoToMono(frame_->_payloadData, frame_->_payloadData,
+ samples_per_channel);
+ }
+ frame_->_vadActivity = AudioFrame::kVadUnknown;
+
+ EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
+
+ max_output_average += MaxAudioFrame(*frame_);
+
+ if (apm_->echo_cancellation()->stream_has_echo()) {
+ has_echo_count++;
+ }
+
+ analog_level = apm_->gain_control()->stream_analog_level();
+ analog_level_average += analog_level;
+ if (apm_->gain_control()->stream_is_saturated()) {
+ is_saturated_count++;
+ }
+ if (apm_->voice_detection()->stream_has_voice()) {
+ has_voice_count++;
+ EXPECT_EQ(AudioFrame::kVadActive, frame_->_vadActivity);
+ } else {
+ EXPECT_EQ(AudioFrame::kVadPassive, frame_->_vadActivity);
+ }
+
+ frame_count++;
+ }
+ max_output_average /= frame_count;
+ analog_level_average /= frame_count;
+
+#if defined(WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE)
+ EchoCancellation::Metrics echo_metrics;
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_cancellation()->GetMetrics(&echo_metrics));
+ int median = 0;
+ int std = 0;
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_cancellation()->GetDelayMetrics(&median, &std));
+
+ int rms_level = apm_->level_estimator()->RMS();
+ EXPECT_LE(0, rms_level);
+ EXPECT_GE(127, rms_level);
+#endif
+
+ if (!write_output_data) {
+ EXPECT_EQ(test->has_echo_count(), has_echo_count);
+ EXPECT_EQ(test->has_voice_count(), has_voice_count);
+ EXPECT_EQ(test->is_saturated_count(), is_saturated_count);
+
+ EXPECT_EQ(test->analog_level_average(), analog_level_average);
+ EXPECT_EQ(test->max_output_average(), max_output_average);
+
+#if defined(WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE)
+ webrtc::audioproc::Test::EchoMetrics reference =
+ test->echo_metrics();
+ TestStats(echo_metrics.residual_echo_return_loss,
+ reference.residual_echo_return_loss());
+ TestStats(echo_metrics.echo_return_loss,
+ reference.echo_return_loss());
+ TestStats(echo_metrics.echo_return_loss_enhancement,
+ reference.echo_return_loss_enhancement());
+ TestStats(echo_metrics.a_nlp,
+ reference.a_nlp());
+
+ webrtc::audioproc::Test::DelayMetrics reference_delay =
+ test->delay_metrics();
+ EXPECT_EQ(median, reference_delay.median());
+ EXPECT_EQ(std, reference_delay.std());
+
+ EXPECT_EQ(test->rms_level(), rms_level);
+#endif
+ } else {
+ test->set_has_echo_count(has_echo_count);
+ test->set_has_voice_count(has_voice_count);
+ test->set_is_saturated_count(is_saturated_count);
+
+ test->set_analog_level_average(analog_level_average);
+ test->set_max_output_average(max_output_average);
+
+#if defined(WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE)
+ webrtc::audioproc::Test::EchoMetrics* message =
+ test->mutable_echo_metrics();
+ WriteStatsMessage(echo_metrics.residual_echo_return_loss,
+ message->mutable_residual_echo_return_loss());
+ WriteStatsMessage(echo_metrics.echo_return_loss,
+ message->mutable_echo_return_loss());
+ WriteStatsMessage(echo_metrics.echo_return_loss_enhancement,
+ message->mutable_echo_return_loss_enhancement());
+ WriteStatsMessage(echo_metrics.a_nlp,
+ message->mutable_a_nlp());
+
+ webrtc::audioproc::Test::DelayMetrics* message_delay =
+ test->mutable_delay_metrics();
+ message_delay->set_median(median);
+ message_delay->set_std(std);
+
+ test->set_rms_level(rms_level);
+#endif
+ }
+
+ rewind(far_file_);
+ rewind(near_file_);
+ }
+
+ if (write_output_data) {
+ WriteMessageLiteToFile(output_filename, output_data);
+ }
+}
} // namespace
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
- ApmEnvironment* env = new ApmEnvironment; // GTest takes ownership.
- ::testing::AddGlobalTestEnvironment(env);
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--write_output_data") == 0) {
diff --git a/src/modules/audio_processing/test/unittest.proto b/src/modules/audio_processing/test/unittest.proto
index cdfacc4..67ba722 100644
--- a/src/modules/audio_processing/test/unittest.proto
+++ b/src/modules/audio_processing/test/unittest.proto
@@ -42,6 +42,8 @@
}
optional DelayMetrics delay_metrics = 12;
+
+ optional int32 rms_level = 13;
}
message OutputData {
diff --git a/src/voice_engine/main/source/channel.cc b/src/voice_engine/main/source/channel.cc
index d83da87..461c055 100644
--- a/src/voice_engine/main/source/channel.cc
+++ b/src/voice_engine/main/source/channel.cc
@@ -50,10 +50,11 @@
if (_includeAudioLevelIndication)
{
+ assert(_rtpAudioProc.get() != NULL);
// Store current audio level in the RTP/RTCP module.
// The level will be used in combination with voice-activity state
// (frameType) to add an RTP header extension
- _rtpRtcpModule.SetAudioLevel(_audioLevel_dBov);
+ _rtpRtcpModule.SetAudioLevel(_rtpAudioProc->level_estimator()->RMS());
}
// Push data from ACM to RTP/RTCP-module to deliver audio frame for
@@ -1085,7 +1086,6 @@
_rtpDumpOut(*RtpDump::CreateRtpDump()),
_outputAudioLevel(),
_externalTransport(false),
- _audioLevel_dBov(100),
_inputFilePlayerPtr(NULL),
_outputFilePlayerPtr(NULL),
_outputFileRecorderPtr(NULL),
@@ -1119,6 +1119,7 @@
_callbackCritSectPtr(NULL),
_transportPtr(NULL),
_encryptionPtr(NULL),
+ _rtpAudioProc(NULL),
_rxAudioProcessingModulePtr(NULL),
#ifdef WEBRTC_DTMF_DETECTION
_telephoneEventDetectionPtr(NULL),
@@ -1546,16 +1547,6 @@
return -1;
}
- if (_rxAudioProcessingModulePtr->echo_cancellation()->
- set_device_sample_rate_hz(
- kVoiceEngineAudioProcessingDeviceSampleRateHz))
- {
- _engineStatisticsPtr->SetLastError(
- VE_APM_ERROR, kTraceWarning,
- "Channel::Init() failed to set the device sample rate to 48K"
- " for far-end AP module");
- }
-
if (_rxAudioProcessingModulePtr->set_sample_rate_hz(8000))
{
_engineStatisticsPtr->SetLastError(
@@ -1568,16 +1559,7 @@
{
_engineStatisticsPtr->SetLastError(
VE_SOUNDCARD_ERROR, kTraceWarning,
- "Init() failed to set channels for the primary audio"
- " stream");
- }
-
- if (_rxAudioProcessingModulePtr->set_num_reverse_channels(1) != 0)
- {
- _engineStatisticsPtr->SetLastError(
- VE_SOUNDCARD_ERROR, kTraceWarning,
- "Init() failed to set channels for the primary audio"
- " stream");
+ "Init() failed to set channels for the primary audio stream");
}
if (_rxAudioProcessingModulePtr->high_pass_filter()->Enable(
@@ -5164,6 +5146,25 @@
int
Channel::SetRTPAudioLevelIndicationStatus(bool enable, unsigned char ID)
{
+ if (_rtpAudioProc.get() == NULL)
+ {
+ _rtpAudioProc.reset(AudioProcessing::Create(VoEModuleId(_instanceId,
+ _channelId)));
+ if (_rtpAudioProc.get() == NULL)
+ {
+ _engineStatisticsPtr->SetLastError(VE_NO_MEMORY, kTraceCritical,
+ "Failed to create AudioProcessing");
+ return -1;
+ }
+ }
+
+ if (_rtpAudioProc->level_estimator()->Enable(enable) !=
+ AudioProcessing::kNoError)
+ {
+ _engineStatisticsPtr->SetLastError(VE_APM_ERROR, kTraceWarning,
+ "Failed to enable AudioProcessing::level_estimator()");
+ }
+
_includeAudioLevelIndication = enable;
return _rtpRtcpModule.SetRTPAudioLevelIndicationStatus(enable, ID);
}
@@ -5837,14 +5838,12 @@
}
WebRtc_UWord32
-Channel::Demultiplex(const AudioFrame& audioFrame,
- const WebRtc_UWord8 audioLevel_dBov)
+Channel::Demultiplex(const AudioFrame& audioFrame)
{
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
- "Channel::Demultiplex(audioLevel_dBov=%u)", audioLevel_dBov);
+ "Channel::Demultiplex()");
_audioFrame = audioFrame;
_audioFrame._id = _channelId;
- _audioLevel_dBov = audioLevel_dBov;
return 0;
}
@@ -5889,6 +5888,40 @@
InsertInbandDtmfTone();
+ if (_includeAudioLevelIndication)
+ {
+ assert(_rtpAudioProc.get() != NULL);
+
+ // Check if settings need to be updated.
+ if (_rtpAudioProc->sample_rate_hz() != _audioFrame._frequencyInHz)
+ {
+ if (_rtpAudioProc->set_sample_rate_hz(_audioFrame._frequencyInHz) !=
+ AudioProcessing::kNoError)
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceVoice,
+ VoEId(_instanceId, _channelId),
+ "Error setting AudioProcessing sample rate");
+ return -1;
+ }
+ }
+
+ if (_rtpAudioProc->num_input_channels() != _audioFrame._audioChannel)
+ {
+ if (_rtpAudioProc->set_num_channels(_audioFrame._audioChannel,
+ _audioFrame._audioChannel)
+ != AudioProcessing::kNoError)
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceVoice,
+ VoEId(_instanceId, _channelId),
+ "Error setting AudioProcessing channels");
+ return -1;
+ }
+ }
+
+ // Performs level analysis only; does not affect the signal.
+ _rtpAudioProc->ProcessStream(&_audioFrame);
+ }
+
return 0;
}
@@ -6632,19 +6665,20 @@
"Channel::ApmProcessRx()");
// Reset the APM frequency if the frequency has changed
- if(_rxAudioProcessingModulePtr->sample_rate_hz()!=audioFrame._frequencyInHz)
+ if (_rxAudioProcessingModulePtr->sample_rate_hz() !=
+ audioFrame._frequencyInHz)
{
- if(_rxAudioProcessingModulePtr->set_sample_rate_hz(
- audioFrame._frequencyInHz))
+ if (_rxAudioProcessingModulePtr->set_sample_rate_hz(
+ audioFrame._frequencyInHz) != 0)
{
WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
- "AudioProcessingModule::set_sample_rate_hz("
- "_frequencyInHz=%u) => error ",
- _audioFrame._frequencyInHz);
+ "AudioProcessingModule::set_sample_rate_hz("
+ "_frequencyInHz=%u) => error",
+ _audioFrame._frequencyInHz);
}
}
- if (_rxAudioProcessingModulePtr->ProcessStream(&audioFrame) == -1)
+ if (_rxAudioProcessingModulePtr->ProcessStream(&audioFrame) != 0)
{
WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
"AudioProcessingModule::ProcessStream() => error");
diff --git a/src/voice_engine/main/source/channel.h b/src/voice_engine/main/source/channel.h
index 9acddad..ec76faa 100644
--- a/src/voice_engine/main/source/channel.h
+++ b/src/voice_engine/main/source/channel.h
@@ -11,28 +11,28 @@
#ifndef WEBRTC_VOICE_ENGINE_CHANNEL_H
#define WEBRTC_VOICE_ENGINE_CHANNEL_H
-#include "voe_network.h"
-
#include "audio_coding_module.h"
+#include "audio_conference_mixer_defines.h"
#include "common_types.h"
-#include "shared_data.h"
+#include "dtmf_inband.h"
+#include "dtmf_inband_queue.h"
+#include "file_player.h"
+#include "file_recorder.h"
+#include "level_indicator.h"
+#include "resampler.h"
#include "rtp_rtcp.h"
+#include "scoped_ptr.h"
+#include "shared_data.h"
#include "voe_audio_processing.h"
+#include "voe_network.h"
#include "voice_engine_defines.h"
#ifndef WEBRTC_EXTERNAL_TRANSPORT
#include "udp_transport.h"
#endif
-#include "audio_conference_mixer_defines.h"
-#include "file_player.h"
-#include "file_recorder.h"
#ifdef WEBRTC_SRTP
#include "SrtpModule.h"
#endif
-#include "dtmf_inband.h"
-#include "dtmf_inband_queue.h"
-#include "level_indicator.h"
-#include "resampler.h"
#ifdef WEBRTC_DTMF_DETECTION
#include "voe_dtmf.h" // TelephoneEventDetectionMethods, TelephoneEventObserver
#endif
@@ -513,8 +513,7 @@
return _socketTransportModule.ReceiveSocketsInitialized();
};
#endif
- WebRtc_UWord32 Demultiplex(const AudioFrame& audioFrame,
- const WebRtc_UWord8 audioLevel_dBov);
+ WebRtc_UWord32 Demultiplex(const AudioFrame& audioFrame);
WebRtc_UWord32 PrepareEncodeAndSend(int mixingFrequency);
WebRtc_UWord32 EncodeAndSend();
@@ -590,6 +589,7 @@
CriticalSectionWrapper* _callbackCritSectPtr; // owned by base
Transport* _transportPtr; // WebRtc socket or external transport
Encryption* _encryptionPtr; // WebRtc SRTP or external encryption
+ scoped_ptr<AudioProcessing> _rtpAudioProc;
AudioProcessing* _rxAudioProcessingModulePtr; // far end AudioProcessing
#ifdef WEBRTC_DTMF_DETECTION
VoETelephoneEventObserver* _telephoneEventDetectionPtr;
diff --git a/src/voice_engine/main/source/transmit_mixer.cc b/src/voice_engine/main/source/transmit_mixer.cc
index f4ccf5f..52e07a8 100644
--- a/src/voice_engine/main/source/transmit_mixer.cc
+++ b/src/voice_engine/main/source/transmit_mixer.cc
@@ -195,9 +195,7 @@
_externalMediaCallbackPtr(NULL),
_mute(false),
_remainingMuteMicTimeMs(0),
- _mixingFrequency(0),
- _includeAudioLevelIndication(false),
- _audioLevel_dBov(100)
+ _mixingFrequency(0)
{
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
"TransmitMixer::TransmitMixer() - ctor");
@@ -371,7 +369,6 @@
if (_mute)
{
AudioFrameOperations::Mute(_audioFrame);
- _audioLevel_dBov = 100;
}
// --- Measure audio level of speech after APM processing
@@ -442,7 +439,7 @@
// load temporary audioframe with current (mixed) microphone signal
AudioFrame tmpAudioFrame = _audioFrame;
- channelPtr->Demultiplex(tmpAudioFrame, _audioLevel_dBov);
+ channelPtr->Demultiplex(tmpAudioFrame);
channelPtr->PrepareEncodeAndSend(_mixingFrequency);
}
channelPtr = sc.GetNextChannel(iterator);
@@ -1323,30 +1320,6 @@
// Store new capture level (only updated when analog AGC is enabled)
_captureLevel = captureLevel;
- // Store current audio level (in dBov) if audio-level-indication
- // functionality has been enabled. This value will be include in an
- // extended RTP header by the RTP module.
- if (_includeAudioLevelIndication)
- {
- if (_audioProcessingModulePtr->level_estimator()->is_enabled())
- {
- LevelEstimator::Metrics metrics;
- LevelEstimator::Metrics reverseMetrics;
- _audioProcessingModulePtr->level_estimator()->GetMetrics(
- &metrics,
- &reverseMetrics);
- const WebRtc_Word16 absAudioLevel_dBov =
- WEBRTC_ABS(metrics.speech.instant);
- _audioLevel_dBov = static_cast<WebRtc_UWord8> (absAudioLevel_dBov);
- } else
- {
- WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
- "TransmitMixer::APMProcessStream() failed to "
- "retrieve level metrics");
- _audioLevel_dBov = 100;
- }
- }
-
// Log notifications
if (_audioProcessingModulePtr->gain_control()->stream_is_saturated())
{
diff --git a/src/voice_engine/main/source/transmit_mixer.h b/src/voice_engine/main/source/transmit_mixer.h
index b832b8b..469b288 100644
--- a/src/voice_engine/main/source/transmit_mixer.h
+++ b/src/voice_engine/main/source/transmit_mixer.h
@@ -69,10 +69,6 @@
WebRtc_Word32 StopSend();
-
- void SetRTPAudioLevelIndicationStatus(bool enable)
- { _includeAudioLevelIndication = enable; }
-
// VoEDtmf
void UpdateMuteMicrophoneTime(const WebRtc_UWord32 lengthMs);
@@ -217,7 +213,6 @@
WebRtc_Word32 _remainingMuteMicTimeMs;
int _mixingFrequency;
bool _includeAudioLevelIndication;
- WebRtc_UWord8 _audioLevel_dBov;
};
#endif // WEBRTC_VOICE_ENGINE_TRANSMIT_MIXER_H
diff --git a/src/voice_engine/main/source/voe_rtp_rtcp_impl.cc b/src/voice_engine/main/source/voe_rtp_rtcp_impl.cc
index a2bbcdf..cbc4d0d 100644
--- a/src/voice_engine/main/source/voe_rtp_rtcp_impl.cc
+++ b/src/voice_engine/main/source/voe_rtp_rtcp_impl.cc
@@ -262,22 +262,6 @@
return -1;
}
- // Set AudioProcessingModule level-metric mode based on user input.
- // Note that the Level Estimator component is currently not supported
- if (_audioProcessingModulePtr->level_estimator()->Enable(enable) != 0)
- {
- _engineStatistics.SetLastError(
- VE_APM_ERROR, kTraceError,
- "SetRTPAudioLevelIndicationStatus() failed to set level-metric"
- "mode");
- return -1;
- }
-
- // Ensure that the transmit mixer reads the audio-level metric for each
- // 10ms packet and copies the same value to all active channels.
- // The metric is derived within the AudioProcessingModule.
- _transmitMixerPtr->SetRTPAudioLevelIndicationStatus(enable);
-
// Set state and ID for the specified channel.
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
diff --git a/src/voice_engine/main/test/auto_test/voe_extended_test.cc b/src/voice_engine/main/test/auto_test/voe_extended_test.cc
index 7b69299..16833f5 100644
--- a/src/voice_engine/main/test/auto_test/voe_extended_test.cc
+++ b/src/voice_engine/main/test/auto_test/voe_extended_test.cc
@@ -6905,6 +6905,67 @@
// VoEExtendedTest::TestRTP_RTCP
// ----------------------------------------------------------------------------
+// Used to validate packets during the RTP audio level indication test.
+class RTPAudioTransport : public Transport {
+ public:
+
+ RTPAudioTransport()
+ : mute_(false) {}
+
+ virtual ~RTPAudioTransport() {}
+
+ void set_mute(bool mute) { mute_ = mute; }
+ bool mute() const { return mute_; }
+
+ // TODO(andrew): use proper error checks here rather than asserts.
+ virtual int SendPacket(int channel, const void* data, int length) {
+ const uint8_t* packet = static_cast<const uint8_t*>(data);
+
+ // Extension bit.
+ assert(packet[0] & 0x10);
+ int index = 12; // Assume standard RTP header.
+ // Header extension ID
+ assert(packet[index++] == 0xBE);
+ assert(packet[index++] == 0xDE);
+ // Header extension length
+ assert(packet[index++] == 0x00);
+ assert(packet[index++] == 0x01);
+
+ // User-defined ID.
+ assert(((packet[index] & 0xf0) >> 4) == 1);
+ // Length
+ assert((packet[index++] & 0x0f) == 0);
+
+ int vad = packet[index] >> 7;
+ int level = packet[index] & 0x7f;
+ if (channel == 0) {
+ printf("%d -%d\n", vad, level);
+ } else if (channel == 1) {
+ printf(" %d -%d\n", vad, level);
+ } else {
+ assert(false);
+ }
+
+ if (mute_) {
+ assert(vad == 0);
+ assert(level == 127);
+ } else {
+ assert(vad == 0 || vad == 1);
+ assert(level >= 0 && level <= 127);
+ }
+
+ return 0;
+ }
+
+ virtual int SendRTCPPacket(int /*channel*/, const void* /*data*/,
+ int /*length*/) {
+ return 0;
+ }
+
+ private:
+ bool mute_;
+};
+
int VoEExtendedTest::TestRTP_RTCP()
{
PrepareTest("RTP_RTCP");
@@ -6912,6 +6973,9 @@
VoEBase* base = _mgr.BasePtr();
VoEFile* file = _mgr.FilePtr();
VoERTP_RTCP* rtp_rtcp = _mgr.RTP_RTCPPtr();
+ VoENetwork* network = _mgr.NetworkPtr();
+ VoEVolumeControl* volume = _mgr.VolumeControlPtr();
+ VoECodec* codec = _mgr.CodecPtr();
XRTPObserver rtpObserver;
@@ -6961,8 +7025,6 @@
TEST_ERROR(VE_INVALID_ARGUMENT);
TEST_MUSTPASS(-1 != rtp_rtcp->SetRTPAudioLevelIndicationStatus(0, false, 15));
MARK();
- // TODO(bjornv): Activate tests below when APM supports level estimation.
- /*
TEST_MUSTPASS(-1 != rtp_rtcp->SetRTPAudioLevelIndicationStatus(1, true, 5));
MARK();
TEST_ERROR(VE_CHANNEL_NOT_VALID);
@@ -6986,10 +7048,70 @@
TEST_MUSTPASS(audioLevelEnabled != false);
TEST_MUSTPASS(ID != id);
}
+ TEST_MUSTPASS(base->StopPlayout(0));
+ TEST_MUSTPASS(base->StopSend(0));
+ TEST_MUSTPASS(base->StopPlayout(0));
+ TEST_MUSTPASS(base->DeleteChannel(0));
- // disable audio-level-rtp-header-extension
- TEST_MUSTPASS(rtp_rtcp->SetRTPAudioLevelIndicationStatus(0, false));
- */
+ RTPAudioTransport rtpAudioTransport;
+ TEST_MUSTPASS(base->CreateChannel());
+ TEST_MUSTPASS(network->RegisterExternalTransport(0, rtpAudioTransport));
+ TEST_MUSTPASS(rtp_rtcp->SetRTPAudioLevelIndicationStatus(0, true));
+ TEST_MUSTPASS(codec->SetVADStatus(0, true));
+
+ printf("\n\nReceving muted packets (expect VAD = 0, Level = -127)...\n");
+ printf("VAD Level [dbFS]\n");
+ SLEEP(2000);
+ rtpAudioTransport.set_mute(true);
+ TEST_MUSTPASS(volume->SetInputMute(0, true));
+ TEST_MUSTPASS(base->StartSend(0));
+ SLEEP(5000);
+ TEST_MUSTPASS(base->StopSend(0));
+ rtpAudioTransport.set_mute(false);
+ TEST_MUSTPASS(volume->SetInputMute(0, false));
+
+ printf("\nReceiving packets from mic (should respond to mic level)...\n");
+ printf("VAD Level [dbFS]\n");
+ SLEEP(2000);
+ TEST_MUSTPASS(base->StartSend(0));
+ SLEEP(5000);
+ TEST_MUSTPASS(base->StopSend(0));
+
+ printf("\nReceiving packets from file (expect mostly VAD = 1)...\n");
+ printf("VAD Level [dbFS]\n");
+ SLEEP(2000);
+ TEST_MUSTPASS(file->StartPlayingFileAsMicrophone(0, _mgr.AudioFilename(),
+ true, true));
+ TEST_MUSTPASS(base->StartSend(0));
+ SLEEP(5000);
+ TEST_MUSTPASS(base->StopSend(0));
+
+ printf("\nMuted and mic on independent channels...\n");
+ printf("Muted Mic\n");
+ SLEEP(2000);
+ ASSERT_TRUE(1 == base->CreateChannel());
+ TEST_MUSTPASS(network->RegisterExternalTransport(1, rtpAudioTransport));
+ TEST_MUSTPASS(rtp_rtcp->SetRTPAudioLevelIndicationStatus(1, true));
+ TEST_MUSTPASS(codec->SetVADStatus(1, true));
+ TEST_MUSTPASS(volume->SetInputMute(0, true));
+ TEST_MUSTPASS(base->StartSend(0));
+ TEST_MUSTPASS(base->StartSend(1));
+ SLEEP(5000);
+ TEST_MUSTPASS(base->StopSend(0));
+ TEST_MUSTPASS(base->StopSend(1));
+
+ TEST_MUSTPASS(network->DeRegisterExternalTransport(0));
+ TEST_MUSTPASS(network->DeRegisterExternalTransport(1));
+ TEST_MUSTPASS(base->DeleteChannel(0));
+ TEST_MUSTPASS(base->DeleteChannel(1));
+
+ TEST_MUSTPASS(base->CreateChannel());
+ TEST_MUSTPASS(base->SetLocalReceiver(0, 12345));
+ TEST_MUSTPASS(base->SetSendDestination(0, 12345, "127.0.0.1"));
+ TEST_MUSTPASS(base->StartReceive(0));
+ TEST_MUSTPASS(base->StartSend(0));
+ TEST_MUSTPASS(base->StartPlayout(0));
+
MARK();
ANL();
@@ -7306,8 +7428,6 @@
//The following test is related to defect 4985 and 4986
TEST_LOG("Turn FEC and VAD on and wait for 4 seconds and ensure that "
"the jitter is still small...");
- VoECodec* codec = _mgr.CodecPtr();
- TEST_MUSTPASS(NULL == codec);
CodecInst cinst;
#if (!defined(MAC_IPHONE) && !defined(WEBRTC_ANDROID))
cinst.pltype = 104;
diff --git a/test/data/audio_processing/output_data_float.pb b/test/data/audio_processing/output_data_float.pb
index 474661e..fcbc3e4 100644
--- a/test/data/audio_processing/output_data_float.pb
+++ b/test/data/audio_processing/output_data_float.pb
Binary files differ