Remove the requirement to call set_sample_rate_hz and friends.
Instead have ProcessStream transparently handle changes to the stream
audio parameters (sample rate and channels). This removes two locks
per 10 ms ProcessStream call taken by VoiceEngine (four total with the
audio level indicator.)
Also, prepare future improvements by having the splitting filter take
a length parameter. This will allow it to work at different sample
rates. Remove the useless splitting_filter wrapper.
TESTED=voe_cmd_test with audio processing enabled and switching between
codecs; unit tests.
R=aluebs@webrtc.org, bjornv@webrtc.org, turaj@webrtc.org, xians@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/3949004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@5346 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/audio_processing/audio_processing_impl.cc b/webrtc/modules/audio_processing/audio_processing_impl.cc
index 4d36ff7..6a89b9c 100644
--- a/webrtc/modules/audio_processing/audio_processing_impl.cc
+++ b/webrtc/modules/audio_processing/audio_processing_impl.cc
@@ -12,6 +12,7 @@
#include <assert.h>
+#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
#include "webrtc/modules/audio_processing/audio_buffer.h"
#include "webrtc/modules/audio_processing/echo_cancellation_impl_wrapper.h"
#include "webrtc/modules/audio_processing/echo_control_mobile_impl.h"
@@ -20,9 +21,9 @@
#include "webrtc/modules/audio_processing/level_estimator_impl.h"
#include "webrtc/modules/audio_processing/noise_suppression_impl.h"
#include "webrtc/modules/audio_processing/processing_component.h"
-#include "webrtc/modules/audio_processing/splitting_filter.h"
#include "webrtc/modules/audio_processing/voice_detection_impl.h"
#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/system_wrappers/interface/compile_assert.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/file_wrapper.h"
#include "webrtc/system_wrappers/interface/logging.h"
@@ -36,9 +37,23 @@
#endif
#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP
+static const int kChunkSizeMs = 10;
+
+#define RETURN_ON_ERR(expr) \
+ do { \
+ int err = expr; \
+ if (err != kNoError) { \
+ return err; \
+ } \
+ } while (0)
+
namespace webrtc {
+
+// Throughout webrtc, it's assumed that success is represented by zero.
+COMPILE_ASSERT(AudioProcessing::kNoError == 0, no_error_must_be_zero);
+
AudioProcessing* AudioProcessing::Create(int id) {
- AudioProcessingImpl* apm = new AudioProcessingImpl(id);
+ AudioProcessingImpl* apm = new AudioProcessingImpl();
if (apm->Initialize() != kNoError) {
delete apm;
apm = NULL;
@@ -50,9 +65,8 @@
int32_t AudioProcessing::TimeUntilNextProcess() { return -1; }
int32_t AudioProcessing::Process() { return -1; }
-AudioProcessingImpl::AudioProcessingImpl(int id)
- : id_(id),
- echo_cancellation_(NULL),
+AudioProcessingImpl::AudioProcessingImpl()
+ : echo_cancellation_(NULL),
echo_control_mobile_(NULL),
gain_control_(NULL),
high_pass_filter_(NULL),
@@ -68,7 +82,7 @@
#endif
sample_rate_hz_(kSampleRate16kHz),
split_sample_rate_hz_(kSampleRate16kHz),
- samples_per_channel_(sample_rate_hz_ / 100),
+ samples_per_channel_(kChunkSizeMs * sample_rate_hz_ / 1000),
stream_delay_ms_(0),
delay_offset_ms_(0),
was_stream_delay_set_(false),
@@ -157,8 +171,6 @@
capture_audio_ = new AudioBuffer(num_input_channels_,
samples_per_channel_);
- was_stream_delay_set_ = false;
-
// Initialize all components.
std::list<ProcessingComponent*>::iterator it;
for (it = component_list_.begin(); it != component_list_.end(); ++it) {
@@ -272,6 +284,49 @@
return num_output_channels_;
}
+int AudioProcessingImpl::MaybeInitializeLocked(int sample_rate_hz,
+ int num_input_channels, int num_output_channels, int num_reverse_channels) {
+ if (sample_rate_hz == sample_rate_hz_ &&
+ num_input_channels == num_input_channels_ &&
+ num_output_channels == num_output_channels_ &&
+ num_reverse_channels == num_reverse_channels_) {
+ return kNoError;
+ }
+
+ if (sample_rate_hz != kSampleRate8kHz &&
+ sample_rate_hz != kSampleRate16kHz &&
+ sample_rate_hz != kSampleRate32kHz) {
+ return kBadSampleRateError;
+ }
+ if (num_output_channels > num_input_channels) {
+ return kBadNumberChannelsError;
+ }
+ // Only mono and stereo supported currently.
+ if (num_input_channels > 2 || num_input_channels < 1 ||
+ num_output_channels > 2 || num_output_channels < 1 ||
+ num_reverse_channels > 2 || num_reverse_channels < 1) {
+ return kBadNumberChannelsError;
+ }
+ if (echo_control_mobile_->is_enabled() && sample_rate_hz > kSampleRate16kHz) {
+ LOG(LS_ERROR) << "AECM only supports 16 or 8 kHz sample rates";
+ return kUnsupportedComponentError;
+ }
+
+ sample_rate_hz_ = sample_rate_hz;
+ samples_per_channel_ = kChunkSizeMs * sample_rate_hz / 1000;
+ num_input_channels_ = num_input_channels;
+ num_output_channels_ = num_output_channels;
+ num_reverse_channels_ = num_reverse_channels;
+
+ if (sample_rate_hz_ == kSampleRate32kHz) {
+ split_sample_rate_hz_ = kSampleRate16kHz;
+ } else {
+ split_sample_rate_hz_ = sample_rate_hz_;
+ }
+
+ return InitializeLocked();
+}
+
int AudioProcessingImpl::ProcessStream(AudioFrame* frame) {
CriticalSectionScoped crit_scoped(crit_);
int err = kNoError;
@@ -279,15 +334,10 @@
if (frame == NULL) {
return kNullPointerError;
}
-
- if (frame->sample_rate_hz_ != sample_rate_hz_) {
- return kBadSampleRateError;
- }
-
- if (frame->num_channels_ != num_input_channels_) {
- return kBadNumberChannelsError;
- }
-
+ // TODO(ajm): We now always set the output channels equal to the input
+ // channels here. Remove the ability to downmix entirely.
+ RETURN_ON_ERR(MaybeInitializeLocked(frame->sample_rate_hz_,
+ frame->num_channels_, frame->num_channels_, num_reverse_channels_));
if (frame->samples_per_channel_ != samples_per_channel_) {
return kBadDataLengthError;
}
@@ -318,11 +368,12 @@
if (analysis_needed(data_processed)) {
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),
- capture_audio_->high_pass_split_data(i),
- capture_audio_->analysis_filter_state1(i),
- capture_audio_->analysis_filter_state2(i));
+ WebRtcSpl_AnalysisQMF(capture_audio_->data(i),
+ capture_audio_->samples_per_channel(),
+ capture_audio_->low_pass_split_data(i),
+ capture_audio_->high_pass_split_data(i),
+ capture_audio_->analysis_filter_state1(i),
+ capture_audio_->analysis_filter_state2(i));
}
}
@@ -369,11 +420,12 @@
if (synthesis_needed(data_processed)) {
for (int i = 0; i < num_output_channels_; i++) {
// Recombine low and high bands.
- SplittingFilterSynthesis(capture_audio_->low_pass_split_data(i),
- capture_audio_->high_pass_split_data(i),
- capture_audio_->data(i),
- capture_audio_->synthesis_filter_state1(i),
- capture_audio_->synthesis_filter_state2(i));
+ WebRtcSpl_SynthesisQMF(capture_audio_->low_pass_split_data(i),
+ capture_audio_->high_pass_split_data(i),
+ capture_audio_->samples_per_split_channel(),
+ capture_audio_->data(i),
+ capture_audio_->synthesis_filter_state1(i),
+ capture_audio_->synthesis_filter_state2(i));
}
}
@@ -403,25 +455,21 @@
return kNoError;
}
+// TODO(ajm): Have AnalyzeReverseStream accept sample rates not matching the
+// primary stream and convert ourselves rather than having the user manage it.
+// We can be smarter and use the splitting filter when appropriate. Similarly,
+// perform downmixing here.
int AudioProcessingImpl::AnalyzeReverseStream(AudioFrame* frame) {
CriticalSectionScoped crit_scoped(crit_);
int err = kNoError;
-
if (frame == NULL) {
return kNullPointerError;
}
-
if (frame->sample_rate_hz_ != sample_rate_hz_) {
return kBadSampleRateError;
}
-
- if (frame->num_channels_ != num_reverse_channels_) {
- return kBadNumberChannelsError;
- }
-
- if (frame->samples_per_channel_ != samples_per_channel_) {
- return kBadDataLengthError;
- }
+ RETURN_ON_ERR(MaybeInitializeLocked(sample_rate_hz_, num_input_channels_,
+ num_output_channels_, frame->num_channels_));
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
if (debug_file_->Open()) {
@@ -440,15 +488,15 @@
render_audio_->DeinterleaveFrom(frame);
- // TODO(ajm): turn the splitting filter into a component?
if (sample_rate_hz_ == kSampleRate32kHz) {
for (int i = 0; i < num_reverse_channels_; i++) {
// Split into low and high band.
- SplittingFilterAnalysis(render_audio_->data(i),
- render_audio_->low_pass_split_data(i),
- render_audio_->high_pass_split_data(i),
- render_audio_->analysis_filter_state1(i),
- render_audio_->analysis_filter_state2(i));
+ WebRtcSpl_AnalysisQMF(render_audio_->data(i),
+ render_audio_->samples_per_channel(),
+ render_audio_->low_pass_split_data(i),
+ render_audio_->high_pass_split_data(i),
+ render_audio_->analysis_filter_state1(i),
+ render_audio_->analysis_filter_state2(i));
}
}
@@ -614,9 +662,6 @@
}
int32_t AudioProcessingImpl::ChangeUniqueId(const int32_t id) {
- CriticalSectionScoped crit_scoped(crit_);
- id_ = id;
-
return kNoError;
}