diff options
Diffstat (limited to 'src/modules/audio_processing/main/source/audio_processing_impl.cc')
-rw-r--r-- | src/modules/audio_processing/main/source/audio_processing_impl.cc | 636 |
1 files changed, 636 insertions, 0 deletions
diff --git a/src/modules/audio_processing/main/source/audio_processing_impl.cc b/src/modules/audio_processing/main/source/audio_processing_impl.cc new file mode 100644 index 0000000000..6440e36ec4 --- /dev/null +++ b/src/modules/audio_processing/main/source/audio_processing_impl.cc @@ -0,0 +1,636 @@ +/* + * 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 <cassert> + +#include "module_common_types.h" + +#include "critical_section_wrapper.h" +#include "file_wrapper.h" + +#include "audio_buffer.h" +#include "echo_cancellation_impl.h" +#include "echo_control_mobile_impl.h" +#include "high_pass_filter_impl.h" +#include "gain_control_impl.h" +#include "level_estimator_impl.h" +#include "noise_suppression_impl.h" +#include "processing_component.h" +#include "splitting_filter.h" +#include "voice_detection_impl.h" + +namespace webrtc { +namespace { + +enum Events { + kInitializeEvent, + kRenderEvent, + kCaptureEvent +}; + +const char kMagicNumber[] = "#!vqetrace1.2"; +} // namespace + +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), + debug_file_(FileWrapper::Create()), + crit_(CriticalSectionWrapper::CreateCriticalSection()), + render_audio_(NULL), + capture_audio_(NULL), + 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_render_input_channels_(1), + num_capture_input_channels_(1), + num_capture_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(); + } + + if (debug_file_->Open()) { + debug_file_->CloseFile(); + } + delete debug_file_; + debug_file_ = NULL; + + delete crit_; + crit_ = NULL; + + if (render_audio_ != NULL) { + delete render_audio_; + render_audio_ = NULL; + } + + if (capture_audio_ != NULL) { + 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_render_input_channels_, + samples_per_channel_); + capture_audio_ = new AudioBuffer(num_capture_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; + } + } + + 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_render_input_channels_ = channels; + + return InitializeLocked(); +} + +int AudioProcessingImpl::num_reverse_channels() const { + return num_render_input_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_capture_input_channels_ = input_channels; + num_capture_output_channels_ = output_channels; + + return InitializeLocked(); +} + +int AudioProcessingImpl::num_input_channels() const { + return num_capture_input_channels_; +} + +int AudioProcessingImpl::num_output_channels() const { + return num_capture_output_channels_; +} + +int AudioProcessingImpl::ProcessStream(AudioFrame* frame) { + CriticalSectionScoped crit_scoped(*crit_); + int err = kNoError; + + if (frame == NULL) { + return kNullPointerError; + } + + if (frame->_frequencyInHz != + static_cast<WebRtc_UWord32>(sample_rate_hz_)) { + return kBadSampleRateError; + } + + if (frame->_audioChannel != num_capture_input_channels_) { + return kBadNumberChannelsError; + } + + if (frame->_payloadDataLengthInSamples != samples_per_channel_) { + return kBadDataLengthError; + } + + if (debug_file_->Open()) { + WebRtc_UWord8 event = kCaptureEvent; + if (!debug_file_->Write(&event, sizeof(event))) { + return kFileError; + } + + if (!debug_file_->Write(&frame->_frequencyInHz, + sizeof(frame->_frequencyInHz))) { + return kFileError; + } + + if (!debug_file_->Write(&frame->_audioChannel, + sizeof(frame->_audioChannel))) { + return kFileError; + } + + if (!debug_file_->Write(&frame->_payloadDataLengthInSamples, + sizeof(frame->_payloadDataLengthInSamples))) { + return kFileError; + } + + if (!debug_file_->Write(frame->_payloadData, + sizeof(WebRtc_Word16) * frame->_payloadDataLengthInSamples * + frame->_audioChannel)) { + return kFileError; + } + } + + capture_audio_->DeinterleaveFrom(frame); + + // TODO(ajm): experiment with mixing and AEC placement. + if (num_capture_output_channels_ < num_capture_input_channels_) { + capture_audio_->Mix(num_capture_output_channels_); + + frame->_audioChannel = num_capture_output_channels_; + } + + if (sample_rate_hz_ == kSampleRate32kHz) { + for (int i = 0; i < num_capture_input_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; + } + + //err = level_estimator_->ProcessCaptureAudio(capture_audio_); + //if (err != kNoError) { + // return err; + //} + + if (sample_rate_hz_ == kSampleRate32kHz) { + for (int i = 0; i < num_capture_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)); + } + } + + capture_audio_->InterleaveTo(frame); + + return kNoError; +} + +int AudioProcessingImpl::AnalyzeReverseStream(AudioFrame* frame) { + CriticalSectionScoped crit_scoped(*crit_); + int err = kNoError; + + if (frame == NULL) { + return kNullPointerError; + } + + if (frame->_frequencyInHz != + static_cast<WebRtc_UWord32>(sample_rate_hz_)) { + return kBadSampleRateError; + } + + if (frame->_audioChannel != num_render_input_channels_) { + return kBadNumberChannelsError; + } + + if (frame->_payloadDataLengthInSamples != samples_per_channel_) { + return kBadDataLengthError; + } + + if (debug_file_->Open()) { + WebRtc_UWord8 event = kRenderEvent; + if (!debug_file_->Write(&event, sizeof(event))) { + return kFileError; + } + + if (!debug_file_->Write(&frame->_frequencyInHz, + sizeof(frame->_frequencyInHz))) { + return kFileError; + } + + if (!debug_file_->Write(&frame->_audioChannel, + sizeof(frame->_audioChannel))) { + return kFileError; + } + + if (!debug_file_->Write(&frame->_payloadDataLengthInSamples, + sizeof(frame->_payloadDataLengthInSamples))) { + return kFileError; + } + + if (!debug_file_->Write(frame->_payloadData, + sizeof(WebRtc_Word16) * frame->_payloadDataLengthInSamples * + frame->_audioChannel)) { + return kFileError; + } + } + + render_audio_->DeinterleaveFrom(frame); + + // TODO(ajm): turn the splitting filter into a component? + if (sample_rate_hz_ == kSampleRate32kHz) { + for (int i = 0; i < num_render_input_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; + } + + //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? +} + +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; + } + + // 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; + } + + if (debug_file_->WriteText("%s\n", kMagicNumber) == -1) { + debug_file_->CloseFile(); + return kFileError; + } + + // TODO(ajm): should we do this? If so, we need the number of channels etc. + // Record the default sample rate. + WebRtc_UWord8 event = kInitializeEvent; + if (!debug_file_->Write(&event, sizeof(event))) { + return kFileError; + } + + if (!debug_file_->Write(&sample_rate_hz_, sizeof(sample_rate_hz_))) { + return kFileError; + } + + return kNoError; +} + +int AudioProcessingImpl::StopDebugRecording() { + CriticalSectionScoped crit_scoped(*crit_); + // We just return if recording hasn't started. + if (debug_file_->Open()) { + if (debug_file_->CloseFile() == -1) { + return kFileError; + } + } + + return kNoError; +} + +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::Version(WebRtc_Word8* version, + WebRtc_UWord32& bytes_remaining, WebRtc_UWord32& position) const { + if (version == NULL) { + /*WEBRTC_TRACE(webrtc::kTraceError, + webrtc::kTraceAudioProcessing, + -1, + "Null version pointer");*/ + return kNullPointerError; + } + memset(&version[position], 0, bytes_remaining); + + WebRtc_Word8 my_version[] = "AudioProcessing 1.0.0"; + // Includes null termination. + WebRtc_UWord32 length = static_cast<WebRtc_UWord32>(strlen(my_version)); + if (bytes_remaining < length) { + /*WEBRTC_TRACE(webrtc::kTraceError, + webrtc::kTraceAudioProcessing, + -1, + "Buffer of insufficient length");*/ + return kBadParameterError; + } + memcpy(&version[position], my_version, length); + bytes_remaining -= length; + position += length; + + std::list<ProcessingComponent*>::const_iterator it; + for (it = component_list_.begin(); it != component_list_.end(); it++) { + char component_version[256]; + strcpy(component_version, "\n"); + int err = (*it)->get_version(&component_version[1], + sizeof(component_version) - 1); + if (err != kNoError) { + return err; + } + if (strncmp(&component_version[1], "\0", 1) == 0) { + // Assume empty if first byte is NULL. + continue; + } + + length = static_cast<WebRtc_UWord32>(strlen(component_version)); + if (bytes_remaining < length) { + /*WEBRTC_TRACE(webrtc::kTraceError, + webrtc::kTraceAudioProcessing, + -1, + "Buffer of insufficient length");*/ + return kBadParameterError; + } + memcpy(&version[position], component_version, length); + bytes_remaining -= length; + position += length; + } + + return kNoError; +} + +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; +} +} // namespace webrtc |