aboutsummaryrefslogtreecommitdiff
path: root/src/modules/audio_processing/audio_processing_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/audio_processing/audio_processing_impl.cc')
-rw-r--r--src/modules/audio_processing/audio_processing_impl.cc652
1 files changed, 652 insertions, 0 deletions
diff --git a/src/modules/audio_processing/audio_processing_impl.cc b/src/modules/audio_processing/audio_processing_impl.cc
new file mode 100644
index 0000000000..9702e9e4c2
--- /dev/null
+++ b/src/modules/audio_processing/audio_processing_impl.cc
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "audio_processing_impl.h"
+
+#include <assert.h>
+
+#include "audio_buffer.h"
+#include "critical_section_wrapper.h"
+#include "echo_cancellation_impl.h"
+#include "echo_control_mobile_impl.h"
+#include "file_wrapper.h"
+#include "high_pass_filter_impl.h"
+#include "gain_control_impl.h"
+#include "level_estimator_impl.h"
+#include "module_common_types.h"
+#include "noise_suppression_impl.h"
+#include "processing_component.h"
+#include "splitting_filter.h"
+#include "voice_detection_impl.h"
+
+#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
+// Files generated at build-time by the protobuf compiler.
+#ifdef WEBRTC_ANDROID
+#include "external/webrtc/src/modules/audio_processing/debug.pb.h"
+#else
+#include "webrtc/audio_processing/debug.pb.h"
+#endif
+#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP
+
+namespace webrtc {
+AudioProcessing* AudioProcessing::Create(int id) {
+ /*WEBRTC_TRACE(webrtc::kTraceModuleCall,
+ webrtc::kTraceAudioProcessing,
+ id,
+ "AudioProcessing::Create()");*/
+
+ AudioProcessingImpl* apm = new AudioProcessingImpl(id);
+ if (apm->Initialize() != kNoError) {
+ delete apm;
+ apm = NULL;
+ }
+
+ return apm;
+}
+
+void AudioProcessing::Destroy(AudioProcessing* apm) {
+ delete static_cast<AudioProcessingImpl*>(apm);
+}
+
+AudioProcessingImpl::AudioProcessingImpl(int id)
+ : id_(id),
+ echo_cancellation_(NULL),
+ echo_control_mobile_(NULL),
+ gain_control_(NULL),
+ high_pass_filter_(NULL),
+ level_estimator_(NULL),
+ noise_suppression_(NULL),
+ voice_detection_(NULL),
+ crit_(CriticalSectionWrapper::CreateCriticalSection()),
+ render_audio_(NULL),
+ capture_audio_(NULL),
+#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
+ debug_file_(FileWrapper::Create()),
+ event_msg_(new audioproc::Event()),
+#endif
+ sample_rate_hz_(kSampleRate16kHz),
+ split_sample_rate_hz_(kSampleRate16kHz),
+ samples_per_channel_(sample_rate_hz_ / 100),
+ stream_delay_ms_(0),
+ was_stream_delay_set_(false),
+ num_reverse_channels_(1),
+ num_input_channels_(1),
+ num_output_channels_(1) {
+
+ echo_cancellation_ = new EchoCancellationImpl(this);
+ component_list_.push_back(echo_cancellation_);
+
+ echo_control_mobile_ = new EchoControlMobileImpl(this);
+ component_list_.push_back(echo_control_mobile_);
+
+ gain_control_ = new GainControlImpl(this);
+ component_list_.push_back(gain_control_);
+
+ high_pass_filter_ = new HighPassFilterImpl(this);
+ component_list_.push_back(high_pass_filter_);
+
+ level_estimator_ = new LevelEstimatorImpl(this);
+ component_list_.push_back(level_estimator_);
+
+ noise_suppression_ = new NoiseSuppressionImpl(this);
+ component_list_.push_back(noise_suppression_);
+
+ voice_detection_ = new VoiceDetectionImpl(this);
+ component_list_.push_back(voice_detection_);
+}
+
+AudioProcessingImpl::~AudioProcessingImpl() {
+ while (!component_list_.empty()) {
+ ProcessingComponent* component = component_list_.front();
+ component->Destroy();
+ delete component;
+ component_list_.pop_front();
+ }
+
+#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
+ if (debug_file_->Open()) {
+ debug_file_->CloseFile();
+ }
+#endif
+
+ delete crit_;
+ crit_ = NULL;
+
+ if (render_audio_) {
+ delete render_audio_;
+ render_audio_ = NULL;
+ }
+
+ if (capture_audio_) {
+ delete capture_audio_;
+ capture_audio_ = NULL;
+ }
+}
+
+CriticalSectionWrapper* AudioProcessingImpl::crit() const {
+ return crit_;
+}
+
+int AudioProcessingImpl::split_sample_rate_hz() const {
+ return split_sample_rate_hz_;
+}
+
+int AudioProcessingImpl::Initialize() {
+ CriticalSectionScoped crit_scoped(*crit_);
+ return InitializeLocked();
+}
+
+int AudioProcessingImpl::InitializeLocked() {
+ if (render_audio_ != NULL) {
+ delete render_audio_;
+ render_audio_ = NULL;
+ }
+
+ if (capture_audio_ != NULL) {
+ delete capture_audio_;
+ capture_audio_ = NULL;
+ }
+
+ render_audio_ = new AudioBuffer(num_reverse_channels_,
+ samples_per_channel_);
+ 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++) {
+ int err = (*it)->Initialize();
+ if (err != kNoError) {
+ return err;
+ }
+ }
+
+#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
+ if (debug_file_->Open()) {
+ int err = WriteInitMessage();
+ if (err != kNoError) {
+ return err;
+ }
+ }
+#endif
+
+ return kNoError;
+}
+
+int AudioProcessingImpl::set_sample_rate_hz(int rate) {
+ CriticalSectionScoped crit_scoped(*crit_);
+ if (rate != kSampleRate8kHz &&
+ rate != kSampleRate16kHz &&
+ rate != kSampleRate32kHz) {
+ return kBadParameterError;
+ }
+
+ sample_rate_hz_ = rate;
+ samples_per_channel_ = rate / 100;
+
+ if (sample_rate_hz_ == kSampleRate32kHz) {
+ split_sample_rate_hz_ = kSampleRate16kHz;
+ } else {
+ split_sample_rate_hz_ = sample_rate_hz_;
+ }
+
+ return InitializeLocked();
+}
+
+int AudioProcessingImpl::sample_rate_hz() const {
+ return sample_rate_hz_;
+}
+
+int AudioProcessingImpl::set_num_reverse_channels(int channels) {
+ CriticalSectionScoped crit_scoped(*crit_);
+ // Only stereo supported currently.
+ if (channels > 2 || channels < 1) {
+ return kBadParameterError;
+ }
+
+ num_reverse_channels_ = channels;
+
+ return InitializeLocked();
+}
+
+int AudioProcessingImpl::num_reverse_channels() const {
+ return num_reverse_channels_;
+}
+
+int AudioProcessingImpl::set_num_channels(
+ int input_channels,
+ int output_channels) {
+ CriticalSectionScoped crit_scoped(*crit_);
+ if (output_channels > input_channels) {
+ return kBadParameterError;
+ }
+
+ // Only stereo supported currently.
+ if (input_channels > 2 || input_channels < 1) {
+ return kBadParameterError;
+ }
+
+ if (output_channels > 2 || output_channels < 1) {
+ return kBadParameterError;
+ }
+
+ num_input_channels_ = input_channels;
+ num_output_channels_ = output_channels;
+
+ return InitializeLocked();
+}
+
+int AudioProcessingImpl::num_input_channels() const {
+ return num_input_channels_;
+}
+
+int AudioProcessingImpl::num_output_channels() const {
+ return num_output_channels_;
+}
+
+int AudioProcessingImpl::ProcessStream(AudioFrame* frame) {
+ CriticalSectionScoped crit_scoped(*crit_);
+ int err = kNoError;
+
+ if (frame == NULL) {
+ return kNullPointerError;
+ }
+
+ if (frame->_frequencyInHz != sample_rate_hz_) {
+ return kBadSampleRateError;
+ }
+
+ if (frame->_audioChannel != num_input_channels_) {
+ return kBadNumberChannelsError;
+ }
+
+ if (frame->_payloadDataLengthInSamples != samples_per_channel_) {
+ return kBadDataLengthError;
+ }
+
+#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
+ if (debug_file_->Open()) {
+ event_msg_->set_type(audioproc::Event::STREAM);
+ audioproc::Stream* msg = event_msg_->mutable_stream();
+ const size_t data_size = sizeof(int16_t) *
+ frame->_payloadDataLengthInSamples *
+ frame->_audioChannel;
+ msg->set_input_data(frame->_payloadData, data_size);
+ msg->set_delay(stream_delay_ms_);
+ msg->set_drift(echo_cancellation_->stream_drift_samples());
+ msg->set_level(gain_control_->stream_analog_level());
+ }
+#endif
+
+ capture_audio_->DeinterleaveFrom(frame);
+
+ // 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_;
+ }
+
+ 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),
+ capture_audio_->high_pass_split_data(i),
+ capture_audio_->analysis_filter_state1(i),
+ capture_audio_->analysis_filter_state2(i));
+ }
+ }
+
+ err = high_pass_filter_->ProcessCaptureAudio(capture_audio_);
+ if (err != kNoError) {
+ return err;
+ }
+
+ err = gain_control_->AnalyzeCaptureAudio(capture_audio_);
+ if (err != kNoError) {
+ return err;
+ }
+
+ err = echo_cancellation_->ProcessCaptureAudio(capture_audio_);
+ if (err != kNoError) {
+ return err;
+ }
+
+ if (echo_control_mobile_->is_enabled() &&
+ noise_suppression_->is_enabled()) {
+ capture_audio_->CopyLowPassToReference();
+ }
+
+ err = noise_suppression_->ProcessCaptureAudio(capture_audio_);
+ if (err != kNoError) {
+ return err;
+ }
+
+ err = echo_control_mobile_->ProcessCaptureAudio(capture_audio_);
+ if (err != kNoError) {
+ return err;
+ }
+
+ err = voice_detection_->ProcessCaptureAudio(capture_audio_);
+ if (err != kNoError) {
+ return err;
+ }
+
+ err = gain_control_->ProcessCaptureAudio(capture_audio_);
+ if (err != kNoError) {
+ return err;
+ }
+
+ 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),
+ capture_audio_->high_pass_split_data(i),
+ capture_audio_->data(i),
+ capture_audio_->synthesis_filter_state1(i),
+ capture_audio_->synthesis_filter_state2(i));
+ }
+ }
+
+ // 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);
+
+#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
+ if (debug_file_->Open()) {
+ audioproc::Stream* msg = event_msg_->mutable_stream();
+ const size_t data_size = sizeof(int16_t) *
+ frame->_payloadDataLengthInSamples *
+ frame->_audioChannel;
+ msg->set_output_data(frame->_payloadData, data_size);
+ err = WriteMessageToDebugFile();
+ if (err != kNoError) {
+ return err;
+ }
+ }
+#endif
+
+ was_stream_delay_set_ = false;
+ return kNoError;
+}
+
+int AudioProcessingImpl::AnalyzeReverseStream(AudioFrame* frame) {
+ CriticalSectionScoped crit_scoped(*crit_);
+ int err = kNoError;
+
+ if (frame == NULL) {
+ return kNullPointerError;
+ }
+
+ if (frame->_frequencyInHz != sample_rate_hz_) {
+ return kBadSampleRateError;
+ }
+
+ if (frame->_audioChannel != num_reverse_channels_) {
+ return kBadNumberChannelsError;
+ }
+
+ if (frame->_payloadDataLengthInSamples != samples_per_channel_) {
+ return kBadDataLengthError;
+ }
+
+#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
+ 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(int16_t) *
+ frame->_payloadDataLengthInSamples *
+ frame->_audioChannel;
+ msg->set_data(frame->_payloadData, data_size);
+ err = WriteMessageToDebugFile();
+ if (err != kNoError) {
+ return err;
+ }
+ }
+#endif
+
+ 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));
+ }
+ }
+
+ // TODO(ajm): warnings possible from components?
+ err = echo_cancellation_->ProcessRenderAudio(render_audio_);
+ if (err != kNoError) {
+ return err;
+ }
+
+ err = echo_control_mobile_->ProcessRenderAudio(render_audio_);
+ if (err != kNoError) {
+ return err;
+ }
+
+ err = gain_control_->ProcessRenderAudio(render_audio_);
+ if (err != kNoError) {
+ return err;
+ }
+
+ return err; // TODO(ajm): this is for returning warnings; necessary?
+}
+
+int AudioProcessingImpl::set_stream_delay_ms(int delay) {
+ was_stream_delay_set_ = true;
+ if (delay < 0) {
+ return kBadParameterError;
+ }
+
+ // TODO(ajm): the max is rather arbitrarily chosen; investigate.
+ if (delay > 500) {
+ stream_delay_ms_ = 500;
+ return kBadStreamParameterWarning;
+ }
+
+ stream_delay_ms_ = delay;
+ return kNoError;
+}
+
+int AudioProcessingImpl::stream_delay_ms() const {
+ return stream_delay_ms_;
+}
+
+bool AudioProcessingImpl::was_stream_delay_set() const {
+ return was_stream_delay_set_;
+}
+
+int AudioProcessingImpl::StartDebugRecording(
+ const char filename[AudioProcessing::kMaxFilenameSize]) {
+ CriticalSectionScoped crit_scoped(*crit_);
+ assert(kMaxFilenameSize == FileWrapper::kMaxFileNameSize);
+
+ if (filename == NULL) {
+ return kNullPointerError;
+ }
+
+#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
+ // Stop any ongoing recording.
+ if (debug_file_->Open()) {
+ if (debug_file_->CloseFile() == -1) {
+ return kFileError;
+ }
+ }
+
+ if (debug_file_->OpenFile(filename, false) == -1) {
+ debug_file_->CloseFile();
+ return kFileError;
+ }
+
+ int err = WriteInitMessage();
+ if (err != kNoError) {
+ return err;
+ }
+ return kNoError;
+#else
+ return kUnsupportedFunctionError;
+#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP
+}
+
+int AudioProcessingImpl::StopDebugRecording() {
+ CriticalSectionScoped crit_scoped(*crit_);
+
+#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
+ // We just return if recording hasn't started.
+ if (debug_file_->Open()) {
+ if (debug_file_->CloseFile() == -1) {
+ return kFileError;
+ }
+ }
+ return kNoError;
+#else
+ return kUnsupportedFunctionError;
+#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP
+}
+
+EchoCancellation* AudioProcessingImpl::echo_cancellation() const {
+ return echo_cancellation_;
+}
+
+EchoControlMobile* AudioProcessingImpl::echo_control_mobile() const {
+ return echo_control_mobile_;
+}
+
+GainControl* AudioProcessingImpl::gain_control() const {
+ return gain_control_;
+}
+
+HighPassFilter* AudioProcessingImpl::high_pass_filter() const {
+ return high_pass_filter_;
+}
+
+LevelEstimator* AudioProcessingImpl::level_estimator() const {
+ return level_estimator_;
+}
+
+NoiseSuppression* AudioProcessingImpl::noise_suppression() const {
+ return noise_suppression_;
+}
+
+VoiceDetection* AudioProcessingImpl::voice_detection() const {
+ return voice_detection_;
+}
+
+WebRtc_Word32 AudioProcessingImpl::ChangeUniqueId(const WebRtc_Word32 id) {
+ CriticalSectionScoped crit_scoped(*crit_);
+ /*WEBRTC_TRACE(webrtc::kTraceModuleCall,
+ webrtc::kTraceAudioProcessing,
+ id_,
+ "ChangeUniqueId(new id = %d)",
+ id);*/
+ id_ = id;
+
+ 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;
+}
+
+#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
+int AudioProcessingImpl::WriteMessageToDebugFile() {
+ int32_t size = event_msg_->ByteSize();
+ if (size <= 0) {
+ return kUnspecifiedError;
+ }
+#if defined(WEBRTC_BIG_ENDIAN)
+ // TODO(ajm): Use little-endian "on the wire". For the moment, we can be
+ // pretty safe in assuming little-endian.
+#endif
+
+ if (!event_msg_->SerializeToString(&event_str_)) {
+ return kUnspecifiedError;
+ }
+
+ // Write message preceded by its size.
+ if (!debug_file_->Write(&size, sizeof(int32_t))) {
+ return kFileError;
+ }
+ if (!debug_file_->Write(event_str_.data(), event_str_.length())) {
+ return kFileError;
+ }
+
+ event_msg_->Clear();
+
+ return 0;
+}
+
+int AudioProcessingImpl::WriteInitMessage() {
+ event_msg_->set_type(audioproc::Event::INIT);
+ audioproc::Init* msg = event_msg_->mutable_init();
+ msg->set_sample_rate(sample_rate_hz_);
+ msg->set_device_sample_rate(echo_cancellation_->device_sample_rate_hz());
+ msg->set_num_input_channels(num_input_channels_);
+ msg->set_num_output_channels(num_output_channels_);
+ msg->set_num_reverse_channels(num_reverse_channels_);
+
+ int err = WriteMessageToDebugFile();
+ if (err != kNoError) {
+ return err;
+ }
+
+ return kNoError;
+}
+#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP
+} // namespace webrtc