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
This commit is contained in:
andrew@webrtc.org
2014-01-07 17:45:09 +00:00
parent 39669c5c8f
commit 60730cfe3c
21 changed files with 308 additions and 466 deletions

View File

@ -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 @@ AudioProcessing* AudioProcessing::Create(int id) {
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 @@ AudioProcessingImpl::AudioProcessingImpl(int id)
#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 @@ int AudioProcessingImpl::InitializeLocked() {
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 @@ int AudioProcessingImpl::num_output_channels() const {
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 @@ int AudioProcessingImpl::ProcessStream(AudioFrame* frame) {
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 @@ int AudioProcessingImpl::ProcessStream(AudioFrame* frame) {
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 @@ int AudioProcessingImpl::ProcessStream(AudioFrame* frame) {
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 @@ int AudioProcessingImpl::ProcessStream(AudioFrame* frame) {
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 @@ int AudioProcessingImpl::AnalyzeReverseStream(AudioFrame* frame) {
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 @@ VoiceDetection* AudioProcessingImpl::voice_detection() const {
}
int32_t AudioProcessingImpl::ChangeUniqueId(const int32_t id) {
CriticalSectionScoped crit_scoped(crit_);
id_ = id;
return kNoError;
}