|  | /* | 
|  | *  Copyright (c) 2016 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 <iostream> | 
|  |  | 
|  | #include "modules/audio_processing/test/aec_dump_based_simulator.h" | 
|  |  | 
|  | #include "modules/audio_processing/test/protobuf_utils.h" | 
|  | #include "rtc_base/checks.h" | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace test { | 
|  | namespace { | 
|  |  | 
|  | // Verify output bitexactness for the fixed interface. | 
|  | // TODO(peah): Check whether it would make sense to add a threshold | 
|  | // to use for checking the bitexactness in a soft manner. | 
|  | bool VerifyFixedBitExactness(const webrtc::audioproc::Stream& msg, | 
|  | const AudioFrame& frame) { | 
|  | if ((sizeof(int16_t) * frame.samples_per_channel_ * frame.num_channels_) != | 
|  | msg.output_data().size()) { | 
|  | return false; | 
|  | } else { | 
|  | const int16_t* frame_data = frame.data(); | 
|  | for (size_t k = 0; k < frame.num_channels_ * frame.samples_per_channel_; | 
|  | ++k) { | 
|  | if (msg.output_data().data()[k] != frame_data[k]) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Verify output bitexactness for the float interface. | 
|  | bool VerifyFloatBitExactness(const webrtc::audioproc::Stream& msg, | 
|  | const StreamConfig& out_config, | 
|  | const ChannelBuffer<float>& out_buf) { | 
|  | if (static_cast<size_t>(msg.output_channel_size()) != | 
|  | out_config.num_channels() || | 
|  | msg.output_channel(0).size() != out_config.num_frames()) { | 
|  | return false; | 
|  | } else { | 
|  | for (int ch = 0; ch < msg.output_channel_size(); ++ch) { | 
|  | for (size_t sample = 0; sample < out_config.num_frames(); ++sample) { | 
|  | if (msg.output_channel(ch).data()[sample] != | 
|  | out_buf.channels()[ch][sample]) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | AecDumpBasedSimulator::AecDumpBasedSimulator( | 
|  | const SimulationSettings& settings, | 
|  | std::unique_ptr<AudioProcessingBuilder> ap_builder) | 
|  | : AudioProcessingSimulator(settings, std::move(ap_builder)) {} | 
|  |  | 
|  | AecDumpBasedSimulator::~AecDumpBasedSimulator() = default; | 
|  |  | 
|  | void AecDumpBasedSimulator::PrepareProcessStreamCall( | 
|  | const webrtc::audioproc::Stream& msg) { | 
|  | if (msg.has_input_data()) { | 
|  | // Fixed interface processing. | 
|  | // Verify interface invariance. | 
|  | RTC_CHECK(interface_used_ == InterfaceType::kFixedInterface || | 
|  | interface_used_ == InterfaceType::kNotSpecified); | 
|  | interface_used_ = InterfaceType::kFixedInterface; | 
|  |  | 
|  | // Populate input buffer. | 
|  | RTC_CHECK_EQ(sizeof(*fwd_frame_.data()) * fwd_frame_.samples_per_channel_ * | 
|  | fwd_frame_.num_channels_, | 
|  | msg.input_data().size()); | 
|  | memcpy(fwd_frame_.mutable_data(), msg.input_data().data(), | 
|  | msg.input_data().size()); | 
|  | } else { | 
|  | // Float interface processing. | 
|  | // Verify interface invariance. | 
|  | RTC_CHECK(interface_used_ == InterfaceType::kFloatInterface || | 
|  | interface_used_ == InterfaceType::kNotSpecified); | 
|  | interface_used_ = InterfaceType::kFloatInterface; | 
|  |  | 
|  | RTC_CHECK_EQ(in_buf_->num_channels(), | 
|  | static_cast<size_t>(msg.input_channel_size())); | 
|  |  | 
|  | // Populate input buffer. | 
|  | for (size_t i = 0; i < in_buf_->num_channels(); ++i) { | 
|  | RTC_CHECK_EQ(in_buf_->num_frames() * sizeof(*in_buf_->channels()[i]), | 
|  | msg.input_channel(i).size()); | 
|  | std::memcpy(in_buf_->channels()[i], msg.input_channel(i).data(), | 
|  | msg.input_channel(i).size()); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (artificial_nearend_buffer_reader_) { | 
|  | if (artificial_nearend_buffer_reader_->Read( | 
|  | artificial_nearend_buf_.get())) { | 
|  | if (msg.has_input_data()) { | 
|  | int16_t* fwd_frame_data = fwd_frame_.mutable_data(); | 
|  | for (size_t k = 0; k < in_buf_->num_frames(); ++k) { | 
|  | fwd_frame_data[k] = rtc::saturated_cast<int16_t>( | 
|  | fwd_frame_data[k] + | 
|  | static_cast<int16_t>(32767 * | 
|  | artificial_nearend_buf_->channels()[0][k])); | 
|  | } | 
|  | } else { | 
|  | for (int i = 0; i < msg.input_channel_size(); ++i) { | 
|  | for (size_t k = 0; k < in_buf_->num_frames(); ++k) { | 
|  | in_buf_->channels()[i][k] += | 
|  | artificial_nearend_buf_->channels()[0][k]; | 
|  | in_buf_->channels()[i][k] = std::min( | 
|  | 32767.f, std::max(-32768.f, in_buf_->channels()[i][k])); | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | if (!artificial_nearend_eof_reported_) { | 
|  | std::cout << "The artificial nearend file ended before the recording."; | 
|  | artificial_nearend_eof_reported_ = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!settings_.stream_delay) { | 
|  | if (msg.has_delay()) { | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->set_stream_delay_ms(msg.delay())); | 
|  | } | 
|  | } else { | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->set_stream_delay_ms(*settings_.stream_delay)); | 
|  | } | 
|  |  | 
|  | if (!settings_.stream_drift_samples) { | 
|  | if (msg.has_drift()) { | 
|  | ap_->echo_cancellation()->set_stream_drift_samples(msg.drift()); | 
|  | } | 
|  | } else { | 
|  | ap_->echo_cancellation()->set_stream_drift_samples( | 
|  | *settings_.stream_drift_samples); | 
|  | } | 
|  |  | 
|  | if (!settings_.use_ts) { | 
|  | if (msg.has_keypress()) { | 
|  | ap_->set_stream_key_pressed(msg.keypress()); | 
|  | } | 
|  | } else { | 
|  | ap_->set_stream_key_pressed(*settings_.use_ts); | 
|  | } | 
|  |  | 
|  | // Level is always logged in AEC dumps. | 
|  | RTC_CHECK(msg.has_level()); | 
|  | aec_dump_mic_level_ = msg.level(); | 
|  | } | 
|  |  | 
|  | void AecDumpBasedSimulator::VerifyProcessStreamBitExactness( | 
|  | const webrtc::audioproc::Stream& msg) { | 
|  | if (bitexact_output_) { | 
|  | if (interface_used_ == InterfaceType::kFixedInterface) { | 
|  | bitexact_output_ = VerifyFixedBitExactness(msg, fwd_frame_); | 
|  | } else { | 
|  | bitexact_output_ = VerifyFloatBitExactness(msg, out_config_, *out_buf_); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void AecDumpBasedSimulator::PrepareReverseProcessStreamCall( | 
|  | const webrtc::audioproc::ReverseStream& msg) { | 
|  | if (msg.has_data()) { | 
|  | // Fixed interface processing. | 
|  | // Verify interface invariance. | 
|  | RTC_CHECK(interface_used_ == InterfaceType::kFixedInterface || | 
|  | interface_used_ == InterfaceType::kNotSpecified); | 
|  | interface_used_ = InterfaceType::kFixedInterface; | 
|  |  | 
|  | // Populate input buffer. | 
|  | RTC_CHECK_EQ(sizeof(int16_t) * rev_frame_.samples_per_channel_ * | 
|  | rev_frame_.num_channels_, | 
|  | msg.data().size()); | 
|  | memcpy(rev_frame_.mutable_data(), msg.data().data(), msg.data().size()); | 
|  | } else { | 
|  | // Float interface processing. | 
|  | // Verify interface invariance. | 
|  | RTC_CHECK(interface_used_ == InterfaceType::kFloatInterface || | 
|  | interface_used_ == InterfaceType::kNotSpecified); | 
|  | interface_used_ = InterfaceType::kFloatInterface; | 
|  |  | 
|  | RTC_CHECK_EQ(reverse_in_buf_->num_channels(), | 
|  | static_cast<size_t>(msg.channel_size())); | 
|  |  | 
|  | // Populate input buffer. | 
|  | for (int i = 0; i < msg.channel_size(); ++i) { | 
|  | RTC_CHECK_EQ(reverse_in_buf_->num_frames() * | 
|  | sizeof(*reverse_in_buf_->channels()[i]), | 
|  | msg.channel(i).size()); | 
|  | std::memcpy(reverse_in_buf_->channels()[i], msg.channel(i).data(), | 
|  | msg.channel(i).size()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void AecDumpBasedSimulator::Process() { | 
|  | CreateAudioProcessor(); | 
|  | dump_input_file_ = OpenFile(settings_.aec_dump_input_filename->c_str(), "rb"); | 
|  |  | 
|  | if (settings_.artificial_nearend_filename) { | 
|  | std::unique_ptr<WavReader> artificial_nearend_file( | 
|  | new WavReader(settings_.artificial_nearend_filename->c_str())); | 
|  |  | 
|  | RTC_CHECK_EQ(1, artificial_nearend_file->num_channels()) | 
|  | << "Only mono files for the artificial nearend are supported, " | 
|  | "reverted to not using the artificial nearend file"; | 
|  |  | 
|  | const int sample_rate_hz = artificial_nearend_file->sample_rate(); | 
|  | artificial_nearend_buffer_reader_.reset( | 
|  | new ChannelBufferWavReader(std::move(artificial_nearend_file))); | 
|  | artificial_nearend_buf_.reset(new ChannelBuffer<float>( | 
|  | rtc::CheckedDivExact(sample_rate_hz, kChunksPerSecond), 1)); | 
|  | } | 
|  |  | 
|  | webrtc::audioproc::Event event_msg; | 
|  | int num_forward_chunks_processed = 0; | 
|  | while (ReadMessageFromFile(dump_input_file_, &event_msg)) { | 
|  | switch (event_msg.type()) { | 
|  | case webrtc::audioproc::Event::INIT: | 
|  | RTC_CHECK(event_msg.has_init()); | 
|  | HandleMessage(event_msg.init()); | 
|  | break; | 
|  | case webrtc::audioproc::Event::STREAM: | 
|  | RTC_CHECK(event_msg.has_stream()); | 
|  | HandleMessage(event_msg.stream()); | 
|  | ++num_forward_chunks_processed; | 
|  | break; | 
|  | case webrtc::audioproc::Event::REVERSE_STREAM: | 
|  | RTC_CHECK(event_msg.has_reverse_stream()); | 
|  | HandleMessage(event_msg.reverse_stream()); | 
|  | break; | 
|  | case webrtc::audioproc::Event::CONFIG: | 
|  | RTC_CHECK(event_msg.has_config()); | 
|  | HandleMessage(event_msg.config()); | 
|  | break; | 
|  | default: | 
|  | RTC_CHECK(false); | 
|  | } | 
|  | } | 
|  |  | 
|  | fclose(dump_input_file_); | 
|  |  | 
|  | DestroyAudioProcessor(); | 
|  | } | 
|  |  | 
|  | void AecDumpBasedSimulator::HandleMessage( | 
|  | const webrtc::audioproc::Config& msg) { | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << "Config at frame:" << std::endl; | 
|  | std::cout << " Forward: " << get_num_process_stream_calls() << std::endl; | 
|  | std::cout << " Reverse: " << get_num_reverse_process_stream_calls() | 
|  | << std::endl; | 
|  | } | 
|  |  | 
|  | if (!settings_.discard_all_settings_in_aecdump) { | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << "Setting used in config:" << std::endl; | 
|  | } | 
|  | Config config; | 
|  | AudioProcessing::Config apm_config; | 
|  |  | 
|  | if (msg.has_aec_enabled() || settings_.use_aec) { | 
|  | bool enable = settings_.use_aec ? *settings_.use_aec : msg.aec_enabled(); | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->echo_cancellation()->Enable(enable)); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " aec_enabled: " << (enable ? "true" : "false") | 
|  | << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_aec_delay_agnostic_enabled() || settings_.use_delay_agnostic) { | 
|  | bool enable = settings_.use_delay_agnostic | 
|  | ? *settings_.use_delay_agnostic | 
|  | : msg.aec_delay_agnostic_enabled(); | 
|  | config.Set<DelayAgnostic>(new DelayAgnostic(enable)); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " aec_delay_agnostic_enabled: " | 
|  | << (enable ? "true" : "false") << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_aec_drift_compensation_enabled() || | 
|  | settings_.use_drift_compensation) { | 
|  | bool enable = settings_.use_drift_compensation | 
|  | ? *settings_.use_drift_compensation | 
|  | : msg.aec_drift_compensation_enabled(); | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->echo_cancellation()->enable_drift_compensation(enable)); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " aec_drift_compensation_enabled: " | 
|  | << (enable ? "true" : "false") << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_aec_extended_filter_enabled() || | 
|  | settings_.use_extended_filter) { | 
|  | bool enable = settings_.use_extended_filter | 
|  | ? *settings_.use_extended_filter | 
|  | : msg.aec_extended_filter_enabled(); | 
|  | config.Set<ExtendedFilter>(new ExtendedFilter(enable)); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " aec_extended_filter_enabled: " | 
|  | << (enable ? "true" : "false") << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_aec_suppression_level() || settings_.aec_suppression_level) { | 
|  | int level = settings_.aec_suppression_level | 
|  | ? *settings_.aec_suppression_level | 
|  | : msg.aec_suppression_level(); | 
|  | RTC_CHECK_EQ( | 
|  | AudioProcessing::kNoError, | 
|  | ap_->echo_cancellation()->set_suppression_level( | 
|  | static_cast<webrtc::EchoCancellation::SuppressionLevel>(level))); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " aec_suppression_level: " << level << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_aecm_enabled() || settings_.use_aecm) { | 
|  | bool enable = | 
|  | settings_.use_aecm ? *settings_.use_aecm : msg.aecm_enabled(); | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->echo_control_mobile()->Enable(enable)); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " aecm_enabled: " << (enable ? "true" : "false") | 
|  | << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_aecm_comfort_noise_enabled() || | 
|  | settings_.use_aecm_comfort_noise) { | 
|  | bool enable = settings_.use_aecm_comfort_noise | 
|  | ? *settings_.use_aecm_comfort_noise | 
|  | : msg.aecm_comfort_noise_enabled(); | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->echo_control_mobile()->enable_comfort_noise(enable)); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " aecm_comfort_noise_enabled: " | 
|  | << (enable ? "true" : "false") << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_aecm_routing_mode() || settings_.aecm_routing_mode) { | 
|  | int routing_mode = settings_.aecm_routing_mode | 
|  | ? *settings_.aecm_routing_mode | 
|  | : msg.aecm_routing_mode(); | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->echo_control_mobile()->set_routing_mode( | 
|  | static_cast<webrtc::EchoControlMobile::RoutingMode>( | 
|  | routing_mode))); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " aecm_routing_mode: " << routing_mode << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_agc_enabled() || settings_.use_agc) { | 
|  | bool enable = settings_.use_agc ? *settings_.use_agc : msg.agc_enabled(); | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->gain_control()->Enable(enable)); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " agc_enabled: " << (enable ? "true" : "false") | 
|  | << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_agc_mode() || settings_.agc_mode) { | 
|  | int mode = settings_.agc_mode ? *settings_.agc_mode : msg.agc_mode(); | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->gain_control()->set_mode( | 
|  | static_cast<webrtc::GainControl::Mode>(mode))); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " agc_mode: " << mode << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_agc_limiter_enabled() || settings_.use_agc_limiter) { | 
|  | bool enable = settings_.use_agc_limiter ? *settings_.use_agc_limiter | 
|  | : msg.agc_limiter_enabled(); | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->gain_control()->enable_limiter(enable)); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " agc_limiter_enabled: " << (enable ? "true" : "false") | 
|  | << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | // TODO(peah): Add support for controlling the Experimental AGC from the | 
|  | // command line. | 
|  | if (msg.has_noise_robust_agc_enabled()) { | 
|  | config.Set<ExperimentalAgc>( | 
|  | new ExperimentalAgc(msg.noise_robust_agc_enabled())); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " noise_robust_agc_enabled: " | 
|  | << (msg.noise_robust_agc_enabled() ? "true" : "false") | 
|  | << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_transient_suppression_enabled() || settings_.use_ts) { | 
|  | bool enable = settings_.use_ts ? *settings_.use_ts | 
|  | : msg.transient_suppression_enabled(); | 
|  | config.Set<ExperimentalNs>(new ExperimentalNs(enable)); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " transient_suppression_enabled: " | 
|  | << (enable ? "true" : "false") << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_intelligibility_enhancer_enabled() || settings_.use_ie) { | 
|  | bool enable = settings_.use_ie ? *settings_.use_ie | 
|  | : msg.intelligibility_enhancer_enabled(); | 
|  | config.Set<Intelligibility>(new Intelligibility(enable)); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " intelligibility_enhancer_enabled: " | 
|  | << (enable ? "true" : "false") << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_hpf_enabled() || settings_.use_hpf) { | 
|  | bool enable = settings_.use_hpf ? *settings_.use_hpf : msg.hpf_enabled(); | 
|  | apm_config.high_pass_filter.enabled = enable; | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " hpf_enabled: " << (enable ? "true" : "false") | 
|  | << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_ns_enabled() || settings_.use_ns) { | 
|  | bool enable = settings_.use_ns ? *settings_.use_ns : msg.ns_enabled(); | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->noise_suppression()->Enable(enable)); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " ns_enabled: " << (enable ? "true" : "false") | 
|  | << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (msg.has_ns_level() || settings_.ns_level) { | 
|  | int level = settings_.ns_level ? *settings_.ns_level : msg.ns_level(); | 
|  | RTC_CHECK_EQ(AudioProcessing::kNoError, | 
|  | ap_->noise_suppression()->set_level( | 
|  | static_cast<NoiseSuppression::Level>(level))); | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << " ns_level: " << level << std::endl; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (settings_.use_verbose_logging && msg.has_experiments_description() && | 
|  | !msg.experiments_description().empty()) { | 
|  | std::cout << " experiments not included by default in the simulation: " | 
|  | << msg.experiments_description() << std::endl; | 
|  | } | 
|  |  | 
|  | if (settings_.use_refined_adaptive_filter) { | 
|  | config.Set<RefinedAdaptiveFilter>( | 
|  | new RefinedAdaptiveFilter(*settings_.use_refined_adaptive_filter)); | 
|  | } | 
|  |  | 
|  | if (settings_.use_ed) { | 
|  | apm_config.residual_echo_detector.enabled = *settings_.use_ed; | 
|  | } | 
|  |  | 
|  | ap_->ApplyConfig(apm_config); | 
|  | ap_->SetExtraOptions(config); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AecDumpBasedSimulator::HandleMessage(const webrtc::audioproc::Init& msg) { | 
|  | RTC_CHECK(msg.has_sample_rate()); | 
|  | RTC_CHECK(msg.has_num_input_channels()); | 
|  | RTC_CHECK(msg.has_num_reverse_channels()); | 
|  | RTC_CHECK(msg.has_reverse_sample_rate()); | 
|  |  | 
|  | if (settings_.use_verbose_logging) { | 
|  | std::cout << "Init at frame:" << std::endl; | 
|  | std::cout << " Forward: " << get_num_process_stream_calls() << std::endl; | 
|  | std::cout << " Reverse: " << get_num_reverse_process_stream_calls() | 
|  | << std::endl; | 
|  | } | 
|  |  | 
|  | int num_output_channels; | 
|  | if (settings_.output_num_channels) { | 
|  | num_output_channels = *settings_.output_num_channels; | 
|  | } else { | 
|  | num_output_channels = msg.has_num_output_channels() | 
|  | ? msg.num_output_channels() | 
|  | : msg.num_input_channels(); | 
|  | } | 
|  |  | 
|  | int output_sample_rate; | 
|  | if (settings_.output_sample_rate_hz) { | 
|  | output_sample_rate = *settings_.output_sample_rate_hz; | 
|  | } else { | 
|  | output_sample_rate = msg.has_output_sample_rate() ? msg.output_sample_rate() | 
|  | : msg.sample_rate(); | 
|  | } | 
|  |  | 
|  | int num_reverse_output_channels; | 
|  | if (settings_.reverse_output_num_channels) { | 
|  | num_reverse_output_channels = *settings_.reverse_output_num_channels; | 
|  | } else { | 
|  | num_reverse_output_channels = msg.has_num_reverse_output_channels() | 
|  | ? msg.num_reverse_output_channels() | 
|  | : msg.num_reverse_channels(); | 
|  | } | 
|  |  | 
|  | int reverse_output_sample_rate; | 
|  | if (settings_.reverse_output_sample_rate_hz) { | 
|  | reverse_output_sample_rate = *settings_.reverse_output_sample_rate_hz; | 
|  | } else { | 
|  | reverse_output_sample_rate = msg.has_reverse_output_sample_rate() | 
|  | ? msg.reverse_output_sample_rate() | 
|  | : msg.reverse_sample_rate(); | 
|  | } | 
|  |  | 
|  | SetupBuffersConfigsOutputs( | 
|  | msg.sample_rate(), output_sample_rate, msg.reverse_sample_rate(), | 
|  | reverse_output_sample_rate, msg.num_input_channels(), num_output_channels, | 
|  | msg.num_reverse_channels(), num_reverse_output_channels); | 
|  | } | 
|  |  | 
|  | void AecDumpBasedSimulator::HandleMessage( | 
|  | const webrtc::audioproc::Stream& msg) { | 
|  | PrepareProcessStreamCall(msg); | 
|  | ProcessStream(interface_used_ == InterfaceType::kFixedInterface); | 
|  | VerifyProcessStreamBitExactness(msg); | 
|  | } | 
|  |  | 
|  | void AecDumpBasedSimulator::HandleMessage( | 
|  | const webrtc::audioproc::ReverseStream& msg) { | 
|  | PrepareReverseProcessStreamCall(msg); | 
|  | ProcessReverseStream(interface_used_ == InterfaceType::kFixedInterface); | 
|  | } | 
|  |  | 
|  | }  // namespace test | 
|  | }  // namespace webrtc |