From bcec8b0b211ef3e7d5ca865f618b198029d67715 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Mon, 12 Oct 2020 18:08:02 -0400 Subject: [PATCH] Update to current webrtc library This is from the upstream library commit id 3326535126e435f1ba647885ce43a8f0f3d317eb, corresponding to Chromium 88.0.4290.1. --- README.md | 9 +- UPDATING.md | 4 +- meson.build | 62 +- meson_options.txt | 3 - webrtc/BUILD.gn | 738 ++++- webrtc/LICENSE | 29 + webrtc/LICENSE_THIRD_PARTY | 419 --- webrtc/api/array_view.h | 315 ++ webrtc/api/audio/audio_frame.cc | 164 + webrtc/api/audio/audio_frame.h | 177 ++ webrtc/api/audio/channel_layout.cc | 282 ++ webrtc/api/audio/channel_layout.h | 165 + webrtc/api/audio/echo_canceller3_config.cc | 270 ++ webrtc/api/audio/echo_canceller3_config.h | 228 ++ webrtc/api/audio/echo_control.h | 68 + webrtc/api/audio_codecs/audio_decoder.cc | 170 + webrtc/api/audio_codecs/audio_decoder.h | 193 ++ webrtc/api/audio_codecs/audio_encoder.cc | 113 + webrtc/api/audio_codecs/audio_encoder.h | 257 ++ webrtc/api/call/bitrate_allocation.h | 45 + webrtc/api/function_view.h | 130 + webrtc/api/meson.build | 46 + webrtc/api/ref_counted_base.h | 43 + .../{common_types.cc => api/rtp_headers.cc} | 33 +- webrtc/api/rtp_headers.h | 190 ++ webrtc/api/rtp_packet_info.cc | 60 + webrtc/api/rtp_packet_info.h | 97 + webrtc/api/rtp_packet_infos.h | 130 + webrtc/api/scoped_refptr.h | 164 + webrtc/api/task_queue/queued_task.h | 32 + webrtc/api/task_queue/task_queue_base.cc | 79 + webrtc/api/task_queue/task_queue_base.h | 83 + webrtc/api/units/data_rate.cc | 34 + webrtc/api/units/data_rate.h | 155 + webrtc/api/units/data_size.cc | 30 + webrtc/api/units/data_size.h | 66 + webrtc/api/units/frequency.cc | 29 + webrtc/api/units/frequency.h | 101 + webrtc/api/units/time_delta.cc | 36 + webrtc/api/units/time_delta.h | 105 + webrtc/api/units/timestamp.cc | 34 + webrtc/api/units/timestamp.h | 138 + webrtc/api/video/color_space.cc | 187 ++ webrtc/api/video/color_space.h | 178 ++ webrtc/api/video/hdr_metadata.cc | 21 + webrtc/api/video/hdr_metadata.h | 105 + webrtc/api/video/video_content_type.cc | 93 + webrtc/api/video/video_content_type.h | 39 + webrtc/api/video/video_rotation.h | 26 + webrtc/api/video/video_timing.cc | 92 + webrtc/api/video/video_timing.h | 129 + webrtc/audio/utility/BUILD.gn | 53 + .../audio/utility/audio_frame_operations.cc | 294 ++ webrtc/audio/utility/audio_frame_operations.h | 105 + webrtc/base/BUILD.gn | 596 ---- webrtc/base/basictypes.h | 74 - webrtc/base/checks.cc | 127 - webrtc/base/checks.h | 229 -- webrtc/base/constructormagic.h | 34 - webrtc/base/criticalsection.cc | 169 - webrtc/base/criticalsection.h | 129 - webrtc/base/event.cc | 135 - webrtc/base/event.h | 53 - webrtc/base/maybe.h | 110 - webrtc/base/meson.build | 34 - webrtc/base/platform_file.cc | 49 - webrtc/base/platform_file.h | 44 - webrtc/base/platform_thread.cc | 86 - webrtc/base/scoped_ptr.h | 636 ---- webrtc/base/stringutils.cc | 133 - webrtc/base/stringutils.h | 318 -- webrtc/base/template_util.h | 127 - webrtc/base/thread_annotations.h | 99 - webrtc/base/thread_checker.h | 91 - webrtc/base/thread_checker_impl.cc | 36 - webrtc/base/thread_checker_impl.h | 48 - webrtc/base/win32.h | 103 - webrtc/common_audio/BUILD.gn | 365 ++- webrtc/common_audio/audio_converter.cc | 153 +- webrtc/common_audio/audio_converter.h | 37 +- webrtc/common_audio/audio_ring_buffer.cc | 75 - webrtc/common_audio/audio_ring_buffer.h | 56 - webrtc/common_audio/audio_util.cc | 9 +- webrtc/common_audio/blocker.cc | 236 -- webrtc/common_audio/blocker.h | 123 - webrtc/common_audio/channel_buffer.cc | 25 +- webrtc/common_audio/channel_buffer.h | 134 +- webrtc/common_audio/fft4g.c | 1332 -------- webrtc/common_audio/fft4g.h | 25 - webrtc/common_audio/fir_filter.cc | 116 - webrtc/common_audio/fir_filter.h | 16 +- webrtc/common_audio/fir_filter_avx2.cc | 88 + webrtc/common_audio/fir_filter_avx2.h | 41 + webrtc/common_audio/fir_filter_c.cc | 61 + webrtc/common_audio/fir_filter_c.h | 38 + webrtc/common_audio/fir_filter_factory.cc | 58 + webrtc/common_audio/fir_filter_factory.h | 32 + webrtc/common_audio/fir_filter_neon.cc | 17 +- webrtc/common_audio/fir_filter_neon.h | 18 +- webrtc/common_audio/fir_filter_sse.cc | 14 +- webrtc/common_audio/fir_filter_sse.h | 20 +- webrtc/common_audio/include/audio_util.h | 98 +- webrtc/common_audio/lapped_transform.cc | 101 - webrtc/common_audio/lapped_transform.h | 123 - webrtc/common_audio/meson.build | 89 +- webrtc/common_audio/real_fourier.cc | 22 +- webrtc/common_audio/real_fourier.h | 23 +- webrtc/common_audio/real_fourier_ooura.cc | 26 +- webrtc/common_audio/real_fourier_ooura.h | 24 +- .../resampler/include/push_resampler.h | 37 +- .../resampler/include/resampler.h | 128 +- .../common_audio/resampler/push_resampler.cc | 135 +- .../resampler/push_sinc_resampler.cc | 9 +- .../resampler/push_sinc_resampler.h | 27 +- webrtc/common_audio/resampler/resampler.cc | 1753 +++++------ .../common_audio/resampler/sinc_resampler.cc | 120 +- .../common_audio/resampler/sinc_resampler.h | 63 +- .../resampler/sinc_resampler_avx2.cc | 66 + .../resampler/sinc_resampler_neon.cc | 9 +- .../resampler/sinc_resampler_sse.cc | 22 +- .../sinusoidal_linear_chirp_source.cc | 7 +- .../sinusoidal_linear_chirp_source.h | 22 +- webrtc/common_audio/ring_buffer.c | 19 +- webrtc/common_audio/ring_buffer.h | 39 +- .../auto_corr_to_refl_coef.c | 2 +- .../signal_processing/auto_correlation.c | 6 +- .../signal_processing/complex_bit_reverse.c | 2 +- .../complex_bit_reverse_arm.S | 2 +- .../complex_bit_reverse_mips.c | 2 +- .../signal_processing/complex_fft.c | 15 +- .../signal_processing/complex_fft_mips.c | 4 +- .../signal_processing/complex_fft_tables.h | 252 +- .../signal_processing/copy_set_operations.c | 2 +- .../signal_processing/cross_correlation.c | 2 +- .../cross_correlation_mips.c | 2 +- .../cross_correlation_neon.c | 3 +- .../signal_processing/division_operations.c | 7 +- ...with_scale.c => dot_product_with_scale.cc} | 8 +- .../dot_product_with_scale.h | 40 + .../signal_processing/downsample_fast.c | 21 +- .../signal_processing/downsample_fast_mips.c | 2 +- .../signal_processing/downsample_fast_neon.c | 2 +- .../common_audio/signal_processing/energy.c | 2 +- .../signal_processing/filter_ar.c | 26 +- .../signal_processing/filter_ar_fast_q12.c | 19 +- .../filter_ar_fast_q12_armv7.S | 2 +- .../filter_ar_fast_q12_mips.c | 8 +- .../signal_processing/filter_ma_fast_q12.c | 14 +- .../signal_processing/get_hanning_window.c | 2 +- .../signal_processing/get_scaling_square.c | 2 +- .../ilbc_specific_functions.c | 6 +- .../signal_processing/include/real_fft.h | 15 +- .../include/signal_processing_library.h | 230 +- .../signal_processing/include/spl_inl.h | 194 +- .../signal_processing/include/spl_inl_armv7.h | 44 +- .../signal_processing/include/spl_inl_mips.h | 205 +- .../signal_processing/levinson_durbin.c | 51 +- .../signal_processing/lpc_to_refl_coef.c | 2 +- .../signal_processing/min_max_operations.c | 26 +- .../min_max_operations_mips.c | 17 +- .../min_max_operations_neon.c | 16 +- .../randomization_functions.c | 2 +- .../common_audio/signal_processing/real_fft.c | 4 +- .../signal_processing/refl_coef_to_lpc.c | 2 +- .../common_audio/signal_processing/resample.c | 4 +- .../signal_processing/resample_48khz.c | 4 +- .../signal_processing/resample_by_2.c | 8 +- .../resample_by_2_internal.c | 28 +- .../resample_by_2_internal.h | 55 +- .../signal_processing/resample_by_2_mips.c | 4 +- .../signal_processing/resample_fractional.c | 2 +- .../common_audio/signal_processing/spl_init.c | 169 +- .../common_audio/signal_processing/spl_inl.c | 24 + .../common_audio/signal_processing/spl_sqrt.c | 22 +- .../signal_processing/splitting_filter.c | 19 +- .../sqrt_of_one_minus_x_squared.c | 2 +- .../vector_scaling_operations.c | 4 +- .../vector_scaling_operations_mips.c | 2 +- webrtc/common_audio/smoothing_filter.cc | 147 + webrtc/common_audio/smoothing_filter.h | 75 + webrtc/common_audio/sparse_fir_filter.cc | 60 - webrtc/common_audio/sparse_fir_filter.h | 52 - .../common_audio/third_party/ooura/BUILD.gn | 58 + webrtc/common_audio/third_party/ooura/LICENSE | 8 + .../ooura/fft_size_128/ooura_fft.cc} | 493 ++- .../ooura/fft_size_128/ooura_fft.h | 64 + .../ooura/fft_size_128/ooura_fft_mips.cc | 1245 ++++++++ .../ooura/fft_size_128/ooura_fft_neon.cc} | 38 +- .../ooura/fft_size_128/ooura_fft_sse2.cc} | 116 +- .../fft_size_128/ooura_fft_tables_common.h | 54 + .../fft_size_128/ooura_fft_tables_neon_sse2.h | 98 + .../third_party/ooura/fft_size_256/fft4g.cc | 866 +++++ .../third_party/ooura/fft_size_256/fft4g.h | 21 + .../third_party/spl_sqrt_floor/BUILD.gn | 24 + .../third_party/spl_sqrt_floor/LICENSE | 27 + .../spl_sqrt_floor}/spl_sqrt_floor.c | 2 +- .../spl_sqrt_floor/spl_sqrt_floor.h | 29 + .../spl_sqrt_floor}/spl_sqrt_floor_arm.S | 2 +- .../spl_sqrt_floor}/spl_sqrt_floor_mips.c | 2 +- webrtc/common_audio/vad/include/vad.h | 16 +- webrtc/common_audio/vad/include/webrtc_vad.h | 23 +- webrtc/common_audio/vad/vad.cc | 13 +- webrtc/common_audio/vad/vad_core.c | 25 +- webrtc/common_audio/vad/vad_core.h | 79 +- webrtc/common_audio/vad/vad_filterbank.c | 22 +- webrtc/common_audio/vad/vad_filterbank.h | 15 +- webrtc/common_audio/vad/vad_gmm.c | 5 +- webrtc/common_audio/vad/vad_gmm.h | 8 +- webrtc/common_audio/vad/vad_sp.c | 14 +- webrtc/common_audio/vad/vad_sp.h | 10 +- webrtc/common_audio/vad/webrtc_vad.c | 8 +- webrtc/common_audio/wav_file.cc | 344 +- webrtc/common_audio/wav_file.h | 107 +- webrtc/common_audio/wav_header.cc | 459 ++- webrtc/common_audio/wav_header.h | 80 +- webrtc/common_audio/window_generator.cc | 23 +- webrtc/common_audio/window_generator.h | 16 +- webrtc/common_types.h | 911 ------ webrtc/meson.build | 34 +- webrtc/modules/BUILD.gn | 247 ++ webrtc/modules/audio_coding/BUILD.gn | 2008 +++++++++--- .../audio_coding/codecs/audio_decoder.cc | 106 - .../audio_coding/codecs/audio_decoder.h | 117 +- .../audio_coding/codecs/audio_encoder.cc | 55 - .../audio_coding/codecs/audio_encoder.h | 139 +- .../codecs/cng/{include => }/webrtc_cng.h | 0 .../codecs/isac/audio_decoder_isac_t.h | 30 +- .../codecs/isac/audio_decoder_isac_t_impl.h | 54 +- .../codecs/isac/audio_encoder_isac_t.h | 67 +- .../codecs/isac/audio_encoder_isac_t_impl.h | 157 +- .../audio_coding/codecs/isac/bandwidth_info.h | 8 +- .../isac/main/include/audio_decoder_isac.h | 12 +- .../isac/main/include/audio_encoder_isac.h | 12 +- .../codecs/isac/main/include/isac.h | 1293 ++++---- .../codecs/isac/main/source/arith_routines.c | 4 +- .../codecs/isac/main/source/arith_routines.h | 60 +- .../isac/main/source/arith_routines_hist.c | 18 +- .../isac/main/source/arith_routines_logist.c | 11 +- .../isac/main/source/audio_decoder_isac.cc | 4 +- .../isac/main/source/audio_encoder_isac.cc | 4 +- .../isac/main/source/bandwidth_estimator.c | 40 +- .../isac/main/source/bandwidth_estimator.h | 213 +- .../codecs/isac/main/source/codec.h | 78 +- .../codecs/isac/main/source/crc.c | 5 +- .../codecs/isac/main/source/crc.h | 15 +- .../codecs/isac/main/source/decode.c | 15 +- .../codecs/isac/main/source/decode_bwe.c | 8 +- .../codecs/isac/main/source/encode.c | 30 +- .../codecs/isac/main/source/encode_lpc_swb.c | 12 +- .../codecs/isac/main/source/encode_lpc_swb.h | 98 +- .../codecs/isac/main/source/entropy_coding.c | 46 +- .../codecs/isac/main/source/entropy_coding.h | 66 +- .../isac/main/source/filter_functions.c | 110 +- .../isac/main/source/filter_functions.h | 23 + .../isac/main/source/filterbank_tables.c | 37 - .../isac/main/source/filterbank_tables.h | 46 - .../codecs/isac/main/source/filterbanks.c | 238 +- .../codecs/isac/main/source/intialize.c | 107 +- .../codecs/isac/main/source/isac.c | 122 +- .../codecs/isac/main/source/isac_float_type.h | 25 +- .../codecs/isac/main/source/isac_vad.c | 409 +++ .../codecs/isac/main/source/isac_vad.h | 45 + .../codecs/isac/main/source/lattice.c | 5 +- .../codecs/isac/main/source/lpc_analysis.c | 65 +- .../codecs/isac/main/source/lpc_analysis.h | 52 +- .../isac/main/source/lpc_gain_swb_tables.c | 5 +- .../isac/main/source/lpc_gain_swb_tables.h | 11 +- .../isac/main/source/lpc_shape_swb12_tables.c | 5 +- .../isac/main/source/lpc_shape_swb12_tables.h | 35 +- .../isac/main/source/lpc_shape_swb16_tables.c | 5 +- .../isac/main/source/lpc_shape_swb16_tables.h | 35 +- .../codecs/isac/main/source/lpc_tables.c | 4 +- .../codecs/isac/main/source/lpc_tables.h | 45 +- .../isac/main/source/os_specific_inline.h | 15 +- .../codecs/isac/main/source/pitch_estimator.c | 88 +- .../codecs/isac/main/source/pitch_estimator.h | 65 +- .../codecs/isac/main/source/pitch_filter.c | 11 +- .../codecs/isac/main/source/pitch_filter.h | 42 + .../isac/main/source/pitch_gain_tables.c | 5 +- .../isac/main/source/pitch_gain_tables.h | 17 +- .../isac/main/source/pitch_lag_tables.c | 4 +- .../isac/main/source/pitch_lag_tables.h | 26 +- .../codecs/isac/main/source/settings.h | 235 +- .../main/source/spectrum_ar_model_tables.c | 4 +- .../main/source/spectrum_ar_model_tables.h | 21 +- .../codecs/isac/main/source/structs.h | 395 +-- .../codecs/isac/main/source/transform.c | 9 +- webrtc/modules/audio_coding/meson.build | 33 +- webrtc/modules/audio_processing/BUILD.gn | 801 +++-- .../modules/audio_processing/aec/aec_common.h | 32 - .../modules/audio_processing/aec/aec_core.c | 1929 ------------ .../modules/audio_processing/aec/aec_core.h | 129 - .../audio_processing/aec/aec_core_internal.h | 202 -- .../audio_processing/aec/aec_core_mips.c | 774 ----- .../audio_processing/aec/aec_core_neon.c | 736 ----- .../audio_processing/aec/aec_core_sse2.c | 731 ----- .../modules/audio_processing/aec/aec_rdft.h | 61 - .../audio_processing/aec/aec_rdft_mips.c | 1187 ------- .../audio_processing/aec/aec_resampler.c | 209 -- .../audio_processing/aec/aec_resampler.h | 39 - .../audio_processing/aec/echo_cancellation.c | 923 ------ .../aec/echo_cancellation_internal.h | 67 - .../aec/include/echo_cancellation.h | 245 -- webrtc/modules/audio_processing/aec3/BUILD.gn | 367 +++ .../aec3/adaptive_fir_filter.cc | 740 +++++ .../aec3/adaptive_fir_filter.h | 191 ++ .../aec3/adaptive_fir_filter_avx2.cc | 187 ++ .../aec3/adaptive_fir_filter_erl.cc | 102 + .../aec3/adaptive_fir_filter_erl.h | 54 + .../aec3/adaptive_fir_filter_erl_avx2.cc | 37 + .../audio_processing/aec3/aec3_common.cc | 58 + .../audio_processing/aec3/aec3_common.h | 114 + .../modules/audio_processing/aec3/aec3_fft.cc | 144 + .../modules/audio_processing/aec3/aec3_fft.h | 75 + .../audio_processing/aec3/aec_state.cc | 477 +++ .../modules/audio_processing/aec3/aec_state.h | 294 ++ .../audio_processing/aec3/alignment_mixer.cc | 160 + .../audio_processing/aec3/alignment_mixer.h | 58 + .../aec3/api_call_jitter_metrics.cc | 121 + .../aec3/api_call_jitter_metrics.h | 60 + .../audio_processing/aec3/block_buffer.cc | 39 + .../audio_processing/aec3/block_buffer.h | 62 + .../aec3/block_delay_buffer.cc | 62 + .../aec3/block_delay_buffer.h | 43 + .../audio_processing/aec3/block_framer.cc | 86 + .../audio_processing/aec3/block_framer.h | 48 + .../audio_processing/aec3/block_processor.cc | 292 ++ .../audio_processing/aec3/block_processor.h | 76 + .../aec3/block_processor_metrics.cc | 104 + .../aec3/block_processor_metrics.h | 47 + .../aec3/clockdrift_detector.cc | 61 + .../aec3/clockdrift_detector.h | 40 + .../aec3/coarse_filter_update_gain.cc | 103 + .../aec3/coarse_filter_update_gain.h | 74 + .../aec3/comfort_noise_generator.cc | 186 ++ .../aec3/comfort_noise_generator.h | 78 + .../audio_processing/aec3/decimator.cc | 91 + .../modules/audio_processing/aec3/decimator.h | 41 + .../audio_processing/aec3/delay_estimate.h | 31 + .../aec3/dominant_nearend_detector.cc | 75 + .../aec3/dominant_nearend_detector.h | 56 + .../aec3/downsampled_render_buffer.cc | 25 + .../aec3/downsampled_render_buffer.h | 58 + .../audio_processing/aec3/echo_audibility.cc | 118 + .../audio_processing/aec3/echo_audibility.h | 86 + .../audio_processing/aec3/echo_canceller3.cc | 868 +++++ .../audio_processing/aec3/echo_canceller3.h | 196 ++ .../aec3/echo_path_delay_estimator.cc | 125 + .../aec3/echo_path_delay_estimator.h | 79 + .../aec3/echo_path_variability.cc | 22 + .../aec3/echo_path_variability.h | 37 + .../audio_processing/aec3/echo_remover.cc | 500 +++ .../audio_processing/aec3/echo_remover.h | 55 + .../aec3/echo_remover_metrics.cc | 246 ++ .../aec3/echo_remover_metrics.h | 81 + .../audio_processing/aec3/erl_estimator.cc | 146 + .../audio_processing/aec3/erl_estimator.h | 57 + .../audio_processing/aec3/erle_estimator.cc | 86 + .../audio_processing/aec3/erle_estimator.h | 99 + .../audio_processing/aec3/fft_buffer.cc | 27 + .../audio_processing/aec3/fft_buffer.h | 60 + .../modules/audio_processing/aec3/fft_data.h | 104 + .../audio_processing/aec3/fft_data_avx2.cc | 33 + .../audio_processing/aec3/filter_analyzer.cc | 280 ++ .../audio_processing/aec3/filter_analyzer.h | 149 + .../audio_processing/aec3/frame_blocker.cc | 88 + .../audio_processing/aec3/frame_blocker.h | 50 + .../aec3/fullband_erle_estimator.cc | 200 ++ .../aec3/fullband_erle_estimator.h | 118 + .../audio_processing/aec3/matched_filter.cc | 464 +++ .../audio_processing/aec3/matched_filter.h | 149 + .../aec3/matched_filter_avx2.cc | 132 + .../aec3/matched_filter_lag_aggregator.cc | 97 + .../aec3/matched_filter_lag_aggregator.h | 58 + .../audio_processing/aec3/moving_average.cc | 60 + .../audio_processing/aec3/moving_average.h | 45 + .../audio_processing/aec3/nearend_detector.h | 42 + .../aec3/refined_filter_update_gain.cc | 174 + .../aec3/refined_filter_update_gain.h | 89 + .../audio_processing/aec3/render_buffer.cc | 80 + .../audio_processing/aec3/render_buffer.h | 116 + .../aec3/render_delay_buffer.cc | 523 +++ .../aec3/render_delay_buffer.h | 86 + .../aec3/render_delay_controller.cc | 196 ++ .../aec3/render_delay_controller.h | 50 + .../aec3/render_delay_controller_metrics.cc | 145 + .../aec3/render_delay_controller_metrics.h | 55 + .../aec3/render_signal_analyzer.cc | 156 + .../aec3/render_signal_analyzer.h | 62 + .../aec3/residual_echo_estimator.cc | 379 +++ .../aec3/residual_echo_estimator.h | 78 + .../aec3/reverb_decay_estimator.cc | 409 +++ .../aec3/reverb_decay_estimator.h | 112 + .../aec3/reverb_frequency_response.cc | 98 + .../aec3/reverb_frequency_response.h | 53 + .../audio_processing/aec3/reverb_model.cc | 59 + .../audio_processing/aec3/reverb_model.h | 58 + .../aec3/reverb_model_estimator.cc | 54 + .../aec3/reverb_model_estimator.h | 67 + .../aec3/signal_dependent_erle_estimator.cc | 406 +++ .../aec3/signal_dependent_erle_estimator.h | 98 + .../audio_processing/aec3/spectrum_buffer.cc | 30 + .../audio_processing/aec3/spectrum_buffer.h | 62 + .../aec3/stationarity_estimator.cc | 243 ++ .../aec3/stationarity_estimator.h | 122 + .../aec3/subband_erle_estimator.cc | 216 ++ .../aec3/subband_erle_estimator.h | 93 + .../aec3/subband_nearend_detector.cc | 70 + .../aec3/subband_nearend_detector.h | 52 + .../audio_processing/aec3/subtractor.cc | 325 ++ .../audio_processing/aec3/subtractor.h | 137 + .../aec3/subtractor_output.cc | 58 + .../audio_processing/aec3/subtractor_output.h | 52 + .../aec3/subtractor_output_analyzer.cc | 57 + .../aec3/subtractor_output_analyzer.h | 44 + .../aec3/suppression_filter.cc | 179 ++ .../aec3/suppression_filter.h | 48 + .../audio_processing/aec3/suppression_gain.cc | 438 +++ .../audio_processing/aec3/suppression_gain.h | 130 + .../audio_processing/aec3/transparent_mode.cc | 237 ++ .../audio_processing/aec3/transparent_mode.h | 46 + .../audio_processing/aec3/vector_math.h | 229 ++ .../audio_processing/aec3/vector_math_avx2.cc | 82 + .../audio_processing/aec_dump/BUILD.gn | 110 + .../aec_dump/aec_dump_factory.h | 48 + .../aec_dump/null_aec_dump_factory.cc | 33 + webrtc/modules/audio_processing/aecm/BUILD.gn | 44 + .../modules/audio_processing/aecm/aecm_core.c | 1233 -------- .../audio_processing/aecm/aecm_core.cc | 1125 +++++++ .../modules/audio_processing/aecm/aecm_core.h | 185 +- .../aecm/{aecm_core_c.c => aecm_core_c.cc} | 608 ++-- .../audio_processing/aecm/aecm_core_mips.c | 1566 --------- .../audio_processing/aecm/aecm_core_mips.cc | 1656 ++++++++++ .../{aecm_core_neon.c => aecm_core_neon.cc} | 52 +- .../audio_processing/aecm/aecm_defines.h | 108 +- .../aecm/echo_control_mobile.c | 702 ----- .../aecm/echo_control_mobile.cc | 599 ++++ .../aecm/{include => }/echo_control_mobile.h | 95 +- webrtc/modules/audio_processing/agc/BUILD.gn | 116 + webrtc/modules/audio_processing/agc/agc.cc | 41 +- webrtc/modules/audio_processing/agc/agc.h | 31 +- .../agc/agc_manager_direct.cc | 599 ++-- .../audio_processing/agc/agc_manager_direct.h | 164 +- .../audio_processing/agc/gain_control.h | 105 + .../audio_processing/agc/gain_map_internal.h | 285 +- .../audio_processing/agc/legacy/analog_agc.c | 1519 --------- .../audio_processing/agc/legacy/analog_agc.cc | 1238 ++++++++ .../audio_processing/agc/legacy/analog_agc.h | 169 +- .../audio_processing/agc/legacy/digital_agc.c | 772 ----- .../agc/legacy/digital_agc.cc | 714 +++++ .../audio_processing/agc/legacy/digital_agc.h | 95 +- .../agc/legacy/gain_control.h | 117 +- .../{histogram.cc => loudness_histogram.cc} | 115 +- .../agc/{histogram.h => loudness_histogram.h} | 37 +- .../modules/audio_processing/agc/mock_agc.h | 34 + .../modules/audio_processing/agc/utility.cc | 6 +- webrtc/modules/audio_processing/agc/utility.h | 10 +- webrtc/modules/audio_processing/agc2/BUILD.gn | 290 ++ .../audio_processing/agc2/adaptive_agc.cc | 90 + .../audio_processing/agc2/adaptive_agc.h | 50 + .../agc2/adaptive_digital_gain_applier.cc | 179 ++ .../agc2/adaptive_digital_gain_applier.h | 69 + .../agc2/adaptive_mode_level_estimator.cc | 198 ++ .../agc2/adaptive_mode_level_estimator.h | 86 + .../agc2/adaptive_mode_level_estimator_agc.cc | 65 + .../agc2/adaptive_mode_level_estimator_agc.h | 51 + .../audio_processing/agc2/agc2_common.h | 86 + .../agc2/agc2_testing_common.cc | 33 + .../agc2/agc2_testing_common.h | 78 + .../audio_processing/agc2/biquad_filter.cc | 36 + .../audio_processing/agc2/biquad_filter.h | 66 + .../agc2/compute_interpolated_gain_curve.cc | 229 ++ .../agc2/compute_interpolated_gain_curve.h | 48 + .../audio_processing/agc2/down_sampler.cc | 99 + .../audio_processing/agc2/down_sampler.h | 42 + .../agc2/fixed_digital_level_estimator.cc | 112 + .../agc2/fixed_digital_level_estimator.h | 65 + .../agc2/fixed_gain_controller.cc | 101 + .../audio_processing/agc2/gain_applier.cc | 102 + .../audio_processing/agc2/gain_applier.h | 44 + .../agc2/interpolated_gain_curve.cc | 195 ++ .../agc2/interpolated_gain_curve.h | 152 + .../modules/audio_processing/agc2/limiter.cc | 150 + .../modules/audio_processing/agc2/limiter.h | 64 + .../agc2/limiter_db_gain_curve.cc | 138 + .../agc2/limiter_db_gain_curve.h | 76 + .../agc2/noise_level_estimator.cc | 114 + .../agc2/noise_level_estimator.h | 43 + .../agc2/noise_spectrum_estimator.cc | 70 + .../agc2/noise_spectrum_estimator.h | 42 + .../audio_processing/agc2/rnn_vad/BUILD.gn | 233 ++ .../agc2/rnn_vad/auto_correlation.cc | 92 + .../agc2/rnn_vad/auto_correlation.h | 49 + .../audio_processing/agc2/rnn_vad/common.cc | 34 + .../audio_processing/agc2/rnn_vad/common.h | 76 + .../agc2/rnn_vad/features_extraction.cc | 90 + .../agc2/rnn_vad/features_extraction.h | 62 + .../agc2/rnn_vad/lp_residual.cc | 138 + .../agc2/rnn_vad/lp_residual.h | 41 + .../agc2/rnn_vad/pitch_info.h | 29 + .../agc2/rnn_vad/pitch_search.cc | 56 + .../agc2/rnn_vad/pitch_search.h | 49 + .../agc2/rnn_vad/pitch_search_internal.cc | 403 +++ .../agc2/rnn_vad/pitch_search_internal.h | 77 + .../agc2/rnn_vad/ring_buffer.h | 66 + .../audio_processing/agc2/rnn_vad/rnn.cc | 425 +++ .../audio_processing/agc2/rnn_vad/rnn.h | 126 + .../agc2/rnn_vad/rnn_vad_tool.cc | 120 + .../agc2/rnn_vad/sequence_buffer.h | 79 + .../agc2/rnn_vad/spectral_features.cc | 213 ++ .../agc2/rnn_vad/spectral_features.h | 79 + .../rnn_vad/spectral_features_internal.cc | 187 ++ .../agc2/rnn_vad/spectral_features_internal.h | 100 + .../agc2/rnn_vad/symmetric_matrix_buffer.h | 94 + .../agc2/rnn_vad/test_utils.cc | 129 + .../agc2/rnn_vad/test_utils.h | 161 + .../agc2/saturation_protector.cc | 121 + .../agc2/saturation_protector.h | 82 + .../agc2/signal_classifier.cc | 177 ++ .../audio_processing/agc2/signal_classifier.h | 73 + .../audio_processing/agc2/vad_with_level.cc | 114 + .../audio_processing/agc2/vad_with_level.h | 58 + .../agc2/vector_float_frame.cc | 39 + .../agc2/vector_float_frame.h | 42 + .../modules/audio_processing/audio_buffer.cc | 691 ++-- .../modules/audio_processing/audio_buffer.h | 225 +- .../audio_processing_builder_impl.cc | 45 + .../audio_processing/audio_processing_impl.cc | 2790 +++++++++++------ .../audio_processing/audio_processing_impl.h | 630 +++- .../audio_processing/beamformer/array_util.cc | 118 - .../audio_processing/beamformer/array_util.h | 116 - .../audio_processing/beamformer/beamformer.h | 48 - .../beamformer/complex_matrix.h | 97 - .../beamformer/covariance_matrix_generator.cc | 114 - .../beamformer/covariance_matrix_generator.h | 54 - .../audio_processing/beamformer/matrix.h | 368 --- .../beamformer/matrix_test_helpers.h | 102 - .../beamformer/nonlinear_beamformer.cc | 570 ---- .../beamformer/nonlinear_beamformer.h | 202 -- webrtc/modules/audio_processing/common.h | 17 +- webrtc/modules/audio_processing/debug.proto | 33 +- .../echo_cancellation_impl.cc | 389 --- .../audio_processing/echo_cancellation_impl.h | 86 - .../echo_control_mobile_impl.cc | 380 ++- .../echo_control_mobile_impl.h | 92 +- .../echo_detector/circular_buffer.cc | 49 + .../echo_detector/circular_buffer.h | 44 + .../echo_detector/mean_variance_estimator.cc | 47 + .../echo_detector/mean_variance_estimator.h | 33 + .../echo_detector/moving_max.cc | 52 + .../echo_detector/moving_max.h | 36 + .../normalized_covariance_estimator.cc | 43 + .../normalized_covariance_estimator.h | 43 + .../audio_processing/gain_control_impl.cc | 466 +-- .../audio_processing/gain_control_impl.h | 96 +- .../audio_processing/gain_controller2.cc | 138 + .../audio_processing/gain_controller2.h | 58 + .../audio_processing/high_pass_filter.cc | 115 + .../audio_processing/high_pass_filter.h | 45 + .../audio_processing/high_pass_filter_impl.cc | 168 - .../audio_processing/high_pass_filter_impl.h | 50 - .../audio_processing/include/aec_dump.cc | 41 + .../audio_processing/include/aec_dump.h | 114 + .../include/audio_frame_proxies.cc | 70 + .../include/audio_frame_proxies.h | 41 + .../include/audio_frame_view.h | 67 + .../include/audio_processing.cc | 124 + .../include/audio_processing.h | 1191 ++++--- .../include/audio_processing_statistics.cc | 22 + .../include/audio_processing_statistics.h | 73 + .../audio_processing/include/config.cc | 23 + .../audio_processing/include/config.h} | 79 +- .../intelligibility_enhancer.cc | 381 --- .../intelligibility_enhancer.h | 184 -- .../intelligibility/intelligibility_utils.cc | 314 -- .../intelligibility/intelligibility_utils.h | 160 - .../audio_processing/level_estimator.cc | 29 + .../audio_processing/level_estimator.h | 47 + .../audio_processing/level_estimator_impl.cc | 86 - .../audio_processing/level_estimator_impl.h | 53 - .../audio_processing/logging/aec_logging.h | 86 - .../logging/aec_logging_file_handling.cc | 57 - .../logging/aec_logging_file_handling.h | 41 - .../logging/apm_data_dumper.cc | 92 + .../logging/apm_data_dumper.h | 287 ++ webrtc/modules/audio_processing/meson.build | 235 +- .../noise_suppression_impl.cc | 181 -- .../audio_processing/noise_suppression_impl.h | 57 - webrtc/modules/audio_processing/ns/BUILD.gn | 102 + webrtc/modules/audio_processing/ns/defines.h | 49 - .../modules/audio_processing/ns/fast_math.cc | 84 + .../modules/audio_processing/ns/fast_math.h | 38 + .../modules/audio_processing/ns/histograms.cc | 47 + .../modules/audio_processing/ns/histograms.h | 55 + .../ns/include/noise_suppression.h | 116 - .../ns/include/noise_suppression_x.h | 88 - .../audio_processing/ns/noise_estimator.cc | 195 ++ .../audio_processing/ns/noise_estimator.h | 77 + .../audio_processing/ns/noise_suppression.c | 59 - .../audio_processing/ns/noise_suppression_x.c | 46 - .../audio_processing/ns/noise_suppressor.cc | 549 ++++ .../audio_processing/ns/noise_suppressor.h | 83 + .../modules/audio_processing/ns/ns_common.h | 34 + .../modules/audio_processing/ns/ns_config.h | 24 + webrtc/modules/audio_processing/ns/ns_core.c | 1416 --------- webrtc/modules/audio_processing/ns/ns_core.h | 190 -- webrtc/modules/audio_processing/ns/ns_fft.cc | 64 + webrtc/modules/audio_processing/ns/ns_fft.h | 45 + webrtc/modules/audio_processing/ns/nsx_core.c | 2112 ------------- webrtc/modules/audio_processing/ns/nsx_core.h | 263 -- .../modules/audio_processing/ns/nsx_core_c.c | 261 -- .../audio_processing/ns/nsx_core_mips.c | 1002 ------ .../audio_processing/ns/nsx_core_neon.c | 598 ---- .../modules/audio_processing/ns/nsx_defines.h | 64 - .../audio_processing/ns/prior_signal_model.cc | 18 + .../audio_processing/ns/prior_signal_model.h | 32 + .../ns/prior_signal_model_estimator.cc | 170 + .../ns/prior_signal_model_estimator.h | 39 + .../ns/quantile_noise_estimator.cc | 88 + .../ns/quantile_noise_estimator.h | 45 + .../audio_processing/ns/signal_model.cc | 24 + .../audio_processing/ns/signal_model.h | 34 + .../ns/signal_model_estimator.cc | 175 ++ .../ns/signal_model_estimator.h | 58 + .../ns/speech_probability_estimator.cc | 103 + .../ns/speech_probability_estimator.h | 51 + .../audio_processing/ns/suppression_params.cc | 49 + .../audio_processing/ns/suppression_params.h | 30 + .../audio_processing/ns/wiener_filter.cc | 120 + .../audio_processing/ns/wiener_filter.h | 57 + .../audio_processing/ns/windows_private.h | 574 ---- .../optionally_built_submodule_creators.cc | 31 + .../optionally_built_submodule_creators.h | 38 + .../audio_processing/processing_component.cc | 111 - .../audio_processing/processing_component.h | 53 - .../render_queue_item_verifier.h | 36 + .../residual_echo_detector.cc | 215 ++ .../audio_processing/residual_echo_detector.h | 90 + webrtc/modules/audio_processing/rms_level.cc | 145 +- webrtc/modules/audio_processing/rms_level.h | 56 +- .../audio_processing/splitting_filter.cc | 133 +- .../audio_processing/splitting_filter.h | 36 +- .../three_band_filter_bank.cc | 308 +- .../audio_processing/three_band_filter_bank.h | 65 +- .../audio_processing/transient/BUILD.gn | 112 + .../transient/click_annotate.cc | 39 +- .../audio_processing/transient/common.h | 10 +- .../transient/daubechies_8_wavelet_coeffs.h | 59 +- .../transient/dyadic_decimator.h | 10 +- .../audio_processing/transient/file_utils.cc | 40 +- .../audio_processing/transient/file_utils.h | 9 +- .../transient/moving_moments.cc | 27 +- .../transient/moving_moments.h | 17 +- .../transient/transient_detector.cc | 47 +- .../transient/transient_detector.h | 22 +- .../transient/transient_suppressor.h | 104 +- ...ressor.cc => transient_suppressor_impl.cc} | 131 +- .../transient/transient_suppressor_impl.h | 123 + .../transient/windows_private.h | 557 ++++ .../audio_processing/transient/wpd_node.cc | 27 +- .../audio_processing/transient/wpd_node.h | 13 +- .../audio_processing/transient/wpd_tree.cc | 33 +- .../audio_processing/transient/wpd_tree.h | 19 +- .../audio_processing/typing_detection.cc | 23 +- .../audio_processing/typing_detection.h | 11 +- .../modules/audio_processing/utility/BUILD.gn | 81 + .../utility/cascaded_biquad_filter.cc | 117 + .../utility/cascaded_biquad_filter.h | 80 + .../{delay_estimator.c => delay_estimator.cc} | 188 +- .../utility/delay_estimator.h | 18 +- .../utility/delay_estimator_internal.h | 13 +- ...r_wrapper.c => delay_estimator_wrapper.cc} | 100 +- .../utility/delay_estimator_wrapper.h | 12 +- .../audio_processing/utility/pffft_wrapper.cc | 135 + .../audio_processing/utility/pffft_wrapper.h | 94 + webrtc/modules/audio_processing/vad/BUILD.gn | 69 + webrtc/modules/audio_processing/vad/common.h | 8 +- webrtc/modules/audio_processing/vad/gmm.cc | 5 +- webrtc/modules/audio_processing/vad/gmm.h | 6 +- .../audio_processing/vad/noise_gmm_tables.h | 27 +- .../audio_processing/vad/pitch_based_vad.cc | 16 +- .../audio_processing/vad/pitch_based_vad.h | 18 +- .../audio_processing/vad/pitch_internal.cc | 6 +- .../audio_processing/vad/pitch_internal.h | 10 +- .../audio_processing/vad/pole_zero_filter.cc | 7 +- .../audio_processing/vad/pole_zero_filter.h | 11 +- .../audio_processing/vad/standalone_vad.cc | 14 +- .../audio_processing/vad/standalone_vad.h | 17 +- .../audio_processing/vad/vad_audio_proc.cc | 42 +- .../audio_processing/vad/vad_audio_proc.h | 55 +- .../vad/vad_audio_proc_internal.h | 33 +- .../vad/vad_circular_buffer.cc | 9 +- .../vad/vad_circular_buffer.h | 10 +- .../vad/voice_activity_detector.cc | 15 +- .../vad/voice_activity_detector.h | 28 +- .../audio_processing/vad/voice_gmm_tables.h | 22 +- .../audio_processing/voice_detection.cc | 92 + .../audio_processing/voice_detection.h | 59 + .../audio_processing/voice_detection_impl.cc | 176 -- .../audio_processing/voice_detection_impl.h | 64 - webrtc/modules/interface/meson.build | 7 - .../modules/interface/module_common_types.h | 816 ----- webrtc/modules/meson.build | 2 +- webrtc/modules/third_party/fft/BUILD.gn | 16 + webrtc/modules/third_party/fft/LICENSE | 25 + .../main/source => third_party/fft}/fft.c | 7 +- .../main/source => third_party/fft}/fft.h | 33 +- webrtc/modules/third_party/fft/meson.build | 13 + .../interface/audio_frame_operations.h | 58 - webrtc/rtc_base/BUILD.gn | 1482 +++++++++ webrtc/{base => rtc_base}/arraysize.h | 9 +- .../atomicops.h => rtc_base/atomic_ops.h} | 57 +- webrtc/rtc_base/buffer.h | 437 +++ webrtc/rtc_base/checks.cc | 207 ++ webrtc/rtc_base/checks.h | 485 +++ .../include => rtc_base}/compile_assert_c.h | 19 +- webrtc/rtc_base/constructor_magic.h | 20 + webrtc/rtc_base/deprecation.h | 45 + webrtc/rtc_base/event.cc | 203 ++ webrtc/rtc_base/event.h | 86 + webrtc/rtc_base/event_tracer.cc | 412 +++ webrtc/rtc_base/event_tracer.h | 82 + .../experiments/field_trial_parser.cc | 247 ++ .../rtc_base/experiments/field_trial_parser.h | 288 ++ webrtc/rtc_base/gtest_prod_util.h | 38 + webrtc/rtc_base/ignore_wundef.h | 33 + webrtc/rtc_base/logging.cc | 562 ++++ webrtc/rtc_base/logging.h | 712 +++++ webrtc/rtc_base/memory/BUILD.gn | 56 + .../memory}/aligned_malloc.cc | 16 +- .../memory}/aligned_malloc.h | 22 +- webrtc/rtc_base/meson.build | 64 + webrtc/rtc_base/numerics/safe_compare.h | 176 ++ .../numerics}/safe_conversions.h | 28 +- .../numerics}/safe_conversions_impl.h | 135 +- webrtc/rtc_base/numerics/safe_minmax.h | 335 ++ webrtc/rtc_base/platform_thread.cc | 192 ++ webrtc/rtc_base/platform_thread.h | 104 + webrtc/rtc_base/platform_thread_types.cc | 115 + .../platform_thread_types.h} | 27 +- webrtc/rtc_base/race_checker.cc | 54 + webrtc/rtc_base/race_checker.h | 78 + webrtc/rtc_base/ref_count.h | 67 + webrtc/rtc_base/ref_counted_object.h | 64 + webrtc/rtc_base/ref_counter.h | 75 + webrtc/rtc_base/sanitizer.h | 144 + webrtc/rtc_base/string_encode.cc | 387 +++ webrtc/rtc_base/string_encode.h | 154 + webrtc/rtc_base/string_to_number.cc | 90 + webrtc/rtc_base/string_to_number.h | 116 + webrtc/rtc_base/string_utils.cc | 53 + webrtc/rtc_base/string_utils.h | 93 + webrtc/rtc_base/strings/string_builder.cc | 141 + webrtc/rtc_base/strings/string_builder.h | 175 ++ webrtc/rtc_base/swap_queue.h | 249 ++ webrtc/rtc_base/synchronization/BUILD.gn | 138 + webrtc/rtc_base/synchronization/mutex.cc | 39 + webrtc/rtc_base/synchronization/mutex.h | 108 + .../synchronization/mutex_critical_section.h | 54 + .../rtc_base/synchronization/mutex_pthread.h | 53 + .../synchronization}/rw_lock_posix.cc | 7 +- .../synchronization}/rw_lock_posix.h | 11 +- .../rtc_base/synchronization/rw_lock_win.cc | 41 + .../synchronization}/rw_lock_win.h | 20 +- .../synchronization/rw_lock_wrapper.cc} | 16 +- .../synchronization}/rw_lock_wrapper.h | 36 +- .../synchronization/sequence_checker.cc | 112 + .../synchronization/sequence_checker.h | 187 ++ webrtc/rtc_base/synchronization/yield.cc | 36 + webrtc/rtc_base/synchronization/yield.h | 20 + .../rtc_base/synchronization/yield_policy.cc | 82 + .../rtc_base/synchronization/yield_policy.h | 38 + webrtc/rtc_base/system/arch.h | 61 + .../include => rtc_base/system}/asm_defines.h | 12 +- webrtc/rtc_base/system/file_wrapper.cc | 127 + webrtc/rtc_base/system/file_wrapper.h | 109 + webrtc/rtc_base/system/ignore_warnings.h | 29 + webrtc/rtc_base/system/inline.h | 31 + webrtc/rtc_base/system/rtc_export.h | 43 + webrtc/rtc_base/system/unused.h | 39 + .../warn_current_thread_is_deadlocked.h | 24 + webrtc/rtc_base/thread_annotations.h | 95 + webrtc/rtc_base/thread_checker.h | 27 + webrtc/rtc_base/time_utils.cc | 327 ++ webrtc/rtc_base/time_utils.h | 139 + webrtc/rtc_base/trace_event.h | 1022 ++++++ webrtc/rtc_base/type_traits.h | 140 + webrtc/rtc_base/units/BUILD.gn | 33 + webrtc/rtc_base/units/unit_base.h | 306 ++ webrtc/rtc_base/win32.h | 104 + webrtc/rtc_base/zero_memory.cc | 38 + webrtc/rtc_base/zero_memory.h | 35 + webrtc/system_wrappers/BUILD.gn | 254 +- .../system_wrappers/include/aligned_array.h | 88 - .../include/condition_variable_wrapper.h | 42 - .../include/cpu_features_wrapper.h | 37 +- .../include/critical_section_wrapper.h | 54 - .../system_wrappers/include/event_wrapper.h | 70 - webrtc/system_wrappers/include/field_trial.h | 102 + webrtc/system_wrappers/include/file_wrapper.h | 78 - .../fix_interlocked_exchange_pointer_win.h | 39 - webrtc/system_wrappers/include/logging.h | 161 - webrtc/system_wrappers/include/metrics.h | 417 ++- .../system_wrappers/include/scoped_vector.h | 157 - webrtc/system_wrappers/include/sleep.h | 6 +- .../system_wrappers/include/static_instance.h | 153 - webrtc/system_wrappers/include/stl_util.h | 265 -- .../system_wrappers/include/thread_wrapper.h | 95 - webrtc/system_wrappers/include/trace.h | 92 - webrtc/system_wrappers/meson.build | 41 +- .../source/condition_variable.cc | 41 - .../source/condition_variable_event_win.cc | 195 -- .../source/condition_variable_event_win.h | 46 - .../source/condition_variable_native_win.cc | 104 - .../source/condition_variable_native_win.h | 54 - webrtc/system_wrappers/source/cpu_features.cc | 73 +- .../source/critical_section.cc | 28 - .../source/critical_section_posix.cc | 41 - .../source/critical_section_posix.h | 36 - .../source/critical_section_win.cc | 33 - .../source/critical_section_win.h | 38 - webrtc/system_wrappers/source/event.cc | 54 - .../source/event_timer_posix.cc | 231 -- .../source/event_timer_posix.h | 60 - .../system_wrappers/source/event_timer_win.cc | 78 - .../system_wrappers/source/event_timer_win.h | 40 - webrtc/system_wrappers/source/field_trial.cc | 155 + webrtc/system_wrappers/source/file_impl.cc | 278 -- webrtc/system_wrappers/source/file_impl.h | 69 - webrtc/system_wrappers/source/logging.cc | 62 - webrtc/system_wrappers/source/metrics.cc | 328 ++ .../system_wrappers/source/metrics_default.cc | 29 - .../system_wrappers/source/rw_lock_generic.cc | 77 - .../system_wrappers/source/rw_lock_generic.h | 46 - webrtc/system_wrappers/source/rw_lock_win.cc | 97 - webrtc/system_wrappers/source/sleep.cc | 2 +- webrtc/system_wrappers/source/thread.cc | 33 - webrtc/system_wrappers/source/thread_posix.cc | 166 - webrtc/system_wrappers/source/thread_posix.h | 53 - webrtc/system_wrappers/source/thread_win.cc | 107 - webrtc/system_wrappers/source/thread_win.h | 48 - webrtc/system_wrappers/source/trace_impl.cc | 604 ---- webrtc/system_wrappers/source/trace_impl.h | 106 - webrtc/system_wrappers/source/trace_posix.cc | 90 - webrtc/system_wrappers/source/trace_posix.h | 39 - webrtc/system_wrappers/source/trace_win.cc | 97 - webrtc/system_wrappers/source/trace_win.h | 36 - webrtc/third_party/pffft/BUILD.gn | 110 + webrtc/third_party/pffft/LICENSE | 1 + webrtc/third_party/pffft/meson.build | 21 + webrtc/third_party/pffft/src/pffft.c | 1881 +++++++++++ webrtc/third_party/pffft/src/pffft.h | 198 ++ webrtc/third_party/rnnoise/BUILD.gn | 1 + webrtc/third_party/rnnoise/COPYING | 1 + webrtc/third_party/rnnoise/meson.build | 15 + .../third_party/rnnoise/src/rnn_activations.h | 102 + .../rnnoise/src/rnn_vad_weights.cc | 401 +++ .../third_party/rnnoise/src/rnn_vad_weights.h | 37 + webrtc/typedefs.h | 111 - 859 files changed, 76187 insertions(+), 49580 deletions(-) create mode 100644 webrtc/LICENSE delete mode 100644 webrtc/LICENSE_THIRD_PARTY create mode 100644 webrtc/api/array_view.h create mode 100644 webrtc/api/audio/audio_frame.cc create mode 100644 webrtc/api/audio/audio_frame.h create mode 100644 webrtc/api/audio/channel_layout.cc create mode 100644 webrtc/api/audio/channel_layout.h create mode 100644 webrtc/api/audio/echo_canceller3_config.cc create mode 100644 webrtc/api/audio/echo_canceller3_config.h create mode 100644 webrtc/api/audio/echo_control.h create mode 100644 webrtc/api/audio_codecs/audio_decoder.cc create mode 100644 webrtc/api/audio_codecs/audio_decoder.h create mode 100644 webrtc/api/audio_codecs/audio_encoder.cc create mode 100644 webrtc/api/audio_codecs/audio_encoder.h create mode 100644 webrtc/api/call/bitrate_allocation.h create mode 100644 webrtc/api/function_view.h create mode 100644 webrtc/api/meson.build create mode 100644 webrtc/api/ref_counted_base.h rename webrtc/{common_types.cc => api/rtp_headers.cc} (62%) create mode 100644 webrtc/api/rtp_headers.h create mode 100644 webrtc/api/rtp_packet_info.cc create mode 100644 webrtc/api/rtp_packet_info.h create mode 100644 webrtc/api/rtp_packet_infos.h create mode 100644 webrtc/api/scoped_refptr.h create mode 100644 webrtc/api/task_queue/queued_task.h create mode 100644 webrtc/api/task_queue/task_queue_base.cc create mode 100644 webrtc/api/task_queue/task_queue_base.h create mode 100644 webrtc/api/units/data_rate.cc create mode 100644 webrtc/api/units/data_rate.h create mode 100644 webrtc/api/units/data_size.cc create mode 100644 webrtc/api/units/data_size.h create mode 100644 webrtc/api/units/frequency.cc create mode 100644 webrtc/api/units/frequency.h create mode 100644 webrtc/api/units/time_delta.cc create mode 100644 webrtc/api/units/time_delta.h create mode 100644 webrtc/api/units/timestamp.cc create mode 100644 webrtc/api/units/timestamp.h create mode 100644 webrtc/api/video/color_space.cc create mode 100644 webrtc/api/video/color_space.h create mode 100644 webrtc/api/video/hdr_metadata.cc create mode 100644 webrtc/api/video/hdr_metadata.h create mode 100644 webrtc/api/video/video_content_type.cc create mode 100644 webrtc/api/video/video_content_type.h create mode 100644 webrtc/api/video/video_rotation.h create mode 100644 webrtc/api/video/video_timing.cc create mode 100644 webrtc/api/video/video_timing.h create mode 100644 webrtc/audio/utility/BUILD.gn create mode 100644 webrtc/audio/utility/audio_frame_operations.cc create mode 100644 webrtc/audio/utility/audio_frame_operations.h delete mode 100644 webrtc/base/BUILD.gn delete mode 100644 webrtc/base/basictypes.h delete mode 100644 webrtc/base/checks.cc delete mode 100644 webrtc/base/checks.h delete mode 100644 webrtc/base/constructormagic.h delete mode 100644 webrtc/base/criticalsection.cc delete mode 100644 webrtc/base/criticalsection.h delete mode 100644 webrtc/base/event.cc delete mode 100644 webrtc/base/event.h delete mode 100644 webrtc/base/maybe.h delete mode 100644 webrtc/base/meson.build delete mode 100644 webrtc/base/platform_file.cc delete mode 100644 webrtc/base/platform_file.h delete mode 100644 webrtc/base/platform_thread.cc delete mode 100644 webrtc/base/scoped_ptr.h delete mode 100644 webrtc/base/stringutils.cc delete mode 100644 webrtc/base/stringutils.h delete mode 100644 webrtc/base/template_util.h delete mode 100644 webrtc/base/thread_annotations.h delete mode 100644 webrtc/base/thread_checker.h delete mode 100644 webrtc/base/thread_checker_impl.cc delete mode 100644 webrtc/base/thread_checker_impl.h delete mode 100644 webrtc/base/win32.h delete mode 100644 webrtc/common_audio/audio_ring_buffer.cc delete mode 100644 webrtc/common_audio/audio_ring_buffer.h delete mode 100644 webrtc/common_audio/blocker.cc delete mode 100644 webrtc/common_audio/blocker.h delete mode 100644 webrtc/common_audio/fft4g.c delete mode 100644 webrtc/common_audio/fft4g.h delete mode 100644 webrtc/common_audio/fir_filter.cc create mode 100644 webrtc/common_audio/fir_filter_avx2.cc create mode 100644 webrtc/common_audio/fir_filter_avx2.h create mode 100644 webrtc/common_audio/fir_filter_c.cc create mode 100644 webrtc/common_audio/fir_filter_c.h create mode 100644 webrtc/common_audio/fir_filter_factory.cc create mode 100644 webrtc/common_audio/fir_filter_factory.h delete mode 100644 webrtc/common_audio/lapped_transform.cc delete mode 100644 webrtc/common_audio/lapped_transform.h create mode 100644 webrtc/common_audio/resampler/sinc_resampler_avx2.cc rename webrtc/common_audio/signal_processing/{dot_product_with_scale.c => dot_product_with_scale.cc} (85%) create mode 100644 webrtc/common_audio/signal_processing/dot_product_with_scale.h create mode 100644 webrtc/common_audio/signal_processing/spl_inl.c create mode 100644 webrtc/common_audio/smoothing_filter.cc create mode 100644 webrtc/common_audio/smoothing_filter.h delete mode 100644 webrtc/common_audio/sparse_fir_filter.cc delete mode 100644 webrtc/common_audio/sparse_fir_filter.h create mode 100644 webrtc/common_audio/third_party/ooura/BUILD.gn create mode 100644 webrtc/common_audio/third_party/ooura/LICENSE rename webrtc/{modules/audio_processing/aec/aec_rdft.c => common_audio/third_party/ooura/fft_size_128/ooura_fft.cc} (64%) create mode 100644 webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.h create mode 100644 webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_mips.cc rename webrtc/{modules/audio_processing/aec/aec_rdft_neon.c => common_audio/third_party/ooura/fft_size_128/ooura_fft_neon.cc} (94%) rename webrtc/{modules/audio_processing/aec/aec_rdft_sse2.c => common_audio/third_party/ooura/fft_size_128/ooura_fft_sse2.cc} (85%) create mode 100644 webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h create mode 100644 webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h create mode 100644 webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.cc create mode 100644 webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.h create mode 100644 webrtc/common_audio/third_party/spl_sqrt_floor/BUILD.gn create mode 100644 webrtc/common_audio/third_party/spl_sqrt_floor/LICENSE rename webrtc/common_audio/{signal_processing => third_party/spl_sqrt_floor}/spl_sqrt_floor.c (96%) create mode 100644 webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h rename webrtc/common_audio/{signal_processing => third_party/spl_sqrt_floor}/spl_sqrt_floor_arm.S (98%) rename webrtc/common_audio/{signal_processing => third_party/spl_sqrt_floor}/spl_sqrt_floor_mips.c (99%) delete mode 100644 webrtc/common_types.h create mode 100644 webrtc/modules/BUILD.gn delete mode 100644 webrtc/modules/audio_coding/codecs/audio_decoder.cc delete mode 100644 webrtc/modules/audio_coding/codecs/audio_encoder.cc rename webrtc/modules/audio_coding/codecs/cng/{include => }/webrtc_cng.h (100%) create mode 100644 webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.h delete mode 100644 webrtc/modules/audio_coding/codecs/isac/main/source/filterbank_tables.c delete mode 100644 webrtc/modules/audio_coding/codecs/isac/main/source/filterbank_tables.h create mode 100644 webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.c create mode 100644 webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.h create mode 100644 webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.h delete mode 100644 webrtc/modules/audio_processing/aec/aec_common.h delete mode 100644 webrtc/modules/audio_processing/aec/aec_core.c delete mode 100644 webrtc/modules/audio_processing/aec/aec_core.h delete mode 100644 webrtc/modules/audio_processing/aec/aec_core_internal.h delete mode 100644 webrtc/modules/audio_processing/aec/aec_core_mips.c delete mode 100644 webrtc/modules/audio_processing/aec/aec_core_neon.c delete mode 100644 webrtc/modules/audio_processing/aec/aec_core_sse2.c delete mode 100644 webrtc/modules/audio_processing/aec/aec_rdft.h delete mode 100644 webrtc/modules/audio_processing/aec/aec_rdft_mips.c delete mode 100644 webrtc/modules/audio_processing/aec/aec_resampler.c delete mode 100644 webrtc/modules/audio_processing/aec/aec_resampler.h delete mode 100644 webrtc/modules/audio_processing/aec/echo_cancellation.c delete mode 100644 webrtc/modules/audio_processing/aec/echo_cancellation_internal.h delete mode 100644 webrtc/modules/audio_processing/aec/include/echo_cancellation.h create mode 100644 webrtc/modules/audio_processing/aec3/BUILD.gn create mode 100644 webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc create mode 100644 webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h create mode 100644 webrtc/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc create mode 100644 webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc create mode 100644 webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.h create mode 100644 webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl_avx2.cc create mode 100644 webrtc/modules/audio_processing/aec3/aec3_common.cc create mode 100644 webrtc/modules/audio_processing/aec3/aec3_common.h create mode 100644 webrtc/modules/audio_processing/aec3/aec3_fft.cc create mode 100644 webrtc/modules/audio_processing/aec3/aec3_fft.h create mode 100644 webrtc/modules/audio_processing/aec3/aec_state.cc create mode 100644 webrtc/modules/audio_processing/aec3/aec_state.h create mode 100644 webrtc/modules/audio_processing/aec3/alignment_mixer.cc create mode 100644 webrtc/modules/audio_processing/aec3/alignment_mixer.h create mode 100644 webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.cc create mode 100644 webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.h create mode 100644 webrtc/modules/audio_processing/aec3/block_buffer.cc create mode 100644 webrtc/modules/audio_processing/aec3/block_buffer.h create mode 100644 webrtc/modules/audio_processing/aec3/block_delay_buffer.cc create mode 100644 webrtc/modules/audio_processing/aec3/block_delay_buffer.h create mode 100644 webrtc/modules/audio_processing/aec3/block_framer.cc create mode 100644 webrtc/modules/audio_processing/aec3/block_framer.h create mode 100644 webrtc/modules/audio_processing/aec3/block_processor.cc create mode 100644 webrtc/modules/audio_processing/aec3/block_processor.h create mode 100644 webrtc/modules/audio_processing/aec3/block_processor_metrics.cc create mode 100644 webrtc/modules/audio_processing/aec3/block_processor_metrics.h create mode 100644 webrtc/modules/audio_processing/aec3/clockdrift_detector.cc create mode 100644 webrtc/modules/audio_processing/aec3/clockdrift_detector.h create mode 100644 webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.cc create mode 100644 webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.h create mode 100644 webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc create mode 100644 webrtc/modules/audio_processing/aec3/comfort_noise_generator.h create mode 100644 webrtc/modules/audio_processing/aec3/decimator.cc create mode 100644 webrtc/modules/audio_processing/aec3/decimator.h create mode 100644 webrtc/modules/audio_processing/aec3/delay_estimate.h create mode 100644 webrtc/modules/audio_processing/aec3/dominant_nearend_detector.cc create mode 100644 webrtc/modules/audio_processing/aec3/dominant_nearend_detector.h create mode 100644 webrtc/modules/audio_processing/aec3/downsampled_render_buffer.cc create mode 100644 webrtc/modules/audio_processing/aec3/downsampled_render_buffer.h create mode 100644 webrtc/modules/audio_processing/aec3/echo_audibility.cc create mode 100644 webrtc/modules/audio_processing/aec3/echo_audibility.h create mode 100644 webrtc/modules/audio_processing/aec3/echo_canceller3.cc create mode 100644 webrtc/modules/audio_processing/aec3/echo_canceller3.h create mode 100644 webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.cc create mode 100644 webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.h create mode 100644 webrtc/modules/audio_processing/aec3/echo_path_variability.cc create mode 100644 webrtc/modules/audio_processing/aec3/echo_path_variability.h create mode 100644 webrtc/modules/audio_processing/aec3/echo_remover.cc create mode 100644 webrtc/modules/audio_processing/aec3/echo_remover.h create mode 100644 webrtc/modules/audio_processing/aec3/echo_remover_metrics.cc create mode 100644 webrtc/modules/audio_processing/aec3/echo_remover_metrics.h create mode 100644 webrtc/modules/audio_processing/aec3/erl_estimator.cc create mode 100644 webrtc/modules/audio_processing/aec3/erl_estimator.h create mode 100644 webrtc/modules/audio_processing/aec3/erle_estimator.cc create mode 100644 webrtc/modules/audio_processing/aec3/erle_estimator.h create mode 100644 webrtc/modules/audio_processing/aec3/fft_buffer.cc create mode 100644 webrtc/modules/audio_processing/aec3/fft_buffer.h create mode 100644 webrtc/modules/audio_processing/aec3/fft_data.h create mode 100644 webrtc/modules/audio_processing/aec3/fft_data_avx2.cc create mode 100644 webrtc/modules/audio_processing/aec3/filter_analyzer.cc create mode 100644 webrtc/modules/audio_processing/aec3/filter_analyzer.h create mode 100644 webrtc/modules/audio_processing/aec3/frame_blocker.cc create mode 100644 webrtc/modules/audio_processing/aec3/frame_blocker.h create mode 100644 webrtc/modules/audio_processing/aec3/fullband_erle_estimator.cc create mode 100644 webrtc/modules/audio_processing/aec3/fullband_erle_estimator.h create mode 100644 webrtc/modules/audio_processing/aec3/matched_filter.cc create mode 100644 webrtc/modules/audio_processing/aec3/matched_filter.h create mode 100644 webrtc/modules/audio_processing/aec3/matched_filter_avx2.cc create mode 100644 webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc create mode 100644 webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.h create mode 100644 webrtc/modules/audio_processing/aec3/moving_average.cc create mode 100644 webrtc/modules/audio_processing/aec3/moving_average.h create mode 100644 webrtc/modules/audio_processing/aec3/nearend_detector.h create mode 100644 webrtc/modules/audio_processing/aec3/refined_filter_update_gain.cc create mode 100644 webrtc/modules/audio_processing/aec3/refined_filter_update_gain.h create mode 100644 webrtc/modules/audio_processing/aec3/render_buffer.cc create mode 100644 webrtc/modules/audio_processing/aec3/render_buffer.h create mode 100644 webrtc/modules/audio_processing/aec3/render_delay_buffer.cc create mode 100644 webrtc/modules/audio_processing/aec3/render_delay_buffer.h create mode 100644 webrtc/modules/audio_processing/aec3/render_delay_controller.cc create mode 100644 webrtc/modules/audio_processing/aec3/render_delay_controller.h create mode 100644 webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.cc create mode 100644 webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.h create mode 100644 webrtc/modules/audio_processing/aec3/render_signal_analyzer.cc create mode 100644 webrtc/modules/audio_processing/aec3/render_signal_analyzer.h create mode 100644 webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc create mode 100644 webrtc/modules/audio_processing/aec3/residual_echo_estimator.h create mode 100644 webrtc/modules/audio_processing/aec3/reverb_decay_estimator.cc create mode 100644 webrtc/modules/audio_processing/aec3/reverb_decay_estimator.h create mode 100644 webrtc/modules/audio_processing/aec3/reverb_frequency_response.cc create mode 100644 webrtc/modules/audio_processing/aec3/reverb_frequency_response.h create mode 100644 webrtc/modules/audio_processing/aec3/reverb_model.cc create mode 100644 webrtc/modules/audio_processing/aec3/reverb_model.h create mode 100644 webrtc/modules/audio_processing/aec3/reverb_model_estimator.cc create mode 100644 webrtc/modules/audio_processing/aec3/reverb_model_estimator.h create mode 100644 webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc create mode 100644 webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.h create mode 100644 webrtc/modules/audio_processing/aec3/spectrum_buffer.cc create mode 100644 webrtc/modules/audio_processing/aec3/spectrum_buffer.h create mode 100644 webrtc/modules/audio_processing/aec3/stationarity_estimator.cc create mode 100644 webrtc/modules/audio_processing/aec3/stationarity_estimator.h create mode 100644 webrtc/modules/audio_processing/aec3/subband_erle_estimator.cc create mode 100644 webrtc/modules/audio_processing/aec3/subband_erle_estimator.h create mode 100644 webrtc/modules/audio_processing/aec3/subband_nearend_detector.cc create mode 100644 webrtc/modules/audio_processing/aec3/subband_nearend_detector.h create mode 100644 webrtc/modules/audio_processing/aec3/subtractor.cc create mode 100644 webrtc/modules/audio_processing/aec3/subtractor.h create mode 100644 webrtc/modules/audio_processing/aec3/subtractor_output.cc create mode 100644 webrtc/modules/audio_processing/aec3/subtractor_output.h create mode 100644 webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.cc create mode 100644 webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.h create mode 100644 webrtc/modules/audio_processing/aec3/suppression_filter.cc create mode 100644 webrtc/modules/audio_processing/aec3/suppression_filter.h create mode 100644 webrtc/modules/audio_processing/aec3/suppression_gain.cc create mode 100644 webrtc/modules/audio_processing/aec3/suppression_gain.h create mode 100644 webrtc/modules/audio_processing/aec3/transparent_mode.cc create mode 100644 webrtc/modules/audio_processing/aec3/transparent_mode.h create mode 100644 webrtc/modules/audio_processing/aec3/vector_math.h create mode 100644 webrtc/modules/audio_processing/aec3/vector_math_avx2.cc create mode 100644 webrtc/modules/audio_processing/aec_dump/BUILD.gn create mode 100644 webrtc/modules/audio_processing/aec_dump/aec_dump_factory.h create mode 100644 webrtc/modules/audio_processing/aec_dump/null_aec_dump_factory.cc create mode 100644 webrtc/modules/audio_processing/aecm/BUILD.gn delete mode 100644 webrtc/modules/audio_processing/aecm/aecm_core.c create mode 100644 webrtc/modules/audio_processing/aecm/aecm_core.cc rename webrtc/modules/audio_processing/aecm/{aecm_core_c.c => aecm_core_c.cc} (63%) delete mode 100644 webrtc/modules/audio_processing/aecm/aecm_core_mips.c create mode 100644 webrtc/modules/audio_processing/aecm/aecm_core_mips.cc rename webrtc/modules/audio_processing/aecm/{aecm_core_neon.c => aecm_core_neon.cc} (84%) delete mode 100644 webrtc/modules/audio_processing/aecm/echo_control_mobile.c create mode 100644 webrtc/modules/audio_processing/aecm/echo_control_mobile.cc rename webrtc/modules/audio_processing/aecm/{include => }/echo_control_mobile.h (78%) create mode 100644 webrtc/modules/audio_processing/agc/BUILD.gn create mode 100644 webrtc/modules/audio_processing/agc/gain_control.h delete mode 100644 webrtc/modules/audio_processing/agc/legacy/analog_agc.c create mode 100644 webrtc/modules/audio_processing/agc/legacy/analog_agc.cc delete mode 100644 webrtc/modules/audio_processing/agc/legacy/digital_agc.c create mode 100644 webrtc/modules/audio_processing/agc/legacy/digital_agc.cc rename webrtc/modules/audio_processing/agc/{histogram.cc => loudness_histogram.cc} (59%) rename webrtc/modules/audio_processing/agc/{histogram.h => loudness_histogram.h} (71%) create mode 100644 webrtc/modules/audio_processing/agc/mock_agc.h create mode 100644 webrtc/modules/audio_processing/agc2/BUILD.gn create mode 100644 webrtc/modules/audio_processing/agc2/adaptive_agc.cc create mode 100644 webrtc/modules/audio_processing/agc2/adaptive_agc.h create mode 100644 webrtc/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc create mode 100644 webrtc/modules/audio_processing/agc2/adaptive_digital_gain_applier.h create mode 100644 webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc create mode 100644 webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator.h create mode 100644 webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc create mode 100644 webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h create mode 100644 webrtc/modules/audio_processing/agc2/agc2_common.h create mode 100644 webrtc/modules/audio_processing/agc2/agc2_testing_common.cc create mode 100644 webrtc/modules/audio_processing/agc2/agc2_testing_common.h create mode 100644 webrtc/modules/audio_processing/agc2/biquad_filter.cc create mode 100644 webrtc/modules/audio_processing/agc2/biquad_filter.h create mode 100644 webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc create mode 100644 webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.h create mode 100644 webrtc/modules/audio_processing/agc2/down_sampler.cc create mode 100644 webrtc/modules/audio_processing/agc2/down_sampler.h create mode 100644 webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.cc create mode 100644 webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.h create mode 100644 webrtc/modules/audio_processing/agc2/fixed_gain_controller.cc create mode 100644 webrtc/modules/audio_processing/agc2/gain_applier.cc create mode 100644 webrtc/modules/audio_processing/agc2/gain_applier.h create mode 100644 webrtc/modules/audio_processing/agc2/interpolated_gain_curve.cc create mode 100644 webrtc/modules/audio_processing/agc2/interpolated_gain_curve.h create mode 100644 webrtc/modules/audio_processing/agc2/limiter.cc create mode 100644 webrtc/modules/audio_processing/agc2/limiter.h create mode 100644 webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.cc create mode 100644 webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.h create mode 100644 webrtc/modules/audio_processing/agc2/noise_level_estimator.cc create mode 100644 webrtc/modules/audio_processing/agc2/noise_level_estimator.h create mode 100644 webrtc/modules/audio_processing/agc2/noise_spectrum_estimator.cc create mode 100644 webrtc/modules/audio_processing/agc2/noise_spectrum_estimator.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/BUILD.gn create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/common.cc create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/common.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.cc create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.cc create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/pitch_info.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.cc create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/ring_buffer.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/rnn.cc create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/rnn.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_tool.cc create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.cc create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.cc create mode 100644 webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.h create mode 100644 webrtc/modules/audio_processing/agc2/saturation_protector.cc create mode 100644 webrtc/modules/audio_processing/agc2/saturation_protector.h create mode 100644 webrtc/modules/audio_processing/agc2/signal_classifier.cc create mode 100644 webrtc/modules/audio_processing/agc2/signal_classifier.h create mode 100644 webrtc/modules/audio_processing/agc2/vad_with_level.cc create mode 100644 webrtc/modules/audio_processing/agc2/vad_with_level.h create mode 100644 webrtc/modules/audio_processing/agc2/vector_float_frame.cc create mode 100644 webrtc/modules/audio_processing/agc2/vector_float_frame.h create mode 100644 webrtc/modules/audio_processing/audio_processing_builder_impl.cc delete mode 100644 webrtc/modules/audio_processing/beamformer/array_util.cc delete mode 100644 webrtc/modules/audio_processing/beamformer/array_util.h delete mode 100644 webrtc/modules/audio_processing/beamformer/beamformer.h delete mode 100644 webrtc/modules/audio_processing/beamformer/complex_matrix.h delete mode 100644 webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.cc delete mode 100644 webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.h delete mode 100644 webrtc/modules/audio_processing/beamformer/matrix.h delete mode 100644 webrtc/modules/audio_processing/beamformer/matrix_test_helpers.h delete mode 100644 webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc delete mode 100644 webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h delete mode 100644 webrtc/modules/audio_processing/echo_cancellation_impl.cc delete mode 100644 webrtc/modules/audio_processing/echo_cancellation_impl.h create mode 100644 webrtc/modules/audio_processing/echo_detector/circular_buffer.cc create mode 100644 webrtc/modules/audio_processing/echo_detector/circular_buffer.h create mode 100644 webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.cc create mode 100644 webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.h create mode 100644 webrtc/modules/audio_processing/echo_detector/moving_max.cc create mode 100644 webrtc/modules/audio_processing/echo_detector/moving_max.h create mode 100644 webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc create mode 100644 webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.h create mode 100644 webrtc/modules/audio_processing/gain_controller2.cc create mode 100644 webrtc/modules/audio_processing/gain_controller2.h create mode 100644 webrtc/modules/audio_processing/high_pass_filter.cc create mode 100644 webrtc/modules/audio_processing/high_pass_filter.h delete mode 100644 webrtc/modules/audio_processing/high_pass_filter_impl.cc delete mode 100644 webrtc/modules/audio_processing/high_pass_filter_impl.h create mode 100644 webrtc/modules/audio_processing/include/aec_dump.cc create mode 100644 webrtc/modules/audio_processing/include/aec_dump.h create mode 100644 webrtc/modules/audio_processing/include/audio_frame_proxies.cc create mode 100644 webrtc/modules/audio_processing/include/audio_frame_proxies.h create mode 100644 webrtc/modules/audio_processing/include/audio_frame_view.h create mode 100644 webrtc/modules/audio_processing/include/audio_processing.cc create mode 100644 webrtc/modules/audio_processing/include/audio_processing_statistics.cc create mode 100644 webrtc/modules/audio_processing/include/audio_processing_statistics.h create mode 100644 webrtc/modules/audio_processing/include/config.cc rename webrtc/{common.h => modules/audio_processing/include/config.h} (61%) delete mode 100644 webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.cc delete mode 100644 webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.h delete mode 100644 webrtc/modules/audio_processing/intelligibility/intelligibility_utils.cc delete mode 100644 webrtc/modules/audio_processing/intelligibility/intelligibility_utils.h create mode 100644 webrtc/modules/audio_processing/level_estimator.cc create mode 100644 webrtc/modules/audio_processing/level_estimator.h delete mode 100644 webrtc/modules/audio_processing/level_estimator_impl.cc delete mode 100644 webrtc/modules/audio_processing/level_estimator_impl.h delete mode 100644 webrtc/modules/audio_processing/logging/aec_logging.h delete mode 100644 webrtc/modules/audio_processing/logging/aec_logging_file_handling.cc delete mode 100644 webrtc/modules/audio_processing/logging/aec_logging_file_handling.h create mode 100644 webrtc/modules/audio_processing/logging/apm_data_dumper.cc create mode 100644 webrtc/modules/audio_processing/logging/apm_data_dumper.h delete mode 100644 webrtc/modules/audio_processing/noise_suppression_impl.cc delete mode 100644 webrtc/modules/audio_processing/noise_suppression_impl.h create mode 100644 webrtc/modules/audio_processing/ns/BUILD.gn delete mode 100644 webrtc/modules/audio_processing/ns/defines.h create mode 100644 webrtc/modules/audio_processing/ns/fast_math.cc create mode 100644 webrtc/modules/audio_processing/ns/fast_math.h create mode 100644 webrtc/modules/audio_processing/ns/histograms.cc create mode 100644 webrtc/modules/audio_processing/ns/histograms.h delete mode 100644 webrtc/modules/audio_processing/ns/include/noise_suppression.h delete mode 100644 webrtc/modules/audio_processing/ns/include/noise_suppression_x.h create mode 100644 webrtc/modules/audio_processing/ns/noise_estimator.cc create mode 100644 webrtc/modules/audio_processing/ns/noise_estimator.h delete mode 100644 webrtc/modules/audio_processing/ns/noise_suppression.c delete mode 100644 webrtc/modules/audio_processing/ns/noise_suppression_x.c create mode 100644 webrtc/modules/audio_processing/ns/noise_suppressor.cc create mode 100644 webrtc/modules/audio_processing/ns/noise_suppressor.h create mode 100644 webrtc/modules/audio_processing/ns/ns_common.h create mode 100644 webrtc/modules/audio_processing/ns/ns_config.h delete mode 100644 webrtc/modules/audio_processing/ns/ns_core.c delete mode 100644 webrtc/modules/audio_processing/ns/ns_core.h create mode 100644 webrtc/modules/audio_processing/ns/ns_fft.cc create mode 100644 webrtc/modules/audio_processing/ns/ns_fft.h delete mode 100644 webrtc/modules/audio_processing/ns/nsx_core.c delete mode 100644 webrtc/modules/audio_processing/ns/nsx_core.h delete mode 100644 webrtc/modules/audio_processing/ns/nsx_core_c.c delete mode 100644 webrtc/modules/audio_processing/ns/nsx_core_mips.c delete mode 100644 webrtc/modules/audio_processing/ns/nsx_core_neon.c delete mode 100644 webrtc/modules/audio_processing/ns/nsx_defines.h create mode 100644 webrtc/modules/audio_processing/ns/prior_signal_model.cc create mode 100644 webrtc/modules/audio_processing/ns/prior_signal_model.h create mode 100644 webrtc/modules/audio_processing/ns/prior_signal_model_estimator.cc create mode 100644 webrtc/modules/audio_processing/ns/prior_signal_model_estimator.h create mode 100644 webrtc/modules/audio_processing/ns/quantile_noise_estimator.cc create mode 100644 webrtc/modules/audio_processing/ns/quantile_noise_estimator.h create mode 100644 webrtc/modules/audio_processing/ns/signal_model.cc create mode 100644 webrtc/modules/audio_processing/ns/signal_model.h create mode 100644 webrtc/modules/audio_processing/ns/signal_model_estimator.cc create mode 100644 webrtc/modules/audio_processing/ns/signal_model_estimator.h create mode 100644 webrtc/modules/audio_processing/ns/speech_probability_estimator.cc create mode 100644 webrtc/modules/audio_processing/ns/speech_probability_estimator.h create mode 100644 webrtc/modules/audio_processing/ns/suppression_params.cc create mode 100644 webrtc/modules/audio_processing/ns/suppression_params.h create mode 100644 webrtc/modules/audio_processing/ns/wiener_filter.cc create mode 100644 webrtc/modules/audio_processing/ns/wiener_filter.h delete mode 100644 webrtc/modules/audio_processing/ns/windows_private.h create mode 100644 webrtc/modules/audio_processing/optionally_built_submodule_creators.cc create mode 100644 webrtc/modules/audio_processing/optionally_built_submodule_creators.h delete mode 100644 webrtc/modules/audio_processing/processing_component.cc delete mode 100644 webrtc/modules/audio_processing/processing_component.h create mode 100644 webrtc/modules/audio_processing/render_queue_item_verifier.h create mode 100644 webrtc/modules/audio_processing/residual_echo_detector.cc create mode 100644 webrtc/modules/audio_processing/residual_echo_detector.h create mode 100644 webrtc/modules/audio_processing/transient/BUILD.gn rename webrtc/modules/audio_processing/transient/{transient_suppressor.cc => transient_suppressor_impl.cc} (78%) create mode 100644 webrtc/modules/audio_processing/transient/transient_suppressor_impl.h create mode 100644 webrtc/modules/audio_processing/transient/windows_private.h create mode 100644 webrtc/modules/audio_processing/utility/BUILD.gn create mode 100644 webrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc create mode 100644 webrtc/modules/audio_processing/utility/cascaded_biquad_filter.h rename webrtc/modules/audio_processing/utility/{delay_estimator.c => delay_estimator.cc} (84%) rename webrtc/modules/audio_processing/utility/{delay_estimator_wrapper.c => delay_estimator_wrapper.cc} (82%) create mode 100644 webrtc/modules/audio_processing/utility/pffft_wrapper.cc create mode 100644 webrtc/modules/audio_processing/utility/pffft_wrapper.h create mode 100644 webrtc/modules/audio_processing/vad/BUILD.gn create mode 100644 webrtc/modules/audio_processing/voice_detection.cc create mode 100644 webrtc/modules/audio_processing/voice_detection.h delete mode 100644 webrtc/modules/audio_processing/voice_detection_impl.cc delete mode 100644 webrtc/modules/audio_processing/voice_detection_impl.h delete mode 100644 webrtc/modules/interface/meson.build delete mode 100644 webrtc/modules/interface/module_common_types.h create mode 100644 webrtc/modules/third_party/fft/BUILD.gn create mode 100644 webrtc/modules/third_party/fft/LICENSE rename webrtc/modules/{audio_coding/codecs/isac/main/source => third_party/fft}/fft.c (99%) rename webrtc/modules/{audio_coding/codecs/isac/main/source => third_party/fft}/fft.h (61%) create mode 100644 webrtc/modules/third_party/fft/meson.build delete mode 100644 webrtc/modules/utility/interface/audio_frame_operations.h create mode 100644 webrtc/rtc_base/BUILD.gn rename webrtc/{base => rtc_base}/arraysize.h (85%) rename webrtc/{base/atomicops.h => rtc_base/atomic_ops.h} (53%) create mode 100644 webrtc/rtc_base/buffer.h create mode 100644 webrtc/rtc_base/checks.cc create mode 100644 webrtc/rtc_base/checks.h rename webrtc/{system_wrappers/include => rtc_base}/compile_assert_c.h (59%) create mode 100644 webrtc/rtc_base/constructor_magic.h create mode 100644 webrtc/rtc_base/deprecation.h create mode 100644 webrtc/rtc_base/event.cc create mode 100644 webrtc/rtc_base/event.h create mode 100644 webrtc/rtc_base/event_tracer.cc create mode 100644 webrtc/rtc_base/event_tracer.h create mode 100644 webrtc/rtc_base/experiments/field_trial_parser.cc create mode 100644 webrtc/rtc_base/experiments/field_trial_parser.h create mode 100644 webrtc/rtc_base/gtest_prod_util.h create mode 100644 webrtc/rtc_base/ignore_wundef.h create mode 100644 webrtc/rtc_base/logging.cc create mode 100644 webrtc/rtc_base/logging.h create mode 100644 webrtc/rtc_base/memory/BUILD.gn rename webrtc/{system_wrappers/source => rtc_base/memory}/aligned_malloc.cc (92%) rename webrtc/{system_wrappers/include => rtc_base/memory}/aligned_malloc.h (75%) create mode 100644 webrtc/rtc_base/meson.build create mode 100644 webrtc/rtc_base/numerics/safe_compare.h rename webrtc/{base => rtc_base/numerics}/safe_conversions.h (69%) rename webrtc/{base => rtc_base/numerics}/safe_conversions_impl.h (51%) create mode 100644 webrtc/rtc_base/numerics/safe_minmax.h create mode 100644 webrtc/rtc_base/platform_thread.cc create mode 100644 webrtc/rtc_base/platform_thread.h create mode 100644 webrtc/rtc_base/platform_thread_types.cc rename webrtc/{base/platform_thread.h => rtc_base/platform_thread_types.h} (55%) create mode 100644 webrtc/rtc_base/race_checker.cc create mode 100644 webrtc/rtc_base/race_checker.h create mode 100644 webrtc/rtc_base/ref_count.h create mode 100644 webrtc/rtc_base/ref_counted_object.h create mode 100644 webrtc/rtc_base/ref_counter.h create mode 100644 webrtc/rtc_base/sanitizer.h create mode 100644 webrtc/rtc_base/string_encode.cc create mode 100644 webrtc/rtc_base/string_encode.h create mode 100644 webrtc/rtc_base/string_to_number.cc create mode 100644 webrtc/rtc_base/string_to_number.h create mode 100644 webrtc/rtc_base/string_utils.cc create mode 100644 webrtc/rtc_base/string_utils.h create mode 100644 webrtc/rtc_base/strings/string_builder.cc create mode 100644 webrtc/rtc_base/strings/string_builder.h create mode 100644 webrtc/rtc_base/swap_queue.h create mode 100644 webrtc/rtc_base/synchronization/BUILD.gn create mode 100644 webrtc/rtc_base/synchronization/mutex.cc create mode 100644 webrtc/rtc_base/synchronization/mutex.h create mode 100644 webrtc/rtc_base/synchronization/mutex_critical_section.h create mode 100644 webrtc/rtc_base/synchronization/mutex_pthread.h rename webrtc/{system_wrappers/source => rtc_base/synchronization}/rw_lock_posix.cc (90%) rename webrtc/{system_wrappers/source => rtc_base/synchronization}/rw_lock_posix.h (76%) create mode 100644 webrtc/rtc_base/synchronization/rw_lock_win.cc rename webrtc/{system_wrappers/source => rtc_base/synchronization}/rw_lock_win.h (59%) rename webrtc/{system_wrappers/source/rw_lock.cc => rtc_base/synchronization/rw_lock_wrapper.cc} (58%) rename webrtc/{system_wrappers/include => rtc_base/synchronization}/rw_lock_wrapper.h (54%) create mode 100644 webrtc/rtc_base/synchronization/sequence_checker.cc create mode 100644 webrtc/rtc_base/synchronization/sequence_checker.h create mode 100644 webrtc/rtc_base/synchronization/yield.cc create mode 100644 webrtc/rtc_base/synchronization/yield.h create mode 100644 webrtc/rtc_base/synchronization/yield_policy.cc create mode 100644 webrtc/rtc_base/synchronization/yield_policy.h create mode 100644 webrtc/rtc_base/system/arch.h rename webrtc/{system_wrappers/include => rtc_base/system}/asm_defines.h (84%) create mode 100644 webrtc/rtc_base/system/file_wrapper.cc create mode 100644 webrtc/rtc_base/system/file_wrapper.h create mode 100644 webrtc/rtc_base/system/ignore_warnings.h create mode 100644 webrtc/rtc_base/system/inline.h create mode 100644 webrtc/rtc_base/system/rtc_export.h create mode 100644 webrtc/rtc_base/system/unused.h create mode 100644 webrtc/rtc_base/system/warn_current_thread_is_deadlocked.h create mode 100644 webrtc/rtc_base/thread_annotations.h create mode 100644 webrtc/rtc_base/thread_checker.h create mode 100644 webrtc/rtc_base/time_utils.cc create mode 100644 webrtc/rtc_base/time_utils.h create mode 100644 webrtc/rtc_base/trace_event.h create mode 100644 webrtc/rtc_base/type_traits.h create mode 100644 webrtc/rtc_base/units/BUILD.gn create mode 100644 webrtc/rtc_base/units/unit_base.h create mode 100644 webrtc/rtc_base/win32.h create mode 100644 webrtc/rtc_base/zero_memory.cc create mode 100644 webrtc/rtc_base/zero_memory.h delete mode 100644 webrtc/system_wrappers/include/aligned_array.h delete mode 100644 webrtc/system_wrappers/include/condition_variable_wrapper.h delete mode 100644 webrtc/system_wrappers/include/critical_section_wrapper.h delete mode 100644 webrtc/system_wrappers/include/event_wrapper.h create mode 100644 webrtc/system_wrappers/include/field_trial.h delete mode 100644 webrtc/system_wrappers/include/file_wrapper.h delete mode 100644 webrtc/system_wrappers/include/fix_interlocked_exchange_pointer_win.h delete mode 100644 webrtc/system_wrappers/include/logging.h delete mode 100644 webrtc/system_wrappers/include/scoped_vector.h delete mode 100644 webrtc/system_wrappers/include/static_instance.h delete mode 100644 webrtc/system_wrappers/include/stl_util.h delete mode 100644 webrtc/system_wrappers/include/thread_wrapper.h delete mode 100644 webrtc/system_wrappers/include/trace.h delete mode 100644 webrtc/system_wrappers/source/condition_variable.cc delete mode 100644 webrtc/system_wrappers/source/condition_variable_event_win.cc delete mode 100644 webrtc/system_wrappers/source/condition_variable_event_win.h delete mode 100644 webrtc/system_wrappers/source/condition_variable_native_win.cc delete mode 100644 webrtc/system_wrappers/source/condition_variable_native_win.h delete mode 100644 webrtc/system_wrappers/source/critical_section.cc delete mode 100644 webrtc/system_wrappers/source/critical_section_posix.cc delete mode 100644 webrtc/system_wrappers/source/critical_section_posix.h delete mode 100644 webrtc/system_wrappers/source/critical_section_win.cc delete mode 100644 webrtc/system_wrappers/source/critical_section_win.h delete mode 100644 webrtc/system_wrappers/source/event.cc delete mode 100644 webrtc/system_wrappers/source/event_timer_posix.cc delete mode 100644 webrtc/system_wrappers/source/event_timer_posix.h delete mode 100644 webrtc/system_wrappers/source/event_timer_win.cc delete mode 100644 webrtc/system_wrappers/source/event_timer_win.h create mode 100644 webrtc/system_wrappers/source/field_trial.cc delete mode 100644 webrtc/system_wrappers/source/file_impl.cc delete mode 100644 webrtc/system_wrappers/source/file_impl.h delete mode 100644 webrtc/system_wrappers/source/logging.cc create mode 100644 webrtc/system_wrappers/source/metrics.cc delete mode 100644 webrtc/system_wrappers/source/metrics_default.cc delete mode 100644 webrtc/system_wrappers/source/rw_lock_generic.cc delete mode 100644 webrtc/system_wrappers/source/rw_lock_generic.h delete mode 100644 webrtc/system_wrappers/source/rw_lock_win.cc delete mode 100644 webrtc/system_wrappers/source/thread.cc delete mode 100644 webrtc/system_wrappers/source/thread_posix.cc delete mode 100644 webrtc/system_wrappers/source/thread_posix.h delete mode 100644 webrtc/system_wrappers/source/thread_win.cc delete mode 100644 webrtc/system_wrappers/source/thread_win.h delete mode 100644 webrtc/system_wrappers/source/trace_impl.cc delete mode 100644 webrtc/system_wrappers/source/trace_impl.h delete mode 100644 webrtc/system_wrappers/source/trace_posix.cc delete mode 100644 webrtc/system_wrappers/source/trace_posix.h delete mode 100644 webrtc/system_wrappers/source/trace_win.cc delete mode 100644 webrtc/system_wrappers/source/trace_win.h create mode 100644 webrtc/third_party/pffft/BUILD.gn create mode 100644 webrtc/third_party/pffft/LICENSE create mode 100644 webrtc/third_party/pffft/meson.build create mode 100644 webrtc/third_party/pffft/src/pffft.c create mode 100644 webrtc/third_party/pffft/src/pffft.h create mode 100644 webrtc/third_party/rnnoise/BUILD.gn create mode 100644 webrtc/third_party/rnnoise/COPYING create mode 100644 webrtc/third_party/rnnoise/meson.build create mode 100644 webrtc/third_party/rnnoise/src/rnn_activations.h create mode 100644 webrtc/third_party/rnnoise/src/rnn_vad_weights.cc create mode 100644 webrtc/third_party/rnnoise/src/rnn_vad_weights.h delete mode 100644 webrtc/typedefs.h diff --git a/README.md b/README.md index 9133f17..ce885d4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ About ===== This is meant to be a more Linux packaging friendly copy of the AudioProcessing -module from the WebRTC[1][2] project. The ideal case is that we make no changes to +module from the WebRTC[1] project. The ideal case is that we make no changes to the code to make tracking upstream code easy. This package currently only includes the AudioProcessing bits, but I am very @@ -11,18 +11,17 @@ the code and hopefully eventually have a single point of packaging all the WebRTC code to help people reuse the code and avoid keeping private copies in several different projects. -[1] http://code.google.com/p/webrtc/ -[2] https://chromium.googlesource.com/external/webrtc/trunk/webrtc.git +[1] https://webrtc.googlesource.com/src Feedback ======== Patches, suggestions welcome. You can send them to the PulseAudio mailing -list[3] or to me at the address below. +list[2] or to me at the address below. -- Arun Raghavan -[3] http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss +[2] http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss Notes ==== diff --git a/UPDATING.md b/UPDATING.md index 3f6a15a..ce851f9 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -8,7 +8,7 @@ project source code. webrtc git repository Chromium uses. 2. Instructions on checking out the Chromium tree are on the - [Chromium site][get-chromium]. As a shortcut, you can look at the DEPS file + [WebRTC repo][get-webrtc]. As a shortcut, you can look at the DEPS file in the Chromium tree for the current webrtc version being used, and then just use that commit hash with the webrtc tree. @@ -61,6 +61,6 @@ project source code. * Run some test streams through the canceller to make sure it is working fine. -[get-chromium]: http://dev.chromium.org/developers/how-tos/get-the-code +[get-webrtc]: https://webrtc.googlesource.com/src/ [meld]: http://meldmerge.org/ [libtool-version-info]: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html diff --git a/meson.build b/meson.build index 076f8f3..a15f2c6 100644 --- a/meson.build +++ b/meson.build @@ -1,12 +1,17 @@ project('webrtc-audio-processing', 'c', 'cpp', - version : '0.3.1', + version : '0.4.0', meson_version : '>= 0.52', default_options : [ 'warning_level=1', - 'buildtype=debugoptimized' ]) + 'buildtype=debugoptimized', + 'c_std=c11', + 'cpp_std=c++14', + ] +) soversion = 0 cc = meson.get_compiler('c') +cpp = meson.get_compiler('cpp') host_system = host_machine.system() @@ -16,14 +21,28 @@ os_deps = [] have_posix = false have_win = false +absl_dep = [ + cpp.find_library('absl_base'), + cpp.find_library('absl_bad_optional_access'), + cpp.find_library('absl_flags_internal'), + cpp.find_library('absl_flags_marshalling'), + cpp.find_library('absl_flags_parse'), + cpp.find_library('absl_flags_registry'), + cpp.find_library('absl_flags_usage_internal'), + cpp.find_library('absl_raw_logging_internal'), + cpp.find_library('absl_strings'), + cpp.find_library('absl_synchronization'), + cpp.find_library('absl_throw_delegate'), +] + if ['darwin', 'ios'].contains(host_system) - os_cflags = ['-DWEBRTC_MAC', '-DWEBRTC_THREAD_RR', '-DWEBRTC_CLOCK_TYPE_REALTIME'] + os_cflags = ['-DWEBRTC_MAC'] if host_system == 'ios' os_cflags += ['-DWEBRTC_IOS'] endif have_posix = true elif host_system == 'android' - os_cflags += ['-DWEBRTC_ANDROID', '-DWEBRTC_LINUX', '-DWEBRTC_THREAD_RR', '-DWEBRTC_CLOCK_TYPE_REALTIME'] + os_cflags += ['-DWEBRTC_ANDROID', '-DWEBRTC_LINUX'] os_deps += [cc.find_library('log')] os_deps += [dependency('gnustl', required : get_option('gnustl'))] have_posix = true @@ -46,7 +65,10 @@ arch_cflags = [] have_arm = false have_armv7 = false have_neon = false +have_mips = false +have_mips64 = false have_x86 = false +have_avx2 = false if ['arm', 'armv7'].contains(host_machine.cpu_family()) if cc.compiles('''#ifdef __ARM_ARCH_ISA_ARM #error no arm arch @@ -67,8 +89,19 @@ if cc.compiles('''#ifndef __aarch64__ have_neon = true arch_cflags += ['-DWEBRTC_ARCH_ARM64', '-DWEBRTC_HAS_NEON'] endif +if ['mips', 'mips64'].contains(host_machine.cpu_family()) + have_mips = true + arch_cflags += ['WEBRTC_ARCH_MIPS_FAMILY'] +endif +if host_machine.cpu_family() == 'mips64' + have_mips64 = true +endif if ['x86', 'x86_64'].contains(host_machine.cpu_family()) have_x86 = true + # This is unconditionally enabled for now, actual usage is determined by + # runtime CPU detection, so we're just assuming the compiler supports avx2 + have_avx2 = true + arch_cflags += ['-DWEBRTC_ENABLE_AVX2'] endif neon_opt = get_option('neon') @@ -84,16 +117,13 @@ if neon_opt != 'no' endif endif -noise_cflags = [] -if get_option('ns_mode') == 'float' - noise_cflags += ['-DWEBRTC_NS_FLOAT=1'] -else - noise_cflags += ['-DWEBRTC_NS_FIXED=1'] -endif - -common_cflags = ['-DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD', '-DNDEBUG'] + platform_cflags + os_cflags + arch_cflags + noise_cflags -common_cxxflags = ['-std=c++11'] + common_cflags -common_deps = os_deps +common_cflags = [ + '-DWEBRTC_LIBRARY_IMPL', + '-DWEBRTC_ENABLE_SYMBOL_EXPORT', + '-DNDEBUG' + ] + platform_cflags + os_cflags + arch_cflags +common_cxxflags = common_cflags +common_deps = os_deps + [absl_dep] webrtc_inc = include_directories('.') subdir('webrtc') @@ -107,7 +137,7 @@ pkgconfig.generate( filebase: 'webrtc-audio-processing', subdirs: 'webrtc_audio_processing', extra_cflags: [ - '-DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD', + '-DWEBRTC_LIBRARY_IMPL', ] + platform_cflags, libraries: libwebrtc_audio_processing, ) @@ -119,7 +149,7 @@ pkgconfig.generate( filebase: 'webrtc-audio-coding', subdirs: 'webrtc_audio_processing', extra_cflags: [ - '-DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD', + '-DWEBRTC_LIBRARY_IMPL', ] + platform_cflags, libraries: libwebrtc_audio_coding, ) diff --git a/meson_options.txt b/meson_options.txt index cfcd3fa..c939fb9 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,6 +1,3 @@ -option('ns_mode', type: 'combo', - choices : ['float', 'fixed'], - description: 'Noise suppresion mode to use.') option('gnustl', type: 'feature', value: 'auto', description: 'Use gnustl for a c++ library implementation (only used on Android)') diff --git a/webrtc/BUILD.gn b/webrtc/BUILD.gn index ac14d7d..cec97c5 100644 --- a/webrtc/BUILD.gn +++ b/webrtc/BUILD.gn @@ -6,22 +6,146 @@ # in the file PATENTS. All contributing project authors may # be found in the AUTHORS file in the root of the source tree. -# TODO(kjellander): Rebase this to webrtc/build/common.gypi changes after r6330. +# This is the root build file for GN. GN will start processing by loading this +# file, and recursively load all dependencies until all dependencies are either +# resolved or known not to exist (which will cause the build to fail). So if +# you add a new build file, there must be some path of dependencies from this +# file to your new one or GN won't know about it. -import("//build/config/crypto.gni") import("//build/config/linux/pkg_config.gni") -import("build/webrtc.gni") -import("//third_party/protobuf/proto_library.gni") +import("//build/config/sanitizers/sanitizers.gni") +import("webrtc.gni") +if (rtc_enable_protobuf) { + import("//third_party/protobuf/proto_library.gni") +} +if (is_android) { + import("//build/config/android/config.gni") + import("//build/config/android/rules.gni") +} + +if (!build_with_chromium) { + # This target should (transitively) cause everything to be built; if you run + # 'ninja default' and then 'ninja all', the second build should do no work. + group("default") { + testonly = true + deps = [ ":webrtc" ] + if (rtc_build_examples) { + deps += [ "examples" ] + } + if (rtc_build_tools) { + deps += [ "rtc_tools" ] + } + if (rtc_include_tests) { + deps += [ + ":rtc_unittests", + ":slow_tests", + ":video_engine_tests", + ":voip_unittests", + ":webrtc_nonparallel_tests", + ":webrtc_perf_tests", + "common_audio:common_audio_unittests", + "common_video:common_video_unittests", + "examples:examples_unittests", + "media:rtc_media_unittests", + "modules:modules_tests", + "modules:modules_unittests", + "modules/audio_coding:audio_coding_tests", + "modules/audio_processing:audio_processing_tests", + "modules/remote_bitrate_estimator:rtp_to_text", + "modules/rtp_rtcp:test_packet_masks_metrics", + "modules/video_capture:video_capture_internal_impl", + "pc:peerconnection_unittests", + "pc:rtc_pc_unittests", + "rtc_tools:rtp_generator", + "rtc_tools:video_replay", + "stats:rtc_stats_unittests", + "system_wrappers:system_wrappers_unittests", + "test", + "video:screenshare_loopback", + "video:sv_loopback", + "video:video_loopback", + ] + if (!is_asan) { + # Do not build :webrtc_lib_link_test because lld complains on some OS + # (e.g. when target_os = "mac") when is_asan=true. For more details, + # see bugs.webrtc.org/11027#c5. + deps += [ ":webrtc_lib_link_test" ] + } + if (is_android) { + deps += [ + "examples:android_examples_junit_tests", + "sdk/android:android_instrumentation_test_apk", + "sdk/android:android_sdk_junit_tests", + ] + } else { + deps += [ "modules/video_capture:video_capture_tests" ] + } + if (rtc_enable_protobuf) { + deps += [ + "audio:low_bandwidth_audio_test", + "logging:rtc_event_log_rtp_dump", + "tools_webrtc/perf:webrtc_dashboard_upload", + ] + } + } + } +} + +# Abseil Flags by default doesn't register command line flags on mobile +# platforms, WebRTC tests requires them (e.g. on simualtors) so this +# config will be applied to testonly targets globally (see webrtc.gni). +config("absl_flags_configs") { + defines = [ "ABSL_FLAGS_STRIP_NAMES=0" ] +} + +config("library_impl_config") { + # Build targets that contain WebRTC implementation need this macro to + # be defined in order to correctly export symbols when is_component_build + # is true. + # For more info see: rtc_base/build/rtc_export.h. + defines = [ "WEBRTC_LIBRARY_IMPL" ] +} # Contains the defines and includes in common.gypi that are duplicated both as # target_defaults and direct_dependent_settings. config("common_inherited_config") { defines = [] + cflags = [] + ldflags = [] + + if (rtc_enable_symbol_export || is_component_build) { + defines = [ "WEBRTC_ENABLE_SYMBOL_EXPORT" ] + } + if (build_with_mozilla) { defines += [ "WEBRTC_MOZILLA_BUILD" ] } + + if (!rtc_builtin_ssl_root_certificates) { + defines += [ "WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS" ] + } + + if (rtc_disable_check_msg) { + defines += [ "RTC_DISABLE_CHECK_MSG" ] + } + + if (rtc_enable_avx2) { + defines += [ "WEBRTC_ENABLE_AVX2" ] + } + + # Some tests need to declare their own trace event handlers. If this define is + # not set, the first time TRACE_EVENT_* is called it will store the return + # value for the current handler in an static variable, so that subsequent + # changes to the handler for that TRACE_EVENT_* will be ignored. + # So when tests are included, we set this define, making it possible to use + # different event handlers in different tests. + if (rtc_include_tests) { + defines += [ "WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=1" ] + } else { + defines += [ "WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=0" ] + } if (build_with_chromium) { - defines = [ "WEBRTC_CHROMIUM_BUILD" ] + defines += [ "WEBRTC_CHROMIUM_BUILD" ] include_dirs = [ # The overrides must be included first as that is the mechanism for # selecting the override headers in Chromium. @@ -29,10 +153,18 @@ config("common_inherited_config") { # Allow includes to be prefixed with webrtc/ in case it is not an # immediate subdirectory of the top-level. - "..", + ".", + + # Just like the root WebRTC directory is added to include path, the + # corresponding directory tree with generated files needs to be added too. + # Note: this path does not change depending on the current target, e.g. + # it is always "//gen/third_party/webrtc" when building with Chromium. + # See also: http://cs.chromium.org/?q=%5C"default_include_dirs + # https://gn.googlesource.com/gn/+/master/docs/reference.md#target_gen_dir + target_gen_dir, ] } - if (is_posix) { + if (is_posix || is_fuchsia) { defines += [ "WEBRTC_POSIX" ] } if (is_ios) { @@ -41,15 +173,15 @@ config("common_inherited_config") { "WEBRTC_IOS", ] } - if (is_ios && rtc_use_objc_h264) { - defines += [ "WEBRTC_OBJC_H264" ] - } - if (is_linux) { + if (is_linux || is_chromeos) { defines += [ "WEBRTC_LINUX" ] } if (is_mac) { defines += [ "WEBRTC_MAC" ] } + if (is_fuchsia) { + defines += [ "WEBRTC_FUCHSIA" ] + } if (is_win) { defines += [ "WEBRTC_WIN" ] } @@ -58,63 +190,173 @@ config("common_inherited_config") { "WEBRTC_LINUX", "WEBRTC_ANDROID", ] + + if (build_with_mozilla) { + defines += [ "WEBRTC_ANDROID_OPENSLES" ] + } + } + if (is_chromeos) { + defines += [ "CHROMEOS" ] + } + + if (rtc_sanitize_coverage != "") { + assert(is_clang, "sanitizer coverage requires clang") + cflags += [ "-fsanitize-coverage=${rtc_sanitize_coverage}" ] + ldflags += [ "-fsanitize-coverage=${rtc_sanitize_coverage}" ] + } + + if (is_ubsan) { + cflags += [ "-fsanitize=float-cast-overflow" ] } } -if (rtc_have_dbus_glib) { - pkg_config("dbus-glib") { - packages = [ "dbus-glib-1" ] +# TODO(bugs.webrtc.org/9693): Remove the possibility to suppress this warning +# as soon as WebRTC compiles without it. +config("no_exit_time_destructors") { + if (is_clang) { + cflags = [ "-Wno-exit-time-destructors" ] + } +} + +# TODO(bugs.webrtc.org/9693): Remove the possibility to suppress this warning +# as soon as WebRTC compiles without it. +config("no_global_constructors") { + if (is_clang) { + cflags = [ "-Wno-global-constructors" ] + } +} + +config("rtc_prod_config") { + # Ideally, WebRTC production code (but not test code) should have these flags. + if (is_clang) { + cflags = [ + "-Wexit-time-destructors", + "-Wglobal-constructors", + ] } } config("common_config") { cflags = [] + cflags_c = [] cflags_cc = [] - if (rtc_restrict_logging) { - defines = [ "WEBRTC_RESTRICT_LOGGING" ] + cflags_objc = [] + defines = [] + + if (rtc_enable_protobuf) { + defines += [ "WEBRTC_ENABLE_PROTOBUF=1" ] + } else { + defines += [ "WEBRTC_ENABLE_PROTOBUF=0" ] } - if (rtc_have_dbus_glib) { - defines += [ "HAVE_DBUS_GLIB" ] - - # TODO(kjellander): Investigate this, it seems like include - # is still not found even if the execution of - # build/config/linux/pkg-config.py dbus-glib-1 returns correct include - # dirs on Linux. - all_dependent_configs = [ "dbus-glib" ] + if (rtc_include_internal_audio_device) { + defines += [ "WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE" ] } + if (rtc_libvpx_build_vp9) { + defines += [ "RTC_ENABLE_VP9" ] + } + + if (rtc_enable_sctp) { + defines += [ "HAVE_SCTP" ] + } + + if (rtc_enable_external_auth) { + defines += [ "ENABLE_EXTERNAL_AUTH" ] + } + + if (rtc_use_h264) { + defines += [ "WEBRTC_USE_H264" ] + } + + if (rtc_use_absl_mutex) { + defines += [ "WEBRTC_ABSL_MUTEX" ] + } + + if (rtc_disable_logging) { + defines += [ "RTC_DISABLE_LOGGING" ] + } + + if (rtc_disable_trace_events) { + defines += [ "RTC_DISABLE_TRACE_EVENTS" ] + } + + if (rtc_disable_metrics) { + defines += [ "RTC_DISABLE_METRICS" ] + } + + if (rtc_exclude_transient_suppressor) { + defines += [ "WEBRTC_EXCLUDE_TRANSIENT_SUPPRESSOR" ] + } + + if (rtc_exclude_audio_processing_module) { + defines += [ "WEBRTC_EXCLUDE_AUDIO_PROCESSING_MODULE" ] + } + + cflags = [] + if (build_with_chromium) { - defines += [ "LOGGING_INSIDE_WEBRTC" ] + defines += [ + # NOTICE: Since common_inherited_config is used in public_configs for our + # targets, there's no point including the defines in that config here. + # TODO(kjellander): Cleanup unused ones and move defines closer to the + # source when webrtc:4256 is completed. + "HAVE_WEBRTC_VIDEO", + "LOGGING_INSIDE_WEBRTC", + ] } else { - if (is_posix) { - # -Wextra is currently disabled in Chromium"s common.gypi. Enable - # for targets that can handle it. For Android/arm64 right now - # there will be an "enumeral and non-enumeral type in conditional - # expression" warning in android_tools/ndk_experimental"s version - # of stlport. - # See: https://code.google.com/p/chromium/issues/detail?id=379699 - if (current_cpu != "arm64" || !is_android) { - cflags = [ - "-Wextra", + if (is_posix || is_fuchsia) { + cflags_c += [ + # TODO(bugs.webrtc.org/9029): enable commented compiler flags. + # Some of these flags should also be added to cflags_objc. - # We need to repeat some flags from Chromium"s common.gypi - # here that get overridden by -Wextra. - "-Wno-unused-parameter", - "-Wno-missing-field-initializers", - "-Wno-strict-overflow", - ] - cflags_cc = [ - "-Wnon-virtual-dtor", + # "-Wextra", (used when building C++ but not when building C) + # "-Wmissing-prototypes", (C/Obj-C only) + # "-Wmissing-declarations", (ensure this is always used C/C++, etc..) + "-Wstrict-prototypes", - # This is enabled for clang; enable for gcc as well. - "-Woverloaded-virtual", - ] - } + # "-Wpointer-arith", (ensure this is always used C/C++, etc..) + # "-Wbad-function-cast", (C/Obj-C only) + # "-Wnested-externs", (C/Obj-C only) + ] + cflags_objc += [ "-Wstrict-prototypes" ] + cflags_cc = [ + "-Wnon-virtual-dtor", + + # This is enabled for clang; enable for gcc as well. + "-Woverloaded-virtual", + ] } if (is_clang) { - cflags += [ "-Wthread-safety" ] + cflags += [ + "-Wc++11-narrowing", + "-Wimplicit-fallthrough", + "-Wthread-safety", + "-Winconsistent-missing-override", + "-Wundef", + ] + + # use_xcode_clang only refers to the iOS toolchain, host binaries use + # chromium's clang always. + if (!is_nacl && + (!use_xcode_clang || current_toolchain == host_toolchain)) { + # Flags NaCl (Clang 3.7) and Xcode 7.3 (Clang clang-703.0.31) do not + # recognize. + cflags += [ "-Wunused-lambda-capture" ] + } + } + + if (is_win && !is_clang) { + # MSVC warning suppressions (needed to use Abseil). + # TODO(bugs.webrtc.org/9274): Remove these warnings as soon as MSVC allows + # external headers warning suppression (or fix them upstream). + cflags += [ "/wd4702" ] # unreachable code + + # MSVC 2019 warning suppressions for C++17 compiling + cflags += + [ "/wd5041" ] # out-of-line definition for constexpr static data + # member is not needed and is deprecated in C++17 } } @@ -129,8 +371,6 @@ config("common_config") { defines += [ "WEBRTC_ARCH_ARM_V7" ] if (arm_use_neon) { defines += [ "WEBRTC_HAS_NEON" ] - } else if (arm_optionally_use_neon) { - defines += [ "WEBRTC_DETECT_NEON" ] } } } @@ -163,119 +403,325 @@ config("common_config") { "-fno-builtin-sinf", ] } -} -source_set("webrtc") { - sources = [ - "call.h", - "config.h", - "frame_callback.h", - "transport.h", - ] - - defines = [] - configs += [ ":common_config" ] - public_configs = [ ":common_inherited_config" ] - - deps = [ - "audio", - ":webrtc_common", - "base:rtc_base", - "call", - "common_audio", - "common_video", - "modules/audio_coding", - "modules/audio_conference_mixer", - "modules/audio_device", - "modules/audio_processing", - "modules/bitrate_controller", - "modules/desktop_capture", - "modules/media_file", - "modules/rtp_rtcp", - "modules/utility", - "modules/video_coding", - "modules/video_processing", - "system_wrappers", - "tools", - "video", - "voice_engine", - ] - - if (build_with_chromium) { - deps += [ - "modules/video_capture", - "modules/video_render", - ] + if (use_fuzzing_engine && optimize_for_fuzzing) { + # Used in Chromium's overrides to disable logging + defines += [ "WEBRTC_UNSAFE_FUZZER_MODE" ] } - if (rtc_enable_protobuf) { - defines += [ "ENABLE_RTC_EVENT_LOG" ] - deps += [ ":rtc_event_log_proto" ] + if (!build_with_chromium && rtc_win_undef_unicode) { + cflags += [ + "/UUNICODE", + "/U_UNICODE", + ] + } +} + +config("common_objc") { + frameworks = [ "Foundation.framework" ] + + if (rtc_use_metal_rendering) { + defines = [ "RTC_SUPPORTS_METAL" ] } } if (!build_with_chromium) { - executable("webrtc_tests") { + # Target to build all the WebRTC production code. + rtc_static_library("webrtc") { + # Only the root target and the test should depend on this. + visibility = [ + "//:default", + "//:webrtc_lib_link_test", + ] + + sources = [] + complete_static_lib = true + suppressed_configs += [ "//build/config/compiler:thin_archive" ] + defines = [] + + deps = [ + "api:create_peerconnection_factory", + "api:libjingle_peerconnection_api", + "api:rtc_error", + "api:transport_api", + "api/crypto", + "api/rtc_event_log:rtc_event_log_factory", + "api/task_queue", + "api/task_queue:default_task_queue_factory", + "audio", + "call", + "common_audio", + "common_video", + "logging:rtc_event_log_api", + "media", + "modules", + "modules/video_capture:video_capture_internal_impl", + "p2p:rtc_p2p", + "pc:libjingle_peerconnection", + "pc:peerconnection", + "pc:rtc_pc", + "pc:rtc_pc_base", + "rtc_base", + "sdk", + "video", + ] + + if (rtc_include_builtin_audio_codecs) { + deps += [ + "api/audio_codecs:builtin_audio_decoder_factory", + "api/audio_codecs:builtin_audio_encoder_factory", + ] + } + + if (rtc_include_builtin_video_codecs) { + deps += [ + "api/video_codecs:builtin_video_decoder_factory", + "api/video_codecs:builtin_video_encoder_factory", + ] + } + + if (build_with_mozilla) { + deps += [ + "api/video:video_frame", + "api/video:video_rtp_headers", + ] + } else { + deps += [ + "api", + "logging", + "p2p", + "pc", + "stats", + ] + } + + if (rtc_enable_protobuf) { + deps += [ "logging:rtc_event_log_proto" ] + } + } + + if (rtc_include_tests && !is_asan) { + rtc_executable("webrtc_lib_link_test") { + testonly = true + + sources = [ "webrtc_lib_link_test.cc" ] + deps = [ + # NOTE: Don't add deps here. If this test fails to link, it means you + # need to add stuff to the webrtc static lib target above. + ":webrtc", + ] + } + } +} + +if (use_libfuzzer || use_afl) { + # This target is only here for gn to discover fuzzer build targets under + # webrtc/test/fuzzers/. + group("webrtc_fuzzers_dummy") { + testonly = true + deps = [ "test/fuzzers:webrtc_fuzzer_main" ] + } +} + +if (rtc_include_tests) { + rtc_test("rtc_unittests") { + testonly = true + + deps = [ + "api:compile_all_headers", + "api:rtc_api_unittests", + "api/audio/test:audio_api_unittests", + "api/audio_codecs/test:audio_codecs_api_unittests", + "api/numerics:numerics_unittests", + "api/transport:stun_unittest", + "api/video/test:rtc_api_video_unittests", + "api/video_codecs/test:video_codecs_api_unittests", + "call:fake_network_pipe_unittests", + "p2p:libstunprober_unittests", + "p2p:rtc_p2p_unittests", + "rtc_base:robo_caller_unittests", + "rtc_base:rtc_base_approved_unittests", + "rtc_base:rtc_base_unittests", + "rtc_base:rtc_json_unittests", + "rtc_base:rtc_numerics_unittests", + "rtc_base:rtc_operations_chain_unittests", + "rtc_base:rtc_task_queue_unittests", + "rtc_base:sigslot_unittest", + "rtc_base:untyped_function_unittest", + "rtc_base:weak_ptr_unittests", + "rtc_base/experiments:experiments_unittests", + "rtc_base/synchronization:sequence_checker_unittests", + "rtc_base/task_utils:pending_task_safety_flag_unittests", + "rtc_base/task_utils:to_queued_task_unittests", + "sdk:sdk_tests", + "test:rtp_test_utils", + "test:test_main", + "test/network:network_emulation_unittests", + ] + + if (rtc_enable_protobuf) { + deps += [ "logging:rtc_event_log_tests" ] + } + + if (is_android) { + # Do not use Chromium's launcher. native_unittests defines its own JNI_OnLoad. + use_default_launcher = false + + deps += [ + "sdk/android:native_unittests", + "sdk/android:native_unittests_java", + "//testing/android/native_test:native_test_support", + ] + shard_timeout = 900 + } + + if (is_ios || is_mac) { + deps += [ "sdk:rtc_unittests_objc" ] + } + } + + rtc_test("benchmarks") { testonly = true deps = [ - ":webrtc", - "modules/video_render:video_render_internal_impl", - "modules/video_capture:video_capture_internal_impl", - "test", + "rtc_base/synchronization:mutex_benchmark", + "test:benchmark_main", + ] + } + + # This runs tests that must run in real time and therefore can take some + # time to execute. They are in a separate executable to avoid making the + # regular unittest suite too slow to run frequently. + rtc_test("slow_tests") { + testonly = true + deps = [ + "rtc_base/task_utils:repeating_task_unittests", + "test:test_main", + ] + } + + # TODO(pbos): Rename test suite, this is no longer "just" for video targets. + video_engine_tests_resources = [ + "resources/foreman_cif_short.yuv", + "resources/voice_engine/audio_long16.pcm", + ] + + if (is_ios) { + bundle_data("video_engine_tests_bundle_data") { + testonly = true + sources = video_engine_tests_resources + outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ] + } + } + + rtc_test("video_engine_tests") { + testonly = true + deps = [ + "audio:audio_tests", + + # TODO(eladalon): call_tests aren't actually video-specific, so we + # should move them to a more appropriate test suite. + "call:call_tests", + "call/adaptation:resource_adaptation_tests", + "test:test_common", + "test:test_main", + "test:video_test_common", + "video:video_tests", + "video/adaptation:video_adaptation_tests", + ] + data = video_engine_tests_resources + if (is_android) { + deps += [ "//testing/android/native_test:native_test_native_code" ] + shard_timeout = 900 + } + if (is_ios) { + deps += [ ":video_engine_tests_bundle_data" ] + } + } + + webrtc_perf_tests_resources = [ + "resources/ConferenceMotion_1280_720_50.yuv", + "resources/audio_coding/speech_mono_16kHz.pcm", + "resources/audio_coding/speech_mono_32_48kHz.pcm", + "resources/audio_coding/testfile32kHz.pcm", + "resources/difficult_photo_1850_1110.yuv", + "resources/foreman_cif.yuv", + "resources/paris_qcif.yuv", + "resources/photo_1850_1110.yuv", + "resources/presentation_1850_1110.yuv", + "resources/voice_engine/audio_long16.pcm", + "resources/web_screenshot_1850_1110.yuv", + ] + + if (is_ios) { + bundle_data("webrtc_perf_tests_bundle_data") { + testonly = true + sources = webrtc_perf_tests_resources + outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ] + } + } + + rtc_test("webrtc_perf_tests") { + testonly = true + deps = [ + "audio:audio_perf_tests", + "call:call_perf_tests", + "modules/audio_coding:audio_coding_perf_tests", + "modules/audio_processing:audio_processing_perf_tests", + "pc:peerconnection_perf_tests", + "test:test_main", + "video:video_full_stack_tests", + "video:video_pc_full_stack_tests", + ] + + data = webrtc_perf_tests_resources + if (is_android) { + deps += [ "//testing/android/native_test:native_test_native_code" ] + shard_timeout = 4500 + } + if (is_ios) { + deps += [ ":webrtc_perf_tests_bundle_data" ] + } + } + + rtc_test("webrtc_nonparallel_tests") { + testonly = true + deps = [ "rtc_base:rtc_base_nonparallel_tests" ] + if (is_android) { + deps += [ "//testing/android/native_test:native_test_support" ] + shard_timeout = 900 + } + } + + rtc_test("voip_unittests") { + testonly = true + deps = [ + "api/voip:voip_engine_factory_unittests", + "audio/voip/test:audio_channel_unittests", + "audio/voip/test:audio_egress_unittests", + "audio/voip/test:audio_ingress_unittests", + "audio/voip/test:voip_core_unittests", + "test:test_main", ] } } -source_set("webrtc_common") { - sources = [ - "common_types.cc", - "common_types.h", - "config.cc", - "config.h", - "engine_configurations.h", - "typedefs.h", - ] - - configs += [ ":common_config" ] - public_configs = [ ":common_inherited_config" ] +# ---- Poisons ---- +# +# Here is one empty dummy target for each poison type (needed because +# "being poisonous with poison type foo" is implemented as "depends on +# //:poison_foo"). +# +# The set of poison_* targets needs to be kept in sync with the +# `all_poison_types` list in webrtc.gni. +# +group("poison_audio_codecs") { } -source_set("gtest_prod") { - sources = [ - "test/testsupport/gtest_prod_util.h", - ] +group("poison_default_task_queue") { } -if (rtc_enable_protobuf) { - proto_library("rtc_event_log_proto") { - sources = [ - "call/rtc_event_log.proto", - ] - proto_out_dir = "webrtc/call" - } +group("poison_rtc_json") { } -source_set("rtc_event_log") { - sources = [ - "call/rtc_event_log.cc", - "call/rtc_event_log.h", - ] - - defines = [] - configs += [ ":common_config" ] - public_configs = [ ":common_inherited_config" ] - - deps = [ - ":webrtc_common", - ] - - if (rtc_enable_protobuf) { - defines += [ "ENABLE_RTC_EVENT_LOG" ] - deps += [ ":rtc_event_log_proto" ] - } - if (is_clang && !is_nacl) { - # Suppress warnings from Chrome's Clang plugins. - # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. - configs -= [ "//build/config/clang:find_bad_constructs" ] - } +group("poison_software_video_codecs") { } diff --git a/webrtc/LICENSE b/webrtc/LICENSE new file mode 100644 index 0000000..4c41b7b --- /dev/null +++ b/webrtc/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2011, The WebRTC project authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/webrtc/LICENSE_THIRD_PARTY b/webrtc/LICENSE_THIRD_PARTY deleted file mode 100644 index b64dbba..0000000 --- a/webrtc/LICENSE_THIRD_PARTY +++ /dev/null @@ -1,419 +0,0 @@ -This source tree contains third party source code which is governed by third -party licenses. Paths to the files and associated licenses are collected here. - -Files governed by third party licenses: -common_audio/fft4g.c -common_audio/signal_processing/spl_sqrt_floor.c -common_audio/signal_processing/spl_sqrt_floor_arm.S -modules/audio_coding/codecs/g711/main/source/g711.c -modules/audio_coding/codecs/g711/main/source/g711.h -modules/audio_coding/codecs/g722/main/source/g722_decode.c -modules/audio_coding/codecs/g722/main/source/g722_enc_dec.h -modules/audio_coding/codecs/g722/main/source/g722_encode.c -modules/audio_coding/codecs/isac/main/source/fft.c -modules/audio_device/mac/portaudio/pa_memorybarrier.h -modules/audio_device/mac/portaudio/pa_ringbuffer.c -modules/audio_device/mac/portaudio/pa_ringbuffer.h -modules/audio_processing/aec/aec_rdft.c -system_wrappers/source/condition_variable_event_win.cc -system_wrappers/source/set_thread_name_win.h -system_wrappers/source/spreadsortlib/constants.hpp -system_wrappers/source/spreadsortlib/spreadsort.hpp - -Individual licenses for each file: -------------------------------------------------------------------------------- -Files: -common_audio/signal_processing/spl_sqrt_floor.c -common_audio/signal_processing/spl_sqrt_floor_arm.S - -License: -/* - * Written by Wilco Dijkstra, 1996. The following email exchange establishes the - * license. - * - * From: Wilco Dijkstra - * Date: Fri, Jun 24, 2011 at 3:20 AM - * Subject: Re: sqrt routine - * To: Kevin Ma - * Hi Kevin, - * Thanks for asking. Those routines are public domain (originally posted to - * comp.sys.arm a long time ago), so you can use them freely for any purpose. - * Cheers, - * Wilco - * - * ----- Original Message ----- - * From: "Kevin Ma" - * To: - * Sent: Thursday, June 23, 2011 11:44 PM - * Subject: Fwd: sqrt routine - * Hi Wilco, - * I saw your sqrt routine from several web sites, including - * http://www.finesse.demon.co.uk/steven/sqrt.html. - * Just wonder if there's any copyright information with your Successive - * approximation routines, or if I can freely use it for any purpose. - * Thanks. - * Kevin - */ -------------------------------------------------------------------------------- -Files: -modules/audio_coding/codecs/g711/main/source/g711.c -modules/audio_coding/codecs/g711/main/source/g711.h - -License: -/* - * SpanDSP - a series of DSP components for telephony - * - * g711.h - In line A-law and u-law conversion routines - * - * Written by Steve Underwood - * - * Copyright (C) 2001 Steve Underwood - * - * Despite my general liking of the GPL, I place this code in the - * public domain for the benefit of all mankind - even the slimy - * ones who might try to proprietize my work and use it to my - * detriment. - */ -------------------------------------------------------------------------------- -Files: -modules/audio_coding/codecs/g722/main/source/g722_decode.c -modules/audio_coding/codecs/g722/main/source/g722_enc_dec.h -modules/audio_coding/codecs/g722/main/source/g722_encode.c - -License: -/* - * SpanDSP - a series of DSP components for telephony - * - * g722_decode.c - The ITU G.722 codec, decode part. - * - * Written by Steve Underwood - * - * Copyright (C) 2005 Steve Underwood - * - * Despite my general liking of the GPL, I place my own contributions - * to this code in the public domain for the benefit of all mankind - - * even the slimy ones who might try to proprietize my work and use it - * to my detriment. - * - * Based in part on a single channel G.722 codec which is: - * - * Copyright (c) CMU 1993 - * Computer Science, Speech Group - * Chengxiang Lu and Alex Hauptmann - */ -------------------------------------------------------------------------------- -Files: -modules/audio_coding/codecs/isac/main/source/fft.c - -License: -/* - * Copyright(c)1995,97 Mark Olesen - * Queen's Univ at Kingston (Canada) - * - * Permission to use, copy, modify, and distribute this software for - * any purpose without fee is hereby granted, provided that this - * entire notice is included in all copies of any software which is - * or includes a copy or modification of this software and in all - * copies of the supporting documentation for such software. - * - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR QUEEN'S - * UNIVERSITY AT KINGSTON MAKES ANY REPRESENTATION OR WARRANTY OF ANY - * KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS - * FITNESS FOR ANY PARTICULAR PURPOSE. - * - * All of which is to say that you can do what you like with this - * source code provided you don't try to sell it as your own and you - * include an unaltered copy of this message (including the - * copyright). - * - * It is also implicitly understood that bug fixes and improvements - * should make their way back to the general Internet community so - * that everyone benefits. - */ -------------------------------------------------------------------------------- -Files: -modules/audio_device/mac/portaudio/pa_memorybarrier.h -modules/audio_device/mac/portaudio/pa_ringbuffer.c -modules/audio_device/mac/portaudio/pa_ringbuffer.h - -License: -/* - * $Id: pa_memorybarrier.h 1240 2007-07-17 13:05:07Z bjornroche $ - * Portable Audio I/O Library - * Memory barrier utilities - * - * Author: Bjorn Roche, XO Audio, LLC - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/* - * $Id: pa_ringbuffer.c 1421 2009-11-18 16:09:05Z bjornroche $ - * Portable Audio I/O Library - * Ring Buffer utility. - * - * Author: Phil Burk, http://www.softsynth.com - * modified for SMP safety on Mac OS X by Bjorn Roche - * modified for SMP safety on Linux by Leland Lucius - * also, allowed for const where possible - * modified for multiple-byte-sized data elements by Sven Fischer - * - * Note that this is safe only for a single-thread reader and a - * single-thread writer. - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ -------------------------------------------------------------------------------- -Files: -common_audio/fft4g.c -modules/audio_processing/aec/aec_rdft.c - -License: -/* - * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html - * Copyright Takuya OOURA, 1996-2001 - * - * You may use, copy, modify and distribute this code for any purpose (include - * commercial use) and without fee. Please refer to this package when you modify - * this code. - */ -------------------------------------------------------------------------------- -Files: -system_wrappers/source/condition_variable_event_win.cc - -Source: -http://www1.cse.wustl.edu/~schmidt/ACE-copying.html - -License: -Copyright and Licensing Information for ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), -and CoSMIC(TM) - -ACE(TM), TAO(TM), CIAO(TM), DAnCE>(TM), and CoSMIC(TM) (henceforth referred to -as "DOC software") are copyrighted by Douglas C. Schmidt and his research -group at Washington University, University of California, Irvine, and -Vanderbilt University, Copyright (c) 1993-2009, all rights reserved. Since DOC -software is open-source, freely available software, you are free to use, -modify, copy, and distribute--perpetually and irrevocably--the DOC software -source code and object code produced from the source, as well as copy and -distribute modified versions of this software. You must, however, include this -copyright statement along with any code built using DOC software that you -release. No copyright statement needs to be provided if you just ship binary -executables of your software products. -You can use DOC software in commercial and/or binary software releases and are -under no obligation to redistribute any of your source code that is built -using DOC software. Note, however, that you may not misappropriate the DOC -software code, such as copyrighting it yourself or claiming authorship of the -DOC software code, in a way that will prevent DOC software from being -distributed freely using an open-source development model. You needn't inform -anyone that you're using DOC software in your software, though we encourage -you to let us know so we can promote your project in the DOC software success -stories. - -The ACE, TAO, CIAO, DAnCE, and CoSMIC web sites are maintained by the DOC -Group at the Institute for Software Integrated Systems (ISIS) and the Center -for Distributed Object Computing of Washington University, St. Louis for the -development of open-source software as part of the open-source software -community. Submissions are provided by the submitter ``as is'' with no -warranties whatsoever, including any warranty of merchantability, -noninfringement of third party intellectual property, or fitness for any -particular purpose. In no event shall the submitter be liable for any direct, -indirect, special, exemplary, punitive, or consequential damages, including -without limitation, lost profits, even if advised of the possibility of such -damages. Likewise, DOC software is provided as is with no warranties of any -kind, including the warranties of design, merchantability, and fitness for a -particular purpose, noninfringement, or arising from a course of dealing, -usage or trade practice. Washington University, UC Irvine, Vanderbilt -University, their employees, and students shall have no liability with respect -to the infringement of copyrights, trade secrets or any patents by DOC -software or any part thereof. Moreover, in no event will Washington -University, UC Irvine, or Vanderbilt University, their employees, or students -be liable for any lost revenue or profits or other special, indirect and -consequential damages. - -DOC software is provided with no support and without any obligation on the -part of Washington University, UC Irvine, Vanderbilt University, their -employees, or students to assist in its use, correction, modification, or -enhancement. A number of companies around the world provide commercial support -for DOC software, however. DOC software is Y2K-compliant, as long as the -underlying OS platform is Y2K-compliant. Likewise, DOC software is compliant -with the new US daylight savings rule passed by Congress as "The Energy Policy -Act of 2005," which established new daylight savings times (DST) rules for the -United States that expand DST as of March 2007. Since DOC software obtains -time/date and calendaring information from operating systems users will not be -affected by the new DST rules as long as they upgrade their operating systems -accordingly. - -The names ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), CoSMIC(TM), Washington -University, UC Irvine, and Vanderbilt University, may not be used to endorse -or promote products or services derived from this source without express -written permission from Washington University, UC Irvine, or Vanderbilt -University. This license grants no permission to call products or services -derived from this source ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), or CoSMIC(TM), -nor does it grant permission for the name Washington University, UC Irvine, or -Vanderbilt University to appear in their names. -------------------------------------------------------------------------------- -Files: -system_wrappers/source/set_thread_name_win.h - -Source: -http://msdn.microsoft.com/en-us/cc300389.aspx#P - -License: -This license governs use of code marked as “sample” or “example” available on -this web site without a license agreement, as provided under the section above -titled “NOTICE SPECIFIC TO SOFTWARE AVAILABLE ON THIS WEB SITE.” If you use -such code (the “software”), you accept this license. If you do not accept the -license, do not use the software. - -1. Definitions - -The terms “reproduce,” “reproduction,” “derivative works,” and “distribution” -have the same meaning here as under U.S. copyright law. - -A “contribution” is the original software, or any additions or changes to the -software. - -A “contributor” is any person that distributes its contribution under this -license. - -“Licensed patents” are a contributor’s patent claims that read directly on its -contribution. - -2. Grant of Rights - -(A) Copyright Grant - Subject to the terms of this license, including the -license conditions and limitations in section 3, each contributor grants you a -non-exclusive, worldwide, royalty-free copyright license to reproduce its -contribution, prepare derivative works of its contribution, and distribute its -contribution or any derivative works that you create. - -(B) Patent Grant - Subject to the terms of this license, including the license -conditions and limitations in section 3, each contributor grants you a -non-exclusive, worldwide, royalty-free license under its licensed patents to -make, have made, use, sell, offer for sale, import, and/or otherwise dispose -of its contribution in the software or derivative works of the contribution in -the software. - -3. Conditions and Limitations - -(A) No Trademark License- This license does not grant you rights to use any -contributors’ name, logo, or trademarks. - -(B) If you bring a patent claim against any contributor over patents that you -claim are infringed by the software, your patent license from such contributor -to the software ends automatically. - -(C) If you distribute any portion of the software, you must retain all -copyright, patent, trademark, and attribution notices that are present in the -software. - -(D) If you distribute any portion of the software in source code form, you may -do so only under this license by including a complete copy of this license -with your distribution. If you distribute any portion of the software in -compiled or object code form, you may only do so under a license that complies -with this license. - -(E) The software is licensed “as-is.” You bear the risk of using it. The -contributors give no express warranties, guarantees or conditions. You may -have additional consumer rights under your local laws which this license -cannot change. To the extent permitted under your local laws, the contributors -exclude the implied warranties of merchantability, fitness for a particular -purpose and non-infringement. - -(F) Platform Limitation - The licenses granted in sections 2(A) and 2(B) -extend only to the software or derivative works that you create that run on a -Microsoft Windows operating system product. -------------------------------------------------------------------------------- -Files: -system_wrappers/source/spreadsortlib/constants.hpp -system_wrappers/source/spreadsortlib/spreadsort.hpp - -License: -/*Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE.*/ diff --git a/webrtc/api/array_view.h b/webrtc/api/array_view.h new file mode 100644 index 0000000..a66369a --- /dev/null +++ b/webrtc/api/array_view.h @@ -0,0 +1,315 @@ +/* + * Copyright 2015 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. + */ + +#ifndef API_ARRAY_VIEW_H_ +#define API_ARRAY_VIEW_H_ + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/type_traits.h" + +namespace rtc { + +// tl;dr: rtc::ArrayView is the same thing as gsl::span from the Guideline +// Support Library. +// +// Many functions read from or write to arrays. The obvious way to do this is +// to use two arguments, a pointer to the first element and an element count: +// +// bool Contains17(const int* arr, size_t size) { +// for (size_t i = 0; i < size; ++i) { +// if (arr[i] == 17) +// return true; +// } +// return false; +// } +// +// This is flexible, since it doesn't matter how the array is stored (C array, +// std::vector, rtc::Buffer, ...), but it's error-prone because the caller has +// to correctly specify the array length: +// +// Contains17(arr, arraysize(arr)); // C array +// Contains17(arr.data(), arr.size()); // std::vector +// Contains17(arr, size); // pointer + size +// ... +// +// It's also kind of messy to have two separate arguments for what is +// conceptually a single thing. +// +// Enter rtc::ArrayView. It contains a T pointer (to an array it doesn't +// own) and a count, and supports the basic things you'd expect, such as +// indexing and iteration. It allows us to write our function like this: +// +// bool Contains17(rtc::ArrayView arr) { +// for (auto e : arr) { +// if (e == 17) +// return true; +// } +// return false; +// } +// +// And even better, because a bunch of things will implicitly convert to +// ArrayView, we can call it like this: +// +// Contains17(arr); // C array +// Contains17(arr); // std::vector +// Contains17(rtc::ArrayView(arr, size)); // pointer + size +// Contains17(nullptr); // nullptr -> empty ArrayView +// ... +// +// ArrayView stores both a pointer and a size, but you may also use +// ArrayView, which has a size that's fixed at compile time (which means +// it only has to store the pointer). +// +// One important point is that ArrayView and ArrayView are +// different types, which allow and don't allow mutation of the array elements, +// respectively. The implicit conversions work just like you'd hope, so that +// e.g. vector will convert to either ArrayView or ArrayView, but const vector will convert only to ArrayView. +// (ArrayView itself can be the source type in such conversions, so +// ArrayView will convert to ArrayView.) +// +// Note: ArrayView is tiny (just a pointer and a count if variable-sized, just +// a pointer if fix-sized) and trivially copyable, so it's probably cheaper to +// pass it by value than by const reference. + +namespace impl { + +// Magic constant for indicating that the size of an ArrayView is variable +// instead of fixed. +enum : std::ptrdiff_t { kArrayViewVarSize = -4711 }; + +// Base class for ArrayViews of fixed nonzero size. +template +class ArrayViewBase { + static_assert(Size > 0, "ArrayView size must be variable or non-negative"); + + public: + ArrayViewBase(T* data, size_t size) : data_(data) {} + + static constexpr size_t size() { return Size; } + static constexpr bool empty() { return false; } + T* data() const { return data_; } + + protected: + static constexpr bool fixed_size() { return true; } + + private: + T* data_; +}; + +// Specialized base class for ArrayViews of fixed zero size. +template +class ArrayViewBase { + public: + explicit ArrayViewBase(T* data, size_t size) {} + + static constexpr size_t size() { return 0; } + static constexpr bool empty() { return true; } + T* data() const { return nullptr; } + + protected: + static constexpr bool fixed_size() { return true; } +}; + +// Specialized base class for ArrayViews of variable size. +template +class ArrayViewBase { + public: + ArrayViewBase(T* data, size_t size) + : data_(size == 0 ? nullptr : data), size_(size) {} + + size_t size() const { return size_; } + bool empty() const { return size_ == 0; } + T* data() const { return data_; } + + protected: + static constexpr bool fixed_size() { return false; } + + private: + T* data_; + size_t size_; +}; + +} // namespace impl + +template +class ArrayView final : public impl::ArrayViewBase { + public: + using value_type = T; + using const_iterator = const T*; + + // Construct an ArrayView from a pointer and a length. + template + ArrayView(U* data, size_t size) + : impl::ArrayViewBase::ArrayViewBase(data, size) { + RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data()); + RTC_DCHECK_EQ(size, this->size()); + RTC_DCHECK_EQ(!this->data(), + this->size() == 0); // data is null iff size == 0. + } + + // Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0 + // cannot be empty. + ArrayView() : ArrayView(nullptr, 0) {} + ArrayView(std::nullptr_t) // NOLINT + : ArrayView() {} + ArrayView(std::nullptr_t, size_t size) + : ArrayView(static_cast(nullptr), size) { + static_assert(Size == 0 || Size == impl::kArrayViewVarSize, ""); + RTC_DCHECK_EQ(0, size); + } + + // Construct an ArrayView from a C-style array. + template + ArrayView(U (&array)[N]) // NOLINT + : ArrayView(array, N) { + static_assert(Size == N || Size == impl::kArrayViewVarSize, + "Array size must match ArrayView size"); + } + + // (Only if size is fixed.) Construct a fixed size ArrayView from a + // non-const std::array instance. For an ArrayView with variable size, the + // used ctor is ArrayView(U& u) instead. + template (N)>::type* = nullptr> + ArrayView(std::array& u) // NOLINT + : ArrayView(u.data(), u.size()) {} + + // (Only if size is fixed.) Construct a fixed size ArrayView where T is + // const from a const(expr) std::array instance. For an ArrayView with + // variable size, the used ctor is ArrayView(U& u) instead. + template (N)>::type* = nullptr> + ArrayView(const std::array& u) // NOLINT + : ArrayView(u.data(), u.size()) {} + + // (Only if size is fixed.) Construct an ArrayView from any type U that has a + // static constexpr size() method whose return value is equal to Size, and a + // data() method whose return value converts implicitly to T*. In particular, + // this means we allow conversion from ArrayView to ArrayView, but not the other way around. We also don't allow conversion from + // ArrayView to ArrayView, or from ArrayView to ArrayView when M != N. + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + ArrayView(U& u) // NOLINT + : ArrayView(u.data(), u.size()) { + static_assert(U::size() == Size, "Sizes must match exactly"); + } + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + ArrayView(const U& u) // NOLINT(runtime/explicit) + : ArrayView(u.data(), u.size()) { + static_assert(U::size() == Size, "Sizes must match exactly"); + } + + // (Only if size is variable.) Construct an ArrayView from any type U that + // has a size() method whose return value converts implicitly to size_t, and + // a data() method whose return value converts implicitly to T*. In + // particular, this means we allow conversion from ArrayView to + // ArrayView, but not the other way around. Other allowed + // conversions include + // ArrayView to ArrayView or ArrayView, + // std::vector to ArrayView or ArrayView, + // const std::vector to ArrayView, + // rtc::Buffer to ArrayView or ArrayView, and + // const rtc::Buffer to ArrayView. + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + ArrayView(U& u) // NOLINT + : ArrayView(u.data(), u.size()) {} + template < + typename U, + typename std::enable_if::value>::type* = nullptr> + ArrayView(const U& u) // NOLINT(runtime/explicit) + : ArrayView(u.data(), u.size()) {} + + // Indexing and iteration. These allow mutation even if the ArrayView is + // const, because the ArrayView doesn't own the array. (To prevent mutation, + // use a const element type.) + T& operator[](size_t idx) const { + RTC_DCHECK_LT(idx, this->size()); + RTC_DCHECK(this->data()); + return this->data()[idx]; + } + T* begin() const { return this->data(); } + T* end() const { return this->data() + this->size(); } + const T* cbegin() const { return this->data(); } + const T* cend() const { return this->data() + this->size(); } + + ArrayView subview(size_t offset, size_t size) const { + return offset < this->size() + ? ArrayView(this->data() + offset, + std::min(size, this->size() - offset)) + : ArrayView(); + } + ArrayView subview(size_t offset) const { + return subview(offset, this->size()); + } +}; + +// Comparing two ArrayViews compares their (pointer,size) pairs; it does *not* +// dereference the pointers. +template +bool operator==(const ArrayView& a, const ArrayView& b) { + return a.data() == b.data() && a.size() == b.size(); +} +template +bool operator!=(const ArrayView& a, const ArrayView& b) { + return !(a == b); +} + +// Variable-size ArrayViews are the size of two pointers; fixed-size ArrayViews +// are the size of one pointer. (And as a special case, fixed-size ArrayViews +// of size 0 require no storage.) +static_assert(sizeof(ArrayView) == 2 * sizeof(int*), ""); +static_assert(sizeof(ArrayView) == sizeof(int*), ""); +static_assert(std::is_empty>::value, ""); + +template +inline ArrayView MakeArrayView(T* data, size_t size) { + return ArrayView(data, size); +} + +// Only for primitive types that have the same size and aligment. +// Allow reinterpret cast of the array view to another primitive type of the +// same size. +// Template arguments order is (U, T, Size) to allow deduction of the template +// arguments in client calls: reinterpret_array_view(array_view). +template +inline ArrayView reinterpret_array_view(ArrayView view) { + static_assert(sizeof(U) == sizeof(T) && alignof(U) == alignof(T), + "ArrayView reinterpret_cast is only supported for casting " + "between views that represent the same chunk of memory."); + static_assert( + std::is_fundamental::value && std::is_fundamental::value, + "ArrayView reinterpret_cast is only supported for casting between " + "fundamental types."); + return ArrayView(reinterpret_cast(view.data()), view.size()); +} + +} // namespace rtc + +#endif // API_ARRAY_VIEW_H_ diff --git a/webrtc/api/audio/audio_frame.cc b/webrtc/api/audio/audio_frame.cc new file mode 100644 index 0000000..c6e5cf4 --- /dev/null +++ b/webrtc/api/audio/audio_frame.cc @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2018 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 "api/audio/audio_frame.h" + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +AudioFrame::AudioFrame() { + // Visual Studio doesn't like this in the class definition. + static_assert(sizeof(data_) == kMaxDataSizeBytes, "kMaxDataSizeBytes"); +} + +void swap(AudioFrame& a, AudioFrame& b) { + using std::swap; + swap(a.timestamp_, b.timestamp_); + swap(a.elapsed_time_ms_, b.elapsed_time_ms_); + swap(a.ntp_time_ms_, b.ntp_time_ms_); + swap(a.samples_per_channel_, b.samples_per_channel_); + swap(a.sample_rate_hz_, b.sample_rate_hz_); + swap(a.num_channels_, b.num_channels_); + swap(a.channel_layout_, b.channel_layout_); + swap(a.speech_type_, b.speech_type_); + swap(a.vad_activity_, b.vad_activity_); + swap(a.profile_timestamp_ms_, b.profile_timestamp_ms_); + swap(a.packet_infos_, b.packet_infos_); + const size_t length_a = a.samples_per_channel_ * a.num_channels_; + const size_t length_b = b.samples_per_channel_ * b.num_channels_; + RTC_DCHECK_LE(length_a, AudioFrame::kMaxDataSizeSamples); + RTC_DCHECK_LE(length_b, AudioFrame::kMaxDataSizeSamples); + std::swap_ranges(a.data_, a.data_ + std::max(length_a, length_b), b.data_); + swap(a.muted_, b.muted_); + swap(a.absolute_capture_timestamp_ms_, b.absolute_capture_timestamp_ms_); +} + +void AudioFrame::Reset() { + ResetWithoutMuting(); + muted_ = true; +} + +void AudioFrame::ResetWithoutMuting() { + // TODO(wu): Zero is a valid value for |timestamp_|. We should initialize + // to an invalid value, or add a new member to indicate invalidity. + timestamp_ = 0; + elapsed_time_ms_ = -1; + ntp_time_ms_ = -1; + samples_per_channel_ = 0; + sample_rate_hz_ = 0; + num_channels_ = 0; + channel_layout_ = CHANNEL_LAYOUT_NONE; + speech_type_ = kUndefined; + vad_activity_ = kVadUnknown; + profile_timestamp_ms_ = 0; + packet_infos_ = RtpPacketInfos(); + absolute_capture_timestamp_ms_ = absl::nullopt; +} + +void AudioFrame::UpdateFrame(uint32_t timestamp, + const int16_t* data, + size_t samples_per_channel, + int sample_rate_hz, + SpeechType speech_type, + VADActivity vad_activity, + size_t num_channels) { + timestamp_ = timestamp; + samples_per_channel_ = samples_per_channel; + sample_rate_hz_ = sample_rate_hz; + speech_type_ = speech_type; + vad_activity_ = vad_activity; + num_channels_ = num_channels; + channel_layout_ = GuessChannelLayout(num_channels); + if (channel_layout_ != CHANNEL_LAYOUT_UNSUPPORTED) { + RTC_DCHECK_EQ(num_channels, ChannelLayoutToChannelCount(channel_layout_)); + } + + const size_t length = samples_per_channel * num_channels; + RTC_CHECK_LE(length, kMaxDataSizeSamples); + if (data != nullptr) { + memcpy(data_, data, sizeof(int16_t) * length); + muted_ = false; + } else { + muted_ = true; + } +} + +void AudioFrame::CopyFrom(const AudioFrame& src) { + if (this == &src) + return; + + timestamp_ = src.timestamp_; + elapsed_time_ms_ = src.elapsed_time_ms_; + ntp_time_ms_ = src.ntp_time_ms_; + packet_infos_ = src.packet_infos_; + muted_ = src.muted(); + samples_per_channel_ = src.samples_per_channel_; + sample_rate_hz_ = src.sample_rate_hz_; + speech_type_ = src.speech_type_; + vad_activity_ = src.vad_activity_; + num_channels_ = src.num_channels_; + channel_layout_ = src.channel_layout_; + absolute_capture_timestamp_ms_ = src.absolute_capture_timestamp_ms(); + + const size_t length = samples_per_channel_ * num_channels_; + RTC_CHECK_LE(length, kMaxDataSizeSamples); + if (!src.muted()) { + memcpy(data_, src.data(), sizeof(int16_t) * length); + muted_ = false; + } +} + +void AudioFrame::UpdateProfileTimeStamp() { + profile_timestamp_ms_ = rtc::TimeMillis(); +} + +int64_t AudioFrame::ElapsedProfileTimeMs() const { + if (profile_timestamp_ms_ == 0) { + // Profiling has not been activated. + return -1; + } + return rtc::TimeSince(profile_timestamp_ms_); +} + +const int16_t* AudioFrame::data() const { + return muted_ ? empty_data() : data_; +} + +// TODO(henrik.lundin) Can we skip zeroing the buffer? +// See https://bugs.chromium.org/p/webrtc/issues/detail?id=5647. +int16_t* AudioFrame::mutable_data() { + if (muted_) { + memset(data_, 0, kMaxDataSizeBytes); + muted_ = false; + } + return data_; +} + +void AudioFrame::Mute() { + muted_ = true; +} + +bool AudioFrame::muted() const { + return muted_; +} + +// static +const int16_t* AudioFrame::empty_data() { + static int16_t* null_data = new int16_t[kMaxDataSizeSamples](); + return &null_data[0]; +} + +} // namespace webrtc diff --git a/webrtc/api/audio/audio_frame.h b/webrtc/api/audio/audio_frame.h new file mode 100644 index 0000000..78539f5 --- /dev/null +++ b/webrtc/api/audio/audio_frame.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef API_AUDIO_AUDIO_FRAME_H_ +#define API_AUDIO_AUDIO_FRAME_H_ + +#include +#include + +#include + +#include "api/audio/channel_layout.h" +#include "api/rtp_packet_infos.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +/* This class holds up to 120 ms of super-wideband (32 kHz) stereo audio. It + * allows for adding and subtracting frames while keeping track of the resulting + * states. + * + * Notes + * - This is a de-facto api, not designed for external use. The AudioFrame class + * is in need of overhaul or even replacement, and anyone depending on it + * should be prepared for that. + * - The total number of samples is samples_per_channel_ * num_channels_. + * - Stereo data is interleaved starting with the left channel. + */ +class AudioFrame { + public: + // Using constexpr here causes linker errors unless the variable also has an + // out-of-class definition, which is impractical in this header-only class. + // (This makes no sense because it compiles as an enum value, which we most + // certainly cannot take the address of, just fine.) C++17 introduces inline + // variables which should allow us to switch to constexpr and keep this a + // header-only class. + enum : size_t { + // Stereo, 32 kHz, 120 ms (2 * 32 * 120) + // Stereo, 192 kHz, 20 ms (2 * 192 * 20) + kMaxDataSizeSamples = 7680, + kMaxDataSizeBytes = kMaxDataSizeSamples * sizeof(int16_t), + }; + + enum VADActivity { kVadActive = 0, kVadPassive = 1, kVadUnknown = 2 }; + enum SpeechType { + kNormalSpeech = 0, + kPLC = 1, + kCNG = 2, + kPLCCNG = 3, + kCodecPLC = 5, + kUndefined = 4 + }; + + AudioFrame(); + + friend void swap(AudioFrame& a, AudioFrame& b); + + // Resets all members to their default state. + void Reset(); + // Same as Reset(), but leaves mute state unchanged. Muting a frame requires + // the buffer to be zeroed on the next call to mutable_data(). Callers + // intending to write to the buffer immediately after Reset() can instead use + // ResetWithoutMuting() to skip this wasteful zeroing. + void ResetWithoutMuting(); + + void UpdateFrame(uint32_t timestamp, + const int16_t* data, + size_t samples_per_channel, + int sample_rate_hz, + SpeechType speech_type, + VADActivity vad_activity, + size_t num_channels = 1); + + void CopyFrom(const AudioFrame& src); + + // Sets a wall-time clock timestamp in milliseconds to be used for profiling + // of time between two points in the audio chain. + // Example: + // t0: UpdateProfileTimeStamp() + // t1: ElapsedProfileTimeMs() => t1 - t0 [msec] + void UpdateProfileTimeStamp(); + // Returns the time difference between now and when UpdateProfileTimeStamp() + // was last called. Returns -1 if UpdateProfileTimeStamp() has not yet been + // called. + int64_t ElapsedProfileTimeMs() const; + + // data() returns a zeroed static buffer if the frame is muted. + // mutable_frame() always returns a non-static buffer; the first call to + // mutable_frame() zeros the non-static buffer and marks the frame unmuted. + const int16_t* data() const; + int16_t* mutable_data(); + + // Prefer to mute frames using AudioFrameOperations::Mute. + void Mute(); + // Frame is muted by default. + bool muted() const; + + size_t max_16bit_samples() const { return kMaxDataSizeSamples; } + size_t samples_per_channel() const { return samples_per_channel_; } + size_t num_channels() const { return num_channels_; } + ChannelLayout channel_layout() const { return channel_layout_; } + int sample_rate_hz() const { return sample_rate_hz_; } + + void set_absolute_capture_timestamp_ms( + int64_t absolute_capture_time_stamp_ms) { + absolute_capture_timestamp_ms_ = absolute_capture_time_stamp_ms; + } + + absl::optional absolute_capture_timestamp_ms() const { + return absolute_capture_timestamp_ms_; + } + + // RTP timestamp of the first sample in the AudioFrame. + uint32_t timestamp_ = 0; + // Time since the first frame in milliseconds. + // -1 represents an uninitialized value. + int64_t elapsed_time_ms_ = -1; + // NTP time of the estimated capture time in local timebase in milliseconds. + // -1 represents an uninitialized value. + int64_t ntp_time_ms_ = -1; + size_t samples_per_channel_ = 0; + int sample_rate_hz_ = 0; + size_t num_channels_ = 0; + ChannelLayout channel_layout_ = CHANNEL_LAYOUT_NONE; + SpeechType speech_type_ = kUndefined; + VADActivity vad_activity_ = kVadUnknown; + // Monotonically increasing timestamp intended for profiling of audio frames. + // Typically used for measuring elapsed time between two different points in + // the audio path. No lock is used to save resources and we are thread safe + // by design. + // TODO(nisse@webrtc.org): consider using absl::optional. + int64_t profile_timestamp_ms_ = 0; + + // Information about packets used to assemble this audio frame. This is needed + // by |SourceTracker| when the frame is delivered to the RTCRtpReceiver's + // MediaStreamTrack, in order to implement getContributingSources(). See: + // https://w3c.github.io/webrtc-pc/#dom-rtcrtpreceiver-getcontributingsources + // + // TODO(bugs.webrtc.org/10757): + // Note that this information might not be fully accurate since we currently + // don't have a proper way to track it across the audio sync buffer. The + // sync buffer is the small sample-holding buffer located after the audio + // decoder and before where samples are assembled into output frames. + // + // |RtpPacketInfos| may also be empty if the audio samples did not come from + // RTP packets. E.g. if the audio were locally generated by packet loss + // concealment, comfort noise generation, etc. + RtpPacketInfos packet_infos_; + + private: + // A permanently zeroed out buffer to represent muted frames. This is a + // header-only class, so the only way to avoid creating a separate empty + // buffer per translation unit is to wrap a static in an inline function. + static const int16_t* empty_data(); + + int16_t data_[kMaxDataSizeSamples]; + bool muted_ = true; + + // Absolute capture timestamp when this audio frame was originally captured. + // This is only valid for audio frames captured on this machine. The absolute + // capture timestamp of a received frame is found in |packet_infos_|. + // This timestamp MUST be based on the same clock as rtc::TimeMillis(). + absl::optional absolute_capture_timestamp_ms_; + + RTC_DISALLOW_COPY_AND_ASSIGN(AudioFrame); +}; + +} // namespace webrtc + +#endif // API_AUDIO_AUDIO_FRAME_H_ diff --git a/webrtc/api/audio/channel_layout.cc b/webrtc/api/audio/channel_layout.cc new file mode 100644 index 0000000..567f4d9 --- /dev/null +++ b/webrtc/api/audio/channel_layout.cc @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2019 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 "api/audio/channel_layout.h" + +#include + +#include "rtc_base/arraysize.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +static const int kLayoutToChannels[] = { + 0, // CHANNEL_LAYOUT_NONE + 0, // CHANNEL_LAYOUT_UNSUPPORTED + 1, // CHANNEL_LAYOUT_MONO + 2, // CHANNEL_LAYOUT_STEREO + 3, // CHANNEL_LAYOUT_2_1 + 3, // CHANNEL_LAYOUT_SURROUND + 4, // CHANNEL_LAYOUT_4_0 + 4, // CHANNEL_LAYOUT_2_2 + 4, // CHANNEL_LAYOUT_QUAD + 5, // CHANNEL_LAYOUT_5_0 + 6, // CHANNEL_LAYOUT_5_1 + 5, // CHANNEL_LAYOUT_5_0_BACK + 6, // CHANNEL_LAYOUT_5_1_BACK + 7, // CHANNEL_LAYOUT_7_0 + 8, // CHANNEL_LAYOUT_7_1 + 8, // CHANNEL_LAYOUT_7_1_WIDE + 2, // CHANNEL_LAYOUT_STEREO_DOWNMIX + 3, // CHANNEL_LAYOUT_2POINT1 + 4, // CHANNEL_LAYOUT_3_1 + 5, // CHANNEL_LAYOUT_4_1 + 6, // CHANNEL_LAYOUT_6_0 + 6, // CHANNEL_LAYOUT_6_0_FRONT + 6, // CHANNEL_LAYOUT_HEXAGONAL + 7, // CHANNEL_LAYOUT_6_1 + 7, // CHANNEL_LAYOUT_6_1_BACK + 7, // CHANNEL_LAYOUT_6_1_FRONT + 7, // CHANNEL_LAYOUT_7_0_FRONT + 8, // CHANNEL_LAYOUT_7_1_WIDE_BACK + 8, // CHANNEL_LAYOUT_OCTAGONAL + 0, // CHANNEL_LAYOUT_DISCRETE + 3, // CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC + 5, // CHANNEL_LAYOUT_4_1_QUAD_SIDE + 0, // CHANNEL_LAYOUT_BITSTREAM +}; + +// The channel orderings for each layout as specified by FFmpeg. Each value +// represents the index of each channel in each layout. Values of -1 mean the +// channel at that index is not used for that layout. For example, the left side +// surround sound channel in FFmpeg's 5.1 layout is in the 5th position (because +// the order is L, R, C, LFE, LS, RS), so +// kChannelOrderings[CHANNEL_LAYOUT_5_1][SIDE_LEFT] = 4; +static const int kChannelOrderings[CHANNEL_LAYOUT_MAX + 1][CHANNELS_MAX + 1] = { + // FL | FR | FC | LFE | BL | BR | FLofC | FRofC | BC | SL | SR + + // CHANNEL_LAYOUT_NONE + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_UNSUPPORTED + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_MONO + {-1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_STEREO + {0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_2_1 + {0, 1, -1, -1, -1, -1, -1, -1, 2, -1, -1}, + + // CHANNEL_LAYOUT_SURROUND + {0, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_4_0 + {0, 1, 2, -1, -1, -1, -1, -1, 3, -1, -1}, + + // CHANNEL_LAYOUT_2_2 + {0, 1, -1, -1, -1, -1, -1, -1, -1, 2, 3}, + + // CHANNEL_LAYOUT_QUAD + {0, 1, -1, -1, 2, 3, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_5_0 + {0, 1, 2, -1, -1, -1, -1, -1, -1, 3, 4}, + + // CHANNEL_LAYOUT_5_1 + {0, 1, 2, 3, -1, -1, -1, -1, -1, 4, 5}, + + // FL | FR | FC | LFE | BL | BR | FLofC | FRofC | BC | SL | SR + + // CHANNEL_LAYOUT_5_0_BACK + {0, 1, 2, -1, 3, 4, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_5_1_BACK + {0, 1, 2, 3, 4, 5, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_7_0 + {0, 1, 2, -1, 5, 6, -1, -1, -1, 3, 4}, + + // CHANNEL_LAYOUT_7_1 + {0, 1, 2, 3, 6, 7, -1, -1, -1, 4, 5}, + + // CHANNEL_LAYOUT_7_1_WIDE + {0, 1, 2, 3, -1, -1, 6, 7, -1, 4, 5}, + + // CHANNEL_LAYOUT_STEREO_DOWNMIX + {0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_2POINT1 + {0, 1, -1, 2, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_3_1 + {0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_4_1 + {0, 1, 2, 4, -1, -1, -1, -1, 3, -1, -1}, + + // CHANNEL_LAYOUT_6_0 + {0, 1, 2, -1, -1, -1, -1, -1, 5, 3, 4}, + + // CHANNEL_LAYOUT_6_0_FRONT + {0, 1, -1, -1, -1, -1, 4, 5, -1, 2, 3}, + + // FL | FR | FC | LFE | BL | BR | FLofC | FRofC | BC | SL | SR + + // CHANNEL_LAYOUT_HEXAGONAL + {0, 1, 2, -1, 3, 4, -1, -1, 5, -1, -1}, + + // CHANNEL_LAYOUT_6_1 + {0, 1, 2, 3, -1, -1, -1, -1, 6, 4, 5}, + + // CHANNEL_LAYOUT_6_1_BACK + {0, 1, 2, 3, 4, 5, -1, -1, 6, -1, -1}, + + // CHANNEL_LAYOUT_6_1_FRONT + {0, 1, -1, 6, -1, -1, 4, 5, -1, 2, 3}, + + // CHANNEL_LAYOUT_7_0_FRONT + {0, 1, 2, -1, -1, -1, 5, 6, -1, 3, 4}, + + // CHANNEL_LAYOUT_7_1_WIDE_BACK + {0, 1, 2, 3, 4, 5, 6, 7, -1, -1, -1}, + + // CHANNEL_LAYOUT_OCTAGONAL + {0, 1, 2, -1, 5, 6, -1, -1, 7, 3, 4}, + + // CHANNEL_LAYOUT_DISCRETE + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC + {0, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + + // CHANNEL_LAYOUT_4_1_QUAD_SIDE + {0, 1, -1, 4, -1, -1, -1, -1, -1, 2, 3}, + + // CHANNEL_LAYOUT_BITSTREAM + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + + // FL | FR | FC | LFE | BL | BR | FLofC | FRofC | BC | SL | SR +}; + +int ChannelLayoutToChannelCount(ChannelLayout layout) { + RTC_DCHECK_LT(static_cast(layout), arraysize(kLayoutToChannels)); + RTC_DCHECK_LE(kLayoutToChannels[layout], kMaxConcurrentChannels); + return kLayoutToChannels[layout]; +} + +// Converts a channel count into a channel layout. +ChannelLayout GuessChannelLayout(int channels) { + switch (channels) { + case 1: + return CHANNEL_LAYOUT_MONO; + case 2: + return CHANNEL_LAYOUT_STEREO; + case 3: + return CHANNEL_LAYOUT_SURROUND; + case 4: + return CHANNEL_LAYOUT_QUAD; + case 5: + return CHANNEL_LAYOUT_5_0; + case 6: + return CHANNEL_LAYOUT_5_1; + case 7: + return CHANNEL_LAYOUT_6_1; + case 8: + return CHANNEL_LAYOUT_7_1; + default: + RTC_DLOG(LS_WARNING) << "Unsupported channel count: " << channels; + } + return CHANNEL_LAYOUT_UNSUPPORTED; +} + +int ChannelOrder(ChannelLayout layout, Channels channel) { + RTC_DCHECK_LT(static_cast(layout), arraysize(kChannelOrderings)); + RTC_DCHECK_LT(static_cast(channel), arraysize(kChannelOrderings[0])); + return kChannelOrderings[layout][channel]; +} + +const char* ChannelLayoutToString(ChannelLayout layout) { + switch (layout) { + case CHANNEL_LAYOUT_NONE: + return "NONE"; + case CHANNEL_LAYOUT_UNSUPPORTED: + return "UNSUPPORTED"; + case CHANNEL_LAYOUT_MONO: + return "MONO"; + case CHANNEL_LAYOUT_STEREO: + return "STEREO"; + case CHANNEL_LAYOUT_2_1: + return "2.1"; + case CHANNEL_LAYOUT_SURROUND: + return "SURROUND"; + case CHANNEL_LAYOUT_4_0: + return "4.0"; + case CHANNEL_LAYOUT_2_2: + return "QUAD_SIDE"; + case CHANNEL_LAYOUT_QUAD: + return "QUAD"; + case CHANNEL_LAYOUT_5_0: + return "5.0"; + case CHANNEL_LAYOUT_5_1: + return "5.1"; + case CHANNEL_LAYOUT_5_0_BACK: + return "5.0_BACK"; + case CHANNEL_LAYOUT_5_1_BACK: + return "5.1_BACK"; + case CHANNEL_LAYOUT_7_0: + return "7.0"; + case CHANNEL_LAYOUT_7_1: + return "7.1"; + case CHANNEL_LAYOUT_7_1_WIDE: + return "7.1_WIDE"; + case CHANNEL_LAYOUT_STEREO_DOWNMIX: + return "STEREO_DOWNMIX"; + case CHANNEL_LAYOUT_2POINT1: + return "2POINT1"; + case CHANNEL_LAYOUT_3_1: + return "3.1"; + case CHANNEL_LAYOUT_4_1: + return "4.1"; + case CHANNEL_LAYOUT_6_0: + return "6.0"; + case CHANNEL_LAYOUT_6_0_FRONT: + return "6.0_FRONT"; + case CHANNEL_LAYOUT_HEXAGONAL: + return "HEXAGONAL"; + case CHANNEL_LAYOUT_6_1: + return "6.1"; + case CHANNEL_LAYOUT_6_1_BACK: + return "6.1_BACK"; + case CHANNEL_LAYOUT_6_1_FRONT: + return "6.1_FRONT"; + case CHANNEL_LAYOUT_7_0_FRONT: + return "7.0_FRONT"; + case CHANNEL_LAYOUT_7_1_WIDE_BACK: + return "7.1_WIDE_BACK"; + case CHANNEL_LAYOUT_OCTAGONAL: + return "OCTAGONAL"; + case CHANNEL_LAYOUT_DISCRETE: + return "DISCRETE"; + case CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC: + return "STEREO_AND_KEYBOARD_MIC"; + case CHANNEL_LAYOUT_4_1_QUAD_SIDE: + return "4.1_QUAD_SIDE"; + case CHANNEL_LAYOUT_BITSTREAM: + return "BITSTREAM"; + } + RTC_NOTREACHED() << "Invalid channel layout provided: " << layout; + return ""; +} + +} // namespace webrtc diff --git a/webrtc/api/audio/channel_layout.h b/webrtc/api/audio/channel_layout.h new file mode 100644 index 0000000..175aee7 --- /dev/null +++ b/webrtc/api/audio/channel_layout.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef API_AUDIO_CHANNEL_LAYOUT_H_ +#define API_AUDIO_CHANNEL_LAYOUT_H_ + +namespace webrtc { + +// This file is derived from Chromium's base/channel_layout.h. + +// Enumerates the various representations of the ordering of audio channels. +// Logged to UMA, so never reuse a value, always add new/greater ones! +enum ChannelLayout { + CHANNEL_LAYOUT_NONE = 0, + CHANNEL_LAYOUT_UNSUPPORTED = 1, + + // Front C + CHANNEL_LAYOUT_MONO = 2, + + // Front L, Front R + CHANNEL_LAYOUT_STEREO = 3, + + // Front L, Front R, Back C + CHANNEL_LAYOUT_2_1 = 4, + + // Front L, Front R, Front C + CHANNEL_LAYOUT_SURROUND = 5, + + // Front L, Front R, Front C, Back C + CHANNEL_LAYOUT_4_0 = 6, + + // Front L, Front R, Side L, Side R + CHANNEL_LAYOUT_2_2 = 7, + + // Front L, Front R, Back L, Back R + CHANNEL_LAYOUT_QUAD = 8, + + // Front L, Front R, Front C, Side L, Side R + CHANNEL_LAYOUT_5_0 = 9, + + // Front L, Front R, Front C, LFE, Side L, Side R + CHANNEL_LAYOUT_5_1 = 10, + + // Front L, Front R, Front C, Back L, Back R + CHANNEL_LAYOUT_5_0_BACK = 11, + + // Front L, Front R, Front C, LFE, Back L, Back R + CHANNEL_LAYOUT_5_1_BACK = 12, + + // Front L, Front R, Front C, Side L, Side R, Back L, Back R + CHANNEL_LAYOUT_7_0 = 13, + + // Front L, Front R, Front C, LFE, Side L, Side R, Back L, Back R + CHANNEL_LAYOUT_7_1 = 14, + + // Front L, Front R, Front C, LFE, Side L, Side R, Front LofC, Front RofC + CHANNEL_LAYOUT_7_1_WIDE = 15, + + // Stereo L, Stereo R + CHANNEL_LAYOUT_STEREO_DOWNMIX = 16, + + // Stereo L, Stereo R, LFE + CHANNEL_LAYOUT_2POINT1 = 17, + + // Stereo L, Stereo R, Front C, LFE + CHANNEL_LAYOUT_3_1 = 18, + + // Stereo L, Stereo R, Front C, Rear C, LFE + CHANNEL_LAYOUT_4_1 = 19, + + // Stereo L, Stereo R, Front C, Side L, Side R, Back C + CHANNEL_LAYOUT_6_0 = 20, + + // Stereo L, Stereo R, Side L, Side R, Front LofC, Front RofC + CHANNEL_LAYOUT_6_0_FRONT = 21, + + // Stereo L, Stereo R, Front C, Rear L, Rear R, Rear C + CHANNEL_LAYOUT_HEXAGONAL = 22, + + // Stereo L, Stereo R, Front C, LFE, Side L, Side R, Rear Center + CHANNEL_LAYOUT_6_1 = 23, + + // Stereo L, Stereo R, Front C, LFE, Back L, Back R, Rear Center + CHANNEL_LAYOUT_6_1_BACK = 24, + + // Stereo L, Stereo R, Side L, Side R, Front LofC, Front RofC, LFE + CHANNEL_LAYOUT_6_1_FRONT = 25, + + // Front L, Front R, Front C, Side L, Side R, Front LofC, Front RofC + CHANNEL_LAYOUT_7_0_FRONT = 26, + + // Front L, Front R, Front C, LFE, Back L, Back R, Front LofC, Front RofC + CHANNEL_LAYOUT_7_1_WIDE_BACK = 27, + + // Front L, Front R, Front C, Side L, Side R, Rear L, Back R, Back C. + CHANNEL_LAYOUT_OCTAGONAL = 28, + + // Channels are not explicitly mapped to speakers. + CHANNEL_LAYOUT_DISCRETE = 29, + + // Front L, Front R, Front C. Front C contains the keyboard mic audio. This + // layout is only intended for input for WebRTC. The Front C channel + // is stripped away in the WebRTC audio input pipeline and never seen outside + // of that. + CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC = 30, + + // Front L, Front R, Side L, Side R, LFE + CHANNEL_LAYOUT_4_1_QUAD_SIDE = 31, + + // Actual channel layout is specified in the bitstream and the actual channel + // count is unknown at Chromium media pipeline level (useful for audio + // pass-through mode). + CHANNEL_LAYOUT_BITSTREAM = 32, + + // Max value, must always equal the largest entry ever logged. + CHANNEL_LAYOUT_MAX = CHANNEL_LAYOUT_BITSTREAM +}; + +// Note: Do not reorder or reassign these values; other code depends on their +// ordering to operate correctly. E.g., CoreAudio channel layout computations. +enum Channels { + LEFT = 0, + RIGHT, + CENTER, + LFE, + BACK_LEFT, + BACK_RIGHT, + LEFT_OF_CENTER, + RIGHT_OF_CENTER, + BACK_CENTER, + SIDE_LEFT, + SIDE_RIGHT, + CHANNELS_MAX = + SIDE_RIGHT, // Must always equal the largest value ever logged. +}; + +// The maximum number of concurrently active channels for all possible layouts. +// ChannelLayoutToChannelCount() will never return a value higher than this. +constexpr int kMaxConcurrentChannels = 8; + +// Returns the expected channel position in an interleaved stream. Values of -1 +// mean the channel at that index is not used for that layout. Values range +// from 0 to ChannelLayoutToChannelCount(layout) - 1. +int ChannelOrder(ChannelLayout layout, Channels channel); + +// Returns the number of channels in a given ChannelLayout. +int ChannelLayoutToChannelCount(ChannelLayout layout); + +// Given the number of channels, return the best layout, +// or return CHANNEL_LAYOUT_UNSUPPORTED if there is no good match. +ChannelLayout GuessChannelLayout(int channels); + +// Returns a string representation of the channel layout. +const char* ChannelLayoutToString(ChannelLayout layout); + +} // namespace webrtc + +#endif // API_AUDIO_CHANNEL_LAYOUT_H_ diff --git a/webrtc/api/audio/echo_canceller3_config.cc b/webrtc/api/audio/echo_canceller3_config.cc new file mode 100644 index 0000000..aeb809e --- /dev/null +++ b/webrtc/api/audio/echo_canceller3_config.cc @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2018 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 "api/audio/echo_canceller3_config.h" + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { +bool Limit(float* value, float min, float max) { + float clamped = rtc::SafeClamp(*value, min, max); + clamped = std::isfinite(clamped) ? clamped : min; + bool res = *value == clamped; + *value = clamped; + return res; +} + +bool Limit(size_t* value, size_t min, size_t max) { + size_t clamped = rtc::SafeClamp(*value, min, max); + bool res = *value == clamped; + *value = clamped; + return res; +} + +bool Limit(int* value, int min, int max) { + int clamped = rtc::SafeClamp(*value, min, max); + bool res = *value == clamped; + *value = clamped; + return res; +} + +bool FloorLimit(size_t* value, size_t min) { + size_t clamped = *value >= min ? *value : min; + bool res = *value == clamped; + *value = clamped; + return res; +} + +} // namespace + +EchoCanceller3Config::EchoCanceller3Config() = default; +EchoCanceller3Config::EchoCanceller3Config(const EchoCanceller3Config& e) = + default; +EchoCanceller3Config& EchoCanceller3Config::operator=( + const EchoCanceller3Config& e) = default; +EchoCanceller3Config::Delay::Delay() = default; +EchoCanceller3Config::Delay::Delay(const EchoCanceller3Config::Delay& e) = + default; +EchoCanceller3Config::Delay& EchoCanceller3Config::Delay::operator=( + const Delay& e) = default; + +EchoCanceller3Config::EchoModel::EchoModel() = default; +EchoCanceller3Config::EchoModel::EchoModel( + const EchoCanceller3Config::EchoModel& e) = default; +EchoCanceller3Config::EchoModel& EchoCanceller3Config::EchoModel::operator=( + const EchoModel& e) = default; + +EchoCanceller3Config::Suppressor::Suppressor() = default; +EchoCanceller3Config::Suppressor::Suppressor( + const EchoCanceller3Config::Suppressor& e) = default; +EchoCanceller3Config::Suppressor& EchoCanceller3Config::Suppressor::operator=( + const Suppressor& e) = default; + +EchoCanceller3Config::Suppressor::MaskingThresholds::MaskingThresholds( + float enr_transparent, + float enr_suppress, + float emr_transparent) + : enr_transparent(enr_transparent), + enr_suppress(enr_suppress), + emr_transparent(emr_transparent) {} +EchoCanceller3Config::Suppressor::MaskingThresholds::MaskingThresholds( + const EchoCanceller3Config::Suppressor::MaskingThresholds& e) = default; +EchoCanceller3Config::Suppressor::MaskingThresholds& +EchoCanceller3Config::Suppressor::MaskingThresholds::operator=( + const MaskingThresholds& e) = default; + +EchoCanceller3Config::Suppressor::Tuning::Tuning(MaskingThresholds mask_lf, + MaskingThresholds mask_hf, + float max_inc_factor, + float max_dec_factor_lf) + : mask_lf(mask_lf), + mask_hf(mask_hf), + max_inc_factor(max_inc_factor), + max_dec_factor_lf(max_dec_factor_lf) {} +EchoCanceller3Config::Suppressor::Tuning::Tuning( + const EchoCanceller3Config::Suppressor::Tuning& e) = default; +EchoCanceller3Config::Suppressor::Tuning& +EchoCanceller3Config::Suppressor::Tuning::operator=(const Tuning& e) = default; + +bool EchoCanceller3Config::Validate(EchoCanceller3Config* config) { + RTC_DCHECK(config); + EchoCanceller3Config* c = config; + bool res = true; + + if (c->delay.down_sampling_factor != 4 && + c->delay.down_sampling_factor != 8) { + c->delay.down_sampling_factor = 4; + res = false; + } + + res = res & Limit(&c->delay.default_delay, 0, 5000); + res = res & Limit(&c->delay.num_filters, 0, 5000); + res = res & Limit(&c->delay.delay_headroom_samples, 0, 5000); + res = res & Limit(&c->delay.hysteresis_limit_blocks, 0, 5000); + res = res & Limit(&c->delay.fixed_capture_delay_samples, 0, 5000); + res = res & Limit(&c->delay.delay_estimate_smoothing, 0.f, 1.f); + res = res & Limit(&c->delay.delay_candidate_detection_threshold, 0.f, 1.f); + res = res & Limit(&c->delay.delay_selection_thresholds.initial, 1, 250); + res = res & Limit(&c->delay.delay_selection_thresholds.converged, 1, 250); + + res = res & FloorLimit(&c->filter.refined.length_blocks, 1); + res = res & Limit(&c->filter.refined.leakage_converged, 0.f, 1000.f); + res = res & Limit(&c->filter.refined.leakage_diverged, 0.f, 1000.f); + res = res & Limit(&c->filter.refined.error_floor, 0.f, 1000.f); + res = res & Limit(&c->filter.refined.error_ceil, 0.f, 100000000.f); + res = res & Limit(&c->filter.refined.noise_gate, 0.f, 100000000.f); + + res = res & FloorLimit(&c->filter.refined_initial.length_blocks, 1); + res = res & Limit(&c->filter.refined_initial.leakage_converged, 0.f, 1000.f); + res = res & Limit(&c->filter.refined_initial.leakage_diverged, 0.f, 1000.f); + res = res & Limit(&c->filter.refined_initial.error_floor, 0.f, 1000.f); + res = res & Limit(&c->filter.refined_initial.error_ceil, 0.f, 100000000.f); + res = res & Limit(&c->filter.refined_initial.noise_gate, 0.f, 100000000.f); + + if (c->filter.refined.length_blocks < + c->filter.refined_initial.length_blocks) { + c->filter.refined_initial.length_blocks = c->filter.refined.length_blocks; + res = false; + } + + res = res & FloorLimit(&c->filter.coarse.length_blocks, 1); + res = res & Limit(&c->filter.coarse.rate, 0.f, 1.f); + res = res & Limit(&c->filter.coarse.noise_gate, 0.f, 100000000.f); + + res = res & FloorLimit(&c->filter.coarse_initial.length_blocks, 1); + res = res & Limit(&c->filter.coarse_initial.rate, 0.f, 1.f); + res = res & Limit(&c->filter.coarse_initial.noise_gate, 0.f, 100000000.f); + + if (c->filter.coarse.length_blocks < c->filter.coarse_initial.length_blocks) { + c->filter.coarse_initial.length_blocks = c->filter.coarse.length_blocks; + res = false; + } + + res = res & Limit(&c->filter.config_change_duration_blocks, 0, 100000); + res = res & Limit(&c->filter.initial_state_seconds, 0.f, 100.f); + + res = res & Limit(&c->erle.min, 1.f, 100000.f); + res = res & Limit(&c->erle.max_l, 1.f, 100000.f); + res = res & Limit(&c->erle.max_h, 1.f, 100000.f); + if (c->erle.min > c->erle.max_l || c->erle.min > c->erle.max_h) { + c->erle.min = std::min(c->erle.max_l, c->erle.max_h); + res = false; + } + res = res & Limit(&c->erle.num_sections, 1, c->filter.refined.length_blocks); + + res = res & Limit(&c->ep_strength.default_gain, 0.f, 1000000.f); + res = res & Limit(&c->ep_strength.default_len, -1.f, 1.f); + + res = + res & Limit(&c->echo_audibility.low_render_limit, 0.f, 32768.f * 32768.f); + res = res & + Limit(&c->echo_audibility.normal_render_limit, 0.f, 32768.f * 32768.f); + res = res & Limit(&c->echo_audibility.floor_power, 0.f, 32768.f * 32768.f); + res = res & Limit(&c->echo_audibility.audibility_threshold_lf, 0.f, + 32768.f * 32768.f); + res = res & Limit(&c->echo_audibility.audibility_threshold_mf, 0.f, + 32768.f * 32768.f); + res = res & Limit(&c->echo_audibility.audibility_threshold_hf, 0.f, + 32768.f * 32768.f); + + res = res & + Limit(&c->render_levels.active_render_limit, 0.f, 32768.f * 32768.f); + res = res & Limit(&c->render_levels.poor_excitation_render_limit, 0.f, + 32768.f * 32768.f); + res = res & Limit(&c->render_levels.poor_excitation_render_limit_ds8, 0.f, + 32768.f * 32768.f); + + res = res & Limit(&c->echo_model.noise_floor_hold, 0, 1000); + res = res & Limit(&c->echo_model.min_noise_floor_power, 0, 2000000.f); + res = res & Limit(&c->echo_model.stationary_gate_slope, 0, 1000000.f); + res = res & Limit(&c->echo_model.noise_gate_power, 0, 1000000.f); + res = res & Limit(&c->echo_model.noise_gate_slope, 0, 1000000.f); + res = res & Limit(&c->echo_model.render_pre_window_size, 0, 100); + res = res & Limit(&c->echo_model.render_post_window_size, 0, 100); + + res = res & Limit(&c->comfort_noise.noise_floor_dbfs, -200.f, 0.f); + + res = res & Limit(&c->suppressor.nearend_average_blocks, 1, 5000); + + res = res & + Limit(&c->suppressor.normal_tuning.mask_lf.enr_transparent, 0.f, 100.f); + res = res & + Limit(&c->suppressor.normal_tuning.mask_lf.enr_suppress, 0.f, 100.f); + res = res & + Limit(&c->suppressor.normal_tuning.mask_lf.emr_transparent, 0.f, 100.f); + res = res & + Limit(&c->suppressor.normal_tuning.mask_hf.enr_transparent, 0.f, 100.f); + res = res & + Limit(&c->suppressor.normal_tuning.mask_hf.enr_suppress, 0.f, 100.f); + res = res & + Limit(&c->suppressor.normal_tuning.mask_hf.emr_transparent, 0.f, 100.f); + res = res & Limit(&c->suppressor.normal_tuning.max_inc_factor, 0.f, 100.f); + res = res & Limit(&c->suppressor.normal_tuning.max_dec_factor_lf, 0.f, 100.f); + + res = res & Limit(&c->suppressor.nearend_tuning.mask_lf.enr_transparent, 0.f, + 100.f); + res = res & + Limit(&c->suppressor.nearend_tuning.mask_lf.enr_suppress, 0.f, 100.f); + res = res & Limit(&c->suppressor.nearend_tuning.mask_lf.emr_transparent, 0.f, + 100.f); + res = res & Limit(&c->suppressor.nearend_tuning.mask_hf.enr_transparent, 0.f, + 100.f); + res = res & + Limit(&c->suppressor.nearend_tuning.mask_hf.enr_suppress, 0.f, 100.f); + res = res & Limit(&c->suppressor.nearend_tuning.mask_hf.emr_transparent, 0.f, + 100.f); + res = res & Limit(&c->suppressor.nearend_tuning.max_inc_factor, 0.f, 100.f); + res = + res & Limit(&c->suppressor.nearend_tuning.max_dec_factor_lf, 0.f, 100.f); + + res = res & Limit(&c->suppressor.dominant_nearend_detection.enr_threshold, + 0.f, 1000000.f); + res = res & Limit(&c->suppressor.dominant_nearend_detection.snr_threshold, + 0.f, 1000000.f); + res = res & Limit(&c->suppressor.dominant_nearend_detection.hold_duration, 0, + 10000); + res = res & Limit(&c->suppressor.dominant_nearend_detection.trigger_threshold, + 0, 10000); + + res = res & + Limit(&c->suppressor.subband_nearend_detection.nearend_average_blocks, + 1, 1024); + res = + res & Limit(&c->suppressor.subband_nearend_detection.subband1.low, 0, 65); + res = res & Limit(&c->suppressor.subband_nearend_detection.subband1.high, + c->suppressor.subband_nearend_detection.subband1.low, 65); + res = + res & Limit(&c->suppressor.subband_nearend_detection.subband2.low, 0, 65); + res = res & Limit(&c->suppressor.subband_nearend_detection.subband2.high, + c->suppressor.subband_nearend_detection.subband2.low, 65); + res = res & Limit(&c->suppressor.subband_nearend_detection.nearend_threshold, + 0.f, 1.e24f); + res = res & Limit(&c->suppressor.subband_nearend_detection.snr_threshold, 0.f, + 1.e24f); + + res = res & Limit(&c->suppressor.high_bands_suppression.enr_threshold, 0.f, + 1000000.f); + res = res & Limit(&c->suppressor.high_bands_suppression.max_gain_during_echo, + 0.f, 1.f); + res = res & Limit(&c->suppressor.high_bands_suppression + .anti_howling_activation_threshold, + 0.f, 32768.f * 32768.f); + res = res & Limit(&c->suppressor.high_bands_suppression.anti_howling_gain, + 0.f, 1.f); + + res = res & Limit(&c->suppressor.floor_first_increase, 0.f, 1000000.f); + + return res; +} +} // namespace webrtc diff --git a/webrtc/api/audio/echo_canceller3_config.h b/webrtc/api/audio/echo_canceller3_config.h new file mode 100644 index 0000000..af57d04 --- /dev/null +++ b/webrtc/api/audio/echo_canceller3_config.h @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef API_AUDIO_ECHO_CANCELLER3_CONFIG_H_ +#define API_AUDIO_ECHO_CANCELLER3_CONFIG_H_ + +#include // size_t + +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Configuration struct for EchoCanceller3 +struct RTC_EXPORT EchoCanceller3Config { + // Checks and updates the config parameters to lie within (mostly) reasonable + // ranges. Returns true if and only of the config did not need to be changed. + static bool Validate(EchoCanceller3Config* config); + + EchoCanceller3Config(); + EchoCanceller3Config(const EchoCanceller3Config& e); + EchoCanceller3Config& operator=(const EchoCanceller3Config& other); + + struct Buffering { + size_t excess_render_detection_interval_blocks = 250; + size_t max_allowed_excess_render_blocks = 8; + } buffering; + + struct Delay { + Delay(); + Delay(const Delay& e); + Delay& operator=(const Delay& e); + size_t default_delay = 5; + size_t down_sampling_factor = 4; + size_t num_filters = 5; + size_t delay_headroom_samples = 32; + size_t hysteresis_limit_blocks = 1; + size_t fixed_capture_delay_samples = 0; + float delay_estimate_smoothing = 0.7f; + float delay_candidate_detection_threshold = 0.2f; + struct DelaySelectionThresholds { + int initial; + int converged; + } delay_selection_thresholds = {5, 20}; + bool use_external_delay_estimator = false; + bool log_warning_on_delay_changes = false; + struct AlignmentMixing { + bool downmix; + bool adaptive_selection; + float activity_power_threshold; + bool prefer_first_two_channels; + }; + AlignmentMixing render_alignment_mixing = {false, true, 10000.f, true}; + AlignmentMixing capture_alignment_mixing = {false, true, 10000.f, false}; + } delay; + + struct Filter { + struct RefinedConfiguration { + size_t length_blocks; + float leakage_converged; + float leakage_diverged; + float error_floor; + float error_ceil; + float noise_gate; + }; + + struct CoarseConfiguration { + size_t length_blocks; + float rate; + float noise_gate; + }; + + RefinedConfiguration refined = {13, 0.00005f, 0.05f, + 0.001f, 2.f, 20075344.f}; + CoarseConfiguration coarse = {13, 0.7f, 20075344.f}; + + RefinedConfiguration refined_initial = {12, 0.005f, 0.5f, + 0.001f, 2.f, 20075344.f}; + CoarseConfiguration coarse_initial = {12, 0.9f, 20075344.f}; + + size_t config_change_duration_blocks = 250; + float initial_state_seconds = 2.5f; + bool conservative_initial_phase = false; + bool enable_coarse_filter_output_usage = true; + bool use_linear_filter = true; + bool export_linear_aec_output = false; + } filter; + + struct Erle { + float min = 1.f; + float max_l = 4.f; + float max_h = 1.5f; + bool onset_detection = true; + size_t num_sections = 1; + bool clamp_quality_estimate_to_zero = true; + bool clamp_quality_estimate_to_one = true; + } erle; + + struct EpStrength { + float default_gain = 1.f; + float default_len = 0.83f; + bool echo_can_saturate = true; + bool bounded_erl = false; + } ep_strength; + + struct EchoAudibility { + float low_render_limit = 4 * 64.f; + float normal_render_limit = 64.f; + float floor_power = 2 * 64.f; + float audibility_threshold_lf = 10; + float audibility_threshold_mf = 10; + float audibility_threshold_hf = 10; + bool use_stationarity_properties = false; + bool use_stationarity_properties_at_init = false; + } echo_audibility; + + struct RenderLevels { + float active_render_limit = 100.f; + float poor_excitation_render_limit = 150.f; + float poor_excitation_render_limit_ds8 = 20.f; + float render_power_gain_db = 0.f; + } render_levels; + + struct EchoRemovalControl { + bool has_clock_drift = false; + bool linear_and_stable_echo_path = false; + } echo_removal_control; + + struct EchoModel { + EchoModel(); + EchoModel(const EchoModel& e); + EchoModel& operator=(const EchoModel& e); + size_t noise_floor_hold = 50; + float min_noise_floor_power = 1638400.f; + float stationary_gate_slope = 10.f; + float noise_gate_power = 27509.42f; + float noise_gate_slope = 0.3f; + size_t render_pre_window_size = 1; + size_t render_post_window_size = 1; + bool model_reverb_in_nonlinear_mode = true; + } echo_model; + + struct ComfortNoise { + float noise_floor_dbfs = -96.03406f; + } comfort_noise; + + struct Suppressor { + Suppressor(); + Suppressor(const Suppressor& e); + Suppressor& operator=(const Suppressor& e); + + size_t nearend_average_blocks = 4; + + struct MaskingThresholds { + MaskingThresholds(float enr_transparent, + float enr_suppress, + float emr_transparent); + MaskingThresholds(const MaskingThresholds& e); + MaskingThresholds& operator=(const MaskingThresholds& e); + float enr_transparent; + float enr_suppress; + float emr_transparent; + }; + + struct Tuning { + Tuning(MaskingThresholds mask_lf, + MaskingThresholds mask_hf, + float max_inc_factor, + float max_dec_factor_lf); + Tuning(const Tuning& e); + Tuning& operator=(const Tuning& e); + MaskingThresholds mask_lf; + MaskingThresholds mask_hf; + float max_inc_factor; + float max_dec_factor_lf; + }; + + Tuning normal_tuning = Tuning(MaskingThresholds(.3f, .4f, .3f), + MaskingThresholds(.07f, .1f, .3f), + 2.0f, + 0.25f); + Tuning nearend_tuning = Tuning(MaskingThresholds(1.09f, 1.1f, .3f), + MaskingThresholds(.1f, .3f, .3f), + 2.0f, + 0.25f); + + struct DominantNearendDetection { + float enr_threshold = .25f; + float enr_exit_threshold = 10.f; + float snr_threshold = 30.f; + int hold_duration = 50; + int trigger_threshold = 12; + bool use_during_initial_phase = true; + } dominant_nearend_detection; + + struct SubbandNearendDetection { + size_t nearend_average_blocks = 1; + struct SubbandRegion { + size_t low; + size_t high; + }; + SubbandRegion subband1 = {1, 1}; + SubbandRegion subband2 = {1, 1}; + float nearend_threshold = 1.f; + float snr_threshold = 1.f; + } subband_nearend_detection; + + bool use_subband_nearend_detection = false; + + struct HighBandsSuppression { + float enr_threshold = 1.f; + float max_gain_during_echo = 1.f; + float anti_howling_activation_threshold = 400.f; + float anti_howling_gain = 1.f; + } high_bands_suppression; + + float floor_first_increase = 0.00001f; + } suppressor; +}; +} // namespace webrtc + +#endif // API_AUDIO_ECHO_CANCELLER3_CONFIG_H_ diff --git a/webrtc/api/audio/echo_control.h b/webrtc/api/audio/echo_control.h new file mode 100644 index 0000000..8d567bf --- /dev/null +++ b/webrtc/api/audio/echo_control.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef API_AUDIO_ECHO_CONTROL_H_ +#define API_AUDIO_ECHO_CONTROL_H_ + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +class AudioBuffer; + +// Interface for an acoustic echo cancellation (AEC) submodule. +class EchoControl { + public: + // Analysis (not changing) of the render signal. + virtual void AnalyzeRender(AudioBuffer* render) = 0; + + // Analysis (not changing) of the capture signal. + virtual void AnalyzeCapture(AudioBuffer* capture) = 0; + + // Processes the capture signal in order to remove the echo. + virtual void ProcessCapture(AudioBuffer* capture, bool level_change) = 0; + + // As above, but also returns the linear filter output. + virtual void ProcessCapture(AudioBuffer* capture, + AudioBuffer* linear_output, + bool level_change) = 0; + + struct Metrics { + double echo_return_loss; + double echo_return_loss_enhancement; + int delay_ms; + }; + + // Collect current metrics from the echo controller. + virtual Metrics GetMetrics() const = 0; + + // Provides an optional external estimate of the audio buffer delay. + virtual void SetAudioBufferDelay(int delay_ms) = 0; + + // Returns wheter the signal is altered. + virtual bool ActiveProcessing() const = 0; + + virtual ~EchoControl() {} +}; + +// Interface for a factory that creates EchoControllers. +class EchoControlFactory { + public: + virtual std::unique_ptr Create(int sample_rate_hz, + int num_render_channels, + int num_capture_channels) = 0; + + virtual ~EchoControlFactory() = default; +}; +} // namespace webrtc + +#endif // API_AUDIO_ECHO_CONTROL_H_ diff --git a/webrtc/api/audio_codecs/audio_decoder.cc b/webrtc/api/audio_codecs/audio_decoder.cc new file mode 100644 index 0000000..97cda27 --- /dev/null +++ b/webrtc/api/audio_codecs/audio_decoder.cc @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2012 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 "api/audio_codecs/audio_decoder.h" + +#include + +#include +#include + +#include "api/array_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/sanitizer.h" +#include "rtc_base/trace_event.h" + +namespace webrtc { + +namespace { + +class OldStyleEncodedFrame final : public AudioDecoder::EncodedAudioFrame { + public: + OldStyleEncodedFrame(AudioDecoder* decoder, rtc::Buffer&& payload) + : decoder_(decoder), payload_(std::move(payload)) {} + + size_t Duration() const override { + const int ret = decoder_->PacketDuration(payload_.data(), payload_.size()); + return ret < 0 ? 0 : static_cast(ret); + } + + absl::optional Decode( + rtc::ArrayView decoded) const override { + auto speech_type = AudioDecoder::kSpeech; + const int ret = decoder_->Decode( + payload_.data(), payload_.size(), decoder_->SampleRateHz(), + decoded.size() * sizeof(int16_t), decoded.data(), &speech_type); + return ret < 0 ? absl::nullopt + : absl::optional( + {static_cast(ret), speech_type}); + } + + private: + AudioDecoder* const decoder_; + const rtc::Buffer payload_; +}; + +} // namespace + +bool AudioDecoder::EncodedAudioFrame::IsDtxPacket() const { + return false; +} + +AudioDecoder::ParseResult::ParseResult() = default; +AudioDecoder::ParseResult::ParseResult(ParseResult&& b) = default; +AudioDecoder::ParseResult::ParseResult(uint32_t timestamp, + int priority, + std::unique_ptr frame) + : timestamp(timestamp), priority(priority), frame(std::move(frame)) { + RTC_DCHECK_GE(priority, 0); +} + +AudioDecoder::ParseResult::~ParseResult() = default; + +AudioDecoder::ParseResult& AudioDecoder::ParseResult::operator=( + ParseResult&& b) = default; + +std::vector AudioDecoder::ParsePayload( + rtc::Buffer&& payload, + uint32_t timestamp) { + std::vector results; + std::unique_ptr frame( + new OldStyleEncodedFrame(this, std::move(payload))); + results.emplace_back(timestamp, 0, std::move(frame)); + return results; +} + +int AudioDecoder::Decode(const uint8_t* encoded, + size_t encoded_len, + int sample_rate_hz, + size_t max_decoded_bytes, + int16_t* decoded, + SpeechType* speech_type) { + TRACE_EVENT0("webrtc", "AudioDecoder::Decode"); + rtc::MsanCheckInitialized(rtc::MakeArrayView(encoded, encoded_len)); + int duration = PacketDuration(encoded, encoded_len); + if (duration >= 0 && + duration * Channels() * sizeof(int16_t) > max_decoded_bytes) { + return -1; + } + return DecodeInternal(encoded, encoded_len, sample_rate_hz, decoded, + speech_type); +} + +int AudioDecoder::DecodeRedundant(const uint8_t* encoded, + size_t encoded_len, + int sample_rate_hz, + size_t max_decoded_bytes, + int16_t* decoded, + SpeechType* speech_type) { + TRACE_EVENT0("webrtc", "AudioDecoder::DecodeRedundant"); + rtc::MsanCheckInitialized(rtc::MakeArrayView(encoded, encoded_len)); + int duration = PacketDurationRedundant(encoded, encoded_len); + if (duration >= 0 && + duration * Channels() * sizeof(int16_t) > max_decoded_bytes) { + return -1; + } + return DecodeRedundantInternal(encoded, encoded_len, sample_rate_hz, decoded, + speech_type); +} + +int AudioDecoder::DecodeRedundantInternal(const uint8_t* encoded, + size_t encoded_len, + int sample_rate_hz, + int16_t* decoded, + SpeechType* speech_type) { + return DecodeInternal(encoded, encoded_len, sample_rate_hz, decoded, + speech_type); +} + +bool AudioDecoder::HasDecodePlc() const { + return false; +} + +size_t AudioDecoder::DecodePlc(size_t num_frames, int16_t* decoded) { + return 0; +} + +// TODO(bugs.webrtc.org/9676): Remove default implementation. +void AudioDecoder::GeneratePlc(size_t /*requested_samples_per_channel*/, + rtc::BufferT* /*concealment_audio*/) {} + +int AudioDecoder::ErrorCode() { + return 0; +} + +int AudioDecoder::PacketDuration(const uint8_t* encoded, + size_t encoded_len) const { + return kNotImplemented; +} + +int AudioDecoder::PacketDurationRedundant(const uint8_t* encoded, + size_t encoded_len) const { + return kNotImplemented; +} + +bool AudioDecoder::PacketHasFec(const uint8_t* encoded, + size_t encoded_len) const { + return false; +} + +AudioDecoder::SpeechType AudioDecoder::ConvertSpeechType(int16_t type) { + switch (type) { + case 0: // TODO(hlundin): Both iSAC and Opus return 0 for speech. + case 1: + return kSpeech; + case 2: + return kComfortNoise; + default: + assert(false); + return kSpeech; + } +} + +} // namespace webrtc diff --git a/webrtc/api/audio_codecs/audio_decoder.h b/webrtc/api/audio_codecs/audio_decoder.h new file mode 100644 index 0000000..557ffe2 --- /dev/null +++ b/webrtc/api/audio_codecs/audio_decoder.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef API_AUDIO_CODECS_AUDIO_DECODER_H_ +#define API_AUDIO_CODECS_AUDIO_DECODER_H_ + +#include +#include + +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "rtc_base/buffer.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +class AudioDecoder { + public: + enum SpeechType { + kSpeech = 1, + kComfortNoise = 2, + }; + + // Used by PacketDuration below. Save the value -1 for errors. + enum { kNotImplemented = -2 }; + + AudioDecoder() = default; + virtual ~AudioDecoder() = default; + + class EncodedAudioFrame { + public: + struct DecodeResult { + size_t num_decoded_samples; + SpeechType speech_type; + }; + + virtual ~EncodedAudioFrame() = default; + + // Returns the duration in samples-per-channel of this audio frame. + // If no duration can be ascertained, returns zero. + virtual size_t Duration() const = 0; + + // Returns true if this packet contains DTX. + virtual bool IsDtxPacket() const; + + // Decodes this frame of audio and writes the result in |decoded|. + // |decoded| must be large enough to store as many samples as indicated by a + // call to Duration() . On success, returns an absl::optional containing the + // total number of samples across all channels, as well as whether the + // decoder produced comfort noise or speech. On failure, returns an empty + // absl::optional. Decode may be called at most once per frame object. + virtual absl::optional Decode( + rtc::ArrayView decoded) const = 0; + }; + + struct ParseResult { + ParseResult(); + ParseResult(uint32_t timestamp, + int priority, + std::unique_ptr frame); + ParseResult(ParseResult&& b); + ~ParseResult(); + + ParseResult& operator=(ParseResult&& b); + + // The timestamp of the frame is in samples per channel. + uint32_t timestamp; + // The relative priority of the frame compared to other frames of the same + // payload and the same timeframe. A higher value means a lower priority. + // The highest priority is zero - negative values are not allowed. + int priority; + std::unique_ptr frame; + }; + + // Let the decoder parse this payload and prepare zero or more decodable + // frames. Each frame must be between 10 ms and 120 ms long. The caller must + // ensure that the AudioDecoder object outlives any frame objects returned by + // this call. The decoder is free to swap or move the data from the |payload| + // buffer. |timestamp| is the input timestamp, in samples, corresponding to + // the start of the payload. + virtual std::vector ParsePayload(rtc::Buffer&& payload, + uint32_t timestamp); + + // TODO(bugs.webrtc.org/10098): The Decode and DecodeRedundant methods are + // obsolete; callers should call ParsePayload instead. For now, subclasses + // must still implement DecodeInternal. + + // Decodes |encode_len| bytes from |encoded| and writes the result in + // |decoded|. The maximum bytes allowed to be written into |decoded| is + // |max_decoded_bytes|. Returns the total number of samples across all + // channels. If the decoder produced comfort noise, |speech_type| + // is set to kComfortNoise, otherwise it is kSpeech. The desired output + // sample rate is provided in |sample_rate_hz|, which must be valid for the + // codec at hand. + int Decode(const uint8_t* encoded, + size_t encoded_len, + int sample_rate_hz, + size_t max_decoded_bytes, + int16_t* decoded, + SpeechType* speech_type); + + // Same as Decode(), but interfaces to the decoders redundant decode function. + // The default implementation simply calls the regular Decode() method. + int DecodeRedundant(const uint8_t* encoded, + size_t encoded_len, + int sample_rate_hz, + size_t max_decoded_bytes, + int16_t* decoded, + SpeechType* speech_type); + + // Indicates if the decoder implements the DecodePlc method. + virtual bool HasDecodePlc() const; + + // Calls the packet-loss concealment of the decoder to update the state after + // one or several lost packets. The caller has to make sure that the + // memory allocated in |decoded| should accommodate |num_frames| frames. + virtual size_t DecodePlc(size_t num_frames, int16_t* decoded); + + // Asks the decoder to generate packet-loss concealment and append it to the + // end of |concealment_audio|. The concealment audio should be in + // channel-interleaved format, with as many channels as the last decoded + // packet produced. The implementation must produce at least + // requested_samples_per_channel, or nothing at all. This is a signal to the + // caller to conceal the loss with other means. If the implementation provides + // concealment samples, it is also responsible for "stitching" it together + // with the decoded audio on either side of the concealment. + // Note: The default implementation of GeneratePlc will be deleted soon. All + // implementations must provide their own, which can be a simple as a no-op. + // TODO(bugs.webrtc.org/9676): Remove default impementation. + virtual void GeneratePlc(size_t requested_samples_per_channel, + rtc::BufferT* concealment_audio); + + // Resets the decoder state (empty buffers etc.). + virtual void Reset() = 0; + + // Returns the last error code from the decoder. + virtual int ErrorCode(); + + // Returns the duration in samples-per-channel of the payload in |encoded| + // which is |encoded_len| bytes long. Returns kNotImplemented if no duration + // estimate is available, or -1 in case of an error. + virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const; + + // Returns the duration in samples-per-channel of the redandant payload in + // |encoded| which is |encoded_len| bytes long. Returns kNotImplemented if no + // duration estimate is available, or -1 in case of an error. + virtual int PacketDurationRedundant(const uint8_t* encoded, + size_t encoded_len) const; + + // Detects whether a packet has forward error correction. The packet is + // comprised of the samples in |encoded| which is |encoded_len| bytes long. + // Returns true if the packet has FEC and false otherwise. + virtual bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const; + + // Returns the actual sample rate of the decoder's output. This value may not + // change during the lifetime of the decoder. + virtual int SampleRateHz() const = 0; + + // The number of channels in the decoder's output. This value may not change + // during the lifetime of the decoder. + virtual size_t Channels() const = 0; + + protected: + static SpeechType ConvertSpeechType(int16_t type); + + virtual int DecodeInternal(const uint8_t* encoded, + size_t encoded_len, + int sample_rate_hz, + int16_t* decoded, + SpeechType* speech_type) = 0; + + virtual int DecodeRedundantInternal(const uint8_t* encoded, + size_t encoded_len, + int sample_rate_hz, + int16_t* decoded, + SpeechType* speech_type); + + private: + RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoder); +}; + +} // namespace webrtc +#endif // API_AUDIO_CODECS_AUDIO_DECODER_H_ diff --git a/webrtc/api/audio_codecs/audio_encoder.cc b/webrtc/api/audio_codecs/audio_encoder.cc new file mode 100644 index 0000000..cd4d200 --- /dev/null +++ b/webrtc/api/audio_codecs/audio_encoder.cc @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2014 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 "api/audio_codecs/audio_encoder.h" + +#include "rtc_base/checks.h" +#include "rtc_base/trace_event.h" + +namespace webrtc { + +ANAStats::ANAStats() = default; +ANAStats::~ANAStats() = default; +ANAStats::ANAStats(const ANAStats&) = default; + +AudioEncoder::EncodedInfo::EncodedInfo() = default; +AudioEncoder::EncodedInfo::EncodedInfo(const EncodedInfo&) = default; +AudioEncoder::EncodedInfo::EncodedInfo(EncodedInfo&&) = default; +AudioEncoder::EncodedInfo::~EncodedInfo() = default; +AudioEncoder::EncodedInfo& AudioEncoder::EncodedInfo::operator=( + const EncodedInfo&) = default; +AudioEncoder::EncodedInfo& AudioEncoder::EncodedInfo::operator=(EncodedInfo&&) = + default; + +int AudioEncoder::RtpTimestampRateHz() const { + return SampleRateHz(); +} + +AudioEncoder::EncodedInfo AudioEncoder::Encode( + uint32_t rtp_timestamp, + rtc::ArrayView audio, + rtc::Buffer* encoded) { + TRACE_EVENT0("webrtc", "AudioEncoder::Encode"); + RTC_CHECK_EQ(audio.size(), + static_cast(NumChannels() * SampleRateHz() / 100)); + + const size_t old_size = encoded->size(); + EncodedInfo info = EncodeImpl(rtp_timestamp, audio, encoded); + RTC_CHECK_EQ(encoded->size() - old_size, info.encoded_bytes); + return info; +} + +bool AudioEncoder::SetFec(bool enable) { + return !enable; +} + +bool AudioEncoder::SetDtx(bool enable) { + return !enable; +} + +bool AudioEncoder::GetDtx() const { + return false; +} + +bool AudioEncoder::SetApplication(Application application) { + return false; +} + +void AudioEncoder::SetMaxPlaybackRate(int frequency_hz) {} + +void AudioEncoder::SetTargetBitrate(int target_bps) {} + +rtc::ArrayView> +AudioEncoder::ReclaimContainedEncoders() { + return nullptr; +} + +bool AudioEncoder::EnableAudioNetworkAdaptor(const std::string& config_string, + RtcEventLog* event_log) { + return false; +} + +void AudioEncoder::DisableAudioNetworkAdaptor() {} + +void AudioEncoder::OnReceivedUplinkPacketLossFraction( + float uplink_packet_loss_fraction) {} + +void AudioEncoder::OnReceivedUplinkRecoverablePacketLossFraction( + float uplink_recoverable_packet_loss_fraction) { + RTC_NOTREACHED(); +} + +void AudioEncoder::OnReceivedTargetAudioBitrate(int target_audio_bitrate_bps) { + OnReceivedUplinkBandwidth(target_audio_bitrate_bps, absl::nullopt); +} + +void AudioEncoder::OnReceivedUplinkBandwidth( + int target_audio_bitrate_bps, + absl::optional bwe_period_ms) {} + +void AudioEncoder::OnReceivedUplinkAllocation(BitrateAllocationUpdate update) { + OnReceivedUplinkBandwidth(update.target_bitrate.bps(), + update.bwe_period.ms()); +} + +void AudioEncoder::OnReceivedRtt(int rtt_ms) {} + +void AudioEncoder::OnReceivedOverhead(size_t overhead_bytes_per_packet) {} + +void AudioEncoder::SetReceiverFrameLengthRange(int min_frame_length_ms, + int max_frame_length_ms) {} + +ANAStats AudioEncoder::GetANAStats() const { + return ANAStats(); +} + +} // namespace webrtc diff --git a/webrtc/api/audio_codecs/audio_encoder.h b/webrtc/api/audio_codecs/audio_encoder.h new file mode 100644 index 0000000..fd2d948 --- /dev/null +++ b/webrtc/api/audio_codecs/audio_encoder.h @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2014 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. + */ + +#ifndef API_AUDIO_CODECS_AUDIO_ENCODER_H_ +#define API_AUDIO_CODECS_AUDIO_ENCODER_H_ + +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/call/bitrate_allocation.h" +#include "api/units/time_delta.h" +#include "rtc_base/buffer.h" +#include "rtc_base/deprecation.h" + +namespace webrtc { + +class RtcEventLog; + +// Statistics related to Audio Network Adaptation. +struct ANAStats { + ANAStats(); + ANAStats(const ANAStats&); + ~ANAStats(); + // Number of actions taken by the ANA bitrate controller since the start of + // the call. If this value is not set, it indicates that the bitrate + // controller is disabled. + absl::optional bitrate_action_counter; + // Number of actions taken by the ANA channel controller since the start of + // the call. If this value is not set, it indicates that the channel + // controller is disabled. + absl::optional channel_action_counter; + // Number of actions taken by the ANA DTX controller since the start of the + // call. If this value is not set, it indicates that the DTX controller is + // disabled. + absl::optional dtx_action_counter; + // Number of actions taken by the ANA FEC controller since the start of the + // call. If this value is not set, it indicates that the FEC controller is + // disabled. + absl::optional fec_action_counter; + // Number of times the ANA frame length controller decided to increase the + // frame length since the start of the call. If this value is not set, it + // indicates that the frame length controller is disabled. + absl::optional frame_length_increase_counter; + // Number of times the ANA frame length controller decided to decrease the + // frame length since the start of the call. If this value is not set, it + // indicates that the frame length controller is disabled. + absl::optional frame_length_decrease_counter; + // The uplink packet loss fractions as set by the ANA FEC controller. If this + // value is not set, it indicates that the ANA FEC controller is not active. + absl::optional uplink_packet_loss_fraction; +}; + +// This is the interface class for encoders in AudioCoding module. Each codec +// type must have an implementation of this class. +class AudioEncoder { + public: + // Used for UMA logging of codec usage. The same codecs, with the + // same values, must be listed in + // src/tools/metrics/histograms/histograms.xml in chromium to log + // correct values. + enum class CodecType { + kOther = 0, // Codec not specified, and/or not listed in this enum + kOpus = 1, + kIsac = 2, + kPcmA = 3, + kPcmU = 4, + kG722 = 5, + kIlbc = 6, + + // Number of histogram bins in the UMA logging of codec types. The + // total number of different codecs that are logged cannot exceed this + // number. + kMaxLoggedAudioCodecTypes + }; + + struct EncodedInfoLeaf { + size_t encoded_bytes = 0; + uint32_t encoded_timestamp = 0; + int payload_type = 0; + bool send_even_if_empty = false; + bool speech = true; + CodecType encoder_type = CodecType::kOther; + }; + + // This is the main struct for auxiliary encoding information. Each encoded + // packet should be accompanied by one EncodedInfo struct, containing the + // total number of |encoded_bytes|, the |encoded_timestamp| and the + // |payload_type|. If the packet contains redundant encodings, the |redundant| + // vector will be populated with EncodedInfoLeaf structs. Each struct in the + // vector represents one encoding; the order of structs in the vector is the + // same as the order in which the actual payloads are written to the byte + // stream. When EncoderInfoLeaf structs are present in the vector, the main + // struct's |encoded_bytes| will be the sum of all the |encoded_bytes| in the + // vector. + struct EncodedInfo : public EncodedInfoLeaf { + EncodedInfo(); + EncodedInfo(const EncodedInfo&); + EncodedInfo(EncodedInfo&&); + ~EncodedInfo(); + EncodedInfo& operator=(const EncodedInfo&); + EncodedInfo& operator=(EncodedInfo&&); + + std::vector redundant; + }; + + virtual ~AudioEncoder() = default; + + // Returns the input sample rate in Hz and the number of input channels. + // These are constants set at instantiation time. + virtual int SampleRateHz() const = 0; + virtual size_t NumChannels() const = 0; + + // Returns the rate at which the RTP timestamps are updated. The default + // implementation returns SampleRateHz(). + virtual int RtpTimestampRateHz() const; + + // Returns the number of 10 ms frames the encoder will put in the next + // packet. This value may only change when Encode() outputs a packet; i.e., + // the encoder may vary the number of 10 ms frames from packet to packet, but + // it must decide the length of the next packet no later than when outputting + // the preceding packet. + virtual size_t Num10MsFramesInNextPacket() const = 0; + + // Returns the maximum value that can be returned by + // Num10MsFramesInNextPacket(). + virtual size_t Max10MsFramesInAPacket() const = 0; + + // Returns the current target bitrate in bits/s. The value -1 means that the + // codec adapts the target automatically, and a current target cannot be + // provided. + virtual int GetTargetBitrate() const = 0; + + // Accepts one 10 ms block of input audio (i.e., SampleRateHz() / 100 * + // NumChannels() samples). Multi-channel audio must be sample-interleaved. + // The encoder appends zero or more bytes of output to |encoded| and returns + // additional encoding information. Encode() checks some preconditions, calls + // EncodeImpl() which does the actual work, and then checks some + // postconditions. + EncodedInfo Encode(uint32_t rtp_timestamp, + rtc::ArrayView audio, + rtc::Buffer* encoded); + + // Resets the encoder to its starting state, discarding any input that has + // been fed to the encoder but not yet emitted in a packet. + virtual void Reset() = 0; + + // Enables or disables codec-internal FEC (forward error correction). Returns + // true if the codec was able to comply. The default implementation returns + // true when asked to disable FEC and false when asked to enable it (meaning + // that FEC isn't supported). + virtual bool SetFec(bool enable); + + // Enables or disables codec-internal VAD/DTX. Returns true if the codec was + // able to comply. The default implementation returns true when asked to + // disable DTX and false when asked to enable it (meaning that DTX isn't + // supported). + virtual bool SetDtx(bool enable); + + // Returns the status of codec-internal DTX. The default implementation always + // returns false. + virtual bool GetDtx() const; + + // Sets the application mode. Returns true if the codec was able to comply. + // The default implementation just returns false. + enum class Application { kSpeech, kAudio }; + virtual bool SetApplication(Application application); + + // Tells the encoder about the highest sample rate the decoder is expected to + // use when decoding the bitstream. The encoder would typically use this + // information to adjust the quality of the encoding. The default + // implementation does nothing. + virtual void SetMaxPlaybackRate(int frequency_hz); + + // This is to be deprecated. Please use |OnReceivedTargetAudioBitrate| + // instead. + // Tells the encoder what average bitrate we'd like it to produce. The + // encoder is free to adjust or disregard the given bitrate (the default + // implementation does the latter). + RTC_DEPRECATED virtual void SetTargetBitrate(int target_bps); + + // Causes this encoder to let go of any other encoders it contains, and + // returns a pointer to an array where they are stored (which is required to + // live as long as this encoder). Unless the returned array is empty, you may + // not call any methods on this encoder afterwards, except for the + // destructor. The default implementation just returns an empty array. + // NOTE: This method is subject to change. Do not call or override it. + virtual rtc::ArrayView> + ReclaimContainedEncoders(); + + // Enables audio network adaptor. Returns true if successful. + virtual bool EnableAudioNetworkAdaptor(const std::string& config_string, + RtcEventLog* event_log); + + // Disables audio network adaptor. + virtual void DisableAudioNetworkAdaptor(); + + // Provides uplink packet loss fraction to this encoder to allow it to adapt. + // |uplink_packet_loss_fraction| is in the range [0.0, 1.0]. + virtual void OnReceivedUplinkPacketLossFraction( + float uplink_packet_loss_fraction); + + RTC_DEPRECATED virtual void OnReceivedUplinkRecoverablePacketLossFraction( + float uplink_recoverable_packet_loss_fraction); + + // Provides target audio bitrate to this encoder to allow it to adapt. + virtual void OnReceivedTargetAudioBitrate(int target_bps); + + // Provides target audio bitrate and corresponding probing interval of + // the bandwidth estimator to this encoder to allow it to adapt. + virtual void OnReceivedUplinkBandwidth(int target_audio_bitrate_bps, + absl::optional bwe_period_ms); + + // Provides target audio bitrate and corresponding probing interval of + // the bandwidth estimator to this encoder to allow it to adapt. + virtual void OnReceivedUplinkAllocation(BitrateAllocationUpdate update); + + // Provides RTT to this encoder to allow it to adapt. + virtual void OnReceivedRtt(int rtt_ms); + + // Provides overhead to this encoder to adapt. The overhead is the number of + // bytes that will be added to each packet the encoder generates. + virtual void OnReceivedOverhead(size_t overhead_bytes_per_packet); + + // To allow encoder to adapt its frame length, it must be provided the frame + // length range that receivers can accept. + virtual void SetReceiverFrameLengthRange(int min_frame_length_ms, + int max_frame_length_ms); + + // Get statistics related to audio network adaptation. + virtual ANAStats GetANAStats() const; + + // The range of frame lengths that are supported or nullopt if there's no sch + // information. This is used to calculated the full bitrate range, including + // overhead. + virtual absl::optional> GetFrameLengthRange() + const = 0; + + protected: + // Subclasses implement this to perform the actual encoding. Called by + // Encode(). + virtual EncodedInfo EncodeImpl(uint32_t rtp_timestamp, + rtc::ArrayView audio, + rtc::Buffer* encoded) = 0; +}; +} // namespace webrtc +#endif // API_AUDIO_CODECS_AUDIO_ENCODER_H_ diff --git a/webrtc/api/call/bitrate_allocation.h b/webrtc/api/call/bitrate_allocation.h new file mode 100644 index 0000000..13c7f74 --- /dev/null +++ b/webrtc/api/call/bitrate_allocation.h @@ -0,0 +1,45 @@ +/* + * Copyright 2018 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. + */ +#ifndef API_CALL_BITRATE_ALLOCATION_H_ +#define API_CALL_BITRATE_ALLOCATION_H_ + +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" + +namespace webrtc { + +// BitrateAllocationUpdate provides information to allocated streams about their +// bitrate allocation. It originates from the BitrateAllocater class and is +// propagated from there. +struct BitrateAllocationUpdate { + // The allocated target bitrate. Media streams should produce this amount of + // data. (Note that this may include packet overhead depending on + // configuration.) + DataRate target_bitrate = DataRate::Zero(); + // The allocated part of the estimated link capacity. This is more stable than + // the target as it is based on the underlying link capacity estimate. This + // should be used to change encoder configuration when the cost of change is + // high. + DataRate stable_target_bitrate = DataRate::Zero(); + // Predicted packet loss ratio. + double packet_loss_ratio = 0; + // Predicted round trip time. + TimeDelta round_trip_time = TimeDelta::PlusInfinity(); + // |bwe_period| is deprecated, use |stable_target_bitrate| allocation instead. + TimeDelta bwe_period = TimeDelta::PlusInfinity(); + // Congestion window pushback bitrate reduction fraction. Used in + // VideoStreamEncoder to reduce the bitrate by the given fraction + // by dropping frames. + double cwnd_reduce_ratio = 0; +}; + +} // namespace webrtc + +#endif // API_CALL_BITRATE_ALLOCATION_H_ diff --git a/webrtc/api/function_view.h b/webrtc/api/function_view.h new file mode 100644 index 0000000..5ae1bd6 --- /dev/null +++ b/webrtc/api/function_view.h @@ -0,0 +1,130 @@ +/* + * Copyright 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. + */ + +#ifndef API_FUNCTION_VIEW_H_ +#define API_FUNCTION_VIEW_H_ + +#include +#include + +#include "rtc_base/checks.h" + +// Just like std::function, FunctionView will wrap any callable and hide its +// actual type, exposing only its signature. But unlike std::function, +// FunctionView doesn't own its callable---it just points to it. Thus, it's a +// good choice mainly as a function argument when the callable argument will +// not be called again once the function has returned. +// +// Its constructors are implicit, so that callers won't have to convert lambdas +// and other callables to FunctionView explicitly. This is +// safe because FunctionView is only a reference to the real callable. +// +// Example use: +// +// void SomeFunction(rtc::FunctionView index_transform); +// ... +// SomeFunction([](int i) { return 2 * i + 1; }); +// +// Note: FunctionView is tiny (essentially just two pointers) and trivially +// copyable, so it's probably cheaper to pass it by value than by const +// reference. + +namespace rtc { + +template +class FunctionView; // Undefined. + +template +class FunctionView final { + public: + // Constructor for lambdas and other callables; it accepts every type of + // argument except those noted in its enable_if call. + template < + typename F, + typename std::enable_if< + // Not for function pointers; we have another constructor for that + // below. + !std::is_function::type>::type>::value && + + // Not for nullptr; we have another constructor for that below. + !std::is_same::type>::value && + + // Not for FunctionView objects; we have another constructor for that + // (the implicitly declared copy constructor). + !std::is_same::type>::type>::value>::type* = nullptr> + FunctionView(F&& f) + : call_(CallVoidPtr::type>) { + f_.void_ptr = &f; + } + + // Constructor that accepts function pointers. If the argument is null, the + // result is an empty FunctionView. + template < + typename F, + typename std::enable_if::type>::type>::value>::type* = + nullptr> + FunctionView(F&& f) + : call_(f ? CallFunPtr::type> : nullptr) { + f_.fun_ptr = reinterpret_cast(f); + } + + // Constructor that accepts nullptr. It creates an empty FunctionView. + template ::type>::value>::type* = nullptr> + FunctionView(F&& f) : call_(nullptr) {} + + // Default constructor. Creates an empty FunctionView. + FunctionView() : call_(nullptr) {} + + RetT operator()(ArgT... args) const { + RTC_DCHECK(call_); + return call_(f_, std::forward(args)...); + } + + // Returns true if we have a function, false if we don't (i.e., we're null). + explicit operator bool() const { return !!call_; } + + private: + union VoidUnion { + void* void_ptr; + void (*fun_ptr)(); + }; + + template + static RetT CallVoidPtr(VoidUnion vu, ArgT... args) { + return (*static_cast(vu.void_ptr))(std::forward(args)...); + } + template + static RetT CallFunPtr(VoidUnion vu, ArgT... args) { + return (reinterpret_cast::type>(vu.fun_ptr))( + std::forward(args)...); + } + + // A pointer to the callable thing, with type information erased. It's a + // union because we have to use separate types depending on if the callable + // thing is a function pointer or something else. + VoidUnion f_; + + // Pointer to a dispatch function that knows the type of the callable thing + // that's stored in f_, and how to call it. A FunctionView object is empty + // (null) iff call_ is null. + RetT (*call_)(VoidUnion, ArgT...); +}; + +} // namespace rtc + +#endif // API_FUNCTION_VIEW_H_ diff --git a/webrtc/api/meson.build b/webrtc/api/meson.build new file mode 100644 index 0000000..1d26709 --- /dev/null +++ b/webrtc/api/meson.build @@ -0,0 +1,46 @@ +api_sources = [ + 'audio/audio_frame.cc', + 'audio/channel_layout.cc', + 'audio/echo_canceller3_config.cc', + 'audio_codecs/audio_decoder.cc', + 'audio_codecs/audio_encoder.cc', + 'rtp_headers.cc', + 'rtp_packet_info.cc', + 'task_queue/task_queue_base.cc', + 'units/data_rate.cc', + 'units/data_size.cc', + 'units/frequency.cc', + 'units/time_delta.cc', + 'units/timestamp.cc', + 'video/color_space.cc', + 'video/hdr_metadata.cc', + 'video/video_content_type.cc', + 'video/video_timing.cc', +] + +api_headers = [ + ['', 'array_view.h'], + ['', 'scoped_refptr.h'], + ['audio', 'echo_canceller3_config.h'], + ['audio', 'echo_control.h'], +] + +foreach h : api_headers + install_headers( + join_paths(h[0], h[1]), + subdir: join_paths('webrtc_audio_processing', 'api', h[0]) + ) +endforeach + + +libapi = static_library('libapi', + api_sources, + dependencies: common_deps, + include_directories: webrtc_inc, + cpp_args : common_cxxflags +) + +api_dep = declare_dependency( + link_with: libapi +) + diff --git a/webrtc/api/ref_counted_base.h b/webrtc/api/ref_counted_base.h new file mode 100644 index 0000000..a1761db --- /dev/null +++ b/webrtc/api/ref_counted_base.h @@ -0,0 +1,43 @@ +/* + * Copyright 2017 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. + */ +#ifndef API_REF_COUNTED_BASE_H_ +#define API_REF_COUNTED_BASE_H_ + +#include "rtc_base/constructor_magic.h" +#include "rtc_base/ref_count.h" +#include "rtc_base/ref_counter.h" + +namespace rtc { + +class RefCountedBase { + public: + RefCountedBase() = default; + + void AddRef() const { ref_count_.IncRef(); } + RefCountReleaseStatus Release() const { + const auto status = ref_count_.DecRef(); + if (status == RefCountReleaseStatus::kDroppedLastRef) { + delete this; + } + return status; + } + + protected: + virtual ~RefCountedBase() = default; + + private: + mutable webrtc::webrtc_impl::RefCounter ref_count_{0}; + + RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedBase); +}; + +} // namespace rtc + +#endif // API_REF_COUNTED_BASE_H_ diff --git a/webrtc/common_types.cc b/webrtc/api/rtp_headers.cc similarity index 62% rename from webrtc/common_types.cc rename to webrtc/api/rtp_headers.cc index 7b99f3c..e0ad9eb 100644 --- a/webrtc/common_types.cc +++ b/webrtc/api/rtp_headers.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * Copyright (c) 2017 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 @@ -8,18 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_types.h" - -#include +#include "api/rtp_headers.h" namespace webrtc { -int InStream::Rewind() { return -1; } - -int OutStream::Rewind() { return -1; } - -StreamDataCounters::StreamDataCounters() : first_packet_time_ms(-1) {} - RTPHeaderExtension::RTPHeaderExtension() : hasTransmissionTimeOffset(false), transmissionTimeOffset(0), @@ -31,8 +23,16 @@ RTPHeaderExtension::RTPHeaderExtension() voiceActivity(false), audioLevel(0), hasVideoRotation(false), - videoRotation(0) { -} + videoRotation(kVideoRotation_0), + hasVideoContentType(false), + videoContentType(VideoContentType::UNSPECIFIED), + has_video_timing(false) {} + +RTPHeaderExtension::RTPHeaderExtension(const RTPHeaderExtension& other) = + default; + +RTPHeaderExtension& RTPHeaderExtension::operator=( + const RTPHeaderExtension& other) = default; RTPHeader::RTPHeader() : markerBit(false), @@ -41,11 +41,14 @@ RTPHeader::RTPHeader() timestamp(0), ssrc(0), numCSRCs(0), + arrOfCSRCs(), paddingLength(0), headerLength(0), payload_type_frequency(0), - extension() { - memset(&arrOfCSRCs, 0, sizeof(arrOfCSRCs)); -} + extension() {} + +RTPHeader::RTPHeader(const RTPHeader& other) = default; + +RTPHeader& RTPHeader::operator=(const RTPHeader& other) = default; } // namespace webrtc diff --git a/webrtc/api/rtp_headers.h b/webrtc/api/rtp_headers.h new file mode 100644 index 0000000..b9a97c8 --- /dev/null +++ b/webrtc/api/rtp_headers.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef API_RTP_HEADERS_H_ +#define API_RTP_HEADERS_H_ + +#include +#include + +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/units/timestamp.h" +#include "api/video/color_space.h" +#include "api/video/video_content_type.h" +#include "api/video/video_rotation.h" +#include "api/video/video_timing.h" + +namespace webrtc { + +struct FeedbackRequest { + // Determines whether the recv delta as specified in + // https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01 + // should be included. + bool include_timestamps; + // Include feedback of received packets in the range [sequence_number - + // sequence_count + 1, sequence_number]. That is, no feedback will be sent if + // sequence_count is zero. + int sequence_count; +}; + +// The Absolute Capture Time extension is used to stamp RTP packets with a NTP +// timestamp showing when the first audio or video frame in a packet was +// originally captured. The intent of this extension is to provide a way to +// accomplish audio-to-video synchronization when RTCP-terminating intermediate +// systems (e.g. mixers) are involved. See: +// http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time +struct AbsoluteCaptureTime { + // Absolute capture timestamp is the NTP timestamp of when the first frame in + // a packet was originally captured. This timestamp MUST be based on the same + // clock as the clock used to generate NTP timestamps for RTCP sender reports + // on the capture system. + // + // It’s not always possible to do an NTP clock readout at the exact moment of + // when a media frame is captured. A capture system MAY postpone the readout + // until a more convenient time. A capture system SHOULD have known delays + // (e.g. from hardware buffers) subtracted from the readout to make the final + // timestamp as close to the actual capture time as possible. + // + // This field is encoded as a 64-bit unsigned fixed-point number with the high + // 32 bits for the timestamp in seconds and low 32 bits for the fractional + // part. This is also known as the UQ32.32 format and is what the RTP + // specification defines as the canonical format to represent NTP timestamps. + uint64_t absolute_capture_timestamp; + + // Estimated capture clock offset is the sender’s estimate of the offset + // between its own NTP clock and the capture system’s NTP clock. The sender is + // here defined as the system that owns the NTP clock used to generate the NTP + // timestamps for the RTCP sender reports on this stream. The sender system is + // typically either the capture system or a mixer. + // + // This field is encoded as a 64-bit two’s complement signed fixed-point + // number with the high 32 bits for the seconds and low 32 bits for the + // fractional part. It’s intended to make it easy for a receiver, that knows + // how to estimate the sender system’s NTP clock, to also estimate the capture + // system’s NTP clock: + // + // Capture NTP Clock = Sender NTP Clock + Capture Clock Offset + absl::optional estimated_capture_clock_offset; +}; + +inline bool operator==(const AbsoluteCaptureTime& lhs, + const AbsoluteCaptureTime& rhs) { + return (lhs.absolute_capture_timestamp == rhs.absolute_capture_timestamp) && + (lhs.estimated_capture_clock_offset == + rhs.estimated_capture_clock_offset); +} + +inline bool operator!=(const AbsoluteCaptureTime& lhs, + const AbsoluteCaptureTime& rhs) { + return !(lhs == rhs); +} + +struct RTPHeaderExtension { + RTPHeaderExtension(); + RTPHeaderExtension(const RTPHeaderExtension& other); + RTPHeaderExtension& operator=(const RTPHeaderExtension& other); + + static constexpr int kAbsSendTimeFraction = 18; + + Timestamp GetAbsoluteSendTimestamp() const { + RTC_DCHECK(hasAbsoluteSendTime); + RTC_DCHECK(absoluteSendTime < (1ul << 24)); + return Timestamp::Micros((absoluteSendTime * 1000000ll) / + (1 << kAbsSendTimeFraction)); + } + + TimeDelta GetAbsoluteSendTimeDelta(uint32_t previous_sendtime) const { + RTC_DCHECK(hasAbsoluteSendTime); + RTC_DCHECK(absoluteSendTime < (1ul << 24)); + RTC_DCHECK(previous_sendtime < (1ul << 24)); + int32_t delta = + static_cast((absoluteSendTime - previous_sendtime) << 8) >> 8; + return TimeDelta::Micros((delta * 1000000ll) / (1 << kAbsSendTimeFraction)); + } + + bool hasTransmissionTimeOffset; + int32_t transmissionTimeOffset; + bool hasAbsoluteSendTime; + uint32_t absoluteSendTime; + absl::optional absolute_capture_time; + bool hasTransportSequenceNumber; + uint16_t transportSequenceNumber; + absl::optional feedback_request; + + // Audio Level includes both level in dBov and voiced/unvoiced bit. See: + // https://tools.ietf.org/html/rfc6464#section-3 + bool hasAudioLevel; + bool voiceActivity; + uint8_t audioLevel; + + // For Coordination of Video Orientation. See + // http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/12.07.00_60/ + // ts_126114v120700p.pdf + bool hasVideoRotation; + VideoRotation videoRotation; + + // TODO(ilnik): Refactor this and one above to be absl::optional() and remove + // a corresponding bool flag. + bool hasVideoContentType; + VideoContentType videoContentType; + + bool has_video_timing; + VideoSendTiming video_timing; + + VideoPlayoutDelay playout_delay; + + // For identification of a stream when ssrc is not signaled. See + // https://tools.ietf.org/html/draft-ietf-avtext-rid-09 + // TODO(danilchap): Update url from draft to release version. + std::string stream_id; + std::string repaired_stream_id; + + // For identifying the media section used to interpret this RTP packet. See + // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-38 + std::string mid; + + absl::optional color_space; +}; + +enum { kRtpCsrcSize = 15 }; // RFC 3550 page 13 + +struct RTPHeader { + RTPHeader(); + RTPHeader(const RTPHeader& other); + RTPHeader& operator=(const RTPHeader& other); + + bool markerBit; + uint8_t payloadType; + uint16_t sequenceNumber; + uint32_t timestamp; + uint32_t ssrc; + uint8_t numCSRCs; + uint32_t arrOfCSRCs[kRtpCsrcSize]; + size_t paddingLength; + size_t headerLength; + int payload_type_frequency; + RTPHeaderExtension extension; +}; + +// RTCP mode to use. Compound mode is described by RFC 4585 and reduced-size +// RTCP mode is described by RFC 5506. +enum class RtcpMode { kOff, kCompound, kReducedSize }; + +enum NetworkState { + kNetworkUp, + kNetworkDown, +}; + +} // namespace webrtc + +#endif // API_RTP_HEADERS_H_ diff --git a/webrtc/api/rtp_packet_info.cc b/webrtc/api/rtp_packet_info.cc new file mode 100644 index 0000000..a9ebd9d --- /dev/null +++ b/webrtc/api/rtp_packet_info.cc @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019 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 "api/rtp_packet_info.h" + +#include +#include + +namespace webrtc { + +RtpPacketInfo::RtpPacketInfo() + : ssrc_(0), rtp_timestamp_(0), receive_time_ms_(-1) {} + +RtpPacketInfo::RtpPacketInfo( + uint32_t ssrc, + std::vector csrcs, + uint32_t rtp_timestamp, + absl::optional audio_level, + absl::optional absolute_capture_time, + int64_t receive_time_ms) + : ssrc_(ssrc), + csrcs_(std::move(csrcs)), + rtp_timestamp_(rtp_timestamp), + audio_level_(audio_level), + absolute_capture_time_(absolute_capture_time), + receive_time_ms_(receive_time_ms) {} + +RtpPacketInfo::RtpPacketInfo(const RTPHeader& rtp_header, + int64_t receive_time_ms) + : ssrc_(rtp_header.ssrc), + rtp_timestamp_(rtp_header.timestamp), + receive_time_ms_(receive_time_ms) { + const auto& extension = rtp_header.extension; + const auto csrcs_count = std::min(rtp_header.numCSRCs, kRtpCsrcSize); + + csrcs_.assign(&rtp_header.arrOfCSRCs[0], &rtp_header.arrOfCSRCs[csrcs_count]); + + if (extension.hasAudioLevel) { + audio_level_ = extension.audioLevel; + } + + absolute_capture_time_ = extension.absolute_capture_time; +} + +bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs) { + return (lhs.ssrc() == rhs.ssrc()) && (lhs.csrcs() == rhs.csrcs()) && + (lhs.rtp_timestamp() == rhs.rtp_timestamp()) && + (lhs.audio_level() == rhs.audio_level()) && + (lhs.absolute_capture_time() == rhs.absolute_capture_time()) && + (lhs.receive_time_ms() == rhs.receive_time_ms()); +} + +} // namespace webrtc diff --git a/webrtc/api/rtp_packet_info.h b/webrtc/api/rtp_packet_info.h new file mode 100644 index 0000000..639ba32 --- /dev/null +++ b/webrtc/api/rtp_packet_info.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef API_RTP_PACKET_INFO_H_ +#define API_RTP_PACKET_INFO_H_ + +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/rtp_headers.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// +// Structure to hold information about a received |RtpPacket|. It is primarily +// used to carry per-packet information from when a packet is received until +// the information is passed to |SourceTracker|. +// +class RTC_EXPORT RtpPacketInfo { + public: + RtpPacketInfo(); + + RtpPacketInfo(uint32_t ssrc, + std::vector csrcs, + uint32_t rtp_timestamp, + absl::optional audio_level, + absl::optional absolute_capture_time, + int64_t receive_time_ms); + + RtpPacketInfo(const RTPHeader& rtp_header, int64_t receive_time_ms); + + RtpPacketInfo(const RtpPacketInfo& other) = default; + RtpPacketInfo(RtpPacketInfo&& other) = default; + RtpPacketInfo& operator=(const RtpPacketInfo& other) = default; + RtpPacketInfo& operator=(RtpPacketInfo&& other) = default; + + uint32_t ssrc() const { return ssrc_; } + void set_ssrc(uint32_t value) { ssrc_ = value; } + + const std::vector& csrcs() const { return csrcs_; } + void set_csrcs(std::vector value) { csrcs_ = std::move(value); } + + uint32_t rtp_timestamp() const { return rtp_timestamp_; } + void set_rtp_timestamp(uint32_t value) { rtp_timestamp_ = value; } + + absl::optional audio_level() const { return audio_level_; } + void set_audio_level(absl::optional value) { audio_level_ = value; } + + const absl::optional& absolute_capture_time() const { + return absolute_capture_time_; + } + void set_absolute_capture_time( + const absl::optional& value) { + absolute_capture_time_ = value; + } + + int64_t receive_time_ms() const { return receive_time_ms_; } + void set_receive_time_ms(int64_t value) { receive_time_ms_ = value; } + + private: + // Fields from the RTP header: + // https://tools.ietf.org/html/rfc3550#section-5.1 + uint32_t ssrc_; + std::vector csrcs_; + uint32_t rtp_timestamp_; + + // Fields from the Audio Level header extension: + // https://tools.ietf.org/html/rfc6464#section-3 + absl::optional audio_level_; + + // Fields from the Absolute Capture Time header extension: + // http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time + absl::optional absolute_capture_time_; + + // Local |webrtc::Clock|-based timestamp of when the packet was received. + int64_t receive_time_ms_; +}; + +bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs); + +inline bool operator!=(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs) { + return !(lhs == rhs); +} + +} // namespace webrtc + +#endif // API_RTP_PACKET_INFO_H_ diff --git a/webrtc/api/rtp_packet_infos.h b/webrtc/api/rtp_packet_infos.h new file mode 100644 index 0000000..d636464 --- /dev/null +++ b/webrtc/api/rtp_packet_infos.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef API_RTP_PACKET_INFOS_H_ +#define API_RTP_PACKET_INFOS_H_ + +#include +#include +#include + +#include "api/ref_counted_base.h" +#include "api/rtp_packet_info.h" +#include "api/scoped_refptr.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Semi-immutable structure to hold information about packets used to assemble +// an audio or video frame. Uses internal reference counting to make it very +// cheap to copy. +// +// We should ideally just use |std::vector| and have it +// |std::move()|-ed as the per-packet information is transferred from one object +// to another. But moving the info, instead of copying it, is not easily done +// for the current video code. +class RTC_EXPORT RtpPacketInfos { + public: + using vector_type = std::vector; + + using value_type = vector_type::value_type; + using size_type = vector_type::size_type; + using difference_type = vector_type::difference_type; + using const_reference = vector_type::const_reference; + using const_pointer = vector_type::const_pointer; + using const_iterator = vector_type::const_iterator; + using const_reverse_iterator = vector_type::const_reverse_iterator; + + using reference = const_reference; + using pointer = const_pointer; + using iterator = const_iterator; + using reverse_iterator = const_reverse_iterator; + + RtpPacketInfos() {} + explicit RtpPacketInfos(const vector_type& entries) + : data_(Data::Create(entries)) {} + + explicit RtpPacketInfos(vector_type&& entries) + : data_(Data::Create(std::move(entries))) {} + + RtpPacketInfos(const RtpPacketInfos& other) = default; + RtpPacketInfos(RtpPacketInfos&& other) = default; + RtpPacketInfos& operator=(const RtpPacketInfos& other) = default; + RtpPacketInfos& operator=(RtpPacketInfos&& other) = default; + + const_reference operator[](size_type pos) const { return entries()[pos]; } + + const_reference at(size_type pos) const { return entries().at(pos); } + const_reference front() const { return entries().front(); } + const_reference back() const { return entries().back(); } + + const_iterator begin() const { return entries().begin(); } + const_iterator end() const { return entries().end(); } + const_reverse_iterator rbegin() const { return entries().rbegin(); } + const_reverse_iterator rend() const { return entries().rend(); } + + const_iterator cbegin() const { return entries().cbegin(); } + const_iterator cend() const { return entries().cend(); } + const_reverse_iterator crbegin() const { return entries().crbegin(); } + const_reverse_iterator crend() const { return entries().crend(); } + + bool empty() const { return entries().empty(); } + size_type size() const { return entries().size(); } + + private: + class Data : public rtc::RefCountedBase { + public: + static rtc::scoped_refptr Create(const vector_type& entries) { + // Performance optimization for the empty case. + if (entries.empty()) { + return nullptr; + } + + return new Data(entries); + } + + static rtc::scoped_refptr Create(vector_type&& entries) { + // Performance optimization for the empty case. + if (entries.empty()) { + return nullptr; + } + + return new Data(std::move(entries)); + } + + const vector_type& entries() const { return entries_; } + + private: + explicit Data(const vector_type& entries) : entries_(entries) {} + explicit Data(vector_type&& entries) : entries_(std::move(entries)) {} + ~Data() override {} + + const vector_type entries_; + }; + + static const vector_type& empty_entries() { + static const vector_type& value = *new vector_type(); + return value; + } + + const vector_type& entries() const { + if (data_ != nullptr) { + return data_->entries(); + } else { + return empty_entries(); + } + } + + rtc::scoped_refptr data_; +}; + +} // namespace webrtc + +#endif // API_RTP_PACKET_INFOS_H_ diff --git a/webrtc/api/scoped_refptr.h b/webrtc/api/scoped_refptr.h new file mode 100644 index 0000000..fa4e83d --- /dev/null +++ b/webrtc/api/scoped_refptr.h @@ -0,0 +1,164 @@ +/* + * Copyright 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. + */ + +// Originally these classes are from Chromium. +// http://src.chromium.org/viewvc/chrome/trunk/src/base/memory/ref_counted.h?view=markup + +// +// A smart pointer class for reference counted objects. Use this class instead +// of calling AddRef and Release manually on a reference counted object to +// avoid common memory leaks caused by forgetting to Release an object +// reference. Sample usage: +// +// class MyFoo : public RefCounted { +// ... +// }; +// +// void some_function() { +// scoped_refptr foo = new MyFoo(); +// foo->Method(param); +// // |foo| is released when this function returns +// } +// +// void some_other_function() { +// scoped_refptr foo = new MyFoo(); +// ... +// foo = nullptr; // explicitly releases |foo| +// ... +// if (foo) +// foo->Method(param); +// } +// +// The above examples show how scoped_refptr acts like a pointer to T. +// Given two scoped_refptr classes, it is also possible to exchange +// references between the two objects, like so: +// +// { +// scoped_refptr a = new MyFoo(); +// scoped_refptr b; +// +// b.swap(a); +// // now, |b| references the MyFoo object, and |a| references null. +// } +// +// To make both |a| and |b| in the above example reference the same MyFoo +// object, simply use the assignment operator: +// +// { +// scoped_refptr a = new MyFoo(); +// scoped_refptr b; +// +// b = a; +// // now, |a| and |b| each own a reference to the same MyFoo object. +// } +// + +#ifndef API_SCOPED_REFPTR_H_ +#define API_SCOPED_REFPTR_H_ + +#include +#include + +namespace rtc { + +template +class scoped_refptr { + public: + typedef T element_type; + + scoped_refptr() : ptr_(nullptr) {} + + scoped_refptr(T* p) : ptr_(p) { // NOLINT(runtime/explicit) + if (ptr_) + ptr_->AddRef(); + } + + scoped_refptr(const scoped_refptr& r) : ptr_(r.ptr_) { + if (ptr_) + ptr_->AddRef(); + } + + template + scoped_refptr(const scoped_refptr& r) : ptr_(r.get()) { + if (ptr_) + ptr_->AddRef(); + } + + // Move constructors. + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.release()) {} + + template + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.release()) {} + + ~scoped_refptr() { + if (ptr_) + ptr_->Release(); + } + + T* get() const { return ptr_; } + operator T*() const { return ptr_; } + T* operator->() const { return ptr_; } + + // Returns the (possibly null) raw pointer, and makes the scoped_refptr hold a + // null pointer, all without touching the reference count of the underlying + // pointed-to object. The object is still reference counted, and the caller of + // release() is now the proud owner of one reference, so it is responsible for + // calling Release() once on the object when no longer using it. + T* release() { + T* retVal = ptr_; + ptr_ = nullptr; + return retVal; + } + + scoped_refptr& operator=(T* p) { + // AddRef first so that self assignment should work + if (p) + p->AddRef(); + if (ptr_) + ptr_->Release(); + ptr_ = p; + return *this; + } + + scoped_refptr& operator=(const scoped_refptr& r) { + return *this = r.ptr_; + } + + template + scoped_refptr& operator=(const scoped_refptr& r) { + return *this = r.get(); + } + + scoped_refptr& operator=(scoped_refptr&& r) noexcept { + scoped_refptr(std::move(r)).swap(*this); + return *this; + } + + template + scoped_refptr& operator=(scoped_refptr&& r) noexcept { + scoped_refptr(std::move(r)).swap(*this); + return *this; + } + + void swap(T** pp) noexcept { + T* p = ptr_; + ptr_ = *pp; + *pp = p; + } + + void swap(scoped_refptr& r) noexcept { swap(&r.ptr_); } + + protected: + T* ptr_; +}; + +} // namespace rtc + +#endif // API_SCOPED_REFPTR_H_ diff --git a/webrtc/api/task_queue/queued_task.h b/webrtc/api/task_queue/queued_task.h new file mode 100644 index 0000000..5748628 --- /dev/null +++ b/webrtc/api/task_queue/queued_task.h @@ -0,0 +1,32 @@ +/* + * Copyright 2018 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. + */ +#ifndef API_TASK_QUEUE_QUEUED_TASK_H_ +#define API_TASK_QUEUE_QUEUED_TASK_H_ + +namespace webrtc { + +// Base interface for asynchronously executed tasks. +// The interface basically consists of a single function, Run(), that executes +// on the target queue. For more details see the Run() method and TaskQueue. +class QueuedTask { + public: + virtual ~QueuedTask() = default; + + // Main routine that will run when the task is executed on the desired queue. + // The task should return |true| to indicate that it should be deleted or + // |false| to indicate that the queue should consider ownership of the task + // having been transferred. Returning |false| can be useful if a task has + // re-posted itself to a different queue or is otherwise being re-used. + virtual bool Run() = 0; +}; + +} // namespace webrtc + +#endif // API_TASK_QUEUE_QUEUED_TASK_H_ diff --git a/webrtc/api/task_queue/task_queue_base.cc b/webrtc/api/task_queue/task_queue_base.cc new file mode 100644 index 0000000..7d3539a --- /dev/null +++ b/webrtc/api/task_queue/task_queue_base.cc @@ -0,0 +1,79 @@ +/* + * Copyright 2019 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 "api/task_queue/task_queue_base.h" + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "rtc_base/checks.h" + +#if defined(ABSL_HAVE_THREAD_LOCAL) + +namespace webrtc { +namespace { + +ABSL_CONST_INIT thread_local TaskQueueBase* current = nullptr; + +} // namespace + +TaskQueueBase* TaskQueueBase::Current() { + return current; +} + +TaskQueueBase::CurrentTaskQueueSetter::CurrentTaskQueueSetter( + TaskQueueBase* task_queue) + : previous_(current) { + current = task_queue; +} + +TaskQueueBase::CurrentTaskQueueSetter::~CurrentTaskQueueSetter() { + current = previous_; +} +} // namespace webrtc + +#elif defined(WEBRTC_POSIX) + +#include + +namespace webrtc { +namespace { + +ABSL_CONST_INIT pthread_key_t g_queue_ptr_tls = 0; + +void InitializeTls() { + RTC_CHECK(pthread_key_create(&g_queue_ptr_tls, nullptr) == 0); +} + +pthread_key_t GetQueuePtrTls() { + static pthread_once_t init_once = PTHREAD_ONCE_INIT; + RTC_CHECK(pthread_once(&init_once, &InitializeTls) == 0); + return g_queue_ptr_tls; +} + +} // namespace + +TaskQueueBase* TaskQueueBase::Current() { + return static_cast(pthread_getspecific(GetQueuePtrTls())); +} + +TaskQueueBase::CurrentTaskQueueSetter::CurrentTaskQueueSetter( + TaskQueueBase* task_queue) + : previous_(TaskQueueBase::Current()) { + pthread_setspecific(GetQueuePtrTls(), task_queue); +} + +TaskQueueBase::CurrentTaskQueueSetter::~CurrentTaskQueueSetter() { + pthread_setspecific(GetQueuePtrTls(), previous_); +} + +} // namespace webrtc + +#else +#error Unsupported platform +#endif diff --git a/webrtc/api/task_queue/task_queue_base.h b/webrtc/api/task_queue/task_queue_base.h new file mode 100644 index 0000000..90b1efd --- /dev/null +++ b/webrtc/api/task_queue/task_queue_base.h @@ -0,0 +1,83 @@ +/* + * Copyright 2019 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. + */ +#ifndef API_TASK_QUEUE_TASK_QUEUE_BASE_H_ +#define API_TASK_QUEUE_TASK_QUEUE_BASE_H_ + +#include + +#include "api/task_queue/queued_task.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +// Asynchronously executes tasks in a way that guarantees that they're executed +// in FIFO order and that tasks never overlap. Tasks may always execute on the +// same worker thread and they may not. To DCHECK that tasks are executing on a +// known task queue, use IsCurrent(). +class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { + public: + // Starts destruction of the task queue. + // On return ensures no task are running and no new tasks are able to start + // on the task queue. + // Responsible for deallocation. Deallocation may happen syncrhoniously during + // Delete or asynchronously after Delete returns. + // Code not running on the TaskQueue should not make any assumption when + // TaskQueue is deallocated and thus should not call any methods after Delete. + // Code running on the TaskQueue should not call Delete, but can assume + // TaskQueue still exists and may call other methods, e.g. PostTask. + virtual void Delete() = 0; + + // Schedules a task to execute. Tasks are executed in FIFO order. + // If |task->Run()| returns true, task is deleted on the task queue + // before next QueuedTask starts executing. + // When a TaskQueue is deleted, pending tasks will not be executed but they + // will be deleted. The deletion of tasks may happen synchronously on the + // TaskQueue or it may happen asynchronously after TaskQueue is deleted. + // This may vary from one implementation to the next so assumptions about + // lifetimes of pending tasks should not be made. + virtual void PostTask(std::unique_ptr task) = 0; + + // Schedules a task to execute a specified number of milliseconds from when + // the call is made. The precision should be considered as "best effort" + // and in some cases, such as on Windows when all high precision timers have + // been used up, can be off by as much as 15 millseconds. + virtual void PostDelayedTask(std::unique_ptr task, + uint32_t milliseconds) = 0; + + // Returns the task queue that is running the current thread. + // Returns nullptr if this thread is not associated with any task queue. + static TaskQueueBase* Current(); + bool IsCurrent() const { return Current() == this; } + + protected: + class CurrentTaskQueueSetter { + public: + explicit CurrentTaskQueueSetter(TaskQueueBase* task_queue); + CurrentTaskQueueSetter(const CurrentTaskQueueSetter&) = delete; + CurrentTaskQueueSetter& operator=(const CurrentTaskQueueSetter&) = delete; + ~CurrentTaskQueueSetter(); + + private: + TaskQueueBase* const previous_; + }; + + // Users of the TaskQueue should call Delete instead of directly deleting + // this object. + virtual ~TaskQueueBase() = default; +}; + +struct TaskQueueDeleter { + void operator()(TaskQueueBase* task_queue) const { task_queue->Delete(); } +}; + +} // namespace webrtc + +#endif // API_TASK_QUEUE_TASK_QUEUE_BASE_H_ diff --git a/webrtc/api/units/data_rate.cc b/webrtc/api/units/data_rate.cc new file mode 100644 index 0000000..f9586c5 --- /dev/null +++ b/webrtc/api/units/data_rate.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 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 "api/units/data_rate.h" + +#include "api/array_view.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +std::string ToString(DataRate value) { + char buf[64]; + rtc::SimpleStringBuilder sb(buf); + if (value.IsPlusInfinity()) { + sb << "+inf bps"; + } else if (value.IsMinusInfinity()) { + sb << "-inf bps"; + } else { + if (value.bps() == 0 || value.bps() % 1000 != 0) { + sb << value.bps() << " bps"; + } else { + sb << value.kbps() << " kbps"; + } + } + return sb.str(); +} +} // namespace webrtc diff --git a/webrtc/api/units/data_rate.h b/webrtc/api/units/data_rate.h new file mode 100644 index 0000000..5c8a61f --- /dev/null +++ b/webrtc/api/units/data_rate.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef API_UNITS_DATA_RATE_H_ +#define API_UNITS_DATA_RATE_H_ + +#ifdef UNIT_TEST +#include // no-presubmit-check TODO(webrtc:8982) +#endif // UNIT_TEST + +#include +#include +#include + +#include "api/units/data_size.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "rtc_base/checks.h" +#include "rtc_base/units/unit_base.h" + +namespace webrtc { +// DataRate is a class that represents a given data rate. This can be used to +// represent bandwidth, encoding bitrate, etc. The internal storage is bits per +// second (bps). +class DataRate final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr DataRate BitsPerSec(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + template + static constexpr DataRate BytesPerSec(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(8, value); + } + template + static constexpr DataRate KilobitsPerSec(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1000, value); + } + static constexpr DataRate Infinity() { return PlusInfinity(); } + + DataRate() = delete; + + template + constexpr T bps() const { + return ToValue(); + } + template + constexpr T bytes_per_sec() const { + return ToFraction<8, T>(); + } + template + constexpr T kbps() const { + return ToFraction<1000, T>(); + } + constexpr int64_t bps_or(int64_t fallback_value) const { + return ToValueOr(fallback_value); + } + constexpr int64_t kbps_or(int64_t fallback_value) const { + return ToFractionOr<1000>(fallback_value); + } + + private: + // Bits per second used internally to simplify debugging by making the value + // more recognizable. + friend class rtc_units_impl::UnitBase; + using RelativeUnit::RelativeUnit; + static constexpr bool one_sided = true; +}; + +namespace data_rate_impl { +inline constexpr int64_t Microbits(const DataSize& size) { + constexpr int64_t kMaxBeforeConversion = + std::numeric_limits::max() / 8000000; + RTC_DCHECK_LE(size.bytes(), kMaxBeforeConversion) + << "size is too large to be expressed in microbits"; + return size.bytes() * 8000000; +} + +inline constexpr int64_t MillibytePerSec(const DataRate& size) { + constexpr int64_t kMaxBeforeConversion = + std::numeric_limits::max() / (1000 / 8); + RTC_DCHECK_LE(size.bps(), kMaxBeforeConversion) + << "rate is too large to be expressed in microbytes per second"; + return size.bps() * (1000 / 8); +} +} // namespace data_rate_impl + +inline constexpr DataRate operator/(const DataSize size, + const TimeDelta duration) { + return DataRate::BitsPerSec(data_rate_impl::Microbits(size) / duration.us()); +} +inline constexpr TimeDelta operator/(const DataSize size, const DataRate rate) { + return TimeDelta::Micros(data_rate_impl::Microbits(size) / rate.bps()); +} +inline constexpr DataSize operator*(const DataRate rate, + const TimeDelta duration) { + int64_t microbits = rate.bps() * duration.us(); + return DataSize::Bytes((microbits + 4000000) / 8000000); +} +inline constexpr DataSize operator*(const TimeDelta duration, + const DataRate rate) { + return rate * duration; +} + +inline constexpr DataSize operator/(const DataRate rate, + const Frequency frequency) { + int64_t millihertz = frequency.millihertz(); + // Note that the value is truncated here reather than rounded, potentially + // introducing an error of .5 bytes if rounding were expected. + return DataSize::Bytes(data_rate_impl::MillibytePerSec(rate) / millihertz); +} +inline constexpr Frequency operator/(const DataRate rate, const DataSize size) { + return Frequency::MilliHertz(data_rate_impl::MillibytePerSec(rate) / + size.bytes()); +} +inline constexpr DataRate operator*(const DataSize size, + const Frequency frequency) { + RTC_DCHECK(frequency.IsZero() || + size.bytes() <= std::numeric_limits::max() / 8 / + frequency.millihertz()); + int64_t millibits_per_second = + size.bytes() * 8 * frequency.millihertz(); + return DataRate::BitsPerSec((millibits_per_second + 500) / 1000); +} +inline constexpr DataRate operator*(const Frequency frequency, + const DataSize size) { + return size * frequency; +} + +std::string ToString(DataRate value); +inline std::string ToLogString(DataRate value) { + return ToString(value); +} + +#ifdef UNIT_TEST +inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982) + std::ostream& stream, // no-presubmit-check TODO(webrtc:8982) + DataRate value) { + return stream << ToString(value); +} +#endif // UNIT_TEST + +} // namespace webrtc + +#endif // API_UNITS_DATA_RATE_H_ diff --git a/webrtc/api/units/data_size.cc b/webrtc/api/units/data_size.cc new file mode 100644 index 0000000..45487df --- /dev/null +++ b/webrtc/api/units/data_size.cc @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 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 "api/units/data_size.h" + +#include "api/array_view.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +std::string ToString(DataSize value) { + char buf[64]; + rtc::SimpleStringBuilder sb(buf); + if (value.IsPlusInfinity()) { + sb << "+inf bytes"; + } else if (value.IsMinusInfinity()) { + sb << "-inf bytes"; + } else { + sb << value.bytes() << " bytes"; + } + return sb.str(); +} +} // namespace webrtc diff --git a/webrtc/api/units/data_size.h b/webrtc/api/units/data_size.h new file mode 100644 index 0000000..27a2a4e --- /dev/null +++ b/webrtc/api/units/data_size.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef API_UNITS_DATA_SIZE_H_ +#define API_UNITS_DATA_SIZE_H_ + +#ifdef UNIT_TEST +#include // no-presubmit-check TODO(webrtc:8982) +#endif // UNIT_TEST + +#include +#include + +#include "rtc_base/units/unit_base.h" + +namespace webrtc { +// DataSize is a class represeting a count of bytes. +class DataSize final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr DataSize Bytes(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + static constexpr DataSize Infinity() { return PlusInfinity(); } + + DataSize() = delete; + + template + constexpr T bytes() const { + return ToValue(); + } + + constexpr int64_t bytes_or(int64_t fallback_value) const { + return ToValueOr(fallback_value); + } + + private: + friend class rtc_units_impl::UnitBase; + using RelativeUnit::RelativeUnit; + static constexpr bool one_sided = true; +}; + +std::string ToString(DataSize value); +inline std::string ToLogString(DataSize value) { + return ToString(value); +} + +#ifdef UNIT_TEST +inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982) + std::ostream& stream, // no-presubmit-check TODO(webrtc:8982) + DataSize value) { + return stream << ToString(value); +} +#endif // UNIT_TEST + +} // namespace webrtc + +#endif // API_UNITS_DATA_SIZE_H_ diff --git a/webrtc/api/units/frequency.cc b/webrtc/api/units/frequency.cc new file mode 100644 index 0000000..2d938a2 --- /dev/null +++ b/webrtc/api/units/frequency.cc @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 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 "api/units/frequency.h" + +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { +std::string ToString(Frequency value) { + char buf[64]; + rtc::SimpleStringBuilder sb(buf); + if (value.IsPlusInfinity()) { + sb << "+inf Hz"; + } else if (value.IsMinusInfinity()) { + sb << "-inf Hz"; + } else if (value.millihertz() % 1000 != 0) { + sb.AppendFormat("%.3f Hz", value.hertz()); + } else { + sb << value.hertz() << " Hz"; + } + return sb.str(); +} +} // namespace webrtc diff --git a/webrtc/api/units/frequency.h b/webrtc/api/units/frequency.h new file mode 100644 index 0000000..88912c6 --- /dev/null +++ b/webrtc/api/units/frequency.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019 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. + */ +#ifndef API_UNITS_FREQUENCY_H_ +#define API_UNITS_FREQUENCY_H_ + +#ifdef UNIT_TEST +#include // no-presubmit-check TODO(webrtc:8982) +#endif // UNIT_TEST + +#include +#include +#include +#include + +#include "api/units/time_delta.h" +#include "rtc_base/units/unit_base.h" + +namespace webrtc { + +class Frequency final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr Frequency MilliHertz(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + template + static constexpr Frequency Hertz(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000, value); + } + template + static constexpr Frequency KiloHertz(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000'000, value); + } + + Frequency() = delete; + + template + constexpr T hertz() const { + return ToFraction<1000, T>(); + } + template + constexpr T millihertz() const { + return ToValue(); + } + + private: + friend class rtc_units_impl::UnitBase; + using RelativeUnit::RelativeUnit; + static constexpr bool one_sided = true; +}; + +inline constexpr Frequency operator/(int64_t nominator, + const TimeDelta& interval) { + constexpr int64_t kKiloPerMicro = 1000 * 1000000; + RTC_DCHECK_LE(nominator, std::numeric_limits::max() / kKiloPerMicro); + RTC_CHECK(interval.IsFinite()); + RTC_CHECK(!interval.IsZero()); + return Frequency::MilliHertz(nominator * kKiloPerMicro / interval.us()); +} + +inline constexpr TimeDelta operator/(int64_t nominator, + const Frequency& frequency) { + constexpr int64_t kMegaPerMilli = 1000000 * 1000; + RTC_DCHECK_LE(nominator, std::numeric_limits::max() / kMegaPerMilli); + RTC_CHECK(frequency.IsFinite()); + RTC_CHECK(!frequency.IsZero()); + return TimeDelta::Micros(nominator * kMegaPerMilli / frequency.millihertz()); +} + +inline constexpr double operator*(Frequency frequency, TimeDelta time_delta) { + return frequency.hertz() * time_delta.seconds(); +} +inline constexpr double operator*(TimeDelta time_delta, Frequency frequency) { + return frequency * time_delta; +} + +std::string ToString(Frequency value); +inline std::string ToLogString(Frequency value) { + return ToString(value); +} + +#ifdef UNIT_TEST +inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982) + std::ostream& stream, // no-presubmit-check TODO(webrtc:8982) + Frequency value) { + return stream << ToString(value); +} +#endif // UNIT_TEST + +} // namespace webrtc +#endif // API_UNITS_FREQUENCY_H_ diff --git a/webrtc/api/units/time_delta.cc b/webrtc/api/units/time_delta.cc new file mode 100644 index 0000000..31bf3e0 --- /dev/null +++ b/webrtc/api/units/time_delta.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 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 "api/units/time_delta.h" + +#include "api/array_view.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +std::string ToString(TimeDelta value) { + char buf[64]; + rtc::SimpleStringBuilder sb(buf); + if (value.IsPlusInfinity()) { + sb << "+inf ms"; + } else if (value.IsMinusInfinity()) { + sb << "-inf ms"; + } else { + if (value.us() == 0 || (value.us() % 1000) != 0) + sb << value.us() << " us"; + else if (value.ms() % 1000 != 0) + sb << value.ms() << " ms"; + else + sb << value.seconds() << " s"; + } + return sb.str(); +} + +} // namespace webrtc diff --git a/webrtc/api/units/time_delta.h b/webrtc/api/units/time_delta.h new file mode 100644 index 0000000..173affc --- /dev/null +++ b/webrtc/api/units/time_delta.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef API_UNITS_TIME_DELTA_H_ +#define API_UNITS_TIME_DELTA_H_ + +#ifdef UNIT_TEST +#include // no-presubmit-check TODO(webrtc:8982) +#endif // UNIT_TEST + +#include +#include +#include + +#include "rtc_base/units/unit_base.h" + +namespace webrtc { + +// TimeDelta represents the difference between two timestamps. Commonly this can +// be a duration. However since two Timestamps are not guaranteed to have the +// same epoch (they might come from different computers, making exact +// synchronisation infeasible), the duration covered by a TimeDelta can be +// undefined. To simplify usage, it can be constructed and converted to +// different units, specifically seconds (s), milliseconds (ms) and +// microseconds (us). +class TimeDelta final : public rtc_units_impl::RelativeUnit { + public: + template + static constexpr TimeDelta Seconds(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000'000, value); + } + template + static constexpr TimeDelta Millis(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000, value); + } + template + static constexpr TimeDelta Micros(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + + TimeDelta() = delete; + + template + constexpr T seconds() const { + return ToFraction<1000000, T>(); + } + template + constexpr T ms() const { + return ToFraction<1000, T>(); + } + template + constexpr T us() const { + return ToValue(); + } + template + constexpr T ns() const { + return ToMultiple<1000, T>(); + } + + constexpr int64_t seconds_or(int64_t fallback_value) const { + return ToFractionOr<1000000>(fallback_value); + } + constexpr int64_t ms_or(int64_t fallback_value) const { + return ToFractionOr<1000>(fallback_value); + } + constexpr int64_t us_or(int64_t fallback_value) const { + return ToValueOr(fallback_value); + } + + constexpr TimeDelta Abs() const { + return us() < 0 ? TimeDelta::Micros(-us()) : *this; + } + + private: + friend class rtc_units_impl::UnitBase; + using RelativeUnit::RelativeUnit; + static constexpr bool one_sided = false; +}; + +std::string ToString(TimeDelta value); +inline std::string ToLogString(TimeDelta value) { + return ToString(value); +} + +#ifdef UNIT_TEST +inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982) + std::ostream& stream, // no-presubmit-check TODO(webrtc:8982) + TimeDelta value) { + return stream << ToString(value); +} +#endif // UNIT_TEST + +} // namespace webrtc + +#endif // API_UNITS_TIME_DELTA_H_ diff --git a/webrtc/api/units/timestamp.cc b/webrtc/api/units/timestamp.cc new file mode 100644 index 0000000..fc4f419 --- /dev/null +++ b/webrtc/api/units/timestamp.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 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 "api/units/timestamp.h" + +#include "api/array_view.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { +std::string ToString(Timestamp value) { + char buf[64]; + rtc::SimpleStringBuilder sb(buf); + if (value.IsPlusInfinity()) { + sb << "+inf ms"; + } else if (value.IsMinusInfinity()) { + sb << "-inf ms"; + } else { + if (value.us() == 0 || (value.us() % 1000) != 0) + sb << value.us() << " us"; + else if (value.ms() % 1000 != 0) + sb << value.ms() << " ms"; + else + sb << value.seconds() << " s"; + } + return sb.str(); +} +} // namespace webrtc diff --git a/webrtc/api/units/timestamp.h b/webrtc/api/units/timestamp.h new file mode 100644 index 0000000..f83477e --- /dev/null +++ b/webrtc/api/units/timestamp.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef API_UNITS_TIMESTAMP_H_ +#define API_UNITS_TIMESTAMP_H_ + +#ifdef UNIT_TEST +#include // no-presubmit-check TODO(webrtc:8982) +#endif // UNIT_TEST + +#include +#include + +#include "api/units/time_delta.h" +#include "rtc_base/checks.h" + +namespace webrtc { +// Timestamp represents the time that has passed since some unspecified epoch. +// The epoch is assumed to be before any represented timestamps, this means that +// negative values are not valid. The most notable feature is that the +// difference of two Timestamps results in a TimeDelta. +class Timestamp final : public rtc_units_impl::UnitBase { + public: + template + static constexpr Timestamp Seconds(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000'000, value); + } + template + static constexpr Timestamp Millis(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromFraction(1'000, value); + } + template + static constexpr Timestamp Micros(T value) { + static_assert(std::is_arithmetic::value, ""); + return FromValue(value); + } + + Timestamp() = delete; + + template + constexpr T seconds() const { + return ToFraction<1000000, T>(); + } + template + constexpr T ms() const { + return ToFraction<1000, T>(); + } + template + constexpr T us() const { + return ToValue(); + } + + constexpr int64_t seconds_or(int64_t fallback_value) const { + return ToFractionOr<1000000>(fallback_value); + } + constexpr int64_t ms_or(int64_t fallback_value) const { + return ToFractionOr<1000>(fallback_value); + } + constexpr int64_t us_or(int64_t fallback_value) const { + return ToValueOr(fallback_value); + } + + constexpr Timestamp operator+(const TimeDelta delta) const { + if (IsPlusInfinity() || delta.IsPlusInfinity()) { + RTC_DCHECK(!IsMinusInfinity()); + RTC_DCHECK(!delta.IsMinusInfinity()); + return PlusInfinity(); + } else if (IsMinusInfinity() || delta.IsMinusInfinity()) { + RTC_DCHECK(!IsPlusInfinity()); + RTC_DCHECK(!delta.IsPlusInfinity()); + return MinusInfinity(); + } + return Timestamp::Micros(us() + delta.us()); + } + constexpr Timestamp operator-(const TimeDelta delta) const { + if (IsPlusInfinity() || delta.IsMinusInfinity()) { + RTC_DCHECK(!IsMinusInfinity()); + RTC_DCHECK(!delta.IsPlusInfinity()); + return PlusInfinity(); + } else if (IsMinusInfinity() || delta.IsPlusInfinity()) { + RTC_DCHECK(!IsPlusInfinity()); + RTC_DCHECK(!delta.IsMinusInfinity()); + return MinusInfinity(); + } + return Timestamp::Micros(us() - delta.us()); + } + constexpr TimeDelta operator-(const Timestamp other) const { + if (IsPlusInfinity() || other.IsMinusInfinity()) { + RTC_DCHECK(!IsMinusInfinity()); + RTC_DCHECK(!other.IsPlusInfinity()); + return TimeDelta::PlusInfinity(); + } else if (IsMinusInfinity() || other.IsPlusInfinity()) { + RTC_DCHECK(!IsPlusInfinity()); + RTC_DCHECK(!other.IsMinusInfinity()); + return TimeDelta::MinusInfinity(); + } + return TimeDelta::Micros(us() - other.us()); + } + constexpr Timestamp& operator-=(const TimeDelta delta) { + *this = *this - delta; + return *this; + } + constexpr Timestamp& operator+=(const TimeDelta delta) { + *this = *this + delta; + return *this; + } + + private: + friend class rtc_units_impl::UnitBase; + using UnitBase::UnitBase; + static constexpr bool one_sided = true; +}; + +std::string ToString(Timestamp value); +inline std::string ToLogString(Timestamp value) { + return ToString(value); +} + +#ifdef UNIT_TEST +inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982) + std::ostream& stream, // no-presubmit-check TODO(webrtc:8982) + Timestamp value) { + return stream << ToString(value); +} +#endif // UNIT_TEST + +} // namespace webrtc + +#endif // API_UNITS_TIMESTAMP_H_ diff --git a/webrtc/api/video/color_space.cc b/webrtc/api/video/color_space.cc new file mode 100644 index 0000000..710bb43 --- /dev/null +++ b/webrtc/api/video/color_space.cc @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2018 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 "api/video/color_space.h" + +namespace webrtc { +namespace { +// Try to convert |enum_value| into the enum class T. |enum_bitmask| is created +// by the funciton below. Returns true if conversion was successful, false +// otherwise. +template +bool SetFromUint8(uint8_t enum_value, uint64_t enum_bitmask, T* out) { + if ((enum_value < 64) && ((enum_bitmask >> enum_value) & 1)) { + *out = static_cast(enum_value); + return true; + } + return false; +} + +// This function serves as an assert for the constexpr function below. It's on +// purpose not declared as constexpr so that it causes a build problem if enum +// values of 64 or above are used. The bitmask and the code generating it would +// have to be extended if the standard is updated to include enum values >= 64. +int EnumMustBeLessThan64() { + return -1; +} + +template +constexpr int MakeMask(const int index, const int length, T (&values)[N]) { + return length > 1 + ? (MakeMask(index, 1, values) + + MakeMask(index + 1, length - 1, values)) + : (static_cast(values[index]) < 64 + ? (uint64_t{1} << static_cast(values[index])) + : EnumMustBeLessThan64()); +} + +// Create a bitmask where each bit corresponds to one potential enum value. +// |values| should be an array listing all possible enum values. The bit is set +// to one if the corresponding enum exists. Only works for enums with values +// less than 64. +template +constexpr uint64_t CreateEnumBitmask(T (&values)[N]) { + return MakeMask(0, N, values); +} + +bool SetChromaSitingFromUint8(uint8_t enum_value, + ColorSpace::ChromaSiting* chroma_siting) { + constexpr ColorSpace::ChromaSiting kChromaSitings[] = { + ColorSpace::ChromaSiting::kUnspecified, + ColorSpace::ChromaSiting::kCollocated, ColorSpace::ChromaSiting::kHalf}; + constexpr uint64_t enum_bitmask = CreateEnumBitmask(kChromaSitings); + + return SetFromUint8(enum_value, enum_bitmask, chroma_siting); +} + +} // namespace + +ColorSpace::ColorSpace() = default; +ColorSpace::ColorSpace(const ColorSpace& other) = default; +ColorSpace::ColorSpace(ColorSpace&& other) = default; +ColorSpace& ColorSpace::operator=(const ColorSpace& other) = default; + +ColorSpace::ColorSpace(PrimaryID primaries, + TransferID transfer, + MatrixID matrix, + RangeID range) + : ColorSpace(primaries, + transfer, + matrix, + range, + ChromaSiting::kUnspecified, + ChromaSiting::kUnspecified, + nullptr) {} + +ColorSpace::ColorSpace(PrimaryID primaries, + TransferID transfer, + MatrixID matrix, + RangeID range, + ChromaSiting chroma_siting_horz, + ChromaSiting chroma_siting_vert, + const HdrMetadata* hdr_metadata) + : primaries_(primaries), + transfer_(transfer), + matrix_(matrix), + range_(range), + chroma_siting_horizontal_(chroma_siting_horz), + chroma_siting_vertical_(chroma_siting_vert), + hdr_metadata_(hdr_metadata ? absl::make_optional(*hdr_metadata) + : absl::nullopt) {} + +ColorSpace::PrimaryID ColorSpace::primaries() const { + return primaries_; +} + +ColorSpace::TransferID ColorSpace::transfer() const { + return transfer_; +} + +ColorSpace::MatrixID ColorSpace::matrix() const { + return matrix_; +} + +ColorSpace::RangeID ColorSpace::range() const { + return range_; +} + +ColorSpace::ChromaSiting ColorSpace::chroma_siting_horizontal() const { + return chroma_siting_horizontal_; +} + +ColorSpace::ChromaSiting ColorSpace::chroma_siting_vertical() const { + return chroma_siting_vertical_; +} + +const HdrMetadata* ColorSpace::hdr_metadata() const { + return hdr_metadata_ ? &*hdr_metadata_ : nullptr; +} + +bool ColorSpace::set_primaries_from_uint8(uint8_t enum_value) { + constexpr PrimaryID kPrimaryIds[] = { + PrimaryID::kBT709, PrimaryID::kUnspecified, PrimaryID::kBT470M, + PrimaryID::kBT470BG, PrimaryID::kSMPTE170M, PrimaryID::kSMPTE240M, + PrimaryID::kFILM, PrimaryID::kBT2020, PrimaryID::kSMPTEST428, + PrimaryID::kSMPTEST431, PrimaryID::kSMPTEST432, PrimaryID::kJEDECP22}; + constexpr uint64_t enum_bitmask = CreateEnumBitmask(kPrimaryIds); + + return SetFromUint8(enum_value, enum_bitmask, &primaries_); +} + +bool ColorSpace::set_transfer_from_uint8(uint8_t enum_value) { + constexpr TransferID kTransferIds[] = { + TransferID::kBT709, TransferID::kUnspecified, + TransferID::kGAMMA22, TransferID::kGAMMA28, + TransferID::kSMPTE170M, TransferID::kSMPTE240M, + TransferID::kLINEAR, TransferID::kLOG, + TransferID::kLOG_SQRT, TransferID::kIEC61966_2_4, + TransferID::kBT1361_ECG, TransferID::kIEC61966_2_1, + TransferID::kBT2020_10, TransferID::kBT2020_12, + TransferID::kSMPTEST2084, TransferID::kSMPTEST428, + TransferID::kARIB_STD_B67}; + constexpr uint64_t enum_bitmask = CreateEnumBitmask(kTransferIds); + + return SetFromUint8(enum_value, enum_bitmask, &transfer_); +} + +bool ColorSpace::set_matrix_from_uint8(uint8_t enum_value) { + constexpr MatrixID kMatrixIds[] = { + MatrixID::kRGB, MatrixID::kBT709, MatrixID::kUnspecified, + MatrixID::kFCC, MatrixID::kBT470BG, MatrixID::kSMPTE170M, + MatrixID::kSMPTE240M, MatrixID::kYCOCG, MatrixID::kBT2020_NCL, + MatrixID::kBT2020_CL, MatrixID::kSMPTE2085, MatrixID::kCDNCLS, + MatrixID::kCDCLS, MatrixID::kBT2100_ICTCP}; + constexpr uint64_t enum_bitmask = CreateEnumBitmask(kMatrixIds); + + return SetFromUint8(enum_value, enum_bitmask, &matrix_); +} + +bool ColorSpace::set_range_from_uint8(uint8_t enum_value) { + constexpr RangeID kRangeIds[] = {RangeID::kInvalid, RangeID::kLimited, + RangeID::kFull, RangeID::kDerived}; + constexpr uint64_t enum_bitmask = CreateEnumBitmask(kRangeIds); + + return SetFromUint8(enum_value, enum_bitmask, &range_); +} + +bool ColorSpace::set_chroma_siting_horizontal_from_uint8(uint8_t enum_value) { + return SetChromaSitingFromUint8(enum_value, &chroma_siting_horizontal_); +} + +bool ColorSpace::set_chroma_siting_vertical_from_uint8(uint8_t enum_value) { + return SetChromaSitingFromUint8(enum_value, &chroma_siting_vertical_); +} + +void ColorSpace::set_hdr_metadata(const HdrMetadata* hdr_metadata) { + hdr_metadata_ = + hdr_metadata ? absl::make_optional(*hdr_metadata) : absl::nullopt; +} + +} // namespace webrtc diff --git a/webrtc/api/video/color_space.h b/webrtc/api/video/color_space.h new file mode 100644 index 0000000..a7ad86b --- /dev/null +++ b/webrtc/api/video/color_space.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef API_VIDEO_COLOR_SPACE_H_ +#define API_VIDEO_COLOR_SPACE_H_ + +#include + +#include "absl/types/optional.h" +#include "api/video/hdr_metadata.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// This class represents color information as specified in T-REC H.273, +// available from https://www.itu.int/rec/T-REC-H.273. +// +// WebRTC's supported codecs: +// - VP9 supports color profiles, see VP9 Bitstream & Decoding Process +// Specification Version 0.6 Section 7.2.2 "Color config semantics" available +// from https://www.webmproject.org. +// - VP8 only supports BT.601, see +// https://tools.ietf.org/html/rfc6386#section-9.2 +// - H264 uses the exact same representation as T-REC H.273. See T-REC-H.264 +// E.2.1, "VUI parameters semantics", available from +// https://www.itu.int/rec/T-REC-H.264. + +class RTC_EXPORT ColorSpace { + public: + enum class PrimaryID : uint8_t { + // The indices are equal to the values specified in T-REC H.273 Table 2. + kBT709 = 1, + kUnspecified = 2, + kBT470M = 4, + kBT470BG = 5, + kSMPTE170M = 6, // Identical to BT601 + kSMPTE240M = 7, + kFILM = 8, + kBT2020 = 9, + kSMPTEST428 = 10, + kSMPTEST431 = 11, + kSMPTEST432 = 12, + kJEDECP22 = 22, // Identical to EBU3213-E + // When adding/removing entries here, please make sure to do the + // corresponding change to kPrimaryIds. + }; + + enum class TransferID : uint8_t { + // The indices are equal to the values specified in T-REC H.273 Table 3. + kBT709 = 1, + kUnspecified = 2, + kGAMMA22 = 4, + kGAMMA28 = 5, + kSMPTE170M = 6, + kSMPTE240M = 7, + kLINEAR = 8, + kLOG = 9, + kLOG_SQRT = 10, + kIEC61966_2_4 = 11, + kBT1361_ECG = 12, + kIEC61966_2_1 = 13, + kBT2020_10 = 14, + kBT2020_12 = 15, + kSMPTEST2084 = 16, + kSMPTEST428 = 17, + kARIB_STD_B67 = 18, + // When adding/removing entries here, please make sure to do the + // corresponding change to kTransferIds. + }; + + enum class MatrixID : uint8_t { + // The indices are equal to the values specified in T-REC H.273 Table 4. + kRGB = 0, + kBT709 = 1, + kUnspecified = 2, + kFCC = 4, + kBT470BG = 5, + kSMPTE170M = 6, + kSMPTE240M = 7, + kYCOCG = 8, + kBT2020_NCL = 9, + kBT2020_CL = 10, + kSMPTE2085 = 11, + kCDNCLS = 12, + kCDCLS = 13, + kBT2100_ICTCP = 14, + // When adding/removing entries here, please make sure to do the + // corresponding change to kMatrixIds. + }; + + enum class RangeID { + // The indices are equal to the values specified at + // https://www.webmproject.org/docs/container/#colour for the element Range. + kInvalid = 0, + // Limited Rec. 709 color range with RGB values ranging from 16 to 235. + kLimited = 1, + // Full RGB color range with RGB valees from 0 to 255. + kFull = 2, + // Range is defined by MatrixCoefficients/TransferCharacteristics. + kDerived = 3, + // When adding/removing entries here, please make sure to do the + // corresponding change to kRangeIds. + }; + + enum class ChromaSiting { + // Chroma siting specifies how chroma is subsampled relative to the luma + // samples in a YUV video frame. + // The indices are equal to the values specified at + // https://www.webmproject.org/docs/container/#colour for the element + // ChromaSitingVert and ChromaSitingHorz. + kUnspecified = 0, + kCollocated = 1, + kHalf = 2, + // When adding/removing entries here, please make sure to do the + // corresponding change to kChromaSitings. + }; + + ColorSpace(); + ColorSpace(const ColorSpace& other); + ColorSpace(ColorSpace&& other); + ColorSpace& operator=(const ColorSpace& other); + ColorSpace(PrimaryID primaries, + TransferID transfer, + MatrixID matrix, + RangeID range); + ColorSpace(PrimaryID primaries, + TransferID transfer, + MatrixID matrix, + RangeID range, + ChromaSiting chroma_siting_horizontal, + ChromaSiting chroma_siting_vertical, + const HdrMetadata* hdr_metadata); + friend bool operator==(const ColorSpace& lhs, const ColorSpace& rhs) { + return lhs.primaries_ == rhs.primaries_ && lhs.transfer_ == rhs.transfer_ && + lhs.matrix_ == rhs.matrix_ && lhs.range_ == rhs.range_ && + lhs.chroma_siting_horizontal_ == rhs.chroma_siting_horizontal_ && + lhs.chroma_siting_vertical_ == rhs.chroma_siting_vertical_ && + lhs.hdr_metadata_ == rhs.hdr_metadata_; + } + friend bool operator!=(const ColorSpace& lhs, const ColorSpace& rhs) { + return !(lhs == rhs); + } + + PrimaryID primaries() const; + TransferID transfer() const; + MatrixID matrix() const; + RangeID range() const; + ChromaSiting chroma_siting_horizontal() const; + ChromaSiting chroma_siting_vertical() const; + const HdrMetadata* hdr_metadata() const; + + bool set_primaries_from_uint8(uint8_t enum_value); + bool set_transfer_from_uint8(uint8_t enum_value); + bool set_matrix_from_uint8(uint8_t enum_value); + bool set_range_from_uint8(uint8_t enum_value); + bool set_chroma_siting_horizontal_from_uint8(uint8_t enum_value); + bool set_chroma_siting_vertical_from_uint8(uint8_t enum_value); + void set_hdr_metadata(const HdrMetadata* hdr_metadata); + + private: + PrimaryID primaries_ = PrimaryID::kUnspecified; + TransferID transfer_ = TransferID::kUnspecified; + MatrixID matrix_ = MatrixID::kUnspecified; + RangeID range_ = RangeID::kInvalid; + ChromaSiting chroma_siting_horizontal_ = ChromaSiting::kUnspecified; + ChromaSiting chroma_siting_vertical_ = ChromaSiting::kUnspecified; + absl::optional hdr_metadata_; +}; + +} // namespace webrtc +#endif // API_VIDEO_COLOR_SPACE_H_ diff --git a/webrtc/api/video/hdr_metadata.cc b/webrtc/api/video/hdr_metadata.cc new file mode 100644 index 0000000..e2a669c --- /dev/null +++ b/webrtc/api/video/hdr_metadata.cc @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 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 "api/video/hdr_metadata.h" + +namespace webrtc { + +HdrMasteringMetadata::Chromaticity::Chromaticity() = default; + +HdrMasteringMetadata::HdrMasteringMetadata() = default; + +HdrMetadata::HdrMetadata() = default; + +} // namespace webrtc diff --git a/webrtc/api/video/hdr_metadata.h b/webrtc/api/video/hdr_metadata.h new file mode 100644 index 0000000..e9001a2 --- /dev/null +++ b/webrtc/api/video/hdr_metadata.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef API_VIDEO_HDR_METADATA_H_ +#define API_VIDEO_HDR_METADATA_H_ + +namespace webrtc { + +// SMPTE ST 2086 mastering metadata, +// see https://ieeexplore.ieee.org/document/8353899. +struct HdrMasteringMetadata { + struct Chromaticity { + Chromaticity(); + + bool operator==(const Chromaticity& rhs) const { + return x == rhs.x && y == rhs.y; + } + + bool Validate() const { + return x >= 0.0 && x <= 1.0 && y >= 0.0 && y <= 1.0; + } + + // xy chromaticity coordinates must be calculated as specified in ISO + // 11664-3:2012 Section 7, and must be specified with four decimal places. + // The x coordinate should be in the range [0.0001, 0.7400] and the y + // coordinate should be in the range [0.0001, 0.8400]. Valid range [0.0000, + // 1.0000]. + float x = 0.0f; + float y = 0.0f; + }; + + HdrMasteringMetadata(); + + bool operator==(const HdrMasteringMetadata& rhs) const { + return ((primary_r == rhs.primary_r) && (primary_g == rhs.primary_g) && + (primary_b == rhs.primary_b) && (white_point == rhs.white_point) && + (luminance_max == rhs.luminance_max) && + (luminance_min == rhs.luminance_min)); + } + + bool Validate() const { + return luminance_max >= 0.0 && luminance_max <= 20000.0 && + luminance_min >= 0.0 && luminance_min <= 5.0 && + primary_r.Validate() && primary_g.Validate() && + primary_b.Validate() && white_point.Validate(); + } + + // The nominal primaries of the mastering display. + Chromaticity primary_r; + Chromaticity primary_g; + Chromaticity primary_b; + + // The nominal chromaticity of the white point of the mastering display. + Chromaticity white_point; + + // The nominal maximum display luminance of the mastering display. Specified + // in the unit candela/m2. The value should be in the range [5, 10000] with + // zero decimal places. Valid range [0, 20000]. + float luminance_max = 0.0f; + + // The nominal minimum display luminance of the mastering display. Specified + // in the unit candela/m2. The value should be in the range [0.0001, 5.0000] + // with four decimal places. Valid range [0.0000, 5.0000]. + float luminance_min = 0.0f; +}; + +// High dynamic range (HDR) metadata common for HDR10 and WebM/VP9-based HDR +// formats. This struct replicates the HDRMetadata struct defined in +// https://cs.chromium.org/chromium/src/media/base/hdr_metadata.h +struct HdrMetadata { + HdrMetadata(); + + bool operator==(const HdrMetadata& rhs) const { + return ( + (max_content_light_level == rhs.max_content_light_level) && + (max_frame_average_light_level == rhs.max_frame_average_light_level) && + (mastering_metadata == rhs.mastering_metadata)); + } + + bool Validate() const { + return max_content_light_level >= 0 && max_content_light_level <= 20000 && + max_frame_average_light_level >= 0 && + max_frame_average_light_level <= 20000 && + mastering_metadata.Validate(); + } + + HdrMasteringMetadata mastering_metadata; + // Max content light level (CLL), i.e. maximum brightness level present in the + // stream, in nits. 1 nit = 1 candela/m2. Valid range [0, 20000]. + int max_content_light_level = 0; + // Max frame-average light level (FALL), i.e. maximum average brightness of + // the brightest frame in the stream, in nits. Valid range [0, 20000]. + int max_frame_average_light_level = 0; +}; + +} // namespace webrtc + +#endif // API_VIDEO_HDR_METADATA_H_ diff --git a/webrtc/api/video/video_content_type.cc b/webrtc/api/video/video_content_type.cc new file mode 100644 index 0000000..9ba3ece --- /dev/null +++ b/webrtc/api/video/video_content_type.cc @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017 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 "api/video/video_content_type.h" + +// VideoContentType stored as a single byte, which is sent over the network. +// Structure: +// +// 0 1 2 3 4 5 6 7 +// +---------------+ +// |r r e e e s s c| +// +// where: +// r - reserved bits. +// e - 3-bit number of an experiment group counted from 1. 0 means there's no +// experiment ongoing. +// s - 2-bit simulcast stream id or spatial layer, counted from 1. 0 means that +// no simulcast information is set. +// c - content type. 0 means real-time video, 1 means screenshare. +// + +namespace webrtc { +namespace videocontenttypehelpers { + +namespace { +static constexpr uint8_t kScreenshareBitsSize = 1; +static constexpr uint8_t kScreenshareBitsMask = + (1u << kScreenshareBitsSize) - 1; + +static constexpr uint8_t kSimulcastShift = 1; +static constexpr uint8_t kSimulcastBitsSize = 2; +static constexpr uint8_t kSimulcastBitsMask = ((1u << kSimulcastBitsSize) - 1) + << kSimulcastShift; // 0b00000110 + +static constexpr uint8_t kExperimentShift = 3; +static constexpr uint8_t kExperimentBitsSize = 3; +static constexpr uint8_t kExperimentBitsMask = + ((1u << kExperimentBitsSize) - 1) << kExperimentShift; // 0b00111000 + +static constexpr uint8_t kTotalBitsSize = + kScreenshareBitsSize + kSimulcastBitsSize + kExperimentBitsSize; +} // namespace + +bool SetExperimentId(VideoContentType* content_type, uint8_t experiment_id) { + // Store in bits 2-4. + if (experiment_id >= (1 << kExperimentBitsSize)) + return false; + *content_type = static_cast( + (static_cast(*content_type) & ~kExperimentBitsMask) | + ((experiment_id << kExperimentShift) & kExperimentBitsMask)); + return true; +} + +bool SetSimulcastId(VideoContentType* content_type, uint8_t simulcast_id) { + // Store in bits 5-6. + if (simulcast_id >= (1 << kSimulcastBitsSize)) + return false; + *content_type = static_cast( + (static_cast(*content_type) & ~kSimulcastBitsMask) | + ((simulcast_id << kSimulcastShift) & kSimulcastBitsMask)); + return true; +} + +uint8_t GetExperimentId(const VideoContentType& content_type) { + return (static_cast(content_type) & kExperimentBitsMask) >> + kExperimentShift; +} +uint8_t GetSimulcastId(const VideoContentType& content_type) { + return (static_cast(content_type) & kSimulcastBitsMask) >> + kSimulcastShift; +} + +bool IsScreenshare(const VideoContentType& content_type) { + return (static_cast(content_type) & kScreenshareBitsMask) > 0; +} + +bool IsValidContentType(uint8_t value) { + // Any 6-bit value is allowed. + return value < (1 << kTotalBitsSize); +} + +const char* ToString(const VideoContentType& content_type) { + return IsScreenshare(content_type) ? "screen" : "realtime"; +} +} // namespace videocontenttypehelpers +} // namespace webrtc diff --git a/webrtc/api/video/video_content_type.h b/webrtc/api/video/video_content_type.h new file mode 100644 index 0000000..2d38a62 --- /dev/null +++ b/webrtc/api/video/video_content_type.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef API_VIDEO_VIDEO_CONTENT_TYPE_H_ +#define API_VIDEO_VIDEO_CONTENT_TYPE_H_ + +#include + +namespace webrtc { + +enum class VideoContentType : uint8_t { + UNSPECIFIED = 0, + SCREENSHARE = 1, +}; + +namespace videocontenttypehelpers { +bool SetExperimentId(VideoContentType* content_type, uint8_t experiment_id); +bool SetSimulcastId(VideoContentType* content_type, uint8_t simulcast_id); + +uint8_t GetExperimentId(const VideoContentType& content_type); +uint8_t GetSimulcastId(const VideoContentType& content_type); + +bool IsScreenshare(const VideoContentType& content_type); + +bool IsValidContentType(uint8_t value); + +const char* ToString(const VideoContentType& content_type); +} // namespace videocontenttypehelpers + +} // namespace webrtc + +#endif // API_VIDEO_VIDEO_CONTENT_TYPE_H_ diff --git a/webrtc/api/video/video_rotation.h b/webrtc/api/video/video_rotation.h new file mode 100644 index 0000000..6a29588 --- /dev/null +++ b/webrtc/api/video/video_rotation.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015 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. + */ + +#ifndef API_VIDEO_VIDEO_ROTATION_H_ +#define API_VIDEO_VIDEO_ROTATION_H_ + +namespace webrtc { + +// enum for clockwise rotation. +enum VideoRotation { + kVideoRotation_0 = 0, + kVideoRotation_90 = 90, + kVideoRotation_180 = 180, + kVideoRotation_270 = 270 +}; + +} // namespace webrtc + +#endif // API_VIDEO_VIDEO_ROTATION_H_ diff --git a/webrtc/api/video/video_timing.cc b/webrtc/api/video/video_timing.cc new file mode 100644 index 0000000..df1bc48 --- /dev/null +++ b/webrtc/api/video/video_timing.cc @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017 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 "api/video/video_timing.h" + +#include "api/array_view.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +uint16_t VideoSendTiming::GetDeltaCappedMs(int64_t base_ms, int64_t time_ms) { + if (time_ms < base_ms) { + RTC_DLOG(LS_ERROR) << "Delta " << (time_ms - base_ms) + << "ms expected to be positive"; + } + return rtc::saturated_cast(time_ms - base_ms); +} + +TimingFrameInfo::TimingFrameInfo() + : rtp_timestamp(0), + capture_time_ms(-1), + encode_start_ms(-1), + encode_finish_ms(-1), + packetization_finish_ms(-1), + pacer_exit_ms(-1), + network_timestamp_ms(-1), + network2_timestamp_ms(-1), + receive_start_ms(-1), + receive_finish_ms(-1), + decode_start_ms(-1), + decode_finish_ms(-1), + render_time_ms(-1), + flags(VideoSendTiming::kNotTriggered) {} + +int64_t TimingFrameInfo::EndToEndDelay() const { + return capture_time_ms >= 0 ? decode_finish_ms - capture_time_ms : -1; +} + +bool TimingFrameInfo::IsLongerThan(const TimingFrameInfo& other) const { + int64_t other_delay = other.EndToEndDelay(); + return other_delay == -1 || EndToEndDelay() > other_delay; +} + +bool TimingFrameInfo::operator<(const TimingFrameInfo& other) const { + return other.IsLongerThan(*this); +} + +bool TimingFrameInfo::operator<=(const TimingFrameInfo& other) const { + return !IsLongerThan(other); +} + +bool TimingFrameInfo::IsOutlier() const { + return !IsInvalid() && (flags & VideoSendTiming::kTriggeredBySize); +} + +bool TimingFrameInfo::IsTimerTriggered() const { + return !IsInvalid() && (flags & VideoSendTiming::kTriggeredByTimer); +} + +bool TimingFrameInfo::IsInvalid() const { + return flags == VideoSendTiming::kInvalid; +} + +std::string TimingFrameInfo::ToString() const { + if (IsInvalid()) { + return ""; + } + + char buf[1024]; + rtc::SimpleStringBuilder sb(buf); + + sb << rtp_timestamp << ',' << capture_time_ms << ',' << encode_start_ms << ',' + << encode_finish_ms << ',' << packetization_finish_ms << ',' + << pacer_exit_ms << ',' << network_timestamp_ms << ',' + << network2_timestamp_ms << ',' << receive_start_ms << ',' + << receive_finish_ms << ',' << decode_start_ms << ',' << decode_finish_ms + << ',' << render_time_ms << ',' << IsOutlier() << ',' + << IsTimerTriggered(); + + return sb.str(); +} + +} // namespace webrtc diff --git a/webrtc/api/video/video_timing.h b/webrtc/api/video/video_timing.h new file mode 100644 index 0000000..fbd9225 --- /dev/null +++ b/webrtc/api/video/video_timing.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef API_VIDEO_VIDEO_TIMING_H_ +#define API_VIDEO_VIDEO_TIMING_H_ + +#include + +#include +#include + +namespace webrtc { + +// Video timing timestamps in ms counted from capture_time_ms of a frame. +// This structure represents data sent in video-timing RTP header extension. +struct VideoSendTiming { + enum TimingFrameFlags : uint8_t { + kNotTriggered = 0, // Timing info valid, but not to be transmitted. + // Used on send-side only. + kTriggeredByTimer = 1 << 0, // Frame marked for tracing by periodic timer. + kTriggeredBySize = 1 << 1, // Frame marked for tracing due to size. + kInvalid = std::numeric_limits::max() // Invalid, ignore! + }; + + // Returns |time_ms - base_ms| capped at max 16-bit value. + // Used to fill this data structure as per + // https://webrtc.org/experiments/rtp-hdrext/video-timing/ extension stores + // 16-bit deltas of timestamps from packet capture time. + static uint16_t GetDeltaCappedMs(int64_t base_ms, int64_t time_ms); + + uint16_t encode_start_delta_ms; + uint16_t encode_finish_delta_ms; + uint16_t packetization_finish_delta_ms; + uint16_t pacer_exit_delta_ms; + uint16_t network_timestamp_delta_ms; + uint16_t network2_timestamp_delta_ms; + uint8_t flags; +}; + +// Used to report precise timings of a 'timing frames'. Contains all important +// timestamps for a lifetime of that specific frame. Reported as a string via +// GetStats(). Only frame which took the longest between two GetStats calls is +// reported. +struct TimingFrameInfo { + TimingFrameInfo(); + + // Returns end-to-end delay of a frame, if sender and receiver timestamps are + // synchronized, -1 otherwise. + int64_t EndToEndDelay() const; + + // Returns true if current frame took longer to process than |other| frame. + // If other frame's clocks are not synchronized, current frame is always + // preferred. + bool IsLongerThan(const TimingFrameInfo& other) const; + + // Returns true if flags are set to indicate this frame was marked for tracing + // due to the size being outside some limit. + bool IsOutlier() const; + + // Returns true if flags are set to indicate this frame was marked fro tracing + // due to cyclic timer. + bool IsTimerTriggered() const; + + // Returns true if the timing data is marked as invalid, in which case it + // should be ignored. + bool IsInvalid() const; + + std::string ToString() const; + + bool operator<(const TimingFrameInfo& other) const; + + bool operator<=(const TimingFrameInfo& other) const; + + uint32_t rtp_timestamp; // Identifier of a frame. + // All timestamps below are in local monotonous clock of a receiver. + // If sender clock is not yet estimated, sender timestamps + // (capture_time_ms ... pacer_exit_ms) are negative values, still + // relatively correct. + int64_t capture_time_ms; // Captrue time of a frame. + int64_t encode_start_ms; // Encode start time. + int64_t encode_finish_ms; // Encode completion time. + int64_t packetization_finish_ms; // Time when frame was passed to pacer. + int64_t pacer_exit_ms; // Time when last packet was pushed out of pacer. + // Two in-network RTP processor timestamps: meaning is application specific. + int64_t network_timestamp_ms; + int64_t network2_timestamp_ms; + int64_t receive_start_ms; // First received packet time. + int64_t receive_finish_ms; // Last received packet time. + int64_t decode_start_ms; // Decode start time. + int64_t decode_finish_ms; // Decode completion time. + int64_t render_time_ms; // Proposed render time to insure smooth playback. + + uint8_t flags; // Flags indicating validity and/or why tracing was triggered. +}; + +// Minimum and maximum playout delay values from capture to render. +// These are best effort values. +// +// A value < 0 indicates no change from previous valid value. +// +// min = max = 0 indicates that the receiver should try and render +// frame as soon as possible. +// +// min = x, max = y indicates that the receiver is free to adapt +// in the range (x, y) based on network jitter. +struct VideoPlayoutDelay { + VideoPlayoutDelay() = default; + VideoPlayoutDelay(int min_ms, int max_ms) : min_ms(min_ms), max_ms(max_ms) {} + int min_ms = -1; + int max_ms = -1; + + bool operator==(const VideoPlayoutDelay& rhs) const { + return min_ms == rhs.min_ms && max_ms == rhs.max_ms; + } +}; + +// TODO(bugs.webrtc.org/7660): Old name, delete after downstream use is updated. +using PlayoutDelay = VideoPlayoutDelay; + +} // namespace webrtc + +#endif // API_VIDEO_VIDEO_TIMING_H_ diff --git a/webrtc/audio/utility/BUILD.gn b/webrtc/audio/utility/BUILD.gn new file mode 100644 index 0000000..54ca046 --- /dev/null +++ b/webrtc/audio/utility/BUILD.gn @@ -0,0 +1,53 @@ +# 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. +import("../../webrtc.gni") + +group("utility") { + deps = [ ":audio_frame_operations" ] +} + +rtc_library("audio_frame_operations") { + visibility = [ "*" ] + sources = [ + "audio_frame_operations.cc", + "audio_frame_operations.h", + "channel_mixer.cc", + "channel_mixer.h", + "channel_mixing_matrix.cc", + "channel_mixing_matrix.h", + ] + + deps = [ + "../../api/audio:audio_frame_api", + "../../common_audio", + "../../rtc_base:checks", + "../../rtc_base:deprecation", + "../../rtc_base:rtc_base_approved", + "../../system_wrappers:field_trial", + ] +} + +if (rtc_include_tests) { + rtc_library("utility_tests") { + testonly = true + sources = [ + "audio_frame_operations_unittest.cc", + "channel_mixer_unittest.cc", + "channel_mixing_matrix_unittest.cc", + ] + deps = [ + ":audio_frame_operations", + "../../api/audio:audio_frame_api", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../test:field_trial", + "../../test:test_support", + "//testing/gtest", + ] + } +} diff --git a/webrtc/audio/utility/audio_frame_operations.cc b/webrtc/audio/utility/audio_frame_operations.cc new file mode 100644 index 0000000..a9d2cf1 --- /dev/null +++ b/webrtc/audio/utility/audio_frame_operations.cc @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2012 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/utility/audio_frame_operations.h" + +#include + +#include +#include +#include + +#include "common_audio/include/audio_util.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { +namespace { + +// 2.7ms @ 48kHz, 4ms @ 32kHz, 8ms @ 16kHz. +const size_t kMuteFadeFrames = 128; +const float kMuteFadeInc = 1.0f / kMuteFadeFrames; + +} // namespace + +void AudioFrameOperations::Add(const AudioFrame& frame_to_add, + AudioFrame* result_frame) { + // Sanity check. + RTC_DCHECK(result_frame); + RTC_DCHECK_GT(result_frame->num_channels_, 0); + RTC_DCHECK_EQ(result_frame->num_channels_, frame_to_add.num_channels_); + + bool no_previous_data = result_frame->muted(); + if (result_frame->samples_per_channel_ != frame_to_add.samples_per_channel_) { + // Special case we have no data to start with. + RTC_DCHECK_EQ(result_frame->samples_per_channel_, 0); + result_frame->samples_per_channel_ = frame_to_add.samples_per_channel_; + no_previous_data = true; + } + + if (result_frame->vad_activity_ == AudioFrame::kVadActive || + frame_to_add.vad_activity_ == AudioFrame::kVadActive) { + result_frame->vad_activity_ = AudioFrame::kVadActive; + } else if (result_frame->vad_activity_ == AudioFrame::kVadUnknown || + frame_to_add.vad_activity_ == AudioFrame::kVadUnknown) { + result_frame->vad_activity_ = AudioFrame::kVadUnknown; + } + + if (result_frame->speech_type_ != frame_to_add.speech_type_) + result_frame->speech_type_ = AudioFrame::kUndefined; + + if (!frame_to_add.muted()) { + const int16_t* in_data = frame_to_add.data(); + int16_t* out_data = result_frame->mutable_data(); + size_t length = + frame_to_add.samples_per_channel_ * frame_to_add.num_channels_; + if (no_previous_data) { + std::copy(in_data, in_data + length, out_data); + } else { + for (size_t i = 0; i < length; i++) { + const int32_t wrap_guard = static_cast(out_data[i]) + + static_cast(in_data[i]); + out_data[i] = rtc::saturated_cast(wrap_guard); + } + } + } +} + +int AudioFrameOperations::MonoToStereo(AudioFrame* frame) { + if (frame->num_channels_ != 1) { + return -1; + } + UpmixChannels(2, frame); + return 0; +} + +int AudioFrameOperations::StereoToMono(AudioFrame* frame) { + if (frame->num_channels_ != 2) { + return -1; + } + DownmixChannels(1, frame); + return frame->num_channels_ == 1 ? 0 : -1; +} + +void AudioFrameOperations::QuadToStereo(const int16_t* src_audio, + size_t samples_per_channel, + int16_t* dst_audio) { + for (size_t i = 0; i < samples_per_channel; i++) { + dst_audio[i * 2] = + (static_cast(src_audio[4 * i]) + src_audio[4 * i + 1]) >> 1; + dst_audio[i * 2 + 1] = + (static_cast(src_audio[4 * i + 2]) + src_audio[4 * i + 3]) >> + 1; + } +} + +int AudioFrameOperations::QuadToStereo(AudioFrame* frame) { + if (frame->num_channels_ != 4) { + return -1; + } + + RTC_DCHECK_LE(frame->samples_per_channel_ * 4, + AudioFrame::kMaxDataSizeSamples); + + if (!frame->muted()) { + QuadToStereo(frame->data(), frame->samples_per_channel_, + frame->mutable_data()); + } + frame->num_channels_ = 2; + + return 0; +} + +void AudioFrameOperations::DownmixChannels(const int16_t* src_audio, + size_t src_channels, + size_t samples_per_channel, + size_t dst_channels, + int16_t* dst_audio) { + if (src_channels > 1 && dst_channels == 1) { + DownmixInterleavedToMono(src_audio, samples_per_channel, src_channels, + dst_audio); + return; + } else if (src_channels == 4 && dst_channels == 2) { + QuadToStereo(src_audio, samples_per_channel, dst_audio); + return; + } + + RTC_NOTREACHED() << "src_channels: " << src_channels + << ", dst_channels: " << dst_channels; +} + +void AudioFrameOperations::DownmixChannels(size_t dst_channels, + AudioFrame* frame) { + RTC_DCHECK_LE(frame->samples_per_channel_ * frame->num_channels_, + AudioFrame::kMaxDataSizeSamples); + if (frame->num_channels_ > 1 && dst_channels == 1) { + if (!frame->muted()) { + DownmixInterleavedToMono(frame->data(), frame->samples_per_channel_, + frame->num_channels_, frame->mutable_data()); + } + frame->num_channels_ = 1; + } else if (frame->num_channels_ == 4 && dst_channels == 2) { + int err = QuadToStereo(frame); + RTC_DCHECK_EQ(err, 0); + } else { + RTC_NOTREACHED() << "src_channels: " << frame->num_channels_ + << ", dst_channels: " << dst_channels; + } +} + +void AudioFrameOperations::UpmixChannels(size_t target_number_of_channels, + AudioFrame* frame) { + RTC_DCHECK_EQ(frame->num_channels_, 1); + RTC_DCHECK_LE(frame->samples_per_channel_ * target_number_of_channels, + AudioFrame::kMaxDataSizeSamples); + + if (frame->num_channels_ != 1 || + frame->samples_per_channel_ * target_number_of_channels > + AudioFrame::kMaxDataSizeSamples) { + return; + } + + if (!frame->muted()) { + // Up-mixing done in place. Going backwards through the frame ensure nothing + // is irrevocably overwritten. + for (int i = frame->samples_per_channel_ - 1; i >= 0; i--) { + for (size_t j = 0; j < target_number_of_channels; ++j) { + frame->mutable_data()[target_number_of_channels * i + j] = + frame->data()[i]; + } + } + } + frame->num_channels_ = target_number_of_channels; +} + +void AudioFrameOperations::SwapStereoChannels(AudioFrame* frame) { + RTC_DCHECK(frame); + if (frame->num_channels_ != 2 || frame->muted()) { + return; + } + + int16_t* frame_data = frame->mutable_data(); + for (size_t i = 0; i < frame->samples_per_channel_ * 2; i += 2) { + std::swap(frame_data[i], frame_data[i + 1]); + } +} + +void AudioFrameOperations::Mute(AudioFrame* frame, + bool previous_frame_muted, + bool current_frame_muted) { + RTC_DCHECK(frame); + if (!previous_frame_muted && !current_frame_muted) { + // Not muted, don't touch. + } else if (previous_frame_muted && current_frame_muted) { + // Frame fully muted. + size_t total_samples = frame->samples_per_channel_ * frame->num_channels_; + RTC_DCHECK_GE(AudioFrame::kMaxDataSizeSamples, total_samples); + frame->Mute(); + } else { + // Fade is a no-op on a muted frame. + if (frame->muted()) { + return; + } + + // Limit number of samples to fade, if frame isn't long enough. + size_t count = kMuteFadeFrames; + float inc = kMuteFadeInc; + if (frame->samples_per_channel_ < kMuteFadeFrames) { + count = frame->samples_per_channel_; + if (count > 0) { + inc = 1.0f / count; + } + } + + size_t start = 0; + size_t end = count; + float start_g = 0.0f; + if (current_frame_muted) { + // Fade out the last |count| samples of frame. + RTC_DCHECK(!previous_frame_muted); + start = frame->samples_per_channel_ - count; + end = frame->samples_per_channel_; + start_g = 1.0f; + inc = -inc; + } else { + // Fade in the first |count| samples of frame. + RTC_DCHECK(previous_frame_muted); + } + + // Perform fade. + int16_t* frame_data = frame->mutable_data(); + size_t channels = frame->num_channels_; + for (size_t j = 0; j < channels; ++j) { + float g = start_g; + for (size_t i = start * channels; i < end * channels; i += channels) { + g += inc; + frame_data[i + j] *= g; + } + } + } +} + +void AudioFrameOperations::Mute(AudioFrame* frame) { + Mute(frame, true, true); +} + +void AudioFrameOperations::ApplyHalfGain(AudioFrame* frame) { + RTC_DCHECK(frame); + RTC_DCHECK_GT(frame->num_channels_, 0); + if (frame->num_channels_ < 1 || frame->muted()) { + return; + } + + int16_t* frame_data = frame->mutable_data(); + for (size_t i = 0; i < frame->samples_per_channel_ * frame->num_channels_; + i++) { + frame_data[i] = frame_data[i] >> 1; + } +} + +int AudioFrameOperations::Scale(float left, float right, AudioFrame* frame) { + if (frame->num_channels_ != 2) { + return -1; + } else if (frame->muted()) { + return 0; + } + + int16_t* frame_data = frame->mutable_data(); + for (size_t i = 0; i < frame->samples_per_channel_; i++) { + frame_data[2 * i] = static_cast(left * frame_data[2 * i]); + frame_data[2 * i + 1] = static_cast(right * frame_data[2 * i + 1]); + } + return 0; +} + +int AudioFrameOperations::ScaleWithSat(float scale, AudioFrame* frame) { + if (frame->muted()) { + return 0; + } + + int16_t* frame_data = frame->mutable_data(); + for (size_t i = 0; i < frame->samples_per_channel_ * frame->num_channels_; + i++) { + frame_data[i] = rtc::saturated_cast(scale * frame_data[i]); + } + return 0; +} +} // namespace webrtc diff --git a/webrtc/audio/utility/audio_frame_operations.h b/webrtc/audio/utility/audio_frame_operations.h new file mode 100644 index 0000000..65c310c --- /dev/null +++ b/webrtc/audio/utility/audio_frame_operations.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef AUDIO_UTILITY_AUDIO_FRAME_OPERATIONS_H_ +#define AUDIO_UTILITY_AUDIO_FRAME_OPERATIONS_H_ + +#include +#include + +#include "api/audio/audio_frame.h" +#include "rtc_base/deprecation.h" + +namespace webrtc { + +// TODO(andrew): consolidate this with utility.h and audio_frame_manipulator.h. +// Change reference parameters to pointers. Consider using a namespace rather +// than a class. +class AudioFrameOperations { + public: + // Add samples in |frame_to_add| with samples in |result_frame| + // putting the results in |results_frame|. The fields + // |vad_activity_| and |speech_type_| of the result frame are + // updated. If |result_frame| is empty (|samples_per_channel_|==0), + // the samples in |frame_to_add| are added to it. The number of + // channels and number of samples per channel must match except when + // |result_frame| is empty. + static void Add(const AudioFrame& frame_to_add, AudioFrame* result_frame); + + // |frame.num_channels_| will be updated. This version checks for sufficient + // buffer size and that |num_channels_| is mono. Use UpmixChannels + // instead. TODO(bugs.webrtc.org/8649): remove. + RTC_DEPRECATED static int MonoToStereo(AudioFrame* frame); + + // |frame.num_channels_| will be updated. This version checks that + // |num_channels_| is stereo. Use DownmixChannels + // instead. TODO(bugs.webrtc.org/8649): remove. + RTC_DEPRECATED static int StereoToMono(AudioFrame* frame); + + // Downmixes 4 channels |src_audio| to stereo |dst_audio|. This is an in-place + // operation, meaning |src_audio| and |dst_audio| may point to the same + // buffer. + static void QuadToStereo(const int16_t* src_audio, + size_t samples_per_channel, + int16_t* dst_audio); + + // |frame.num_channels_| will be updated. This version checks that + // |num_channels_| is 4 channels. + static int QuadToStereo(AudioFrame* frame); + + // Downmixes |src_channels| |src_audio| to |dst_channels| |dst_audio|. + // This is an in-place operation, meaning |src_audio| and |dst_audio| + // may point to the same buffer. Supported channel combinations are + // Stereo to Mono, Quad to Mono, and Quad to Stereo. + static void DownmixChannels(const int16_t* src_audio, + size_t src_channels, + size_t samples_per_channel, + size_t dst_channels, + int16_t* dst_audio); + + // |frame.num_channels_| will be updated. This version checks that + // |num_channels_| and |dst_channels| are valid and performs relevant downmix. + // Supported channel combinations are N channels to Mono, and Quad to Stereo. + static void DownmixChannels(size_t dst_channels, AudioFrame* frame); + + // |frame.num_channels_| will be updated. This version checks that + // |num_channels_| and |dst_channels| are valid and performs relevant + // downmix. Supported channel combinations are Mono to N + // channels. The single channel is replicated. + static void UpmixChannels(size_t target_number_of_channels, + AudioFrame* frame); + + // Swap the left and right channels of |frame|. Fails silently if |frame| is + // not stereo. + static void SwapStereoChannels(AudioFrame* frame); + + // Conditionally zero out contents of |frame| for implementing audio mute: + // |previous_frame_muted| && |current_frame_muted| - Zero out whole frame. + // |previous_frame_muted| && !|current_frame_muted| - Fade-in at frame start. + // !|previous_frame_muted| && |current_frame_muted| - Fade-out at frame end. + // !|previous_frame_muted| && !|current_frame_muted| - Leave frame untouched. + static void Mute(AudioFrame* frame, + bool previous_frame_muted, + bool current_frame_muted); + + // Zero out contents of frame. + static void Mute(AudioFrame* frame); + + // Halve samples in |frame|. + static void ApplyHalfGain(AudioFrame* frame); + + static int Scale(float left, float right, AudioFrame* frame); + + static int ScaleWithSat(float scale, AudioFrame* frame); +}; + +} // namespace webrtc + +#endif // AUDIO_UTILITY_AUDIO_FRAME_OPERATIONS_H_ diff --git a/webrtc/base/BUILD.gn b/webrtc/base/BUILD.gn deleted file mode 100644 index 11a2664..0000000 --- a/webrtc/base/BUILD.gn +++ /dev/null @@ -1,596 +0,0 @@ -# Copyright (c) 2014 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. - -import("//build/config/crypto.gni") -import("//build/config/ui.gni") -import("../build/webrtc.gni") - -config("rtc_base_config") { - include_dirs = [ - "//third_party/jsoncpp/overrides/include", - "//third_party/jsoncpp/source/include", - ] - - defines = [ - "FEATURE_ENABLE_SSL", - "LOGGING=1", - ] - - if (is_posix) { - # TODO(henrike): issue 3307, make rtc_base build without disabling - # these flags. - cflags_cc = [ "-Wno-non-virtual-dtor" ] - } -} - -config("rtc_base_chromium_config") { - defines = [ "NO_MAIN_THREAD_WRAPPING" ] -} - -config("openssl_config") { - defines = [ - "SSL_USE_OPENSSL", - "HAVE_OPENSSL_SSL_H", - ] -} - -config("ios_config") { - libs = [ - "CFNetwork.framework", - - #"Foundation.framework", # Already included in //build/config:default_libs. - "Security.framework", - "SystemConfiguration.framework", - - #"UIKit.framework", # Already included in //build/config:default_libs. - ] -} - -config("mac_config") { - libs = [ - "Cocoa.framework", - - #"Foundation.framework", # Already included in //build/config:default_libs. - #"IOKit.framework", # Already included in //build/config:default_libs. - #"Security.framework", # Already included in //build/config:default_libs. - "SystemConfiguration.framework", - ] -} - -config("mac_x86_config") { - libs = [ - #"Carbon.framework", # Already included in //build/config:default_libs. - ] -} - -if (is_linux && !build_with_chromium) { - # Provides the same functionality as the //crypto:platform target, which - # WebRTC cannot use as we don't sync src/crypto from Chromium. - group("linux_system_ssl") { - if (use_openssl) { - deps = [ - "//third_party/boringssl", - ] - } - } -} - -if (rtc_build_ssl == 0) { - config("external_ssl_library") { - assert(rtc_ssl_root != "", - "You must specify rtc_ssl_root when rtc_build_ssl==0.") - include_dirs = [ rtc_ssl_root ] - } -} - -# The subset of rtc_base approved for use outside of libjingle. -static_library("rtc_base_approved") { - configs += [ "..:common_config" ] - public_configs = [ "..:common_inherited_config" ] - - sources = [ - "array_view.h", - "atomicops.h", - "bitbuffer.cc", - "bitbuffer.h", - "buffer.cc", - "buffer.h", - "bufferqueue.cc", - "bufferqueue.h", - "bytebuffer.cc", - "bytebuffer.h", - "byteorder.h", - "checks.cc", - "checks.h", - "criticalsection.cc", - "criticalsection.h", - "event.cc", - "event.h", - "event_tracer.cc", - "event_tracer.h", - "exp_filter.cc", - "exp_filter.h", - "maybe.h", - "md5.cc", - "md5.h", - "md5digest.cc", - "md5digest.h", - "platform_file.cc", - "platform_file.h", - "platform_thread.cc", - "platform_thread.h", - "safe_conversions.h", - "safe_conversions_impl.h", - "scoped_ptr.h", - "stringencode.cc", - "stringencode.h", - "stringutils.cc", - "stringutils.h", - "systeminfo.cc", - "systeminfo.h", - "template_util.h", - "thread_annotations.h", - "thread_checker.h", - "thread_checker_impl.cc", - "thread_checker_impl.h", - "timeutils.cc", - "timeutils.h", - "trace_event.h", - ] - - if (!build_with_chromium) { - sources += [ - "basictypes.h", - "constructormagic.h", - "logging.cc", - "logging.h", - ] - } -} - -static_library("rtc_base") { - cflags = [] - cflags_cc = [] - libs = [] - deps = [ - ":rtc_base_approved", - ] - - configs += [ - "..:common_config", - ":rtc_base_config", - ] - - public_configs = [ - "..:common_inherited_config", - ":rtc_base_config", - ] - - defines = [ "LOGGING=1" ] - - sources = [ - "arraysize.h", - "asyncfile.cc", - "asyncfile.h", - "asyncinvoker-inl.h", - "asyncinvoker.cc", - "asyncinvoker.h", - "asyncpacketsocket.cc", - "asyncpacketsocket.h", - "asyncresolverinterface.cc", - "asyncresolverinterface.h", - "asyncsocket.cc", - "asyncsocket.h", - "asynctcpsocket.cc", - "asynctcpsocket.h", - "asyncudpsocket.cc", - "asyncudpsocket.h", - "autodetectproxy.cc", - "autodetectproxy.h", - "base64.cc", - "base64.h", - "basicdefs.h", - "common.cc", - "common.h", - "crc32.cc", - "crc32.h", - "cryptstring.cc", - "cryptstring.h", - "diskcache.cc", - "diskcache.h", - "filerotatingstream.cc", - "filerotatingstream.h", - "fileutils.cc", - "fileutils.h", - "firewallsocketserver.cc", - "firewallsocketserver.h", - "flags.cc", - "flags.h", - "format_macros.h", - "gunit_prod.h", - "helpers.cc", - "helpers.h", - "httpbase.cc", - "httpbase.h", - "httpclient.cc", - "httpclient.h", - "httpcommon-inl.h", - "httpcommon.cc", - "httpcommon.h", - "httprequest.cc", - "httprequest.h", - "iosfilesystem.mm", - "ipaddress.cc", - "ipaddress.h", - "linked_ptr.h", - "mathutils.h", - "messagedigest.cc", - "messagedigest.h", - "messagehandler.cc", - "messagehandler.h", - "messagequeue.cc", - "messagequeue.h", - "nethelpers.cc", - "nethelpers.h", - "network.cc", - "network.h", - "networkmonitor.cc", - "networkmonitor.h", - "nullsocketserver.h", - "pathutils.cc", - "pathutils.h", - "physicalsocketserver.cc", - "physicalsocketserver.h", - "proxydetect.cc", - "proxydetect.h", - "proxyinfo.cc", - "proxyinfo.h", - "ratelimiter.cc", - "ratelimiter.h", - "ratetracker.cc", - "ratetracker.h", - "rtccertificate.cc", - "rtccertificate.h", - "scoped_autorelease_pool.h", - "scoped_autorelease_pool.mm", - "sha1.cc", - "sha1.h", - "sha1digest.cc", - "sha1digest.h", - "signalthread.cc", - "signalthread.h", - "sigslot.cc", - "sigslot.h", - "sigslotrepeater.h", - "socket.h", - "socketadapters.cc", - "socketadapters.h", - "socketaddress.cc", - "socketaddress.h", - "socketaddresspair.cc", - "socketaddresspair.h", - "socketfactory.h", - "socketpool.cc", - "socketpool.h", - "socketserver.h", - "socketstream.cc", - "socketstream.h", - "ssladapter.cc", - "ssladapter.h", - "sslfingerprint.cc", - "sslfingerprint.h", - "sslidentity.cc", - "sslidentity.h", - "sslsocketfactory.cc", - "sslsocketfactory.h", - "sslstreamadapter.cc", - "sslstreamadapter.h", - "sslstreamadapterhelper.cc", - "sslstreamadapterhelper.h", - "stream.cc", - "stream.h", - "task.cc", - "task.h", - "taskparent.cc", - "taskparent.h", - "taskrunner.cc", - "taskrunner.h", - "thread.cc", - "thread.h", - "timing.cc", - "timing.h", - "urlencode.cc", - "urlencode.h", - "worker.cc", - "worker.h", - ] - - if (is_posix) { - sources += [ - "unixfilesystem.cc", - "unixfilesystem.h", - ] - } - - if (build_with_chromium) { - sources += [ - "../../webrtc_overrides/webrtc/base/logging.cc", - "../../webrtc_overrides/webrtc/base/logging.h", - ] - - deps += [ "..:webrtc_common" ] - - if (is_win) { - sources += [ "../../webrtc_overrides/webrtc/base/win32socketinit.cc" ] - } - - include_dirs = [ - "../../webrtc_overrides", - "../../boringssl/src/include", - ] - - public_configs += [ ":rtc_base_chromium_config" ] - } else { - sources += [ - "bandwidthsmoother.cc", - "bandwidthsmoother.h", - "bind.h", - "bind.h.pump", - "callback.h", - "callback.h.pump", - "fileutils_mock.h", - "genericslot.h", - "genericslot.h.pump", - "httpserver.cc", - "httpserver.h", - "json.cc", - "json.h", - "logsinks.cc", - "logsinks.h", - "mathutils.h", - "multipart.cc", - "multipart.h", - "natserver.cc", - "natserver.h", - "natsocketfactory.cc", - "natsocketfactory.h", - "nattypes.cc", - "nattypes.h", - "optionsfile.cc", - "optionsfile.h", - "profiler.cc", - "profiler.h", - "proxyserver.cc", - "proxyserver.h", - "refcount.h", - "referencecountedsingletonfactory.h", - "rollingaccumulator.h", - "scoped_ref_ptr.h", - "scopedptrcollection.h", - "sec_buffer.h", - "sharedexclusivelock.cc", - "sharedexclusivelock.h", - "sslconfig.h", - "sslroots.h", - "testclient.cc", - "testclient.h", - "transformadapter.cc", - "transformadapter.h", - "versionparsing.cc", - "versionparsing.h", - "virtualsocketserver.cc", - "virtualsocketserver.h", - "window.h", - "windowpicker.h", - "windowpickerfactory.h", - ] - - deps += [ "..:webrtc_common" ] - - if (is_posix) { - sources += [ - "latebindingsymboltable.cc", - "latebindingsymboltable.cc.def", - "latebindingsymboltable.h", - "latebindingsymboltable.h.def", - "posix.cc", - "posix.h", - ] - } - - if (is_linux) { - sources += [ - "dbus.cc", - "dbus.h", - "libdbusglibsymboltable.cc", - "libdbusglibsymboltable.h", - "linuxfdwalk.c", - "linuxfdwalk.h", - ] - } - - if (is_mac) { - sources += [ - "macasyncsocket.cc", - "macasyncsocket.h", - "maccocoasocketserver.h", - "maccocoasocketserver.mm", - "macsocketserver.cc", - "macsocketserver.h", - "macwindowpicker.cc", - "macwindowpicker.h", - ] - } - - if (is_win) { - sources += [ - "diskcache_win32.cc", - "diskcache_win32.h", - "win32regkey.cc", - "win32regkey.h", - "win32socketinit.cc", - "win32socketinit.h", - "win32socketserver.cc", - "win32socketserver.h", - ] - } - if (rtc_build_json) { - deps += [ "//third_party/jsoncpp" ] - } else { - include_dirs += [ rtc_jsoncpp_root ] - - # When defined changes the include path for json.h to where it is - # expected to be when building json outside of the standalone build. - defines += [ "WEBRTC_EXTERNAL_JSON" ] - } - } # !build_with_chromium - - # TODO(henrike): issue 3307, make rtc_base build with the Chromium default - # compiler settings. - configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ "//build/config/compiler:no_chromium_code" ] - if (!is_win) { - cflags += [ "-Wno-uninitialized" ] - cflags_cc += [ "-Wno-non-virtual-dtor" ] - } - - if (use_openssl) { - public_configs += [ ":openssl_config" ] - if (rtc_build_ssl) { - deps += [ "//third_party/boringssl" ] - } else { - configs += [ "external_ssl_library" ] - } - sources += [ - "openssl.h", - "openssladapter.cc", - "openssladapter.h", - "openssldigest.cc", - "openssldigest.h", - "opensslidentity.cc", - "opensslidentity.h", - "opensslstreamadapter.cc", - "opensslstreamadapter.h", - ] - } - - if (is_android) { - sources += [ - "ifaddrs-android.cc", - "ifaddrs-android.h", - ] - - libs += [ - "log", - "GLESv2", - ] - } - - if (is_ios) { - all_dependent_configs = [ ":ios_config" ] - - sources += [ - "macconversion.cc", - "macconversion.h", - ] - } - - if (use_x11) { - sources += [ - "x11windowpicker.cc", - "x11windowpicker.h", - ] - libs += [ - "dl", - "rt", - "Xext", - "X11", - "Xcomposite", - "Xrender", - ] - } - - if (is_linux) { - libs += [ - "dl", - "rt", - ] - } - - if (is_mac) { - sources += [ - "maccocoathreadhelper.h", - "maccocoathreadhelper.mm", - "macconversion.cc", - "macconversion.h", - "macutils.cc", - "macutils.h", - ] - - all_dependent_configs = [ ":mac_config" ] - - if (current_cpu == "x86") { - all_dependent_configs += [ ":mac_x86_config" ] - } - } - - if (is_win) { - sources += [ - "win32.cc", - "win32.h", - "win32filesystem.cc", - "win32filesystem.h", - "win32securityerrors.cc", - "win32window.cc", - "win32window.h", - "win32windowpicker.cc", - "win32windowpicker.h", - "winfirewall.cc", - "winfirewall.h", - "winping.cc", - "winping.h", - ] - - libs += [ - "crypt32.lib", - "iphlpapi.lib", - "secur32.lib", - ] - - cflags += [ - # Suppress warnings about WIN32_LEAN_AND_MEAN. - "/wd4005", - "/wd4703", - ] - - defines += [ "_CRT_NONSTDC_NO_DEPRECATE" ] - } - - if (is_posix && is_debug) { - # The Chromium build/common.gypi defines this for all posix - # _except_ for ios & mac. We want it there as well, e.g. - # because ASSERT and friends trigger off of it. - defines += [ "_DEBUG" ] - } - - if (is_ios || (is_mac && current_cpu != "x86")) { - defines += [ "CARBON_DEPRECATED=YES" ] - } - - if (is_linux || is_android) { - sources += [ - "linux.cc", - "linux.h", - ] - } - - if (is_nacl) { - deps += [ "//native_client_sdk/src/libraries/nacl_io" ] - defines += [ "timezone=_timezone" ] - } -} diff --git a/webrtc/base/basictypes.h b/webrtc/base/basictypes.h deleted file mode 100644 index 4c3d5d1..0000000 --- a/webrtc/base/basictypes.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2004 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. - */ - -#ifndef WEBRTC_BASE_BASICTYPES_H_ -#define WEBRTC_BASE_BASICTYPES_H_ - -#include // for NULL, size_t -#include // for uintptr_t and (u)int_t types. - -#ifdef HAVE_CONFIG_H -#include "config.h" // NOLINT -#endif - -// Detect compiler is for x86 or x64. -#if defined(__x86_64__) || defined(_M_X64) || \ - defined(__i386__) || defined(_M_IX86) -#define CPU_X86 1 -#endif - -// Detect compiler is for arm. -#if defined(__arm__) || defined(_M_ARM) -#define CPU_ARM 1 -#endif - -#if defined(CPU_X86) && defined(CPU_ARM) -#error CPU_X86 and CPU_ARM both defined. -#endif - -#if !defined(RTC_ARCH_CPU_BIG_ENDIAN) && !defined(RTC_ARCH_CPU_LITTLE_ENDIAN) -// x86, arm or GCC provided __BYTE_ORDER__ macros -#if CPU_X86 || CPU_ARM || \ - (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) -#define RTC_ARCH_CPU_LITTLE_ENDIAN -#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#define RTC_ARCH_CPU_BIG_ENDIAN -#else -#error RTC_ARCH_CPU_BIG_ENDIAN or RTC_ARCH_CPU_LITTLE_ENDIAN should be defined. -#endif -#endif - -#if defined(RTC_ARCH_CPU_BIG_ENDIAN) && defined(RTC_ARCH_CPU_LITTLE_ENDIAN) -#error RTC_ARCH_CPU_BIG_ENDIAN and RTC_ARCH_CPU_LITTLE_ENDIAN both defined. -#endif - -#if defined(WEBRTC_WIN) -typedef int socklen_t; -#endif - -// The following only works for C++ -#ifdef __cplusplus - -#ifndef ALIGNP -#define ALIGNP(p, t) \ - (reinterpret_cast(((reinterpret_cast(p) + \ - ((t) - 1)) & ~((t) - 1)))) -#endif - -#define RTC_IS_ALIGNED(p, a) (!((uintptr_t)(p) & ((a) - 1))) - -// Use these to declare and define a static local variable that gets leaked so -// that its destructors are not called at exit. -#define RTC_DEFINE_STATIC_LOCAL(type, name, arguments) \ - static type& name = *new type arguments - -#endif // __cplusplus - -#endif // WEBRTC_BASE_BASICTYPES_H_ diff --git a/webrtc/base/checks.cc b/webrtc/base/checks.cc deleted file mode 100644 index 49a31f2..0000000 --- a/webrtc/base/checks.cc +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2006 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. - */ - -// Most of this was borrowed (with minor modifications) from V8's and Chromium's -// src/base/logging.cc. - -// Use the C++ version to provide __GLIBCXX__. -#include -#include -#include - -#if defined(__GLIBCXX__) && !defined(__UCLIBC__) -#include -#include -#endif - -#if defined(WEBRTC_ANDROID) -#define LOG_TAG "rtc" -#include // NOLINT -#endif - -#include "webrtc/base/checks.h" - -#if defined(_MSC_VER) -// Warning C4722: destructor never returns, potential memory leak. -// FatalMessage's dtor very intentionally aborts. -#pragma warning(disable:4722) -#endif - -namespace rtc { - -void VPrintError(const char* format, va_list args) { -#if defined(WEBRTC_ANDROID) - __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args); -#else - vfprintf(stderr, format, args); -#endif -} - -void PrintError(const char* format, ...) { - va_list args; - va_start(args, format); - VPrintError(format, args); - va_end(args); -} - -// TODO(ajm): This works on Mac (although the parsing fails) but I don't seem -// to get usable symbols on Linux. This is copied from V8. Chromium has a more -// advanced stace trace system; also more difficult to copy. -void DumpBacktrace() { -#if defined(__GLIBCXX__) && !defined(__UCLIBC__) - void* trace[100]; - int size = backtrace(trace, sizeof(trace) / sizeof(*trace)); - char** symbols = backtrace_symbols(trace, size); - PrintError("\n==== C stack trace ===============================\n\n"); - if (size == 0) { - PrintError("(empty)\n"); - } else if (symbols == NULL) { - PrintError("(no symbols)\n"); - } else { - for (int i = 1; i < size; ++i) { - char mangled[201]; - if (sscanf(symbols[i], "%*[^(]%*[(]%200[^)+]", mangled) == 1) { // NOLINT - PrintError("%2d: ", i); - int status; - size_t length; - char* demangled = abi::__cxa_demangle(mangled, NULL, &length, &status); - PrintError("%s\n", demangled != NULL ? demangled : mangled); - free(demangled); - } else { - // If parsing failed, at least print the unparsed symbol. - PrintError("%s\n", symbols[i]); - } - } - } - free(symbols); -#endif -} - -FatalMessage::FatalMessage(const char* file, int line) { - Init(file, line); -} - -FatalMessage::FatalMessage(const char* file, int line, std::string* result) { - Init(file, line); - stream_ << "Check failed: " << *result << std::endl << "# "; - delete result; -} - -NO_RETURN FatalMessage::~FatalMessage() { - fflush(stdout); - fflush(stderr); - stream_ << std::endl << "#" << std::endl; - PrintError(stream_.str().c_str()); - DumpBacktrace(); - fflush(stderr); - abort(); -} - -void FatalMessage::Init(const char* file, int line) { - stream_ << std::endl << std::endl << "#" << std::endl << "# Fatal error in " - << file << ", line " << line << std::endl << "# "; -} - -// MSVC doesn't like complex extern templates and DLLs. -#if !defined(COMPILER_MSVC) -// Explicit instantiations for commonly used comparisons. -template std::string* MakeCheckOpString( - const int&, const int&, const char* names); -template std::string* MakeCheckOpString( - const unsigned long&, const unsigned long&, const char* names); -template std::string* MakeCheckOpString( - const unsigned long&, const unsigned int&, const char* names); -template std::string* MakeCheckOpString( - const unsigned int&, const unsigned long&, const char* names); -template std::string* MakeCheckOpString( - const std::string&, const std::string&, const char* name); -#endif - -} // namespace rtc diff --git a/webrtc/base/checks.h b/webrtc/base/checks.h deleted file mode 100644 index 681361a..0000000 --- a/webrtc/base/checks.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2006 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. - */ - -#ifndef WEBRTC_BASE_CHECKS_H_ -#define WEBRTC_BASE_CHECKS_H_ - -#include -#include - -#include "webrtc/typedefs.h" - -// The macros here print a message to stderr and abort under various -// conditions. All will accept additional stream messages. For example: -// RTC_DCHECK_EQ(foo, bar) << "I'm printed when foo != bar."; -// -// - RTC_CHECK(x) is an assertion that x is always true, and that if it isn't, -// it's better to terminate the process than to continue. During development, -// the reason that it's better to terminate might simply be that the error -// handling code isn't in place yet; in production, the reason might be that -// the author of the code truly believes that x will always be true, but that -// she recognizes that if she is wrong, abrupt and unpleasant process -// termination is still better than carrying on with the assumption violated. -// -// RTC_CHECK always evaluates its argument, so it's OK for x to have side -// effects. -// -// - RTC_DCHECK(x) is the same as RTC_CHECK(x)---an assertion that x is always -// true---except that x will only be evaluated in debug builds; in production -// builds, x is simply assumed to be true. This is useful if evaluating x is -// expensive and the expected cost of failing to detect the violated -// assumption is acceptable. You should not handle cases where a production -// build fails to spot a violated condition, even those that would result in -// crashes. If the code needs to cope with the error, make it cope, but don't -// call RTC_DCHECK; if the condition really can't occur, but you'd sleep -// better at night knowing that the process will suicide instead of carrying -// on in case you were wrong, use RTC_CHECK instead of RTC_DCHECK. -// -// RTC_DCHECK only evaluates its argument in debug builds, so if x has visible -// side effects, you need to write e.g. -// bool w = x; RTC_DCHECK(w); -// -// - RTC_CHECK_EQ, _NE, _GT, ..., and RTC_DCHECK_EQ, _NE, _GT, ... are -// specialized variants of RTC_CHECK and RTC_DCHECK that print prettier -// messages if the condition doesn't hold. Prefer them to raw RTC_CHECK and -// RTC_DCHECK. -// -// - FATAL() aborts unconditionally. -// -// TODO(ajm): Ideally, checks.h would be combined with logging.h, but -// consolidation with system_wrappers/logging.h should happen first. - -namespace rtc { - -// Helper macro which avoids evaluating the arguments to a stream if -// the condition doesn't hold. -#define RTC_LAZY_STREAM(stream, condition) \ - !(condition) ? static_cast(0) : rtc::FatalMessageVoidify() & (stream) - -// The actual stream used isn't important. We reference condition in the code -// but don't evaluate it; this is to avoid "unused variable" warnings (we do so -// in a particularly convoluted way with an extra ?: because that appears to be -// the simplest construct that keeps Visual Studio from complaining about -// condition being unused). -#define RTC_EAT_STREAM_PARAMETERS(condition) \ - (true ? true : !(condition)) \ - ? static_cast(0) \ - : rtc::FatalMessageVoidify() & rtc::FatalMessage("", 0).stream() - -// RTC_CHECK dies with a fatal error if condition is not true. It is *not* -// controlled by NDEBUG, so the check will be executed regardless of -// compilation mode. -// -// We make sure RTC_CHECK et al. always evaluates their arguments, as -// doing RTC_CHECK(FunctionWithSideEffect()) is a common idiom. -#define RTC_CHECK(condition) \ - RTC_LAZY_STREAM(rtc::FatalMessage(__FILE__, __LINE__).stream(), \ - !(condition)) \ - << "Check failed: " #condition << std::endl << "# " - -// Helper macro for binary operators. -// Don't use this macro directly in your code, use RTC_CHECK_EQ et al below. -// -// TODO(akalin): Rewrite this so that constructs like if (...) -// RTC_CHECK_EQ(...) else { ... } work properly. -#define RTC_CHECK_OP(name, op, val1, val2) \ - if (std::string* _result = \ - rtc::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \ - rtc::FatalMessage(__FILE__, __LINE__, _result).stream() - -// Build the error message string. This is separate from the "Impl" -// function template because it is not performance critical and so can -// be out of line, while the "Impl" code should be inline. Caller -// takes ownership of the returned string. -template -std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { - std::ostringstream ss; - ss << names << " (" << v1 << " vs. " << v2 << ")"; - std::string* msg = new std::string(ss.str()); - return msg; -} - -// MSVC doesn't like complex extern templates and DLLs. -#if !defined(COMPILER_MSVC) -// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated -// in logging.cc. -extern template std::string* MakeCheckOpString( - const int&, const int&, const char* names); -extern template -std::string* MakeCheckOpString( - const unsigned long&, const unsigned long&, const char* names); -extern template -std::string* MakeCheckOpString( - const unsigned long&, const unsigned int&, const char* names); -extern template -std::string* MakeCheckOpString( - const unsigned int&, const unsigned long&, const char* names); -extern template -std::string* MakeCheckOpString( - const std::string&, const std::string&, const char* name); -#endif - -// Helper functions for RTC_CHECK_OP macro. -// The (int, int) specialization works around the issue that the compiler -// will not instantiate the template version of the function on values of -// unnamed enum type - see comment below. -#define DEFINE_RTC_CHECK_OP_IMPL(name, op) \ - template \ - inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ - const char* names) { \ - if (v1 op v2) \ - return NULL; \ - else \ - return rtc::MakeCheckOpString(v1, v2, names); \ - } \ - inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ - if (v1 op v2) \ - return NULL; \ - else \ - return rtc::MakeCheckOpString(v1, v2, names); \ - } -DEFINE_RTC_CHECK_OP_IMPL(EQ, ==) -DEFINE_RTC_CHECK_OP_IMPL(NE, !=) -DEFINE_RTC_CHECK_OP_IMPL(LE, <=) -DEFINE_RTC_CHECK_OP_IMPL(LT, < ) -DEFINE_RTC_CHECK_OP_IMPL(GE, >=) -DEFINE_RTC_CHECK_OP_IMPL(GT, > ) -#undef DEFINE_RTC_CHECK_OP_IMPL - -#define RTC_CHECK_EQ(val1, val2) RTC_CHECK_OP(EQ, ==, val1, val2) -#define RTC_CHECK_NE(val1, val2) RTC_CHECK_OP(NE, !=, val1, val2) -#define RTC_CHECK_LE(val1, val2) RTC_CHECK_OP(LE, <=, val1, val2) -#define RTC_CHECK_LT(val1, val2) RTC_CHECK_OP(LT, < , val1, val2) -#define RTC_CHECK_GE(val1, val2) RTC_CHECK_OP(GE, >=, val1, val2) -#define RTC_CHECK_GT(val1, val2) RTC_CHECK_OP(GT, > , val1, val2) - -// The RTC_DCHECK macro is equivalent to RTC_CHECK except that it only generates -// code in debug builds. It does reference the condition parameter in all cases, -// though, so callers won't risk getting warnings about unused variables. -#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) -#define RTC_DCHECK_IS_ON 1 -#define RTC_DCHECK(condition) RTC_CHECK(condition) -#define RTC_DCHECK_EQ(v1, v2) RTC_CHECK_EQ(v1, v2) -#define RTC_DCHECK_NE(v1, v2) RTC_CHECK_NE(v1, v2) -#define RTC_DCHECK_LE(v1, v2) RTC_CHECK_LE(v1, v2) -#define RTC_DCHECK_LT(v1, v2) RTC_CHECK_LT(v1, v2) -#define RTC_DCHECK_GE(v1, v2) RTC_CHECK_GE(v1, v2) -#define RTC_DCHECK_GT(v1, v2) RTC_CHECK_GT(v1, v2) -#else -#define RTC_DCHECK_IS_ON 0 -#define RTC_DCHECK(condition) RTC_EAT_STREAM_PARAMETERS(condition) -#define RTC_DCHECK_EQ(v1, v2) RTC_EAT_STREAM_PARAMETERS((v1) == (v2)) -#define RTC_DCHECK_NE(v1, v2) RTC_EAT_STREAM_PARAMETERS((v1) != (v2)) -#define RTC_DCHECK_LE(v1, v2) RTC_EAT_STREAM_PARAMETERS((v1) <= (v2)) -#define RTC_DCHECK_LT(v1, v2) RTC_EAT_STREAM_PARAMETERS((v1) < (v2)) -#define RTC_DCHECK_GE(v1, v2) RTC_EAT_STREAM_PARAMETERS((v1) >= (v2)) -#define RTC_DCHECK_GT(v1, v2) RTC_EAT_STREAM_PARAMETERS((v1) > (v2)) -#endif - -// This is identical to LogMessageVoidify but in name. -class FatalMessageVoidify { - public: - FatalMessageVoidify() { } - // This has to be an operator with a precedence lower than << but - // higher than ?: - void operator&(std::ostream&) { } -}; - -#define RTC_UNREACHABLE_CODE_HIT false -#define RTC_NOTREACHED() RTC_DCHECK(RTC_UNREACHABLE_CODE_HIT) - -#define FATAL() rtc::FatalMessage(__FILE__, __LINE__).stream() -// TODO(ajm): Consider adding RTC_NOTIMPLEMENTED macro when -// base/logging.h and system_wrappers/logging.h are consolidated such that we -// can match the Chromium behavior. - -// Like a stripped-down LogMessage from logging.h, except that it aborts. -class FatalMessage { - public: - FatalMessage(const char* file, int line); - // Used for RTC_CHECK_EQ(), etc. Takes ownership of the given string. - FatalMessage(const char* file, int line, std::string* result); - NO_RETURN ~FatalMessage(); - - std::ostream& stream() { return stream_; } - - private: - void Init(const char* file, int line); - - std::ostringstream stream_; -}; - -// Performs the integer division a/b and returns the result. CHECKs that the -// remainder is zero. -template -inline T CheckedDivExact(T a, T b) { - RTC_CHECK_EQ(a % b, static_cast(0)); - return a / b; -} - -} // namespace rtc - -#endif // WEBRTC_BASE_CHECKS_H_ diff --git a/webrtc/base/constructormagic.h b/webrtc/base/constructormagic.h deleted file mode 100644 index 6ef7826..0000000 --- a/webrtc/base/constructormagic.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2004 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. - */ - -#ifndef WEBRTC_BASE_CONSTRUCTORMAGIC_H_ -#define WEBRTC_BASE_CONSTRUCTORMAGIC_H_ - -// Put this in the declarations for a class to be unassignable. -#define RTC_DISALLOW_ASSIGN(TypeName) \ - void operator=(const TypeName&) = delete - -// A macro to disallow the copy constructor and operator= functions. This should -// be used in the declarations for a class. -#define RTC_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) = delete; \ - RTC_DISALLOW_ASSIGN(TypeName) - -// A macro to disallow all the implicit constructors, namely the default -// constructor, copy constructor and operator= functions. -// -// This should be used in the declarations for a class that wants to prevent -// anyone from instantiating it. This is especially useful for classes -// containing only static methods. -#define RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ - TypeName() = delete; \ - RTC_DISALLOW_COPY_AND_ASSIGN(TypeName) - -#endif // WEBRTC_BASE_CONSTRUCTORMAGIC_H_ diff --git a/webrtc/base/criticalsection.cc b/webrtc/base/criticalsection.cc deleted file mode 100644 index 1f50c23..0000000 --- a/webrtc/base/criticalsection.cc +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2015 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 "webrtc/base/criticalsection.h" - -#include "webrtc/base/checks.h" - -namespace rtc { - -CriticalSection::CriticalSection() { -#if defined(WEBRTC_WIN) - InitializeCriticalSection(&crit_); -#else - pthread_mutexattr_t mutex_attribute; - pthread_mutexattr_init(&mutex_attribute); - pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&mutex_, &mutex_attribute); - pthread_mutexattr_destroy(&mutex_attribute); - CS_DEBUG_CODE(thread_ = 0); - CS_DEBUG_CODE(recursion_count_ = 0); -#endif -} - -CriticalSection::~CriticalSection() { -#if defined(WEBRTC_WIN) - DeleteCriticalSection(&crit_); -#else - pthread_mutex_destroy(&mutex_); -#endif -} - -void CriticalSection::Enter() EXCLUSIVE_LOCK_FUNCTION() { -#if defined(WEBRTC_WIN) - EnterCriticalSection(&crit_); -#else - pthread_mutex_lock(&mutex_); -#if CS_DEBUG_CHECKS - if (!recursion_count_) { - RTC_DCHECK(!thread_); - thread_ = pthread_self(); - } else { - RTC_DCHECK(CurrentThreadIsOwner()); - } - ++recursion_count_; -#endif -#endif -} - -bool CriticalSection::TryEnter() EXCLUSIVE_TRYLOCK_FUNCTION(true) { -#if defined(WEBRTC_WIN) - return TryEnterCriticalSection(&crit_) != FALSE; -#else - if (pthread_mutex_trylock(&mutex_) != 0) - return false; -#if CS_DEBUG_CHECKS - if (!recursion_count_) { - RTC_DCHECK(!thread_); - thread_ = pthread_self(); - } else { - RTC_DCHECK(CurrentThreadIsOwner()); - } - ++recursion_count_; -#endif - return true; -#endif -} -void CriticalSection::Leave() UNLOCK_FUNCTION() { - RTC_DCHECK(CurrentThreadIsOwner()); -#if defined(WEBRTC_WIN) - LeaveCriticalSection(&crit_); -#else -#if CS_DEBUG_CHECKS - --recursion_count_; - RTC_DCHECK(recursion_count_ >= 0); - if (!recursion_count_) - thread_ = 0; -#endif - pthread_mutex_unlock(&mutex_); -#endif -} - -bool CriticalSection::CurrentThreadIsOwner() const { -#if defined(WEBRTC_WIN) - // OwningThread has type HANDLE but actually contains the Thread ID: - // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de - // Converting through size_t avoids the VS 2015 warning C4312: conversion from - // 'type1' to 'type2' of greater size - return crit_.OwningThread == - reinterpret_cast(static_cast(GetCurrentThreadId())); -#else -#if CS_DEBUG_CHECKS - return pthread_equal(thread_, pthread_self()); -#else - return true; -#endif // CS_DEBUG_CHECKS -#endif -} - -bool CriticalSection::IsLocked() const { -#if defined(WEBRTC_WIN) - return crit_.LockCount != -1; -#else -#if CS_DEBUG_CHECKS - return thread_ != 0; -#else - return true; -#endif -#endif -} - -CritScope::CritScope(CriticalSection* cs) : cs_(cs) { cs_->Enter(); } -CritScope::~CritScope() { cs_->Leave(); } - -TryCritScope::TryCritScope(CriticalSection* cs) - : cs_(cs), locked_(cs->TryEnter()) { - CS_DEBUG_CODE(lock_was_called_ = false); -} - -TryCritScope::~TryCritScope() { - CS_DEBUG_CODE(RTC_DCHECK(lock_was_called_)); - if (locked_) - cs_->Leave(); -} - -bool TryCritScope::locked() const { - CS_DEBUG_CODE(lock_was_called_ = true); - return locked_; -} - -void GlobalLockPod::Lock() { -#if !defined(WEBRTC_WIN) - const struct timespec ts_null = {0}; -#endif - - while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) { -#if defined(WEBRTC_WIN) - ::Sleep(0); -#else - nanosleep(&ts_null, nullptr); -#endif - } -} - -void GlobalLockPod::Unlock() { - int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0); - RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first"; -} - -GlobalLock::GlobalLock() { - lock_acquired = 0; -} - -GlobalLockScope::GlobalLockScope(GlobalLockPod* lock) - : lock_(lock) { - lock_->Lock(); -} - -GlobalLockScope::~GlobalLockScope() { - lock_->Unlock(); -} - -} // namespace rtc diff --git a/webrtc/base/criticalsection.h b/webrtc/base/criticalsection.h deleted file mode 100644 index ddbf857..0000000 --- a/webrtc/base/criticalsection.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2004 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. - */ - -#ifndef WEBRTC_BASE_CRITICALSECTION_H_ -#define WEBRTC_BASE_CRITICALSECTION_H_ - -#include "webrtc/base/atomicops.h" -#include "webrtc/base/constructormagic.h" -#include "webrtc/base/thread_annotations.h" - -#if defined(WEBRTC_WIN) -// Include winsock2.h before including to maintain consistency with -// win32.h. We can't include win32.h directly here since it pulls in -// headers such as basictypes.h which causes problems in Chromium where webrtc -// exists as two separate projects, webrtc and libjingle. -#include -#include -#include // must come after windows headers. -#endif // defined(WEBRTC_WIN) - -#if defined(WEBRTC_POSIX) -#include -#endif - -#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) -#define CS_DEBUG_CHECKS 1 -#endif - -#if CS_DEBUG_CHECKS -#define CS_DEBUG_CODE(x) x -#else // !CS_DEBUG_CHECKS -#define CS_DEBUG_CODE(x) -#endif // !CS_DEBUG_CHECKS - -namespace rtc { - -class LOCKABLE CriticalSection { - public: - CriticalSection(); - ~CriticalSection(); - - void Enter() EXCLUSIVE_LOCK_FUNCTION(); - bool TryEnter() EXCLUSIVE_TRYLOCK_FUNCTION(true); - void Leave() UNLOCK_FUNCTION(); - - // Use only for RTC_DCHECKing. - bool CurrentThreadIsOwner() const; - // Use only for RTC_DCHECKing. - bool IsLocked() const; - - private: -#if defined(WEBRTC_WIN) - CRITICAL_SECTION crit_; -#elif defined(WEBRTC_POSIX) - pthread_mutex_t mutex_; - CS_DEBUG_CODE(pthread_t thread_); - CS_DEBUG_CODE(int recursion_count_); -#endif -}; - -// CritScope, for serializing execution through a scope. -class SCOPED_LOCKABLE CritScope { - public: - explicit CritScope(CriticalSection* cs) EXCLUSIVE_LOCK_FUNCTION(cs); - ~CritScope() UNLOCK_FUNCTION(); - private: - CriticalSection* const cs_; - RTC_DISALLOW_COPY_AND_ASSIGN(CritScope); -}; - -// Tries to lock a critical section on construction via -// CriticalSection::TryEnter, and unlocks on destruction if the -// lock was taken. Never blocks. -// -// IMPORTANT: Unlike CritScope, the lock may not be owned by this thread in -// subsequent code. Users *must* check locked() to determine if the -// lock was taken. If you're not calling locked(), you're doing it wrong! -class TryCritScope { - public: - explicit TryCritScope(CriticalSection* cs); - ~TryCritScope(); -#if defined(WEBRTC_WIN) - _Check_return_ bool locked() const; -#else - bool locked() const __attribute__((warn_unused_result)); -#endif - private: - CriticalSection* const cs_; - const bool locked_; - CS_DEBUG_CODE(mutable bool lock_was_called_); - RTC_DISALLOW_COPY_AND_ASSIGN(TryCritScope); -}; - -// A POD lock used to protect global variables. Do NOT use for other purposes. -// No custom constructor or private data member should be added. -class LOCKABLE GlobalLockPod { - public: - void Lock() EXCLUSIVE_LOCK_FUNCTION(); - - void Unlock() UNLOCK_FUNCTION(); - - volatile int lock_acquired; -}; - -class GlobalLock : public GlobalLockPod { - public: - GlobalLock(); -}; - -// GlobalLockScope, for serializing execution through a scope. -class SCOPED_LOCKABLE GlobalLockScope { - public: - explicit GlobalLockScope(GlobalLockPod* lock) EXCLUSIVE_LOCK_FUNCTION(lock); - ~GlobalLockScope() UNLOCK_FUNCTION(); - private: - GlobalLockPod* const lock_; - RTC_DISALLOW_COPY_AND_ASSIGN(GlobalLockScope); -}; - -} // namespace rtc - -#endif // WEBRTC_BASE_CRITICALSECTION_H_ diff --git a/webrtc/base/event.cc b/webrtc/base/event.cc deleted file mode 100644 index a9af208..0000000 --- a/webrtc/base/event.cc +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2004 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 "webrtc/base/event.h" - -#if defined(WEBRTC_WIN) -#include -#elif defined(WEBRTC_POSIX) -#include -#include -#include -#else -#error "Must define either WEBRTC_WIN or WEBRTC_POSIX." -#endif - -#include "webrtc/base/checks.h" - -namespace rtc { - -#if defined(WEBRTC_WIN) - -Event::Event(bool manual_reset, bool initially_signaled) { - event_handle_ = ::CreateEvent(NULL, // Security attributes. - manual_reset, - initially_signaled, - NULL); // Name. - RTC_CHECK(event_handle_); -} - -Event::~Event() { - CloseHandle(event_handle_); -} - -void Event::Set() { - SetEvent(event_handle_); -} - -void Event::Reset() { - ResetEvent(event_handle_); -} - -bool Event::Wait(int milliseconds) { - DWORD ms = (milliseconds == kForever) ? INFINITE : milliseconds; - return (WaitForSingleObject(event_handle_, ms) == WAIT_OBJECT_0); -} - -#elif defined(WEBRTC_POSIX) - -Event::Event(bool manual_reset, bool initially_signaled) - : is_manual_reset_(manual_reset), - event_status_(initially_signaled) { - RTC_CHECK(pthread_mutex_init(&event_mutex_, NULL) == 0); - RTC_CHECK(pthread_cond_init(&event_cond_, NULL) == 0); -} - -Event::~Event() { - pthread_mutex_destroy(&event_mutex_); - pthread_cond_destroy(&event_cond_); -} - -void Event::Set() { - pthread_mutex_lock(&event_mutex_); - event_status_ = true; - pthread_cond_broadcast(&event_cond_); - pthread_mutex_unlock(&event_mutex_); -} - -void Event::Reset() { - pthread_mutex_lock(&event_mutex_); - event_status_ = false; - pthread_mutex_unlock(&event_mutex_); -} - -bool Event::Wait(int milliseconds) { - pthread_mutex_lock(&event_mutex_); - int error = 0; - - if (milliseconds != kForever) { - // Converting from seconds and microseconds (1e-6) plus - // milliseconds (1e-3) to seconds and nanoseconds (1e-9). - - struct timespec ts; -#if HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE - // Use relative time version, which tends to be more efficient for - // pthread implementations where provided (like on Android). - ts.tv_sec = milliseconds / 1000; - ts.tv_nsec = (milliseconds % 1000) * 1000000; -#else - struct timeval tv; - gettimeofday(&tv, NULL); - - ts.tv_sec = tv.tv_sec + (milliseconds / 1000); - ts.tv_nsec = tv.tv_usec * 1000 + (milliseconds % 1000) * 1000000; - - // Handle overflow. - if (ts.tv_nsec >= 1000000000) { - ts.tv_sec++; - ts.tv_nsec -= 1000000000; - } -#endif - - while (!event_status_ && error == 0) { -#if HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE - error = pthread_cond_timedwait_relative_np( - &event_cond_, &event_mutex_, &ts); -#else - error = pthread_cond_timedwait(&event_cond_, &event_mutex_, &ts); -#endif - } - } else { - while (!event_status_ && error == 0) - error = pthread_cond_wait(&event_cond_, &event_mutex_); - } - - // NOTE(liulk): Exactly one thread will auto-reset this event. All - // the other threads will think it's unsignaled. This seems to be - // consistent with auto-reset events in WEBRTC_WIN - if (error == 0 && !is_manual_reset_) - event_status_ = false; - - pthread_mutex_unlock(&event_mutex_); - - return (error == 0); -} - -#endif - -} // namespace rtc diff --git a/webrtc/base/event.h b/webrtc/base/event.h deleted file mode 100644 index 5237151..0000000 --- a/webrtc/base/event.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2004 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. - */ - -#ifndef WEBRTC_BASE_EVENT_H__ -#define WEBRTC_BASE_EVENT_H__ - -#if defined(WEBRTC_WIN) -#include "webrtc/base/win32.h" // NOLINT: consider this a system header. -#elif defined(WEBRTC_POSIX) -#include -#else -#error "Must define either WEBRTC_WIN or WEBRTC_POSIX." -#endif - -#include "webrtc/base/basictypes.h" - -namespace rtc { - -class Event { - public: - static const int kForever = -1; - - Event(bool manual_reset, bool initially_signaled); - ~Event(); - - void Set(); - void Reset(); - - // Wait for the event to become signaled, for the specified number of - // |milliseconds|. To wait indefinetly, pass kForever. - bool Wait(int milliseconds); - - private: -#if defined(WEBRTC_WIN) - HANDLE event_handle_; -#elif defined(WEBRTC_POSIX) - pthread_mutex_t event_mutex_; - pthread_cond_t event_cond_; - const bool is_manual_reset_; - bool event_status_; -#endif -}; - -} // namespace rtc - -#endif // WEBRTC_BASE_EVENT_H__ diff --git a/webrtc/base/maybe.h b/webrtc/base/maybe.h deleted file mode 100644 index 1df94de..0000000 --- a/webrtc/base/maybe.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2015 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. - */ - -#ifndef WEBRTC_BASE_MAYBE_H_ -#define WEBRTC_BASE_MAYBE_H_ - -#include -#include - -#include "webrtc/base/checks.h" - -namespace rtc { - -// Simple std::experimental::optional-wannabe. It either contains a T or not. -// In order to keep the implementation simple and portable, this implementation -// actually contains a (default-constructed) T even when it supposedly doesn't -// contain a value; use e.g. rtc::scoped_ptr instead if that's too -// expensive. -// -// A moved-from Maybe may only be destroyed, and assigned to if T allows -// being assigned to after having been moved from. Specifically, you may not -// assume that it just doesn't contain a value anymore. -// -// TODO(kwiberg): Get rid of this class when the standard library has -// std::optional (and we're allowed to use it). -template -class Maybe final { - public: - // Construct an empty Maybe. - Maybe() : has_value_(false) {} - - // Construct a Maybe that contains a value. - explicit Maybe(const T& val) : value_(val), has_value_(true) {} - explicit Maybe(T&& val) : value_(static_cast(val)), has_value_(true) {} - - // Copy and move constructors. - // TODO(kwiberg): =default the move constructor when MSVC supports it. - Maybe(const Maybe&) = default; - Maybe(Maybe&& m) - : value_(static_cast(m.value_)), has_value_(m.has_value_) {} - - // Assignment. - // TODO(kwiberg): =default the move assignment op when MSVC supports it. - Maybe& operator=(const Maybe&) = default; - Maybe& operator=(Maybe&& m) { - value_ = static_cast(m.value_); - has_value_ = m.has_value_; - return *this; - } - - friend void swap(Maybe& m1, Maybe& m2) { - using std::swap; - swap(m1.value_, m2.value_); - swap(m1.has_value_, m2.has_value_); - } - - // Conversion to bool to test if we have a value. - explicit operator bool() const { return has_value_; } - - // Dereferencing. Only allowed if we have a value. - const T* operator->() const { - RTC_DCHECK(has_value_); - return &value_; - } - T* operator->() { - RTC_DCHECK(has_value_); - return &value_; - } - const T& operator*() const { - RTC_DCHECK(has_value_); - return value_; - } - T& operator*() { - RTC_DCHECK(has_value_); - return value_; - } - - // Dereference with a default value in case we don't have a value. - const T& value_or(const T& default_val) const { - return has_value_ ? value_ : default_val; - } - - // Equality tests. Two Maybes are equal if they contain equivalent values, or - // if they're both empty. - friend bool operator==(const Maybe& m1, const Maybe& m2) { - return m1.has_value_ && m2.has_value_ ? m1.value_ == m2.value_ - : m1.has_value_ == m2.has_value_; - } - friend bool operator!=(const Maybe& m1, const Maybe& m2) { - return m1.has_value_ && m2.has_value_ ? m1.value_ != m2.value_ - : m1.has_value_ != m2.has_value_; - } - - private: - // Invariant: Unless *this has been moved from, value_ is default-initialized - // (or copied or moved from a default-initialized T) if !has_value_. - T value_; - bool has_value_; -}; - -} // namespace rtc - -#endif // WEBRTC_BASE_MAYBE_H_ diff --git a/webrtc/base/meson.build b/webrtc/base/meson.build deleted file mode 100644 index 2f634a3..0000000 --- a/webrtc/base/meson.build +++ /dev/null @@ -1,34 +0,0 @@ -base_sources = [ - 'criticalsection.cc', - 'checks.cc', - 'event.cc', - 'platform_thread.cc', - 'platform_file.cc', - 'stringutils.cc', - 'thread_checker_impl.cc', -] - -base_headers = [ - 'arraysize.h', - 'checks.h', - 'constructormagic.h', - 'basictypes.h', - 'maybe.h', - 'platform_file.h', -] - -install_headers(base_headers, - subdir: 'webrtc_audio_processing/webrtc/base' -) - -libbase = static_library('libbase', - base_sources, - dependencies: common_deps, - include_directories: webrtc_inc, - cpp_args : common_cxxflags -) - -base_dep = declare_dependency( - link_with: libbase -) - diff --git a/webrtc/base/platform_file.cc b/webrtc/base/platform_file.cc deleted file mode 100644 index d518b74..0000000 --- a/webrtc/base/platform_file.cc +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2014 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 "webrtc/base/platform_file.h" - -#if defined(WEBRTC_WIN) -#include -#else -#include -#endif - -namespace rtc { - -#if defined(WEBRTC_WIN) -const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE; - -FILE* FdopenPlatformFileForWriting(PlatformFile file) { - if (file == kInvalidPlatformFileValue) - return NULL; - int fd = _open_osfhandle(reinterpret_cast(file), 0); - if (fd < 0) - return NULL; - - return _fdopen(fd, "w"); -} - -bool ClosePlatformFile(PlatformFile file) { - return CloseHandle(file) != 0; -} -#else -const PlatformFile kInvalidPlatformFileValue = -1; - -FILE* FdopenPlatformFileForWriting(PlatformFile file) { - return fdopen(file, "w"); -} - -bool ClosePlatformFile(PlatformFile file) { - return close(file); -} -#endif - -} // namespace rtc diff --git a/webrtc/base/platform_file.h b/webrtc/base/platform_file.h deleted file mode 100644 index 12e08e9..0000000 --- a/webrtc/base/platform_file.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2014 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. - */ - -#ifndef WEBRTC_BASE_PLATFORM_FILE_H_ -#define WEBRTC_BASE_PLATFORM_FILE_H_ - -#include - -#if defined(WEBRTC_WIN) -#include -#endif - -namespace rtc { - -#if defined(WEBRTC_WIN) -typedef HANDLE PlatformFile; -#elif defined(WEBRTC_POSIX) -typedef int PlatformFile; -#else -#error Unsupported platform -#endif - -extern const PlatformFile kInvalidPlatformFileValue; - -// Associates a standard FILE stream with an existing PlatformFile. -// Note that after this function has returned a valid FILE stream, -// the PlatformFile should no longer be used. -FILE* FdopenPlatformFileForWriting(PlatformFile file); - -// Closes a PlatformFile. -// Don't use ClosePlatformFile to close a file opened with FdopenPlatformFile. -// Use fclose instead. -bool ClosePlatformFile(PlatformFile file); - -} // namespace rtc - -#endif // WEBRTC_BASE_PLATFORM_FILE_H_ diff --git a/webrtc/base/platform_thread.cc b/webrtc/base/platform_thread.cc deleted file mode 100644 index 707ccf8..0000000 --- a/webrtc/base/platform_thread.cc +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2015 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 "webrtc/base/platform_thread.h" - -#include - -#include "webrtc/base/checks.h" - -#if defined(WEBRTC_LINUX) -#include -#include -#elif defined(WEBRTC_GNU) -#include -#endif - -namespace rtc { - -PlatformThreadId CurrentThreadId() { - PlatformThreadId ret; -#if defined(WEBRTC_WIN) - ret = GetCurrentThreadId(); -#elif defined(WEBRTC_POSIX) -#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) - ret = pthread_mach_thread_np(pthread_self()); -#elif defined(WEBRTC_LINUX) - ret = syscall(__NR_gettid); -#elif defined(WEBRTC_ANDROID) - ret = gettid(); -#elif defined(WEBRTC_GNU) - ret = pthread_self(); -#else - // Default implementation for nacl and solaris. - ret = reinterpret_cast(pthread_self()); -#endif -#endif // defined(WEBRTC_POSIX) - RTC_DCHECK(ret); - return ret; -} - -PlatformThreadRef CurrentThreadRef() { -#if defined(WEBRTC_WIN) - return GetCurrentThreadId(); -#elif defined(WEBRTC_POSIX) - return pthread_self(); -#endif -} - -bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b) { -#if defined(WEBRTC_WIN) - return a == b; -#elif defined(WEBRTC_POSIX) - return pthread_equal(a, b); -#endif -} - -void SetCurrentThreadName(const char* name) { - RTC_DCHECK(strlen(name) < 64); -#if defined(WEBRTC_WIN) - struct { - DWORD dwType; - LPCSTR szName; - DWORD dwThreadID; - DWORD dwFlags; - } threadname_info = {0x1000, name, static_cast(-1), 0}; - - __try { - ::RaiseException(0x406D1388, 0, sizeof(threadname_info) / sizeof(DWORD), - reinterpret_cast(&threadname_info)); - } __except (EXCEPTION_EXECUTE_HANDLER) { - } -#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID) - prctl(PR_SET_NAME, reinterpret_cast(name)); -#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS) - pthread_setname_np(name); -#endif -} - -} // namespace rtc diff --git a/webrtc/base/scoped_ptr.h b/webrtc/base/scoped_ptr.h deleted file mode 100644 index 203a001..0000000 --- a/webrtc/base/scoped_ptr.h +++ /dev/null @@ -1,636 +0,0 @@ -/* - * Copyright 2012 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. - */ - -// Borrowed from Chromium's src/base/memory/scoped_ptr.h. - -// Scopers help you manage ownership of a pointer, helping you easily manage a -// pointer within a scope, and automatically destroying the pointer at the end -// of a scope. There are two main classes you will use, which correspond to the -// operators new/delete and new[]/delete[]. -// -// Example usage (scoped_ptr): -// { -// scoped_ptr foo(new Foo("wee")); -// } // foo goes out of scope, releasing the pointer with it. -// -// { -// scoped_ptr foo; // No pointer managed. -// foo.reset(new Foo("wee")); // Now a pointer is managed. -// foo.reset(new Foo("wee2")); // Foo("wee") was destroyed. -// foo.reset(new Foo("wee3")); // Foo("wee2") was destroyed. -// foo->Method(); // Foo::Method() called. -// foo.get()->Method(); // Foo::Method() called. -// SomeFunc(foo.release()); // SomeFunc takes ownership, foo no longer -// // manages a pointer. -// foo.reset(new Foo("wee4")); // foo manages a pointer again. -// foo.reset(); // Foo("wee4") destroyed, foo no longer -// // manages a pointer. -// } // foo wasn't managing a pointer, so nothing was destroyed. -// -// Example usage (scoped_ptr): -// { -// scoped_ptr foo(new Foo[100]); -// foo.get()->Method(); // Foo::Method on the 0th element. -// foo[10].Method(); // Foo::Method on the 10th element. -// } -// -// These scopers also implement part of the functionality of C++11 unique_ptr -// in that they are "movable but not copyable." You can use the scopers in -// the parameter and return types of functions to signify ownership transfer -// in to and out of a function. When calling a function that has a scoper -// as the argument type, it must be called with the result of an analogous -// scoper's Pass() function or another function that generates a temporary; -// passing by copy will NOT work. Here is an example using scoped_ptr: -// -// void TakesOwnership(scoped_ptr arg) { -// // Do something with arg -// } -// scoped_ptr CreateFoo() { -// // No need for calling Pass() because we are constructing a temporary -// // for the return value. -// return scoped_ptr(new Foo("new")); -// } -// scoped_ptr PassThru(scoped_ptr arg) { -// return arg.Pass(); -// } -// -// { -// scoped_ptr ptr(new Foo("yay")); // ptr manages Foo("yay"). -// TakesOwnership(ptr.Pass()); // ptr no longer owns Foo("yay"). -// scoped_ptr ptr2 = CreateFoo(); // ptr2 owns the return Foo. -// scoped_ptr ptr3 = // ptr3 now owns what was in ptr2. -// PassThru(ptr2.Pass()); // ptr2 is correspondingly nullptr. -// } -// -// Notice that if you do not call Pass() when returning from PassThru(), or -// when invoking TakesOwnership(), the code will not compile because scopers -// are not copyable; they only implement move semantics which require calling -// the Pass() function to signify a destructive transfer of state. CreateFoo() -// is different though because we are constructing a temporary on the return -// line and thus can avoid needing to call Pass(). -// -// Pass() properly handles upcast in initialization, i.e. you can use a -// scoped_ptr to initialize a scoped_ptr: -// -// scoped_ptr foo(new Foo()); -// scoped_ptr parent(foo.Pass()); -// -// PassAs<>() should be used to upcast return value in return statement: -// -// scoped_ptr CreateFoo() { -// scoped_ptr result(new FooChild()); -// return result.PassAs(); -// } -// -// Note that PassAs<>() is implemented only for scoped_ptr, but not for -// scoped_ptr. This is because casting array pointers may not be safe. - -#ifndef WEBRTC_BASE_SCOPED_PTR_H__ -#define WEBRTC_BASE_SCOPED_PTR_H__ - -// This is an implementation designed to match the anticipated future TR2 -// implementation of the scoped_ptr class. - -#include -#include -#include - -#include // For std::swap(). - -#include "webrtc/base/constructormagic.h" -#include "webrtc/base/template_util.h" -#include "webrtc/typedefs.h" - -namespace rtc { - -// Function object which deletes its parameter, which must be a pointer. -// If C is an array type, invokes 'delete[]' on the parameter; otherwise, -// invokes 'delete'. The default deleter for scoped_ptr. -template -struct DefaultDeleter { - DefaultDeleter() {} - template DefaultDeleter(const DefaultDeleter& other) { - // IMPLEMENTATION NOTE: C++11 20.7.1.1.2p2 only provides this constructor - // if U* is implicitly convertible to T* and U is not an array type. - // - // Correct implementation should use SFINAE to disable this - // constructor. However, since there are no other 1-argument constructors, - // using a static_assert based on is_convertible<> and requiring - // complete types is simpler and will cause compile failures for equivalent - // misuses. - // - // Note, the is_convertible check also ensures that U is not an - // array. T is guaranteed to be a non-array, so any U* where U is an array - // cannot convert to T*. - enum { T_must_be_complete = sizeof(T) }; - enum { U_must_be_complete = sizeof(U) }; - static_assert(rtc::is_convertible::value, - "U* must implicitly convert to T*"); - } - inline void operator()(T* ptr) const { - enum { type_must_be_complete = sizeof(T) }; - delete ptr; - } -}; - -// Specialization of DefaultDeleter for array types. -template -struct DefaultDeleter { - inline void operator()(T* ptr) const { - enum { type_must_be_complete = sizeof(T) }; - delete[] ptr; - } - - private: - // Disable this operator for any U != T because it is undefined to execute - // an array delete when the static type of the array mismatches the dynamic - // type. - // - // References: - // C++98 [expr.delete]p3 - // http://cplusplus.github.com/LWG/lwg-defects.html#938 - template void operator()(U* array) const; -}; - -template -struct DefaultDeleter { - // Never allow someone to declare something like scoped_ptr. - static_assert(sizeof(T) == -1, "do not use array with size as type"); -}; - -// Function object which invokes 'free' on its parameter, which must be -// a pointer. Can be used to store malloc-allocated pointers in scoped_ptr: -// -// scoped_ptr foo_ptr( -// static_cast(malloc(sizeof(int)))); -struct FreeDeleter { - inline void operator()(void* ptr) const { - free(ptr); - } -}; - -namespace internal { - -template -struct ShouldAbortOnSelfReset { - template - static rtc::internal::NoType Test(const typename U::AllowSelfReset*); - - template - static rtc::internal::YesType Test(...); - - static const bool value = - sizeof(Test(0)) == sizeof(rtc::internal::YesType); -}; - -// Minimal implementation of the core logic of scoped_ptr, suitable for -// reuse in both scoped_ptr and its specializations. -template -class scoped_ptr_impl { - public: - explicit scoped_ptr_impl(T* p) : data_(p) {} - - // Initializer for deleters that have data parameters. - scoped_ptr_impl(T* p, const D& d) : data_(p, d) {} - - // Templated constructor that destructively takes the value from another - // scoped_ptr_impl. - template - scoped_ptr_impl(scoped_ptr_impl* other) - : data_(other->release(), other->get_deleter()) { - // We do not support move-only deleters. We could modify our move - // emulation to have rtc::subtle::move() and rtc::subtle::forward() - // functions that are imperfect emulations of their C++11 equivalents, - // but until there's a requirement, just assume deleters are copyable. - } - - template - void TakeState(scoped_ptr_impl* other) { - // See comment in templated constructor above regarding lack of support - // for move-only deleters. - reset(other->release()); - get_deleter() = other->get_deleter(); - } - - ~scoped_ptr_impl() { - if (data_.ptr != nullptr) { - // Not using get_deleter() saves one function call in non-optimized - // builds. - static_cast(data_)(data_.ptr); - } - } - - void reset(T* p) { - // This is a self-reset, which is no longer allowed for default deleters: - // https://crbug.com/162971 - assert(!ShouldAbortOnSelfReset::value || p == nullptr || p != data_.ptr); - - // Note that running data_.ptr = p can lead to undefined behavior if - // get_deleter()(get()) deletes this. In order to prevent this, reset() - // should update the stored pointer before deleting its old value. - // - // However, changing reset() to use that behavior may cause current code to - // break in unexpected ways. If the destruction of the owned object - // dereferences the scoped_ptr when it is destroyed by a call to reset(), - // then it will incorrectly dispatch calls to |p| rather than the original - // value of |data_.ptr|. - // - // During the transition period, set the stored pointer to nullptr while - // deleting the object. Eventually, this safety check will be removed to - // prevent the scenario initially described from occurring and - // http://crbug.com/176091 can be closed. - T* old = data_.ptr; - data_.ptr = nullptr; - if (old != nullptr) - static_cast(data_)(old); - data_.ptr = p; - } - - T* get() const { return data_.ptr; } - - D& get_deleter() { return data_; } - const D& get_deleter() const { return data_; } - - void swap(scoped_ptr_impl& p2) { - // Standard swap idiom: 'using std::swap' ensures that std::swap is - // present in the overload set, but we call swap unqualified so that - // any more-specific overloads can be used, if available. - using std::swap; - swap(static_cast(data_), static_cast(p2.data_)); - swap(data_.ptr, p2.data_.ptr); - } - - T* release() { - T* old_ptr = data_.ptr; - data_.ptr = nullptr; - return old_ptr; - } - - T** accept() { - reset(nullptr); - return &(data_.ptr); - } - - T** use() { - return &(data_.ptr); - } - - private: - // Needed to allow type-converting constructor. - template friend class scoped_ptr_impl; - - // Use the empty base class optimization to allow us to have a D - // member, while avoiding any space overhead for it when D is an - // empty class. See e.g. http://www.cantrip.org/emptyopt.html for a good - // discussion of this technique. - struct Data : public D { - explicit Data(T* ptr_in) : ptr(ptr_in) {} - Data(T* ptr_in, const D& other) : D(other), ptr(ptr_in) {} - T* ptr; - }; - - Data data_; - - RTC_DISALLOW_COPY_AND_ASSIGN(scoped_ptr_impl); -}; - -} // namespace internal - -// A scoped_ptr is like a T*, except that the destructor of scoped_ptr -// automatically deletes the pointer it holds (if any). -// That is, scoped_ptr owns the T object that it points to. -// Like a T*, a scoped_ptr may hold either nullptr or a pointer to a T -// object. Also like T*, scoped_ptr is thread-compatible, and once you -// dereference it, you get the thread safety guarantees of T. -// -// The size of scoped_ptr is small. On most compilers, when using the -// DefaultDeleter, sizeof(scoped_ptr) == sizeof(T*). Custom deleters will -// increase the size proportional to whatever state they need to have. See -// comments inside scoped_ptr_impl<> for details. -// -// Current implementation targets having a strict subset of C++11's -// unique_ptr<> features. Known deficiencies include not supporting move-only -// deleters, function pointers as deleters, and deleters with reference -// types. -template > -class scoped_ptr { - - // TODO(ajm): If we ever import RefCountedBase, this check needs to be - // enabled. - //static_assert(rtc::internal::IsNotRefCounted::value, - // "T is refcounted type and needs scoped refptr"); - - public: - // The element and deleter types. - typedef T element_type; - typedef D deleter_type; - - // Constructor. Defaults to initializing with nullptr. - scoped_ptr() : impl_(nullptr) {} - - // Constructor. Takes ownership of p. - explicit scoped_ptr(element_type* p) : impl_(p) {} - - // Constructor. Allows initialization of a stateful deleter. - scoped_ptr(element_type* p, const D& d) : impl_(p, d) {} - - // Constructor. Allows construction from a nullptr. - scoped_ptr(decltype(nullptr)) : impl_(nullptr) {} - - // Constructor. Allows construction from a scoped_ptr rvalue for a - // convertible type and deleter. - // - // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this constructor distinct - // from the normal move constructor. By C++11 20.7.1.2.1.21, this constructor - // has different post-conditions if D is a reference type. Since this - // implementation does not support deleters with reference type, - // we do not need a separate move constructor allowing us to avoid one - // use of SFINAE. You only need to care about this if you modify the - // implementation of scoped_ptr. - template - scoped_ptr(scoped_ptr&& other) - : impl_(&other.impl_) { - static_assert(!rtc::is_array::value, "U cannot be an array"); - } - - // operator=. Allows assignment from a scoped_ptr rvalue for a convertible - // type and deleter. - // - // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this operator= distinct from - // the normal move assignment operator. By C++11 20.7.1.2.3.4, this templated - // form has different requirements on for move-only Deleters. Since this - // implementation does not support move-only Deleters, we do not need a - // separate move assignment operator allowing us to avoid one use of SFINAE. - // You only need to care about this if you modify the implementation of - // scoped_ptr. - template - scoped_ptr& operator=(scoped_ptr&& rhs) { - static_assert(!rtc::is_array::value, "U cannot be an array"); - impl_.TakeState(&rhs.impl_); - return *this; - } - - // operator=. Allows assignment from a nullptr. Deletes the currently owned - // object, if any. - scoped_ptr& operator=(decltype(nullptr)) { - reset(); - return *this; - } - - // Deleted copy constructor and copy assignment, to make the type move-only. - scoped_ptr(const scoped_ptr& other) = delete; - scoped_ptr& operator=(const scoped_ptr& other) = delete; - - // Get an rvalue reference. (sp.Pass() does the same thing as std::move(sp).) - scoped_ptr&& Pass() { return static_cast(*this); } - - // Reset. Deletes the currently owned object, if any. - // Then takes ownership of a new object, if given. - void reset(element_type* p = nullptr) { impl_.reset(p); } - - // Accessors to get the owned object. - // operator* and operator-> will assert() if there is no current object. - element_type& operator*() const { - assert(impl_.get() != nullptr); - return *impl_.get(); - } - element_type* operator->() const { - assert(impl_.get() != nullptr); - return impl_.get(); - } - element_type* get() const { return impl_.get(); } - - // Access to the deleter. - deleter_type& get_deleter() { return impl_.get_deleter(); } - const deleter_type& get_deleter() const { return impl_.get_deleter(); } - - // Allow scoped_ptr to be used in boolean expressions, but not - // implicitly convertible to a real bool (which is dangerous). - // - // Note that this trick is only safe when the == and != operators - // are declared explicitly, as otherwise "scoped_ptr1 == - // scoped_ptr2" will compile but do the wrong thing (i.e., convert - // to Testable and then do the comparison). - private: - typedef rtc::internal::scoped_ptr_impl - scoped_ptr::*Testable; - - public: - operator Testable() const { - return impl_.get() ? &scoped_ptr::impl_ : nullptr; - } - - // Comparison operators. - // These return whether two scoped_ptr refer to the same object, not just to - // two different but equal objects. - bool operator==(const element_type* p) const { return impl_.get() == p; } - bool operator!=(const element_type* p) const { return impl_.get() != p; } - - // Swap two scoped pointers. - void swap(scoped_ptr& p2) { - impl_.swap(p2.impl_); - } - - // Release a pointer. - // The return value is the current pointer held by this object. If this object - // holds a nullptr, the return value is nullptr. After this operation, this - // object will hold a nullptr, and will not own the object any more. - element_type* release() WARN_UNUSED_RESULT { - return impl_.release(); - } - - // Delete the currently held pointer and return a pointer - // to allow overwriting of the current pointer address. - element_type** accept() WARN_UNUSED_RESULT { - return impl_.accept(); - } - - // Return a pointer to the current pointer address. - element_type** use() WARN_UNUSED_RESULT { - return impl_.use(); - } - - private: - // Needed to reach into |impl_| in the constructor. - template friend class scoped_ptr; - rtc::internal::scoped_ptr_impl impl_; - - // Forbidden for API compatibility with std::unique_ptr. - explicit scoped_ptr(int disallow_construction_from_null); - - // Forbid comparison of scoped_ptr types. If U != T, it totally - // doesn't make sense, and if U == T, it still doesn't make sense - // because you should never have the same object owned by two different - // scoped_ptrs. - template bool operator==(scoped_ptr const& p2) const; - template bool operator!=(scoped_ptr const& p2) const; -}; - -template -class scoped_ptr { - public: - // The element and deleter types. - typedef T element_type; - typedef D deleter_type; - - // Constructor. Defaults to initializing with nullptr. - scoped_ptr() : impl_(nullptr) {} - - // Constructor. Stores the given array. Note that the argument's type - // must exactly match T*. In particular: - // - it cannot be a pointer to a type derived from T, because it is - // inherently unsafe in the general case to access an array through a - // pointer whose dynamic type does not match its static type (eg., if - // T and the derived types had different sizes access would be - // incorrectly calculated). Deletion is also always undefined - // (C++98 [expr.delete]p3). If you're doing this, fix your code. - // - it cannot be const-qualified differently from T per unique_ptr spec - // (http://cplusplus.github.com/LWG/lwg-active.html#2118). Users wanting - // to work around this may use implicit_cast(). - // However, because of the first bullet in this comment, users MUST - // NOT use implicit_cast() to upcast the static type of the array. - explicit scoped_ptr(element_type* array) : impl_(array) {} - - // Constructor. Allows construction from a nullptr. - scoped_ptr(decltype(nullptr)) : impl_(nullptr) {} - - // Constructor. Allows construction from a scoped_ptr rvalue. - scoped_ptr(scoped_ptr&& other) : impl_(&other.impl_) {} - - // operator=. Allows assignment from a scoped_ptr rvalue. - scoped_ptr& operator=(scoped_ptr&& rhs) { - impl_.TakeState(&rhs.impl_); - return *this; - } - - // operator=. Allows assignment from a nullptr. Deletes the currently owned - // array, if any. - scoped_ptr& operator=(decltype(nullptr)) { - reset(); - return *this; - } - - // Deleted copy constructor and copy assignment, to make the type move-only. - scoped_ptr(const scoped_ptr& other) = delete; - scoped_ptr& operator=(const scoped_ptr& other) = delete; - - // Get an rvalue reference. (sp.Pass() does the same thing as std::move(sp).) - scoped_ptr&& Pass() { return static_cast(*this); } - - // Reset. Deletes the currently owned array, if any. - // Then takes ownership of a new object, if given. - void reset(element_type* array = nullptr) { impl_.reset(array); } - - // Accessors to get the owned array. - element_type& operator[](size_t i) const { - assert(impl_.get() != nullptr); - return impl_.get()[i]; - } - element_type* get() const { return impl_.get(); } - - // Access to the deleter. - deleter_type& get_deleter() { return impl_.get_deleter(); } - const deleter_type& get_deleter() const { return impl_.get_deleter(); } - - // Allow scoped_ptr to be used in boolean expressions, but not - // implicitly convertible to a real bool (which is dangerous). - private: - typedef rtc::internal::scoped_ptr_impl - scoped_ptr::*Testable; - - public: - operator Testable() const { - return impl_.get() ? &scoped_ptr::impl_ : nullptr; - } - - // Comparison operators. - // These return whether two scoped_ptr refer to the same object, not just to - // two different but equal objects. - bool operator==(element_type* array) const { return impl_.get() == array; } - bool operator!=(element_type* array) const { return impl_.get() != array; } - - // Swap two scoped pointers. - void swap(scoped_ptr& p2) { - impl_.swap(p2.impl_); - } - - // Release a pointer. - // The return value is the current pointer held by this object. If this object - // holds a nullptr, the return value is nullptr. After this operation, this - // object will hold a nullptr, and will not own the object any more. - element_type* release() WARN_UNUSED_RESULT { - return impl_.release(); - } - - // Delete the currently held pointer and return a pointer - // to allow overwriting of the current pointer address. - element_type** accept() WARN_UNUSED_RESULT { - return impl_.accept(); - } - - // Return a pointer to the current pointer address. - element_type** use() WARN_UNUSED_RESULT { - return impl_.use(); - } - - private: - // Force element_type to be a complete type. - enum { type_must_be_complete = sizeof(element_type) }; - - // Actually hold the data. - rtc::internal::scoped_ptr_impl impl_; - - // Disable initialization from any type other than element_type*, by - // providing a constructor that matches such an initialization, but is - // private and has no definition. This is disabled because it is not safe to - // call delete[] on an array whose static type does not match its dynamic - // type. - template explicit scoped_ptr(U* array); - explicit scoped_ptr(int disallow_construction_from_null); - - // Disable reset() from any type other than element_type*, for the same - // reasons as the constructor above. - template void reset(U* array); - void reset(int disallow_reset_from_null); - - // Forbid comparison of scoped_ptr types. If U != T, it totally - // doesn't make sense, and if U == T, it still doesn't make sense - // because you should never have the same object owned by two different - // scoped_ptrs. - template bool operator==(scoped_ptr const& p2) const; - template bool operator!=(scoped_ptr const& p2) const; -}; - -template -void swap(rtc::scoped_ptr& p1, rtc::scoped_ptr& p2) { - p1.swap(p2); -} - -} // namespace rtc - -template -bool operator==(T* p1, const rtc::scoped_ptr& p2) { - return p1 == p2.get(); -} - -template -bool operator!=(T* p1, const rtc::scoped_ptr& p2) { - return p1 != p2.get(); -} - -// A function to convert T* into scoped_ptr -// Doing e.g. make_scoped_ptr(new FooBarBaz(arg)) is a shorter notation -// for scoped_ptr >(new FooBarBaz(arg)) -template -rtc::scoped_ptr rtc_make_scoped_ptr(T* ptr) { - return rtc::scoped_ptr(ptr); -} - -#endif // #ifndef WEBRTC_BASE_SCOPED_PTR_H__ diff --git a/webrtc/base/stringutils.cc b/webrtc/base/stringutils.cc deleted file mode 100644 index 9580253..0000000 --- a/webrtc/base/stringutils.cc +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2004 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 "webrtc/base/checks.h" -#include "webrtc/base/stringutils.h" - -namespace rtc { - -bool memory_check(const void* memory, int c, size_t count) { - const char* char_memory = static_cast(memory); - char char_c = static_cast(c); - for (size_t i = 0; i < count; ++i) { - if (char_memory[i] != char_c) { - return false; - } - } - return true; -} - -bool string_match(const char* target, const char* pattern) { - while (*pattern) { - if (*pattern == '*') { - if (!*++pattern) { - return true; - } - while (*target) { - if ((toupper(*pattern) == toupper(*target)) - && string_match(target + 1, pattern + 1)) { - return true; - } - ++target; - } - return false; - } else { - if (toupper(*pattern) != toupper(*target)) { - return false; - } - ++target; - ++pattern; - } - } - return !*target; -} - -#if defined(WEBRTC_WIN) -int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n, - CharacterTransformation transformation) { - wchar_t c1, c2; - while (true) { - if (n-- == 0) return 0; - c1 = transformation(*s1); - // Double check that characters are not UTF-8 - RTC_DCHECK_LT(static_cast(*s2), 128); - // Note: *s2 gets implicitly promoted to wchar_t - c2 = transformation(*s2); - if (c1 != c2) return (c1 < c2) ? -1 : 1; - if (!c1) return 0; - ++s1; - ++s2; - } -} - -size_t asccpyn(wchar_t* buffer, size_t buflen, - const char* source, size_t srclen) { - if (buflen <= 0) - return 0; - - if (srclen == SIZE_UNKNOWN) { - srclen = strlenn(source, buflen - 1); - } else if (srclen >= buflen) { - srclen = buflen - 1; - } -#if !defined(NDEBUG) - // Double check that characters are not UTF-8 - for (size_t pos = 0; pos < srclen; ++pos) - RTC_DCHECK_LT(static_cast(source[pos]), 128); -#endif - std::copy(source, source + srclen, buffer); - buffer[srclen] = 0; - return srclen; -} - -#endif // WEBRTC_WIN - -void replace_substrs(const char *search, - size_t search_len, - const char *replace, - size_t replace_len, - std::string *s) { - size_t pos = 0; - while ((pos = s->find(search, pos, search_len)) != std::string::npos) { - s->replace(pos, search_len, replace, replace_len); - pos += replace_len; - } -} - -bool starts_with(const char *s1, const char *s2) { - return strncmp(s1, s2, strlen(s2)) == 0; -} - -bool ends_with(const char *s1, const char *s2) { - size_t s1_length = strlen(s1); - size_t s2_length = strlen(s2); - - if (s2_length > s1_length) { - return false; - } - - const char* start = s1 + (s1_length - s2_length); - return strncmp(start, s2, s2_length) == 0; -} - -static const char kWhitespace[] = " \n\r\t"; - -std::string string_trim(const std::string& s) { - std::string::size_type first = s.find_first_not_of(kWhitespace); - std::string::size_type last = s.find_last_not_of(kWhitespace); - - if (first == std::string::npos || last == std::string::npos) { - return std::string(""); - } - - return s.substr(first, last - first + 1); -} - -} // namespace rtc diff --git a/webrtc/base/stringutils.h b/webrtc/base/stringutils.h deleted file mode 100644 index 5a6f42a..0000000 --- a/webrtc/base/stringutils.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright 2004 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. - */ - -#ifndef WEBRTC_BASE_STRINGUTILS_H__ -#define WEBRTC_BASE_STRINGUTILS_H__ - -#include -#include -#include -#include - -#if defined(WEBRTC_WIN) -#include -#include -#define alloca _alloca -#endif // WEBRTC_WIN - -#if defined(WEBRTC_POSIX) -#ifdef BSD -#include -#else // BSD -#include -#endif // !BSD -#endif // WEBRTC_POSIX - -#include - -#include "webrtc/base/basictypes.h" - -/////////////////////////////////////////////////////////////////////////////// -// Generic string/memory utilities -/////////////////////////////////////////////////////////////////////////////// - -#define STACK_ARRAY(TYPE, LEN) static_cast(::alloca((LEN)*sizeof(TYPE))) - -namespace rtc { - -// Complement to memset. Verifies memory consists of count bytes of value c. -bool memory_check(const void* memory, int c, size_t count); - -// Determines whether the simple wildcard pattern matches target. -// Alpha characters in pattern match case-insensitively. -// Asterisks in pattern match 0 or more characters. -// Ex: string_match("www.TEST.GOOGLE.COM", "www.*.com") -> true -bool string_match(const char* target, const char* pattern); - -} // namespace rtc - -/////////////////////////////////////////////////////////////////////////////// -// Rename a bunch of common string functions so they are consistent across -// platforms and between char and wchar_t variants. -// Here is the full list of functions that are unified: -// strlen, strcmp, stricmp, strncmp, strnicmp -// strchr, vsnprintf, strtoul, tolowercase -// tolowercase is like tolower, but not compatible with end-of-file value -// -// It's not clear if we will ever use wchar_t strings on unix. In theory, -// all strings should be Utf8 all the time, except when interfacing with Win32 -// APIs that require Utf16. -/////////////////////////////////////////////////////////////////////////////// - -inline char tolowercase(char c) { - return static_cast(tolower(c)); -} - -#if defined(WEBRTC_WIN) - -inline size_t strlen(const wchar_t* s) { - return wcslen(s); -} -inline int strcmp(const wchar_t* s1, const wchar_t* s2) { - return wcscmp(s1, s2); -} -inline int stricmp(const wchar_t* s1, const wchar_t* s2) { - return _wcsicmp(s1, s2); -} -inline int strncmp(const wchar_t* s1, const wchar_t* s2, size_t n) { - return wcsncmp(s1, s2, n); -} -inline int strnicmp(const wchar_t* s1, const wchar_t* s2, size_t n) { - return _wcsnicmp(s1, s2, n); -} -inline const wchar_t* strchr(const wchar_t* s, wchar_t c) { - return wcschr(s, c); -} -inline const wchar_t* strstr(const wchar_t* haystack, const wchar_t* needle) { - return wcsstr(haystack, needle); -} -#ifndef vsnprintf -inline int vsnprintf(wchar_t* buf, size_t n, const wchar_t* fmt, va_list args) { - return _vsnwprintf(buf, n, fmt, args); -} -#endif // !vsnprintf -inline unsigned long strtoul(const wchar_t* snum, wchar_t** end, int base) { - return wcstoul(snum, end, base); -} -inline wchar_t tolowercase(wchar_t c) { - return static_cast(towlower(c)); -} - -#endif // WEBRTC_WIN - -#if defined(WEBRTC_POSIX) - -inline int _stricmp(const char* s1, const char* s2) { - return strcasecmp(s1, s2); -} -inline int _strnicmp(const char* s1, const char* s2, size_t n) { - return strncasecmp(s1, s2, n); -} - -#endif // WEBRTC_POSIX - -/////////////////////////////////////////////////////////////////////////////// -// Traits simplifies porting string functions to be CTYPE-agnostic -/////////////////////////////////////////////////////////////////////////////// - -namespace rtc { - -const size_t SIZE_UNKNOWN = static_cast(-1); - -template -struct Traits { - // STL string type - //typedef XXX string; - // Null-terminated string - //inline static const CTYPE* empty_str(); -}; - -/////////////////////////////////////////////////////////////////////////////// -// String utilities which work with char or wchar_t -/////////////////////////////////////////////////////////////////////////////// - -template -inline const CTYPE* nonnull(const CTYPE* str, const CTYPE* def_str = NULL) { - return str ? str : (def_str ? def_str : Traits::empty_str()); -} - -template -const CTYPE* strchr(const CTYPE* str, const CTYPE* chs) { - for (size_t i=0; str[i]; ++i) { - for (size_t j=0; chs[j]; ++j) { - if (str[i] == chs[j]) { - return str + i; - } - } - } - return 0; -} - -template -const CTYPE* strchrn(const CTYPE* str, size_t slen, CTYPE ch) { - for (size_t i=0; i -size_t strlenn(const CTYPE* buffer, size_t buflen) { - size_t bufpos = 0; - while (buffer[bufpos] && (bufpos < buflen)) { - ++bufpos; - } - return bufpos; -} - -// Safe versions of strncpy, strncat, snprintf and vsnprintf that always -// null-terminate. - -template -size_t strcpyn(CTYPE* buffer, size_t buflen, - const CTYPE* source, size_t srclen = SIZE_UNKNOWN) { - if (buflen <= 0) - return 0; - - if (srclen == SIZE_UNKNOWN) { - srclen = strlenn(source, buflen - 1); - } else if (srclen >= buflen) { - srclen = buflen - 1; - } - memcpy(buffer, source, srclen * sizeof(CTYPE)); - buffer[srclen] = 0; - return srclen; -} - -template -size_t strcatn(CTYPE* buffer, size_t buflen, - const CTYPE* source, size_t srclen = SIZE_UNKNOWN) { - if (buflen <= 0) - return 0; - - size_t bufpos = strlenn(buffer, buflen - 1); - return bufpos + strcpyn(buffer + bufpos, buflen - bufpos, source, srclen); -} - -// Some compilers (clang specifically) require vsprintfn be defined before -// sprintfn. -template -size_t vsprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, - va_list args) { - int len = vsnprintf(buffer, buflen, format, args); - if ((len < 0) || (static_cast(len) >= buflen)) { - len = static_cast(buflen - 1); - buffer[len] = 0; - } - return len; -} - -template -size_t sprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, ...); -template -size_t sprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, ...) { - va_list args; - va_start(args, format); - size_t len = vsprintfn(buffer, buflen, format, args); - va_end(args); - return len; -} - -/////////////////////////////////////////////////////////////////////////////// -// Allow safe comparing and copying ascii (not UTF-8) with both wide and -// non-wide character strings. -/////////////////////////////////////////////////////////////////////////////// - -inline int asccmp(const char* s1, const char* s2) { - return strcmp(s1, s2); -} -inline int ascicmp(const char* s1, const char* s2) { - return _stricmp(s1, s2); -} -inline int ascncmp(const char* s1, const char* s2, size_t n) { - return strncmp(s1, s2, n); -} -inline int ascnicmp(const char* s1, const char* s2, size_t n) { - return _strnicmp(s1, s2, n); -} -inline size_t asccpyn(char* buffer, size_t buflen, - const char* source, size_t srclen = SIZE_UNKNOWN) { - return strcpyn(buffer, buflen, source, srclen); -} - -#if defined(WEBRTC_WIN) - -typedef wchar_t(*CharacterTransformation)(wchar_t); -inline wchar_t identity(wchar_t c) { return c; } -int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n, - CharacterTransformation transformation); - -inline int asccmp(const wchar_t* s1, const char* s2) { - return ascii_string_compare(s1, s2, static_cast(-1), identity); -} -inline int ascicmp(const wchar_t* s1, const char* s2) { - return ascii_string_compare(s1, s2, static_cast(-1), tolowercase); -} -inline int ascncmp(const wchar_t* s1, const char* s2, size_t n) { - return ascii_string_compare(s1, s2, n, identity); -} -inline int ascnicmp(const wchar_t* s1, const char* s2, size_t n) { - return ascii_string_compare(s1, s2, n, tolowercase); -} -size_t asccpyn(wchar_t* buffer, size_t buflen, - const char* source, size_t srclen = SIZE_UNKNOWN); - -#endif // WEBRTC_WIN - -/////////////////////////////////////////////////////////////////////////////// -// Traits specializations -/////////////////////////////////////////////////////////////////////////////// - -template<> -struct Traits { - typedef std::string string; - inline static const char* empty_str() { return ""; } -}; - -/////////////////////////////////////////////////////////////////////////////// -// Traits specializations (Windows only, currently) -/////////////////////////////////////////////////////////////////////////////// - -#if defined(WEBRTC_WIN) - -template<> -struct Traits { - typedef std::wstring string; - inline static const wchar_t* empty_str() { return L""; } -}; - -#endif // WEBRTC_WIN - -// Replaces all occurrences of "search" with "replace". -void replace_substrs(const char *search, - size_t search_len, - const char *replace, - size_t replace_len, - std::string *s); - -// True iff s1 starts with s2. -bool starts_with(const char *s1, const char *s2); - -// True iff s1 ends with s2. -bool ends_with(const char *s1, const char *s2); - -// Remove leading and trailing whitespaces. -std::string string_trim(const std::string& s); - -} // namespace rtc - -#endif // WEBRTC_BASE_STRINGUTILS_H__ diff --git a/webrtc/base/template_util.h b/webrtc/base/template_util.h deleted file mode 100644 index 31464cf..0000000 --- a/webrtc/base/template_util.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2013 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. - */ - -// Borrowed from Chromium's src/base/template_util.h. - -#ifndef WEBRTC_BASE_TEMPLATE_UTIL_H_ -#define WEBRTC_BASE_TEMPLATE_UTIL_H_ - -#include // For size_t. - -namespace rtc { - -// Template definitions from tr1. - -template -struct integral_constant { - static const T value = v; - typedef T value_type; - typedef integral_constant type; -}; - -template const T integral_constant::value; - -typedef integral_constant true_type; -typedef integral_constant false_type; - -template struct is_pointer : false_type {}; -template struct is_pointer : true_type {}; - -template struct is_same : public false_type {}; -template struct is_same : true_type {}; - -template struct is_array : public false_type {}; -template struct is_array : public true_type {}; -template struct is_array : public true_type {}; - -template struct is_non_const_reference : false_type {}; -template struct is_non_const_reference : true_type {}; -template struct is_non_const_reference : false_type {}; - -template struct is_void : false_type {}; -template <> struct is_void : true_type {}; - -template -struct remove_reference { - typedef T type; -}; -template -struct remove_reference { - typedef T type; -}; -template -struct remove_reference { - typedef T type; -}; - -namespace internal { - -// Types YesType and NoType are guaranteed such that sizeof(YesType) < -// sizeof(NoType). -typedef char YesType; - -struct NoType { - YesType dummy[2]; -}; - -// This class is an implementation detail for is_convertible, and you -// don't need to know how it works to use is_convertible. For those -// who care: we declare two different functions, one whose argument is -// of type To and one with a variadic argument list. We give them -// return types of different size, so we can use sizeof to trick the -// compiler into telling us which function it would have chosen if we -// had called it with an argument of type From. See Alexandrescu's -// _Modern C++ Design_ for more details on this sort of trick. - -struct ConvertHelper { - template - static YesType Test(To); - - template - static NoType Test(...); - - template - static From& Create(); -}; - -// Used to determine if a type is a struct/union/class. Inspired by Boost's -// is_class type_trait implementation. -struct IsClassHelper { - template - static YesType Test(void(C::*)(void)); - - template - static NoType Test(...); -}; - -} // namespace internal - -// Inherits from true_type if From is convertible to To, false_type otherwise. -// -// Note that if the type is convertible, this will be a true_type REGARDLESS -// of whether or not the conversion would emit a warning. -template -struct is_convertible - : integral_constant( - internal::ConvertHelper::Create())) == - sizeof(internal::YesType)> { -}; - -template -struct is_class - : integral_constant(0)) == - sizeof(internal::YesType)> { -}; - -} // namespace rtc - -#endif // WEBRTC_BASE_TEMPLATE_UTIL_H_ diff --git a/webrtc/base/thread_annotations.h b/webrtc/base/thread_annotations.h deleted file mode 100644 index 612242d..0000000 --- a/webrtc/base/thread_annotations.h +++ /dev/null @@ -1,99 +0,0 @@ -// -// Copyright (c) 2013 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. -// -// Borrowed from -// https://code.google.com/p/gperftools/source/browse/src/base/thread_annotations.h -// but adapted for clang attributes instead of the gcc. -// -// This header file contains the macro definitions for thread safety -// annotations that allow the developers to document the locking policies -// of their multi-threaded code. The annotations can also help program -// analysis tools to identify potential thread safety issues. - -#ifndef BASE_THREAD_ANNOTATIONS_H_ -#define BASE_THREAD_ANNOTATIONS_H_ - -#if defined(__clang__) && (!defined(SWIG)) -#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) -#else -#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op -#endif - -// Document if a shared variable/field needs to be protected by a lock. -// GUARDED_BY allows the user to specify a particular lock that should be -// held when accessing the annotated variable, while GUARDED_VAR only -// indicates a shared variable should be guarded (by any lock). GUARDED_VAR -// is primarily used when the client cannot express the name of the lock. -#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) -#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded) - -// Document if the memory location pointed to by a pointer should be guarded -// by a lock when dereferencing the pointer. Similar to GUARDED_VAR, -// PT_GUARDED_VAR is primarily used when the client cannot express the name -// of the lock. Note that a pointer variable to a shared memory location -// could itself be a shared variable. For example, if a shared global pointer -// q, which is guarded by mu1, points to a shared memory location that is -// guarded by mu2, q should be annotated as follows: -// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2); -#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded_by(x)) -#define PT_GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded) - -// Document the acquisition order between locks that can be held -// simultaneously by a thread. For any two locks that need to be annotated -// to establish an acquisition order, only one of them needs the annotation. -// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER -// and ACQUIRED_BEFORE.) -#define ACQUIRED_AFTER(x) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(x)) -#define ACQUIRED_BEFORE(x) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(x)) - -// The following three annotations document the lock requirements for -// functions/methods. - -// Document if a function expects certain locks to be held before it is called -#define EXCLUSIVE_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) - -#define SHARED_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) - -// Document the locks acquired in the body of the function. These locks -// cannot be held when calling this function (as google3's Mutex locks are -// non-reentrant). -#define LOCKS_EXCLUDED(x) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(x)) - -// Document the lock the annotated function returns without acquiring it. -#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) - -// Document if a class/type is a lockable type (such as the Mutex class). -#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable) - -// Document if a class is a scoped lockable type (such as the MutexLock class). -#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) - -// The following annotations specify lock and unlock primitives. -#define EXCLUSIVE_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) - -#define SHARED_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) - -#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) - -#define SHARED_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) - -#define UNLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) - -// An escape hatch for thread safety analysis to ignore the annotated function. -#define NO_THREAD_SAFETY_ANALYSIS \ - THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) - -#endif // BASE_THREAD_ANNOTATIONS_H_ diff --git a/webrtc/base/thread_checker.h b/webrtc/base/thread_checker.h deleted file mode 100644 index 6cd7d7b..0000000 --- a/webrtc/base/thread_checker.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -// Borrowed from Chromium's src/base/threading/thread_checker.h. - -#ifndef WEBRTC_BASE_THREAD_CHECKER_H_ -#define WEBRTC_BASE_THREAD_CHECKER_H_ - -// Apart from debug builds, we also enable the thread checker in -// builds with DCHECK_ALWAYS_ON so that trybots and waterfall bots -// with this define will get the same level of thread checking as -// debug bots. -// -// Note that this does not perfectly match situations where RTC_DCHECK is -// enabled. For example a non-official release build may have -// DCHECK_ALWAYS_ON undefined (and therefore ThreadChecker would be -// disabled) but have RTC_DCHECKs enabled at runtime. -#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) -#define ENABLE_THREAD_CHECKER 1 -#else -#define ENABLE_THREAD_CHECKER 0 -#endif - -#include "webrtc/base/thread_checker_impl.h" - -namespace rtc { - -// Do nothing implementation, for use in release mode. -// -// Note: You should almost always use the ThreadChecker class to get the -// right version for your build configuration. -class ThreadCheckerDoNothing { - public: - bool CalledOnValidThread() const { - return true; - } - - void DetachFromThread() {} -}; - -// ThreadChecker is a helper class used to help verify that some methods of a -// class are called from the same thread. It provides identical functionality to -// base::NonThreadSafe, but it is meant to be held as a member variable, rather -// than inherited from base::NonThreadSafe. -// -// While inheriting from base::NonThreadSafe may give a clear indication about -// the thread-safety of a class, it may also lead to violations of the style -// guide with regard to multiple inheritance. The choice between having a -// ThreadChecker member and inheriting from base::NonThreadSafe should be based -// on whether: -// - Derived classes need to know the thread they belong to, as opposed to -// having that functionality fully encapsulated in the base class. -// - Derived classes should be able to reassign the base class to another -// thread, via DetachFromThread. -// -// If neither of these are true, then having a ThreadChecker member and calling -// CalledOnValidThread is the preferable solution. -// -// Example: -// class MyClass { -// public: -// void Foo() { -// RTC_DCHECK(thread_checker_.CalledOnValidThread()); -// ... (do stuff) ... -// } -// -// private: -// ThreadChecker thread_checker_; -// } -// -// In Release mode, CalledOnValidThread will always return true. -#if ENABLE_THREAD_CHECKER -class ThreadChecker : public ThreadCheckerImpl { -}; -#else -class ThreadChecker : public ThreadCheckerDoNothing { -}; -#endif // ENABLE_THREAD_CHECKER - -#undef ENABLE_THREAD_CHECKER - -} // namespace rtc - -#endif // WEBRTC_BASE_THREAD_CHECKER_H_ diff --git a/webrtc/base/thread_checker_impl.cc b/webrtc/base/thread_checker_impl.cc deleted file mode 100644 index ea88308..0000000 --- a/webrtc/base/thread_checker_impl.cc +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -// Borrowed from Chromium's src/base/threading/thread_checker_impl.cc. - -#include "webrtc/base/thread_checker_impl.h" - -namespace rtc { - -ThreadCheckerImpl::ThreadCheckerImpl() : valid_thread_(CurrentThreadRef()) { -} - -ThreadCheckerImpl::~ThreadCheckerImpl() { -} - -bool ThreadCheckerImpl::CalledOnValidThread() const { - const PlatformThreadRef current_thread = CurrentThreadRef(); - CritScope scoped_lock(&lock_); - if (!valid_thread_) // Set if previously detached. - valid_thread_ = current_thread; - return IsThreadRefEqual(valid_thread_, current_thread); -} - -void ThreadCheckerImpl::DetachFromThread() { - CritScope scoped_lock(&lock_); - valid_thread_ = 0; -} - -} // namespace rtc diff --git a/webrtc/base/thread_checker_impl.h b/webrtc/base/thread_checker_impl.h deleted file mode 100644 index 7b39ada..0000000 --- a/webrtc/base/thread_checker_impl.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -// Borrowed from Chromium's src/base/threading/thread_checker_impl.h. - -#ifndef WEBRTC_BASE_THREAD_CHECKER_IMPL_H_ -#define WEBRTC_BASE_THREAD_CHECKER_IMPL_H_ - -#include "webrtc/base/criticalsection.h" -#include "webrtc/base/platform_thread.h" - -namespace rtc { - -// Real implementation of ThreadChecker, for use in debug mode, or -// for temporary use in release mode (e.g. to RTC_CHECK on a threading issue -// seen only in the wild). -// -// Note: You should almost always use the ThreadChecker class to get the -// right version for your build configuration. -class ThreadCheckerImpl { - public: - ThreadCheckerImpl(); - ~ThreadCheckerImpl(); - - bool CalledOnValidThread() const; - - // Changes the thread that is checked for in CalledOnValidThread. This may - // be useful when an object may be created on one thread and then used - // exclusively on another thread. - void DetachFromThread(); - - private: - mutable CriticalSection lock_; - // This is mutable so that CalledOnValidThread can set it. - // It's guarded by |lock_|. - mutable PlatformThreadRef valid_thread_; -}; - -} // namespace rtc - -#endif // WEBRTC_BASE_THREAD_CHECKER_IMPL_H_ diff --git a/webrtc/base/win32.h b/webrtc/base/win32.h deleted file mode 100644 index 6969c10..0000000 --- a/webrtc/base/win32.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2004 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. - */ -#ifndef WEBRTC_BASE_WIN32_H_ -#define WEBRTC_BASE_WIN32_H_ -#if defined(WEBRTC_WIN) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -// Make sure we don't get min/max macros -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include -#include -#ifndef SECURITY_MANDATORY_LABEL_AUTHORITY -// Add defines that we use if we are compiling against older sdks -#define SECURITY_MANDATORY_MEDIUM_RID (0x00002000L) -#define TokenIntegrityLevel static_cast(0x19) -typedef struct _TOKEN_MANDATORY_LABEL { - SID_AND_ATTRIBUTES Label; -} TOKEN_MANDATORY_LABEL, *PTOKEN_MANDATORY_LABEL; -#endif // SECURITY_MANDATORY_LABEL_AUTHORITY -#undef SetPort -#include -#include "webrtc/base/stringutils.h" -#include "webrtc/base/basictypes.h" -namespace rtc { -const char* win32_inet_ntop(int af, const void *src, char* dst, socklen_t size); -int win32_inet_pton(int af, const char* src, void *dst); -inline std::wstring ToUtf16(const char* utf8, size_t len) { - int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), - NULL, 0); - wchar_t* ws = STACK_ARRAY(wchar_t, len16); - ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), ws, len16); - return std::wstring(ws, len16); -} -inline std::wstring ToUtf16(const std::string& str) { - return ToUtf16(str.data(), str.length()); -} -inline std::string ToUtf8(const wchar_t* wide, size_t len) { - int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), - NULL, 0, NULL, NULL); - char* ns = STACK_ARRAY(char, len8); - ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), ns, len8, - NULL, NULL); - return std::string(ns, len8); -} -inline std::string ToUtf8(const wchar_t* wide) { - return ToUtf8(wide, wcslen(wide)); -} -inline std::string ToUtf8(const std::wstring& wstr) { - return ToUtf8(wstr.data(), wstr.length()); -} -// Convert FILETIME to time_t -void FileTimeToUnixTime(const FILETIME& ft, time_t* ut); -// Convert time_t to FILETIME -void UnixTimeToFileTime(const time_t& ut, FILETIME * ft); -// Convert a Utf8 path representation to a non-length-limited Unicode pathname. -bool Utf8ToWindowsFilename(const std::string& utf8, std::wstring* filename); -// Convert a FILETIME to a UInt64 -inline uint64_t ToUInt64(const FILETIME& ft) { - ULARGE_INTEGER r = {{ft.dwLowDateTime, ft.dwHighDateTime}}; - return r.QuadPart; -} -enum WindowsMajorVersions { - kWindows2000 = 5, - kWindowsVista = 6, -}; -bool GetOsVersion(int* major, int* minor, int* build); -inline bool IsWindowsVistaOrLater() { - int major; - return (GetOsVersion(&major, NULL, NULL) && major >= kWindowsVista); -} -inline bool IsWindowsXpOrLater() { - int major, minor; - return (GetOsVersion(&major, &minor, NULL) && - (major >= kWindowsVista || - (major == kWindows2000 && minor >= 1))); -} -inline bool IsWindows8OrLater() { - int major, minor; - return (GetOsVersion(&major, &minor, NULL) && - (major > kWindowsVista || - (major == kWindowsVista && minor >= 2))); -} -// Determine the current integrity level of the process. -bool GetCurrentProcessIntegrityLevel(int* level); -inline bool IsCurrentProcessLowIntegrity() { - int level; - return (GetCurrentProcessIntegrityLevel(&level) && - level < SECURITY_MANDATORY_MEDIUM_RID); -} -bool AdjustCurrentProcessPrivilege(const TCHAR* privilege, bool to_enable); -} // namespace rtc -#endif // WEBRTC_WIN -#endif // WEBRTC_BASE_WIN32_H_ diff --git a/webrtc/common_audio/BUILD.gn b/webrtc/common_audio/BUILD.gn index b01b318..a03e9ab 100644 --- a/webrtc/common_audio/BUILD.gn +++ b/webrtc/common_audio/BUILD.gn @@ -6,37 +6,19 @@ # in the file PATENTS. All contributing project authors may # be found in the AUTHORS file in the root of the source tree. -import("//build/config/arm.gni") -import("../build/webrtc.gni") +import("../webrtc.gni") -config("common_audio_config") { - include_dirs = [ - "resampler/include", - "signal_processing/include", - "vad/include", - ] -} +visibility = [ ":*" ] -source_set("common_audio") { +rtc_library("common_audio") { + visibility += [ "*" ] sources = [ "audio_converter.cc", "audio_converter.h", - "audio_ring_buffer.cc", - "audio_ring_buffer.h", "audio_util.cc", - "blocker.cc", - "blocker.h", "channel_buffer.cc", "channel_buffer.h", - "fft4g.c", - "fft4g.h", - "fir_filter.cc", - "fir_filter.h", - "fir_filter_neon.h", - "fir_filter_sse.h", "include/audio_util.h", - "lapped_transform.cc", - "lapped_transform.h", "real_fourier.cc", "real_fourier.h", "real_fourier_ooura.cc", @@ -48,7 +30,77 @@ source_set("common_audio") { "resampler/push_sinc_resampler.h", "resampler/resampler.cc", "resampler/sinc_resampler.cc", - "resampler/sinc_resampler.h", + "smoothing_filter.cc", + "smoothing_filter.h", + "vad/include/vad.h", + "vad/vad.cc", + "wav_file.cc", + "wav_file.h", + "wav_header.cc", + "wav_header.h", + "window_generator.cc", + "window_generator.h", + ] + + deps = [ + ":common_audio_c", + ":sinc_resampler", + "../api:array_view", + "../rtc_base:checks", + "../rtc_base:gtest_prod", + "../rtc_base:rtc_base_approved", + "../rtc_base:sanitizer", + "../rtc_base/memory:aligned_malloc", + "../rtc_base/system:arch", + "../rtc_base/system:file_wrapper", + "../system_wrappers", + "third_party/ooura:fft_size_256", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + + defines = [] + + if (rtc_build_with_neon) { + deps += [ ":common_audio_neon" ] + } + + if (current_cpu == "x86" || current_cpu == "x64") { + deps += [ ":common_audio_sse2" ] + deps += [ ":common_audio_avx2" ] + } +} + +rtc_source_set("mock_common_audio") { + visibility += webrtc_default_visibility + testonly = true + sources = [ + "mocks/mock_smoothing_filter.h", + "vad/mock/mock_vad.h", + ] + deps = [ + ":common_audio", + "../test:test_support", + ] +} + +rtc_source_set("common_audio_c_arm_asm") { + sources = [] + deps = [] + if (current_cpu == "arm") { + sources += [ "signal_processing/complex_bit_reverse_arm.S" ] + + if (arm_version >= 7) { + sources += [ "signal_processing/filter_ar_fast_q12_armv7.S" ] + } else { + sources += [ "signal_processing/filter_ar_fast_q12.c" ] + } + deps += [ "../rtc_base/system:asm_defines" ] + } +} + +rtc_library("common_audio_c") { + visibility += webrtc_default_visibility + sources = [ "ring_buffer.c", "ring_buffer.h", "signal_processing/auto_corr_to_refl_coef.c", @@ -57,7 +109,6 @@ source_set("common_audio") { "signal_processing/copy_set_operations.c", "signal_processing/cross_correlation.c", "signal_processing/division_operations.c", - "signal_processing/dot_product_with_scale.c", "signal_processing/downsample_fast.c", "signal_processing/energy.c", "signal_processing/filter_ar.c", @@ -68,6 +119,7 @@ source_set("common_audio") { "signal_processing/include/real_fft.h", "signal_processing/include/signal_processing_library.h", "signal_processing/include/spl_inl.h", + "signal_processing/include/spl_inl_armv7.h", "signal_processing/levinson_durbin.c", "signal_processing/lpc_to_refl_coef.c", "signal_processing/min_max_operations.c", @@ -81,15 +133,12 @@ source_set("common_audio") { "signal_processing/resample_by_2_internal.h", "signal_processing/resample_fractional.c", "signal_processing/spl_init.c", + "signal_processing/spl_inl.c", "signal_processing/spl_sqrt.c", "signal_processing/splitting_filter.c", "signal_processing/sqrt_of_one_minus_x_squared.c", "signal_processing/vector_scaling_operations.c", - "sparse_fir_filter.cc", - "sparse_fir_filter.h", - "vad/include/vad.h", "vad/include/webrtc_vad.h", - "vad/vad.cc", "vad/vad_core.c", "vad/vad_core.h", "vad/vad_filterbank.c", @@ -99,47 +148,8 @@ source_set("common_audio") { "vad/vad_sp.c", "vad/vad_sp.h", "vad/webrtc_vad.c", - "wav_file.cc", - "wav_file.h", - "wav_header.cc", - "wav_header.h", - "window_generator.cc", - "window_generator.h", ] - deps = [ - "../system_wrappers", - ] - - defines = [] - if (rtc_use_openmax_dl) { - sources += [ - "real_fourier_openmax.cc", - "real_fourier_openmax.h", - ] - defines += [ "RTC_USE_OPENMAX_DL" ] - if (rtc_build_openmax_dl) { - deps += [ "//third_party/openmax_dl/dl" ] - } - } - - if (current_cpu == "arm") { - sources += [ - "signal_processing/complex_bit_reverse_arm.S", - "signal_processing/spl_sqrt_floor_arm.S", - ] - - if (arm_version >= 7) { - sources += [ "signal_processing/filter_ar_fast_q12_armv7.S" ] - } else { - sources += [ "signal_processing/filter_ar_fast_q12.c" ] - } - } - - if (rtc_build_with_neon) { - deps += [ ":common_audio_neon" ] - } - if (current_cpu == "mipsel") { sources += [ "signal_processing/complex_bit_reverse_mips.c", @@ -150,7 +160,6 @@ source_set("common_audio") { "signal_processing/include/spl_inl_mips.h", "signal_processing/min_max_operations_mips.c", "signal_processing/resample_by_2_mips.c", - "signal_processing/spl_sqrt_floor_mips.c", ] if (mips_dsp_rev > 0) { sources += [ "signal_processing/vector_scaling_operations_mips.c" ] @@ -163,81 +172,227 @@ source_set("common_audio") { sources += [ "signal_processing/complex_bit_reverse.c", "signal_processing/filter_ar_fast_q12.c", - "signal_processing/spl_sqrt_floor.c", ] } - if (is_win) { - cflags = [ "/wd4334" ] # Ignore warning on shift operator promotion. - } + deps = [ + ":common_audio_c_arm_asm", + ":common_audio_cc", + "../rtc_base:checks", + "../rtc_base:compile_assert_c", + "../rtc_base:rtc_base_approved", + "../rtc_base:sanitizer", + "../rtc_base/system:arch", + "../system_wrappers", + "third_party/ooura:fft_size_256", + "third_party/spl_sqrt_floor", + ] +} - configs += [ "..:common_config" ] - - public_configs = [ - "..:common_inherited_config", - ":common_audio_config", +rtc_library("common_audio_cc") { + sources = [ + "signal_processing/dot_product_with_scale.cc", + "signal_processing/dot_product_with_scale.h", ] - if (is_clang) { - # Suppress warnings from Chrome's Clang plugins. - # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. - configs -= [ "//build/config/clang:find_bad_constructs" ] - } + deps = [ + "../rtc_base:rtc_base_approved", + "../system_wrappers", + ] +} +rtc_source_set("sinc_resampler") { + sources = [ "resampler/sinc_resampler.h" ] + deps = [ + "../rtc_base:gtest_prod", + "../rtc_base:rtc_base_approved", + "../rtc_base/memory:aligned_malloc", + "../rtc_base/system:arch", + "../system_wrappers", + ] +} + +rtc_source_set("fir_filter") { + visibility += webrtc_default_visibility + sources = [ "fir_filter.h" ] +} + +rtc_library("fir_filter_factory") { + visibility += webrtc_default_visibility + sources = [ + "fir_filter_c.cc", + "fir_filter_c.h", + "fir_filter_factory.cc", + "fir_filter_factory.h", + ] + deps = [ + ":fir_filter", + "../rtc_base:checks", + "../rtc_base:rtc_base_approved", + "../rtc_base/system:arch", + "../system_wrappers", + ] if (current_cpu == "x86" || current_cpu == "x64") { deps += [ ":common_audio_sse2" ] + deps += [ ":common_audio_avx2" ] + } + if (rtc_build_with_neon) { + deps += [ ":common_audio_neon" ] } } if (current_cpu == "x86" || current_cpu == "x64") { - source_set("common_audio_sse2") { + rtc_library("common_audio_sse2") { sources = [ "fir_filter_sse.cc", + "fir_filter_sse.h", "resampler/sinc_resampler_sse.cc", ] - if (is_posix) { + if (is_posix || is_fuchsia) { cflags = [ "-msse2" ] } - configs += [ "..:common_inherited_config" ] + deps = [ + ":fir_filter", + ":sinc_resampler", + "../rtc_base:checks", + "../rtc_base:rtc_base_approved", + "../rtc_base/memory:aligned_malloc", + ] + } - if (is_clang) { - # Suppress warnings from Chrome's Clang plugins. - # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. - configs -= [ "//build/config/clang:find_bad_constructs" ] + rtc_library("common_audio_avx2") { + sources = [ + "fir_filter_avx2.cc", + "fir_filter_avx2.h", + "resampler/sinc_resampler_avx2.cc", + ] + + if (is_win) { + cflags = [ "/arch:AVX2" ] + } else { + cflags = [ + "-mavx2", + "-mfma", + ] } + + deps = [ + ":fir_filter", + ":sinc_resampler", + "../rtc_base:checks", + "../rtc_base:rtc_base_approved", + "../rtc_base/memory:aligned_malloc", + ] } } if (rtc_build_with_neon) { - source_set("common_audio_neon") { + rtc_library("common_audio_neon") { sources = [ "fir_filter_neon.cc", + "fir_filter_neon.h", "resampler/sinc_resampler_neon.cc", + ] + + if (current_cpu != "arm64") { + # Enable compilation for the NEON instruction set. + suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] + cflags = [ "-mfpu=neon" ] + } + + deps = [ + ":common_audio_neon_c", + ":fir_filter", + ":sinc_resampler", + "../rtc_base:checks", + "../rtc_base:rtc_base_approved", + "../rtc_base/memory:aligned_malloc", + ] + } + + rtc_library("common_audio_neon_c") { + visibility += webrtc_default_visibility + sources = [ "signal_processing/cross_correlation_neon.c", "signal_processing/downsample_fast_neon.c", "signal_processing/min_max_operations_neon.c", ] if (current_cpu != "arm64") { - # Enable compilation for the NEON instruction set. This is needed - # since //build/config/arm.gni only enables NEON for iOS, not Android. - # This provides the same functionality as webrtc/build/arm_neon.gypi. - configs -= [ "//build/config/compiler:compiler_arm_fpu" ] + # Enable compilation for the NEON instruction set. + suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] cflags = [ "-mfpu=neon" ] } - # Disable LTO on NEON targets due to compiler bug. - # TODO(fdegans): Enable this. See crbug.com/408997. - if (rtc_use_lto) { - cflags -= [ - "-flto", - "-ffat-lto-objects", - ] - } - - configs += [ "..:common_config" ] - public_configs = [ "..:common_inherited_config" ] + deps = [ + ":common_audio_c", + "../rtc_base:checks", + "../rtc_base:rtc_base_approved", + "../rtc_base/system:arch", + ] + } +} + +if (rtc_include_tests) { + rtc_test("common_audio_unittests") { + visibility += webrtc_default_visibility + testonly = true + + sources = [ + "audio_converter_unittest.cc", + "audio_util_unittest.cc", + "channel_buffer_unittest.cc", + "fir_filter_unittest.cc", + "real_fourier_unittest.cc", + "resampler/push_resampler_unittest.cc", + "resampler/push_sinc_resampler_unittest.cc", + "resampler/resampler_unittest.cc", + "resampler/sinusoidal_linear_chirp_source.cc", + "resampler/sinusoidal_linear_chirp_source.h", + "ring_buffer_unittest.cc", + "signal_processing/real_fft_unittest.cc", + "signal_processing/signal_processing_unittest.cc", + "smoothing_filter_unittest.cc", + "vad/vad_core_unittest.cc", + "vad/vad_filterbank_unittest.cc", + "vad/vad_gmm_unittest.cc", + "vad/vad_sp_unittest.cc", + "vad/vad_unittest.cc", + "vad/vad_unittest.h", + "wav_file_unittest.cc", + "wav_header_unittest.cc", + "window_generator_unittest.cc", + ] + + # Does not compile on iOS for arm: webrtc:5544. + if (!is_ios || target_cpu != "arm") { + sources += [ "resampler/sinc_resampler_unittest.cc" ] + } + + deps = [ + ":common_audio", + ":common_audio_c", + ":fir_filter", + ":fir_filter_factory", + ":sinc_resampler", + "../rtc_base:checks", + "../rtc_base:rtc_base_approved", + "../rtc_base:rtc_base_tests_utils", + "../rtc_base/system:arch", + "../system_wrappers", + "../test:fileutils", + "../test:rtc_expect_death", + "../test:test_main", + "../test:test_support", + "//testing/gtest", + ] + + if (is_android) { + deps += [ "//testing/android/native_test:native_test_support" ] + + shard_timeout = 900 + } } } diff --git a/webrtc/common_audio/audio_converter.cc b/webrtc/common_audio/audio_converter.cc index f1709ae..485ec80 100644 --- a/webrtc/common_audio/audio_converter.cc +++ b/webrtc/common_audio/audio_converter.cc @@ -8,32 +8,36 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/audio_converter.h" +#include "common_audio/audio_converter.h" #include +#include +#include +#include -#include "webrtc/base/checks.h" -#include "webrtc/base/safe_conversions.h" -#include "webrtc/common_audio/channel_buffer.h" -#include "webrtc/common_audio/resampler/push_sinc_resampler.h" -#include "webrtc/system_wrappers/include/scoped_vector.h" - -using rtc::checked_cast; +#include "common_audio/channel_buffer.h" +#include "common_audio/resampler/push_sinc_resampler.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" namespace webrtc { class CopyConverter : public AudioConverter { public: - CopyConverter(int src_channels, size_t src_frames, int dst_channels, + CopyConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, size_t dst_frames) : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {} - ~CopyConverter() override {}; + ~CopyConverter() override {} - void Convert(const float* const* src, size_t src_size, float* const* dst, + void Convert(const float* const* src, + size_t src_size, + float* const* dst, size_t dst_capacity) override { CheckSizes(src_size, dst_capacity); if (src != dst) { - for (int i = 0; i < src_channels(); ++i) + for (size_t i = 0; i < src_channels(); ++i) std::memcpy(dst[i], src[i], dst_frames() * sizeof(*dst[i])); } } @@ -41,17 +45,21 @@ class CopyConverter : public AudioConverter { class UpmixConverter : public AudioConverter { public: - UpmixConverter(int src_channels, size_t src_frames, int dst_channels, + UpmixConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, size_t dst_frames) : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {} - ~UpmixConverter() override {}; + ~UpmixConverter() override {} - void Convert(const float* const* src, size_t src_size, float* const* dst, + void Convert(const float* const* src, + size_t src_size, + float* const* dst, size_t dst_capacity) override { CheckSizes(src_size, dst_capacity); for (size_t i = 0; i < dst_frames(); ++i) { const float value = src[0][i]; - for (int j = 0; j < dst_channels(); ++j) + for (size_t j = 0; j < dst_channels(); ++j) dst[j][i] = value; } } @@ -59,19 +67,22 @@ class UpmixConverter : public AudioConverter { class DownmixConverter : public AudioConverter { public: - DownmixConverter(int src_channels, size_t src_frames, int dst_channels, + DownmixConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, size_t dst_frames) - : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) { - } - ~DownmixConverter() override {}; + : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {} + ~DownmixConverter() override {} - void Convert(const float* const* src, size_t src_size, float* const* dst, + void Convert(const float* const* src, + size_t src_size, + float* const* dst, size_t dst_capacity) override { CheckSizes(src_size, dst_capacity); float* dst_mono = dst[0]; for (size_t i = 0; i < src_frames(); ++i) { float sum = 0; - for (int j = 0; j < src_channels(); ++j) + for (size_t j = 0; j < src_channels(); ++j) sum += src[j][i]; dst_mono[i] = sum / src_channels(); } @@ -80,16 +91,21 @@ class DownmixConverter : public AudioConverter { class ResampleConverter : public AudioConverter { public: - ResampleConverter(int src_channels, size_t src_frames, int dst_channels, + ResampleConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, size_t dst_frames) : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) { resamplers_.reserve(src_channels); - for (int i = 0; i < src_channels; ++i) - resamplers_.push_back(new PushSincResampler(src_frames, dst_frames)); + for (size_t i = 0; i < src_channels; ++i) + resamplers_.push_back(std::unique_ptr( + new PushSincResampler(src_frames, dst_frames))); } - ~ResampleConverter() override {}; + ~ResampleConverter() override {} - void Convert(const float* const* src, size_t src_size, float* const* dst, + void Convert(const float* const* src, + size_t src_size, + float* const* dst, size_t dst_capacity) override { CheckSizes(src_size, dst_capacity); for (size_t i = 0; i < resamplers_.size(); ++i) @@ -97,69 +113,73 @@ class ResampleConverter : public AudioConverter { } private: - ScopedVector resamplers_; + std::vector> resamplers_; }; // Apply a vector of converters in serial, in the order given. At least two // converters must be provided. class CompositionConverter : public AudioConverter { public: - CompositionConverter(ScopedVector converters) - : converters_(converters.Pass()) { - RTC_CHECK_GE(converters_.size(), 2u); + explicit CompositionConverter( + std::vector> converters) + : converters_(std::move(converters)) { + RTC_CHECK_GE(converters_.size(), 2); // We need an intermediate buffer after every converter. for (auto it = converters_.begin(); it != converters_.end() - 1; ++it) - buffers_.push_back(new ChannelBuffer((*it)->dst_frames(), - (*it)->dst_channels())); + buffers_.push_back( + std::unique_ptr>(new ChannelBuffer( + (*it)->dst_frames(), (*it)->dst_channels()))); } - ~CompositionConverter() override {}; + ~CompositionConverter() override {} - void Convert(const float* const* src, size_t src_size, float* const* dst, + void Convert(const float* const* src, + size_t src_size, + float* const* dst, size_t dst_capacity) override { converters_.front()->Convert(src, src_size, buffers_.front()->channels(), buffers_.front()->size()); for (size_t i = 2; i < converters_.size(); ++i) { - auto src_buffer = buffers_[i - 2]; - auto dst_buffer = buffers_[i - 1]; - converters_[i]->Convert(src_buffer->channels(), - src_buffer->size(), - dst_buffer->channels(), - dst_buffer->size()); + auto& src_buffer = buffers_[i - 2]; + auto& dst_buffer = buffers_[i - 1]; + converters_[i]->Convert(src_buffer->channels(), src_buffer->size(), + dst_buffer->channels(), dst_buffer->size()); } converters_.back()->Convert(buffers_.back()->channels(), buffers_.back()->size(), dst, dst_capacity); } private: - ScopedVector converters_; - ScopedVector> buffers_; + std::vector> converters_; + std::vector>> buffers_; }; -rtc::scoped_ptr AudioConverter::Create(int src_channels, +std::unique_ptr AudioConverter::Create(size_t src_channels, size_t src_frames, - int dst_channels, + size_t dst_channels, size_t dst_frames) { - rtc::scoped_ptr sp; + std::unique_ptr sp; if (src_channels > dst_channels) { if (src_frames != dst_frames) { - ScopedVector converters; - converters.push_back(new DownmixConverter(src_channels, src_frames, - dst_channels, src_frames)); - converters.push_back(new ResampleConverter(dst_channels, src_frames, - dst_channels, dst_frames)); - sp.reset(new CompositionConverter(converters.Pass())); + std::vector> converters; + converters.push_back(std::unique_ptr(new DownmixConverter( + src_channels, src_frames, dst_channels, src_frames))); + converters.push_back( + std::unique_ptr(new ResampleConverter( + dst_channels, src_frames, dst_channels, dst_frames))); + sp.reset(new CompositionConverter(std::move(converters))); } else { sp.reset(new DownmixConverter(src_channels, src_frames, dst_channels, dst_frames)); } } else if (src_channels < dst_channels) { if (src_frames != dst_frames) { - ScopedVector converters; - converters.push_back(new ResampleConverter(src_channels, src_frames, - src_channels, dst_frames)); - converters.push_back(new UpmixConverter(src_channels, dst_frames, - dst_channels, dst_frames)); - sp.reset(new CompositionConverter(converters.Pass())); + std::vector> converters; + converters.push_back( + std::unique_ptr(new ResampleConverter( + src_channels, src_frames, src_channels, dst_frames))); + converters.push_back(std::unique_ptr(new UpmixConverter( + src_channels, dst_frames, dst_channels, dst_frames))); + sp.reset(new CompositionConverter(std::move(converters))); } else { sp.reset(new UpmixConverter(src_channels, src_frames, dst_channels, dst_frames)); @@ -168,22 +188,21 @@ rtc::scoped_ptr AudioConverter::Create(int src_channels, sp.reset(new ResampleConverter(src_channels, src_frames, dst_channels, dst_frames)); } else { - sp.reset(new CopyConverter(src_channels, src_frames, dst_channels, - dst_frames)); + sp.reset( + new CopyConverter(src_channels, src_frames, dst_channels, dst_frames)); } - return sp.Pass(); + return sp; } // For CompositionConverter. AudioConverter::AudioConverter() - : src_channels_(0), - src_frames_(0), - dst_channels_(0), - dst_frames_(0) {} + : src_channels_(0), src_frames_(0), dst_channels_(0), dst_frames_(0) {} -AudioConverter::AudioConverter(int src_channels, size_t src_frames, - int dst_channels, size_t dst_frames) +AudioConverter::AudioConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, + size_t dst_frames) : src_channels_(src_channels), src_frames_(src_frames), dst_channels_(dst_channels), diff --git a/webrtc/common_audio/audio_converter.h b/webrtc/common_audio/audio_converter.h index 7d1513b..481ac08 100644 --- a/webrtc/common_audio/audio_converter.h +++ b/webrtc/common_audio/audio_converter.h @@ -8,11 +8,14 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_AUDIO_CONVERTER_H_ -#define WEBRTC_COMMON_AUDIO_AUDIO_CONVERTER_H_ +#ifndef COMMON_AUDIO_AUDIO_CONVERTER_H_ +#define COMMON_AUDIO_AUDIO_CONVERTER_H_ -#include "webrtc/base/constructormagic.h" -#include "webrtc/base/scoped_ptr.h" +#include + +#include + +#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -26,36 +29,40 @@ class AudioConverter { public: // Returns a new AudioConverter, which will use the supplied format for its // lifetime. Caller is responsible for the memory. - static rtc::scoped_ptr Create(int src_channels, + static std::unique_ptr Create(size_t src_channels, size_t src_frames, - int dst_channels, + size_t dst_channels, size_t dst_frames); - virtual ~AudioConverter() {}; + virtual ~AudioConverter() {} // Convert |src|, containing |src_size| samples, to |dst|, having a sample // capacity of |dst_capacity|. Both point to a series of buffers containing // the samples for each channel. The sizes must correspond to the format // passed to Create(). - virtual void Convert(const float* const* src, size_t src_size, - float* const* dst, size_t dst_capacity) = 0; + virtual void Convert(const float* const* src, + size_t src_size, + float* const* dst, + size_t dst_capacity) = 0; - int src_channels() const { return src_channels_; } + size_t src_channels() const { return src_channels_; } size_t src_frames() const { return src_frames_; } - int dst_channels() const { return dst_channels_; } + size_t dst_channels() const { return dst_channels_; } size_t dst_frames() const { return dst_frames_; } protected: AudioConverter(); - AudioConverter(int src_channels, size_t src_frames, int dst_channels, + AudioConverter(size_t src_channels, + size_t src_frames, + size_t dst_channels, size_t dst_frames); // Helper to RTC_CHECK that inputs are correctly sized. void CheckSizes(size_t src_size, size_t dst_capacity) const; private: - const int src_channels_; + const size_t src_channels_; const size_t src_frames_; - const int dst_channels_; + const size_t dst_channels_; const size_t dst_frames_; RTC_DISALLOW_COPY_AND_ASSIGN(AudioConverter); @@ -63,4 +70,4 @@ class AudioConverter { } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_AUDIO_CONVERTER_H_ +#endif // COMMON_AUDIO_AUDIO_CONVERTER_H_ diff --git a/webrtc/common_audio/audio_ring_buffer.cc b/webrtc/common_audio/audio_ring_buffer.cc deleted file mode 100644 index a29e53a..0000000 --- a/webrtc/common_audio/audio_ring_buffer.cc +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2015 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 "webrtc/common_audio/audio_ring_buffer.h" - -#include "webrtc/base/checks.h" -#include "webrtc/common_audio/ring_buffer.h" - -// This is a simple multi-channel wrapper over the ring_buffer.h C interface. - -namespace webrtc { - -AudioRingBuffer::AudioRingBuffer(size_t channels, size_t max_frames) { - buffers_.reserve(channels); - for (size_t i = 0; i < channels; ++i) - buffers_.push_back(WebRtc_CreateBuffer(max_frames, sizeof(float))); -} - -AudioRingBuffer::~AudioRingBuffer() { - for (auto buf : buffers_) - WebRtc_FreeBuffer(buf); -} - -void AudioRingBuffer::Write(const float* const* data, size_t channels, - size_t frames) { - RTC_DCHECK_EQ(buffers_.size(), channels); - for (size_t i = 0; i < channels; ++i) { - const size_t written = WebRtc_WriteBuffer(buffers_[i], data[i], frames); - RTC_CHECK_EQ(written, frames); - } -} - -void AudioRingBuffer::Read(float* const* data, size_t channels, size_t frames) { - RTC_DCHECK_EQ(buffers_.size(), channels); - for (size_t i = 0; i < channels; ++i) { - const size_t read = - WebRtc_ReadBuffer(buffers_[i], nullptr, data[i], frames); - RTC_CHECK_EQ(read, frames); - } -} - -size_t AudioRingBuffer::ReadFramesAvailable() const { - // All buffers have the same amount available. - return WebRtc_available_read(buffers_[0]); -} - -size_t AudioRingBuffer::WriteFramesAvailable() const { - // All buffers have the same amount available. - return WebRtc_available_write(buffers_[0]); -} - -void AudioRingBuffer::MoveReadPositionForward(size_t frames) { - for (auto buf : buffers_) { - const size_t moved = - static_cast(WebRtc_MoveReadPtr(buf, static_cast(frames))); - RTC_CHECK_EQ(moved, frames); - } -} - -void AudioRingBuffer::MoveReadPositionBackward(size_t frames) { - for (auto buf : buffers_) { - const size_t moved = static_cast( - -WebRtc_MoveReadPtr(buf, -static_cast(frames))); - RTC_CHECK_EQ(moved, frames); - } -} - -} // namespace webrtc diff --git a/webrtc/common_audio/audio_ring_buffer.h b/webrtc/common_audio/audio_ring_buffer.h deleted file mode 100644 index 58e543a..0000000 --- a/webrtc/common_audio/audio_ring_buffer.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2015 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. - */ -#ifndef WEBRTC_COMMON_AUDIO_AUDIO_RING_BUFFER_H_ -#define WEBRTC_COMMON_AUDIO_AUDIO_RING_BUFFER_H_ - -#include -#include - -struct RingBuffer; - -namespace webrtc { - -// A ring buffer tailored for float deinterleaved audio. Any operation that -// cannot be performed as requested will cause a crash (e.g. insufficient data -// in the buffer to fulfill a read request.) -class AudioRingBuffer final { - public: - // Specify the number of channels and maximum number of frames the buffer will - // contain. - AudioRingBuffer(size_t channels, size_t max_frames); - ~AudioRingBuffer(); - - // Copies |data| to the buffer and advances the write pointer. |channels| must - // be the same as at creation time. - void Write(const float* const* data, size_t channels, size_t frames); - - // Copies from the buffer to |data| and advances the read pointer. |channels| - // must be the same as at creation time. - void Read(float* const* data, size_t channels, size_t frames); - - size_t ReadFramesAvailable() const; - size_t WriteFramesAvailable() const; - - // Moves the read position. The forward version advances the read pointer - // towards the write pointer and the backward verison withdraws the read - // pointer away from the write pointer (i.e. flushing and stuffing the buffer - // respectively.) - void MoveReadPositionForward(size_t frames); - void MoveReadPositionBackward(size_t frames); - - private: - // We don't use a ScopedVector because it doesn't support a specialized - // deleter (like scoped_ptr for instance.) - std::vector buffers_; -}; - -} // namespace webrtc - -#endif // WEBRTC_COMMON_AUDIO_AUDIO_RING_BUFFER_H_ diff --git a/webrtc/common_audio/audio_util.cc b/webrtc/common_audio/audio_util.cc index 2ce2eba..b1e4d9a 100644 --- a/webrtc/common_audio/audio_util.cc +++ b/webrtc/common_audio/audio_util.cc @@ -8,9 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/include/audio_util.h" - -#include "webrtc/typedefs.h" +#include "common_audio/include/audio_util.h" namespace webrtc { @@ -24,6 +22,11 @@ void S16ToFloat(const int16_t* src, size_t size, float* dest) { dest[i] = S16ToFloat(src[i]); } +void S16ToFloatS16(const int16_t* src, size_t size, float* dest) { + for (size_t i = 0; i < size; ++i) + dest[i] = src[i]; +} + void FloatS16ToS16(const float* src, size_t size, int16_t* dest) { for (size_t i = 0; i < size; ++i) dest[i] = FloatS16ToS16(src[i]); diff --git a/webrtc/common_audio/blocker.cc b/webrtc/common_audio/blocker.cc deleted file mode 100644 index 0133550..0000000 --- a/webrtc/common_audio/blocker.cc +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2014 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 "webrtc/common_audio/blocker.h" - -#include - -#include "webrtc/base/checks.h" - -namespace { - -// Adds |a| and |b| frame by frame into |result| (basically matrix addition). -void AddFrames(const float* const* a, - size_t a_start_index, - const float* const* b, - int b_start_index, - size_t num_frames, - int num_channels, - float* const* result, - size_t result_start_index) { - for (int i = 0; i < num_channels; ++i) { - for (size_t j = 0; j < num_frames; ++j) { - result[i][j + result_start_index] = - a[i][j + a_start_index] + b[i][j + b_start_index]; - } - } -} - -// Copies |src| into |dst| channel by channel. -void CopyFrames(const float* const* src, - size_t src_start_index, - size_t num_frames, - int num_channels, - float* const* dst, - size_t dst_start_index) { - for (int i = 0; i < num_channels; ++i) { - memcpy(&dst[i][dst_start_index], - &src[i][src_start_index], - num_frames * sizeof(dst[i][dst_start_index])); - } -} - -// Moves |src| into |dst| channel by channel. -void MoveFrames(const float* const* src, - size_t src_start_index, - size_t num_frames, - int num_channels, - float* const* dst, - size_t dst_start_index) { - for (int i = 0; i < num_channels; ++i) { - memmove(&dst[i][dst_start_index], - &src[i][src_start_index], - num_frames * sizeof(dst[i][dst_start_index])); - } -} - -void ZeroOut(float* const* buffer, - size_t starting_idx, - size_t num_frames, - int num_channels) { - for (int i = 0; i < num_channels; ++i) { - memset(&buffer[i][starting_idx], 0, - num_frames * sizeof(buffer[i][starting_idx])); - } -} - -// Pointwise multiplies each channel of |frames| with |window|. Results are -// stored in |frames|. -void ApplyWindow(const float* window, - size_t num_frames, - int num_channels, - float* const* frames) { - for (int i = 0; i < num_channels; ++i) { - for (size_t j = 0; j < num_frames; ++j) { - frames[i][j] = frames[i][j] * window[j]; - } - } -} - -size_t gcd(size_t a, size_t b) { - size_t tmp; - while (b) { - tmp = a; - a = b; - b = tmp % b; - } - return a; -} - -} // namespace - -namespace webrtc { - -Blocker::Blocker(size_t chunk_size, - size_t block_size, - int num_input_channels, - int num_output_channels, - const float* window, - size_t shift_amount, - BlockerCallback* callback) - : chunk_size_(chunk_size), - block_size_(block_size), - num_input_channels_(num_input_channels), - num_output_channels_(num_output_channels), - initial_delay_(block_size_ - gcd(chunk_size, shift_amount)), - frame_offset_(0), - input_buffer_(num_input_channels_, chunk_size_ + initial_delay_), - output_buffer_(chunk_size_ + initial_delay_, num_output_channels_), - input_block_(block_size_, num_input_channels_), - output_block_(block_size_, num_output_channels_), - window_(new float[block_size_]), - shift_amount_(shift_amount), - callback_(callback) { - RTC_CHECK_LE(num_output_channels_, num_input_channels_); - RTC_CHECK_LE(shift_amount_, block_size_); - - memcpy(window_.get(), window, block_size_ * sizeof(*window_.get())); - input_buffer_.MoveReadPositionBackward(initial_delay_); -} - -// When block_size < chunk_size the input and output buffers look like this: -// -// delay* chunk_size chunk_size + delay* -// buffer: <-------------|---------------------|---------------|> -// _a_ _b_ _c_ -// -// On each call to ProcessChunk(): -// 1. New input gets read into sections _b_ and _c_ of the input buffer. -// 2. We block starting from frame_offset. -// 3. We block until we reach a block |bl| that doesn't contain any frames -// from sections _a_ or _b_ of the input buffer. -// 4. We window the current block, fire the callback for processing, window -// again, and overlap/add to the output buffer. -// 5. We copy sections _a_ and _b_ of the output buffer into output. -// 6. For both the input and the output buffers, we copy section _c_ into -// section _a_. -// 7. We set the new frame_offset to be the difference between the first frame -// of |bl| and the border between sections _b_ and _c_. -// -// When block_size > chunk_size the input and output buffers look like this: -// -// chunk_size delay* chunk_size + delay* -// buffer: <-------------|---------------------|---------------|> -// _a_ _b_ _c_ -// -// On each call to ProcessChunk(): -// The procedure is the same as above, except for: -// 1. New input gets read into section _c_ of the input buffer. -// 3. We block until we reach a block |bl| that doesn't contain any frames -// from section _a_ of the input buffer. -// 5. We copy section _a_ of the output buffer into output. -// 6. For both the input and the output buffers, we copy sections _b_ and _c_ -// into section _a_ and _b_. -// 7. We set the new frame_offset to be the difference between the first frame -// of |bl| and the border between sections _a_ and _b_. -// -// * delay here refers to inintial_delay_ -// -// TODO(claguna): Look at using ring buffers to eliminate some copies. -void Blocker::ProcessChunk(const float* const* input, - size_t chunk_size, - int num_input_channels, - int num_output_channels, - float* const* output) { - RTC_CHECK_EQ(chunk_size, chunk_size_); - RTC_CHECK_EQ(num_input_channels, num_input_channels_); - RTC_CHECK_EQ(num_output_channels, num_output_channels_); - - input_buffer_.Write(input, num_input_channels, chunk_size_); - size_t first_frame_in_block = frame_offset_; - - // Loop through blocks. - while (first_frame_in_block < chunk_size_) { - input_buffer_.Read(input_block_.channels(), num_input_channels, - block_size_); - input_buffer_.MoveReadPositionBackward(block_size_ - shift_amount_); - - ApplyWindow(window_.get(), - block_size_, - num_input_channels_, - input_block_.channels()); - callback_->ProcessBlock(input_block_.channels(), - block_size_, - num_input_channels_, - num_output_channels_, - output_block_.channels()); - ApplyWindow(window_.get(), - block_size_, - num_output_channels_, - output_block_.channels()); - - AddFrames(output_buffer_.channels(), - first_frame_in_block, - output_block_.channels(), - 0, - block_size_, - num_output_channels_, - output_buffer_.channels(), - first_frame_in_block); - - first_frame_in_block += shift_amount_; - } - - // Copy output buffer to output - CopyFrames(output_buffer_.channels(), - 0, - chunk_size_, - num_output_channels_, - output, - 0); - - // Copy output buffer [chunk_size_, chunk_size_ + initial_delay] - // to output buffer [0, initial_delay], zero the rest. - MoveFrames(output_buffer_.channels(), - chunk_size, - initial_delay_, - num_output_channels_, - output_buffer_.channels(), - 0); - ZeroOut(output_buffer_.channels(), - initial_delay_, - chunk_size_, - num_output_channels_); - - // Calculate new starting frames. - frame_offset_ = first_frame_in_block - chunk_size_; -} - -} // namespace webrtc diff --git a/webrtc/common_audio/blocker.h b/webrtc/common_audio/blocker.h deleted file mode 100644 index 025638a..0000000 --- a/webrtc/common_audio/blocker.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#ifndef WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_ -#define WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_ - -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/audio_ring_buffer.h" -#include "webrtc/common_audio/channel_buffer.h" - -namespace webrtc { - -// The callback function to process audio in the time domain. Input has already -// been windowed, and output will be windowed. The number of input channels -// must be >= the number of output channels. -class BlockerCallback { - public: - virtual ~BlockerCallback() {} - - virtual void ProcessBlock(const float* const* input, - size_t num_frames, - int num_input_channels, - int num_output_channels, - float* const* output) = 0; -}; - -// The main purpose of Blocker is to abstract away the fact that often we -// receive a different number of audio frames than our transform takes. For -// example, most FFTs work best when the fft-size is a power of 2, but suppose -// we receive 20ms of audio at a sample rate of 48000. That comes to 960 frames -// of audio, which is not a power of 2. Blocker allows us to specify the -// transform and all other necessary processing via the Process() callback -// function without any constraints on the transform-size -// (read: |block_size_|) or received-audio-size (read: |chunk_size_|). -// We handle this for the multichannel audio case, allowing for different -// numbers of input and output channels (for example, beamforming takes 2 or -// more input channels and returns 1 output channel). Audio signals are -// represented as deinterleaved floats in the range [-1, 1]. -// -// Blocker is responsible for: -// - blocking audio while handling potential discontinuities on the edges -// of chunks -// - windowing blocks before sending them to Process() -// - windowing processed blocks, and overlap-adding them together before -// sending back a processed chunk -// -// To use blocker: -// 1. Impelment a BlockerCallback object |bc|. -// 2. Instantiate a Blocker object |b|, passing in |bc|. -// 3. As you receive audio, call b.ProcessChunk() to get processed audio. -// -// A small amount of delay is added to the first received chunk to deal with -// the difference in chunk/block sizes. This delay is <= chunk_size. -// -// Ownership of window is retained by the caller. That is, Blocker makes a -// copy of window and does not attempt to delete it. -class Blocker { - public: - Blocker(size_t chunk_size, - size_t block_size, - int num_input_channels, - int num_output_channels, - const float* window, - size_t shift_amount, - BlockerCallback* callback); - - void ProcessChunk(const float* const* input, - size_t chunk_size, - int num_input_channels, - int num_output_channels, - float* const* output); - - private: - const size_t chunk_size_; - const size_t block_size_; - const int num_input_channels_; - const int num_output_channels_; - - // The number of frames of delay to add at the beginning of the first chunk. - const size_t initial_delay_; - - // The frame index into the input buffer where the first block should be read - // from. This is necessary because shift_amount_ is not necessarily a - // multiple of chunk_size_, so blocks won't line up at the start of the - // buffer. - size_t frame_offset_; - - // Since blocks nearly always overlap, there are certain blocks that require - // frames from the end of one chunk and the beginning of the next chunk. The - // input and output buffers are responsible for saving those frames between - // calls to ProcessChunk(). - // - // Both contain |initial delay| + |chunk_size| frames. The input is a fairly - // standard FIFO, but due to the overlap-add it's harder to use an - // AudioRingBuffer for the output. - AudioRingBuffer input_buffer_; - ChannelBuffer output_buffer_; - - // Space for the input block (can't wrap because of windowing). - ChannelBuffer input_block_; - - // Space for the output block (can't wrap because of overlap/add). - ChannelBuffer output_block_; - - rtc::scoped_ptr window_; - - // The amount of frames between the start of contiguous blocks. For example, - // |shift_amount_| = |block_size_| / 2 for a Hann window. - size_t shift_amount_; - - BlockerCallback* callback_; -}; - -} // namespace webrtc - -#endif // WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_ diff --git a/webrtc/common_audio/channel_buffer.cc b/webrtc/common_audio/channel_buffer.cc index d3dc7c0..b9b8c25 100644 --- a/webrtc/common_audio/channel_buffer.cc +++ b/webrtc/common_audio/channel_buffer.cc @@ -8,18 +8,25 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/channel_buffer.h" +#include "common_audio/channel_buffer.h" + +#include + +#include "common_audio/include/audio_util.h" +#include "rtc_base/checks.h" namespace webrtc { IFChannelBuffer::IFChannelBuffer(size_t num_frames, - int num_channels, + size_t num_channels, size_t num_bands) : ivalid_(true), ibuf_(num_frames, num_channels, num_bands), fvalid_(true), fbuf_(num_frames, num_channels, num_bands) {} +IFChannelBuffer::~IFChannelBuffer() = default; + ChannelBuffer* IFChannelBuffer::ibuf() { RefreshI(); fvalid_ = false; @@ -44,10 +51,11 @@ const ChannelBuffer* IFChannelBuffer::fbuf_const() const { void IFChannelBuffer::RefreshF() const { if (!fvalid_) { - assert(ivalid_); + RTC_DCHECK(ivalid_); + fbuf_.set_num_channels(ibuf_.num_channels()); const int16_t* const* int_channels = ibuf_.channels(); float* const* float_channels = fbuf_.channels(); - for (int i = 0; i < ibuf_.num_channels(); ++i) { + for (size_t i = 0; i < ibuf_.num_channels(); ++i) { for (size_t j = 0; j < ibuf_.num_frames(); ++j) { float_channels[i][j] = int_channels[i][j]; } @@ -58,13 +66,12 @@ void IFChannelBuffer::RefreshF() const { void IFChannelBuffer::RefreshI() const { if (!ivalid_) { - assert(fvalid_); + RTC_DCHECK(fvalid_); int16_t* const* int_channels = ibuf_.channels(); + ibuf_.set_num_channels(fbuf_.num_channels()); const float* const* float_channels = fbuf_.channels(); - for (int i = 0; i < ibuf_.num_channels(); ++i) { - FloatS16ToS16(float_channels[i], - ibuf_.num_frames(), - int_channels[i]); + for (size_t i = 0; i < fbuf_.num_channels(); ++i) { + FloatS16ToS16(float_channels[i], ibuf_.num_frames(), int_channels[i]); } ivalid_ = true; } diff --git a/webrtc/common_audio/channel_buffer.h b/webrtc/common_audio/channel_buffer.h index a308d7b..f027080 100644 --- a/webrtc/common_audio/channel_buffer.h +++ b/webrtc/common_audio/channel_buffer.h @@ -8,17 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_CHANNEL_BUFFER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_CHANNEL_BUFFER_H_ +#ifndef COMMON_AUDIO_CHANNEL_BUFFER_H_ +#define COMMON_AUDIO_CHANNEL_BUFFER_H_ #include -#include "webrtc/base/checks.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/include/audio_util.h" -#ifndef WEBRTC_AUDIO_PROCESSING_ONLY_BUILD -#include "webrtc/test/testsupport/gtest_prod_util.h" -#endif +#include +#include + +#include "api/array_view.h" +#include "common_audio/include/audio_util.h" +#include "rtc_base/checks.h" +#include "rtc_base/gtest_prod_util.h" namespace webrtc { @@ -41,49 +42,68 @@ namespace webrtc { template class ChannelBuffer { public: - ChannelBuffer(size_t num_frames, - int num_channels, - size_t num_bands = 1) + ChannelBuffer(size_t num_frames, size_t num_channels, size_t num_bands = 1) : data_(new T[num_frames * num_channels]()), channels_(new T*[num_channels * num_bands]), bands_(new T*[num_channels * num_bands]), num_frames_(num_frames), num_frames_per_band_(num_frames / num_bands), + num_allocated_channels_(num_channels), num_channels_(num_channels), - num_bands_(num_bands) { - for (int i = 0; i < num_channels_; ++i) { - for (size_t j = 0; j < num_bands_; ++j) { - channels_[j * num_channels_ + i] = - &data_[i * num_frames_ + j * num_frames_per_band_]; - bands_[i * num_bands_ + j] = channels_[j * num_channels_ + i]; + num_bands_(num_bands), + bands_view_(num_allocated_channels_, + std::vector>(num_bands_)), + channels_view_( + num_bands_, + std::vector>(num_allocated_channels_)) { + // Temporarily cast away const_ness to allow populating the array views. + auto* bands_view = + const_cast>>*>(&bands_view_); + auto* channels_view = + const_cast>>*>( + &channels_view_); + + for (size_t ch = 0; ch < num_allocated_channels_; ++ch) { + for (size_t band = 0; band < num_bands_; ++band) { + (*channels_view)[band][ch] = rtc::ArrayView( + &data_[ch * num_frames_ + band * num_frames_per_band_], + num_frames_per_band_); + (*bands_view)[ch][band] = channels_view_[band][ch]; + channels_[band * num_allocated_channels_ + ch] = + channels_view_[band][ch].data(); + bands_[ch * num_bands_ + band] = + channels_[band * num_allocated_channels_ + ch]; } } } - // Returns a pointer array to the full-band channels (or lower band channels). - // Usage: - // channels()[channel][sample]. - // Where: - // 0 <= channel < |num_channels_| - // 0 <= sample < |num_frames_| - T* const* channels() { return channels(0); } - const T* const* channels() const { return channels(0); } - - // Returns a pointer array to the channels for a specific band. - // Usage: - // channels(band)[channel][sample]. + // Returns a pointer array to the channels. + // If band is explicitly specificed, the channels for a specific band are + // returned and the usage becomes: channels(band)[channel][sample]. // Where: // 0 <= band < |num_bands_| - // 0 <= channel < |num_channels_| + // 0 <= channel < |num_allocated_channels_| // 0 <= sample < |num_frames_per_band_| - const T* const* channels(size_t band) const { + + // If band is not explicitly specified, the full-band channels (or lower band + // channels) are returned and the usage becomes: channels()[channel][sample]. + // Where: + // 0 <= channel < |num_allocated_channels_| + // 0 <= sample < |num_frames_| + const T* const* channels(size_t band = 0) const { RTC_DCHECK_LT(band, num_bands_); - return &channels_[band * num_channels_]; + return &channels_[band * num_allocated_channels_]; } - T* const* channels(size_t band) { + T* const* channels(size_t band = 0) { const ChannelBuffer* t = this; return const_cast(t->channels(band)); } + rtc::ArrayView> channels_view(size_t band = 0) { + return channels_view_[band]; + } + rtc::ArrayView> channels_view(size_t band = 0) const { + return channels_view_[band]; + } // Returns a pointer array to the bands for a specific channel. // Usage: @@ -92,21 +112,28 @@ class ChannelBuffer { // 0 <= channel < |num_channels_| // 0 <= band < |num_bands_| // 0 <= sample < |num_frames_per_band_| - const T* const* bands(int channel) const { + const T* const* bands(size_t channel) const { RTC_DCHECK_LT(channel, num_channels_); RTC_DCHECK_GE(channel, 0); return &bands_[channel * num_bands_]; } - T* const* bands(int channel) { + T* const* bands(size_t channel) { const ChannelBuffer* t = this; return const_cast(t->bands(channel)); } + rtc::ArrayView> bands_view(size_t channel) { + return bands_view_[channel]; + } + rtc::ArrayView> bands_view(size_t channel) const { + return bands_view_[channel]; + } + // Sets the |slice| pointers to the |start_frame| position for each channel. // Returns |slice| for convenience. const T* const* Slice(T** slice, size_t start_frame) const { RTC_DCHECK_LT(start_frame, num_frames_); - for (int i = 0; i < num_channels_; ++i) + for (size_t i = 0; i < num_channels_; ++i) slice[i] = &channels_[i][start_frame]; return slice; } @@ -117,9 +144,14 @@ class ChannelBuffer { size_t num_frames() const { return num_frames_; } size_t num_frames_per_band() const { return num_frames_per_band_; } - int num_channels() const { return num_channels_; } + size_t num_channels() const { return num_channels_; } size_t num_bands() const { return num_bands_; } - size_t size() const {return num_frames_ * num_channels_; } + size_t size() const { return num_frames_ * num_allocated_channels_; } + + void set_num_channels(size_t num_channels) { + RTC_DCHECK_LE(num_channels, num_allocated_channels_); + num_channels_ = num_channels; + } void SetDataForTesting(const T* data, size_t size) { RTC_CHECK_EQ(size, this->size()); @@ -127,13 +159,18 @@ class ChannelBuffer { } private: - rtc::scoped_ptr data_; - rtc::scoped_ptr channels_; - rtc::scoped_ptr bands_; + std::unique_ptr data_; + std::unique_ptr channels_; + std::unique_ptr bands_; const size_t num_frames_; const size_t num_frames_per_band_; - const int num_channels_; + // Number of channels the internal buffer holds. + const size_t num_allocated_channels_; + // Number of channels the user sees. + size_t num_channels_; const size_t num_bands_; + const std::vector>> bands_view_; + const std::vector>> channels_view_; }; // One int16_t and one float ChannelBuffer that are kept in sync. The sync is @@ -144,7 +181,8 @@ class ChannelBuffer { // fbuf() until the next call to any of the other functions. class IFChannelBuffer { public: - IFChannelBuffer(size_t num_frames, int num_channels, size_t num_bands = 1); + IFChannelBuffer(size_t num_frames, size_t num_channels, size_t num_bands = 1); + ~IFChannelBuffer(); ChannelBuffer* ibuf(); ChannelBuffer* fbuf(); @@ -153,7 +191,13 @@ class IFChannelBuffer { size_t num_frames() const { return ibuf_.num_frames(); } size_t num_frames_per_band() const { return ibuf_.num_frames_per_band(); } - int num_channels() const { return ibuf_.num_channels(); } + size_t num_channels() const { + return ivalid_ ? ibuf_.num_channels() : fbuf_.num_channels(); + } + void set_num_channels(size_t num_channels) { + ibuf_.set_num_channels(num_channels); + fbuf_.set_num_channels(num_channels); + } size_t num_bands() const { return ibuf_.num_bands(); } private: @@ -168,4 +212,4 @@ class IFChannelBuffer { } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_CHANNEL_BUFFER_H_ +#endif // COMMON_AUDIO_CHANNEL_BUFFER_H_ diff --git a/webrtc/common_audio/fft4g.c b/webrtc/common_audio/fft4g.c deleted file mode 100644 index 9cf7b9f..0000000 --- a/webrtc/common_audio/fft4g.c +++ /dev/null @@ -1,1332 +0,0 @@ -/* - * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html - * Copyright Takuya OOURA, 1996-2001 - * - * You may use, copy, modify and distribute this code for any purpose (include - * commercial use) and without fee. Please refer to this package when you modify - * this code. - * - * Changes: - * Trivial type modifications by the WebRTC authors. - */ - -/* -Fast Fourier/Cosine/Sine Transform - dimension :one - data length :power of 2 - decimation :frequency - radix :4, 2 - data :inplace - table :use -functions - cdft: Complex Discrete Fourier Transform - rdft: Real Discrete Fourier Transform - ddct: Discrete Cosine Transform - ddst: Discrete Sine Transform - dfct: Cosine Transform of RDFT (Real Symmetric DFT) - dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) -function prototypes - void cdft(int, int, float *, int *, float *); - void rdft(size_t, int, float *, size_t *, float *); - void ddct(int, int, float *, int *, float *); - void ddst(int, int, float *, int *, float *); - void dfct(int, float *, float *, int *, float *); - void dfst(int, float *, float *, int *, float *); - - --------- Complex DFT (Discrete Fourier Transform) -------- - [definition] - - X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k - X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k - ip[0] = 0; // first time only - cdft(2*n, 1, a, ip, w); - - ip[0] = 0; // first time only - cdft(2*n, -1, a, ip, w); - [parameters] - 2*n :data length (int) - n >= 1, n = power of 2 - a[0...2*n-1] :input/output data (float *) - input data - a[2*j] = Re(x[j]), - a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) - strictly, - length of ip >= - 2+(1<<(int)(log(n+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n/2-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - cdft(2*n, -1, a, ip, w); - is - cdft(2*n, 1, a, ip, w); - for (j = 0; j <= 2 * n - 1; j++) { - a[j] *= 1.0 / n; - } - . - - --------- Real DFT / Inverse of Real DFT -------- - [definition] - RDFT - R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 - I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0 IRDFT (excluding scale) - a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + - sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + - sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k - ip[0] = 0; // first time only - rdft(n, 1, a, ip, w); - - ip[0] = 0; // first time only - rdft(n, -1, a, ip, w); - [parameters] - n :data length (size_t) - n >= 2, n = power of 2 - a[0...n-1] :input/output data (float *) - - output data - a[2*k] = R[k], 0<=k - input data - a[2*j] = R[j], 0<=j= 2+sqrt(n/2) - strictly, - length of ip >= - 2+(1<<(int)(log(n/2+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n/2-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - rdft(n, 1, a, ip, w); - is - rdft(n, -1, a, ip, w); - for (j = 0; j <= n - 1; j++) { - a[j] *= 2.0 / n; - } - . - - --------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- - [definition] - IDCT (excluding scale) - C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k DCT - C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k - ip[0] = 0; // first time only - ddct(n, 1, a, ip, w); - - ip[0] = 0; // first time only - ddct(n, -1, a, ip, w); - [parameters] - n :data length (int) - n >= 2, n = power of 2 - a[0...n-1] :input/output data (float *) - output data - a[k] = C[k], 0<=k= 2+sqrt(n/2) - strictly, - length of ip >= - 2+(1<<(int)(log(n/2+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n*5/4-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - ddct(n, -1, a, ip, w); - is - a[0] *= 0.5; - ddct(n, 1, a, ip, w); - for (j = 0; j <= n - 1; j++) { - a[j] *= 2.0 / n; - } - . - - --------- DST (Discrete Sine Transform) / Inverse of DST -------- - [definition] - IDST (excluding scale) - S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k DST - S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0 - ip[0] = 0; // first time only - ddst(n, 1, a, ip, w); - - ip[0] = 0; // first time only - ddst(n, -1, a, ip, w); - [parameters] - n :data length (int) - n >= 2, n = power of 2 - a[0...n-1] :input/output data (float *) - - input data - a[j] = A[j], 0 - output data - a[k] = S[k], 0= 2+sqrt(n/2) - strictly, - length of ip >= - 2+(1<<(int)(log(n/2+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n*5/4-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - ddst(n, -1, a, ip, w); - is - a[0] *= 0.5; - ddst(n, 1, a, ip, w); - for (j = 0; j <= n - 1; j++) { - a[j] *= 2.0 / n; - } - . - - --------- Cosine Transform of RDFT (Real Symmetric DFT) -------- - [definition] - C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n - [usage] - ip[0] = 0; // first time only - dfct(n, a, t, ip, w); - [parameters] - n :data length - 1 (int) - n >= 2, n = power of 2 - a[0...n] :input/output data (float *) - output data - a[k] = C[k], 0<=k<=n - t[0...n/2] :work area (float *) - ip[0...*] :work area for bit reversal (int *) - length of ip >= 2+sqrt(n/4) - strictly, - length of ip >= - 2+(1<<(int)(log(n/4+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n*5/8-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - a[0] *= 0.5; - a[n] *= 0.5; - dfct(n, a, t, ip, w); - is - a[0] *= 0.5; - a[n] *= 0.5; - dfct(n, a, t, ip, w); - for (j = 0; j <= n; j++) { - a[j] *= 2.0 / n; - } - . - - --------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- - [definition] - S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0= 2, n = power of 2 - a[0...n-1] :input/output data (float *) - output data - a[k] = S[k], 0= 2+sqrt(n/4) - strictly, - length of ip >= - 2+(1<<(int)(log(n/4+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n*5/8-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - dfst(n, a, t, ip, w); - is - dfst(n, a, t, ip, w); - for (j = 1; j <= n - 1; j++) { - a[j] *= 2.0 / n; - } - . - - -Appendix : - The cos/sin table is recalculated when the larger table required. - w[] and ip[] are compatible with all routines. -*/ - -#include - -static void makewt(size_t nw, size_t *ip, float *w); -static void makect(size_t nc, size_t *ip, float *c); -static void bitrv2(size_t n, size_t *ip, float *a); -#if 0 // Not used. -static void bitrv2conj(int n, int *ip, float *a); -#endif -static void cftfsub(size_t n, float *a, float *w); -static void cftbsub(size_t n, float *a, float *w); -static void cft1st(size_t n, float *a, float *w); -static void cftmdl(size_t n, size_t l, float *a, float *w); -static void rftfsub(size_t n, float *a, size_t nc, float *c); -static void rftbsub(size_t n, float *a, size_t nc, float *c); -#if 0 // Not used. -static void dctsub(int n, float *a, int nc, float *c) -static void dstsub(int n, float *a, int nc, float *c) -#endif - - -#if 0 // Not used. -void WebRtc_cdft(int n, int isgn, float *a, int *ip, float *w) -{ - if (n > (ip[0] << 2)) { - makewt(n >> 2, ip, w); - } - if (n > 4) { - if (isgn >= 0) { - bitrv2(n, ip + 2, a); - cftfsub(n, a, w); - } else { - bitrv2conj(n, ip + 2, a); - cftbsub(n, a, w); - } - } else if (n == 4) { - cftfsub(n, a, w); - } -} -#endif - - -void WebRtc_rdft(size_t n, int isgn, float *a, size_t *ip, float *w) -{ - size_t nw, nc; - float xi; - - nw = ip[0]; - if (n > (nw << 2)) { - nw = n >> 2; - makewt(nw, ip, w); - } - nc = ip[1]; - if (n > (nc << 2)) { - nc = n >> 2; - makect(nc, ip, w + nw); - } - if (isgn >= 0) { - if (n > 4) { - bitrv2(n, ip + 2, a); - cftfsub(n, a, w); - rftfsub(n, a, nc, w + nw); - } else if (n == 4) { - cftfsub(n, a, w); - } - xi = a[0] - a[1]; - a[0] += a[1]; - a[1] = xi; - } else { - a[1] = 0.5f * (a[0] - a[1]); - a[0] -= a[1]; - if (n > 4) { - rftbsub(n, a, nc, w + nw); - bitrv2(n, ip + 2, a); - cftbsub(n, a, w); - } else if (n == 4) { - cftfsub(n, a, w); - } - } -} - -#if 0 // Not used. -static void ddct(int n, int isgn, float *a, int *ip, float *w) -{ - int j, nw, nc; - float xr; - - nw = ip[0]; - if (n > (nw << 2)) { - nw = n >> 2; - makewt(nw, ip, w); - } - nc = ip[1]; - if (n > nc) { - nc = n; - makect(nc, ip, w + nw); - } - if (isgn < 0) { - xr = a[n - 1]; - for (j = n - 2; j >= 2; j -= 2) { - a[j + 1] = a[j] - a[j - 1]; - a[j] += a[j - 1]; - } - a[1] = a[0] - xr; - a[0] += xr; - if (n > 4) { - rftbsub(n, a, nc, w + nw); - bitrv2(n, ip + 2, a); - cftbsub(n, a, w); - } else if (n == 4) { - cftfsub(n, a, w); - } - } - dctsub(n, a, nc, w + nw); - if (isgn >= 0) { - if (n > 4) { - bitrv2(n, ip + 2, a); - cftfsub(n, a, w); - rftfsub(n, a, nc, w + nw); - } else if (n == 4) { - cftfsub(n, a, w); - } - xr = a[0] - a[1]; - a[0] += a[1]; - for (j = 2; j < n; j += 2) { - a[j - 1] = a[j] - a[j + 1]; - a[j] += a[j + 1]; - } - a[n - 1] = xr; - } -} - - -static void ddst(int n, int isgn, float *a, int *ip, float *w) -{ - int j, nw, nc; - float xr; - - nw = ip[0]; - if (n > (nw << 2)) { - nw = n >> 2; - makewt(nw, ip, w); - } - nc = ip[1]; - if (n > nc) { - nc = n; - makect(nc, ip, w + nw); - } - if (isgn < 0) { - xr = a[n - 1]; - for (j = n - 2; j >= 2; j -= 2) { - a[j + 1] = -a[j] - a[j - 1]; - a[j] -= a[j - 1]; - } - a[1] = a[0] + xr; - a[0] -= xr; - if (n > 4) { - rftbsub(n, a, nc, w + nw); - bitrv2(n, ip + 2, a); - cftbsub(n, a, w); - } else if (n == 4) { - cftfsub(n, a, w); - } - } - dstsub(n, a, nc, w + nw); - if (isgn >= 0) { - if (n > 4) { - bitrv2(n, ip + 2, a); - cftfsub(n, a, w); - rftfsub(n, a, nc, w + nw); - } else if (n == 4) { - cftfsub(n, a, w); - } - xr = a[0] - a[1]; - a[0] += a[1]; - for (j = 2; j < n; j += 2) { - a[j - 1] = -a[j] - a[j + 1]; - a[j] -= a[j + 1]; - } - a[n - 1] = -xr; - } -} - - -static void dfct(int n, float *a, float *t, int *ip, float *w) -{ - int j, k, l, m, mh, nw, nc; - float xr, xi, yr, yi; - - nw = ip[0]; - if (n > (nw << 3)) { - nw = n >> 3; - makewt(nw, ip, w); - } - nc = ip[1]; - if (n > (nc << 1)) { - nc = n >> 1; - makect(nc, ip, w + nw); - } - m = n >> 1; - yi = a[m]; - xi = a[0] + a[n]; - a[0] -= a[n]; - t[0] = xi - yi; - t[m] = xi + yi; - if (n > 2) { - mh = m >> 1; - for (j = 1; j < mh; j++) { - k = m - j; - xr = a[j] - a[n - j]; - xi = a[j] + a[n - j]; - yr = a[k] - a[n - k]; - yi = a[k] + a[n - k]; - a[j] = xr; - a[k] = yr; - t[j] = xi - yi; - t[k] = xi + yi; - } - t[mh] = a[mh] + a[n - mh]; - a[mh] -= a[n - mh]; - dctsub(m, a, nc, w + nw); - if (m > 4) { - bitrv2(m, ip + 2, a); - cftfsub(m, a, w); - rftfsub(m, a, nc, w + nw); - } else if (m == 4) { - cftfsub(m, a, w); - } - a[n - 1] = a[0] - a[1]; - a[1] = a[0] + a[1]; - for (j = m - 2; j >= 2; j -= 2) { - a[2 * j + 1] = a[j] + a[j + 1]; - a[2 * j - 1] = a[j] - a[j + 1]; - } - l = 2; - m = mh; - while (m >= 2) { - dctsub(m, t, nc, w + nw); - if (m > 4) { - bitrv2(m, ip + 2, t); - cftfsub(m, t, w); - rftfsub(m, t, nc, w + nw); - } else if (m == 4) { - cftfsub(m, t, w); - } - a[n - l] = t[0] - t[1]; - a[l] = t[0] + t[1]; - k = 0; - for (j = 2; j < m; j += 2) { - k += l << 2; - a[k - l] = t[j] - t[j + 1]; - a[k + l] = t[j] + t[j + 1]; - } - l <<= 1; - mh = m >> 1; - for (j = 0; j < mh; j++) { - k = m - j; - t[j] = t[m + k] - t[m + j]; - t[k] = t[m + k] + t[m + j]; - } - t[mh] = t[m + mh]; - m = mh; - } - a[l] = t[0]; - a[n] = t[2] - t[1]; - a[0] = t[2] + t[1]; - } else { - a[1] = a[0]; - a[2] = t[0]; - a[0] = t[1]; - } -} - -static void dfst(int n, float *a, float *t, int *ip, float *w) -{ - int j, k, l, m, mh, nw, nc; - float xr, xi, yr, yi; - - nw = ip[0]; - if (n > (nw << 3)) { - nw = n >> 3; - makewt(nw, ip, w); - } - nc = ip[1]; - if (n > (nc << 1)) { - nc = n >> 1; - makect(nc, ip, w + nw); - } - if (n > 2) { - m = n >> 1; - mh = m >> 1; - for (j = 1; j < mh; j++) { - k = m - j; - xr = a[j] + a[n - j]; - xi = a[j] - a[n - j]; - yr = a[k] + a[n - k]; - yi = a[k] - a[n - k]; - a[j] = xr; - a[k] = yr; - t[j] = xi + yi; - t[k] = xi - yi; - } - t[0] = a[mh] - a[n - mh]; - a[mh] += a[n - mh]; - a[0] = a[m]; - dstsub(m, a, nc, w + nw); - if (m > 4) { - bitrv2(m, ip + 2, a); - cftfsub(m, a, w); - rftfsub(m, a, nc, w + nw); - } else if (m == 4) { - cftfsub(m, a, w); - } - a[n - 1] = a[1] - a[0]; - a[1] = a[0] + a[1]; - for (j = m - 2; j >= 2; j -= 2) { - a[2 * j + 1] = a[j] - a[j + 1]; - a[2 * j - 1] = -a[j] - a[j + 1]; - } - l = 2; - m = mh; - while (m >= 2) { - dstsub(m, t, nc, w + nw); - if (m > 4) { - bitrv2(m, ip + 2, t); - cftfsub(m, t, w); - rftfsub(m, t, nc, w + nw); - } else if (m == 4) { - cftfsub(m, t, w); - } - a[n - l] = t[1] - t[0]; - a[l] = t[0] + t[1]; - k = 0; - for (j = 2; j < m; j += 2) { - k += l << 2; - a[k - l] = -t[j] - t[j + 1]; - a[k + l] = t[j] - t[j + 1]; - } - l <<= 1; - mh = m >> 1; - for (j = 1; j < mh; j++) { - k = m - j; - t[j] = t[m + k] + t[m + j]; - t[k] = t[m + k] - t[m + j]; - } - t[0] = t[m + mh]; - m = mh; - } - a[l] = t[0]; - } - a[0] = 0; -} -#endif // Not used. - - -/* -------- initializing routines -------- */ - - -#include - -static void makewt(size_t nw, size_t *ip, float *w) -{ - size_t j, nwh; - float delta, x, y; - - ip[0] = nw; - ip[1] = 1; - if (nw > 2) { - nwh = nw >> 1; - delta = atanf(1.0f) / nwh; - w[0] = 1; - w[1] = 0; - w[nwh] = (float)cos(delta * nwh); - w[nwh + 1] = w[nwh]; - if (nwh > 2) { - for (j = 2; j < nwh; j += 2) { - x = (float)cos(delta * j); - y = (float)sin(delta * j); - w[j] = x; - w[j + 1] = y; - w[nw - j] = y; - w[nw - j + 1] = x; - } - bitrv2(nw, ip + 2, w); - } - } -} - - -static void makect(size_t nc, size_t *ip, float *c) -{ - size_t j, nch; - float delta; - - ip[1] = nc; - if (nc > 1) { - nch = nc >> 1; - delta = atanf(1.0f) / nch; - c[0] = (float)cos(delta * nch); - c[nch] = 0.5f * c[0]; - for (j = 1; j < nch; j++) { - c[j] = 0.5f * (float)cos(delta * j); - c[nc - j] = 0.5f * (float)sin(delta * j); - } - } -} - - -/* -------- child routines -------- */ - - -static void bitrv2(size_t n, size_t *ip, float *a) -{ - size_t j, j1, k, k1, l, m, m2; - float xr, xi, yr, yi; - - ip[0] = 0; - l = n; - m = 1; - while ((m << 3) < l) { - l >>= 1; - for (j = 0; j < m; j++) { - ip[m + j] = ip[j] + l; - } - m <<= 1; - } - m2 = 2 * m; - if ((m << 3) == l) { - for (k = 0; k < m; k++) { - for (j = 0; j < k; j++) { - j1 = 2 * j + ip[k]; - k1 = 2 * k + ip[j]; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 -= m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - j1 = 2 * k + m2 + ip[k]; - k1 = j1 + m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - } else { - for (k = 1; k < m; k++) { - for (j = 0; j < k; j++) { - j1 = 2 * j + ip[k]; - k1 = 2 * k + ip[j]; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - } - } -} - -#if 0 // Not used. -static void bitrv2conj(int n, int *ip, float *a) -{ - int j, j1, k, k1, l, m, m2; - float xr, xi, yr, yi; - - ip[0] = 0; - l = n; - m = 1; - while ((m << 3) < l) { - l >>= 1; - for (j = 0; j < m; j++) { - ip[m + j] = ip[j] + l; - } - m <<= 1; - } - m2 = 2 * m; - if ((m << 3) == l) { - for (k = 0; k < m; k++) { - for (j = 0; j < k; j++) { - j1 = 2 * j + ip[k]; - k1 = 2 * k + ip[j]; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 -= m2; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - k1 = 2 * k + ip[k]; - a[k1 + 1] = -a[k1 + 1]; - j1 = k1 + m2; - k1 = j1 + m2; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - k1 += m2; - a[k1 + 1] = -a[k1 + 1]; - } - } else { - a[1] = -a[1]; - a[m2 + 1] = -a[m2 + 1]; - for (k = 1; k < m; k++) { - for (j = 0; j < k; j++) { - j1 = 2 * j + ip[k]; - k1 = 2 * k + ip[j]; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += m2; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - k1 = 2 * k + ip[k]; - a[k1 + 1] = -a[k1 + 1]; - a[k1 + m2 + 1] = -a[k1 + m2 + 1]; - } - } -} -#endif - -static void cftfsub(size_t n, float *a, float *w) -{ - size_t j, j1, j2, j3, l; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - l = 2; - if (n > 8) { - cft1st(n, a, w); - l = 8; - while ((l << 2) < n) { - cftmdl(n, l, a, w); - l <<= 2; - } - } - if ((l << 2) == n) { - for (j = 0; j < l; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - a[j2] = x0r - x2r; - a[j2 + 1] = x0i - x2i; - a[j1] = x1r - x3i; - a[j1 + 1] = x1i + x3r; - a[j3] = x1r + x3i; - a[j3 + 1] = x1i - x3r; - } - } else { - for (j = 0; j < l; j += 2) { - j1 = j + l; - x0r = a[j] - a[j1]; - x0i = a[j + 1] - a[j1 + 1]; - a[j] += a[j1]; - a[j + 1] += a[j1 + 1]; - a[j1] = x0r; - a[j1 + 1] = x0i; - } - } -} - - -static void cftbsub(size_t n, float *a, float *w) -{ - size_t j, j1, j2, j3, l; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - l = 2; - if (n > 8) { - cft1st(n, a, w); - l = 8; - while ((l << 2) < n) { - cftmdl(n, l, a, w); - l <<= 2; - } - } - if ((l << 2) == n) { - for (j = 0; j < l; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = -a[j + 1] - a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = -a[j + 1] + a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i - x2i; - a[j2] = x0r - x2r; - a[j2 + 1] = x0i + x2i; - a[j1] = x1r - x3i; - a[j1 + 1] = x1i - x3r; - a[j3] = x1r + x3i; - a[j3 + 1] = x1i + x3r; - } - } else { - for (j = 0; j < l; j += 2) { - j1 = j + l; - x0r = a[j] - a[j1]; - x0i = -a[j + 1] + a[j1 + 1]; - a[j] += a[j1]; - a[j + 1] = -a[j + 1] - a[j1 + 1]; - a[j1] = x0r; - a[j1 + 1] = x0i; - } - } -} - - -static void cft1st(size_t n, float *a, float *w) -{ - size_t j, k1, k2; - float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - x0r = a[0] + a[2]; - x0i = a[1] + a[3]; - x1r = a[0] - a[2]; - x1i = a[1] - a[3]; - x2r = a[4] + a[6]; - x2i = a[5] + a[7]; - x3r = a[4] - a[6]; - x3i = a[5] - a[7]; - a[0] = x0r + x2r; - a[1] = x0i + x2i; - a[4] = x0r - x2r; - a[5] = x0i - x2i; - a[2] = x1r - x3i; - a[3] = x1i + x3r; - a[6] = x1r + x3i; - a[7] = x1i - x3r; - wk1r = w[2]; - x0r = a[8] + a[10]; - x0i = a[9] + a[11]; - x1r = a[8] - a[10]; - x1i = a[9] - a[11]; - x2r = a[12] + a[14]; - x2i = a[13] + a[15]; - x3r = a[12] - a[14]; - x3i = a[13] - a[15]; - a[8] = x0r + x2r; - a[9] = x0i + x2i; - a[12] = x2i - x0i; - a[13] = x0r - x2r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[10] = wk1r * (x0r - x0i); - a[11] = wk1r * (x0r + x0i); - x0r = x3i + x1r; - x0i = x3r - x1i; - a[14] = wk1r * (x0i - x0r); - a[15] = wk1r * (x0i + x0r); - k1 = 0; - for (j = 16; j < n; j += 16) { - k1 += 2; - k2 = 2 * k1; - wk2r = w[k1]; - wk2i = w[k1 + 1]; - wk1r = w[k2]; - wk1i = w[k2 + 1]; - wk3r = wk1r - 2 * wk2i * wk1i; - wk3i = 2 * wk2i * wk1r - wk1i; - x0r = a[j] + a[j + 2]; - x0i = a[j + 1] + a[j + 3]; - x1r = a[j] - a[j + 2]; - x1i = a[j + 1] - a[j + 3]; - x2r = a[j + 4] + a[j + 6]; - x2i = a[j + 5] + a[j + 7]; - x3r = a[j + 4] - a[j + 6]; - x3i = a[j + 5] - a[j + 7]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j + 4] = wk2r * x0r - wk2i * x0i; - a[j + 5] = wk2r * x0i + wk2i * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j + 2] = wk1r * x0r - wk1i * x0i; - a[j + 3] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j + 6] = wk3r * x0r - wk3i * x0i; - a[j + 7] = wk3r * x0i + wk3i * x0r; - wk1r = w[k2 + 2]; - wk1i = w[k2 + 3]; - wk3r = wk1r - 2 * wk2r * wk1i; - wk3i = 2 * wk2r * wk1r - wk1i; - x0r = a[j + 8] + a[j + 10]; - x0i = a[j + 9] + a[j + 11]; - x1r = a[j + 8] - a[j + 10]; - x1i = a[j + 9] - a[j + 11]; - x2r = a[j + 12] + a[j + 14]; - x2i = a[j + 13] + a[j + 15]; - x3r = a[j + 12] - a[j + 14]; - x3i = a[j + 13] - a[j + 15]; - a[j + 8] = x0r + x2r; - a[j + 9] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j + 12] = -wk2i * x0r - wk2r * x0i; - a[j + 13] = -wk2i * x0i + wk2r * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j + 10] = wk1r * x0r - wk1i * x0i; - a[j + 11] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j + 14] = wk3r * x0r - wk3i * x0i; - a[j + 15] = wk3r * x0i + wk3i * x0r; - } -} - - -static void cftmdl(size_t n, size_t l, float *a, float *w) -{ - size_t j, j1, j2, j3, k, k1, k2, m, m2; - float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - m = l << 2; - for (j = 0; j < l; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - a[j2] = x0r - x2r; - a[j2 + 1] = x0i - x2i; - a[j1] = x1r - x3i; - a[j1 + 1] = x1i + x3r; - a[j3] = x1r + x3i; - a[j3 + 1] = x1i - x3r; - } - wk1r = w[2]; - for (j = m; j < l + m; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - a[j2] = x2i - x0i; - a[j2 + 1] = x0r - x2r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j1] = wk1r * (x0r - x0i); - a[j1 + 1] = wk1r * (x0r + x0i); - x0r = x3i + x1r; - x0i = x3r - x1i; - a[j3] = wk1r * (x0i - x0r); - a[j3 + 1] = wk1r * (x0i + x0r); - } - k1 = 0; - m2 = 2 * m; - for (k = m2; k < n; k += m2) { - k1 += 2; - k2 = 2 * k1; - wk2r = w[k1]; - wk2i = w[k1 + 1]; - wk1r = w[k2]; - wk1i = w[k2 + 1]; - wk3r = wk1r - 2 * wk2i * wk1i; - wk3i = 2 * wk2i * wk1r - wk1i; - for (j = k; j < l + k; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j2] = wk2r * x0r - wk2i * x0i; - a[j2 + 1] = wk2r * x0i + wk2i * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j1] = wk1r * x0r - wk1i * x0i; - a[j1 + 1] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j3] = wk3r * x0r - wk3i * x0i; - a[j3 + 1] = wk3r * x0i + wk3i * x0r; - } - wk1r = w[k2 + 2]; - wk1i = w[k2 + 3]; - wk3r = wk1r - 2 * wk2r * wk1i; - wk3i = 2 * wk2r * wk1r - wk1i; - for (j = k + m; j < l + (k + m); j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j2] = -wk2i * x0r - wk2r * x0i; - a[j2 + 1] = -wk2i * x0i + wk2r * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j1] = wk1r * x0r - wk1i * x0i; - a[j1 + 1] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j3] = wk3r * x0r - wk3i * x0i; - a[j3 + 1] = wk3r * x0i + wk3i * x0r; - } - } -} - - -static void rftfsub(size_t n, float *a, size_t nc, float *c) -{ - size_t j, k, kk, ks, m; - float wkr, wki, xr, xi, yr, yi; - - m = n >> 1; - ks = 2 * nc / m; - kk = 0; - for (j = 2; j < m; j += 2) { - k = n - j; - kk += ks; - wkr = 0.5f - c[nc - kk]; - wki = c[kk]; - xr = a[j] - a[k]; - xi = a[j + 1] + a[k + 1]; - yr = wkr * xr - wki * xi; - yi = wkr * xi + wki * xr; - a[j] -= yr; - a[j + 1] -= yi; - a[k] += yr; - a[k + 1] -= yi; - } -} - - -static void rftbsub(size_t n, float *a, size_t nc, float *c) -{ - size_t j, k, kk, ks, m; - float wkr, wki, xr, xi, yr, yi; - - a[1] = -a[1]; - m = n >> 1; - ks = 2 * nc / m; - kk = 0; - for (j = 2; j < m; j += 2) { - k = n - j; - kk += ks; - wkr = 0.5f - c[nc - kk]; - wki = c[kk]; - xr = a[j] - a[k]; - xi = a[j + 1] + a[k + 1]; - yr = wkr * xr + wki * xi; - yi = wkr * xi - wki * xr; - a[j] -= yr; - a[j + 1] = yi - a[j + 1]; - a[k] += yr; - a[k + 1] = yi - a[k + 1]; - } - a[m + 1] = -a[m + 1]; -} - -#if 0 // Not used. -static void dctsub(int n, float *a, int nc, float *c) -{ - int j, k, kk, ks, m; - float wkr, wki, xr; - - m = n >> 1; - ks = nc / n; - kk = 0; - for (j = 1; j < m; j++) { - k = n - j; - kk += ks; - wkr = c[kk] - c[nc - kk]; - wki = c[kk] + c[nc - kk]; - xr = wki * a[j] - wkr * a[k]; - a[j] = wkr * a[j] + wki * a[k]; - a[k] = xr; - } - a[m] *= c[0]; -} - - -static void dstsub(int n, float *a, int nc, float *c) -{ - int j, k, kk, ks, m; - float wkr, wki, xr; - - m = n >> 1; - ks = nc / n; - kk = 0; - for (j = 1; j < m; j++) { - k = n - j; - kk += ks; - wkr = c[kk] - c[nc - kk]; - wki = c[kk] + c[nc - kk]; - xr = wki * a[k] - wkr * a[j]; - a[k] = wkr * a[k] + wki * a[j]; - a[j] = xr; - } - a[m] *= c[0]; -} -#endif // Not used. diff --git a/webrtc/common_audio/fft4g.h b/webrtc/common_audio/fft4g.h deleted file mode 100644 index 6dd792f..0000000 --- a/webrtc/common_audio/fft4g.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_COMMON_AUDIO_FFT4G_H_ -#define WEBRTC_COMMON_AUDIO_FFT4G_H_ - -#if defined(__cplusplus) -extern "C" { -#endif - -// Refer to fft4g.c for documentation. -void WebRtc_rdft(size_t n, int isgn, float *a, size_t *ip, float *w); - -#if defined(__cplusplus) -} -#endif - -#endif // WEBRTC_COMMON_AUDIO_FFT4G_H_ diff --git a/webrtc/common_audio/fir_filter.cc b/webrtc/common_audio/fir_filter.cc deleted file mode 100644 index dc1b776..0000000 --- a/webrtc/common_audio/fir_filter.cc +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2014 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 "webrtc/common_audio/fir_filter.h" - -#include -#include - -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/fir_filter_neon.h" -#include "webrtc/common_audio/fir_filter_sse.h" -#include "webrtc/system_wrappers/include/cpu_features_wrapper.h" - -namespace webrtc { - -class FIRFilterC : public FIRFilter { - public: - FIRFilterC(const float* coefficients, - size_t coefficients_length); - - void Filter(const float* in, size_t length, float* out) override; - - private: - size_t coefficients_length_; - size_t state_length_; - rtc::scoped_ptr coefficients_; - rtc::scoped_ptr state_; -}; - -FIRFilter* FIRFilter::Create(const float* coefficients, - size_t coefficients_length, - size_t max_input_length) { - if (!coefficients || coefficients_length <= 0 || max_input_length <= 0) { - assert(false); - return NULL; - } - - FIRFilter* filter = NULL; -// If we know the minimum architecture at compile time, avoid CPU detection. -#if defined(WEBRTC_ARCH_X86_FAMILY) -#if defined(__SSE2__) - filter = - new FIRFilterSSE2(coefficients, coefficients_length, max_input_length); -#else - // x86 CPU detection required. - if (WebRtc_GetCPUInfo(kSSE2)) { - filter = - new FIRFilterSSE2(coefficients, coefficients_length, max_input_length); - } else { - filter = new FIRFilterC(coefficients, coefficients_length); - } -#endif -#elif defined(WEBRTC_HAS_NEON) - filter = - new FIRFilterNEON(coefficients, coefficients_length, max_input_length); -#elif defined(WEBRTC_DETECT_NEON) - if (WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) { - filter = - new FIRFilterNEON(coefficients, coefficients_length, max_input_length); - } else { - filter = new FIRFilterC(coefficients, coefficients_length); - } -#else - filter = new FIRFilterC(coefficients, coefficients_length); -#endif - - return filter; -} - -FIRFilterC::FIRFilterC(const float* coefficients, size_t coefficients_length) - : coefficients_length_(coefficients_length), - state_length_(coefficients_length - 1), - coefficients_(new float[coefficients_length_]), - state_(new float[state_length_]) { - for (size_t i = 0; i < coefficients_length_; ++i) { - coefficients_[i] = coefficients[coefficients_length_ - i - 1]; - } - memset(state_.get(), 0, state_length_ * sizeof(state_[0])); -} - -void FIRFilterC::Filter(const float* in, size_t length, float* out) { - assert(length > 0); - - // Convolves the input signal |in| with the filter kernel |coefficients_| - // taking into account the previous state. - for (size_t i = 0; i < length; ++i) { - out[i] = 0.f; - size_t j; - for (j = 0; state_length_ > i && j < state_length_ - i; ++j) { - out[i] += state_[i + j] * coefficients_[j]; - } - for (; j < coefficients_length_; ++j) { - out[i] += in[j + i - state_length_] * coefficients_[j]; - } - } - - // Update current state. - if (length >= state_length_) { - memcpy( - state_.get(), &in[length - state_length_], state_length_ * sizeof(*in)); - } else { - memmove(state_.get(), - &state_[length], - (state_length_ - length) * sizeof(state_[0])); - memcpy(&state_[state_length_ - length], in, length * sizeof(*in)); - } -} - -} // namespace webrtc diff --git a/webrtc/common_audio/fir_filter.h b/webrtc/common_audio/fir_filter.h index a5dc6ec..a76e936 100644 --- a/webrtc/common_audio/fir_filter.h +++ b/webrtc/common_audio/fir_filter.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_FIR_FILTER_H_ -#define WEBRTC_COMMON_AUDIO_FIR_FILTER_H_ +#ifndef COMMON_AUDIO_FIR_FILTER_H_ +#define COMMON_AUDIO_FIR_FILTER_H_ #include @@ -18,16 +18,6 @@ namespace webrtc { // Finite Impulse Response filter using floating-point arithmetic. class FIRFilter { public: - // Creates a filter with the given coefficients. All initial state values will - // be zeros. - // The length of the chunks fed to the filter should never be greater than - // |max_input_length|. This is needed because, when vectorizing it is - // necessary to concatenate the input after the state, and resizing this array - // dynamically is expensive. - static FIRFilter* Create(const float* coefficients, - size_t coefficients_length, - size_t max_input_length); - virtual ~FIRFilter() {} // Filters the |in| data supplied. @@ -37,4 +27,4 @@ class FIRFilter { } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_H_ +#endif // COMMON_AUDIO_FIR_FILTER_H_ diff --git a/webrtc/common_audio/fir_filter_avx2.cc b/webrtc/common_audio/fir_filter_avx2.cc new file mode 100644 index 0000000..26468e2 --- /dev/null +++ b/webrtc/common_audio/fir_filter_avx2.cc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020 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 "common_audio/fir_filter_avx2.h" + +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/memory/aligned_malloc.h" + +namespace webrtc { + +FIRFilterAVX2::FIRFilterAVX2(const float* unaligned_coefficients, + size_t unaligned_coefficients_length, + size_t max_input_length) + : // Closest higher multiple of eight. + coefficients_length_((unaligned_coefficients_length + 7) & ~0x07), + state_length_(coefficients_length_ - 1), + coefficients_(static_cast( + AlignedMalloc(sizeof(float) * coefficients_length_, 32))), + state_(static_cast( + AlignedMalloc(sizeof(float) * (max_input_length + state_length_), + 32))) { + // Add zeros at the end of the coefficients. + RTC_DCHECK_GE(coefficients_length_, unaligned_coefficients_length); + size_t padding = coefficients_length_ - unaligned_coefficients_length; + memset(coefficients_.get(), 0, padding * sizeof(coefficients_[0])); + // The coefficients are reversed to compensate for the order in which the + // input samples are acquired (most recent last). + for (size_t i = 0; i < unaligned_coefficients_length; ++i) { + coefficients_[i + padding] = + unaligned_coefficients[unaligned_coefficients_length - i - 1]; + } + memset(state_.get(), 0, + (max_input_length + state_length_) * sizeof(state_[0])); +} + +FIRFilterAVX2::~FIRFilterAVX2() = default; + +void FIRFilterAVX2::Filter(const float* in, size_t length, float* out) { + RTC_DCHECK_GT(length, 0); + + memcpy(&state_[state_length_], in, length * sizeof(*in)); + + // Convolves the input signal |in| with the filter kernel |coefficients_| + // taking into account the previous state. + for (size_t i = 0; i < length; ++i) { + float* in_ptr = &state_[i]; + float* coef_ptr = coefficients_.get(); + + __m256 m_sum = _mm256_setzero_ps(); + __m256 m_in; + + // Depending on if the pointer is aligned with 32 bytes or not it is loaded + // differently. + if (reinterpret_cast(in_ptr) & 0x1F) { + for (size_t j = 0; j < coefficients_length_; j += 8) { + m_in = _mm256_loadu_ps(in_ptr + j); + m_sum = _mm256_fmadd_ps(m_in, _mm256_load_ps(coef_ptr + j), m_sum); + } + } else { + for (size_t j = 0; j < coefficients_length_; j += 8) { + m_in = _mm256_load_ps(in_ptr + j); + m_sum = _mm256_fmadd_ps(m_in, _mm256_load_ps(coef_ptr + j), m_sum); + } + } + __m128 m128_sum = _mm_add_ps(_mm256_extractf128_ps(m_sum, 0), + _mm256_extractf128_ps(m_sum, 1)); + m128_sum = _mm_add_ps(_mm_movehl_ps(m128_sum, m128_sum), m128_sum); + _mm_store_ss(out + i, + _mm_add_ss(m128_sum, _mm_shuffle_ps(m128_sum, m128_sum, 1))); + } + + // Update current state. + memmove(state_.get(), &state_[length], state_length_ * sizeof(state_[0])); +} + +} // namespace webrtc diff --git a/webrtc/common_audio/fir_filter_avx2.h b/webrtc/common_audio/fir_filter_avx2.h new file mode 100644 index 0000000..893b60b --- /dev/null +++ b/webrtc/common_audio/fir_filter_avx2.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 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. + */ + +#ifndef COMMON_AUDIO_FIR_FILTER_AVX2_H_ +#define COMMON_AUDIO_FIR_FILTER_AVX2_H_ + +#include + +#include + +#include "common_audio/fir_filter.h" +#include "rtc_base/memory/aligned_malloc.h" + +namespace webrtc { + +class FIRFilterAVX2 : public FIRFilter { + public: + FIRFilterAVX2(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + ~FIRFilterAVX2() override; + + void Filter(const float* in, size_t length, float* out) override; + + private: + const size_t coefficients_length_; + const size_t state_length_; + std::unique_ptr coefficients_; + std::unique_ptr state_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_FIR_FILTER_AVX2_H_ diff --git a/webrtc/common_audio/fir_filter_c.cc b/webrtc/common_audio/fir_filter_c.cc new file mode 100644 index 0000000..3f1fa09 --- /dev/null +++ b/webrtc/common_audio/fir_filter_c.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014 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 "common_audio/fir_filter_c.h" + +#include + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +FIRFilterC::~FIRFilterC() {} + +FIRFilterC::FIRFilterC(const float* coefficients, size_t coefficients_length) + : coefficients_length_(coefficients_length), + state_length_(coefficients_length - 1), + coefficients_(new float[coefficients_length_]), + state_(new float[state_length_]) { + for (size_t i = 0; i < coefficients_length_; ++i) { + coefficients_[i] = coefficients[coefficients_length_ - i - 1]; + } + memset(state_.get(), 0, state_length_ * sizeof(state_[0])); +} + +void FIRFilterC::Filter(const float* in, size_t length, float* out) { + RTC_DCHECK_GT(length, 0); + + // Convolves the input signal |in| with the filter kernel |coefficients_| + // taking into account the previous state. + for (size_t i = 0; i < length; ++i) { + out[i] = 0.f; + size_t j; + for (j = 0; state_length_ > i && j < state_length_ - i; ++j) { + out[i] += state_[i + j] * coefficients_[j]; + } + for (; j < coefficients_length_; ++j) { + out[i] += in[j + i - state_length_] * coefficients_[j]; + } + } + + // Update current state. + if (length >= state_length_) { + memcpy(state_.get(), &in[length - state_length_], + state_length_ * sizeof(*in)); + } else { + memmove(state_.get(), &state_[length], + (state_length_ - length) * sizeof(state_[0])); + memcpy(&state_[state_length_ - length], in, length * sizeof(*in)); + } +} + +} // namespace webrtc diff --git a/webrtc/common_audio/fir_filter_c.h b/webrtc/common_audio/fir_filter_c.h new file mode 100644 index 0000000..b2ae4c3 --- /dev/null +++ b/webrtc/common_audio/fir_filter_c.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef COMMON_AUDIO_FIR_FILTER_C_H_ +#define COMMON_AUDIO_FIR_FILTER_C_H_ + +#include + +#include + +#include "common_audio/fir_filter.h" + +namespace webrtc { + +class FIRFilterC : public FIRFilter { + public: + FIRFilterC(const float* coefficients, size_t coefficients_length); + ~FIRFilterC() override; + + void Filter(const float* in, size_t length, float* out) override; + + private: + size_t coefficients_length_; + size_t state_length_; + std::unique_ptr coefficients_; + std::unique_ptr state_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_FIR_FILTER_C_H_ diff --git a/webrtc/common_audio/fir_filter_factory.cc b/webrtc/common_audio/fir_filter_factory.cc new file mode 100644 index 0000000..4bcf052 --- /dev/null +++ b/webrtc/common_audio/fir_filter_factory.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 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 "common_audio/fir_filter_factory.h" + +#include "common_audio/fir_filter_c.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_HAS_NEON) +#include "common_audio/fir_filter_neon.h" +#elif defined(WEBRTC_ARCH_X86_FAMILY) +#include "common_audio/fir_filter_avx2.h" +#include "common_audio/fir_filter_sse.h" +#include "system_wrappers/include/cpu_features_wrapper.h" // kSSE2, WebRtc_G... +#endif + +namespace webrtc { + +FIRFilter* CreateFirFilter(const float* coefficients, + size_t coefficients_length, + size_t max_input_length) { + if (!coefficients || coefficients_length <= 0 || max_input_length <= 0) { + RTC_NOTREACHED(); + return nullptr; + } + + FIRFilter* filter = nullptr; +// If we know the minimum architecture at compile time, avoid CPU detection. +#if defined(WEBRTC_ARCH_X86_FAMILY) + // x86 CPU detection required. + if (GetCPUInfo(kAVX2)) { + filter = + new FIRFilterAVX2(coefficients, coefficients_length, max_input_length); + } else if (GetCPUInfo(kSSE2)) { + filter = + new FIRFilterSSE2(coefficients, coefficients_length, max_input_length); + } else { + filter = new FIRFilterC(coefficients, coefficients_length); + } +#elif defined(WEBRTC_HAS_NEON) + filter = + new FIRFilterNEON(coefficients, coefficients_length, max_input_length); +#else + filter = new FIRFilterC(coefficients, coefficients_length); +#endif + + return filter; +} + +} // namespace webrtc diff --git a/webrtc/common_audio/fir_filter_factory.h b/webrtc/common_audio/fir_filter_factory.h new file mode 100644 index 0000000..a952541 --- /dev/null +++ b/webrtc/common_audio/fir_filter_factory.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014 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. + */ + +#ifndef COMMON_AUDIO_FIR_FILTER_FACTORY_H_ +#define COMMON_AUDIO_FIR_FILTER_FACTORY_H_ + +#include + +namespace webrtc { + +class FIRFilter; + +// Creates a filter with the given coefficients. All initial state values will +// be zeros. +// The length of the chunks fed to the filter should never be greater than +// |max_input_length|. This is needed because, when vectorizing it is +// necessary to concatenate the input after the state, and resizing this array +// dynamically is expensive. +FIRFilter* CreateFirFilter(const float* coefficients, + size_t coefficients_length, + size_t max_input_length); + +} // namespace webrtc + +#endif // COMMON_AUDIO_FIR_FILTER_FACTORY_H_ diff --git a/webrtc/common_audio/fir_filter_neon.cc b/webrtc/common_audio/fir_filter_neon.cc index a815626..f668841 100644 --- a/webrtc/common_audio/fir_filter_neon.cc +++ b/webrtc/common_audio/fir_filter_neon.cc @@ -8,16 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/fir_filter_neon.h" +#include "common_audio/fir_filter_neon.h" #include -#include #include -#include "webrtc/system_wrappers/include/aligned_malloc.h" +#include "rtc_base/checks.h" +#include "rtc_base/memory/aligned_malloc.h" namespace webrtc { +FIRFilterNEON::~FIRFilterNEON() {} + FIRFilterNEON::FIRFilterNEON(const float* coefficients, size_t coefficients_length, size_t max_input_length) @@ -37,13 +39,12 @@ FIRFilterNEON::FIRFilterNEON(const float* coefficients, for (size_t i = 0; i < coefficients_length; ++i) { coefficients_[i + padding] = coefficients[coefficients_length - i - 1]; } - memset(state_.get(), - 0.f, + memset(state_.get(), 0.f, (max_input_length + state_length_) * sizeof(state_[0])); } void FIRFilterNEON::Filter(const float* in, size_t length, float* out) { - assert(length > 0); + RTC_DCHECK_GT(length, 0); memcpy(&state_[state_length_], in, length * sizeof(*in)); @@ -57,8 +58,8 @@ void FIRFilterNEON::Filter(const float* in, size_t length, float* out) { float32x4_t m_in; for (size_t j = 0; j < coefficients_length_; j += 4) { - m_in = vld1q_f32(in_ptr + j); - m_sum = vmlaq_f32(m_sum, m_in, vld1q_f32(coef_ptr + j)); + m_in = vld1q_f32(in_ptr + j); + m_sum = vmlaq_f32(m_sum, m_in, vld1q_f32(coef_ptr + j)); } float32x2_t m_half = vadd_f32(vget_high_f32(m_sum), vget_low_f32(m_sum)); diff --git a/webrtc/common_audio/fir_filter_neon.h b/webrtc/common_audio/fir_filter_neon.h index 3aa6168..1ffefd8 100644 --- a/webrtc/common_audio/fir_filter_neon.h +++ b/webrtc/common_audio/fir_filter_neon.h @@ -8,12 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_ -#define WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_ +#ifndef COMMON_AUDIO_FIR_FILTER_NEON_H_ +#define COMMON_AUDIO_FIR_FILTER_NEON_H_ -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/fir_filter.h" -#include "webrtc/system_wrappers/include/aligned_malloc.h" +#include + +#include "common_audio/fir_filter.h" +#include "rtc_base/memory/aligned_malloc.h" namespace webrtc { @@ -22,16 +23,17 @@ class FIRFilterNEON : public FIRFilter { FIRFilterNEON(const float* coefficients, size_t coefficients_length, size_t max_input_length); + ~FIRFilterNEON() override; void Filter(const float* in, size_t length, float* out) override; private: size_t coefficients_length_; size_t state_length_; - rtc::scoped_ptr coefficients_; - rtc::scoped_ptr state_; + std::unique_ptr coefficients_; + std::unique_ptr state_; }; } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_NEON_H_ +#endif // COMMON_AUDIO_FIR_FILTER_NEON_H_ diff --git a/webrtc/common_audio/fir_filter_sse.cc b/webrtc/common_audio/fir_filter_sse.cc index adbb2b7..ee75fb3 100644 --- a/webrtc/common_audio/fir_filter_sse.cc +++ b/webrtc/common_audio/fir_filter_sse.cc @@ -8,16 +8,19 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/fir_filter_sse.h" +#include "common_audio/fir_filter_sse.h" -#include +#include #include #include -#include "webrtc/system_wrappers/include/aligned_malloc.h" +#include "rtc_base/checks.h" +#include "rtc_base/memory/aligned_malloc.h" namespace webrtc { +FIRFilterSSE2::~FIRFilterSSE2() {} + FIRFilterSSE2::FIRFilterSSE2(const float* coefficients, size_t coefficients_length, size_t max_input_length) @@ -37,13 +40,12 @@ FIRFilterSSE2::FIRFilterSSE2(const float* coefficients, for (size_t i = 0; i < coefficients_length; ++i) { coefficients_[i + padding] = coefficients[coefficients_length - i - 1]; } - memset(state_.get(), - 0, + memset(state_.get(), 0, (max_input_length + state_length_) * sizeof(state_[0])); } void FIRFilterSSE2::Filter(const float* in, size_t length, float* out) { - assert(length > 0); + RTC_DCHECK_GT(length, 0); memcpy(&state_[state_length_], in, length * sizeof(*in)); diff --git a/webrtc/common_audio/fir_filter_sse.h b/webrtc/common_audio/fir_filter_sse.h index a3325cd..32f4945 100644 --- a/webrtc/common_audio/fir_filter_sse.h +++ b/webrtc/common_audio/fir_filter_sse.h @@ -8,12 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_ -#define WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_ +#ifndef COMMON_AUDIO_FIR_FILTER_SSE_H_ +#define COMMON_AUDIO_FIR_FILTER_SSE_H_ -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/fir_filter.h" -#include "webrtc/system_wrappers/include/aligned_malloc.h" +#include + +#include + +#include "common_audio/fir_filter.h" +#include "rtc_base/memory/aligned_malloc.h" namespace webrtc { @@ -22,16 +25,17 @@ class FIRFilterSSE2 : public FIRFilter { FIRFilterSSE2(const float* coefficients, size_t coefficients_length, size_t max_input_length); + ~FIRFilterSSE2() override; void Filter(const float* in, size_t length, float* out) override; private: size_t coefficients_length_; size_t state_length_; - rtc::scoped_ptr coefficients_; - rtc::scoped_ptr state_; + std::unique_ptr coefficients_; + std::unique_ptr state_; }; } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_FIR_FILTER_SSE_H_ +#endif // COMMON_AUDIO_FIR_FILTER_SSE_H_ diff --git a/webrtc/common_audio/include/audio_util.h b/webrtc/common_audio/include/audio_util.h index 2c0028c..f6b6bfd 100644 --- a/webrtc/common_audio/include/audio_util.h +++ b/webrtc/common_audio/include/audio_util.h @@ -8,15 +8,17 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ -#define WEBRTC_COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ +#ifndef COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ +#define COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ -#include +#include + +#include +#include #include +#include -#include "webrtc/base/checks.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/typedefs.h" +#include "rtc_base/checks.h" namespace webrtc { @@ -25,46 +27,70 @@ typedef std::numeric_limits limits_int16; // The conversion functions use the following naming convention: // S16: int16_t [-32768, 32767] // Float: float [-1.0, 1.0] -// FloatS16: float [-32768.0, 32767.0] -static inline int16_t FloatToS16(float v) { - if (v > 0) - return v >= 1 ? limits_int16::max() - : static_cast(v * limits_int16::max() + 0.5f); - return v <= -1 ? limits_int16::min() - : static_cast(-v * limits_int16::min() - 0.5f); -} - +// FloatS16: float [-32768.0, 32768.0] +// Dbfs: float [-20.0*log(10, 32768), 0] = [-90.3, 0] +// The ratio conversion functions use this naming convention: +// Ratio: float (0, +inf) +// Db: float (-inf, +inf) static inline float S16ToFloat(int16_t v) { - static const float kMaxInt16Inverse = 1.f / limits_int16::max(); - static const float kMinInt16Inverse = 1.f / limits_int16::min(); - return v * (v > 0 ? kMaxInt16Inverse : -kMinInt16Inverse); + constexpr float kScaling = 1.f / 32768.f; + return v * kScaling; } static inline int16_t FloatS16ToS16(float v) { - static const float kMaxRound = limits_int16::max() - 0.5f; - static const float kMinRound = limits_int16::min() + 0.5f; - if (v > 0) - return v >= kMaxRound ? limits_int16::max() - : static_cast(v + 0.5f); - return v <= kMinRound ? limits_int16::min() : static_cast(v - 0.5f); + v = std::min(v, 32767.f); + v = std::max(v, -32768.f); + return static_cast(v + std::copysign(0.5f, v)); +} + +static inline int16_t FloatToS16(float v) { + v *= 32768.f; + v = std::min(v, 32767.f); + v = std::max(v, -32768.f); + return static_cast(v + std::copysign(0.5f, v)); } static inline float FloatToFloatS16(float v) { - return v * (v > 0 ? limits_int16::max() : -limits_int16::min()); + v = std::min(v, 1.f); + v = std::max(v, -1.f); + return v * 32768.f; } static inline float FloatS16ToFloat(float v) { - static const float kMaxInt16Inverse = 1.f / limits_int16::max(); - static const float kMinInt16Inverse = 1.f / limits_int16::min(); - return v * (v > 0 ? kMaxInt16Inverse : -kMinInt16Inverse); + v = std::min(v, 32768.f); + v = std::max(v, -32768.f); + constexpr float kScaling = 1.f / 32768.f; + return v * kScaling; } void FloatToS16(const float* src, size_t size, int16_t* dest); void S16ToFloat(const int16_t* src, size_t size, float* dest); +void S16ToFloatS16(const int16_t* src, size_t size, float* dest); void FloatS16ToS16(const float* src, size_t size, int16_t* dest); void FloatToFloatS16(const float* src, size_t size, float* dest); void FloatS16ToFloat(const float* src, size_t size, float* dest); +inline float DbToRatio(float v) { + return std::pow(10.0f, v / 20.0f); +} + +inline float DbfsToFloatS16(float v) { + static constexpr float kMaximumAbsFloatS16 = -limits_int16::min(); + return DbToRatio(v) * kMaximumAbsFloatS16; +} + +inline float FloatS16ToDbfs(float v) { + RTC_DCHECK_GE(v, 0); + + // kMinDbfs is equal to -20.0 * log10(-limits_int16::min()) + static constexpr float kMinDbfs = -90.30899869919436f; + if (v <= 1.0f) { + return kMinDbfs; + } + // Equal to 20 * log10(v / (-limits_int16::min())) + return 20.0f * std::log10(v) + kMinDbfs; +} + // Copy audio from |src| channels to |dest| channels unless |src| and |dest| // point to the same address. |src| and |dest| must have the same number of // channels, and there must be sufficient space allocated in |dest|. @@ -87,11 +113,11 @@ void CopyAudioIfNeeded(const T* const* src, template void Deinterleave(const T* interleaved, size_t samples_per_channel, - int num_channels, + size_t num_channels, T* const* deinterleaved) { - for (int i = 0; i < num_channels; ++i) { + for (size_t i = 0; i < num_channels; ++i) { T* channel = deinterleaved[i]; - int interleaved_idx = i; + size_t interleaved_idx = i; for (size_t j = 0; j < samples_per_channel; ++j) { channel[j] = interleaved[interleaved_idx]; interleaved_idx += num_channels; @@ -105,11 +131,11 @@ void Deinterleave(const T* interleaved, template void Interleave(const T* const* deinterleaved, size_t samples_per_channel, - int num_channels, + size_t num_channels, T* interleaved) { - for (int i = 0; i < num_channels; ++i) { + for (size_t i = 0; i < num_channels; ++i) { const T* channel = deinterleaved[i]; - int interleaved_idx = i; + size_t interleaved_idx = i; for (size_t j = 0; j < samples_per_channel; ++j) { interleaved[interleaved_idx] = channel[j]; interleaved_idx += num_channels; @@ -155,7 +181,7 @@ void DownmixInterleavedToMonoImpl(const T* interleaved, int num_channels, T* deinterleaved) { RTC_DCHECK_GT(num_channels, 0); - RTC_DCHECK_GT(num_frames, 0u); + RTC_DCHECK_GT(num_frames, 0); const T* const end = interleaved + num_frames * num_channels; @@ -185,4 +211,4 @@ void DownmixInterleavedToMono(const int16_t* interleaved, } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ +#endif // COMMON_AUDIO_INCLUDE_AUDIO_UTIL_H_ diff --git a/webrtc/common_audio/lapped_transform.cc b/webrtc/common_audio/lapped_transform.cc deleted file mode 100644 index c01f1d9..0000000 --- a/webrtc/common_audio/lapped_transform.cc +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2014 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 "webrtc/common_audio/lapped_transform.h" - -#include -#include -#include - -#include "webrtc/base/checks.h" -#include "webrtc/common_audio/real_fourier.h" - -namespace webrtc { - -void LappedTransform::BlockThunk::ProcessBlock(const float* const* input, - size_t num_frames, - int num_input_channels, - int num_output_channels, - float* const* output) { - RTC_CHECK_EQ(num_input_channels, parent_->num_in_channels_); - RTC_CHECK_EQ(num_output_channels, parent_->num_out_channels_); - RTC_CHECK_EQ(parent_->block_length_, num_frames); - - for (int i = 0; i < num_input_channels; ++i) { - memcpy(parent_->real_buf_.Row(i), input[i], - num_frames * sizeof(*input[0])); - parent_->fft_->Forward(parent_->real_buf_.Row(i), - parent_->cplx_pre_.Row(i)); - } - - size_t block_length = RealFourier::ComplexLength( - RealFourier::FftOrder(num_frames)); - RTC_CHECK_EQ(parent_->cplx_length_, block_length); - parent_->block_processor_->ProcessAudioBlock(parent_->cplx_pre_.Array(), - num_input_channels, - parent_->cplx_length_, - num_output_channels, - parent_->cplx_post_.Array()); - - for (int i = 0; i < num_output_channels; ++i) { - parent_->fft_->Inverse(parent_->cplx_post_.Row(i), - parent_->real_buf_.Row(i)); - memcpy(output[i], parent_->real_buf_.Row(i), - num_frames * sizeof(*input[0])); - } -} - -LappedTransform::LappedTransform(int num_in_channels, - int num_out_channels, - size_t chunk_length, - const float* window, - size_t block_length, - size_t shift_amount, - Callback* callback) - : blocker_callback_(this), - num_in_channels_(num_in_channels), - num_out_channels_(num_out_channels), - block_length_(block_length), - chunk_length_(chunk_length), - block_processor_(callback), - blocker_(chunk_length_, - block_length_, - num_in_channels_, - num_out_channels_, - window, - shift_amount, - &blocker_callback_), - fft_(RealFourier::Create(RealFourier::FftOrder(block_length_))), - cplx_length_(RealFourier::ComplexLength(fft_->order())), - real_buf_(num_in_channels, - block_length_, - RealFourier::kFftBufferAlignment), - cplx_pre_(num_in_channels, - cplx_length_, - RealFourier::kFftBufferAlignment), - cplx_post_(num_out_channels, - cplx_length_, - RealFourier::kFftBufferAlignment) { - RTC_CHECK(num_in_channels_ > 0 && num_out_channels_ > 0); - RTC_CHECK_GT(block_length_, 0u); - RTC_CHECK_GT(chunk_length_, 0u); - RTC_CHECK(block_processor_); - - // block_length_ power of 2? - RTC_CHECK_EQ(0u, block_length_ & (block_length_ - 1)); -} - -void LappedTransform::ProcessChunk(const float* const* in_chunk, - float* const* out_chunk) { - blocker_.ProcessChunk(in_chunk, chunk_length_, num_in_channels_, - num_out_channels_, out_chunk); -} - -} // namespace webrtc diff --git a/webrtc/common_audio/lapped_transform.h b/webrtc/common_audio/lapped_transform.h deleted file mode 100644 index 21e10e3..0000000 --- a/webrtc/common_audio/lapped_transform.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#ifndef WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_ -#define WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_ - -#include - -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/blocker.h" -#include "webrtc/common_audio/real_fourier.h" -#include "webrtc/system_wrappers/include/aligned_array.h" - -namespace webrtc { - -// Helper class for audio processing modules which operate on frequency domain -// input derived from the windowed time domain audio stream. -// -// The input audio chunk is sliced into possibly overlapping blocks, multiplied -// by a window and transformed with an FFT implementation. The transformed data -// is supplied to the given callback for processing. The processed output is -// then inverse transformed into the time domain and spliced back into a chunk -// which constitutes the final output of this processing module. -class LappedTransform { - public: - class Callback { - public: - virtual ~Callback() {} - - virtual void ProcessAudioBlock(const std::complex* const* in_block, - int num_in_channels, size_t frames, - int num_out_channels, - std::complex* const* out_block) = 0; - }; - - // Construct a transform instance. |chunk_length| is the number of samples in - // each channel. |window| defines the window, owned by the caller (a copy is - // made internally); |window| should have length equal to |block_length|. - // |block_length| defines the length of a block, in samples. - // |shift_amount| is in samples. |callback| is the caller-owned audio - // processing function called for each block of the input chunk. - LappedTransform(int num_in_channels, - int num_out_channels, - size_t chunk_length, - const float* window, - size_t block_length, - size_t shift_amount, - Callback* callback); - ~LappedTransform() {} - - // Main audio processing helper method. Internally slices |in_chunk| into - // blocks, transforms them to frequency domain, calls the callback for each - // block and returns a de-blocked time domain chunk of audio through - // |out_chunk|. Both buffers are caller-owned. - void ProcessChunk(const float* const* in_chunk, float* const* out_chunk); - - // Get the chunk length. - // - // The chunk length is the number of samples per channel that must be passed - // to ProcessChunk via the parameter in_chunk. - // - // Returns the same chunk_length passed to the LappedTransform constructor. - size_t chunk_length() const { return chunk_length_; } - - // Get the number of input channels. - // - // This is the number of arrays that must be passed to ProcessChunk via - // in_chunk. - // - // Returns the same num_in_channels passed to the LappedTransform constructor. - int num_in_channels() const { return num_in_channels_; } - - // Get the number of output channels. - // - // This is the number of arrays that must be passed to ProcessChunk via - // out_chunk. - // - // Returns the same num_out_channels passed to the LappedTransform - // constructor. - int num_out_channels() const { return num_out_channels_; } - - private: - // Internal middleware callback, given to the blocker. Transforms each block - // and hands it over to the processing method given at construction time. - class BlockThunk : public BlockerCallback { - public: - explicit BlockThunk(LappedTransform* parent) : parent_(parent) {} - - virtual void ProcessBlock(const float* const* input, size_t num_frames, - int num_input_channels, int num_output_channels, - float* const* output); - - private: - LappedTransform* const parent_; - } blocker_callback_; - - const int num_in_channels_; - const int num_out_channels_; - - const size_t block_length_; - const size_t chunk_length_; - - Callback* const block_processor_; - Blocker blocker_; - - rtc::scoped_ptr fft_; - const size_t cplx_length_; - AlignedArray real_buf_; - AlignedArray > cplx_pre_; - AlignedArray > cplx_post_; -}; - -} // namespace webrtc - -#endif // WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_ - diff --git a/webrtc/common_audio/meson.build b/webrtc/common_audio/meson.build index e9cbbf8..e0c9129 100644 --- a/webrtc/common_audio/meson.build +++ b/webrtc/common_audio/meson.build @@ -1,19 +1,29 @@ common_audio_sources = [ + 'audio_converter.cc', + 'audio_util.cc', + 'channel_buffer.cc', + 'fir_filter_c.cc', + 'fir_filter_factory.cc', + 'real_fourier.cc', + 'real_fourier_ooura.cc', 'resampler/push_resampler.cc', 'resampler/push_sinc_resampler.cc', 'resampler/resampler.cc', 'resampler/sinc_resampler.cc', 'resampler/sinusoidal_linear_chirp_source.cc', - 'signal_processing/auto_corr_to_refl_coef.c', + 'ring_buffer.c', 'signal_processing/auto_correlation.c', + 'signal_processing/auto_corr_to_refl_coef.c', + 'signal_processing/complex_bit_reverse.c', 'signal_processing/complex_fft.c', 'signal_processing/copy_set_operations.c', 'signal_processing/cross_correlation.c', 'signal_processing/division_operations.c', - 'signal_processing/dot_product_with_scale.c', + 'signal_processing/dot_product_with_scale.cc', 'signal_processing/downsample_fast.c', - 'signal_processing/energy.c', 'signal_processing/filter_ar.c', + 'signal_processing/filter_ar_fast_q12.c', + 'signal_processing/energy.c', 'signal_processing/filter_ma_fast_q12.c', 'signal_processing/get_hanning_window.c', 'signal_processing/get_scaling_square.c', @@ -24,34 +34,27 @@ common_audio_sources = [ 'signal_processing/randomization_functions.c', 'signal_processing/real_fft.c', 'signal_processing/refl_coef_to_lpc.c', - 'signal_processing/resample.c', 'signal_processing/resample_48khz.c', 'signal_processing/resample_by_2.c', 'signal_processing/resample_by_2_internal.c', + 'signal_processing/resample.c', 'signal_processing/resample_fractional.c', 'signal_processing/spl_init.c', - 'signal_processing/spl_sqrt.c', + 'signal_processing/spl_inl.c', 'signal_processing/splitting_filter.c', + 'signal_processing/spl_sqrt.c', 'signal_processing/sqrt_of_one_minus_x_squared.c', 'signal_processing/vector_scaling_operations.c', + 'smoothing_filter.cc', + 'third_party/ooura/fft_size_128/ooura_fft.cc', + 'third_party/ooura/fft_size_256/fft4g.cc', + 'third_party/spl_sqrt_floor/spl_sqrt_floor.c', 'vad/vad.cc', 'vad/vad_core.c', 'vad/vad_filterbank.c', 'vad/vad_gmm.c', 'vad/vad_sp.c', 'vad/webrtc_vad.c', - 'audio_converter.cc', - 'audio_ring_buffer.cc', - 'audio_util.cc', - 'blocker.cc', - 'channel_buffer.cc', - 'fft4g.c', - 'fir_filter.cc', - 'lapped_transform.cc', - 'real_fourier.cc', - 'real_fourier_ooura.cc', - 'ring_buffer.c', - 'sparse_fir_filter.cc', 'wav_file.cc', 'wav_header.cc', 'window_generator.cc', @@ -59,18 +62,52 @@ common_audio_sources = [ arch_libs = [] if have_x86 - arch_libs += [static_library('common_audio_sse2', - ['resampler/sinc_resampler_sse.cc', 'fir_filter_sse.cc'], - dependencies: common_deps, - include_directories: webrtc_inc, - c_args: common_cflags + ['-msse2'], - cpp_args: common_cxxflags + ['-msse2'])] + arch_libs += [ + static_library('common_audio_sse2', + [ + 'fir_filter_sse.cc', + 'resampler/sinc_resampler_sse.cc', + 'third_party/ooura/fft_size_128/ooura_fft_sse2.cc', + ], + dependencies: common_deps, + include_directories: webrtc_inc, + c_args: common_cflags + ['-msse2'], + cpp_args: common_cxxflags + ['-msse2'] + ) + ] + arch_libs += [ + static_library('common_audio_avx', + [ + 'fir_filter_avx2.cc', + 'resampler/sinc_resampler_avx2.cc', + ], + dependencies: common_deps, + include_directories: webrtc_inc, + c_args: common_cflags + ['-mavx2', '-mfma'], + cpp_args: common_cxxflags + ['-mavx2', '-mfma'] + ) + ] +endif + +if have_mips + common_audio_sources += [ + 'signal_processing/complex_bit_reverse_mips.c', + 'signal_processing/complex_fft_mips.c', + 'signal_processing/cross_correlation_mips.c', + 'signal_processing/downsample_fast_mips.c', + 'signal_processing/filter_ar_fast_q12_mips.c', + 'signal_processing/min_max_operations_mips.c', + 'signal_processing/resample_by_2_mips.c', + 'signal_processing/vector_scaling_operations_mips.c', + 'third_party/ooura/fft_size_128/ooura_fft_mips.c', + 'third_party/spl_sqrt_floor/spl_sqrt_floor_mips.c', + ] endif if have_arm common_audio_sources += [ 'signal_processing/complex_bit_reverse_arm.S', - 'signal_processing/spl_sqrt_floor_arm.S', + 'third_party/spl_sqrt_floor/spl_sqrt_floor_arm.S', ] endif @@ -82,11 +119,12 @@ endif if have_neon common_audio_sources += [ + 'fir_filter_neon.cc', 'resampler/sinc_resampler_neon.cc', 'signal_processing/cross_correlation_neon.c', 'signal_processing/downsample_fast_neon.c', 'signal_processing/min_max_operations_neon.c', - 'fir_filter_neon.cc', + 'third_party/ooura/fft_size_128/ooura_fft_neon.c', ] endif @@ -94,7 +132,6 @@ if not have_arm common_audio_sources += [ 'signal_processing/complex_bit_reverse.c', 'signal_processing/filter_ar_fast_q12.c', - 'signal_processing/spl_sqrt_floor.c', ] endif diff --git a/webrtc/common_audio/real_fourier.cc b/webrtc/common_audio/real_fourier.cc index fef3c60..7365844 100644 --- a/webrtc/common_audio/real_fourier.cc +++ b/webrtc/common_audio/real_fourier.cc @@ -8,25 +8,20 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/real_fourier.h" +#include "common_audio/real_fourier.h" -#include "webrtc/base/checks.h" -#include "webrtc/common_audio/real_fourier_ooura.h" -#include "webrtc/common_audio/real_fourier_openmax.h" -#include "webrtc/common_audio/signal_processing/include/spl_inl.h" +#include "common_audio/real_fourier_ooura.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" namespace webrtc { using std::complex; -const int RealFourier::kFftBufferAlignment = 32; +const size_t RealFourier::kFftBufferAlignment = 32; -rtc::scoped_ptr RealFourier::Create(int fft_order) { -#if defined(RTC_USE_OPENMAX_DL) - return rtc::scoped_ptr(new RealFourierOpenmax(fft_order)); -#else - return rtc::scoped_ptr(new RealFourierOoura(fft_order)); -#endif +std::unique_ptr RealFourier::Create(int fft_order) { + return std::unique_ptr(new RealFourierOoura(fft_order)); } int RealFourier::FftOrder(size_t length) { @@ -36,7 +31,7 @@ int RealFourier::FftOrder(size_t length) { size_t RealFourier::FftLength(int order) { RTC_CHECK_GE(order, 0); - return static_cast(1 << order); + return size_t{1} << order; } size_t RealFourier::ComplexLength(int order) { @@ -54,4 +49,3 @@ RealFourier::fft_cplx_scoper RealFourier::AllocCplxBuffer(int count) { } } // namespace webrtc - diff --git a/webrtc/common_audio/real_fourier.h b/webrtc/common_audio/real_fourier.h index ce3bbff..4881fb7 100644 --- a/webrtc/common_audio/real_fourier.h +++ b/webrtc/common_audio/real_fourier.h @@ -8,13 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_ -#define WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_ +#ifndef COMMON_AUDIO_REAL_FOURIER_H_ +#define COMMON_AUDIO_REAL_FOURIER_H_ + +#include #include +#include -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/system_wrappers/include/aligned_malloc.h" +#include "rtc_base/memory/aligned_malloc.h" // Uniform interface class for the real DFT and its inverse, for power-of-2 // input lengths. Also contains helper functions for buffer allocation, taking @@ -25,17 +27,17 @@ namespace webrtc { class RealFourier { public: // Shorthand typenames for the scopers used by the buffer allocation helpers. - typedef rtc::scoped_ptr fft_real_scoper; - typedef rtc::scoped_ptr[], AlignedFreeDeleter> + typedef std::unique_ptr fft_real_scoper; + typedef std::unique_ptr[], AlignedFreeDeleter> fft_cplx_scoper; // The alignment required for all input and output buffers, in bytes. - static const int kFftBufferAlignment; + static const size_t kFftBufferAlignment; // Construct a wrapper instance for the given input order, which must be // between 1 and kMaxFftOrder, inclusively. - static rtc::scoped_ptr Create(int fft_order); - virtual ~RealFourier() {}; + static std::unique_ptr Create(int fft_order); + virtual ~RealFourier() {} // Helper to compute the smallest FFT order (a power of 2) which will contain // the given input length. @@ -71,5 +73,4 @@ class RealFourier { } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_ - +#endif // COMMON_AUDIO_REAL_FOURIER_H_ diff --git a/webrtc/common_audio/real_fourier_ooura.cc b/webrtc/common_audio/real_fourier_ooura.cc index 8cd4c86..9acda54 100644 --- a/webrtc/common_audio/real_fourier_ooura.cc +++ b/webrtc/common_audio/real_fourier_ooura.cc @@ -8,13 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/real_fourier_ooura.h" +#include "common_audio/real_fourier_ooura.h" -#include #include +#include -#include "webrtc/base/checks.h" -#include "webrtc/common_audio/fft4g.h" +#include "common_audio/third_party/ooura/fft_size_256/fft4g.h" +#include "rtc_base/checks.h" namespace webrtc { @@ -28,8 +28,8 @@ void Conjugate(complex* array, size_t complex_length) { } size_t ComputeWorkIpSize(size_t fft_length) { - return static_cast(2 + std::ceil(std::sqrt( - static_cast(fft_length)))); + return static_cast( + 2 + std::ceil(std::sqrt(static_cast(fft_length)))); } } // namespace @@ -45,11 +45,13 @@ RealFourierOoura::RealFourierOoura(int fft_order) RTC_CHECK_GE(fft_order, 1); } +RealFourierOoura::~RealFourierOoura() = default; + void RealFourierOoura::Forward(const float* src, complex* dest) const { { // This cast is well-defined since C++11. See "Non-static data members" at: // http://en.cppreference.com/w/cpp/numeric/complex - auto dest_float = reinterpret_cast(dest); + auto* dest_float = reinterpret_cast(dest); std::copy(src, src + length_, dest_float); WebRtc_rdft(length_, 1, dest_float, work_ip_.get(), work_w_.get()); } @@ -63,7 +65,7 @@ void RealFourierOoura::Forward(const float* src, complex* dest) const { void RealFourierOoura::Inverse(const complex* src, float* dest) const { { - auto dest_complex = reinterpret_cast*>(dest); + auto* dest_complex = reinterpret_cast*>(dest); // The real output array is shorter than the input complex array by one // complex element. const size_t dest_complex_length = complex_length_ - 1; @@ -71,8 +73,8 @@ void RealFourierOoura::Inverse(const complex* src, float* dest) const { // Restore Ooura's conjugate definition. Conjugate(dest_complex, dest_complex_length); // Restore real[n/2] to imag[0]. - dest_complex[0] = complex(dest_complex[0].real(), - src[complex_length_ - 1].real()); + dest_complex[0] = + complex(dest_complex[0].real(), src[complex_length_ - 1].real()); } WebRtc_rdft(length_, -1, dest, work_ip_.get(), work_w_.get()); @@ -82,4 +84,8 @@ void RealFourierOoura::Inverse(const complex* src, float* dest) const { std::for_each(dest, dest + length_, [scale](float& v) { v *= scale; }); } +int RealFourierOoura::order() const { + return order_; +} + } // namespace webrtc diff --git a/webrtc/common_audio/real_fourier_ooura.h b/webrtc/common_audio/real_fourier_ooura.h index 8d094bf..ae85dfd 100644 --- a/webrtc/common_audio/real_fourier_ooura.h +++ b/webrtc/common_audio/real_fourier_ooura.h @@ -8,38 +8,38 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_REAL_FOURIER_OOURA_H_ -#define WEBRTC_COMMON_AUDIO_REAL_FOURIER_OOURA_H_ +#ifndef COMMON_AUDIO_REAL_FOURIER_OOURA_H_ +#define COMMON_AUDIO_REAL_FOURIER_OOURA_H_ + +#include #include +#include -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/real_fourier.h" +#include "common_audio/real_fourier.h" namespace webrtc { class RealFourierOoura : public RealFourier { public: explicit RealFourierOoura(int fft_order); + ~RealFourierOoura() override; void Forward(const float* src, std::complex* dest) const override; void Inverse(const std::complex* src, float* dest) const override; - int order() const override { - return order_; - } + int order() const override; private: const int order_; const size_t length_; const size_t complex_length_; // These are work arrays for Ooura. The names are based on the comments in - // fft4g.c. - const rtc::scoped_ptr work_ip_; - const rtc::scoped_ptr work_w_; + // common_audio/third_party/ooura/fft_size_256/fft4g.cc. + const std::unique_ptr work_ip_; + const std::unique_ptr work_w_; }; } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_REAL_FOURIER_OOURA_H_ - +#endif // COMMON_AUDIO_REAL_FOURIER_OOURA_H_ diff --git a/webrtc/common_audio/resampler/include/push_resampler.h b/webrtc/common_audio/resampler/include/push_resampler.h index b5c0003..3da6712 100644 --- a/webrtc/common_audio/resampler/include/push_resampler.h +++ b/webrtc/common_audio/resampler/include/push_resampler.h @@ -8,11 +8,11 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_ -#define WEBRTC_COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_ +#ifndef COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_ +#define COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_ -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/typedefs.h" +#include +#include namespace webrtc { @@ -28,25 +28,32 @@ class PushResampler { // Must be called whenever the parameters change. Free to be called at any // time as it is a no-op if parameters have not changed since the last call. - int InitializeIfNeeded(int src_sample_rate_hz, int dst_sample_rate_hz, - int num_channels); + int InitializeIfNeeded(int src_sample_rate_hz, + int dst_sample_rate_hz, + size_t num_channels); // Returns the total number of samples provided in destination (e.g. 32 kHz, // 2 channel audio gives 640 samples). int Resample(const T* src, size_t src_length, T* dst, size_t dst_capacity); private: - rtc::scoped_ptr sinc_resampler_; - rtc::scoped_ptr sinc_resampler_right_; int src_sample_rate_hz_; int dst_sample_rate_hz_; - int num_channels_; - rtc::scoped_ptr src_left_; - rtc::scoped_ptr src_right_; - rtc::scoped_ptr dst_left_; - rtc::scoped_ptr dst_right_; -}; + size_t num_channels_; + // Vector that is needed to provide the proper inputs and outputs to the + // interleave/de-interleave methods used in Resample. This needs to be + // heap-allocated on the state to support an arbitrary number of channels + // without doing run-time heap-allocations in the Resample method. + std::vector channel_data_array_; + struct ChannelResampler { + std::unique_ptr resampler; + std::vector source; + std::vector destination; + }; + + std::vector channel_resamplers_; +}; } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_ +#endif // COMMON_AUDIO_RESAMPLER_INCLUDE_PUSH_RESAMPLER_H_ diff --git a/webrtc/common_audio/resampler/include/resampler.h b/webrtc/common_audio/resampler/include/resampler.h index 0d4c1af..41940f9 100644 --- a/webrtc/common_audio/resampler/include/resampler.h +++ b/webrtc/common_audio/resampler/include/resampler.h @@ -8,88 +8,92 @@ * be found in the AUTHORS file in the root of the source tree. */ - /* * A wrapper for resampling a numerous amount of sampling combinations. */ -#ifndef WEBRTC_RESAMPLER_RESAMPLER_H_ -#define WEBRTC_RESAMPLER_RESAMPLER_H_ +#ifndef COMMON_AUDIO_RESAMPLER_INCLUDE_RESAMPLER_H_ +#define COMMON_AUDIO_RESAMPLER_INCLUDE_RESAMPLER_H_ #include - -#include "webrtc/typedefs.h" +#include namespace webrtc { // All methods return 0 on success and -1 on failure. -class Resampler -{ +class Resampler { + public: + Resampler(); + Resampler(int inFreq, int outFreq, size_t num_channels); + ~Resampler(); -public: - Resampler(); - Resampler(int inFreq, int outFreq, int num_channels); - ~Resampler(); + // Reset all states + int Reset(int inFreq, int outFreq, size_t num_channels); - // Reset all states - int Reset(int inFreq, int outFreq, int num_channels); + // Reset all states if any parameter has changed + int ResetIfNeeded(int inFreq, int outFreq, size_t num_channels); - // Reset all states if any parameter has changed - int ResetIfNeeded(int inFreq, int outFreq, int num_channels); + // Resample samplesIn to samplesOut. + int Push(const int16_t* samplesIn, + size_t lengthIn, + int16_t* samplesOut, + size_t maxLen, + size_t& outLen); // NOLINT: to avoid changing APIs - // Resample samplesIn to samplesOut. - int Push(const int16_t* samplesIn, size_t lengthIn, int16_t* samplesOut, - size_t maxLen, size_t &outLen); + private: + enum ResamplerMode { + kResamplerMode1To1, + kResamplerMode1To2, + kResamplerMode1To3, + kResamplerMode1To4, + kResamplerMode1To6, + kResamplerMode1To12, + kResamplerMode2To3, + kResamplerMode2To11, + kResamplerMode4To11, + kResamplerMode8To11, + kResamplerMode11To16, + kResamplerMode11To32, + kResamplerMode2To1, + kResamplerMode3To1, + kResamplerMode4To1, + kResamplerMode6To1, + kResamplerMode12To1, + kResamplerMode3To2, + kResamplerMode11To2, + kResamplerMode11To4, + kResamplerMode11To8 + }; -private: - enum ResamplerMode - { - kResamplerMode1To1, - kResamplerMode1To2, - kResamplerMode1To3, - kResamplerMode1To4, - kResamplerMode1To6, - kResamplerMode1To12, - kResamplerMode2To3, - kResamplerMode2To11, - kResamplerMode4To11, - kResamplerMode8To11, - kResamplerMode11To16, - kResamplerMode11To32, - kResamplerMode2To1, - kResamplerMode3To1, - kResamplerMode4To1, - kResamplerMode6To1, - kResamplerMode12To1, - kResamplerMode3To2, - kResamplerMode11To2, - kResamplerMode11To4, - kResamplerMode11To8 - }; + // Computes the resampler mode for a given sampling frequency pair. + // Returns -1 for unsupported frequency pairs. + static int ComputeResamplerMode(int in_freq_hz, + int out_freq_hz, + ResamplerMode* mode); - // Generic pointers since we don't know what states we'll need - void* state1_; - void* state2_; - void* state3_; + // Generic pointers since we don't know what states we'll need + void* state1_; + void* state2_; + void* state3_; - // Storage if needed - int16_t* in_buffer_; - int16_t* out_buffer_; - size_t in_buffer_size_; - size_t out_buffer_size_; - size_t in_buffer_size_max_; - size_t out_buffer_size_max_; + // Storage if needed + int16_t* in_buffer_; + int16_t* out_buffer_; + size_t in_buffer_size_; + size_t out_buffer_size_; + size_t in_buffer_size_max_; + size_t out_buffer_size_max_; - int my_in_frequency_khz_; - int my_out_frequency_khz_; - ResamplerMode my_mode_; - int num_channels_; + int my_in_frequency_khz_; + int my_out_frequency_khz_; + ResamplerMode my_mode_; + size_t num_channels_; - // Extra instance for stereo - Resampler* slave_left_; - Resampler* slave_right_; + // Extra instance for stereo + Resampler* helper_left_; + Resampler* helper_right_; }; } // namespace webrtc -#endif // WEBRTC_RESAMPLER_RESAMPLER_H_ +#endif // COMMON_AUDIO_RESAMPLER_INCLUDE_RESAMPLER_H_ diff --git a/webrtc/common_audio/resampler/push_resampler.cc b/webrtc/common_audio/resampler/push_resampler.cc index 566acde..d7aa8d7 100644 --- a/webrtc/common_audio/resampler/push_resampler.cc +++ b/webrtc/common_audio/resampler/push_resampler.cc @@ -8,40 +8,78 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/resampler/include/push_resampler.h" +#include "common_audio/resampler/include/push_resampler.h" +#include #include -#include "webrtc/common_audio/include/audio_util.h" -#include "webrtc/common_audio/resampler/include/resampler.h" -#include "webrtc/common_audio/resampler/push_sinc_resampler.h" +#include + +#include "common_audio/include/audio_util.h" +#include "common_audio/resampler/push_sinc_resampler.h" +#include "rtc_base/checks.h" namespace webrtc { +namespace { +// These checks were factored out into a non-templatized function +// due to problems with clang on Windows in debug builds. +// For some reason having the DCHECKs inline in the template code +// caused the compiler to generate code that threw off the linker. +// TODO(tommi): Re-enable when we've figured out what the problem is. +// http://crbug.com/615050 +void CheckValidInitParams(int src_sample_rate_hz, + int dst_sample_rate_hz, + size_t num_channels) { +// The below checks are temporarily disabled on WEBRTC_WIN due to problems +// with clang debug builds. +#if !defined(WEBRTC_WIN) && defined(__clang__) + RTC_DCHECK_GT(src_sample_rate_hz, 0); + RTC_DCHECK_GT(dst_sample_rate_hz, 0); + RTC_DCHECK_GT(num_channels, 0); +#endif +} + +void CheckExpectedBufferSizes(size_t src_length, + size_t dst_capacity, + size_t num_channels, + int src_sample_rate, + int dst_sample_rate) { +// The below checks are temporarily disabled on WEBRTC_WIN due to problems +// with clang debug builds. +// TODO(tommi): Re-enable when we've figured out what the problem is. +// http://crbug.com/615050 +#if !defined(WEBRTC_WIN) && defined(__clang__) + const size_t src_size_10ms = src_sample_rate * num_channels / 100; + const size_t dst_size_10ms = dst_sample_rate * num_channels / 100; + RTC_DCHECK_EQ(src_length, src_size_10ms); + RTC_DCHECK_GE(dst_capacity, dst_size_10ms); +#endif +} +} // namespace template PushResampler::PushResampler() - : src_sample_rate_hz_(0), - dst_sample_rate_hz_(0), - num_channels_(0) { -} + : src_sample_rate_hz_(0), dst_sample_rate_hz_(0), num_channels_(0) {} template -PushResampler::~PushResampler() { -} +PushResampler::~PushResampler() {} template int PushResampler::InitializeIfNeeded(int src_sample_rate_hz, int dst_sample_rate_hz, - int num_channels) { + size_t num_channels) { + CheckValidInitParams(src_sample_rate_hz, dst_sample_rate_hz, num_channels); + if (src_sample_rate_hz == src_sample_rate_hz_ && dst_sample_rate_hz == dst_sample_rate_hz_ && - num_channels == num_channels_) + num_channels == num_channels_) { // No-op if settings haven't changed. return 0; + } - if (src_sample_rate_hz <= 0 || dst_sample_rate_hz <= 0 || - num_channels <= 0 || num_channels > 2) + if (src_sample_rate_hz <= 0 || dst_sample_rate_hz <= 0 || num_channels <= 0) { return -1; + } src_sample_rate_hz_ = src_sample_rate_hz; dst_sample_rate_hz_ = dst_sample_rate_hz; @@ -51,29 +89,28 @@ int PushResampler::InitializeIfNeeded(int src_sample_rate_hz, static_cast(src_sample_rate_hz / 100); const size_t dst_size_10ms_mono = static_cast(dst_sample_rate_hz / 100); - sinc_resampler_.reset(new PushSincResampler(src_size_10ms_mono, - dst_size_10ms_mono)); - if (num_channels_ == 2) { - src_left_.reset(new T[src_size_10ms_mono]); - src_right_.reset(new T[src_size_10ms_mono]); - dst_left_.reset(new T[dst_size_10ms_mono]); - dst_right_.reset(new T[dst_size_10ms_mono]); - sinc_resampler_right_.reset(new PushSincResampler(src_size_10ms_mono, - dst_size_10ms_mono)); + channel_resamplers_.clear(); + for (size_t i = 0; i < num_channels; ++i) { + channel_resamplers_.push_back(ChannelResampler()); + auto channel_resampler = channel_resamplers_.rbegin(); + channel_resampler->resampler = std::make_unique( + src_size_10ms_mono, dst_size_10ms_mono); + channel_resampler->source.resize(src_size_10ms_mono); + channel_resampler->destination.resize(dst_size_10ms_mono); } + channel_data_array_.resize(num_channels_); + return 0; } template -int PushResampler::Resample(const T* src, size_t src_length, T* dst, +int PushResampler::Resample(const T* src, + size_t src_length, + T* dst, size_t dst_capacity) { - const size_t src_size_10ms = - static_cast(src_sample_rate_hz_ * num_channels_ / 100); - const size_t dst_size_10ms = - static_cast(dst_sample_rate_hz_ * num_channels_ / 100); - if (src_length != src_size_10ms || dst_capacity < dst_size_10ms) - return -1; + CheckExpectedBufferSizes(src_length, dst_capacity, num_channels_, + src_sample_rate_hz_, dst_sample_rate_hz_); if (src_sample_rate_hz_ == dst_sample_rate_hz_) { // The old resampler provides this memcpy facility in the case of matching @@ -81,26 +118,30 @@ int PushResampler::Resample(const T* src, size_t src_length, T* dst, memcpy(dst, src, src_length * sizeof(T)); return static_cast(src_length); } - if (num_channels_ == 2) { - const size_t src_length_mono = src_length / num_channels_; - const size_t dst_capacity_mono = dst_capacity / num_channels_; - T* deinterleaved[] = {src_left_.get(), src_right_.get()}; - Deinterleave(src, src_length_mono, num_channels_, deinterleaved); - size_t dst_length_mono = - sinc_resampler_->Resample(src_left_.get(), src_length_mono, - dst_left_.get(), dst_capacity_mono); - sinc_resampler_right_->Resample(src_right_.get(), src_length_mono, - dst_right_.get(), dst_capacity_mono); + const size_t src_length_mono = src_length / num_channels_; + const size_t dst_capacity_mono = dst_capacity / num_channels_; - deinterleaved[0] = dst_left_.get(); - deinterleaved[1] = dst_right_.get(); - Interleave(deinterleaved, dst_length_mono, num_channels_, dst); - return static_cast(dst_length_mono * num_channels_); - } else { - return static_cast( - sinc_resampler_->Resample(src, src_length, dst, dst_capacity)); + for (size_t ch = 0; ch < num_channels_; ++ch) { + channel_data_array_[ch] = channel_resamplers_[ch].source.data(); } + + Deinterleave(src, src_length_mono, num_channels_, channel_data_array_.data()); + + size_t dst_length_mono = 0; + + for (auto& resampler : channel_resamplers_) { + dst_length_mono = resampler.resampler->Resample( + resampler.source.data(), src_length_mono, resampler.destination.data(), + dst_capacity_mono); + } + + for (size_t ch = 0; ch < num_channels_; ++ch) { + channel_data_array_[ch] = channel_resamplers_[ch].destination.data(); + } + + Interleave(channel_data_array_.data(), dst_length_mono, num_channels_, dst); + return static_cast(dst_length_mono * num_channels_); } // Explictly generate required instantiations. diff --git a/webrtc/common_audio/resampler/push_sinc_resampler.cc b/webrtc/common_audio/resampler/push_sinc_resampler.cc index a740423..3bfead2 100644 --- a/webrtc/common_audio/resampler/push_sinc_resampler.cc +++ b/webrtc/common_audio/resampler/push_sinc_resampler.cc @@ -8,12 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/resampler/push_sinc_resampler.h" +#include "common_audio/resampler/push_sinc_resampler.h" #include -#include "webrtc/base/checks.h" -#include "webrtc/common_audio/include/audio_util.h" +#include "common_audio/include/audio_util.h" +#include "rtc_base/checks.h" namespace webrtc { @@ -28,8 +28,7 @@ PushSincResampler::PushSincResampler(size_t source_frames, first_pass_(true), source_available_(0) {} -PushSincResampler::~PushSincResampler() { -} +PushSincResampler::~PushSincResampler() {} size_t PushSincResampler::Resample(const int16_t* source, size_t source_length, diff --git a/webrtc/common_audio/resampler/push_sinc_resampler.h b/webrtc/common_audio/resampler/push_sinc_resampler.h index cefc62a..bd609c4 100644 --- a/webrtc/common_audio/resampler/push_sinc_resampler.h +++ b/webrtc/common_audio/resampler/push_sinc_resampler.h @@ -8,13 +8,16 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_ -#define WEBRTC_COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_ +#ifndef COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_ +#define COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_ -#include "webrtc/base/constructormagic.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/resampler/sinc_resampler.h" -#include "webrtc/typedefs.h" +#include +#include + +#include + +#include "common_audio/resampler/sinc_resampler.h" +#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -35,8 +38,10 @@ class PushSincResampler : public SincResamplerCallback { // at least as large as |destination_frames|. Returns the number of samples // provided in destination (for convenience, since this will always be equal // to |destination_frames|). - size_t Resample(const int16_t* source, size_t source_frames, - int16_t* destination, size_t destination_capacity); + size_t Resample(const int16_t* source, + size_t source_frames, + int16_t* destination, + size_t destination_capacity); size_t Resample(const float* source, size_t source_frames, float* destination, @@ -56,8 +61,8 @@ class PushSincResampler : public SincResamplerCallback { friend class PushSincResamplerTest; SincResampler* get_resampler_for_testing() { return resampler_.get(); } - rtc::scoped_ptr resampler_; - rtc::scoped_ptr float_buffer_; + std::unique_ptr resampler_; + std::unique_ptr float_buffer_; const float* source_ptr_; const int16_t* source_ptr_int_; const size_t destination_frames_; @@ -73,4 +78,4 @@ class PushSincResampler : public SincResamplerCallback { } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_ +#endif // COMMON_AUDIO_RESAMPLER_PUSH_SINC_RESAMPLER_H_ diff --git a/webrtc/common_audio/resampler/resampler.cc b/webrtc/common_audio/resampler/resampler.cc index c9e7a1f..ccfed5a 100644 --- a/webrtc/common_audio/resampler/resampler.cc +++ b/webrtc/common_audio/resampler/resampler.cc @@ -8,16 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ - /* * A wrapper for resampling a numerous amount of sampling combinations. */ +#include "common_audio/resampler/include/resampler.h" + +#include #include #include -#include "webrtc/common_audio/resampler/include/resampler.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/logging.h" namespace webrtc { @@ -35,925 +37,888 @@ Resampler::Resampler() my_out_frequency_khz_(0), my_mode_(kResamplerMode1To1), num_channels_(0), - slave_left_(nullptr), - slave_right_(nullptr) { -} + helper_left_(nullptr), + helper_right_(nullptr) {} -Resampler::Resampler(int inFreq, int outFreq, int num_channels) +Resampler::Resampler(int inFreq, int outFreq, size_t num_channels) : Resampler() { Reset(inFreq, outFreq, num_channels); } -Resampler::~Resampler() -{ - if (state1_) - { - free(state1_); - } - if (state2_) - { - free(state2_); - } - if (state3_) - { - free(state3_); - } - if (in_buffer_) - { - free(in_buffer_); - } - if (out_buffer_) - { - free(out_buffer_); - } - if (slave_left_) - { - delete slave_left_; - } - if (slave_right_) - { - delete slave_right_; - } +Resampler::~Resampler() { + if (state1_) { + free(state1_); + } + if (state2_) { + free(state2_); + } + if (state3_) { + free(state3_); + } + if (in_buffer_) { + free(in_buffer_); + } + if (out_buffer_) { + free(out_buffer_); + } + if (helper_left_) { + delete helper_left_; + } + if (helper_right_) { + delete helper_right_; + } } -int Resampler::ResetIfNeeded(int inFreq, int outFreq, int num_channels) -{ - int tmpInFreq_kHz = inFreq / 1000; - int tmpOutFreq_kHz = outFreq / 1000; +int Resampler::ResetIfNeeded(int inFreq, int outFreq, size_t num_channels) { + int tmpInFreq_kHz = inFreq / 1000; + int tmpOutFreq_kHz = outFreq / 1000; - if ((tmpInFreq_kHz != my_in_frequency_khz_) || (tmpOutFreq_kHz != my_out_frequency_khz_) - || (num_channels != num_channels_)) - { - return Reset(inFreq, outFreq, num_channels); - } else - { - return 0; - } + if ((tmpInFreq_kHz != my_in_frequency_khz_) || + (tmpOutFreq_kHz != my_out_frequency_khz_) || + (num_channels != num_channels_)) { + return Reset(inFreq, outFreq, num_channels); + } else { + return 0; + } } -int Resampler::Reset(int inFreq, int outFreq, int num_channels) -{ - if (num_channels != 1 && num_channels != 2) { - return -1; - } - num_channels_ = num_channels; +int Resampler::Reset(int inFreq, int outFreq, size_t num_channels) { + if (num_channels != 1 && num_channels != 2) { + RTC_LOG(LS_WARNING) + << "Reset() called with unsupported channel count, num_channels = " + << num_channels; + return -1; + } + ResamplerMode mode; + if (ComputeResamplerMode(inFreq, outFreq, &mode) != 0) { + RTC_LOG(LS_WARNING) + << "Reset() called with unsupported sample rates, inFreq = " << inFreq + << ", outFreq = " << outFreq; + return -1; + } + // Reinitialize internal state for the frequencies and sample rates. + num_channels_ = num_channels; + my_mode_ = mode; - if (state1_) - { - free(state1_); - state1_ = NULL; - } - if (state2_) - { - free(state2_); - state2_ = NULL; - } - if (state3_) - { - free(state3_); - state3_ = NULL; - } - if (in_buffer_) - { - free(in_buffer_); - in_buffer_ = NULL; - } - if (out_buffer_) - { - free(out_buffer_); - out_buffer_ = NULL; - } - if (slave_left_) - { - delete slave_left_; - slave_left_ = NULL; - } - if (slave_right_) - { - delete slave_right_; - slave_right_ = NULL; - } + if (state1_) { + free(state1_); + state1_ = nullptr; + } + if (state2_) { + free(state2_); + state2_ = nullptr; + } + if (state3_) { + free(state3_); + state3_ = nullptr; + } + if (in_buffer_) { + free(in_buffer_); + in_buffer_ = nullptr; + } + if (out_buffer_) { + free(out_buffer_); + out_buffer_ = nullptr; + } + if (helper_left_) { + delete helper_left_; + helper_left_ = nullptr; + } + if (helper_right_) { + delete helper_right_; + helper_right_ = nullptr; + } - in_buffer_size_ = 0; - out_buffer_size_ = 0; - in_buffer_size_max_ = 0; - out_buffer_size_max_ = 0; + in_buffer_size_ = 0; + out_buffer_size_ = 0; + in_buffer_size_max_ = 0; + out_buffer_size_max_ = 0; - // Start with a math exercise, Euclid's algorithm to find the gcd: - int a = inFreq; - int b = outFreq; - int c = a % b; - while (c != 0) - { - a = b; - b = c; - c = a % b; - } - // b is now the gcd; + // We need to track what domain we're in. + my_in_frequency_khz_ = inFreq / 1000; + my_out_frequency_khz_ = outFreq / 1000; - // We need to track what domain we're in. - my_in_frequency_khz_ = inFreq / 1000; - my_out_frequency_khz_ = outFreq / 1000; + if (num_channels_ == 2) { + // Create two mono resamplers. + helper_left_ = new Resampler(inFreq, outFreq, 1); + helper_right_ = new Resampler(inFreq, outFreq, 1); + } - // Scale with GCD - inFreq = inFreq / b; - outFreq = outFreq / b; + // Now create the states we need. + switch (my_mode_) { + case kResamplerMode1To1: + // No state needed; + break; + case kResamplerMode1To2: + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode1To3: + state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); + WebRtcSpl_ResetResample16khzTo48khz( + static_cast(state1_)); + break; + case kResamplerMode1To4: + // 1:2 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + // 2:4 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode1To6: + // 1:2 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + // 2:6 + state2_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); + WebRtcSpl_ResetResample16khzTo48khz( + static_cast(state2_)); + break; + case kResamplerMode1To12: + // 1:2 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + // 2:4 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + // 4:12 + state3_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); + WebRtcSpl_ResetResample16khzTo48khz( + static_cast(state3_)); + break; + case kResamplerMode2To3: + // 2:6 + state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); + WebRtcSpl_ResetResample16khzTo48khz( + static_cast(state1_)); + // 6:3 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode2To11: + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); - if (num_channels_ == 2) - { - // Create two mono resamplers. - slave_left_ = new Resampler(inFreq, outFreq, 1); - slave_right_ = new Resampler(inFreq, outFreq, 1); - } + state2_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz)); + WebRtcSpl_ResetResample8khzTo22khz( + static_cast(state2_)); + break; + case kResamplerMode4To11: + state1_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz)); + WebRtcSpl_ResetResample8khzTo22khz( + static_cast(state1_)); + break; + case kResamplerMode8To11: + state1_ = malloc(sizeof(WebRtcSpl_State16khzTo22khz)); + WebRtcSpl_ResetResample16khzTo22khz( + static_cast(state1_)); + break; + case kResamplerMode11To16: + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); - if (inFreq == outFreq) - { - my_mode_ = kResamplerMode1To1; - } else if (inFreq == 1) - { - switch (outFreq) - { - case 2: - my_mode_ = kResamplerMode1To2; - break; - case 3: - my_mode_ = kResamplerMode1To3; - break; - case 4: - my_mode_ = kResamplerMode1To4; - break; - case 6: - my_mode_ = kResamplerMode1To6; - break; - case 12: - my_mode_ = kResamplerMode1To12; - break; - default: - return -1; - } - } else if (outFreq == 1) - { - switch (inFreq) - { - case 2: - my_mode_ = kResamplerMode2To1; - break; - case 3: - my_mode_ = kResamplerMode3To1; - break; - case 4: - my_mode_ = kResamplerMode4To1; - break; - case 6: - my_mode_ = kResamplerMode6To1; - break; - case 12: - my_mode_ = kResamplerMode12To1; - break; - default: - return -1; - } - } else if ((inFreq == 2) && (outFreq == 3)) - { - my_mode_ = kResamplerMode2To3; - } else if ((inFreq == 2) && (outFreq == 11)) - { - my_mode_ = kResamplerMode2To11; - } else if ((inFreq == 4) && (outFreq == 11)) - { - my_mode_ = kResamplerMode4To11; - } else if ((inFreq == 8) && (outFreq == 11)) - { - my_mode_ = kResamplerMode8To11; - } else if ((inFreq == 3) && (outFreq == 2)) - { - my_mode_ = kResamplerMode3To2; - } else if ((inFreq == 11) && (outFreq == 2)) - { - my_mode_ = kResamplerMode11To2; - } else if ((inFreq == 11) && (outFreq == 4)) - { - my_mode_ = kResamplerMode11To4; - } else if ((inFreq == 11) && (outFreq == 16)) - { - my_mode_ = kResamplerMode11To16; - } else if ((inFreq == 11) && (outFreq == 32)) - { - my_mode_ = kResamplerMode11To32; - } else if ((inFreq == 11) && (outFreq == 8)) - { - my_mode_ = kResamplerMode11To8; - } else - { + state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); + WebRtcSpl_ResetResample22khzTo16khz( + static_cast(state2_)); + break; + case kResamplerMode11To32: + // 11 -> 22 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + + // 22 -> 16 + state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); + WebRtcSpl_ResetResample22khzTo16khz( + static_cast(state2_)); + + // 16 -> 32 + state3_ = malloc(8 * sizeof(int32_t)); + memset(state3_, 0, 8 * sizeof(int32_t)); + + break; + case kResamplerMode2To1: + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode3To1: + state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); + WebRtcSpl_ResetResample48khzTo16khz( + static_cast(state1_)); + break; + case kResamplerMode4To1: + // 4:2 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + // 2:1 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode6To1: + // 6:2 + state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); + WebRtcSpl_ResetResample48khzTo16khz( + static_cast(state1_)); + // 2:1 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode12To1: + // 12:4 + state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); + WebRtcSpl_ResetResample48khzTo16khz( + static_cast(state1_)); + // 4:2 + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + // 2:1 + state3_ = malloc(8 * sizeof(int32_t)); + memset(state3_, 0, 8 * sizeof(int32_t)); + break; + case kResamplerMode3To2: + // 3:6 + state1_ = malloc(8 * sizeof(int32_t)); + memset(state1_, 0, 8 * sizeof(int32_t)); + // 6:2 + state2_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); + WebRtcSpl_ResetResample48khzTo16khz( + static_cast(state2_)); + break; + case kResamplerMode11To2: + state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz)); + WebRtcSpl_ResetResample22khzTo8khz( + static_cast(state1_)); + + state2_ = malloc(8 * sizeof(int32_t)); + memset(state2_, 0, 8 * sizeof(int32_t)); + + break; + case kResamplerMode11To4: + state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz)); + WebRtcSpl_ResetResample22khzTo8khz( + static_cast(state1_)); + break; + case kResamplerMode11To8: + state1_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); + WebRtcSpl_ResetResample22khzTo16khz( + static_cast(state1_)); + break; + } + + return 0; +} + +int Resampler::ComputeResamplerMode(int in_freq_hz, + int out_freq_hz, + ResamplerMode* mode) { + // Start with a math exercise, Euclid's algorithm to find the gcd: + int a = in_freq_hz; + int b = out_freq_hz; + int c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + // b is now the gcd; + + // Scale with GCD + const int reduced_in_freq = in_freq_hz / b; + const int reduced_out_freq = out_freq_hz / b; + + if (reduced_in_freq == reduced_out_freq) { + *mode = kResamplerMode1To1; + } else if (reduced_in_freq == 1) { + switch (reduced_out_freq) { + case 2: + *mode = kResamplerMode1To2; + break; + case 3: + *mode = kResamplerMode1To3; + break; + case 4: + *mode = kResamplerMode1To4; + break; + case 6: + *mode = kResamplerMode1To6; + break; + case 12: + *mode = kResamplerMode1To12; + break; + default: return -1; } - - // Now create the states we need - switch (my_mode_) - { - case kResamplerMode1To1: - // No state needed; - break; - case kResamplerMode1To2: - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode1To3: - state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); - WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state1_); - break; - case kResamplerMode1To4: - // 1:2 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - // 2:4 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode1To6: - // 1:2 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - // 2:6 - state2_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); - WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state2_); - break; - case kResamplerMode1To12: - // 1:2 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - // 2:4 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - // 4:12 - state3_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); - WebRtcSpl_ResetResample16khzTo48khz( - (WebRtcSpl_State16khzTo48khz*) state3_); - break; - case kResamplerMode2To3: - // 2:6 - state1_ = malloc(sizeof(WebRtcSpl_State16khzTo48khz)); - WebRtcSpl_ResetResample16khzTo48khz((WebRtcSpl_State16khzTo48khz *)state1_); - // 6:3 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode2To11: - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - - state2_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz)); - WebRtcSpl_ResetResample8khzTo22khz((WebRtcSpl_State8khzTo22khz *)state2_); - break; - case kResamplerMode4To11: - state1_ = malloc(sizeof(WebRtcSpl_State8khzTo22khz)); - WebRtcSpl_ResetResample8khzTo22khz((WebRtcSpl_State8khzTo22khz *)state1_); - break; - case kResamplerMode8To11: - state1_ = malloc(sizeof(WebRtcSpl_State16khzTo22khz)); - WebRtcSpl_ResetResample16khzTo22khz((WebRtcSpl_State16khzTo22khz *)state1_); - break; - case kResamplerMode11To16: - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - - state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); - WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state2_); - break; - case kResamplerMode11To32: - // 11 -> 22 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - - // 22 -> 16 - state2_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); - WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state2_); - - // 16 -> 32 - state3_ = malloc(8 * sizeof(int32_t)); - memset(state3_, 0, 8 * sizeof(int32_t)); - - break; - case kResamplerMode2To1: - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode3To1: - state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); - WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state1_); - break; - case kResamplerMode4To1: - // 4:2 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - // 2:1 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode6To1: - // 6:2 - state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); - WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state1_); - // 2:1 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode12To1: - // 12:4 - state1_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); - WebRtcSpl_ResetResample48khzTo16khz( - (WebRtcSpl_State48khzTo16khz*) state1_); - // 4:2 - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - // 2:1 - state3_ = malloc(8 * sizeof(int32_t)); - memset(state3_, 0, 8 * sizeof(int32_t)); - break; - case kResamplerMode3To2: - // 3:6 - state1_ = malloc(8 * sizeof(int32_t)); - memset(state1_, 0, 8 * sizeof(int32_t)); - // 6:2 - state2_ = malloc(sizeof(WebRtcSpl_State48khzTo16khz)); - WebRtcSpl_ResetResample48khzTo16khz((WebRtcSpl_State48khzTo16khz *)state2_); - break; - case kResamplerMode11To2: - state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz)); - WebRtcSpl_ResetResample22khzTo8khz((WebRtcSpl_State22khzTo8khz *)state1_); - - state2_ = malloc(8 * sizeof(int32_t)); - memset(state2_, 0, 8 * sizeof(int32_t)); - - break; - case kResamplerMode11To4: - state1_ = malloc(sizeof(WebRtcSpl_State22khzTo8khz)); - WebRtcSpl_ResetResample22khzTo8khz((WebRtcSpl_State22khzTo8khz *)state1_); - break; - case kResamplerMode11To8: - state1_ = malloc(sizeof(WebRtcSpl_State22khzTo16khz)); - WebRtcSpl_ResetResample22khzTo16khz((WebRtcSpl_State22khzTo16khz *)state1_); - break; - + } else if (reduced_out_freq == 1) { + switch (reduced_in_freq) { + case 2: + *mode = kResamplerMode2To1; + break; + case 3: + *mode = kResamplerMode3To1; + break; + case 4: + *mode = kResamplerMode4To1; + break; + case 6: + *mode = kResamplerMode6To1; + break; + case 12: + *mode = kResamplerMode12To1; + break; + default: + return -1; } - - return 0; + } else if ((reduced_in_freq == 2) && (reduced_out_freq == 3)) { + *mode = kResamplerMode2To3; + } else if ((reduced_in_freq == 2) && (reduced_out_freq == 11)) { + *mode = kResamplerMode2To11; + } else if ((reduced_in_freq == 4) && (reduced_out_freq == 11)) { + *mode = kResamplerMode4To11; + } else if ((reduced_in_freq == 8) && (reduced_out_freq == 11)) { + *mode = kResamplerMode8To11; + } else if ((reduced_in_freq == 3) && (reduced_out_freq == 2)) { + *mode = kResamplerMode3To2; + } else if ((reduced_in_freq == 11) && (reduced_out_freq == 2)) { + *mode = kResamplerMode11To2; + } else if ((reduced_in_freq == 11) && (reduced_out_freq == 4)) { + *mode = kResamplerMode11To4; + } else if ((reduced_in_freq == 11) && (reduced_out_freq == 16)) { + *mode = kResamplerMode11To16; + } else if ((reduced_in_freq == 11) && (reduced_out_freq == 32)) { + *mode = kResamplerMode11To32; + } else if ((reduced_in_freq == 11) && (reduced_out_freq == 8)) { + *mode = kResamplerMode11To8; + } else { + return -1; + } + return 0; } // Synchronous resampling, all output samples are written to samplesOut -int Resampler::Push(const int16_t * samplesIn, size_t lengthIn, - int16_t* samplesOut, size_t maxLen, size_t &outLen) -{ - if (num_channels_ == 2) - { - // Split up the signal and call the slave object for each channel - int16_t* left = (int16_t*)malloc(lengthIn * sizeof(int16_t) / 2); - int16_t* right = (int16_t*)malloc(lengthIn * sizeof(int16_t) / 2); - int16_t* out_left = (int16_t*)malloc(maxLen / 2 * sizeof(int16_t)); - int16_t* out_right = - (int16_t*)malloc(maxLen / 2 * sizeof(int16_t)); - int res = 0; - for (size_t i = 0; i < lengthIn; i += 2) - { - left[i >> 1] = samplesIn[i]; - right[i >> 1] = samplesIn[i + 1]; - } - - // It's OK to overwrite the local parameter, since it's just a copy - lengthIn = lengthIn / 2; - - size_t actualOutLen_left = 0; - size_t actualOutLen_right = 0; - // Do resampling for right channel - res |= slave_left_->Push(left, lengthIn, out_left, maxLen / 2, actualOutLen_left); - res |= slave_right_->Push(right, lengthIn, out_right, maxLen / 2, actualOutLen_right); - if (res || (actualOutLen_left != actualOutLen_right)) - { - free(left); - free(right); - free(out_left); - free(out_right); - return -1; - } - - // Reassemble the signal - for (size_t i = 0; i < actualOutLen_left; i++) - { - samplesOut[i * 2] = out_left[i]; - samplesOut[i * 2 + 1] = out_right[i]; - } - outLen = 2 * actualOutLen_left; - - free(left); - free(right); - free(out_left); - free(out_right); - - return 0; +int Resampler::Push(const int16_t* samplesIn, + size_t lengthIn, + int16_t* samplesOut, + size_t maxLen, + size_t& outLen) { + if (num_channels_ == 2) { + // Split up the signal and call the helper object for each channel + int16_t* left = + static_cast(malloc(lengthIn * sizeof(int16_t) / 2)); + int16_t* right = + static_cast(malloc(lengthIn * sizeof(int16_t) / 2)); + int16_t* out_left = + static_cast(malloc(maxLen / 2 * sizeof(int16_t))); + int16_t* out_right = + static_cast(malloc(maxLen / 2 * sizeof(int16_t))); + int res = 0; + for (size_t i = 0; i < lengthIn; i += 2) { + left[i >> 1] = samplesIn[i]; + right[i >> 1] = samplesIn[i + 1]; } - // Containers for temp samples - int16_t* tmp; - int16_t* tmp_2; - // tmp data for resampling routines - int32_t* tmp_mem; - - switch (my_mode_) - { - case kResamplerMode1To1: - memcpy(samplesOut, samplesIn, lengthIn * sizeof(int16_t)); - outLen = lengthIn; - break; - case kResamplerMode1To2: - if (maxLen < (lengthIn * 2)) - { - return -1; - } - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_); - outLen = lengthIn * 2; - return 0; - case kResamplerMode1To3: - - // We can only handle blocks of 160 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 160) != 0) - { - return -1; - } - if (maxLen < (lengthIn * 3)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t)); - - for (size_t i = 0; i < lengthIn; i += 160) - { - WebRtcSpl_Resample16khzTo48khz(samplesIn + i, samplesOut + i * 3, - (WebRtcSpl_State16khzTo48khz *)state1_, - tmp_mem); - } - outLen = lengthIn * 3; - free(tmp_mem); - return 0; - case kResamplerMode1To4: - if (maxLen < (lengthIn * 4)) - { - return -1; - } - - tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn); - // 1:2 - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - // 2:4 - WebRtcSpl_UpsampleBy2(tmp, lengthIn * 2, samplesOut, (int32_t*)state2_); - outLen = lengthIn * 4; - free(tmp); - return 0; - case kResamplerMode1To6: - // We can only handle blocks of 80 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 80) != 0) - { - return -1; - } - if (maxLen < (lengthIn * 6)) - { - return -1; - } - - //1:2 - - tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t)); - tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn); - - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - outLen = lengthIn * 2; - - for (size_t i = 0; i < outLen; i += 160) - { - WebRtcSpl_Resample16khzTo48khz(tmp + i, samplesOut + i * 3, - (WebRtcSpl_State16khzTo48khz *)state2_, - tmp_mem); - } - outLen = outLen * 3; - free(tmp_mem); - free(tmp); - - return 0; - case kResamplerMode1To12: - // We can only handle blocks of 40 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 40) != 0) { - return -1; - } - if (maxLen < (lengthIn * 12)) { - return -1; - } - - tmp_mem = (int32_t*) malloc(336 * sizeof(int32_t)); - tmp = (int16_t*) malloc(sizeof(int16_t) * 4 * lengthIn); - //1:2 - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, - (int32_t*) state1_); - outLen = lengthIn * 2; - //2:4 - WebRtcSpl_UpsampleBy2(samplesOut, outLen, tmp, (int32_t*) state2_); - outLen = outLen * 2; - // 4:12 - for (size_t i = 0; i < outLen; i += 160) { - // WebRtcSpl_Resample16khzTo48khz() takes a block of 160 samples - // as input and outputs a resampled block of 480 samples. The - // data is now actually in 32 kHz sampling rate, despite the - // function name, and with a resampling factor of three becomes - // 96 kHz. - WebRtcSpl_Resample16khzTo48khz(tmp + i, samplesOut + i * 3, - (WebRtcSpl_State16khzTo48khz*) state3_, - tmp_mem); - } - outLen = outLen * 3; - free(tmp_mem); - free(tmp); - - return 0; - case kResamplerMode2To3: - if (maxLen < (lengthIn * 3 / 2)) - { - return -1; - } - // 2:6 - // We can only handle blocks of 160 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 160) != 0) - { - return -1; - } - tmp = static_cast (malloc(sizeof(int16_t) * lengthIn * 3)); - tmp_mem = (int32_t*)malloc(336 * sizeof(int32_t)); - for (size_t i = 0; i < lengthIn; i += 160) - { - WebRtcSpl_Resample16khzTo48khz(samplesIn + i, tmp + i * 3, - (WebRtcSpl_State16khzTo48khz *)state1_, - tmp_mem); - } - lengthIn = lengthIn * 3; - // 6:3 - WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut, (int32_t*)state2_); - outLen = lengthIn / 2; - free(tmp); - free(tmp_mem); - return 0; - case kResamplerMode2To11: - - // We can only handle blocks of 80 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 80) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 11) / 2)) - { - return -1; - } - tmp = (int16_t*)malloc(sizeof(int16_t) * 2 * lengthIn); - // 1:2 - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - lengthIn *= 2; - - tmp_mem = (int32_t*)malloc(98 * sizeof(int32_t)); - - for (size_t i = 0; i < lengthIn; i += 80) - { - WebRtcSpl_Resample8khzTo22khz(tmp + i, samplesOut + (i * 11) / 4, - (WebRtcSpl_State8khzTo22khz *)state2_, - tmp_mem); - } - outLen = (lengthIn * 11) / 4; - free(tmp_mem); - free(tmp); - return 0; - case kResamplerMode4To11: - - // We can only handle blocks of 80 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 80) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 11) / 4)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(98 * sizeof(int32_t)); - - for (size_t i = 0; i < lengthIn; i += 80) - { - WebRtcSpl_Resample8khzTo22khz(samplesIn + i, samplesOut + (i * 11) / 4, - (WebRtcSpl_State8khzTo22khz *)state1_, - tmp_mem); - } - outLen = (lengthIn * 11) / 4; - free(tmp_mem); - return 0; - case kResamplerMode8To11: - // We can only handle blocks of 160 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 160) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 11) / 8)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(88 * sizeof(int32_t)); - - for (size_t i = 0; i < lengthIn; i += 160) - { - WebRtcSpl_Resample16khzTo22khz(samplesIn + i, samplesOut + (i * 11) / 8, - (WebRtcSpl_State16khzTo22khz *)state1_, - tmp_mem); - } - outLen = (lengthIn * 11) / 8; - free(tmp_mem); - return 0; - - case kResamplerMode11To16: - // We can only handle blocks of 110 samples - if ((lengthIn % 110) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 16) / 11)) - { - return -1; - } - - tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t)); - tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn * 2)); - - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - - for (size_t i = 0; i < (lengthIn * 2); i += 220) - { - WebRtcSpl_Resample22khzTo16khz(tmp + i, samplesOut + (i / 220) * 160, - (WebRtcSpl_State22khzTo16khz *)state2_, - tmp_mem); - } - - outLen = (lengthIn * 16) / 11; - - free(tmp_mem); - free(tmp); - return 0; - - case kResamplerMode11To32: - - // We can only handle blocks of 110 samples - if ((lengthIn % 110) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 32) / 11)) - { - return -1; - } - - tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t)); - tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn * 2)); - - // 11 -> 22 kHz in samplesOut - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_); - - // 22 -> 16 in tmp - for (size_t i = 0; i < (lengthIn * 2); i += 220) - { - WebRtcSpl_Resample22khzTo16khz(samplesOut + i, tmp + (i / 220) * 160, - (WebRtcSpl_State22khzTo16khz *)state2_, - tmp_mem); - } - - // 16 -> 32 in samplesOut - WebRtcSpl_UpsampleBy2(tmp, (lengthIn * 16) / 11, samplesOut, - (int32_t*)state3_); - - outLen = (lengthIn * 32) / 11; - - free(tmp_mem); - free(tmp); - return 0; - - case kResamplerMode2To1: - if (maxLen < (lengthIn / 2)) - { - return -1; - } - WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, samplesOut, (int32_t*)state1_); - outLen = lengthIn / 2; - return 0; - case kResamplerMode3To1: - // We can only handle blocks of 480 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 480) != 0) - { - return -1; - } - if (maxLen < (lengthIn / 3)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t)); - - for (size_t i = 0; i < lengthIn; i += 480) - { - WebRtcSpl_Resample48khzTo16khz(samplesIn + i, samplesOut + i / 3, - (WebRtcSpl_State48khzTo16khz *)state1_, - tmp_mem); - } - outLen = lengthIn / 3; - free(tmp_mem); - return 0; - case kResamplerMode4To1: - if (maxLen < (lengthIn / 4)) - { - return -1; - } - tmp = (int16_t*)malloc(sizeof(int16_t) * lengthIn / 2); - // 4:2 - WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - // 2:1 - WebRtcSpl_DownsampleBy2(tmp, lengthIn / 2, samplesOut, (int32_t*)state2_); - outLen = lengthIn / 4; - free(tmp); - return 0; - - case kResamplerMode6To1: - // We can only handle blocks of 480 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 480) != 0) - { - return -1; - } - if (maxLen < (lengthIn / 6)) - { - return -1; - } - - tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t)); - tmp = (int16_t*)malloc((sizeof(int16_t) * lengthIn) / 3); - - for (size_t i = 0; i < lengthIn; i += 480) - { - WebRtcSpl_Resample48khzTo16khz(samplesIn + i, tmp + i / 3, - (WebRtcSpl_State48khzTo16khz *)state1_, - tmp_mem); - } - outLen = lengthIn / 3; - free(tmp_mem); - WebRtcSpl_DownsampleBy2(tmp, outLen, samplesOut, (int32_t*)state2_); - free(tmp); - outLen = outLen / 2; - return 0; - case kResamplerMode12To1: - // We can only handle blocks of 480 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 480) != 0) { - return -1; - } - if (maxLen < (lengthIn / 12)) { - return -1; - } - - tmp_mem = (int32_t*) malloc(496 * sizeof(int32_t)); - tmp = (int16_t*) malloc((sizeof(int16_t) * lengthIn) / 3); - tmp_2 = (int16_t*) malloc((sizeof(int16_t) * lengthIn) / 6); - // 12:4 - for (size_t i = 0; i < lengthIn; i += 480) { - // WebRtcSpl_Resample48khzTo16khz() takes a block of 480 samples - // as input and outputs a resampled block of 160 samples. The - // data is now actually in 96 kHz sampling rate, despite the - // function name, and with a resampling factor of 1/3 becomes - // 32 kHz. - WebRtcSpl_Resample48khzTo16khz(samplesIn + i, tmp + i / 3, - (WebRtcSpl_State48khzTo16khz*) state1_, - tmp_mem); - } - outLen = lengthIn / 3; - free(tmp_mem); - // 4:2 - WebRtcSpl_DownsampleBy2(tmp, outLen, tmp_2, (int32_t*) state2_); - outLen = outLen / 2; - free(tmp); - // 2:1 - WebRtcSpl_DownsampleBy2(tmp_2, outLen, samplesOut, - (int32_t*) state3_); - free(tmp_2); - outLen = outLen / 2; - return 0; - case kResamplerMode3To2: - if (maxLen < (lengthIn * 2 / 3)) - { - return -1; - } - // 3:6 - tmp = static_cast (malloc(sizeof(int16_t) * lengthIn * 2)); - WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, (int32_t*)state1_); - lengthIn *= 2; - // 6:2 - // We can only handle blocks of 480 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 480) != 0) - { - free(tmp); - return -1; - } - tmp_mem = (int32_t*)malloc(496 * sizeof(int32_t)); - for (size_t i = 0; i < lengthIn; i += 480) - { - WebRtcSpl_Resample48khzTo16khz(tmp + i, samplesOut + i / 3, - (WebRtcSpl_State48khzTo16khz *)state2_, - tmp_mem); - } - outLen = lengthIn / 3; - free(tmp); - free(tmp_mem); - return 0; - case kResamplerMode11To2: - // We can only handle blocks of 220 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 220) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 2) / 11)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(126 * sizeof(int32_t)); - tmp = (int16_t*)malloc((lengthIn * 4) / 11 * sizeof(int16_t)); - - for (size_t i = 0; i < lengthIn; i += 220) - { - WebRtcSpl_Resample22khzTo8khz(samplesIn + i, tmp + (i * 4) / 11, - (WebRtcSpl_State22khzTo8khz *)state1_, - tmp_mem); - } - lengthIn = (lengthIn * 4) / 11; - - WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut, - (int32_t*)state2_); - outLen = lengthIn / 2; - - free(tmp_mem); - free(tmp); - return 0; - case kResamplerMode11To4: - // We can only handle blocks of 220 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 220) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 4) / 11)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(126 * sizeof(int32_t)); - - for (size_t i = 0; i < lengthIn; i += 220) - { - WebRtcSpl_Resample22khzTo8khz(samplesIn + i, samplesOut + (i * 4) / 11, - (WebRtcSpl_State22khzTo8khz *)state1_, - tmp_mem); - } - outLen = (lengthIn * 4) / 11; - free(tmp_mem); - return 0; - case kResamplerMode11To8: - // We can only handle blocks of 160 samples - // Can be fixed, but I don't think it's needed - if ((lengthIn % 220) != 0) - { - return -1; - } - if (maxLen < ((lengthIn * 8) / 11)) - { - return -1; - } - tmp_mem = (int32_t*)malloc(104 * sizeof(int32_t)); - - for (size_t i = 0; i < lengthIn; i += 220) - { - WebRtcSpl_Resample22khzTo16khz(samplesIn + i, samplesOut + (i * 8) / 11, - (WebRtcSpl_State22khzTo16khz *)state1_, - tmp_mem); - } - outLen = (lengthIn * 8) / 11; - free(tmp_mem); - return 0; - break; + // It's OK to overwrite the local parameter, since it's just a copy + lengthIn = lengthIn / 2; + size_t actualOutLen_left = 0; + size_t actualOutLen_right = 0; + // Do resampling for right channel + res |= helper_left_->Push(left, lengthIn, out_left, maxLen / 2, + actualOutLen_left); + res |= helper_right_->Push(right, lengthIn, out_right, maxLen / 2, + actualOutLen_right); + if (res || (actualOutLen_left != actualOutLen_right)) { + free(left); + free(right); + free(out_left); + free(out_right); + return -1; } + + // Reassemble the signal + for (size_t i = 0; i < actualOutLen_left; i++) { + samplesOut[i * 2] = out_left[i]; + samplesOut[i * 2 + 1] = out_right[i]; + } + outLen = 2 * actualOutLen_left; + + free(left); + free(right); + free(out_left); + free(out_right); + return 0; + } + + // Containers for temp samples + int16_t* tmp; + int16_t* tmp_2; + // tmp data for resampling routines + int32_t* tmp_mem; + + switch (my_mode_) { + case kResamplerMode1To1: + memcpy(samplesOut, samplesIn, lengthIn * sizeof(int16_t)); + outLen = lengthIn; + break; + case kResamplerMode1To2: + if (maxLen < (lengthIn * 2)) { + return -1; + } + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, + static_cast(state1_)); + outLen = lengthIn * 2; + return 0; + case kResamplerMode1To3: + + // We can only handle blocks of 160 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 160) != 0) { + return -1; + } + if (maxLen < (lengthIn * 3)) { + return -1; + } + tmp_mem = static_cast(malloc(336 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 160) { + WebRtcSpl_Resample16khzTo48khz( + samplesIn + i, samplesOut + i * 3, + static_cast(state1_), tmp_mem); + } + outLen = lengthIn * 3; + free(tmp_mem); + return 0; + case kResamplerMode1To4: + if (maxLen < (lengthIn * 4)) { + return -1; + } + + tmp = static_cast(malloc(sizeof(int16_t) * 2 * lengthIn)); + // 1:2 + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + // 2:4 + WebRtcSpl_UpsampleBy2(tmp, lengthIn * 2, samplesOut, + static_cast(state2_)); + outLen = lengthIn * 4; + free(tmp); + return 0; + case kResamplerMode1To6: + // We can only handle blocks of 80 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 80) != 0) { + return -1; + } + if (maxLen < (lengthIn * 6)) { + return -1; + } + + // 1:2 + + tmp_mem = static_cast(malloc(336 * sizeof(int32_t))); + tmp = static_cast(malloc(sizeof(int16_t) * 2 * lengthIn)); + + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + outLen = lengthIn * 2; + + for (size_t i = 0; i < outLen; i += 160) { + WebRtcSpl_Resample16khzTo48khz( + tmp + i, samplesOut + i * 3, + static_cast(state2_), tmp_mem); + } + outLen = outLen * 3; + free(tmp_mem); + free(tmp); + + return 0; + case kResamplerMode1To12: + // We can only handle blocks of 40 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 40) != 0) { + return -1; + } + if (maxLen < (lengthIn * 12)) { + return -1; + } + + tmp_mem = static_cast(malloc(336 * sizeof(int32_t))); + tmp = static_cast(malloc(sizeof(int16_t) * 4 * lengthIn)); + // 1:2 + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, + static_cast(state1_)); + outLen = lengthIn * 2; + // 2:4 + WebRtcSpl_UpsampleBy2(samplesOut, outLen, tmp, + static_cast(state2_)); + outLen = outLen * 2; + // 4:12 + for (size_t i = 0; i < outLen; i += 160) { + // WebRtcSpl_Resample16khzTo48khz() takes a block of 160 samples + // as input and outputs a resampled block of 480 samples. The + // data is now actually in 32 kHz sampling rate, despite the + // function name, and with a resampling factor of three becomes + // 96 kHz. + WebRtcSpl_Resample16khzTo48khz( + tmp + i, samplesOut + i * 3, + static_cast(state3_), tmp_mem); + } + outLen = outLen * 3; + free(tmp_mem); + free(tmp); + + return 0; + case kResamplerMode2To3: + if (maxLen < (lengthIn * 3 / 2)) { + return -1; + } + // 2:6 + // We can only handle blocks of 160 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 160) != 0) { + return -1; + } + tmp = static_cast(malloc(sizeof(int16_t) * lengthIn * 3)); + tmp_mem = static_cast(malloc(336 * sizeof(int32_t))); + for (size_t i = 0; i < lengthIn; i += 160) { + WebRtcSpl_Resample16khzTo48khz( + samplesIn + i, tmp + i * 3, + static_cast(state1_), tmp_mem); + } + lengthIn = lengthIn * 3; + // 6:3 + WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut, + static_cast(state2_)); + outLen = lengthIn / 2; + free(tmp); + free(tmp_mem); + return 0; + case kResamplerMode2To11: + + // We can only handle blocks of 80 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 80) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 11) / 2)) { + return -1; + } + tmp = static_cast(malloc(sizeof(int16_t) * 2 * lengthIn)); + // 1:2 + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + lengthIn *= 2; + + tmp_mem = static_cast(malloc(98 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 80) { + WebRtcSpl_Resample8khzTo22khz( + tmp + i, samplesOut + (i * 11) / 4, + static_cast(state2_), tmp_mem); + } + outLen = (lengthIn * 11) / 4; + free(tmp_mem); + free(tmp); + return 0; + case kResamplerMode4To11: + + // We can only handle blocks of 80 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 80) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 11) / 4)) { + return -1; + } + tmp_mem = static_cast(malloc(98 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 80) { + WebRtcSpl_Resample8khzTo22khz( + samplesIn + i, samplesOut + (i * 11) / 4, + static_cast(state1_), tmp_mem); + } + outLen = (lengthIn * 11) / 4; + free(tmp_mem); + return 0; + case kResamplerMode8To11: + // We can only handle blocks of 160 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 160) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 11) / 8)) { + return -1; + } + tmp_mem = static_cast(malloc(88 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 160) { + WebRtcSpl_Resample16khzTo22khz( + samplesIn + i, samplesOut + (i * 11) / 8, + static_cast(state1_), tmp_mem); + } + outLen = (lengthIn * 11) / 8; + free(tmp_mem); + return 0; + + case kResamplerMode11To16: + // We can only handle blocks of 110 samples + if ((lengthIn % 110) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 16) / 11)) { + return -1; + } + + tmp_mem = static_cast(malloc(104 * sizeof(int32_t))); + tmp = static_cast(malloc((sizeof(int16_t) * lengthIn * 2))); + + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + + for (size_t i = 0; i < (lengthIn * 2); i += 220) { + WebRtcSpl_Resample22khzTo16khz( + tmp + i, samplesOut + (i / 220) * 160, + static_cast(state2_), tmp_mem); + } + + outLen = (lengthIn * 16) / 11; + + free(tmp_mem); + free(tmp); + return 0; + + case kResamplerMode11To32: + + // We can only handle blocks of 110 samples + if ((lengthIn % 110) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 32) / 11)) { + return -1; + } + + tmp_mem = static_cast(malloc(104 * sizeof(int32_t))); + tmp = static_cast(malloc((sizeof(int16_t) * lengthIn * 2))); + + // 11 -> 22 kHz in samplesOut + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, samplesOut, + static_cast(state1_)); + + // 22 -> 16 in tmp + for (size_t i = 0; i < (lengthIn * 2); i += 220) { + WebRtcSpl_Resample22khzTo16khz( + samplesOut + i, tmp + (i / 220) * 160, + static_cast(state2_), tmp_mem); + } + + // 16 -> 32 in samplesOut + WebRtcSpl_UpsampleBy2(tmp, (lengthIn * 16) / 11, samplesOut, + static_cast(state3_)); + + outLen = (lengthIn * 32) / 11; + + free(tmp_mem); + free(tmp); + return 0; + + case kResamplerMode2To1: + if (maxLen < (lengthIn / 2)) { + return -1; + } + WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, samplesOut, + static_cast(state1_)); + outLen = lengthIn / 2; + return 0; + case kResamplerMode3To1: + // We can only handle blocks of 480 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 480) != 0) { + return -1; + } + if (maxLen < (lengthIn / 3)) { + return -1; + } + tmp_mem = static_cast(malloc(496 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 480) { + WebRtcSpl_Resample48khzTo16khz( + samplesIn + i, samplesOut + i / 3, + static_cast(state1_), tmp_mem); + } + outLen = lengthIn / 3; + free(tmp_mem); + return 0; + case kResamplerMode4To1: + if (maxLen < (lengthIn / 4)) { + return -1; + } + tmp = static_cast(malloc(sizeof(int16_t) * lengthIn / 2)); + // 4:2 + WebRtcSpl_DownsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + // 2:1 + WebRtcSpl_DownsampleBy2(tmp, lengthIn / 2, samplesOut, + static_cast(state2_)); + outLen = lengthIn / 4; + free(tmp); + return 0; + + case kResamplerMode6To1: + // We can only handle blocks of 480 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 480) != 0) { + return -1; + } + if (maxLen < (lengthIn / 6)) { + return -1; + } + + tmp_mem = static_cast(malloc(496 * sizeof(int32_t))); + tmp = static_cast(malloc((sizeof(int16_t) * lengthIn) / 3)); + + for (size_t i = 0; i < lengthIn; i += 480) { + WebRtcSpl_Resample48khzTo16khz( + samplesIn + i, tmp + i / 3, + static_cast(state1_), tmp_mem); + } + outLen = lengthIn / 3; + free(tmp_mem); + WebRtcSpl_DownsampleBy2(tmp, outLen, samplesOut, + static_cast(state2_)); + free(tmp); + outLen = outLen / 2; + return 0; + case kResamplerMode12To1: + // We can only handle blocks of 480 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 480) != 0) { + return -1; + } + if (maxLen < (lengthIn / 12)) { + return -1; + } + + tmp_mem = static_cast(malloc(496 * sizeof(int32_t))); + tmp = static_cast(malloc((sizeof(int16_t) * lengthIn) / 3)); + tmp_2 = static_cast(malloc((sizeof(int16_t) * lengthIn) / 6)); + // 12:4 + for (size_t i = 0; i < lengthIn; i += 480) { + // WebRtcSpl_Resample48khzTo16khz() takes a block of 480 samples + // as input and outputs a resampled block of 160 samples. The + // data is now actually in 96 kHz sampling rate, despite the + // function name, and with a resampling factor of 1/3 becomes + // 32 kHz. + WebRtcSpl_Resample48khzTo16khz( + samplesIn + i, tmp + i / 3, + static_cast(state1_), tmp_mem); + } + outLen = lengthIn / 3; + free(tmp_mem); + // 4:2 + WebRtcSpl_DownsampleBy2(tmp, outLen, tmp_2, + static_cast(state2_)); + outLen = outLen / 2; + free(tmp); + // 2:1 + WebRtcSpl_DownsampleBy2(tmp_2, outLen, samplesOut, + static_cast(state3_)); + free(tmp_2); + outLen = outLen / 2; + return 0; + case kResamplerMode3To2: + if (maxLen < (lengthIn * 2 / 3)) { + return -1; + } + // 3:6 + tmp = static_cast(malloc(sizeof(int16_t) * lengthIn * 2)); + WebRtcSpl_UpsampleBy2(samplesIn, lengthIn, tmp, + static_cast(state1_)); + lengthIn *= 2; + // 6:2 + // We can only handle blocks of 480 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 480) != 0) { + free(tmp); + return -1; + } + tmp_mem = static_cast(malloc(496 * sizeof(int32_t))); + for (size_t i = 0; i < lengthIn; i += 480) { + WebRtcSpl_Resample48khzTo16khz( + tmp + i, samplesOut + i / 3, + static_cast(state2_), tmp_mem); + } + outLen = lengthIn / 3; + free(tmp); + free(tmp_mem); + return 0; + case kResamplerMode11To2: + // We can only handle blocks of 220 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 220) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 2) / 11)) { + return -1; + } + tmp_mem = static_cast(malloc(126 * sizeof(int32_t))); + tmp = + static_cast(malloc((lengthIn * 4) / 11 * sizeof(int16_t))); + + for (size_t i = 0; i < lengthIn; i += 220) { + WebRtcSpl_Resample22khzTo8khz( + samplesIn + i, tmp + (i * 4) / 11, + static_cast(state1_), tmp_mem); + } + lengthIn = (lengthIn * 4) / 11; + + WebRtcSpl_DownsampleBy2(tmp, lengthIn, samplesOut, + static_cast(state2_)); + outLen = lengthIn / 2; + + free(tmp_mem); + free(tmp); + return 0; + case kResamplerMode11To4: + // We can only handle blocks of 220 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 220) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 4) / 11)) { + return -1; + } + tmp_mem = static_cast(malloc(126 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 220) { + WebRtcSpl_Resample22khzTo8khz( + samplesIn + i, samplesOut + (i * 4) / 11, + static_cast(state1_), tmp_mem); + } + outLen = (lengthIn * 4) / 11; + free(tmp_mem); + return 0; + case kResamplerMode11To8: + // We can only handle blocks of 160 samples + // Can be fixed, but I don't think it's needed + if ((lengthIn % 220) != 0) { + return -1; + } + if (maxLen < ((lengthIn * 8) / 11)) { + return -1; + } + tmp_mem = static_cast(malloc(104 * sizeof(int32_t))); + + for (size_t i = 0; i < lengthIn; i += 220) { + WebRtcSpl_Resample22khzTo16khz( + samplesIn + i, samplesOut + (i * 8) / 11, + static_cast(state1_), tmp_mem); + } + outLen = (lengthIn * 8) / 11; + free(tmp_mem); + return 0; + break; + } + return 0; } } // namespace webrtc diff --git a/webrtc/common_audio/resampler/sinc_resampler.cc b/webrtc/common_audio/resampler/sinc_resampler.cc index 69ac220..4fa78c5 100644 --- a/webrtc/common_audio/resampler/sinc_resampler.cc +++ b/webrtc/common_audio/resampler/sinc_resampler.cc @@ -85,16 +85,17 @@ // MSVC++ requires this to be set before any other includes to get M_PI. #define _USE_MATH_DEFINES -#include "webrtc/common_audio/resampler/sinc_resampler.h" +#include "common_audio/resampler/sinc_resampler.h" -#include #include +#include #include #include -#include "webrtc/system_wrappers/include/cpu_features_wrapper.h" -#include "webrtc/typedefs.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" // kSSE2, WebRtc_G... namespace webrtc { @@ -118,35 +119,25 @@ double SincScaleFactor(double io_ratio) { } // namespace -// If we know the minimum architecture at compile time, avoid CPU detection. -#if defined(WEBRTC_ARCH_X86_FAMILY) -#if defined(__SSE2__) -#define CONVOLVE_FUNC Convolve_SSE -void SincResampler::InitializeCPUSpecificFeatures() {} -#else -// x86 CPU detection required. Function will be set by -// InitializeCPUSpecificFeatures(). -// TODO(dalecurtis): Once Chrome moves to an SSE baseline this can be removed. -#define CONVOLVE_FUNC convolve_proc_ +const size_t SincResampler::kKernelSize; +// If we know the minimum architecture at compile time, avoid CPU detection. void SincResampler::InitializeCPUSpecificFeatures() { - convolve_proc_ = WebRtc_GetCPUInfo(kSSE2) ? Convolve_SSE : Convolve_C; -} -#endif -#elif defined(WEBRTC_HAS_NEON) -#define CONVOLVE_FUNC Convolve_NEON -void SincResampler::InitializeCPUSpecificFeatures() {} -#elif defined(WEBRTC_DETECT_NEON) -#define CONVOLVE_FUNC convolve_proc_ -void SincResampler::InitializeCPUSpecificFeatures() { - convolve_proc_ = WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON ? - Convolve_NEON : Convolve_C; -} +#if defined(WEBRTC_HAS_NEON) + convolve_proc_ = Convolve_NEON; +#elif defined(WEBRTC_ARCH_X86_FAMILY) + // Using AVX2 instead of SSE2 when AVX2 supported. + if (GetCPUInfo(kAVX2)) + convolve_proc_ = Convolve_AVX2; + else if (GetCPUInfo(kSSE2)) + convolve_proc_ = Convolve_SSE; + else + convolve_proc_ = Convolve_C; #else -// Unknown architecture. -#define CONVOLVE_FUNC Convolve_C -void SincResampler::InitializeCPUSpecificFeatures() {} + // Unknown architecture. + convolve_proc_ = Convolve_C; #endif +} SincResampler::SincResampler(double io_sample_rate_ratio, size_t request_frames, @@ -155,27 +146,23 @@ SincResampler::SincResampler(double io_sample_rate_ratio, read_cb_(read_cb), request_frames_(request_frames), input_buffer_size_(request_frames_ + kKernelSize), - // Create input buffers with a 16-byte alignment for SSE optimizations. + // Create input buffers with a 32-byte alignment for SIMD optimizations. kernel_storage_(static_cast( - AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))), + AlignedMalloc(sizeof(float) * kKernelStorageSize, 32))), kernel_pre_sinc_storage_(static_cast( - AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))), + AlignedMalloc(sizeof(float) * kKernelStorageSize, 32))), kernel_window_storage_(static_cast( - AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))), + AlignedMalloc(sizeof(float) * kKernelStorageSize, 32))), input_buffer_(static_cast( - AlignedMalloc(sizeof(float) * input_buffer_size_, 16))), -#if defined(WEBRTC_CPU_DETECTION) - convolve_proc_(NULL), -#endif + AlignedMalloc(sizeof(float) * input_buffer_size_, 32))), + convolve_proc_(nullptr), r1_(input_buffer_.get()), r2_(input_buffer_.get() + kKernelSize / 2) { -#if defined(WEBRTC_CPU_DETECTION) InitializeCPUSpecificFeatures(); - assert(convolve_proc_); -#endif - assert(request_frames_ > 0); + RTC_DCHECK(convolve_proc_); + RTC_DCHECK_GT(request_frames_, 0); Flush(); - assert(block_size_ > kKernelSize); + RTC_DCHECK_GT(block_size_, kKernelSize); memset(kernel_storage_.get(), 0, sizeof(*kernel_storage_.get()) * kKernelStorageSize); @@ -198,11 +185,11 @@ void SincResampler::UpdateRegions(bool second_load) { block_size_ = r4_ - r2_; // r1_ at the beginning of the buffer. - assert(r1_ == input_buffer_.get()); + RTC_DCHECK_EQ(r1_, input_buffer_.get()); // r1_ left of r2_, r4_ left of r3_ and size correct. - assert(r2_ - r1_ == r4_ - r3_); + RTC_DCHECK_EQ(r2_ - r1_, r4_ - r3_); // r2_ left of r3. - assert(r2_ < r3_); + RTC_DCHECK_LT(r2_, r3_); } void SincResampler::InitializeKernel() { @@ -221,23 +208,23 @@ void SincResampler::InitializeKernel() { for (size_t i = 0; i < kKernelSize; ++i) { const size_t idx = i + offset_idx * kKernelSize; - const float pre_sinc = static_cast(M_PI * - (static_cast(i) - static_cast(kKernelSize / 2) - - subsample_offset)); + const float pre_sinc = static_cast( + M_PI * (static_cast(i) - static_cast(kKernelSize / 2) - + subsample_offset)); kernel_pre_sinc_storage_[idx] = pre_sinc; // Compute Blackman window, matching the offset of the sinc(). const float x = (i - subsample_offset) / kKernelSize; const float window = static_cast(kA0 - kA1 * cos(2.0 * M_PI * x) + - kA2 * cos(4.0 * M_PI * x)); + kA2 * cos(4.0 * M_PI * x)); kernel_window_storage_[idx] = window; // Compute the sinc with offset, then window the sinc() function and store // at the correct offset. - kernel_storage_[idx] = static_cast(window * - ((pre_sinc == 0) ? - sinc_scale_factor : - (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); + kernel_storage_[idx] = static_cast( + window * ((pre_sinc == 0) + ? sinc_scale_factor + : (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); } } } @@ -259,10 +246,10 @@ void SincResampler::SetRatio(double io_sample_rate_ratio) { const float window = kernel_window_storage_[idx]; const float pre_sinc = kernel_pre_sinc_storage_[idx]; - kernel_storage_[idx] = static_cast(window * - ((pre_sinc == 0) ? - sinc_scale_factor : - (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); + kernel_storage_[idx] = static_cast( + window * ((pre_sinc == 0) + ? sinc_scale_factor + : (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); } } } @@ -289,7 +276,7 @@ void SincResampler::Resample(size_t frames, float* destination) { for (int i = static_cast( ceil((block_size_ - virtual_source_idx_) / current_io_ratio)); i > 0; --i) { - assert(virtual_source_idx_ < block_size_); + RTC_DCHECK_LT(virtual_source_idx_, block_size_); // |virtual_source_idx_| lies in between two kernel offsets so figure out // what they are. @@ -305,10 +292,10 @@ void SincResampler::Resample(size_t frames, float* destination) { const float* const k1 = kernel_ptr + offset_idx * kKernelSize; const float* const k2 = k1 + kKernelSize; - // Ensure |k1|, |k2| are 16-byte aligned for SIMD usage. Should always be - // true so long as kKernelSize is a multiple of 16. - assert(0u == (reinterpret_cast(k1) & 0x0F)); - assert(0u == (reinterpret_cast(k2) & 0x0F)); + // Ensure |k1|, |k2| are 32-byte aligned for SIMD usage. Should always be + // true so long as kKernelSize is a multiple of 32. + RTC_DCHECK_EQ(0, reinterpret_cast(k1) % 32); + RTC_DCHECK_EQ(0, reinterpret_cast(k2) % 32); // Initialize input pointer based on quantized |virtual_source_idx_|. const float* const input_ptr = r1_ + source_idx; @@ -316,8 +303,8 @@ void SincResampler::Resample(size_t frames, float* destination) { // Figure out how much to weight each kernel's "convolution". const double kernel_interpolation_factor = virtual_offset_idx - offset_idx; - *destination++ = CONVOLVE_FUNC( - input_ptr, k1, k2, kernel_interpolation_factor); + *destination++ = + convolve_proc_(input_ptr, k1, k2, kernel_interpolation_factor); // Advance the virtual index. virtual_source_idx_ += current_io_ratio; @@ -356,7 +343,8 @@ void SincResampler::Flush() { UpdateRegions(false); } -float SincResampler::Convolve_C(const float* input_ptr, const float* k1, +float SincResampler::Convolve_C(const float* input_ptr, + const float* k1, const float* k2, double kernel_interpolation_factor) { float sum1 = 0; @@ -372,7 +360,7 @@ float SincResampler::Convolve_C(const float* input_ptr, const float* k1, // Linearly interpolate the two "convolutions". return static_cast((1.0 - kernel_interpolation_factor) * sum1 + - kernel_interpolation_factor * sum2); + kernel_interpolation_factor * sum2); } } // namespace webrtc diff --git a/webrtc/common_audio/resampler/sinc_resampler.h b/webrtc/common_audio/resampler/sinc_resampler.h index 4ce8d37..a72a0c6 100644 --- a/webrtc/common_audio/resampler/sinc_resampler.h +++ b/webrtc/common_audio/resampler/sinc_resampler.h @@ -11,16 +11,17 @@ // Modified from the Chromium original here: // src/media/base/sinc_resampler.h -#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_ -#define WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_ +#ifndef COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_ +#define COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_ -#include "webrtc/base/constructormagic.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/system_wrappers/include/aligned_malloc.h" -#ifndef WEBRTC_AUDIO_PROCESSING_ONLY_BUILD -#include "webrtc/test/testsupport/gtest_prod_util.h" -#endif -#include "webrtc/typedefs.h" +#include + +#include + +#include "rtc_base/constructor_magic.h" +#include "rtc_base/gtest_prod_util.h" +#include "rtc_base/memory/aligned_malloc.h" +#include "rtc_base/system/arch.h" namespace webrtc { @@ -87,10 +88,8 @@ class SincResampler { float* get_kernel_for_testing() { return kernel_storage_.get(); } private: -#ifndef WEBRTC_AUDIO_PROCESSING_ONLY_BUILD FRIEND_TEST_ALL_PREFIXES(SincResamplerTest, Convolve); FRIEND_TEST_ALL_PREFIXES(SincResamplerTest, ConvolveBenchmark); -#endif void InitializeKernel(); void UpdateRegions(bool second_load); @@ -104,14 +103,22 @@ class SincResampler { // Compute convolution of |k1| and |k2| over |input_ptr|, resultant sums are // linearly interpolated using |kernel_interpolation_factor|. On x86 and ARM // the underlying implementation is chosen at run time. - static float Convolve_C(const float* input_ptr, const float* k1, - const float* k2, double kernel_interpolation_factor); + static float Convolve_C(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor); #if defined(WEBRTC_ARCH_X86_FAMILY) - static float Convolve_SSE(const float* input_ptr, const float* k1, + static float Convolve_SSE(const float* input_ptr, + const float* k1, const float* k2, double kernel_interpolation_factor); -#elif defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON) - static float Convolve_NEON(const float* input_ptr, const float* k1, + static float Convolve_AVX2(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor); +#elif defined(WEBRTC_HAS_NEON) + static float Convolve_NEON(const float* input_ptr, + const float* k1, const float* k2, double kernel_interpolation_factor); #endif @@ -141,22 +148,22 @@ class SincResampler { // Contains kKernelOffsetCount kernels back-to-back, each of size kKernelSize. // The kernel offsets are sub-sample shifts of a windowed sinc shifted from // 0.0 to 1.0 sample. - rtc::scoped_ptr kernel_storage_; - rtc::scoped_ptr kernel_pre_sinc_storage_; - rtc::scoped_ptr kernel_window_storage_; + std::unique_ptr kernel_storage_; + std::unique_ptr kernel_pre_sinc_storage_; + std::unique_ptr kernel_window_storage_; // Data from the source is copied into this buffer for each processing pass. - rtc::scoped_ptr input_buffer_; + std::unique_ptr input_buffer_; - // Stores the runtime selection of which Convolve function to use. - // TODO(ajm): Move to using a global static which must only be initialized - // once by the user. We're not doing this initially, because we don't have - // e.g. a LazyInstance helper in webrtc. -#if defined(WEBRTC_CPU_DETECTION) - typedef float (*ConvolveProc)(const float*, const float*, const float*, +// Stores the runtime selection of which Convolve function to use. +// TODO(ajm): Move to using a global static which must only be initialized +// once by the user. We're not doing this initially, because we don't have +// e.g. a LazyInstance helper in webrtc. + typedef float (*ConvolveProc)(const float*, + const float*, + const float*, double); ConvolveProc convolve_proc_; -#endif // Pointers to the various regions inside |input_buffer_|. See the diagram at // the top of the .cc file for more information. @@ -171,4 +178,4 @@ class SincResampler { } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_ +#endif // COMMON_AUDIO_RESAMPLER_SINC_RESAMPLER_H_ diff --git a/webrtc/common_audio/resampler/sinc_resampler_avx2.cc b/webrtc/common_audio/resampler/sinc_resampler_avx2.cc new file mode 100644 index 0000000..3eb5d4a --- /dev/null +++ b/webrtc/common_audio/resampler/sinc_resampler_avx2.cc @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020 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 +#include +#include +#include + +#include "common_audio/resampler/sinc_resampler.h" + +namespace webrtc { + +float SincResampler::Convolve_AVX2(const float* input_ptr, + const float* k1, + const float* k2, + double kernel_interpolation_factor) { + __m256 m_input; + __m256 m_sums1 = _mm256_setzero_ps(); + __m256 m_sums2 = _mm256_setzero_ps(); + + // Based on |input_ptr| alignment, we need to use loadu or load. Unrolling + // these loops has not been tested or benchmarked. + bool aligned_input = (reinterpret_cast(input_ptr) & 0x1F) == 0; + if (!aligned_input) { + for (size_t i = 0; i < kKernelSize; i += 8) { + m_input = _mm256_loadu_ps(input_ptr + i); + m_sums1 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k1 + i), m_sums1); + m_sums2 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k2 + i), m_sums2); + } + } else { + for (size_t i = 0; i < kKernelSize; i += 8) { + m_input = _mm256_load_ps(input_ptr + i); + m_sums1 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k1 + i), m_sums1); + m_sums2 = _mm256_fmadd_ps(m_input, _mm256_load_ps(k2 + i), m_sums2); + } + } + + // Linearly interpolate the two "convolutions". + __m128 m128_sums1 = _mm_add_ps(_mm256_extractf128_ps(m_sums1, 0), + _mm256_extractf128_ps(m_sums1, 1)); + __m128 m128_sums2 = _mm_add_ps(_mm256_extractf128_ps(m_sums2, 0), + _mm256_extractf128_ps(m_sums2, 1)); + m128_sums1 = _mm_mul_ps( + m128_sums1, + _mm_set_ps1(static_cast(1.0 - kernel_interpolation_factor))); + m128_sums2 = _mm_mul_ps( + m128_sums2, _mm_set_ps1(static_cast(kernel_interpolation_factor))); + m128_sums1 = _mm_add_ps(m128_sums1, m128_sums2); + + // Sum components together. + float result; + m128_sums2 = _mm_add_ps(_mm_movehl_ps(m128_sums1, m128_sums1), m128_sums1); + _mm_store_ss(&result, _mm_add_ss(m128_sums2, + _mm_shuffle_ps(m128_sums2, m128_sums2, 1))); + + return result; +} + +} // namespace webrtc diff --git a/webrtc/common_audio/resampler/sinc_resampler_neon.cc b/webrtc/common_audio/resampler/sinc_resampler_neon.cc index e909a6c..9ee918b 100644 --- a/webrtc/common_audio/resampler/sinc_resampler_neon.cc +++ b/webrtc/common_audio/resampler/sinc_resampler_neon.cc @@ -11,13 +11,14 @@ // Modified from the Chromium original: // src/media/base/sinc_resampler.cc -#include "webrtc/common_audio/resampler/sinc_resampler.h" - #include +#include "common_audio/resampler/sinc_resampler.h" + namespace webrtc { -float SincResampler::Convolve_NEON(const float* input_ptr, const float* k1, +float SincResampler::Convolve_NEON(const float* input_ptr, + const float* k1, const float* k2, double kernel_interpolation_factor) { float32x4_t m_input; @@ -25,7 +26,7 @@ float SincResampler::Convolve_NEON(const float* input_ptr, const float* k1, float32x4_t m_sums2 = vmovq_n_f32(0); const float* upper = input_ptr + kKernelSize; - for (; input_ptr < upper; ) { + for (; input_ptr < upper;) { m_input = vld1q_f32(input_ptr); input_ptr += 4; m_sums1 = vmlaq_f32(m_sums1, m_input, vld1q_f32(k1)); diff --git a/webrtc/common_audio/resampler/sinc_resampler_sse.cc b/webrtc/common_audio/resampler/sinc_resampler_sse.cc index 9e3953f..f6a24d0 100644 --- a/webrtc/common_audio/resampler/sinc_resampler_sse.cc +++ b/webrtc/common_audio/resampler/sinc_resampler_sse.cc @@ -11,13 +11,16 @@ // Modified from the Chromium original: // src/media/base/simd/sinc_resampler_sse.cc -#include "webrtc/common_audio/resampler/sinc_resampler.h" - +#include +#include #include +#include "common_audio/resampler/sinc_resampler.h" + namespace webrtc { -float SincResampler::Convolve_SSE(const float* input_ptr, const float* k1, +float SincResampler::Convolve_SSE(const float* input_ptr, + const float* k1, const float* k2, double kernel_interpolation_factor) { __m128 m_input; @@ -41,17 +44,18 @@ float SincResampler::Convolve_SSE(const float* input_ptr, const float* k1, } // Linearly interpolate the two "convolutions". - m_sums1 = _mm_mul_ps(m_sums1, _mm_set_ps1( - static_cast(1.0 - kernel_interpolation_factor))); - m_sums2 = _mm_mul_ps(m_sums2, _mm_set_ps1( - static_cast(kernel_interpolation_factor))); + m_sums1 = _mm_mul_ps( + m_sums1, + _mm_set_ps1(static_cast(1.0 - kernel_interpolation_factor))); + m_sums2 = _mm_mul_ps( + m_sums2, _mm_set_ps1(static_cast(kernel_interpolation_factor))); m_sums1 = _mm_add_ps(m_sums1, m_sums2); // Sum components together. float result; m_sums2 = _mm_add_ps(_mm_movehl_ps(m_sums1, m_sums1), m_sums1); - _mm_store_ss(&result, _mm_add_ss(m_sums2, _mm_shuffle_ps( - m_sums2, m_sums2, 1))); + _mm_store_ss(&result, + _mm_add_ss(m_sums2, _mm_shuffle_ps(m_sums2, m_sums2, 1))); return result; } diff --git a/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc b/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc index 5d21568..2afdd1b 100644 --- a/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc +++ b/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.cc @@ -11,7 +11,7 @@ // MSVC++ requires this to be set before any other includes to get M_PI. #define _USE_MATH_DEFINES -#include "webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h" +#include "common_audio/resampler/sinusoidal_linear_chirp_source.h" #include @@ -43,8 +43,7 @@ void SinusoidalLinearChirpSource::Run(size_t frames, float* destination) { } else { // Sinusoidal linear chirp. double t = (current_index_ - delay_samples_) / sample_rate_; - destination[i] = - sin(2 * M_PI * (kMinFrequency * t + (k_ / 2) * t * t)); + destination[i] = sin(2 * M_PI * (kMinFrequency * t + (k_ / 2) * t * t)); } } } @@ -52,7 +51,7 @@ void SinusoidalLinearChirpSource::Run(size_t frames, float* destination) { double SinusoidalLinearChirpSource::Frequency(size_t position) { return kMinFrequency + (position - delay_samples_) * - (max_frequency_ - kMinFrequency) / total_samples_; + (max_frequency_ - kMinFrequency) / total_samples_; } } // namespace webrtc diff --git a/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h b/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h index 1807f86..81f6a24 100644 --- a/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h +++ b/webrtc/common_audio/resampler/sinusoidal_linear_chirp_source.h @@ -11,11 +11,11 @@ // Modified from the Chromium original here: // src/media/base/sinc_resampler_unittest.cc -#ifndef WEBRTC_COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ -#define WEBRTC_COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ +#ifndef COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ +#define COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ -#include "webrtc/base/constructormagic.h" -#include "webrtc/common_audio/resampler/sinc_resampler.h" +#include "common_audio/resampler/sinc_resampler.h" +#include "rtc_base/constructor_magic.h" namespace webrtc { @@ -26,19 +26,19 @@ class SinusoidalLinearChirpSource : public SincResamplerCallback { public: // |delay_samples| can be used to insert a fractional sample delay into the // source. It will produce zeros until non-negative time is reached. - SinusoidalLinearChirpSource(int sample_rate, size_t samples, - double max_frequency, double delay_samples); + SinusoidalLinearChirpSource(int sample_rate, + size_t samples, + double max_frequency, + double delay_samples); - virtual ~SinusoidalLinearChirpSource() {} + ~SinusoidalLinearChirpSource() override {} void Run(size_t frames, float* destination) override; double Frequency(size_t position); private: - enum { - kMinFrequency = 5 - }; + enum { kMinFrequency = 5 }; int sample_rate_; size_t total_samples_; @@ -52,4 +52,4 @@ class SinusoidalLinearChirpSource : public SincResamplerCallback { } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ +#endif // COMMON_AUDIO_RESAMPLER_SINUSOIDAL_LINEAR_CHIRP_SOURCE_H_ diff --git a/webrtc/common_audio/ring_buffer.c b/webrtc/common_audio/ring_buffer.c index 60fb5df..a20ada5 100644 --- a/webrtc/common_audio/ring_buffer.c +++ b/webrtc/common_audio/ring_buffer.c @@ -11,26 +11,12 @@ // A ring buffer to hold arbitrary data. Provides no thread safety. Unless // otherwise specified, functions return 0 on success and -1 on error. -#include "webrtc/common_audio/ring_buffer.h" +#include "common_audio/ring_buffer.h" #include // size_t #include #include -enum Wrap { - SAME_WRAP, - DIFF_WRAP -}; - -struct RingBuffer { - size_t read_pos; - size_t write_pos; - size_t element_count; - size_t element_size; - enum Wrap rw_wrap; - char* data; -}; - // Get address of region(s) from which we can read data. // If the region is contiguous, |data_ptr_bytes_2| will be zero. // If non-contiguous, |data_ptr_bytes_2| will be the size in bytes of the second @@ -132,7 +118,6 @@ size_t WebRtc_ReadBuffer(RingBuffer* self, &buf_ptr_bytes_1, &buf_ptr_2, &buf_ptr_bytes_2); - if (buf_ptr_bytes_2 > 0) { // We have a wrap around when reading the buffer. Copy the buffer data to // |data| and point to it. @@ -145,7 +130,7 @@ size_t WebRtc_ReadBuffer(RingBuffer* self, } if (data_ptr) { // |buf_ptr_1| == |data| in the case of a wrap. - *data_ptr = buf_ptr_1; + *data_ptr = read_count == 0 ? NULL : buf_ptr_1; } // Update read position diff --git a/webrtc/common_audio/ring_buffer.h b/webrtc/common_audio/ring_buffer.h index 4125c48..bcc40e1 100644 --- a/webrtc/common_audio/ring_buffer.h +++ b/webrtc/common_audio/ring_buffer.h @@ -11,8 +11,10 @@ // A ring buffer to hold arbitrary data. Provides no thread safety. Unless // otherwise specified, functions return 0 on success and -1 on error. -#ifndef WEBRTC_COMMON_AUDIO_RING_BUFFER_H_ -#define WEBRTC_COMMON_AUDIO_RING_BUFFER_H_ +#ifndef COMMON_AUDIO_RING_BUFFER_H_ +#define COMMON_AUDIO_RING_BUFFER_H_ + +// TODO(alessiob): Used by AEC, AECm and AudioRingBuffer. Remove when possible. #ifdef __cplusplus extern "C" { @@ -20,21 +22,31 @@ extern "C" { #include // size_t -typedef struct RingBuffer RingBuffer; +enum Wrap { SAME_WRAP, DIFF_WRAP }; -// Creates and initializes the buffer. Returns NULL on failure. +typedef struct RingBuffer { + size_t read_pos; + size_t write_pos; + size_t element_count; + size_t element_size; + enum Wrap rw_wrap; + char* data; +} RingBuffer; + +// Creates and initializes the buffer. Returns null on failure. RingBuffer* WebRtc_CreateBuffer(size_t element_count, size_t element_size); void WebRtc_InitBuffer(RingBuffer* handle); void WebRtc_FreeBuffer(void* handle); -// Reads data from the buffer. The |data_ptr| will point to the address where -// it is located. If all |element_count| data are feasible to read without -// buffer wrap around |data_ptr| will point to the location in the buffer. -// Otherwise, the data will be copied to |data| (memory allocation done by the -// user) and |data_ptr| points to the address of |data|. |data_ptr| is only -// guaranteed to be valid until the next call to WebRtc_WriteBuffer(). +// Reads data from the buffer. Returns the number of elements that were read. +// The |data_ptr| will point to the address where the read data is located. +// If no data can be read, |data_ptr| is set to |NULL|. If all data can be read +// without buffer wrap around then |data_ptr| will point to the location in the +// buffer. Otherwise, the data will be copied to |data| (memory allocation done +// by the user) and |data_ptr| points to the address of |data|. |data_ptr| is +// only guaranteed to be valid until the next call to WebRtc_WriteBuffer(). // -// To force a copying to |data|, pass a NULL |data_ptr|. +// To force a copying to |data|, pass a null |data_ptr|. // // Returns number of elements read. size_t WebRtc_ReadBuffer(RingBuffer* handle, @@ -43,7 +55,8 @@ size_t WebRtc_ReadBuffer(RingBuffer* handle, size_t element_count); // Writes |data| to buffer and returns the number of elements written. -size_t WebRtc_WriteBuffer(RingBuffer* handle, const void* data, +size_t WebRtc_WriteBuffer(RingBuffer* handle, + const void* data, size_t element_count); // Moves the buffer read position and returns the number of elements moved. @@ -63,4 +76,4 @@ size_t WebRtc_available_write(const RingBuffer* handle); } #endif -#endif // WEBRTC_COMMON_AUDIO_RING_BUFFER_H_ +#endif // COMMON_AUDIO_RING_BUFFER_H_ diff --git a/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c b/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c index f99dd62..a3ec24f 100644 --- a/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c +++ b/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c @@ -15,7 +15,7 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" void WebRtcSpl_AutoCorrToReflCoef(const int32_t *R, int use_order, int16_t *K) { diff --git a/webrtc/common_audio/signal_processing/auto_correlation.c b/webrtc/common_audio/signal_processing/auto_correlation.c index fda4fff..1455820 100644 --- a/webrtc/common_audio/signal_processing/auto_correlation.c +++ b/webrtc/common_audio/signal_processing/auto_correlation.c @@ -8,9 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" -#include +#include "rtc_base/checks.h" size_t WebRtcSpl_AutoCorrelation(const int16_t* in_vector, size_t in_vector_length, @@ -22,7 +22,7 @@ size_t WebRtcSpl_AutoCorrelation(const int16_t* in_vector, int16_t smax = 0; int scaling = 0; - assert(order <= in_vector_length); + RTC_DCHECK_LE(order, in_vector_length); // Find the maximum absolute value of the samples. smax = WebRtcSpl_MaxAbsValueW16(in_vector, in_vector_length); diff --git a/webrtc/common_audio/signal_processing/complex_bit_reverse.c b/webrtc/common_audio/signal_processing/complex_bit_reverse.c index c8bd2dc..1c82cff 100644 --- a/webrtc/common_audio/signal_processing/complex_bit_reverse.c +++ b/webrtc/common_audio/signal_processing/complex_bit_reverse.c @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" /* Tables for data buffer indexes that are bit reversed and thus need to be * swapped. Note that, index_7[{0, 2, 4, ...}] are for the left side of the swap diff --git a/webrtc/common_audio/signal_processing/complex_bit_reverse_arm.S b/webrtc/common_audio/signal_processing/complex_bit_reverse_arm.S index 93de99f..be8e181 100644 --- a/webrtc/common_audio/signal_processing/complex_bit_reverse_arm.S +++ b/webrtc/common_audio/signal_processing/complex_bit_reverse_arm.S @@ -12,7 +12,7 @@ @ for ARMv5 platforms. @ Reference C code is in file complex_bit_reverse.c. Bit-exact. -#include "webrtc/system_wrappers/include/asm_defines.h" +#include "rtc_base/system/asm_defines.h" GLOBAL_FUNCTION WebRtcSpl_ComplexBitReverse .align 2 diff --git a/webrtc/common_audio/signal_processing/complex_bit_reverse_mips.c b/webrtc/common_audio/signal_processing/complex_bit_reverse_mips.c index 583fe4f..9007b19 100644 --- a/webrtc/common_audio/signal_processing/complex_bit_reverse_mips.c +++ b/webrtc/common_audio/signal_processing/complex_bit_reverse_mips.c @@ -9,7 +9,7 @@ */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" static int16_t coefTable_7[] = { 4, 256, 8, 128, 12, 384, 16, 64, diff --git a/webrtc/common_audio/signal_processing/complex_fft.c b/webrtc/common_audio/signal_processing/complex_fft.c index 97ebacc..ddc9a97 100644 --- a/webrtc/common_audio/signal_processing/complex_fft.c +++ b/webrtc/common_audio/signal_processing/complex_fft.c @@ -15,8 +15,9 @@ * */ -#include "webrtc/common_audio/signal_processing/complex_fft_tables.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/complex_fft_tables.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/system/arch.h" #define CFFTSFT 14 #define CFFTRND 1 @@ -134,8 +135,8 @@ int WebRtcSpl_ComplexFFT(int16_t frfi[], int stages, int mode) tr32 >>= 15 - CFFTSFT; ti32 >>= 15 - CFFTSFT; - qr32 = ((int32_t)frfi[2 * i]) << CFFTSFT; - qi32 = ((int32_t)frfi[2 * i + 1]) << CFFTSFT; + qr32 = ((int32_t)frfi[2 * i]) * (1 << CFFTSFT); + qi32 = ((int32_t)frfi[2 * i + 1]) * (1 << CFFTSFT); frfi[2 * j] = (int16_t)( (qr32 - tr32 + CFFTRND2) >> (1 + CFFTSFT)); @@ -166,7 +167,7 @@ int WebRtcSpl_ComplexIFFT(int16_t frfi[], int stages, int mode) /* The 1024-value is a constant given from the size of kSinTable1024[], * and should not be changed depending on the input parameter 'stages' */ - n = 1 << stages; + n = ((size_t)1) << stages; if (n > 1024) return -1; @@ -276,8 +277,8 @@ int WebRtcSpl_ComplexIFFT(int16_t frfi[], int stages, int mode) tr32 >>= 15 - CIFFTSFT; ti32 >>= 15 - CIFFTSFT; - qr32 = ((int32_t)frfi[2 * i]) << CIFFTSFT; - qi32 = ((int32_t)frfi[2 * i + 1]) << CIFFTSFT; + qr32 = ((int32_t)frfi[2 * i]) * (1 << CIFFTSFT); + qi32 = ((int32_t)frfi[2 * i + 1]) * (1 << CIFFTSFT); frfi[2 * j] = (int16_t)( (qr32 - tr32 + round2) >> (shift + CIFFTSFT)); diff --git a/webrtc/common_audio/signal_processing/complex_fft_mips.c b/webrtc/common_audio/signal_processing/complex_fft_mips.c index 34c4f23..27071f8 100644 --- a/webrtc/common_audio/signal_processing/complex_fft_mips.c +++ b/webrtc/common_audio/signal_processing/complex_fft_mips.c @@ -9,8 +9,8 @@ */ -#include "webrtc/common_audio/signal_processing/complex_fft_tables.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/complex_fft_tables.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" #define CFFTSFT 14 #define CFFTRND 1 diff --git a/webrtc/common_audio/signal_processing/complex_fft_tables.h b/webrtc/common_audio/signal_processing/complex_fft_tables.h index ca7b7fe..90fac07 100644 --- a/webrtc/common_audio/signal_processing/complex_fft_tables.h +++ b/webrtc/common_audio/signal_processing/complex_fft_tables.h @@ -8,141 +8,125 @@ * be found in the AUTHORS file in the root of the source tree. */ +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_COMPLEX_FFT_TABLES_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_COMPLEX_FFT_TABLES_H_ -#ifndef WEBRTC_COMMON_AUDIO_SIGNAL_PROCESSING_COMPLEX_FFT_TABLES_H_ -#define WEBRTC_COMMON_AUDIO_SIGNAL_PROCESSING_COMPLEX_FFT_TABLES_H_ - -#include "webrtc/typedefs.h" +#include static const int16_t kSinTable1024[] = { - 0, 201, 402, 603, 804, 1005, 1206, 1406, - 1607, 1808, 2009, 2209, 2410, 2610, 2811, 3011, - 3211, 3411, 3611, 3811, 4011, 4210, 4409, 4608, - 4807, 5006, 5205, 5403, 5601, 5799, 5997, 6195, - 6392, 6589, 6786, 6982, 7179, 7375, 7571, 7766, - 7961, 8156, 8351, 8545, 8739, 8932, 9126, 9319, - 9511, 9703, 9895, 10087, 10278, 10469, 10659, 10849, - 11038, 11227, 11416, 11604, 11792, 11980, 12166, 12353, - 12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827, - 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268, - 15446, 15623, 15799, 15975, 16150, 16325, 16499, 16672, - 16845, 17017, 17189, 17360, 17530, 17699, 17868, 18036, - 18204, 18371, 18537, 18702, 18867, 19031, 19194, 19357, - 19519, 19680, 19840, 20000, 20159, 20317, 20474, 20631, - 20787, 20942, 21096, 21249, 21402, 21554, 21705, 21855, - 22004, 22153, 22301, 22448, 22594, 22739, 22883, 23027, - 23169, 23311, 23452, 23592, 23731, 23869, 24006, 24143, - 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201, - 25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198, - 26318, 26437, 26556, 26673, 26789, 26905, 27019, 27132, - 27244, 27355, 27466, 27575, 27683, 27790, 27896, 28001, - 28105, 28208, 28309, 28410, 28510, 28608, 28706, 28802, - 28897, 28992, 29085, 29177, 29268, 29358, 29446, 29534, - 29621, 29706, 29790, 29873, 29955, 30036, 30116, 30195, - 30272, 30349, 30424, 30498, 30571, 30643, 30713, 30783, - 30851, 30918, 30984, 31049, 31113, 31175, 31236, 31297, - 31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735, - 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097, - 32137, 32176, 32213, 32249, 32284, 32318, 32350, 32382, - 32412, 32441, 32468, 32495, 32520, 32544, 32567, 32588, - 32609, 32628, 32646, 32662, 32678, 32692, 32705, 32717, - 32727, 32736, 32744, 32751, 32757, 32761, 32764, 32766, - 32767, 32766, 32764, 32761, 32757, 32751, 32744, 32736, - 32727, 32717, 32705, 32692, 32678, 32662, 32646, 32628, - 32609, 32588, 32567, 32544, 32520, 32495, 32468, 32441, - 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176, - 32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833, - 31785, 31735, 31684, 31633, 31580, 31525, 31470, 31413, - 31356, 31297, 31236, 31175, 31113, 31049, 30984, 30918, - 30851, 30783, 30713, 30643, 30571, 30498, 30424, 30349, - 30272, 30195, 30116, 30036, 29955, 29873, 29790, 29706, - 29621, 29534, 29446, 29358, 29268, 29177, 29085, 28992, - 28897, 28802, 28706, 28608, 28510, 28410, 28309, 28208, - 28105, 28001, 27896, 27790, 27683, 27575, 27466, 27355, - 27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437, - 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456, - 25329, 25201, 25072, 24942, 24811, 24679, 24546, 24413, - 24278, 24143, 24006, 23869, 23731, 23592, 23452, 23311, - 23169, 23027, 22883, 22739, 22594, 22448, 22301, 22153, - 22004, 21855, 21705, 21554, 21402, 21249, 21096, 20942, - 20787, 20631, 20474, 20317, 20159, 20000, 19840, 19680, - 19519, 19357, 19194, 19031, 18867, 18702, 18537, 18371, - 18204, 18036, 17868, 17699, 17530, 17360, 17189, 17017, - 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623, - 15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191, - 14009, 13827, 13645, 13462, 13278, 13094, 12909, 12724, - 12539, 12353, 12166, 11980, 11792, 11604, 11416, 11227, - 11038, 10849, 10659, 10469, 10278, 10087, 9895, 9703, - 9511, 9319, 9126, 8932, 8739, 8545, 8351, 8156, - 7961, 7766, 7571, 7375, 7179, 6982, 6786, 6589, - 6392, 6195, 5997, 5799, 5601, 5403, 5205, 5006, - 4807, 4608, 4409, 4210, 4011, 3811, 3611, 3411, - 3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808, - 1607, 1406, 1206, 1005, 804, 603, 402, 201, - 0, -201, -402, -603, -804, -1005, -1206, -1406, - -1607, -1808, -2009, -2209, -2410, -2610, -2811, -3011, - -3211, -3411, -3611, -3811, -4011, -4210, -4409, -4608, - -4807, -5006, -5205, -5403, -5601, -5799, -5997, -6195, - -6392, -6589, -6786, -6982, -7179, -7375, -7571, -7766, - -7961, -8156, -8351, -8545, -8739, -8932, -9126, -9319, - -9511, -9703, -9895, -10087, -10278, -10469, -10659, -10849, - -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353, - -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827, - -14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268, - -15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672, - -16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036, - -18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357, - -19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631, - -20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855, - -22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027, - -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143, - -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201, - -25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198, - -26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132, - -27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001, - -28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802, - -28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534, - -29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195, - -30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783, - -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297, - -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735, - -31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097, - -32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382, - -32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588, - -32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717, - -32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766, - -32767, -32766, -32764, -32761, -32757, -32751, -32744, -32736, - -32727, -32717, -32705, -32692, -32678, -32662, -32646, -32628, - -32609, -32588, -32567, -32544, -32520, -32495, -32468, -32441, - -32412, -32382, -32350, -32318, -32284, -32249, -32213, -32176, - -32137, -32097, -32056, -32014, -31970, -31926, -31880, -31833, - -31785, -31735, -31684, -31633, -31580, -31525, -31470, -31413, - -31356, -31297, -31236, -31175, -31113, -31049, -30984, -30918, - -30851, -30783, -30713, -30643, -30571, -30498, -30424, -30349, - -30272, -30195, -30116, -30036, -29955, -29873, -29790, -29706, - -29621, -29534, -29446, -29358, -29268, -29177, -29085, -28992, - -28897, -28802, -28706, -28608, -28510, -28410, -28309, -28208, - -28105, -28001, -27896, -27790, -27683, -27575, -27466, -27355, - -27244, -27132, -27019, -26905, -26789, -26673, -26556, -26437, - -26318, -26198, -26077, -25954, -25831, -25707, -25582, -25456, - -25329, -25201, -25072, -24942, -24811, -24679, -24546, -24413, - -24278, -24143, -24006, -23869, -23731, -23592, -23452, -23311, - -23169, -23027, -22883, -22739, -22594, -22448, -22301, -22153, - -22004, -21855, -21705, -21554, -21402, -21249, -21096, -20942, - -20787, -20631, -20474, -20317, -20159, -20000, -19840, -19680, - -19519, -19357, -19194, -19031, -18867, -18702, -18537, -18371, - -18204, -18036, -17868, -17699, -17530, -17360, -17189, -17017, - -16845, -16672, -16499, -16325, -16150, -15975, -15799, -15623, - -15446, -15268, -15090, -14911, -14732, -14552, -14372, -14191, - -14009, -13827, -13645, -13462, -13278, -13094, -12909, -12724, - -12539, -12353, -12166, -11980, -11792, -11604, -11416, -11227, - -11038, -10849, -10659, -10469, -10278, -10087, -9895, -9703, - -9511, -9319, -9126, -8932, -8739, -8545, -8351, -8156, - -7961, -7766, -7571, -7375, -7179, -6982, -6786, -6589, - -6392, -6195, -5997, -5799, -5601, -5403, -5205, -5006, - -4807, -4608, -4409, -4210, -4011, -3811, -3611, -3411, - -3211, -3011, -2811, -2610, -2410, -2209, -2009, -1808, - -1607, -1406, -1206, -1005, -804, -603, -402, -201 -}; + 0, 201, 402, 603, 804, 1005, 1206, 1406, 1607, + 1808, 2009, 2209, 2410, 2610, 2811, 3011, 3211, 3411, + 3611, 3811, 4011, 4210, 4409, 4608, 4807, 5006, 5205, + 5403, 5601, 5799, 5997, 6195, 6392, 6589, 6786, 6982, + 7179, 7375, 7571, 7766, 7961, 8156, 8351, 8545, 8739, + 8932, 9126, 9319, 9511, 9703, 9895, 10087, 10278, 10469, + 10659, 10849, 11038, 11227, 11416, 11604, 11792, 11980, 12166, + 12353, 12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827, + 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268, 15446, + 15623, 15799, 15975, 16150, 16325, 16499, 16672, 16845, 17017, + 17189, 17360, 17530, 17699, 17868, 18036, 18204, 18371, 18537, + 18702, 18867, 19031, 19194, 19357, 19519, 19680, 19840, 20000, + 20159, 20317, 20474, 20631, 20787, 20942, 21096, 21249, 21402, + 21554, 21705, 21855, 22004, 22153, 22301, 22448, 22594, 22739, + 22883, 23027, 23169, 23311, 23452, 23592, 23731, 23869, 24006, + 24143, 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201, + 25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198, 26318, + 26437, 26556, 26673, 26789, 26905, 27019, 27132, 27244, 27355, + 27466, 27575, 27683, 27790, 27896, 28001, 28105, 28208, 28309, + 28410, 28510, 28608, 28706, 28802, 28897, 28992, 29085, 29177, + 29268, 29358, 29446, 29534, 29621, 29706, 29790, 29873, 29955, + 30036, 30116, 30195, 30272, 30349, 30424, 30498, 30571, 30643, + 30713, 30783, 30851, 30918, 30984, 31049, 31113, 31175, 31236, + 31297, 31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735, + 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097, 32137, + 32176, 32213, 32249, 32284, 32318, 32350, 32382, 32412, 32441, + 32468, 32495, 32520, 32544, 32567, 32588, 32609, 32628, 32646, + 32662, 32678, 32692, 32705, 32717, 32727, 32736, 32744, 32751, + 32757, 32761, 32764, 32766, 32767, 32766, 32764, 32761, 32757, + 32751, 32744, 32736, 32727, 32717, 32705, 32692, 32678, 32662, + 32646, 32628, 32609, 32588, 32567, 32544, 32520, 32495, 32468, + 32441, 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176, + 32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833, 31785, + 31735, 31684, 31633, 31580, 31525, 31470, 31413, 31356, 31297, + 31236, 31175, 31113, 31049, 30984, 30918, 30851, 30783, 30713, + 30643, 30571, 30498, 30424, 30349, 30272, 30195, 30116, 30036, + 29955, 29873, 29790, 29706, 29621, 29534, 29446, 29358, 29268, + 29177, 29085, 28992, 28897, 28802, 28706, 28608, 28510, 28410, + 28309, 28208, 28105, 28001, 27896, 27790, 27683, 27575, 27466, + 27355, 27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437, + 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456, 25329, + 25201, 25072, 24942, 24811, 24679, 24546, 24413, 24278, 24143, + 24006, 23869, 23731, 23592, 23452, 23311, 23169, 23027, 22883, + 22739, 22594, 22448, 22301, 22153, 22004, 21855, 21705, 21554, + 21402, 21249, 21096, 20942, 20787, 20631, 20474, 20317, 20159, + 20000, 19840, 19680, 19519, 19357, 19194, 19031, 18867, 18702, + 18537, 18371, 18204, 18036, 17868, 17699, 17530, 17360, 17189, + 17017, 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623, + 15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191, 14009, + 13827, 13645, 13462, 13278, 13094, 12909, 12724, 12539, 12353, + 12166, 11980, 11792, 11604, 11416, 11227, 11038, 10849, 10659, + 10469, 10278, 10087, 9895, 9703, 9511, 9319, 9126, 8932, + 8739, 8545, 8351, 8156, 7961, 7766, 7571, 7375, 7179, + 6982, 6786, 6589, 6392, 6195, 5997, 5799, 5601, 5403, + 5205, 5006, 4807, 4608, 4409, 4210, 4011, 3811, 3611, + 3411, 3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808, + 1607, 1406, 1206, 1005, 804, 603, 402, 201, 0, + -201, -402, -603, -804, -1005, -1206, -1406, -1607, -1808, + -2009, -2209, -2410, -2610, -2811, -3011, -3211, -3411, -3611, + -3811, -4011, -4210, -4409, -4608, -4807, -5006, -5205, -5403, + -5601, -5799, -5997, -6195, -6392, -6589, -6786, -6982, -7179, + -7375, -7571, -7766, -7961, -8156, -8351, -8545, -8739, -8932, + -9126, -9319, -9511, -9703, -9895, -10087, -10278, -10469, -10659, + -10849, -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353, + -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827, -14009, + -14191, -14372, -14552, -14732, -14911, -15090, -15268, -15446, -15623, + -15799, -15975, -16150, -16325, -16499, -16672, -16845, -17017, -17189, + -17360, -17530, -17699, -17868, -18036, -18204, -18371, -18537, -18702, + -18867, -19031, -19194, -19357, -19519, -19680, -19840, -20000, -20159, + -20317, -20474, -20631, -20787, -20942, -21096, -21249, -21402, -21554, + -21705, -21855, -22004, -22153, -22301, -22448, -22594, -22739, -22883, + -23027, -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143, + -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201, -25329, + -25456, -25582, -25707, -25831, -25954, -26077, -26198, -26318, -26437, + -26556, -26673, -26789, -26905, -27019, -27132, -27244, -27355, -27466, + -27575, -27683, -27790, -27896, -28001, -28105, -28208, -28309, -28410, + -28510, -28608, -28706, -28802, -28897, -28992, -29085, -29177, -29268, + -29358, -29446, -29534, -29621, -29706, -29790, -29873, -29955, -30036, + -30116, -30195, -30272, -30349, -30424, -30498, -30571, -30643, -30713, + -30783, -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297, + -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735, -31785, + -31833, -31880, -31926, -31970, -32014, -32056, -32097, -32137, -32176, + -32213, -32249, -32284, -32318, -32350, -32382, -32412, -32441, -32468, + -32495, -32520, -32544, -32567, -32588, -32609, -32628, -32646, -32662, + -32678, -32692, -32705, -32717, -32727, -32736, -32744, -32751, -32757, + -32761, -32764, -32766, -32767, -32766, -32764, -32761, -32757, -32751, + -32744, -32736, -32727, -32717, -32705, -32692, -32678, -32662, -32646, + -32628, -32609, -32588, -32567, -32544, -32520, -32495, -32468, -32441, + -32412, -32382, -32350, -32318, -32284, -32249, -32213, -32176, -32137, + -32097, -32056, -32014, -31970, -31926, -31880, -31833, -31785, -31735, + -31684, -31633, -31580, -31525, -31470, -31413, -31356, -31297, -31236, + -31175, -31113, -31049, -30984, -30918, -30851, -30783, -30713, -30643, + -30571, -30498, -30424, -30349, -30272, -30195, -30116, -30036, -29955, + -29873, -29790, -29706, -29621, -29534, -29446, -29358, -29268, -29177, + -29085, -28992, -28897, -28802, -28706, -28608, -28510, -28410, -28309, + -28208, -28105, -28001, -27896, -27790, -27683, -27575, -27466, -27355, + -27244, -27132, -27019, -26905, -26789, -26673, -26556, -26437, -26318, + -26198, -26077, -25954, -25831, -25707, -25582, -25456, -25329, -25201, + -25072, -24942, -24811, -24679, -24546, -24413, -24278, -24143, -24006, + -23869, -23731, -23592, -23452, -23311, -23169, -23027, -22883, -22739, + -22594, -22448, -22301, -22153, -22004, -21855, -21705, -21554, -21402, + -21249, -21096, -20942, -20787, -20631, -20474, -20317, -20159, -20000, + -19840, -19680, -19519, -19357, -19194, -19031, -18867, -18702, -18537, + -18371, -18204, -18036, -17868, -17699, -17530, -17360, -17189, -17017, + -16845, -16672, -16499, -16325, -16150, -15975, -15799, -15623, -15446, + -15268, -15090, -14911, -14732, -14552, -14372, -14191, -14009, -13827, + -13645, -13462, -13278, -13094, -12909, -12724, -12539, -12353, -12166, + -11980, -11792, -11604, -11416, -11227, -11038, -10849, -10659, -10469, + -10278, -10087, -9895, -9703, -9511, -9319, -9126, -8932, -8739, + -8545, -8351, -8156, -7961, -7766, -7571, -7375, -7179, -6982, + -6786, -6589, -6392, -6195, -5997, -5799, -5601, -5403, -5205, + -5006, -4807, -4608, -4409, -4210, -4011, -3811, -3611, -3411, + -3211, -3011, -2811, -2610, -2410, -2209, -2009, -1808, -1607, + -1406, -1206, -1005, -804, -603, -402, -201}; -#endif // WEBRTC_COMMON_AUDIO_SIGNAL_PROCESSING_COMPLEX_FFT_TABLES_H_ +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_COMPLEX_FFT_TABLES_H_ diff --git a/webrtc/common_audio/signal_processing/copy_set_operations.c b/webrtc/common_audio/signal_processing/copy_set_operations.c index 9d7cf47..ae709d4 100644 --- a/webrtc/common_audio/signal_processing/copy_set_operations.c +++ b/webrtc/common_audio/signal_processing/copy_set_operations.c @@ -23,7 +23,7 @@ */ #include -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" void WebRtcSpl_MemSetW16(int16_t *ptr, int16_t set_value, size_t length) diff --git a/webrtc/common_audio/signal_processing/cross_correlation.c b/webrtc/common_audio/signal_processing/cross_correlation.c index d7c9f2b..c6267c9 100644 --- a/webrtc/common_audio/signal_processing/cross_correlation.c +++ b/webrtc/common_audio/signal_processing/cross_correlation.c @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" /* C version of WebRtcSpl_CrossCorrelation() for generic platforms. */ void WebRtcSpl_CrossCorrelationC(int32_t* cross_correlation, diff --git a/webrtc/common_audio/signal_processing/cross_correlation_mips.c b/webrtc/common_audio/signal_processing/cross_correlation_mips.c index b236402..c395101 100644 --- a/webrtc/common_audio/signal_processing/cross_correlation_mips.c +++ b/webrtc/common_audio/signal_processing/cross_correlation_mips.c @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" void WebRtcSpl_CrossCorrelation_mips(int32_t* cross_correlation, const int16_t* seq1, diff --git a/webrtc/common_audio/signal_processing/cross_correlation_neon.c b/webrtc/common_audio/signal_processing/cross_correlation_neon.c index 918b671..f2afbdf 100644 --- a/webrtc/common_audio/signal_processing/cross_correlation_neon.c +++ b/webrtc/common_audio/signal_processing/cross_correlation_neon.c @@ -8,7 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/system/arch.h" #include diff --git a/webrtc/common_audio/signal_processing/division_operations.c b/webrtc/common_audio/signal_processing/division_operations.c index eaa06a1..c6195e7 100644 --- a/webrtc/common_audio/signal_processing/division_operations.c +++ b/webrtc/common_audio/signal_processing/division_operations.c @@ -21,7 +21,8 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/sanitizer.h" uint32_t WebRtcSpl_DivU32U16(uint32_t num, uint16_t den) { @@ -97,7 +98,8 @@ int32_t WebRtcSpl_DivResultInQ31(int32_t num, int32_t den) return div; } -int32_t WebRtcSpl_DivW32HiLow(int32_t num, int16_t den_hi, int16_t den_low) +int32_t RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/5486 +WebRtcSpl_DivW32HiLow(int32_t num, int16_t den_hi, int16_t den_low) { int16_t approx, tmp_hi, tmp_low, num_hi, num_low; int32_t tmpW32; @@ -110,6 +112,7 @@ int32_t WebRtcSpl_DivW32HiLow(int32_t num, int16_t den_hi, int16_t den_low) // tmpW32 = den * approx tmpW32 = (int32_t)0x7fffffffL - tmpW32; // result in Q30 (tmpW32 = 2.0-(den*approx)) + // UBSan: 2147483647 - -2 cannot be represented in type 'int' // Store tmpW32 in hi and low format tmp_hi = (int16_t)(tmpW32 >> 16); diff --git a/webrtc/common_audio/signal_processing/dot_product_with_scale.c b/webrtc/common_audio/signal_processing/dot_product_with_scale.cc similarity index 85% rename from webrtc/common_audio/signal_processing/dot_product_with_scale.c rename to webrtc/common_audio/signal_processing/dot_product_with_scale.cc index 1302d62..00799da 100644 --- a/webrtc/common_audio/signal_processing/dot_product_with_scale.c +++ b/webrtc/common_audio/signal_processing/dot_product_with_scale.cc @@ -8,13 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/dot_product_with_scale.h" + +#include "rtc_base/numerics/safe_conversions.h" int32_t WebRtcSpl_DotProductWithScale(const int16_t* vector1, const int16_t* vector2, size_t length, int scaling) { - int32_t sum = 0; + int64_t sum = 0; size_t i = 0; /* Unroll the loop to improve performance. */ @@ -28,5 +30,5 @@ int32_t WebRtcSpl_DotProductWithScale(const int16_t* vector1, sum += (vector1[i] * vector2[i]) >> scaling; } - return sum; + return rtc::saturated_cast(sum); } diff --git a/webrtc/common_audio/signal_processing/dot_product_with_scale.h b/webrtc/common_audio/signal_processing/dot_product_with_scale.h new file mode 100644 index 0000000..bb892d4 --- /dev/null +++ b/webrtc/common_audio/signal_processing/dot_product_with_scale.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_DOT_PRODUCT_WITH_SCALE_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_DOT_PRODUCT_WITH_SCALE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Calculates the dot product between two (int16_t) vectors. +// +// Input: +// - vector1 : Vector 1 +// - vector2 : Vector 2 +// - vector_length : Number of samples used in the dot product +// - scaling : The number of right bit shifts to apply on each term +// during calculation to avoid overflow, i.e., the +// output will be in Q(-|scaling|) +// +// Return value : The dot product in Q(-scaling) +int32_t WebRtcSpl_DotProductWithScale(const int16_t* vector1, + const int16_t* vector2, + size_t length, + int scaling); + +#ifdef __cplusplus +} +#endif // __cplusplus +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_DOT_PRODUCT_WITH_SCALE_H_ diff --git a/webrtc/common_audio/signal_processing/downsample_fast.c b/webrtc/common_audio/signal_processing/downsample_fast.c index 726a888..80fdc58 100644 --- a/webrtc/common_audio/signal_processing/downsample_fast.c +++ b/webrtc/common_audio/signal_processing/downsample_fast.c @@ -8,7 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" + +#include "rtc_base/checks.h" +#include "rtc_base/sanitizer.h" // TODO(Bjornv): Change the function parameter order to WebRTC code style. // C version of WebRtcSpl_DownsampleFast() for generic platforms. @@ -20,6 +23,7 @@ int WebRtcSpl_DownsampleFastC(const int16_t* data_in, size_t coefficients_length, int factor, size_t delay) { + int16_t* const original_data_out = data_out; size_t i = 0; size_t j = 0; int32_t out_s32 = 0; @@ -31,11 +35,20 @@ int WebRtcSpl_DownsampleFastC(const int16_t* data_in, return -1; } + rtc_MsanCheckInitialized(coefficients, sizeof(coefficients[0]), + coefficients_length); + for (i = delay; i < endpos; i += factor) { out_s32 = 2048; // Round value, 0.5 in Q12. for (j = 0; j < coefficients_length; j++) { - out_s32 += coefficients[j] * data_in[i - j]; // Q12. + // Negative overflow is permitted here, because this is + // auto-regressive filters, and the state for each batch run is + // stored in the "negative" positions of the output vector. + rtc_MsanCheckInitialized(&data_in[(ptrdiff_t) i - (ptrdiff_t) j], + sizeof(data_in[0]), 1); + // out_s32 is in Q12 domain. + out_s32 += coefficients[j] * data_in[(ptrdiff_t) i - (ptrdiff_t) j]; } out_s32 >>= 12; // Q0. @@ -44,5 +57,9 @@ int WebRtcSpl_DownsampleFastC(const int16_t* data_in, *data_out++ = WebRtcSpl_SatW32ToW16(out_s32); } + RTC_DCHECK_EQ(original_data_out + data_out_length, data_out); + rtc_MsanCheckInitialized(original_data_out, sizeof(original_data_out[0]), + data_out_length); + return 0; } diff --git a/webrtc/common_audio/signal_processing/downsample_fast_mips.c b/webrtc/common_audio/signal_processing/downsample_fast_mips.c index ac39401..0f3f3a0 100644 --- a/webrtc/common_audio/signal_processing/downsample_fast_mips.c +++ b/webrtc/common_audio/signal_processing/downsample_fast_mips.c @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" // Version of WebRtcSpl_DownsampleFast() for MIPS platforms. int WebRtcSpl_DownsampleFast_mips(const int16_t* data_in, diff --git a/webrtc/common_audio/signal_processing/downsample_fast_neon.c b/webrtc/common_audio/signal_processing/downsample_fast_neon.c index 58732da..36fc0c8 100644 --- a/webrtc/common_audio/signal_processing/downsample_fast_neon.c +++ b/webrtc/common_audio/signal_processing/downsample_fast_neon.c @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" #include diff --git a/webrtc/common_audio/signal_processing/energy.c b/webrtc/common_audio/signal_processing/energy.c index e83f1a6..5cce6b8 100644 --- a/webrtc/common_audio/signal_processing/energy.c +++ b/webrtc/common_audio/signal_processing/energy.c @@ -15,7 +15,7 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" int32_t WebRtcSpl_Energy(int16_t* vector, size_t vector_length, diff --git a/webrtc/common_audio/signal_processing/filter_ar.c b/webrtc/common_audio/signal_processing/filter_ar.c index dfbc4c2..b1f666d 100644 --- a/webrtc/common_audio/signal_processing/filter_ar.c +++ b/webrtc/common_audio/signal_processing/filter_ar.c @@ -15,7 +15,9 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" + +#include "rtc_base/checks.h" size_t WebRtcSpl_FilterAR(const int16_t* a, size_t a_length, @@ -29,7 +31,7 @@ size_t WebRtcSpl_FilterAR(const int16_t* a, int16_t* filtered_low, size_t filtered_low_length) { - int32_t o; + int64_t o; int32_t oLOW; size_t i, j, stop; const int16_t* x_ptr = &x[0]; @@ -40,19 +42,23 @@ size_t WebRtcSpl_FilterAR(const int16_t* a, { // Calculate filtered[i] and filtered_low[i] const int16_t* a_ptr = &a[1]; - int16_t* filtered_ptr = &filtered[i - 1]; - int16_t* filtered_low_ptr = &filtered_low[i - 1]; + // The index can become negative, but the arrays will never be indexed + // with it when negative. Nevertheless, the index cannot be a size_t + // because of this. + int filtered_ix = (int)i - 1; int16_t* state_ptr = &state[state_length - 1]; int16_t* state_low_ptr = &state_low[state_length - 1]; - o = (int32_t)(*x_ptr++) << 12; + o = (int32_t)(*x_ptr++) * (1 << 12); oLOW = (int32_t)0; stop = (i < a_length) ? i + 1 : a_length; for (j = 1; j < stop; j++) { - o -= *a_ptr * *filtered_ptr--; - oLOW -= *a_ptr++ * *filtered_low_ptr--; + RTC_DCHECK_GE(filtered_ix, 0); + o -= *a_ptr * filtered[filtered_ix]; + oLOW -= *a_ptr++ * filtered_low[filtered_ix]; + --filtered_ix; } for (j = i + 1; j < a_length; j++) { @@ -62,8 +68,8 @@ size_t WebRtcSpl_FilterAR(const int16_t* a, o += (oLOW >> 12); *filteredFINAL_ptr = (int16_t)((o + (int32_t)2048) >> 12); - *filteredFINAL_LOW_ptr++ = (int16_t)(o - ((int32_t)(*filteredFINAL_ptr++) - << 12)); + *filteredFINAL_LOW_ptr++ = + (int16_t)(o - ((int32_t)(*filteredFINAL_ptr++) * (1 << 12))); } // Save the filter state @@ -81,7 +87,7 @@ size_t WebRtcSpl_FilterAR(const int16_t* a, for (i = 0; i < x_length; i++) { state[state_length - x_length + i] = filtered[i]; - state[state_length - x_length + i] = filtered_low[i]; + state_low[state_length - x_length + i] = filtered_low[i]; } } diff --git a/webrtc/common_audio/signal_processing/filter_ar_fast_q12.c b/webrtc/common_audio/signal_processing/filter_ar_fast_q12.c index 70001a0..8b8bdb1 100644 --- a/webrtc/common_audio/signal_processing/filter_ar_fast_q12.c +++ b/webrtc/common_audio/signal_processing/filter_ar_fast_q12.c @@ -7,9 +7,11 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#include -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "stddef.h" + +#include "rtc_base/checks.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" // TODO(bjornv): Change the return type to report errors. @@ -21,15 +23,18 @@ void WebRtcSpl_FilterARFastQ12(const int16_t* data_in, size_t i = 0; size_t j = 0; - assert(data_length > 0); - assert(coefficients_length > 1); + RTC_DCHECK_GT(data_length, 0); + RTC_DCHECK_GT(coefficients_length, 1); for (i = 0; i < data_length; i++) { - int32_t output = 0; - int32_t sum = 0; + int64_t output = 0; + int64_t sum = 0; for (j = coefficients_length - 1; j > 0; j--) { - sum += coefficients[j] * data_out[i - j]; + // Negative overflow is permitted here, because this is + // auto-regressive filters, and the state for each batch run is + // stored in the "negative" positions of the output vector. + sum += coefficients[j] * data_out[(ptrdiff_t) i - (ptrdiff_t) j]; } output = coefficients[0] * data_in[i]; diff --git a/webrtc/common_audio/signal_processing/filter_ar_fast_q12_armv7.S b/webrtc/common_audio/signal_processing/filter_ar_fast_q12_armv7.S index f163627..60319d2 100644 --- a/webrtc/common_audio/signal_processing/filter_ar_fast_q12_armv7.S +++ b/webrtc/common_audio/signal_processing/filter_ar_fast_q12_armv7.S @@ -35,7 +35,7 @@ @ r11: Scratch @ r12: &coefficients[j] -#include "webrtc/system_wrappers/include/asm_defines.h" +#include "rtc_base/system/asm_defines.h" GLOBAL_FUNCTION WebRtcSpl_FilterARFastQ12 .align 2 diff --git a/webrtc/common_audio/signal_processing/filter_ar_fast_q12_mips.c b/webrtc/common_audio/signal_processing/filter_ar_fast_q12_mips.c index 0384701..b9ad30f 100644 --- a/webrtc/common_audio/signal_processing/filter_ar_fast_q12_mips.c +++ b/webrtc/common_audio/signal_processing/filter_ar_fast_q12_mips.c @@ -7,9 +7,9 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#include -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" void WebRtcSpl_FilterARFastQ12(const int16_t* data_in, int16_t* data_out, @@ -25,8 +25,8 @@ void WebRtcSpl_FilterARFastQ12(const int16_t* data_in, int min16 = 0xFFFF8000; #endif // #if !defined(MIPS_DSP_R1_LE) - assert(data_length > 0); - assert(coefficients_length > 1); + RTC_DCHECK_GT(data_length, 0); + RTC_DCHECK_GT(coefficients_length, 1); __asm __volatile ( ".set push \n\t" diff --git a/webrtc/common_audio/signal_processing/filter_ma_fast_q12.c b/webrtc/common_audio/signal_processing/filter_ma_fast_q12.c index f4d9a3d..329d47e 100644 --- a/webrtc/common_audio/signal_processing/filter_ma_fast_q12.c +++ b/webrtc/common_audio/signal_processing/filter_ma_fast_q12.c @@ -15,7 +15,9 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" + +#include "rtc_base/sanitizer.h" void WebRtcSpl_FilterMAFastQ12(const int16_t* in_ptr, int16_t* out_ptr, @@ -24,13 +26,21 @@ void WebRtcSpl_FilterMAFastQ12(const int16_t* in_ptr, size_t length) { size_t i, j; + + rtc_MsanCheckInitialized(B, sizeof(B[0]), B_length); + rtc_MsanCheckInitialized(in_ptr - B_length + 1, sizeof(in_ptr[0]), + B_length + length - 1); + for (i = 0; i < length; i++) { int32_t o = 0; for (j = 0; j < B_length; j++) { - o += B[j] * in_ptr[i - j]; + // Negative overflow is permitted here, because this is + // auto-regressive filters, and the state for each batch run is + // stored in the "negative" positions of the output vector. + o += B[j] * in_ptr[(ptrdiff_t) i - (ptrdiff_t) j]; } // If output is higher than 32768, saturate it. Same with negative side diff --git a/webrtc/common_audio/signal_processing/get_hanning_window.c b/webrtc/common_audio/signal_processing/get_hanning_window.c index d83ac21..8f29da8 100644 --- a/webrtc/common_audio/signal_processing/get_hanning_window.c +++ b/webrtc/common_audio/signal_processing/get_hanning_window.c @@ -15,7 +15,7 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" // Hanning table with 256 entries static const int16_t kHanningTable[] = { diff --git a/webrtc/common_audio/signal_processing/get_scaling_square.c b/webrtc/common_audio/signal_processing/get_scaling_square.c index 82e3c8b..4eb1269 100644 --- a/webrtc/common_audio/signal_processing/get_scaling_square.c +++ b/webrtc/common_audio/signal_processing/get_scaling_square.c @@ -15,7 +15,7 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" int16_t WebRtcSpl_GetScalingSquare(int16_t* in_vector, size_t in_vector_length, diff --git a/webrtc/common_audio/signal_processing/ilbc_specific_functions.c b/webrtc/common_audio/signal_processing/ilbc_specific_functions.c index 301a922..cbdd3dc 100644 --- a/webrtc/common_audio/signal_processing/ilbc_specific_functions.c +++ b/webrtc/common_audio/signal_processing/ilbc_specific_functions.c @@ -19,7 +19,7 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" void WebRtcSpl_ReverseOrderMultArrayElements(int16_t *out, const int16_t *in, const int16_t *win, @@ -64,7 +64,7 @@ void WebRtcSpl_AddVectorsAndShift(int16_t *out, const int16_t *in1, } } -void WebRtcSpl_AddAffineVectorToVector(int16_t *out, int16_t *in, +void WebRtcSpl_AddAffineVectorToVector(int16_t *out, const int16_t *in, int16_t gain, int32_t add_constant, int16_t right_shifts, size_t vector_length) @@ -77,7 +77,7 @@ void WebRtcSpl_AddAffineVectorToVector(int16_t *out, int16_t *in, } } -void WebRtcSpl_AffineTransformVector(int16_t *out, int16_t *in, +void WebRtcSpl_AffineTransformVector(int16_t *out, const int16_t *in, int16_t gain, int32_t add_constant, int16_t right_shifts, size_t vector_length) { diff --git a/webrtc/common_audio/signal_processing/include/real_fft.h b/webrtc/common_audio/signal_processing/include/real_fft.h index e7942f0..8445066 100644 --- a/webrtc/common_audio/signal_processing/include/real_fft.h +++ b/webrtc/common_audio/signal_processing/include/real_fft.h @@ -8,15 +8,14 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_REAL_FFT_H_ -#define WEBRTC_COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_REAL_FFT_H_ +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_REAL_FFT_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_REAL_FFT_H_ -#include "webrtc/typedefs.h" +#include // For ComplexFFT(), the maximum fft order is 10; -// for OpenMax FFT in ARM, it is 12; // WebRTC APM uses orders of only 7 and 8. -enum {kMaxFFTOrder = 10}; +enum { kMaxFFTOrder = 10 }; struct RealFFT; @@ -57,7 +56,7 @@ void WebRtcSpl_FreeRealFFT(struct RealFFT* self); // // Return Value: // 0 - FFT calculation is successful. -// -1 - Error with bad arguments (NULL pointers). +// -1 - Error with bad arguments (null pointers). int WebRtcSpl_RealForwardFFT(struct RealFFT* self, const int16_t* real_data_in, int16_t* complex_data_out); @@ -85,7 +84,7 @@ int WebRtcSpl_RealForwardFFT(struct RealFFT* self, // 0 or a positive number - a value that the elements in the |real_data_out| // should be shifted left with in order to get // correct physical values. -// -1 - Error with bad arguments (NULL pointers). +// -1 - Error with bad arguments (null pointers). int WebRtcSpl_RealInverseFFT(struct RealFFT* self, const int16_t* complex_data_in, int16_t* real_data_out); @@ -94,4 +93,4 @@ int WebRtcSpl_RealInverseFFT(struct RealFFT* self, } #endif -#endif // WEBRTC_COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_REAL_FFT_H_ +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_REAL_FFT_H_ diff --git a/webrtc/common_audio/signal_processing/include/signal_processing_library.h b/webrtc/common_audio/signal_processing/include/signal_processing_library.h index 2e96883..4ad92c4 100644 --- a/webrtc/common_audio/signal_processing/include/signal_processing_library.h +++ b/webrtc/common_audio/signal_processing/include/signal_processing_library.h @@ -8,91 +8,84 @@ * be found in the AUTHORS file in the root of the source tree. */ - /* - * This header file includes all of the fix point signal processing library (SPL) function - * descriptions and declarations. - * For specific function calls, see bottom of file. + * This header file includes all of the fix point signal processing library + * (SPL) function descriptions and declarations. For specific function calls, + * see bottom of file. */ -#ifndef WEBRTC_SPL_SIGNAL_PROCESSING_LIBRARY_H_ -#define WEBRTC_SPL_SIGNAL_PROCESSING_LIBRARY_H_ +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SIGNAL_PROCESSING_LIBRARY_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SIGNAL_PROCESSING_LIBRARY_H_ #include -#include "webrtc/typedefs.h" + +#include "common_audio/signal_processing/dot_product_with_scale.h" // Macros specific for the fixed point implementation -#define WEBRTC_SPL_WORD16_MAX 32767 -#define WEBRTC_SPL_WORD16_MIN -32768 -#define WEBRTC_SPL_WORD32_MAX (int32_t)0x7fffffff -#define WEBRTC_SPL_WORD32_MIN (int32_t)0x80000000 -#define WEBRTC_SPL_MAX_LPC_ORDER 14 -#define WEBRTC_SPL_MIN(A, B) (A < B ? A : B) // Get min value -#define WEBRTC_SPL_MAX(A, B) (A > B ? A : B) // Get max value +#define WEBRTC_SPL_WORD16_MAX 32767 +#define WEBRTC_SPL_WORD16_MIN -32768 +#define WEBRTC_SPL_WORD32_MAX (int32_t)0x7fffffff +#define WEBRTC_SPL_WORD32_MIN (int32_t)0x80000000 +#define WEBRTC_SPL_MAX_LPC_ORDER 14 +#define WEBRTC_SPL_MIN(A, B) (A < B ? A : B) // Get min value +#define WEBRTC_SPL_MAX(A, B) (A > B ? A : B) // Get max value // TODO(kma/bjorn): For the next two macros, investigate how to correct the code // for inputs of a = WEBRTC_SPL_WORD16_MIN or WEBRTC_SPL_WORD32_MIN. -#define WEBRTC_SPL_ABS_W16(a) \ - (((int16_t)a >= 0) ? ((int16_t)a) : -((int16_t)a)) -#define WEBRTC_SPL_ABS_W32(a) \ - (((int32_t)a >= 0) ? ((int32_t)a) : -((int32_t)a)) +#define WEBRTC_SPL_ABS_W16(a) (((int16_t)a >= 0) ? ((int16_t)a) : -((int16_t)a)) +#define WEBRTC_SPL_ABS_W32(a) (((int32_t)a >= 0) ? ((int32_t)a) : -((int32_t)a)) -#define WEBRTC_SPL_MUL(a, b) \ - ((int32_t) ((int32_t)(a) * (int32_t)(b))) -#define WEBRTC_SPL_UMUL(a, b) \ - ((uint32_t) ((uint32_t)(a) * (uint32_t)(b))) -#define WEBRTC_SPL_UMUL_32_16(a, b) \ - ((uint32_t) ((uint32_t)(a) * (uint16_t)(b))) -#define WEBRTC_SPL_MUL_16_U16(a, b) \ - ((int32_t)(int16_t)(a) * (uint16_t)(b)) +#define WEBRTC_SPL_MUL(a, b) ((int32_t)((int32_t)(a) * (int32_t)(b))) +#define WEBRTC_SPL_UMUL(a, b) ((uint32_t)((uint32_t)(a) * (uint32_t)(b))) +#define WEBRTC_SPL_UMUL_32_16(a, b) ((uint32_t)((uint32_t)(a) * (uint16_t)(b))) +#define WEBRTC_SPL_MUL_16_U16(a, b) ((int32_t)(int16_t)(a) * (uint16_t)(b)) +// clang-format off +// clang-format would choose some identation +// leading to presubmit error (cpplint.py) #ifndef WEBRTC_ARCH_ARM_V7 // For ARMv7 platforms, these are inline functions in spl_inl_armv7.h #ifndef MIPS32_LE // For MIPS platforms, these are inline functions in spl_inl_mips.h -#define WEBRTC_SPL_MUL_16_16(a, b) \ - ((int32_t) (((int16_t)(a)) * ((int16_t)(b)))) +#define WEBRTC_SPL_MUL_16_16(a, b) ((int32_t)(((int16_t)(a)) * ((int16_t)(b)))) #define WEBRTC_SPL_MUL_16_32_RSFT16(a, b) \ - (WEBRTC_SPL_MUL_16_16(a, b >> 16) \ - + ((WEBRTC_SPL_MUL_16_16(a, (b & 0xffff) >> 1) + 0x4000) >> 15)) + (WEBRTC_SPL_MUL_16_16(a, b >> 16) + \ + ((WEBRTC_SPL_MUL_16_16(a, (b & 0xffff) >> 1) + 0x4000) >> 15)) #endif #endif -#define WEBRTC_SPL_MUL_16_32_RSFT11(a, b) \ - ((WEBRTC_SPL_MUL_16_16(a, (b) >> 16) << 5) \ - + (((WEBRTC_SPL_MUL_16_U16(a, (uint16_t)(b)) >> 1) + 0x0200) >> 10)) -#define WEBRTC_SPL_MUL_16_32_RSFT14(a, b) \ - ((WEBRTC_SPL_MUL_16_16(a, (b) >> 16) << 2) \ - + (((WEBRTC_SPL_MUL_16_U16(a, (uint16_t)(b)) >> 1) + 0x1000) >> 13)) -#define WEBRTC_SPL_MUL_16_32_RSFT15(a, b) \ - ((WEBRTC_SPL_MUL_16_16(a, (b) >> 16) << 1) \ - + (((WEBRTC_SPL_MUL_16_U16(a, (uint16_t)(b)) >> 1) + 0x2000) >> 14)) +#define WEBRTC_SPL_MUL_16_32_RSFT11(a, b) \ + (WEBRTC_SPL_MUL_16_16(a, (b) >> 16) * (1 << 5) + \ + (((WEBRTC_SPL_MUL_16_U16(a, (uint16_t)(b)) >> 1) + 0x0200) >> 10)) +#define WEBRTC_SPL_MUL_16_32_RSFT14(a, b) \ + (WEBRTC_SPL_MUL_16_16(a, (b) >> 16) * (1 << 2) + \ + (((WEBRTC_SPL_MUL_16_U16(a, (uint16_t)(b)) >> 1) + 0x1000) >> 13)) +#define WEBRTC_SPL_MUL_16_32_RSFT15(a, b) \ + ((WEBRTC_SPL_MUL_16_16(a, (b) >> 16) * (1 << 1)) + \ + (((WEBRTC_SPL_MUL_16_U16(a, (uint16_t)(b)) >> 1) + 0x2000) >> 14)) +// clang-format on -#define WEBRTC_SPL_MUL_16_16_RSFT(a, b, c) \ - (WEBRTC_SPL_MUL_16_16(a, b) >> (c)) +#define WEBRTC_SPL_MUL_16_16_RSFT(a, b, c) (WEBRTC_SPL_MUL_16_16(a, b) >> (c)) #define WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(a, b, c) \ - ((WEBRTC_SPL_MUL_16_16(a, b) + ((int32_t) \ - (((int32_t)1) << ((c) - 1)))) >> (c)) + ((WEBRTC_SPL_MUL_16_16(a, b) + ((int32_t)(((int32_t)1) << ((c)-1)))) >> (c)) // C + the 32 most significant bits of A * B #define WEBRTC_SPL_SCALEDIFF32(A, B, C) \ - (C + (B >> 16) * A + (((uint32_t)(0x0000FFFF & B) * A) >> 16)) + (C + (B >> 16) * A + (((uint32_t)(B & 0x0000FFFF) * A) >> 16)) -#define WEBRTC_SPL_SAT(a, b, c) (b > a ? a : b < c ? c : b) +#define WEBRTC_SPL_SAT(a, b, c) (b > a ? a : b < c ? c : b) // Shifting with negative numbers allowed // Positive means left shift -#define WEBRTC_SPL_SHIFT_W32(x, c) \ - (((c) >= 0) ? ((x) << (c)) : ((x) >> (-(c)))) +#define WEBRTC_SPL_SHIFT_W32(x, c) ((c) >= 0 ? (x) * (1 << (c)) : (x) >> -(c)) // Shifting with negative numbers not allowed // We cannot do casting here due to signed/unsigned problem -#define WEBRTC_SPL_LSHIFT_W32(x, c) ((x) << (c)) +#define WEBRTC_SPL_LSHIFT_W32(x, c) ((x) << (c)) -#define WEBRTC_SPL_RSHIFT_U32(x, c) ((uint32_t)(x) >> (c)) +#define WEBRTC_SPL_RSHIFT_U32(x, c) ((uint32_t)(x) >> (c)) -#define WEBRTC_SPL_RAND(a) \ - ((int16_t)((((int16_t)a * 18816) >> 7) & 0x00007fff)) +#define WEBRTC_SPL_RAND(a) ((int16_t)((((int16_t)a * 18816) >> 7) & 0x00007fff)) #ifdef __cplusplus extern "C" { @@ -102,17 +95,10 @@ extern "C" { memcpy(v1, v2, (length) * sizeof(int16_t)) // inline functions: -#include "webrtc/common_audio/signal_processing/include/spl_inl.h" +#include "common_audio/signal_processing/include/spl_inl.h" -// Initialize SPL. Currently it contains only function pointer initialization. -// If the underlying platform is known to be ARM-Neon (WEBRTC_HAS_NEON defined), -// the pointers will be assigned to code optimized for Neon; otherwise -// if run-time Neon detection (WEBRTC_DETECT_NEON) is enabled, the pointers -// will be assigned to either Neon code or generic C code; otherwise, generic C -// code will be assigned. -// Note that this function MUST be called in any application that uses SPL -// functions. -void WebRtcSpl_Init(); +// third party math functions +#include "common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h" int16_t WebRtcSpl_GetScalingSquare(int16_t* in_vector, size_t in_vector_length, @@ -133,13 +119,10 @@ void WebRtcSpl_CopyFromEndW16(const int16_t* in_vector, size_t in_vector_length, size_t samples, int16_t* out_vector); -void WebRtcSpl_ZerosArrayW16(int16_t* vector, - size_t vector_length); -void WebRtcSpl_ZerosArrayW32(int32_t* vector, - size_t vector_length); +void WebRtcSpl_ZerosArrayW16(int16_t* vector, size_t vector_length); +void WebRtcSpl_ZerosArrayW32(int32_t* vector, size_t vector_length); // End: Copy and set operations. - // Minimum and maximum operation functions and their pointers. // Implementation in min_max_operations.c. @@ -151,9 +134,9 @@ void WebRtcSpl_ZerosArrayW32(int32_t* vector, // // Return value : Maximum absolute value in vector. typedef int16_t (*MaxAbsValueW16)(const int16_t* vector, size_t length); -extern MaxAbsValueW16 WebRtcSpl_MaxAbsValueW16; +extern const MaxAbsValueW16 WebRtcSpl_MaxAbsValueW16; int16_t WebRtcSpl_MaxAbsValueW16C(const int16_t* vector, size_t length); -#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON) +#if defined(WEBRTC_HAS_NEON) int16_t WebRtcSpl_MaxAbsValueW16Neon(const int16_t* vector, size_t length); #endif #if defined(MIPS32_LE) @@ -168,9 +151,9 @@ int16_t WebRtcSpl_MaxAbsValueW16_mips(const int16_t* vector, size_t length); // // Return value : Maximum absolute value in vector. typedef int32_t (*MaxAbsValueW32)(const int32_t* vector, size_t length); -extern MaxAbsValueW32 WebRtcSpl_MaxAbsValueW32; +extern const MaxAbsValueW32 WebRtcSpl_MaxAbsValueW32; int32_t WebRtcSpl_MaxAbsValueW32C(const int32_t* vector, size_t length); -#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON) +#if defined(WEBRTC_HAS_NEON) int32_t WebRtcSpl_MaxAbsValueW32Neon(const int32_t* vector, size_t length); #endif #if defined(MIPS_DSP_R1_LE) @@ -185,9 +168,9 @@ int32_t WebRtcSpl_MaxAbsValueW32_mips(const int32_t* vector, size_t length); // // Return value : Maximum sample value in |vector|. typedef int16_t (*MaxValueW16)(const int16_t* vector, size_t length); -extern MaxValueW16 WebRtcSpl_MaxValueW16; +extern const MaxValueW16 WebRtcSpl_MaxValueW16; int16_t WebRtcSpl_MaxValueW16C(const int16_t* vector, size_t length); -#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON) +#if defined(WEBRTC_HAS_NEON) int16_t WebRtcSpl_MaxValueW16Neon(const int16_t* vector, size_t length); #endif #if defined(MIPS32_LE) @@ -202,9 +185,9 @@ int16_t WebRtcSpl_MaxValueW16_mips(const int16_t* vector, size_t length); // // Return value : Maximum sample value in |vector|. typedef int32_t (*MaxValueW32)(const int32_t* vector, size_t length); -extern MaxValueW32 WebRtcSpl_MaxValueW32; +extern const MaxValueW32 WebRtcSpl_MaxValueW32; int32_t WebRtcSpl_MaxValueW32C(const int32_t* vector, size_t length); -#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON) +#if defined(WEBRTC_HAS_NEON) int32_t WebRtcSpl_MaxValueW32Neon(const int32_t* vector, size_t length); #endif #if defined(MIPS32_LE) @@ -219,9 +202,9 @@ int32_t WebRtcSpl_MaxValueW32_mips(const int32_t* vector, size_t length); // // Return value : Minimum sample value in |vector|. typedef int16_t (*MinValueW16)(const int16_t* vector, size_t length); -extern MinValueW16 WebRtcSpl_MinValueW16; +extern const MinValueW16 WebRtcSpl_MinValueW16; int16_t WebRtcSpl_MinValueW16C(const int16_t* vector, size_t length); -#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON) +#if defined(WEBRTC_HAS_NEON) int16_t WebRtcSpl_MinValueW16Neon(const int16_t* vector, size_t length); #endif #if defined(MIPS32_LE) @@ -236,9 +219,9 @@ int16_t WebRtcSpl_MinValueW16_mips(const int16_t* vector, size_t length); // // Return value : Minimum sample value in |vector|. typedef int32_t (*MinValueW32)(const int32_t* vector, size_t length); -extern MinValueW32 WebRtcSpl_MinValueW32; +extern const MinValueW32 WebRtcSpl_MinValueW32; int32_t WebRtcSpl_MinValueW32C(const int32_t* vector, size_t length); -#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON) +#if defined(WEBRTC_HAS_NEON) int32_t WebRtcSpl_MinValueW32Neon(const int32_t* vector, size_t length); #endif #if defined(MIPS32_LE) @@ -299,7 +282,6 @@ size_t WebRtcSpl_MinIndexW32(const int32_t* vector, size_t length); // End: Minimum and maximum operations. - // Vector scaling operations. Implementation in vector_scaling_operations.c. // Description at bottom of file. void WebRtcSpl_VectorBitShiftW16(int16_t* out_vector, @@ -325,9 +307,11 @@ void WebRtcSpl_ScaleVectorWithSat(const int16_t* in_vector, size_t vector_length, int16_t right_shifts); void WebRtcSpl_ScaleAndAddVectors(const int16_t* in_vector1, - int16_t gain1, int right_shifts1, + int16_t gain1, + int right_shifts1, const int16_t* in_vector2, - int16_t gain2, int right_shifts2, + int16_t gain2, + int right_shifts2, int16_t* out_vector, size_t vector_length); @@ -346,8 +330,8 @@ void WebRtcSpl_ScaleAndAddVectors(const int16_t* in_vector1, // // Output: // - out_vector : Output vector -// Return value : 0 if OK, -1 if (in_vector1 == NULL -// || in_vector2 == NULL || out_vector == NULL +// Return value : 0 if OK, -1 if (in_vector1 == null +// || in_vector2 == null || out_vector == null // || length <= 0 || right_shift < 0). typedef int (*ScaleAndAddVectorsWithRound)(const int16_t* in_vector1, int16_t in_vector1_scale, @@ -356,7 +340,7 @@ typedef int (*ScaleAndAddVectorsWithRound)(const int16_t* in_vector1, int right_shifts, int16_t* out_vector, size_t length); -extern ScaleAndAddVectorsWithRound WebRtcSpl_ScaleAndAddVectorsWithRound; +extern const ScaleAndAddVectorsWithRound WebRtcSpl_ScaleAndAddVectorsWithRound; int WebRtcSpl_ScaleAndAddVectorsWithRoundC(const int16_t* in_vector1, int16_t in_vector1_scale, const int16_t* in_vector2, @@ -393,13 +377,13 @@ void WebRtcSpl_AddVectorsAndShift(int16_t* out_vector, size_t vector_length, int16_t right_shifts); void WebRtcSpl_AddAffineVectorToVector(int16_t* out_vector, - int16_t* in_vector, + const int16_t* in_vector, int16_t gain, int32_t add_constant, int16_t right_shifts, size_t vector_length); void WebRtcSpl_AffineTransformVector(int16_t* out_vector, - int16_t* in_vector, + const int16_t* in_vector, int16_t gain, int32_t add_constant, int16_t right_shifts, @@ -523,7 +507,7 @@ typedef void (*CrossCorrelation)(int32_t* cross_correlation, size_t dim_cross_correlation, int right_shifts, int step_seq2); -extern CrossCorrelation WebRtcSpl_CrossCorrelation; +extern const CrossCorrelation WebRtcSpl_CrossCorrelation; void WebRtcSpl_CrossCorrelationC(int32_t* cross_correlation, const int16_t* seq1, const int16_t* seq2, @@ -531,7 +515,7 @@ void WebRtcSpl_CrossCorrelationC(int32_t* cross_correlation, size_t dim_cross_correlation, int right_shifts, int step_seq2); -#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON) +#if defined(WEBRTC_HAS_NEON) void WebRtcSpl_CrossCorrelationNeon(int32_t* cross_correlation, const int16_t* seq1, const int16_t* seq2, @@ -585,7 +569,6 @@ int16_t WebRtcSpl_RandUArray(int16_t* vector, // Math functions int32_t WebRtcSpl_Sqrt(int32_t value); -int32_t WebRtcSpl_SqrtFloor(int32_t value); // Divisions. Implementations collected in division_operations.c and // descriptions at bottom of this file. @@ -600,22 +583,6 @@ int32_t WebRtcSpl_Energy(int16_t* vector, size_t vector_length, int* scale_factor); -// Calculates the dot product between two (int16_t) vectors. -// -// Input: -// - vector1 : Vector 1 -// - vector2 : Vector 2 -// - vector_length : Number of samples used in the dot product -// - scaling : The number of right bit shifts to apply on each term -// during calculation to avoid overflow, i.e., the -// output will be in Q(-|scaling|) -// -// Return value : The dot product in Q(-scaling) -int32_t WebRtcSpl_DotProductWithScale(const int16_t* vector1, - const int16_t* vector2, - size_t length, - int scaling); - // Filter operations. size_t WebRtcSpl_FilterAR(const int16_t* ar_coef, size_t ar_coef_length, @@ -689,7 +656,7 @@ typedef int (*DownsampleFast)(const int16_t* data_in, size_t coefficients_length, int factor, size_t delay); -extern DownsampleFast WebRtcSpl_DownsampleFast; +extern const DownsampleFast WebRtcSpl_DownsampleFast; int WebRtcSpl_DownsampleFastC(const int16_t* data_in, size_t data_in_length, int16_t* data_out, @@ -698,7 +665,7 @@ int WebRtcSpl_DownsampleFastC(const int16_t* data_in, size_t coefficients_length, int factor, size_t delay); -#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON) +#if defined(WEBRTC_HAS_NEON) int WebRtcSpl_DownsampleFastNeon(const int16_t* data_in, size_t data_in_length, int16_t* data_out, @@ -795,7 +762,8 @@ typedef struct { int32_t S_16_8[8]; } WebRtcSpl_State22khzTo8khz; -void WebRtcSpl_Resample22khzTo8khz(const int16_t* in, int16_t* out, +void WebRtcSpl_Resample22khzTo8khz(const int16_t* in, + int16_t* out, WebRtcSpl_State22khzTo8khz* state, int32_t* tmpmem); @@ -808,7 +776,8 @@ typedef struct { int32_t S_11_22[8]; } WebRtcSpl_State8khzTo22khz; -void WebRtcSpl_Resample8khzTo22khz(const int16_t* in, int16_t* out, +void WebRtcSpl_Resample8khzTo22khz(const int16_t* in, + int16_t* out, WebRtcSpl_State8khzTo22khz* state, int32_t* tmpmem); @@ -848,7 +817,8 @@ typedef struct { int32_t S_32_16[8]; } WebRtcSpl_State48khzTo16khz; -void WebRtcSpl_Resample48khzTo16khz(const int16_t* in, int16_t* out, +void WebRtcSpl_Resample48khzTo16khz(const int16_t* in, + int16_t* out, WebRtcSpl_State48khzTo16khz* state, int32_t* tmpmem); @@ -860,7 +830,8 @@ typedef struct { int32_t S_24_48[8]; } WebRtcSpl_State16khzTo48khz; -void WebRtcSpl_Resample16khzTo48khz(const int16_t* in, int16_t* out, +void WebRtcSpl_Resample16khzTo48khz(const int16_t* in, + int16_t* out, WebRtcSpl_State16khzTo48khz* state, int32_t* tmpmem); @@ -873,7 +844,8 @@ typedef struct { int32_t S_16_8[8]; } WebRtcSpl_State48khzTo8khz; -void WebRtcSpl_Resample48khzTo8khz(const int16_t* in, int16_t* out, +void WebRtcSpl_Resample48khzTo8khz(const int16_t* in, + int16_t* out, WebRtcSpl_State48khzTo8khz* state, int32_t* tmpmem); @@ -886,7 +858,8 @@ typedef struct { int32_t S_24_48[8]; } WebRtcSpl_State8khzTo48khz; -void WebRtcSpl_Resample8khzTo48khz(const int16_t* in, int16_t* out, +void WebRtcSpl_Resample8khzTo48khz(const int16_t* in, + int16_t* out, WebRtcSpl_State8khzTo48khz* state, int32_t* tmpmem); @@ -899,11 +872,15 @@ void WebRtcSpl_ResetResample8khzTo48khz(WebRtcSpl_State8khzTo48khz* state); * ******************************************************************/ -void WebRtcSpl_DownsampleBy2(const int16_t* in, size_t len, - int16_t* out, int32_t* filtState); +void WebRtcSpl_DownsampleBy2(const int16_t* in, + size_t len, + int16_t* out, + int32_t* filtState); -void WebRtcSpl_UpsampleBy2(const int16_t* in, size_t len, - int16_t* out, int32_t* filtState); +void WebRtcSpl_UpsampleBy2(const int16_t* in, + size_t len, + int16_t* out, + int32_t* filtState); /************************************************************ * END OF RESAMPLING FUNCTIONS @@ -924,7 +901,7 @@ void WebRtcSpl_SynthesisQMF(const int16_t* low_band, #ifdef __cplusplus } #endif // __cplusplus -#endif // WEBRTC_SPL_SIGNAL_PROCESSING_LIBRARY_H_ +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SIGNAL_PROCESSING_LIBRARY_H_ // // WebRtcSpl_AddSatW16(...) @@ -1352,23 +1329,6 @@ void WebRtcSpl_SynthesisQMF(const int16_t* low_band, // Return value : Result of the sqrt calculation // -// -// WebRtcSpl_SqrtFloor(...) -// -// Returns the square root of the input value |value|. The precision of this -// function is rounding down integer precision, i.e., sqrt(8) gives 2 as answer. -// If |value| is a negative number then 0 is returned. -// -// Algorithm: -// -// An iterative 4 cylce/bit routine -// -// Input: -// - value : Value to calculate sqrt of -// -// Return value : Result of the sqrt calculation -// - // // WebRtcSpl_DivU32U16(...) // diff --git a/webrtc/common_audio/signal_processing/include/spl_inl.h b/webrtc/common_audio/signal_processing/include/spl_inl.h index d3cc6de..656a312 100644 --- a/webrtc/common_audio/signal_processing/include/spl_inl.h +++ b/webrtc/common_audio/signal_processing/include/spl_inl.h @@ -8,24 +8,71 @@ * be found in the AUTHORS file in the root of the source tree. */ - // This header file includes the inline functions in // the fix point signal processing library. -#ifndef WEBRTC_SPL_SPL_INL_H_ -#define WEBRTC_SPL_SPL_INL_H_ +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_H_ + +#include "rtc_base/compile_assert_c.h" + +extern const int8_t kWebRtcSpl_CountLeadingZeros32_Table[64]; + +// Don't call this directly except in tests! +static __inline int WebRtcSpl_CountLeadingZeros32_NotBuiltin(uint32_t n) { + // Normalize n by rounding up to the nearest number that is a sequence of 0 + // bits followed by a sequence of 1 bits. This number has the same number of + // leading zeros as the original n. There are exactly 33 such values. + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + + // Multiply the modified n with a constant selected (by exhaustive search) + // such that each of the 33 possible values of n give a product whose 6 most + // significant bits are unique. Then look up the answer in the table. + return kWebRtcSpl_CountLeadingZeros32_Table[(n * 0x8c0b2891) >> 26]; +} + +// Don't call this directly except in tests! +static __inline int WebRtcSpl_CountLeadingZeros64_NotBuiltin(uint64_t n) { + const int leading_zeros = n >> 32 == 0 ? 32 : 0; + return leading_zeros + WebRtcSpl_CountLeadingZeros32_NotBuiltin( + (uint32_t)(n >> (32 - leading_zeros))); +} + +// Returns the number of leading zero bits in the argument. +static __inline int WebRtcSpl_CountLeadingZeros32(uint32_t n) { +#ifdef __GNUC__ + RTC_COMPILE_ASSERT(sizeof(unsigned int) == sizeof(uint32_t)); + return n == 0 ? 32 : __builtin_clz(n); +#else + return WebRtcSpl_CountLeadingZeros32_NotBuiltin(n); +#endif +} + +// Returns the number of leading zero bits in the argument. +static __inline int WebRtcSpl_CountLeadingZeros64(uint64_t n) { +#ifdef __GNUC__ + RTC_COMPILE_ASSERT(sizeof(unsigned long long) == sizeof(uint64_t)); // NOLINT + return n == 0 ? 64 : __builtin_clzll(n); +#else + return WebRtcSpl_CountLeadingZeros64_NotBuiltin(n); +#endif +} #ifdef WEBRTC_ARCH_ARM_V7 -#include "webrtc/common_audio/signal_processing/include/spl_inl_armv7.h" +#include "common_audio/signal_processing/include/spl_inl_armv7.h" #else #if defined(MIPS32_LE) -#include "webrtc/common_audio/signal_processing/include/spl_inl_mips.h" +#include "common_audio/signal_processing/include/spl_inl_mips.h" #endif #if !defined(MIPS_DSP_R1_LE) static __inline int16_t WebRtcSpl_SatW32ToW16(int32_t value32) { - int16_t out16 = (int16_t) value32; + int16_t out16 = (int16_t)value32; if (value32 > 32767) out16 = 32767; @@ -35,132 +82,65 @@ static __inline int16_t WebRtcSpl_SatW32ToW16(int32_t value32) { return out16; } -static __inline int32_t WebRtcSpl_AddSatW32(int32_t l_var1, int32_t l_var2) { - int32_t l_sum; +static __inline int32_t WebRtcSpl_AddSatW32(int32_t a, int32_t b) { + // Do the addition in unsigned numbers, since signed overflow is undefined + // behavior. + const int32_t sum = (int32_t)((uint32_t)a + (uint32_t)b); - // Perform long addition - l_sum = l_var1 + l_var2; - - if (l_var1 < 0) { // Check for underflow. - if ((l_var2 < 0) && (l_sum >= 0)) { - l_sum = (int32_t)0x80000000; - } - } else { // Check for overflow. - if ((l_var2 > 0) && (l_sum < 0)) { - l_sum = (int32_t)0x7FFFFFFF; - } + // a + b can't overflow if a and b have different signs. If they have the + // same sign, a + b also has the same sign iff it didn't overflow. + if ((a < 0) == (b < 0) && (a < 0) != (sum < 0)) { + // The direction of the overflow is obvious from the sign of a + b. + return sum < 0 ? INT32_MAX : INT32_MIN; } - - return l_sum; + return sum; } -static __inline int32_t WebRtcSpl_SubSatW32(int32_t l_var1, int32_t l_var2) { - int32_t l_diff; +static __inline int32_t WebRtcSpl_SubSatW32(int32_t a, int32_t b) { + // Do the subtraction in unsigned numbers, since signed overflow is undefined + // behavior. + const int32_t diff = (int32_t)((uint32_t)a - (uint32_t)b); - // Perform subtraction. - l_diff = l_var1 - l_var2; - - if (l_var1 < 0) { // Check for underflow. - if ((l_var2 > 0) && (l_diff > 0)) { - l_diff = (int32_t)0x80000000; - } - } else { // Check for overflow. - if ((l_var2 < 0) && (l_diff < 0)) { - l_diff = (int32_t)0x7FFFFFFF; - } + // a - b can't overflow if a and b have the same sign. If they have different + // signs, a - b has the same sign as a iff it didn't overflow. + if ((a < 0) != (b < 0) && (a < 0) != (diff < 0)) { + // The direction of the overflow is obvious from the sign of a - b. + return diff < 0 ? INT32_MAX : INT32_MIN; } - - return l_diff; + return diff; } static __inline int16_t WebRtcSpl_AddSatW16(int16_t a, int16_t b) { - return WebRtcSpl_SatW32ToW16((int32_t) a + (int32_t) b); + return WebRtcSpl_SatW32ToW16((int32_t)a + (int32_t)b); } static __inline int16_t WebRtcSpl_SubSatW16(int16_t var1, int16_t var2) { - return WebRtcSpl_SatW32ToW16((int32_t) var1 - (int32_t) var2); + return WebRtcSpl_SatW32ToW16((int32_t)var1 - (int32_t)var2); } #endif // #if !defined(MIPS_DSP_R1_LE) #if !defined(MIPS32_LE) static __inline int16_t WebRtcSpl_GetSizeInBits(uint32_t n) { - int16_t bits; - - if (0xFFFF0000 & n) { - bits = 16; - } else { - bits = 0; - } - if (0x0000FF00 & (n >> bits)) bits += 8; - if (0x000000F0 & (n >> bits)) bits += 4; - if (0x0000000C & (n >> bits)) bits += 2; - if (0x00000002 & (n >> bits)) bits += 1; - if (0x00000001 & (n >> bits)) bits += 1; - - return bits; + return 32 - WebRtcSpl_CountLeadingZeros32(n); } +// Return the number of steps a can be left-shifted without overflow, +// or 0 if a == 0. static __inline int16_t WebRtcSpl_NormW32(int32_t a) { - int16_t zeros; - - if (a == 0) { - return 0; - } - else if (a < 0) { - a = ~a; - } - - if (!(0xFFFF8000 & a)) { - zeros = 16; - } else { - zeros = 0; - } - if (!(0xFF800000 & (a << zeros))) zeros += 8; - if (!(0xF8000000 & (a << zeros))) zeros += 4; - if (!(0xE0000000 & (a << zeros))) zeros += 2; - if (!(0xC0000000 & (a << zeros))) zeros += 1; - - return zeros; + return a == 0 ? 0 : WebRtcSpl_CountLeadingZeros32(a < 0 ? ~a : a) - 1; } +// Return the number of steps a can be left-shifted without overflow, +// or 0 if a == 0. static __inline int16_t WebRtcSpl_NormU32(uint32_t a) { - int16_t zeros; - - if (a == 0) return 0; - - if (!(0xFFFF0000 & a)) { - zeros = 16; - } else { - zeros = 0; - } - if (!(0xFF000000 & (a << zeros))) zeros += 8; - if (!(0xF0000000 & (a << zeros))) zeros += 4; - if (!(0xC0000000 & (a << zeros))) zeros += 2; - if (!(0x80000000 & (a << zeros))) zeros += 1; - - return zeros; + return a == 0 ? 0 : WebRtcSpl_CountLeadingZeros32(a); } +// Return the number of steps a can be left-shifted without overflow, +// or 0 if a == 0. static __inline int16_t WebRtcSpl_NormW16(int16_t a) { - int16_t zeros; - - if (a == 0) { - return 0; - } - else if (a < 0) { - a = ~a; - } - - if (!(0xFF80 & a)) { - zeros = 8; - } else { - zeros = 0; - } - if (!(0xF800 & (a << zeros))) zeros += 4; - if (!(0xE000 & (a << zeros))) zeros += 2; - if (!(0xC000 & (a << zeros))) zeros += 1; - - return zeros; + const int32_t a32 = a; + return a == 0 ? 0 : WebRtcSpl_CountLeadingZeros32(a < 0 ? ~a32 : a32) - 17; } static __inline int32_t WebRtc_MulAccumW16(int16_t a, int16_t b, int32_t c) { @@ -170,4 +150,4 @@ static __inline int32_t WebRtc_MulAccumW16(int16_t a, int16_t b, int32_t c) { #endif // WEBRTC_ARCH_ARM_V7 -#endif // WEBRTC_SPL_SPL_INL_H_ +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_H_ diff --git a/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h b/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h index 2718801..930e91e 100644 --- a/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h +++ b/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h @@ -8,13 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ - /* This header file includes the inline functions for ARM processors in * the fix point signal processing library. */ -#ifndef WEBRTC_SPL_SPL_INL_ARMV7_H_ -#define WEBRTC_SPL_SPL_INL_ARMV7_H_ +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_ARMV7_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_ARMV7_H_ /* TODO(kma): Replace some assembly code with GCC intrinsics * (e.g. __builtin_clz). @@ -26,35 +25,37 @@ */ static __inline int32_t WEBRTC_SPL_MUL_16_32_RSFT16(int16_t a, int32_t b) { int32_t tmp = 0; - __asm __volatile ("smulwb %0, %1, %2":"=r"(tmp):"r"(b), "r"(a)); + __asm __volatile("smulwb %0, %1, %2" : "=r"(tmp) : "r"(b), "r"(a)); return tmp; } static __inline int32_t WEBRTC_SPL_MUL_16_16(int16_t a, int16_t b) { int32_t tmp = 0; - __asm __volatile ("smulbb %0, %1, %2":"=r"(tmp):"r"(a), "r"(b)); + __asm __volatile("smulbb %0, %1, %2" : "=r"(tmp) : "r"(a), "r"(b)); return tmp; } // TODO(kma): add unit test. static __inline int32_t WebRtc_MulAccumW16(int16_t a, int16_t b, int32_t c) { int32_t tmp = 0; - __asm __volatile ("smlabb %0, %1, %2, %3":"=r"(tmp):"r"(a), "r"(b), "r"(c)); + __asm __volatile("smlabb %0, %1, %2, %3" + : "=r"(tmp) + : "r"(a), "r"(b), "r"(c)); return tmp; } static __inline int16_t WebRtcSpl_AddSatW16(int16_t a, int16_t b) { int32_t s_sum = 0; - __asm __volatile ("qadd16 %0, %1, %2":"=r"(s_sum):"r"(a), "r"(b)); + __asm __volatile("qadd16 %0, %1, %2" : "=r"(s_sum) : "r"(a), "r"(b)); - return (int16_t) s_sum; + return (int16_t)s_sum; } static __inline int32_t WebRtcSpl_AddSatW32(int32_t l_var1, int32_t l_var2) { int32_t l_sum = 0; - __asm __volatile ("qadd %0, %1, %2":"=r"(l_sum):"r"(l_var1), "r"(l_var2)); + __asm __volatile("qadd %0, %1, %2" : "=r"(l_sum) : "r"(l_var1), "r"(l_var2)); return l_sum; } @@ -62,7 +63,7 @@ static __inline int32_t WebRtcSpl_AddSatW32(int32_t l_var1, int32_t l_var2) { static __inline int32_t WebRtcSpl_SubSatW32(int32_t l_var1, int32_t l_var2) { int32_t l_sub = 0; - __asm __volatile ("qsub %0, %1, %2":"=r"(l_sub):"r"(l_var1), "r"(l_var2)); + __asm __volatile("qsub %0, %1, %2" : "=r"(l_sub) : "r"(l_var1), "r"(l_var2)); return l_sub; } @@ -70,7 +71,7 @@ static __inline int32_t WebRtcSpl_SubSatW32(int32_t l_var1, int32_t l_var2) { static __inline int16_t WebRtcSpl_SubSatW16(int16_t var1, int16_t var2) { int32_t s_sub = 0; - __asm __volatile ("qsub16 %0, %1, %2":"=r"(s_sub):"r"(var1), "r"(var2)); + __asm __volatile("qsub16 %0, %1, %2" : "=r"(s_sub) : "r"(var1), "r"(var2)); return (int16_t)s_sub; } @@ -78,7 +79,7 @@ static __inline int16_t WebRtcSpl_SubSatW16(int16_t var1, int16_t var2) { static __inline int16_t WebRtcSpl_GetSizeInBits(uint32_t n) { int32_t tmp = 0; - __asm __volatile ("clz %0, %1":"=r"(tmp):"r"(n)); + __asm __volatile("clz %0, %1" : "=r"(tmp) : "r"(n)); return (int16_t)(32 - tmp); } @@ -88,12 +89,11 @@ static __inline int16_t WebRtcSpl_NormW32(int32_t a) { if (a == 0) { return 0; - } - else if (a < 0) { + } else if (a < 0) { a ^= 0xFFFFFFFF; } - __asm __volatile ("clz %0, %1":"=r"(tmp):"r"(a)); + __asm __volatile("clz %0, %1" : "=r"(tmp) : "r"(a)); return (int16_t)(tmp - 1); } @@ -101,9 +101,10 @@ static __inline int16_t WebRtcSpl_NormW32(int32_t a) { static __inline int16_t WebRtcSpl_NormU32(uint32_t a) { int tmp = 0; - if (a == 0) return 0; + if (a == 0) + return 0; - __asm __volatile ("clz %0, %1":"=r"(tmp):"r"(a)); + __asm __volatile("clz %0, %1" : "=r"(tmp) : "r"(a)); return (int16_t)tmp; } @@ -114,12 +115,11 @@ static __inline int16_t WebRtcSpl_NormW16(int16_t a) { if (a_32 == 0) { return 0; - } - else if (a_32 < 0) { + } else if (a_32 < 0) { a_32 ^= 0xFFFFFFFF; } - __asm __volatile ("clz %0, %1":"=r"(tmp):"r"(a_32)); + __asm __volatile("clz %0, %1" : "=r"(tmp) : "r"(a_32)); return (int16_t)(tmp - 17); } @@ -128,9 +128,9 @@ static __inline int16_t WebRtcSpl_NormW16(int16_t a) { static __inline int16_t WebRtcSpl_SatW32ToW16(int32_t value32) { int32_t out = 0; - __asm __volatile ("ssat %0, #16, %1" : "=r"(out) : "r"(value32)); + __asm __volatile("ssat %0, #16, %1" : "=r"(out) : "r"(value32)); return (int16_t)out; } -#endif // WEBRTC_SPL_SPL_INL_ARMV7_H_ +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_ARMV7_H_ diff --git a/webrtc/common_audio/signal_processing/include/spl_inl_mips.h b/webrtc/common_audio/signal_processing/include/spl_inl_mips.h index cd04bdd..1db95e8 100644 --- a/webrtc/common_audio/signal_processing/include/spl_inl_mips.h +++ b/webrtc/common_audio/signal_processing/include/spl_inl_mips.h @@ -8,72 +8,65 @@ * be found in the AUTHORS file in the root of the source tree. */ - // This header file includes the inline functions in // the fix point signal processing library. -#ifndef WEBRTC_SPL_SPL_INL_MIPS_H_ -#define WEBRTC_SPL_SPL_INL_MIPS_H_ +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_MIPS_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_MIPS_H_ -static __inline int32_t WEBRTC_SPL_MUL_16_16(int32_t a, - int32_t b) { +static __inline int32_t WEBRTC_SPL_MUL_16_16(int32_t a, int32_t b) { int32_t value32 = 0; int32_t a1 = 0, b1 = 0; __asm __volatile( #if defined(MIPS32_R2_LE) - "seh %[a1], %[a] \n\t" - "seh %[b1], %[b] \n\t" + "seh %[a1], %[a] \n\t" + "seh %[b1], %[b] \n\t" #else - "sll %[a1], %[a], 16 \n\t" - "sll %[b1], %[b], 16 \n\t" - "sra %[a1], %[a1], 16 \n\t" - "sra %[b1], %[b1], 16 \n\t" + "sll %[a1], %[a], 16 \n\t" + "sll %[b1], %[b], 16 \n\t" + "sra %[a1], %[a1], 16 \n\t" + "sra %[b1], %[b1], 16 \n\t" #endif - "mul %[value32], %[a1], %[b1] \n\t" - : [value32] "=r" (value32), [a1] "=&r" (a1), [b1] "=&r" (b1) - : [a] "r" (a), [b] "r" (b) - : "hi", "lo" - ); + "mul %[value32], %[a1], %[b1] \n\t" + : [value32] "=r"(value32), [a1] "=&r"(a1), [b1] "=&r"(b1) + : [a] "r"(a), [b] "r"(b) + : "hi", "lo"); return value32; } -static __inline int32_t WEBRTC_SPL_MUL_16_32_RSFT16(int16_t a, - int32_t b) { +static __inline int32_t WEBRTC_SPL_MUL_16_32_RSFT16(int16_t a, int32_t b) { int32_t value32 = 0, b1 = 0, b2 = 0; int32_t a1 = 0; __asm __volatile( #if defined(MIPS32_R2_LE) - "seh %[a1], %[a] \n\t" + "seh %[a1], %[a] \n\t" #else - "sll %[a1], %[a], 16 \n\t" - "sra %[a1], %[a1], 16 \n\t" + "sll %[a1], %[a], 16 \n\t" + "sra %[a1], %[a1], 16 \n\t" #endif - "andi %[b2], %[b], 0xFFFF \n\t" - "sra %[b1], %[b], 16 \n\t" - "sra %[b2], %[b2], 1 \n\t" - "mul %[value32], %[a1], %[b1] \n\t" - "mul %[b2], %[a1], %[b2] \n\t" - "addiu %[b2], %[b2], 0x4000 \n\t" - "sra %[b2], %[b2], 15 \n\t" - "addu %[value32], %[value32], %[b2] \n\t" - : [value32] "=&r" (value32), [b1] "=&r" (b1), [b2] "=&r" (b2), - [a1] "=&r" (a1) - : [a] "r" (a), [b] "r" (b) - : "hi", "lo" - ); + "andi %[b2], %[b], 0xFFFF \n\t" + "sra %[b1], %[b], 16 \n\t" + "sra %[b2], %[b2], 1 \n\t" + "mul %[value32], %[a1], %[b1] \n\t" + "mul %[b2], %[a1], %[b2] \n\t" + "addiu %[b2], %[b2], 0x4000 \n\t" + "sra %[b2], %[b2], 15 \n\t" + "addu %[value32], %[value32], %[b2] \n\t" + : [value32] "=&r"(value32), [b1] "=&r"(b1), [b2] "=&r"(b2), [a1] "=&r"(a1) + : [a] "r"(a), [b] "r"(b) + : "hi", "lo"); return value32; } #if defined(MIPS_DSP_R1_LE) static __inline int16_t WebRtcSpl_SatW32ToW16(int32_t value32) { __asm __volatile( - "shll_s.w %[value32], %[value32], 16 \n\t" - "sra %[value32], %[value32], 16 \n\t" - : [value32] "+r" (value32) - : - ); + "shll_s.w %[value32], %[value32], 16 \n\t" + "sra %[value32], %[value32], 16 \n\t" + : [value32] "+r"(value32) + :); int16_t out16 = (int16_t)value32; return out16; } @@ -81,11 +74,9 @@ static __inline int16_t WebRtcSpl_SatW32ToW16(int32_t value32) { static __inline int16_t WebRtcSpl_AddSatW16(int16_t a, int16_t b) { int32_t value32 = 0; - __asm __volatile( - "addq_s.ph %[value32], %[a], %[b] \n\t" - : [value32] "=r" (value32) - : [a] "r" (a), [b] "r" (b) - ); + __asm __volatile("addq_s.ph %[value32], %[a], %[b] \n\t" + : [value32] "=r"(value32) + : [a] "r"(a), [b] "r"(b)); return (int16_t)value32; } @@ -93,10 +84,9 @@ static __inline int32_t WebRtcSpl_AddSatW32(int32_t l_var1, int32_t l_var2) { int32_t l_sum; __asm __volatile( - "addq_s.w %[l_sum], %[l_var1], %[l_var2] \n\t" - : [l_sum] "=r" (l_sum) - : [l_var1] "r" (l_var1), [l_var2] "r" (l_var2) - ); + "addq_s.w %[l_sum], %[l_var1], %[l_var2] \n\t" + : [l_sum] "=r"(l_sum) + : [l_var1] "r"(l_var1), [l_var2] "r"(l_var2)); return l_sum; } @@ -104,11 +94,9 @@ static __inline int32_t WebRtcSpl_AddSatW32(int32_t l_var1, int32_t l_var2) { static __inline int16_t WebRtcSpl_SubSatW16(int16_t var1, int16_t var2) { int32_t value32; - __asm __volatile( - "subq_s.ph %[value32], %[var1], %[var2] \n\t" - : [value32] "=r" (value32) - : [var1] "r" (var1), [var2] "r" (var2) - ); + __asm __volatile("subq_s.ph %[value32], %[var1], %[var2] \n\t" + : [value32] "=r"(value32) + : [var1] "r"(var1), [var2] "r"(var2)); return (int16_t)value32; } @@ -117,10 +105,9 @@ static __inline int32_t WebRtcSpl_SubSatW32(int32_t l_var1, int32_t l_var2) { int32_t l_diff; __asm __volatile( - "subq_s.w %[l_diff], %[l_var1], %[l_var2] \n\t" - : [l_diff] "=r" (l_diff) - : [l_var1] "r" (l_var1), [l_var2] "r" (l_var2) - ); + "subq_s.w %[l_diff], %[l_var1], %[l_var2] \n\t" + : [l_diff] "=r"(l_diff) + : [l_var1] "r"(l_var1), [l_var2] "r"(l_var2)); return l_diff; } @@ -131,11 +118,10 @@ static __inline int16_t WebRtcSpl_GetSizeInBits(uint32_t n) { int i32 = 32; __asm __volatile( - "clz %[bits], %[n] \n\t" - "subu %[bits], %[i32], %[bits] \n\t" - : [bits] "=&r" (bits) - : [n] "r" (n), [i32] "r" (i32) - ); + "clz %[bits], %[n] \n\t" + "subu %[bits], %[i32], %[bits] \n\t" + : [bits] "=&r"(bits) + : [n] "r"(n), [i32] "r"(i32)); return (int16_t)bits; } @@ -144,21 +130,20 @@ static __inline int16_t WebRtcSpl_NormW32(int32_t a) { int zeros = 0; __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "bnez %[a], 1f \n\t" - " sra %[zeros], %[a], 31 \n\t" - "b 2f \n\t" - " move %[zeros], $zero \n\t" - "1: \n\t" - "xor %[zeros], %[a], %[zeros] \n\t" - "clz %[zeros], %[zeros] \n\t" - "addiu %[zeros], %[zeros], -1 \n\t" - "2: \n\t" - ".set pop \n\t" - : [zeros]"=&r"(zeros) - : [a] "r" (a) - ); + ".set push \n\t" + ".set noreorder \n\t" + "bnez %[a], 1f \n\t" + " sra %[zeros], %[a], 31 \n\t" + "b 2f \n\t" + " move %[zeros], $zero \n\t" + "1: \n\t" + "xor %[zeros], %[a], %[zeros] \n\t" + "clz %[zeros], %[zeros] \n\t" + "addiu %[zeros], %[zeros], -1 \n\t" + "2: \n\t" + ".set pop \n\t" + : [zeros] "=&r"(zeros) + : [a] "r"(a)); return (int16_t)zeros; } @@ -166,11 +151,9 @@ static __inline int16_t WebRtcSpl_NormW32(int32_t a) { static __inline int16_t WebRtcSpl_NormU32(uint32_t a) { int zeros = 0; - __asm __volatile( - "clz %[zeros], %[a] \n\t" - : [zeros] "=r" (zeros) - : [a] "r" (a) - ); + __asm __volatile("clz %[zeros], %[a] \n\t" + : [zeros] "=r"(zeros) + : [a] "r"(a)); return (int16_t)(zeros & 0x1f); } @@ -180,46 +163,42 @@ static __inline int16_t WebRtcSpl_NormW16(int16_t a) { int a0 = a << 16; __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "bnez %[a0], 1f \n\t" - " sra %[zeros], %[a0], 31 \n\t" - "b 2f \n\t" - " move %[zeros], $zero \n\t" - "1: \n\t" - "xor %[zeros], %[a0], %[zeros] \n\t" - "clz %[zeros], %[zeros] \n\t" - "addiu %[zeros], %[zeros], -1 \n\t" - "2: \n\t" - ".set pop \n\t" - : [zeros]"=&r"(zeros) - : [a0] "r" (a0) - ); + ".set push \n\t" + ".set noreorder \n\t" + "bnez %[a0], 1f \n\t" + " sra %[zeros], %[a0], 31 \n\t" + "b 2f \n\t" + " move %[zeros], $zero \n\t" + "1: \n\t" + "xor %[zeros], %[a0], %[zeros] \n\t" + "clz %[zeros], %[zeros] \n\t" + "addiu %[zeros], %[zeros], -1 \n\t" + "2: \n\t" + ".set pop \n\t" + : [zeros] "=&r"(zeros) + : [a0] "r"(a0)); return (int16_t)zeros; } -static __inline int32_t WebRtc_MulAccumW16(int16_t a, - int16_t b, - int32_t c) { +static __inline int32_t WebRtc_MulAccumW16(int16_t a, int16_t b, int32_t c) { int32_t res = 0, c1 = 0; __asm __volatile( #if defined(MIPS32_R2_LE) - "seh %[a], %[a] \n\t" - "seh %[b], %[b] \n\t" + "seh %[a], %[a] \n\t" + "seh %[b], %[b] \n\t" #else - "sll %[a], %[a], 16 \n\t" - "sll %[b], %[b], 16 \n\t" - "sra %[a], %[a], 16 \n\t" - "sra %[b], %[b], 16 \n\t" + "sll %[a], %[a], 16 \n\t" + "sll %[b], %[b], 16 \n\t" + "sra %[a], %[a], 16 \n\t" + "sra %[b], %[b], 16 \n\t" #endif - "mul %[res], %[a], %[b] \n\t" - "addu %[c1], %[c], %[res] \n\t" - : [c1] "=r" (c1), [res] "=&r" (res) - : [a] "r" (a), [b] "r" (b), [c] "r" (c) - : "hi", "lo" - ); + "mul %[res], %[a], %[b] \n\t" + "addu %[c1], %[c], %[res] \n\t" + : [c1] "=r"(c1), [res] "=&r"(res) + : [a] "r"(a), [b] "r"(b), [c] "r"(c) + : "hi", "lo"); return (c1); } -#endif // WEBRTC_SPL_SPL_INL_MIPS_H_ +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_INCLUDE_SPL_INL_MIPS_H_ diff --git a/webrtc/common_audio/signal_processing/levinson_durbin.c b/webrtc/common_audio/signal_processing/levinson_durbin.c index d46e551..2c5cbae 100644 --- a/webrtc/common_audio/signal_processing/levinson_durbin.c +++ b/webrtc/common_audio/signal_processing/levinson_durbin.c @@ -15,12 +15,14 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/sanitizer.h" #define SPL_LEVINSON_MAXORDER 20 -int16_t WebRtcSpl_LevinsonDurbin(const int32_t* R, int16_t* A, int16_t* K, - size_t order) +int16_t RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/5486 +WebRtcSpl_LevinsonDurbin(const int32_t* R, int16_t* A, int16_t* K, + size_t order) { size_t i, j; // Auto-correlation coefficients in high precision @@ -43,16 +45,17 @@ int16_t WebRtcSpl_LevinsonDurbin(const int32_t* R, int16_t* A, int16_t* K, for (i = 0; i <= order; ++i) { - temp1W32 = WEBRTC_SPL_LSHIFT_W32(R[i], norm); + temp1W32 = R[i] * (1 << norm); + // UBSan: 12 * 268435456 cannot be represented in type 'int' + // Put R in hi and low format R_hi[i] = (int16_t)(temp1W32 >> 16); - R_low[i] = (int16_t)((temp1W32 - ((int32_t)R_hi[i] << 16)) >> 1); + R_low[i] = (int16_t)((temp1W32 - ((int32_t)R_hi[i] * 65536)) >> 1); } // K = A[1] = -R[1] / R[0] - temp2W32 = WEBRTC_SPL_LSHIFT_W32((int32_t)R_hi[1],16) - + WEBRTC_SPL_LSHIFT_W32((int32_t)R_low[1],1); // R[1] in Q31 + temp2W32 = R[1] * (1 << norm); // R[1] in Q31 temp3W32 = WEBRTC_SPL_ABS_W32(temp2W32); // abs R[1] temp1W32 = WebRtcSpl_DivW32HiLow(temp3W32, R_hi[0], R_low[0]); // abs(R[1])/R[0] in Q31 // Put back the sign on R[1] @@ -63,7 +66,7 @@ int16_t WebRtcSpl_LevinsonDurbin(const int32_t* R, int16_t* A, int16_t* K, // Put K in hi and low format K_hi = (int16_t)(temp1W32 >> 16); - K_low = (int16_t)((temp1W32 - ((int32_t)K_hi << 16)) >> 1); + K_low = (int16_t)((temp1W32 - ((int32_t)K_hi * 65536)) >> 1); // Store first reflection coefficient K[0] = K_hi; @@ -72,11 +75,11 @@ int16_t WebRtcSpl_LevinsonDurbin(const int32_t* R, int16_t* A, int16_t* K, // Put A[1] in hi and low format A_hi[1] = (int16_t)(temp1W32 >> 16); - A_low[1] = (int16_t)((temp1W32 - ((int32_t)A_hi[1] << 16)) >> 1); + A_low[1] = (int16_t)((temp1W32 - ((int32_t)A_hi[1] * 65536)) >> 1); // Alpha = R[0] * (1-K^2) - temp1W32 = ((K_hi * K_low >> 14) + K_hi * K_hi) << 1; // = k^2 in Q31 + temp1W32 = ((K_hi * K_low >> 14) + K_hi * K_hi) * 2; // = k^2 in Q31 temp1W32 = WEBRTC_SPL_ABS_W32(temp1W32); // Guard against <0 temp1W32 = (int32_t)0x7fffffffL - temp1W32; // temp1W32 = (1 - K[0]*K[0]) in Q31 @@ -112,14 +115,14 @@ int16_t WebRtcSpl_LevinsonDurbin(const int32_t* R, int16_t* A, int16_t* K, for (j = 1; j < i; j++) { // temp1W32 is in Q31 - temp1W32 += (R_hi[j] * A_hi[i - j] << 1) + + temp1W32 += (R_hi[j] * A_hi[i - j] * 2) + (((R_hi[j] * A_low[i - j] >> 15) + - (R_low[j] * A_hi[i - j] >> 15)) << 1); + (R_low[j] * A_hi[i - j] >> 15)) * 2); } - temp1W32 = WEBRTC_SPL_LSHIFT_W32(temp1W32, 4); - temp1W32 += (WEBRTC_SPL_LSHIFT_W32((int32_t)R_hi[i], 16) - + WEBRTC_SPL_LSHIFT_W32((int32_t)R_low[i], 1)); + temp1W32 = temp1W32 * 16; + temp1W32 += ((int32_t)R_hi[i] * 65536) + + WEBRTC_SPL_LSHIFT_W32((int32_t)R_low[i], 1); // K = -temp1W32 / Alpha temp2W32 = WEBRTC_SPL_ABS_W32(temp1W32); // abs(temp1W32) @@ -135,7 +138,7 @@ int16_t WebRtcSpl_LevinsonDurbin(const int32_t* R, int16_t* A, int16_t* K, norm = WebRtcSpl_NormW32(temp3W32); if ((Alpha_exp <= norm) || (temp3W32 == 0)) { - temp3W32 = WEBRTC_SPL_LSHIFT_W32(temp3W32, Alpha_exp); + temp3W32 = temp3W32 * (1 << Alpha_exp); } else { if (temp3W32 > 0) @@ -149,7 +152,7 @@ int16_t WebRtcSpl_LevinsonDurbin(const int32_t* R, int16_t* A, int16_t* K, // Put K on hi and low format K_hi = (int16_t)(temp3W32 >> 16); - K_low = (int16_t)((temp3W32 - ((int32_t)K_hi << 16)) >> 1); + K_low = (int16_t)((temp3W32 - ((int32_t)K_hi * 65536)) >> 1); // Store Reflection coefficient in Q15 K[i - 1] = K_hi; @@ -171,17 +174,17 @@ int16_t WebRtcSpl_LevinsonDurbin(const int32_t* R, int16_t* A, int16_t* K, for (j = 1; j < i; j++) { // temp1W32 = A[j] in Q27 - temp1W32 = WEBRTC_SPL_LSHIFT_W32((int32_t)A_hi[j],16) + temp1W32 = (int32_t)A_hi[j] * 65536 + WEBRTC_SPL_LSHIFT_W32((int32_t)A_low[j],1); // temp1W32 += K*A[i-j] in Q27 temp1W32 += (K_hi * A_hi[i - j] + (K_hi * A_low[i - j] >> 15) + - (K_low * A_hi[i - j] >> 15)) << 1; + (K_low * A_hi[i - j] >> 15)) * 2; // Put Anew in hi and low format A_upd_hi[j] = (int16_t)(temp1W32 >> 16); A_upd_low[j] = (int16_t)( - (temp1W32 - ((int32_t)A_upd_hi[j] << 16)) >> 1); + (temp1W32 - ((int32_t)A_upd_hi[j] * 65536)) >> 1); } // temp3W32 = K in Q27 (Convert from Q31 to Q27) @@ -190,11 +193,11 @@ int16_t WebRtcSpl_LevinsonDurbin(const int32_t* R, int16_t* A, int16_t* K, // Store Anew in hi and low format A_upd_hi[i] = (int16_t)(temp3W32 >> 16); A_upd_low[i] = (int16_t)( - (temp3W32 - ((int32_t)A_upd_hi[i] << 16)) >> 1); + (temp3W32 - ((int32_t)A_upd_hi[i] * 65536)) >> 1); // Alpha = Alpha * (1-K^2) - temp1W32 = ((K_hi * K_low >> 14) + K_hi * K_hi) << 1; // K*K in Q31 + temp1W32 = ((K_hi * K_low >> 14) + K_hi * K_hi) * 2; // K*K in Q31 temp1W32 = WEBRTC_SPL_ABS_W32(temp1W32); // Guard against <0 temp1W32 = (int32_t)0x7fffffffL - temp1W32; // 1 - K*K in Q31 @@ -237,10 +240,10 @@ int16_t WebRtcSpl_LevinsonDurbin(const int32_t* R, int16_t* A, int16_t* K, for (i = 1; i <= order; i++) { // temp1W32 in Q27 - temp1W32 = WEBRTC_SPL_LSHIFT_W32((int32_t)A_hi[i], 16) + temp1W32 = (int32_t)A_hi[i] * 65536 + WEBRTC_SPL_LSHIFT_W32((int32_t)A_low[i], 1); // Round and store upper word - A[i] = (int16_t)(((temp1W32 << 1) + 32768) >> 16); + A[i] = (int16_t)(((temp1W32 * 2) + 32768) >> 16); } return 1; // Stable filters } diff --git a/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c b/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c index edcebd4..7a5e251 100644 --- a/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c +++ b/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c @@ -15,7 +15,7 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" #define SPL_LPC_TO_REFL_COEF_MAX_AR_MODEL_ORDER 50 diff --git a/webrtc/common_audio/signal_processing/min_max_operations.c b/webrtc/common_audio/signal_processing/min_max_operations.c index 4a962f8..d249a02 100644 --- a/webrtc/common_audio/signal_processing/min_max_operations.c +++ b/webrtc/common_audio/signal_processing/min_max_operations.c @@ -24,10 +24,10 @@ * */ -#include #include -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" // TODO(bjorn/kma): Consolidate function pairs (e.g. combine // WebRtcSpl_MaxAbsValueW16C and WebRtcSpl_MaxAbsIndexW16 into a single one.) @@ -38,7 +38,7 @@ int16_t WebRtcSpl_MaxAbsValueW16C(const int16_t* vector, size_t length) { size_t i = 0; int absolute = 0, maximum = 0; - assert(length > 0); + RTC_DCHECK_GT(length, 0); for (i = 0; i < length; i++) { absolute = abs((int)vector[i]); @@ -64,7 +64,7 @@ int32_t WebRtcSpl_MaxAbsValueW32C(const int32_t* vector, size_t length) { uint32_t absolute = 0, maximum = 0; size_t i = 0; - assert(length > 0); + RTC_DCHECK_GT(length, 0); for (i = 0; i < length; i++) { absolute = abs((int)vector[i]); @@ -83,7 +83,7 @@ int16_t WebRtcSpl_MaxValueW16C(const int16_t* vector, size_t length) { int16_t maximum = WEBRTC_SPL_WORD16_MIN; size_t i = 0; - assert(length > 0); + RTC_DCHECK_GT(length, 0); for (i = 0; i < length; i++) { if (vector[i] > maximum) @@ -97,7 +97,7 @@ int32_t WebRtcSpl_MaxValueW32C(const int32_t* vector, size_t length) { int32_t maximum = WEBRTC_SPL_WORD32_MIN; size_t i = 0; - assert(length > 0); + RTC_DCHECK_GT(length, 0); for (i = 0; i < length; i++) { if (vector[i] > maximum) @@ -111,7 +111,7 @@ int16_t WebRtcSpl_MinValueW16C(const int16_t* vector, size_t length) { int16_t minimum = WEBRTC_SPL_WORD16_MAX; size_t i = 0; - assert(length > 0); + RTC_DCHECK_GT(length, 0); for (i = 0; i < length; i++) { if (vector[i] < minimum) @@ -125,7 +125,7 @@ int32_t WebRtcSpl_MinValueW32C(const int32_t* vector, size_t length) { int32_t minimum = WEBRTC_SPL_WORD32_MAX; size_t i = 0; - assert(length > 0); + RTC_DCHECK_GT(length, 0); for (i = 0; i < length; i++) { if (vector[i] < minimum) @@ -141,7 +141,7 @@ size_t WebRtcSpl_MaxAbsIndexW16(const int16_t* vector, size_t length) { size_t i = 0, index = 0; int absolute = 0, maximum = 0; - assert(length > 0); + RTC_DCHECK_GT(length, 0); for (i = 0; i < length; i++) { absolute = abs((int)vector[i]); @@ -160,7 +160,7 @@ size_t WebRtcSpl_MaxIndexW16(const int16_t* vector, size_t length) { size_t i = 0, index = 0; int16_t maximum = WEBRTC_SPL_WORD16_MIN; - assert(length > 0); + RTC_DCHECK_GT(length, 0); for (i = 0; i < length; i++) { if (vector[i] > maximum) { @@ -177,7 +177,7 @@ size_t WebRtcSpl_MaxIndexW32(const int32_t* vector, size_t length) { size_t i = 0, index = 0; int32_t maximum = WEBRTC_SPL_WORD32_MIN; - assert(length > 0); + RTC_DCHECK_GT(length, 0); for (i = 0; i < length; i++) { if (vector[i] > maximum) { @@ -194,7 +194,7 @@ size_t WebRtcSpl_MinIndexW16(const int16_t* vector, size_t length) { size_t i = 0, index = 0; int16_t minimum = WEBRTC_SPL_WORD16_MAX; - assert(length > 0); + RTC_DCHECK_GT(length, 0); for (i = 0; i < length; i++) { if (vector[i] < minimum) { @@ -211,7 +211,7 @@ size_t WebRtcSpl_MinIndexW32(const int32_t* vector, size_t length) { size_t i = 0, index = 0; int32_t minimum = WEBRTC_SPL_WORD32_MAX; - assert(length > 0); + RTC_DCHECK_GT(length, 0); for (i = 0; i < length; i++) { if (vector[i] < minimum) { diff --git a/webrtc/common_audio/signal_processing/min_max_operations_mips.c b/webrtc/common_audio/signal_processing/min_max_operations_mips.c index 28de45b..8a7fc65 100644 --- a/webrtc/common_audio/signal_processing/min_max_operations_mips.c +++ b/webrtc/common_audio/signal_processing/min_max_operations_mips.c @@ -16,9 +16,8 @@ * */ -#include - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" // Maximum absolute value of word16 vector. int16_t WebRtcSpl_MaxAbsValueW16_mips(const int16_t* vector, size_t length) { @@ -26,7 +25,7 @@ int16_t WebRtcSpl_MaxAbsValueW16_mips(const int16_t* vector, size_t length) { int32_t tmp32_0, tmp32_1, tmp32_2, tmp32_3; size_t i, loop_size; - assert(length > 0); + RTC_DCHECK_GT(length, 0); #if defined(MIPS_DSP_R1) const int32_t* tmpvec32 = (int32_t*)vector; @@ -230,7 +229,7 @@ int32_t WebRtcSpl_MaxAbsValueW32_mips(const int32_t* vector, size_t length) { uint32_t absolute = 0, maximum = 0; int tmp1 = 0, max_value = 0x7fffffff; - assert(length > 0); + RTC_DCHECK_GT(length, 0); __asm__ volatile ( ".set push \n\t" @@ -264,7 +263,7 @@ int16_t WebRtcSpl_MaxValueW16_mips(const int16_t* vector, size_t length) { int tmp1; int16_t value; - assert(length > 0); + RTC_DCHECK_GT(length, 0); __asm__ volatile ( ".set push \n\t" @@ -292,7 +291,7 @@ int32_t WebRtcSpl_MaxValueW32_mips(const int32_t* vector, size_t length) { int32_t maximum = WEBRTC_SPL_WORD32_MIN; int tmp1, value; - assert(length > 0); + RTC_DCHECK_GT(length, 0); __asm__ volatile ( ".set push \n\t" @@ -322,7 +321,7 @@ int16_t WebRtcSpl_MinValueW16_mips(const int16_t* vector, size_t length) { int tmp1; int16_t value; - assert(length > 0); + RTC_DCHECK_GT(length, 0); __asm__ volatile ( ".set push \n\t" @@ -351,7 +350,7 @@ int32_t WebRtcSpl_MinValueW32_mips(const int32_t* vector, size_t length) { int32_t minimum = WEBRTC_SPL_WORD32_MAX; int tmp1, value; - assert(length > 0); + RTC_DCHECK_GT(length, 0); __asm__ volatile ( ".set push \n\t" diff --git a/webrtc/common_audio/signal_processing/min_max_operations_neon.c b/webrtc/common_audio/signal_processing/min_max_operations_neon.c index 6fbbf94..53217df 100644 --- a/webrtc/common_audio/signal_processing/min_max_operations_neon.c +++ b/webrtc/common_audio/signal_processing/min_max_operations_neon.c @@ -9,16 +9,16 @@ */ #include -#include #include -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" // Maximum absolute value of word16 vector. C version for generic platforms. int16_t WebRtcSpl_MaxAbsValueW16Neon(const int16_t* vector, size_t length) { int absolute = 0, maximum = 0; - assert(length > 0); + RTC_DCHECK_GT(length, 0); const int16_t* p_start = vector; size_t rest = length & 7; @@ -76,7 +76,7 @@ int32_t WebRtcSpl_MaxAbsValueW32Neon(const int32_t* vector, size_t length) { size_t i = 0; size_t residual = length & 0x7; - assert(length > 0); + RTC_DCHECK_GT(length, 0); const int32_t* p_start = vector; uint32x4_t max32x4_0 = vdupq_n_u32(0); @@ -128,7 +128,7 @@ int16_t WebRtcSpl_MaxValueW16Neon(const int16_t* vector, size_t length) { size_t i = 0; size_t residual = length & 0x7; - assert(length > 0); + RTC_DCHECK_GT(length, 0); const int16_t* p_start = vector; int16x8_t max16x8 = vdupq_n_s16(WEBRTC_SPL_WORD16_MIN); @@ -166,7 +166,7 @@ int32_t WebRtcSpl_MaxValueW32Neon(const int32_t* vector, size_t length) { size_t i = 0; size_t residual = length & 0x7; - assert(length > 0); + RTC_DCHECK_GT(length, 0); const int32_t* p_start = vector; int32x4_t max32x4_0 = vdupq_n_s32(WEBRTC_SPL_WORD32_MIN); @@ -208,7 +208,7 @@ int16_t WebRtcSpl_MinValueW16Neon(const int16_t* vector, size_t length) { size_t i = 0; size_t residual = length & 0x7; - assert(length > 0); + RTC_DCHECK_GT(length, 0); const int16_t* p_start = vector; int16x8_t min16x8 = vdupq_n_s16(WEBRTC_SPL_WORD16_MAX); @@ -246,7 +246,7 @@ int32_t WebRtcSpl_MinValueW32Neon(const int32_t* vector, size_t length) { size_t i = 0; size_t residual = length & 0x7; - assert(length > 0); + RTC_DCHECK_GT(length, 0); const int32_t* p_start = vector; int32x4_t min32x4_0 = vdupq_n_s32(WEBRTC_SPL_WORD32_MAX); diff --git a/webrtc/common_audio/signal_processing/randomization_functions.c b/webrtc/common_audio/signal_processing/randomization_functions.c index 73f2409..a445c57 100644 --- a/webrtc/common_audio/signal_processing/randomization_functions.c +++ b/webrtc/common_audio/signal_processing/randomization_functions.c @@ -19,7 +19,7 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" static const uint32_t kMaxSeedUsed = 0x80000000; diff --git a/webrtc/common_audio/signal_processing/real_fft.c b/webrtc/common_audio/signal_processing/real_fft.c index 92daae4..780e517 100644 --- a/webrtc/common_audio/signal_processing/real_fft.c +++ b/webrtc/common_audio/signal_processing/real_fft.c @@ -8,11 +8,11 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/signal_processing/include/real_fft.h" +#include "common_audio/signal_processing/include/real_fft.h" #include -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" struct RealFFT { int order; diff --git a/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c b/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c index 06a29b6..b0858b2 100644 --- a/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c +++ b/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c @@ -15,7 +15,7 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" void WebRtcSpl_ReflCoefToLpc(const int16_t *k, int use_order, int16_t *a) { diff --git a/webrtc/common_audio/signal_processing/resample.c b/webrtc/common_audio/signal_processing/resample.c index 45fe52a..d4b2736 100644 --- a/webrtc/common_audio/signal_processing/resample.c +++ b/webrtc/common_audio/signal_processing/resample.c @@ -15,8 +15,8 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/common_audio/signal_processing/resample_by_2_internal.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/resample_by_2_internal.h" // Declaration of internally used functions static void WebRtcSpl_32khzTo22khzIntToShort(const int32_t *In, int16_t *Out, diff --git a/webrtc/common_audio/signal_processing/resample_48khz.c b/webrtc/common_audio/signal_processing/resample_48khz.c index 2220cc3..8518e7b 100644 --- a/webrtc/common_audio/signal_processing/resample_48khz.c +++ b/webrtc/common_audio/signal_processing/resample_48khz.c @@ -16,8 +16,8 @@ */ #include -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/common_audio/signal_processing/resample_by_2_internal.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/resample_by_2_internal.h" //////////////////////////// ///// 48 kHz -> 16 kHz ///// diff --git a/webrtc/common_audio/signal_processing/resample_by_2.c b/webrtc/common_audio/signal_processing/resample_by_2.c index dcba82e..73e1950 100644 --- a/webrtc/common_audio/signal_processing/resample_by_2.c +++ b/webrtc/common_audio/signal_processing/resample_by_2.c @@ -15,7 +15,7 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" #ifdef WEBRTC_ARCH_ARM_V7 @@ -83,7 +83,7 @@ void WebRtcSpl_DownsampleBy2(const int16_t* in, size_t len, for (i = (len >> 1); i > 0; i--) { // lower allpass filter - in32 = (int32_t)(*in++) << 10; + in32 = (int32_t)(*in++) * (1 << 10); diff = in32 - state1; tmp1 = MUL_ACCUM_1(kResampleAllpass2[0], diff, state0); state0 = in32; @@ -95,7 +95,7 @@ void WebRtcSpl_DownsampleBy2(const int16_t* in, size_t len, state2 = tmp2; // upper allpass filter - in32 = (int32_t)(*in++) << 10; + in32 = (int32_t)(*in++) * (1 << 10); diff = in32 - state5; tmp1 = MUL_ACCUM_1(kResampleAllpass1[0], diff, state4); state4 = in32; @@ -141,7 +141,7 @@ void WebRtcSpl_UpsampleBy2(const int16_t* in, size_t len, for (i = len; i > 0; i--) { // lower allpass filter - in32 = (int32_t)(*in++) << 10; + in32 = (int32_t)(*in++) * (1 << 10); diff = in32 - state1; tmp1 = MUL_ACCUM_1(kResampleAllpass1[0], diff, state0); state0 = in32; diff --git a/webrtc/common_audio/signal_processing/resample_by_2_internal.c b/webrtc/common_audio/signal_processing/resample_by_2_internal.c index 085069c..99592b2 100644 --- a/webrtc/common_audio/signal_processing/resample_by_2_internal.c +++ b/webrtc/common_audio/signal_processing/resample_by_2_internal.c @@ -14,7 +14,8 @@ * */ -#include "webrtc/common_audio/signal_processing/resample_by_2_internal.h" +#include "common_audio/signal_processing/resample_by_2_internal.h" +#include "rtc_base/sanitizer.h" // allpass filter coefficients. static const int16_t kResampleAllpass[2][3] = { @@ -28,8 +29,9 @@ static const int16_t kResampleAllpass[2][3] = { // output: int16_t (saturated) (of length len/2) // state: filter state array; length = 8 -void WebRtcSpl_DownBy2IntToShort(int32_t *in, int32_t len, int16_t *out, - int32_t *state) +void RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/5486 +WebRtcSpl_DownBy2IntToShort(int32_t *in, int32_t len, int16_t *out, + int32_t *state) { int32_t tmp0, tmp1, diff; int32_t i; @@ -41,6 +43,8 @@ void WebRtcSpl_DownBy2IntToShort(int32_t *in, int32_t len, int16_t *out, { tmp0 = in[i << 1]; diff = tmp0 - state[1]; + // UBSan: -1771017321 - 999586185 cannot be represented in type 'int' + // scale down and round diff = (diff + (1 << 13)) >> 14; tmp1 = state[0] + diff * kResampleAllpass[1][0]; @@ -121,10 +125,11 @@ void WebRtcSpl_DownBy2IntToShort(int32_t *in, int32_t len, int16_t *out, // output: int32_t (shifted 15 positions to the left, + offset 16384) (of length len/2) // state: filter state array; length = 8 -void WebRtcSpl_DownBy2ShortToInt(const int16_t *in, - int32_t len, - int32_t *out, - int32_t *state) +void RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/5486 +WebRtcSpl_DownBy2ShortToInt(const int16_t *in, + int32_t len, + int32_t *out, + int32_t *state) { int32_t tmp0, tmp1, diff; int32_t i; @@ -141,6 +146,8 @@ void WebRtcSpl_DownBy2ShortToInt(const int16_t *in, tmp1 = state[0] + diff * kResampleAllpass[1][0]; state[0] = tmp0; diff = tmp1 - state[2]; + // UBSan: -1379909682 - 834099714 cannot be represented in type 'int' + // scale down and truncate diff = diff >> 14; if (diff < 0) @@ -549,8 +556,9 @@ void WebRtcSpl_LPBy2ShortToInt(const int16_t* in, int32_t len, int32_t* out, // input: int32_t (shifted 15 positions to the left, + offset 16384) // output: int32_t (normalized, not saturated) // state: filter state array; length = 8 -void WebRtcSpl_LPBy2IntToInt(const int32_t* in, int32_t len, int32_t* out, - int32_t* state) +void RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/5486 +WebRtcSpl_LPBy2IntToInt(const int32_t* in, int32_t len, int32_t* out, + int32_t* state) { int32_t tmp0, tmp1, diff; int32_t i; @@ -594,6 +602,8 @@ void WebRtcSpl_LPBy2IntToInt(const int32_t* in, int32_t len, int32_t* out, { tmp0 = in[i << 1]; diff = tmp0 - state[5]; + // UBSan: -794814117 - 1566149201 cannot be represented in type 'int' + // scale down and round diff = (diff + (1 << 13)) >> 14; tmp1 = state[4] + diff * kResampleAllpass[0][0]; diff --git a/webrtc/common_audio/signal_processing/resample_by_2_internal.h b/webrtc/common_audio/signal_processing/resample_by_2_internal.h index 5c9533e..145395a 100644 --- a/webrtc/common_audio/signal_processing/resample_by_2_internal.h +++ b/webrtc/common_audio/signal_processing/resample_by_2_internal.h @@ -8,40 +8,53 @@ * be found in the AUTHORS file in the root of the source tree. */ - /* * This header file contains some internal resampling functions. * */ -#ifndef WEBRTC_SPL_RESAMPLE_BY_2_INTERNAL_H_ -#define WEBRTC_SPL_RESAMPLE_BY_2_INTERNAL_H_ +#ifndef COMMON_AUDIO_SIGNAL_PROCESSING_RESAMPLE_BY_2_INTERNAL_H_ +#define COMMON_AUDIO_SIGNAL_PROCESSING_RESAMPLE_BY_2_INTERNAL_H_ -#include "webrtc/typedefs.h" +#include /******************************************************************* * resample_by_2_fast.c * Functions for internal use in the other resample functions ******************************************************************/ -void WebRtcSpl_DownBy2IntToShort(int32_t *in, int32_t len, int16_t *out, - int32_t *state); +void WebRtcSpl_DownBy2IntToShort(int32_t* in, + int32_t len, + int16_t* out, + int32_t* state); -void WebRtcSpl_DownBy2ShortToInt(const int16_t *in, int32_t len, - int32_t *out, int32_t *state); +void WebRtcSpl_DownBy2ShortToInt(const int16_t* in, + int32_t len, + int32_t* out, + int32_t* state); -void WebRtcSpl_UpBy2ShortToInt(const int16_t *in, int32_t len, - int32_t *out, int32_t *state); +void WebRtcSpl_UpBy2ShortToInt(const int16_t* in, + int32_t len, + int32_t* out, + int32_t* state); -void WebRtcSpl_UpBy2IntToInt(const int32_t *in, int32_t len, int32_t *out, - int32_t *state); - -void WebRtcSpl_UpBy2IntToShort(const int32_t *in, int32_t len, - int16_t *out, int32_t *state); - -void WebRtcSpl_LPBy2ShortToInt(const int16_t* in, int32_t len, - int32_t* out, int32_t* state); - -void WebRtcSpl_LPBy2IntToInt(const int32_t* in, int32_t len, int32_t* out, +void WebRtcSpl_UpBy2IntToInt(const int32_t* in, + int32_t len, + int32_t* out, int32_t* state); -#endif // WEBRTC_SPL_RESAMPLE_BY_2_INTERNAL_H_ +void WebRtcSpl_UpBy2IntToShort(const int32_t* in, + int32_t len, + int16_t* out, + int32_t* state); + +void WebRtcSpl_LPBy2ShortToInt(const int16_t* in, + int32_t len, + int32_t* out, + int32_t* state); + +void WebRtcSpl_LPBy2IntToInt(const int32_t* in, + int32_t len, + int32_t* out, + int32_t* state); + +#endif // COMMON_AUDIO_SIGNAL_PROCESSING_RESAMPLE_BY_2_INTERNAL_H_ diff --git a/webrtc/common_audio/signal_processing/resample_by_2_mips.c b/webrtc/common_audio/signal_processing/resample_by_2_mips.c index ec5fc8b..f41bab7 100644 --- a/webrtc/common_audio/signal_processing/resample_by_2_mips.c +++ b/webrtc/common_audio/signal_processing/resample_by_2_mips.c @@ -17,11 +17,13 @@ #if defined(MIPS32_LE) -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#if !defined(MIPS_DSP_R2_LE) // allpass filter coefficients. static const uint16_t kResampleAllpass1[3] = {3284, 24441, 49528}; static const uint16_t kResampleAllpass2[3] = {12199, 37471, 60255}; +#endif // Multiply a 32-bit value with a 16-bit value and accumulate to another input: #define MUL_ACCUM_1(a, b, c) WEBRTC_SPL_SCALEDIFF32(a, b, c) diff --git a/webrtc/common_audio/signal_processing/resample_fractional.c b/webrtc/common_audio/signal_processing/resample_fractional.c index 6409fba..9ffe0ac 100644 --- a/webrtc/common_audio/signal_processing/resample_fractional.c +++ b/webrtc/common_audio/signal_processing/resample_fractional.c @@ -15,7 +15,7 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" // interpolation coefficients static const int16_t kCoefficients48To32[2][8] = { diff --git a/webrtc/common_audio/signal_processing/spl_init.c b/webrtc/common_audio/signal_processing/spl_init.c index fdab038..cf37d47 100644 --- a/webrtc/common_audio/signal_processing/spl_init.c +++ b/webrtc/common_audio/signal_processing/spl_init.c @@ -8,133 +8,62 @@ * be found in the AUTHORS file in the root of the source tree. */ -/* The global function contained in this file initializes SPL function - * pointers, currently only for ARM platforms. - * - * Some code came from common/rtcd.c in the WebM project. - */ +// Some code came from common/rtcd.c in the WebM project. -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/system_wrappers/include/cpu_features_wrapper.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" -/* Declare function pointers. */ -MaxAbsValueW16 WebRtcSpl_MaxAbsValueW16; -MaxAbsValueW32 WebRtcSpl_MaxAbsValueW32; -MaxValueW16 WebRtcSpl_MaxValueW16; -MaxValueW32 WebRtcSpl_MaxValueW32; -MinValueW16 WebRtcSpl_MinValueW16; -MinValueW32 WebRtcSpl_MinValueW32; -CrossCorrelation WebRtcSpl_CrossCorrelation; -DownsampleFast WebRtcSpl_DownsampleFast; -ScaleAndAddVectorsWithRound WebRtcSpl_ScaleAndAddVectorsWithRound; +// TODO(bugs.webrtc.org/9553): These function pointers are useless. Refactor +// things so that we simply have a bunch of regular functions with different +// implementations for different platforms. -#if (defined(WEBRTC_DETECT_NEON) || !defined(WEBRTC_HAS_NEON)) && \ - !defined(MIPS32_LE) -/* Initialize function pointers to the generic C version. */ -static void InitPointersToC() { - WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16C; - WebRtcSpl_MaxAbsValueW32 = WebRtcSpl_MaxAbsValueW32C; - WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16C; - WebRtcSpl_MaxValueW32 = WebRtcSpl_MaxValueW32C; - WebRtcSpl_MinValueW16 = WebRtcSpl_MinValueW16C; - WebRtcSpl_MinValueW32 = WebRtcSpl_MinValueW32C; - WebRtcSpl_CrossCorrelation = WebRtcSpl_CrossCorrelationC; - WebRtcSpl_DownsampleFast = WebRtcSpl_DownsampleFastC; - WebRtcSpl_ScaleAndAddVectorsWithRound = - WebRtcSpl_ScaleAndAddVectorsWithRoundC; -} -#endif +#if defined(WEBRTC_HAS_NEON) -#if defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON) -/* Initialize function pointers to the Neon version. */ -static void InitPointersToNeon() { - WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16Neon; - WebRtcSpl_MaxAbsValueW32 = WebRtcSpl_MaxAbsValueW32Neon; - WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16Neon; - WebRtcSpl_MaxValueW32 = WebRtcSpl_MaxValueW32Neon; - WebRtcSpl_MinValueW16 = WebRtcSpl_MinValueW16Neon; - WebRtcSpl_MinValueW32 = WebRtcSpl_MinValueW32Neon; - WebRtcSpl_CrossCorrelation = WebRtcSpl_CrossCorrelationNeon; - WebRtcSpl_DownsampleFast = WebRtcSpl_DownsampleFastNeon; - WebRtcSpl_ScaleAndAddVectorsWithRound = - WebRtcSpl_ScaleAndAddVectorsWithRoundC; -} -#endif +const MaxAbsValueW16 WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16Neon; +const MaxAbsValueW32 WebRtcSpl_MaxAbsValueW32 = WebRtcSpl_MaxAbsValueW32Neon; +const MaxValueW16 WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16Neon; +const MaxValueW32 WebRtcSpl_MaxValueW32 = WebRtcSpl_MaxValueW32Neon; +const MinValueW16 WebRtcSpl_MinValueW16 = WebRtcSpl_MinValueW16Neon; +const MinValueW32 WebRtcSpl_MinValueW32 = WebRtcSpl_MinValueW32Neon; +const CrossCorrelation WebRtcSpl_CrossCorrelation = + WebRtcSpl_CrossCorrelationNeon; +const DownsampleFast WebRtcSpl_DownsampleFast = WebRtcSpl_DownsampleFastNeon; +const ScaleAndAddVectorsWithRound WebRtcSpl_ScaleAndAddVectorsWithRound = + WebRtcSpl_ScaleAndAddVectorsWithRoundC; -#if defined(MIPS32_LE) -/* Initialize function pointers to the MIPS version. */ -static void InitPointersToMIPS() { - WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16_mips; - WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16_mips; - WebRtcSpl_MaxValueW32 = WebRtcSpl_MaxValueW32_mips; - WebRtcSpl_MinValueW16 = WebRtcSpl_MinValueW16_mips; - WebRtcSpl_MinValueW32 = WebRtcSpl_MinValueW32_mips; - WebRtcSpl_CrossCorrelation = WebRtcSpl_CrossCorrelation_mips; - WebRtcSpl_DownsampleFast = WebRtcSpl_DownsampleFast_mips; -#if defined(MIPS_DSP_R1_LE) - WebRtcSpl_MaxAbsValueW32 = WebRtcSpl_MaxAbsValueW32_mips; - WebRtcSpl_ScaleAndAddVectorsWithRound = - WebRtcSpl_ScaleAndAddVectorsWithRound_mips; -#else - WebRtcSpl_MaxAbsValueW32 = WebRtcSpl_MaxAbsValueW32C; - WebRtcSpl_ScaleAndAddVectorsWithRound = - WebRtcSpl_ScaleAndAddVectorsWithRoundC; -#endif -} -#endif - -static void InitFunctionPointers(void) { -#if defined(WEBRTC_DETECT_NEON) - if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) { - InitPointersToNeon(); - } else { - InitPointersToC(); - } -#elif defined(WEBRTC_HAS_NEON) - InitPointersToNeon(); #elif defined(MIPS32_LE) - InitPointersToMIPS(); + +const MaxAbsValueW16 WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16_mips; +const MaxAbsValueW32 WebRtcSpl_MaxAbsValueW32 = +#ifdef MIPS_DSP_R1_LE + WebRtcSpl_MaxAbsValueW32_mips; #else - InitPointersToC(); -#endif /* WEBRTC_DETECT_NEON */ -} + WebRtcSpl_MaxAbsValueW32C; +#endif +const MaxValueW16 WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16_mips; +const MaxValueW32 WebRtcSpl_MaxValueW32 = WebRtcSpl_MaxValueW32_mips; +const MinValueW16 WebRtcSpl_MinValueW16 = WebRtcSpl_MinValueW16_mips; +const MinValueW32 WebRtcSpl_MinValueW32 = WebRtcSpl_MinValueW32_mips; +const CrossCorrelation WebRtcSpl_CrossCorrelation = + WebRtcSpl_CrossCorrelation_mips; +const DownsampleFast WebRtcSpl_DownsampleFast = WebRtcSpl_DownsampleFast_mips; +const ScaleAndAddVectorsWithRound WebRtcSpl_ScaleAndAddVectorsWithRound = +#ifdef MIPS_DSP_R1_LE + WebRtcSpl_ScaleAndAddVectorsWithRound_mips; +#else + WebRtcSpl_ScaleAndAddVectorsWithRoundC; +#endif -#if defined(WEBRTC_POSIX) -#include +#else -static void once(void (*func)(void)) { - static pthread_once_t lock = PTHREAD_ONCE_INIT; - pthread_once(&lock, func); -} +const MaxAbsValueW16 WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16C; +const MaxAbsValueW32 WebRtcSpl_MaxAbsValueW32 = WebRtcSpl_MaxAbsValueW32C; +const MaxValueW16 WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16C; +const MaxValueW32 WebRtcSpl_MaxValueW32 = WebRtcSpl_MaxValueW32C; +const MinValueW16 WebRtcSpl_MinValueW16 = WebRtcSpl_MinValueW16C; +const MinValueW32 WebRtcSpl_MinValueW32 = WebRtcSpl_MinValueW32C; +const CrossCorrelation WebRtcSpl_CrossCorrelation = WebRtcSpl_CrossCorrelationC; +const DownsampleFast WebRtcSpl_DownsampleFast = WebRtcSpl_DownsampleFastC; +const ScaleAndAddVectorsWithRound WebRtcSpl_ScaleAndAddVectorsWithRound = + WebRtcSpl_ScaleAndAddVectorsWithRoundC; -#elif defined(_WIN32) -#include - -static void once(void (*func)(void)) { - /* Didn't use InitializeCriticalSection() since there's no race-free context - * in which to execute it. - * - * TODO(kma): Change to different implementation (e.g. - * InterlockedCompareExchangePointer) to avoid issues similar to - * http://code.google.com/p/webm/issues/detail?id=467. - */ - static CRITICAL_SECTION lock = {(void *)((size_t)-1), -1, 0, 0, 0, 0}; - static int done = 0; - - EnterCriticalSection(&lock); - if (!done) { - func(); - done = 1; - } - LeaveCriticalSection(&lock); -} - -/* There's no fallback version as an #else block here to ensure thread safety. - * In case of neither pthread for WEBRTC_POSIX nor _WIN32 is present, build - * system should pick it up. - */ -#endif /* WEBRTC_POSIX */ - -void WebRtcSpl_Init() { - once(InitFunctionPointers); -} +#endif diff --git a/webrtc/common_audio/signal_processing/spl_inl.c b/webrtc/common_audio/signal_processing/spl_inl.c new file mode 100644 index 0000000..d09e308 --- /dev/null +++ b/webrtc/common_audio/signal_processing/spl_inl.c @@ -0,0 +1,24 @@ +/* + * 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 + +#include "common_audio/signal_processing/include/spl_inl.h" + +// Table used by WebRtcSpl_CountLeadingZeros32_NotBuiltin. For each uint32_t n +// that's a sequence of 0 bits followed by a sequence of 1 bits, the entry at +// index (n * 0x8c0b2891) >> 26 in this table gives the number of zero bits in +// n. +const int8_t kWebRtcSpl_CountLeadingZeros32_Table[64] = { + 32, 8, 17, -1, -1, 14, -1, -1, -1, 20, -1, -1, -1, 28, -1, 18, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 26, 25, 24, + 4, 11, 23, 31, 3, 7, 10, 16, 22, 30, -1, -1, 2, 6, 13, 9, + -1, 15, -1, 21, -1, 29, 19, -1, -1, -1, -1, -1, 1, 27, 5, 12, +}; diff --git a/webrtc/common_audio/signal_processing/spl_sqrt.c b/webrtc/common_audio/signal_processing/spl_sqrt.c index 24db4f8..cf9448a 100644 --- a/webrtc/common_audio/signal_processing/spl_sqrt.c +++ b/webrtc/common_audio/signal_processing/spl_sqrt.c @@ -15,9 +15,8 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" - -#include +#include "rtc_base/checks.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" int32_t WebRtcSpl_SqrtLocal(int32_t in); @@ -139,8 +138,19 @@ int32_t WebRtcSpl_Sqrt(int32_t value) A = value; - if (A == 0) - return (int32_t)0; // sqrt(0) = 0 + // The convention in this function is to calculate sqrt(abs(A)). Negate the + // input if it is negative. + if (A < 0) { + if (A == WEBRTC_SPL_WORD32_MIN) { + // This number cannot be held in an int32_t after negating. + // Map it to the maximum positive value. + A = WEBRTC_SPL_WORD32_MAX; + } else { + A = -A; + } + } else if (A == 0) { + return 0; // sqrt(0) = 0 + } sh = WebRtcSpl_NormW32(A); // # shifts to normalize A A = WEBRTC_SPL_LSHIFT_W32(A, sh); // Normalize A @@ -155,7 +165,7 @@ int32_t WebRtcSpl_Sqrt(int32_t value) x_norm = (int16_t)(A >> 16); // x_norm = AH nshift = (sh / 2); - assert(nshift >= 0); + RTC_DCHECK_GE(nshift, 0); A = (int32_t)WEBRTC_SPL_LSHIFT_W32((int32_t)x_norm, 16); A = WEBRTC_SPL_ABS_W32(A); // A = abs(x_norm<<16) diff --git a/webrtc/common_audio/signal_processing/splitting_filter.c b/webrtc/common_audio/signal_processing/splitting_filter.c index 36fcf35..399433f 100644 --- a/webrtc/common_audio/signal_processing/splitting_filter.c +++ b/webrtc/common_audio/signal_processing/splitting_filter.c @@ -13,9 +13,8 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" - -#include +#include "rtc_base/checks.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" // Maximum number of samples in a low/high-band frame. enum @@ -136,14 +135,14 @@ void WebRtcSpl_AnalysisQMF(const int16_t* in_data, size_t in_data_length, int32_t filter1[kMaxBandFrameLength]; int32_t filter2[kMaxBandFrameLength]; const size_t band_length = in_data_length / 2; - assert(in_data_length % 2 == 0); - assert(band_length <= kMaxBandFrameLength); + RTC_DCHECK_EQ(0, in_data_length % 2); + RTC_DCHECK_LE(band_length, kMaxBandFrameLength); // Split even and odd samples. Also shift them to Q10. for (i = 0, k = 0; i < band_length; i++, k += 2) { - half_in2[i] = WEBRTC_SPL_LSHIFT_W32((int32_t)in_data[k], 10); - half_in1[i] = WEBRTC_SPL_LSHIFT_W32((int32_t)in_data[k + 1], 10); + half_in2[i] = ((int32_t)in_data[k]) * (1 << 10); + half_in1[i] = ((int32_t)in_data[k + 1]) * (1 << 10); } // All pass filter even and odd samples, independently. @@ -175,16 +174,16 @@ void WebRtcSpl_SynthesisQMF(const int16_t* low_band, const int16_t* high_band, int32_t filter2[kMaxBandFrameLength]; size_t i; int16_t k; - assert(band_length <= kMaxBandFrameLength); + RTC_DCHECK_LE(band_length, kMaxBandFrameLength); // Obtain the sum and difference channels out of upper and lower-band channels. // Also shift to Q10 domain. for (i = 0; i < band_length; i++) { tmp = (int32_t)low_band[i] + (int32_t)high_band[i]; - half_in1[i] = WEBRTC_SPL_LSHIFT_W32(tmp, 10); + half_in1[i] = tmp * (1 << 10); tmp = (int32_t)low_band[i] - (int32_t)high_band[i]; - half_in2[i] = WEBRTC_SPL_LSHIFT_W32(tmp, 10); + half_in2[i] = tmp * (1 << 10); } // all-pass filter the sum and difference channels diff --git a/webrtc/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c b/webrtc/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c index ff78b52..a77fd40 100644 --- a/webrtc/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c +++ b/webrtc/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c @@ -15,7 +15,7 @@ * */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" void WebRtcSpl_SqrtOfOneMinusXSquared(int16_t *xQ15, size_t vector_length, int16_t *yQ15) diff --git a/webrtc/common_audio/signal_processing/vector_scaling_operations.c b/webrtc/common_audio/signal_processing/vector_scaling_operations.c index fdefd06..7307dc7 100644 --- a/webrtc/common_audio/signal_processing/vector_scaling_operations.c +++ b/webrtc/common_audio/signal_processing/vector_scaling_operations.c @@ -20,7 +20,7 @@ * WebRtcSpl_ScaleAndAddVectorsWithRoundC() */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" void WebRtcSpl_VectorBitShiftW16(int16_t *res, size_t length, const int16_t *in, int16_t right_shifts) @@ -37,7 +37,7 @@ void WebRtcSpl_VectorBitShiftW16(int16_t *res, size_t length, { for (i = length; i > 0; i--) { - (*res++) = ((*in++) << (-right_shifts)); + (*res++) = ((*in++) * (1 << (-right_shifts))); } } } diff --git a/webrtc/common_audio/signal_processing/vector_scaling_operations_mips.c b/webrtc/common_audio/signal_processing/vector_scaling_operations_mips.c index dd73eea..ba2d26d 100644 --- a/webrtc/common_audio/signal_processing/vector_scaling_operations_mips.c +++ b/webrtc/common_audio/signal_processing/vector_scaling_operations_mips.c @@ -14,7 +14,7 @@ * WebRtcSpl_ScaleAndAddVectorsWithRound_mips() */ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" int WebRtcSpl_ScaleAndAddVectorsWithRound_mips(const int16_t* in_vector1, int16_t in_vector1_scale, diff --git a/webrtc/common_audio/smoothing_filter.cc b/webrtc/common_audio/smoothing_filter.cc new file mode 100644 index 0000000..961f4a1 --- /dev/null +++ b/webrtc/common_audio/smoothing_filter.cc @@ -0,0 +1,147 @@ +/* + * 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 "common_audio/smoothing_filter.h" + +#include + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +SmoothingFilterImpl::SmoothingFilterImpl(int init_time_ms) + : init_time_ms_(init_time_ms), + // Duing the initalization time, we use an increasing alpha. Specifically, + // alpha(n) = exp(-powf(init_factor_, n)), + // where |init_factor_| is chosen such that + // alpha(init_time_ms_) = exp(-1.0f / init_time_ms_), + init_factor_(init_time_ms_ == 0 + ? 0.0f + : powf(init_time_ms_, -1.0f / init_time_ms_)), + // |init_const_| is to a factor to help the calculation during + // initialization phase. + init_const_(init_time_ms_ == 0 + ? 0.0f + : init_time_ms_ - + powf(init_time_ms_, 1.0f - 1.0f / init_time_ms_)) { + UpdateAlpha(init_time_ms_); +} + +SmoothingFilterImpl::~SmoothingFilterImpl() = default; + +void SmoothingFilterImpl::AddSample(float sample) { + const int64_t now_ms = rtc::TimeMillis(); + + if (!init_end_time_ms_) { + // This is equivalent to assuming the filter has been receiving the same + // value as the first sample since time -infinity. + state_ = last_sample_ = sample; + init_end_time_ms_ = now_ms + init_time_ms_; + last_state_time_ms_ = now_ms; + return; + } + + ExtrapolateLastSample(now_ms); + last_sample_ = sample; +} + +absl::optional SmoothingFilterImpl::GetAverage() { + if (!init_end_time_ms_) { + // |init_end_time_ms_| undefined since we have not received any sample. + return absl::nullopt; + } + ExtrapolateLastSample(rtc::TimeMillis()); + return state_; +} + +bool SmoothingFilterImpl::SetTimeConstantMs(int time_constant_ms) { + if (!init_end_time_ms_ || last_state_time_ms_ < *init_end_time_ms_) { + return false; + } + UpdateAlpha(time_constant_ms); + return true; +} + +void SmoothingFilterImpl::UpdateAlpha(int time_constant_ms) { + alpha_ = time_constant_ms == 0 ? 0.0f : std::exp(-1.0f / time_constant_ms); +} + +void SmoothingFilterImpl::ExtrapolateLastSample(int64_t time_ms) { + RTC_DCHECK_GE(time_ms, last_state_time_ms_); + RTC_DCHECK(init_end_time_ms_); + + float multiplier = 0.0f; + + if (time_ms <= *init_end_time_ms_) { + // Current update is to be made during initialization phase. + // We update the state as if the |alpha| has been increased according + // alpha(n) = exp(-powf(init_factor_, n)), + // where n is the time (in millisecond) since the first sample received. + // With algebraic derivation as shown in the Appendix, we can find that the + // state can be updated in a similar manner as if alpha is a constant, + // except for a different multiplier. + if (init_time_ms_ == 0) { + // This means |init_factor_| = 0. + multiplier = 0.0f; + } else if (init_time_ms_ == 1) { + // This means |init_factor_| = 1. + multiplier = std::exp(last_state_time_ms_ - time_ms); + } else { + multiplier = std::exp( + -(powf(init_factor_, last_state_time_ms_ - *init_end_time_ms_) - + powf(init_factor_, time_ms - *init_end_time_ms_)) / + init_const_); + } + } else { + if (last_state_time_ms_ < *init_end_time_ms_) { + // The latest state update was made during initialization phase. + // We first extrapolate to the initialization time. + ExtrapolateLastSample(*init_end_time_ms_); + // Then extrapolate the rest by the following. + } + multiplier = powf(alpha_, time_ms - last_state_time_ms_); + } + + state_ = multiplier * state_ + (1.0f - multiplier) * last_sample_; + last_state_time_ms_ = time_ms; +} + +} // namespace webrtc + +// Appendix: derivation of extrapolation during initialization phase. +// (LaTeX syntax) +// Assuming +// \begin{align} +// y(n) &= \alpha_{n-1} y(n-1) + \left(1 - \alpha_{n-1}\right) x(m) \\* +// &= \left(\prod_{i=m}^{n-1} \alpha_i\right) y(m) + +// \left(1 - \prod_{i=m}^{n-1} \alpha_i \right) x(m) +// \end{align} +// Taking $\alpha_{n} = \exp(-\gamma^n)$, $\gamma$ denotes init\_factor\_, the +// multiplier becomes +// \begin{align} +// \prod_{i=m}^{n-1} \alpha_i +// &= \exp\left(-\sum_{i=m}^{n-1} \gamma^i \right) \\* +// &= \begin{cases} +// \exp\left(-\frac{\gamma^m - \gamma^n}{1 - \gamma} \right) +// & \gamma \neq 1 \\* +// m-n & \gamma = 1 +// \end{cases} +// \end{align} +// We know $\gamma = T^{-\frac{1}{T}}$, where $T$ denotes init\_time\_ms\_. Then +// $1 - \gamma$ approaches zero when $T$ increases. This can cause numerical +// difficulties. We multiply $T$ (if $T > 0$) to both numerator and denominator +// in the fraction. See. +// \begin{align} +// \frac{\gamma^m - \gamma^n}{1 - \gamma} +// &= \frac{T^\frac{T-m}{T} - T^\frac{T-n}{T}}{T - T^{1-\frac{1}{T}}} +// \end{align} diff --git a/webrtc/common_audio/smoothing_filter.h b/webrtc/common_audio/smoothing_filter.h new file mode 100644 index 0000000..e96d52a --- /dev/null +++ b/webrtc/common_audio/smoothing_filter.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#ifndef COMMON_AUDIO_SMOOTHING_FILTER_H_ +#define COMMON_AUDIO_SMOOTHING_FILTER_H_ + +#include + +#include "absl/types/optional.h" + +namespace webrtc { + +class SmoothingFilter { + public: + virtual ~SmoothingFilter() = default; + virtual void AddSample(float sample) = 0; + virtual absl::optional GetAverage() = 0; + virtual bool SetTimeConstantMs(int time_constant_ms) = 0; +}; + +// SmoothingFilterImpl applies an exponential filter +// alpha = exp(-1.0 / time_constant_ms); +// y[t] = alpha * y[t-1] + (1 - alpha) * sample; +// This implies a sample rate of 1000 Hz, i.e., 1 sample / ms. +// But SmoothingFilterImpl allows sparse samples. All missing samples will be +// assumed to equal the last received sample. +class SmoothingFilterImpl final : public SmoothingFilter { + public: + // |init_time_ms| is initialization time. It defines a period starting from + // the arriving time of the first sample. During this period, the exponential + // filter uses a varying time constant so that a smaller time constant will be + // applied to the earlier samples. This is to allow the the filter to adapt to + // earlier samples quickly. After the initialization period, the time constant + // will be set to |init_time_ms| first and can be changed through + // |SetTimeConstantMs|. + explicit SmoothingFilterImpl(int init_time_ms); + + SmoothingFilterImpl() = delete; + SmoothingFilterImpl(const SmoothingFilterImpl&) = delete; + SmoothingFilterImpl& operator=(const SmoothingFilterImpl&) = delete; + + ~SmoothingFilterImpl() override; + + void AddSample(float sample) override; + absl::optional GetAverage() override; + bool SetTimeConstantMs(int time_constant_ms) override; + + // Methods used for unittests. + float alpha() const { return alpha_; } + + private: + void UpdateAlpha(int time_constant_ms); + void ExtrapolateLastSample(int64_t time_ms); + + const int init_time_ms_; + const float init_factor_; + const float init_const_; + + absl::optional init_end_time_ms_; + float last_sample_; + float alpha_; + float state_; + int64_t last_state_time_ms_; +}; + +} // namespace webrtc + +#endif // COMMON_AUDIO_SMOOTHING_FILTER_H_ diff --git a/webrtc/common_audio/sparse_fir_filter.cc b/webrtc/common_audio/sparse_fir_filter.cc deleted file mode 100644 index 5862b7c..0000000 --- a/webrtc/common_audio/sparse_fir_filter.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2015 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 "webrtc/common_audio/sparse_fir_filter.h" - -#include "webrtc/base/checks.h" - -namespace webrtc { - -SparseFIRFilter::SparseFIRFilter(const float* nonzero_coeffs, - size_t num_nonzero_coeffs, - size_t sparsity, - size_t offset) - : sparsity_(sparsity), - offset_(offset), - nonzero_coeffs_(nonzero_coeffs, nonzero_coeffs + num_nonzero_coeffs), - state_(sparsity_ * (num_nonzero_coeffs - 1) + offset_, 0.f) { - RTC_CHECK_GE(num_nonzero_coeffs, 1u); - RTC_CHECK_GE(sparsity, 1u); -} - -void SparseFIRFilter::Filter(const float* in, size_t length, float* out) { - // Convolves the input signal |in| with the filter kernel |nonzero_coeffs_| - // taking into account the previous state. - for (size_t i = 0; i < length; ++i) { - out[i] = 0.f; - size_t j; - for (j = 0; i >= j * sparsity_ + offset_ && - j < nonzero_coeffs_.size(); ++j) { - out[i] += in[i - j * sparsity_ - offset_] * nonzero_coeffs_[j]; - } - for (; j < nonzero_coeffs_.size(); ++j) { - out[i] += state_[i + (nonzero_coeffs_.size() - j - 1) * sparsity_] * - nonzero_coeffs_[j]; - } - } - - // Update current state. - if (state_.size() > 0u) { - if (length >= state_.size()) { - std::memcpy(&state_[0], - &in[length - state_.size()], - state_.size() * sizeof(*in)); - } else { - std::memmove(&state_[0], - &state_[length], - (state_.size() - length) * sizeof(state_[0])); - std::memcpy(&state_[state_.size() - length], in, length * sizeof(*in)); - } - } -} - -} // namespace webrtc diff --git a/webrtc/common_audio/sparse_fir_filter.h b/webrtc/common_audio/sparse_fir_filter.h deleted file mode 100644 index 2ba5cf4..0000000 --- a/webrtc/common_audio/sparse_fir_filter.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2015 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. - */ - -#ifndef WEBRTC_COMMON_AUDIO_SPARSE_FIR_FILTER_H_ -#define WEBRTC_COMMON_AUDIO_SPARSE_FIR_FILTER_H_ - -#include -#include - -#include "webrtc/base/constructormagic.h" - -namespace webrtc { - -// A Finite Impulse Response filter implementation which takes advantage of a -// sparse structure with uniformly distributed non-zero coefficients. -class SparseFIRFilter final { - public: - // |num_nonzero_coeffs| is the number of non-zero coefficients, - // |nonzero_coeffs|. They are assumed to be uniformly distributed every - // |sparsity| samples and with an initial |offset|. The rest of the filter - // coefficients will be assumed zeros. For example, with sparsity = 3, and - // offset = 1 the filter coefficients will be: - // B = [0 coeffs[0] 0 0 coeffs[1] 0 0 coeffs[2] ... ] - // All initial state values will be zeros. - SparseFIRFilter(const float* nonzero_coeffs, - size_t num_nonzero_coeffs, - size_t sparsity, - size_t offset); - - // Filters the |in| data supplied. - // |out| must be previously allocated and it must be at least of |length|. - void Filter(const float* in, size_t length, float* out); - - private: - const size_t sparsity_; - const size_t offset_; - const std::vector nonzero_coeffs_; - std::vector state_; - - RTC_DISALLOW_COPY_AND_ASSIGN(SparseFIRFilter); -}; - -} // namespace webrtc - -#endif // WEBRTC_COMMON_AUDIO_SPARSE_FIR_FILTER_H_ diff --git a/webrtc/common_audio/third_party/ooura/BUILD.gn b/webrtc/common_audio/third_party/ooura/BUILD.gn new file mode 100644 index 0000000..0cdf98e --- /dev/null +++ b/webrtc/common_audio/third_party/ooura/BUILD.gn @@ -0,0 +1,58 @@ +# Copyright (c) 2020 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. + +import("../../../webrtc.gni") + +rtc_library("fft_size_128") { + sources = [ + "fft_size_128/ooura_fft.cc", + "fft_size_128/ooura_fft.h", + "fft_size_128/ooura_fft_tables_common.h", + ] + deps = [ + "../../../rtc_base/system:arch", + "../../../system_wrappers", + ] + cflags = [] + + if (current_cpu == "x86" || current_cpu == "x64") { + sources += [ + "fft_size_128/ooura_fft_sse2.cc", + "fft_size_128/ooura_fft_tables_neon_sse2.h", + ] + if (is_posix || is_fuchsia) { + cflags += [ "-msse2" ] + } + } + + if (rtc_build_with_neon) { + sources += [ + "fft_size_128/ooura_fft_neon.cc", + "fft_size_128/ooura_fft_tables_neon_sse2.h", + ] + + deps += [ "../../../common_audio" ] + + if (current_cpu != "arm64") { + # Enable compilation for the NEON instruction set. + suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] + cflags += [ "-mfpu=neon" ] + } + } + + if (current_cpu == "mipsel" && mips_float_abi == "hard") { + sources += [ "fft_size_128/ooura_fft_mips.cc" ] + } +} + +rtc_library("fft_size_256") { + sources = [ + "fft_size_256/fft4g.cc", + "fft_size_256/fft4g.h", + ] +} diff --git a/webrtc/common_audio/third_party/ooura/LICENSE b/webrtc/common_audio/third_party/ooura/LICENSE new file mode 100644 index 0000000..3bf870a --- /dev/null +++ b/webrtc/common_audio/third_party/ooura/LICENSE @@ -0,0 +1,8 @@ +/* + * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html + * Copyright Takuya OOURA, 1996-2001 + * + * You may use, copy, modify and distribute this code for any purpose (include + * commercial use) and without fee. Please refer to this package when you modify + * this code. + */ diff --git a/webrtc/modules/audio_processing/aec/aec_rdft.c b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.cc similarity index 64% rename from webrtc/modules/audio_processing/aec/aec_rdft.c rename to webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.cc index 03efc10..6933120 100644 --- a/webrtc/modules/audio_processing/aec/aec_rdft.c +++ b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.cc @@ -10,6 +10,8 @@ * - Trivial type modifications. * - Minimal code subset to do rdft of length 128. * - Optimizations because of known length. + * - Removed the global variables by moving the code in to a class in order + * to make it thread safe. * * All changes are covered by the WebRTC license and IP grant: * Use of this source code is governed by a BSD-style license @@ -19,184 +21,17 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/aec/aec_rdft.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" -#include +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h" +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" -#include "webrtc/system_wrappers/include/cpu_features_wrapper.h" -#include "webrtc/typedefs.h" +namespace webrtc { -// These tables used to be computed at run-time. For example, refer to: -// https://code.google.com/p/webrtc/source/browse/trunk/webrtc/modules/audio_processing/aec/aec_rdft.c?r=6564 -// to see the initialization code. -const float rdft_w[64] = { - 1.0000000000f, 0.0000000000f, 0.7071067691f, 0.7071067691f, - 0.9238795638f, 0.3826834559f, 0.3826834559f, 0.9238795638f, - 0.9807852507f, 0.1950903237f, 0.5555702448f, 0.8314695954f, - 0.8314695954f, 0.5555702448f, 0.1950903237f, 0.9807852507f, - 0.9951847196f, 0.0980171412f, 0.6343933344f, 0.7730104327f, - 0.8819212914f, 0.4713967443f, 0.2902846634f, 0.9569403529f, - 0.9569403529f, 0.2902846634f, 0.4713967443f, 0.8819212914f, - 0.7730104327f, 0.6343933344f, 0.0980171412f, 0.9951847196f, - 0.7071067691f, 0.4993977249f, 0.4975923598f, 0.4945882559f, - 0.4903926253f, 0.4850156307f, 0.4784701765f, 0.4707720280f, - 0.4619397819f, 0.4519946277f, 0.4409606457f, 0.4288643003f, - 0.4157347977f, 0.4016037583f, 0.3865052164f, 0.3704755902f, - 0.3535533845f, 0.3357794881f, 0.3171966672f, 0.2978496552f, - 0.2777851224f, 0.2570513785f, 0.2356983721f, 0.2137775421f, - 0.1913417280f, 0.1684449315f, 0.1451423317f, 0.1214900985f, - 0.0975451618f, 0.0733652338f, 0.0490085706f, 0.0245338380f, -}; -const float rdft_wk3ri_first[16] = { - 1.000000000f, 0.000000000f, 0.382683456f, 0.923879564f, - 0.831469536f, 0.555570245f, -0.195090353f, 0.980785251f, - 0.956940353f, 0.290284693f, 0.098017156f, 0.995184720f, - 0.634393334f, 0.773010492f, -0.471396863f, 0.881921172f, -}; -const float rdft_wk3ri_second[16] = { - -0.707106769f, 0.707106769f, -0.923879564f, -0.382683456f, - -0.980785251f, 0.195090353f, -0.555570245f, -0.831469536f, - -0.881921172f, 0.471396863f, -0.773010492f, -0.634393334f, - -0.995184720f, -0.098017156f, -0.290284693f, -0.956940353f, -}; -ALIGN16_BEG const float ALIGN16_END rdft_wk1r[32] = { - 1.000000000f, 1.000000000f, 0.707106769f, 0.707106769f, - 0.923879564f, 0.923879564f, 0.382683456f, 0.382683456f, - 0.980785251f, 0.980785251f, 0.555570245f, 0.555570245f, - 0.831469595f, 0.831469595f, 0.195090324f, 0.195090324f, - 0.995184720f, 0.995184720f, 0.634393334f, 0.634393334f, - 0.881921291f, 0.881921291f, 0.290284663f, 0.290284663f, - 0.956940353f, 0.956940353f, 0.471396744f, 0.471396744f, - 0.773010433f, 0.773010433f, 0.098017141f, 0.098017141f, -}; -ALIGN16_BEG const float ALIGN16_END rdft_wk2r[32] = { - 1.000000000f, 1.000000000f, -0.000000000f, -0.000000000f, - 0.707106769f, 0.707106769f, -0.707106769f, -0.707106769f, - 0.923879564f, 0.923879564f, -0.382683456f, -0.382683456f, - 0.382683456f, 0.382683456f, -0.923879564f, -0.923879564f, - 0.980785251f, 0.980785251f, -0.195090324f, -0.195090324f, - 0.555570245f, 0.555570245f, -0.831469595f, -0.831469595f, - 0.831469595f, 0.831469595f, -0.555570245f, -0.555570245f, - 0.195090324f, 0.195090324f, -0.980785251f, -0.980785251f, -}; -ALIGN16_BEG const float ALIGN16_END rdft_wk3r[32] = { - 1.000000000f, 1.000000000f, -0.707106769f, -0.707106769f, - 0.382683456f, 0.382683456f, -0.923879564f, -0.923879564f, - 0.831469536f, 0.831469536f, -0.980785251f, -0.980785251f, - -0.195090353f, -0.195090353f, -0.555570245f, -0.555570245f, - 0.956940353f, 0.956940353f, -0.881921172f, -0.881921172f, - 0.098017156f, 0.098017156f, -0.773010492f, -0.773010492f, - 0.634393334f, 0.634393334f, -0.995184720f, -0.995184720f, - -0.471396863f, -0.471396863f, -0.290284693f, -0.290284693f, -}; -ALIGN16_BEG const float ALIGN16_END rdft_wk1i[32] = { - -0.000000000f, 0.000000000f, -0.707106769f, 0.707106769f, - -0.382683456f, 0.382683456f, -0.923879564f, 0.923879564f, - -0.195090324f, 0.195090324f, -0.831469595f, 0.831469595f, - -0.555570245f, 0.555570245f, -0.980785251f, 0.980785251f, - -0.098017141f, 0.098017141f, -0.773010433f, 0.773010433f, - -0.471396744f, 0.471396744f, -0.956940353f, 0.956940353f, - -0.290284663f, 0.290284663f, -0.881921291f, 0.881921291f, - -0.634393334f, 0.634393334f, -0.995184720f, 0.995184720f, -}; -ALIGN16_BEG const float ALIGN16_END rdft_wk2i[32] = { - -0.000000000f, 0.000000000f, -1.000000000f, 1.000000000f, - -0.707106769f, 0.707106769f, -0.707106769f, 0.707106769f, - -0.382683456f, 0.382683456f, -0.923879564f, 0.923879564f, - -0.923879564f, 0.923879564f, -0.382683456f, 0.382683456f, - -0.195090324f, 0.195090324f, -0.980785251f, 0.980785251f, - -0.831469595f, 0.831469595f, -0.555570245f, 0.555570245f, - -0.555570245f, 0.555570245f, -0.831469595f, 0.831469595f, - -0.980785251f, 0.980785251f, -0.195090324f, 0.195090324f, -}; -ALIGN16_BEG const float ALIGN16_END rdft_wk3i[32] = { - -0.000000000f, 0.000000000f, -0.707106769f, 0.707106769f, - -0.923879564f, 0.923879564f, 0.382683456f, -0.382683456f, - -0.555570245f, 0.555570245f, -0.195090353f, 0.195090353f, - -0.980785251f, 0.980785251f, 0.831469536f, -0.831469536f, - -0.290284693f, 0.290284693f, -0.471396863f, 0.471396863f, - -0.995184720f, 0.995184720f, 0.634393334f, -0.634393334f, - -0.773010492f, 0.773010492f, 0.098017156f, -0.098017156f, - -0.881921172f, 0.881921172f, 0.956940353f, -0.956940353f, -}; -ALIGN16_BEG const float ALIGN16_END cftmdl_wk1r[4] = { - 0.707106769f, 0.707106769f, 0.707106769f, -0.707106769f, -}; - -static void bitrv2_128_C(float* a) { - /* - Following things have been attempted but are no faster: - (a) Storing the swap indexes in a LUT (index calculations are done - for 'free' while waiting on memory/L1). - (b) Consolidate the load/store of two consecutive floats by a 64 bit - integer (execution is memory/L1 bound). - (c) Do a mix of floats and 64 bit integer to maximize register - utilization (execution is memory/L1 bound). - (d) Replacing ip[i] by ((k<<31)>>25) + ((k >> 1)<<5). - (e) Hard-coding of the offsets to completely eliminates index - calculations. - */ - - unsigned int j, j1, k, k1; - float xr, xi, yr, yi; - - static const int ip[4] = {0, 64, 32, 96}; - for (k = 0; k < 4; k++) { - for (j = 0; j < k; j++) { - j1 = 2 * j + ip[k]; - k1 = 2 * k + ip[j]; - xr = a[j1 + 0]; - xi = a[j1 + 1]; - yr = a[k1 + 0]; - yi = a[k1 + 1]; - a[j1 + 0] = yr; - a[j1 + 1] = yi; - a[k1 + 0] = xr; - a[k1 + 1] = xi; - j1 += 8; - k1 += 16; - xr = a[j1 + 0]; - xi = a[j1 + 1]; - yr = a[k1 + 0]; - yi = a[k1 + 1]; - a[j1 + 0] = yr; - a[j1 + 1] = yi; - a[k1 + 0] = xr; - a[k1 + 1] = xi; - j1 += 8; - k1 -= 8; - xr = a[j1 + 0]; - xi = a[j1 + 1]; - yr = a[k1 + 0]; - yi = a[k1 + 1]; - a[j1 + 0] = yr; - a[j1 + 1] = yi; - a[k1 + 0] = xr; - a[k1 + 1] = xi; - j1 += 8; - k1 += 16; - xr = a[j1 + 0]; - xi = a[j1 + 1]; - yr = a[k1 + 0]; - yi = a[k1 + 1]; - a[j1 + 0] = yr; - a[j1 + 1] = yi; - a[k1 + 0] = xr; - a[k1 + 1] = xi; - } - j1 = 2 * k + 8 + ip[k]; - k1 = j1 + 8; - xr = a[j1 + 0]; - xi = a[j1 + 1]; - yr = a[k1 + 0]; - yi = a[k1 + 1]; - a[j1 + 0] = yr; - a[j1 + 1] = yi; - a[k1 + 0] = xr; - a[k1 + 1] = xi; - } -} +namespace { +#if !(defined(MIPS_FPU_LE) || defined(WEBRTC_HAS_NEON)) static void cft1st_128_C(float* a) { const int n = 128; int j, k1, k2; @@ -431,67 +266,6 @@ static void cftmdl_128_C(float* a) { } } -static void cftfsub_128_C(float* a) { - int j, j1, j2, j3, l; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - cft1st_128(a); - cftmdl_128(a); - l = 32; - for (j = 0; j < l; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - a[j2] = x0r - x2r; - a[j2 + 1] = x0i - x2i; - a[j1] = x1r - x3i; - a[j1 + 1] = x1i + x3r; - a[j3] = x1r + x3i; - a[j3 + 1] = x1i - x3r; - } -} - -static void cftbsub_128_C(float* a) { - int j, j1, j2, j3, l; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - cft1st_128(a); - cftmdl_128(a); - l = 32; - - for (j = 0; j < l; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = -a[j + 1] - a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = -a[j + 1] + a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i - x2i; - a[j2] = x0r - x2r; - a[j2 + 1] = x0i + x2i; - a[j1] = x1r - x3i; - a[j1 + 1] = x1i - x3r; - a[j3] = x1r + x3i; - a[j3 + 1] = x1i + x3r; - } -} - static void rftfsub_128_C(float* a) { const float* c = rdft_w + 32; int j1, j2, k1, k2; @@ -535,8 +309,29 @@ static void rftbsub_128_C(float* a) { } a[65] = -a[65]; } +#endif -void aec_rdft_forward_128(float* a) { +} // namespace + +OouraFft::OouraFft(bool sse2_available) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + use_sse2_ = sse2_available; +#else + use_sse2_ = false; +#endif +} + +OouraFft::OouraFft() { +#if defined(WEBRTC_ARCH_X86_FAMILY) + use_sse2_ = (GetCPUInfo(kSSE2) != 0); +#else + use_sse2_ = false; +#endif +} + +OouraFft::~OouraFft() = default; + +void OouraFft::Fft(float* a) const { float xi; bitrv2_128(a); cftfsub_128(a); @@ -545,8 +340,7 @@ void aec_rdft_forward_128(float* a) { a[0] += a[1]; a[1] = xi; } - -void aec_rdft_inverse_128(float* a) { +void OouraFft::InverseFft(float* a) const { a[1] = 0.5f * (a[0] - a[1]); a[0] -= a[1]; rftbsub_128(a); @@ -554,36 +348,201 @@ void aec_rdft_inverse_128(float* a) { cftbsub_128(a); } -// code path selection -RftSub128 cft1st_128; -RftSub128 cftmdl_128; -RftSub128 rftfsub_128; -RftSub128 rftbsub_128; -RftSub128 cftfsub_128; -RftSub128 cftbsub_128; -RftSub128 bitrv2_128; - -void aec_rdft_init(void) { - cft1st_128 = cft1st_128_C; - cftmdl_128 = cftmdl_128_C; - rftfsub_128 = rftfsub_128_C; - rftbsub_128 = rftbsub_128_C; - cftfsub_128 = cftfsub_128_C; - cftbsub_128 = cftbsub_128_C; - bitrv2_128 = bitrv2_128_C; -#if defined(WEBRTC_ARCH_X86_FAMILY) - if (WebRtc_GetCPUInfo(kSSE2)) { - aec_rdft_init_sse2(); - } -#endif +void OouraFft::cft1st_128(float* a) const { #if defined(MIPS_FPU_LE) - aec_rdft_init_mips(); -#endif -#if defined(WEBRTC_HAS_NEON) - aec_rdft_init_neon(); -#elif defined(WEBRTC_DETECT_NEON) - if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) { - aec_rdft_init_neon(); + cft1st_128_mips(a); +#elif defined(WEBRTC_HAS_NEON) + cft1st_128_neon(a); +#elif defined(WEBRTC_ARCH_X86_FAMILY) + if (use_sse2_) { + cft1st_128_SSE2(a); + } else { + cft1st_128_C(a); } +#else + cft1st_128_C(a); #endif } +void OouraFft::cftmdl_128(float* a) const { +#if defined(MIPS_FPU_LE) + cftmdl_128_mips(a); +#elif defined(WEBRTC_HAS_NEON) + cftmdl_128_neon(a); +#elif defined(WEBRTC_ARCH_X86_FAMILY) + if (use_sse2_) { + cftmdl_128_SSE2(a); + } else { + cftmdl_128_C(a); + } +#else + cftmdl_128_C(a); +#endif +} +void OouraFft::rftfsub_128(float* a) const { +#if defined(MIPS_FPU_LE) + rftfsub_128_mips(a); +#elif defined(WEBRTC_HAS_NEON) + rftfsub_128_neon(a); +#elif defined(WEBRTC_ARCH_X86_FAMILY) + if (use_sse2_) { + rftfsub_128_SSE2(a); + } else { + rftfsub_128_C(a); + } +#else + rftfsub_128_C(a); +#endif +} + +void OouraFft::rftbsub_128(float* a) const { +#if defined(MIPS_FPU_LE) + rftbsub_128_mips(a); +#elif defined(WEBRTC_HAS_NEON) + rftbsub_128_neon(a); +#elif defined(WEBRTC_ARCH_X86_FAMILY) + if (use_sse2_) { + rftbsub_128_SSE2(a); + } else { + rftbsub_128_C(a); + } +#else + rftbsub_128_C(a); +#endif +} + +void OouraFft::cftbsub_128(float* a) const { + int j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + cft1st_128(a); + cftmdl_128(a); + l = 32; + + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = -a[j + 1] - a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = -a[j + 1] + a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i + x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i - x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i + x3r; + } +} + +void OouraFft::cftfsub_128(float* a) const { + int j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + cft1st_128(a); + cftmdl_128(a); + l = 32; + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } +} + +void OouraFft::bitrv2_128(float* a) const { + /* + Following things have been attempted but are no faster: + (a) Storing the swap indexes in a LUT (index calculations are done + for 'free' while waiting on memory/L1). + (b) Consolidate the load/store of two consecutive floats by a 64 bit + integer (execution is memory/L1 bound). + (c) Do a mix of floats and 64 bit integer to maximize register + utilization (execution is memory/L1 bound). + (d) Replacing ip[i] by ((k<<31)>>25) + ((k >> 1)<<5). + (e) Hard-coding of the offsets to completely eliminates index + calculations. + */ + + unsigned int j, j1, k, k1; + float xr, xi, yr, yi; + + const int ip[4] = {0, 64, 32, 96}; + for (k = 0; k < 4; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1 + 0]; + xi = a[j1 + 1]; + yr = a[k1 + 0]; + yi = a[k1 + 1]; + a[j1 + 0] = yr; + a[j1 + 1] = yi; + a[k1 + 0] = xr; + a[k1 + 1] = xi; + j1 += 8; + k1 += 16; + xr = a[j1 + 0]; + xi = a[j1 + 1]; + yr = a[k1 + 0]; + yi = a[k1 + 1]; + a[j1 + 0] = yr; + a[j1 + 1] = yi; + a[k1 + 0] = xr; + a[k1 + 1] = xi; + j1 += 8; + k1 -= 8; + xr = a[j1 + 0]; + xi = a[j1 + 1]; + yr = a[k1 + 0]; + yi = a[k1 + 1]; + a[j1 + 0] = yr; + a[j1 + 1] = yi; + a[k1 + 0] = xr; + a[k1 + 1] = xi; + j1 += 8; + k1 += 16; + xr = a[j1 + 0]; + xi = a[j1 + 1]; + yr = a[k1 + 0]; + yi = a[k1 + 1]; + a[j1 + 0] = yr; + a[j1 + 1] = yi; + a[k1 + 0] = xr; + a[k1 + 1] = xi; + } + j1 = 2 * k + 8 + ip[k]; + k1 = j1 + 8; + xr = a[j1 + 0]; + xi = a[j1 + 1]; + yr = a[k1 + 0]; + yi = a[k1 + 1]; + a[j1 + 0] = yr; + a[j1 + 1] = yi; + a[k1 + 0] = xr; + a[k1 + 1] = xi; + } +} + +} // namespace webrtc diff --git a/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.h b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.h new file mode 100644 index 0000000..8273dfe --- /dev/null +++ b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_H_ + +#include "rtc_base/system/arch.h" + +namespace webrtc { + +#if defined(WEBRTC_ARCH_X86_FAMILY) +void cft1st_128_SSE2(float* a); +void cftmdl_128_SSE2(float* a); +void rftfsub_128_SSE2(float* a); +void rftbsub_128_SSE2(float* a); +#endif + +#if defined(MIPS_FPU_LE) +void cft1st_128_mips(float* a); +void cftmdl_128_mips(float* a); +void rftfsub_128_mips(float* a); +void rftbsub_128_mips(float* a); +#endif + +#if defined(WEBRTC_HAS_NEON) +void cft1st_128_neon(float* a); +void cftmdl_128_neon(float* a); +void rftfsub_128_neon(float* a); +void rftbsub_128_neon(float* a); +#endif + +class OouraFft { + public: + // Ctor allowing the availability of SSE2 support to be specified. + explicit OouraFft(bool sse2_available); + + // Deprecated: This Ctor will soon be removed. + OouraFft(); + ~OouraFft(); + void Fft(float* a) const; + void InverseFft(float* a) const; + + private: + void cft1st_128(float* a) const; + void cftmdl_128(float* a) const; + void rftfsub_128(float* a) const; + void rftbsub_128(float* a) const; + + void cftfsub_128(float* a) const; + void cftbsub_128(float* a) const; + void bitrv2_128(float* a) const; + bool use_sse2_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_H_ diff --git a/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_mips.cc b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_mips.cc new file mode 100644 index 0000000..4c231e3 --- /dev/null +++ b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_mips.cc @@ -0,0 +1,1245 @@ +/* + * Copyright (c) 2014 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 "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h" + +namespace webrtc { + +#if defined(MIPS_FPU_LE) +void bitrv2_128_mips(float* a) { + // n is 128 + float xr, xi, yr, yi; + + xr = a[8]; + xi = a[9]; + yr = a[16]; + yi = a[17]; + a[8] = yr; + a[9] = yi; + a[16] = xr; + a[17] = xi; + + xr = a[64]; + xi = a[65]; + yr = a[2]; + yi = a[3]; + a[64] = yr; + a[65] = yi; + a[2] = xr; + a[3] = xi; + + xr = a[72]; + xi = a[73]; + yr = a[18]; + yi = a[19]; + a[72] = yr; + a[73] = yi; + a[18] = xr; + a[19] = xi; + + xr = a[80]; + xi = a[81]; + yr = a[10]; + yi = a[11]; + a[80] = yr; + a[81] = yi; + a[10] = xr; + a[11] = xi; + + xr = a[88]; + xi = a[89]; + yr = a[26]; + yi = a[27]; + a[88] = yr; + a[89] = yi; + a[26] = xr; + a[27] = xi; + + xr = a[74]; + xi = a[75]; + yr = a[82]; + yi = a[83]; + a[74] = yr; + a[75] = yi; + a[82] = xr; + a[83] = xi; + + xr = a[32]; + xi = a[33]; + yr = a[4]; + yi = a[5]; + a[32] = yr; + a[33] = yi; + a[4] = xr; + a[5] = xi; + + xr = a[40]; + xi = a[41]; + yr = a[20]; + yi = a[21]; + a[40] = yr; + a[41] = yi; + a[20] = xr; + a[21] = xi; + + xr = a[48]; + xi = a[49]; + yr = a[12]; + yi = a[13]; + a[48] = yr; + a[49] = yi; + a[12] = xr; + a[13] = xi; + + xr = a[56]; + xi = a[57]; + yr = a[28]; + yi = a[29]; + a[56] = yr; + a[57] = yi; + a[28] = xr; + a[29] = xi; + + xr = a[34]; + xi = a[35]; + yr = a[68]; + yi = a[69]; + a[34] = yr; + a[35] = yi; + a[68] = xr; + a[69] = xi; + + xr = a[42]; + xi = a[43]; + yr = a[84]; + yi = a[85]; + a[42] = yr; + a[43] = yi; + a[84] = xr; + a[85] = xi; + + xr = a[50]; + xi = a[51]; + yr = a[76]; + yi = a[77]; + a[50] = yr; + a[51] = yi; + a[76] = xr; + a[77] = xi; + + xr = a[58]; + xi = a[59]; + yr = a[92]; + yi = a[93]; + a[58] = yr; + a[59] = yi; + a[92] = xr; + a[93] = xi; + + xr = a[44]; + xi = a[45]; + yr = a[52]; + yi = a[53]; + a[44] = yr; + a[45] = yi; + a[52] = xr; + a[53] = xi; + + xr = a[96]; + xi = a[97]; + yr = a[6]; + yi = a[7]; + a[96] = yr; + a[97] = yi; + a[6] = xr; + a[7] = xi; + + xr = a[104]; + xi = a[105]; + yr = a[22]; + yi = a[23]; + a[104] = yr; + a[105] = yi; + a[22] = xr; + a[23] = xi; + + xr = a[112]; + xi = a[113]; + yr = a[14]; + yi = a[15]; + a[112] = yr; + a[113] = yi; + a[14] = xr; + a[15] = xi; + + xr = a[120]; + xi = a[121]; + yr = a[30]; + yi = a[31]; + a[120] = yr; + a[121] = yi; + a[30] = xr; + a[31] = xi; + + xr = a[98]; + xi = a[99]; + yr = a[70]; + yi = a[71]; + a[98] = yr; + a[99] = yi; + a[70] = xr; + a[71] = xi; + + xr = a[106]; + xi = a[107]; + yr = a[86]; + yi = a[87]; + a[106] = yr; + a[107] = yi; + a[86] = xr; + a[87] = xi; + + xr = a[114]; + xi = a[115]; + yr = a[78]; + yi = a[79]; + a[114] = yr; + a[115] = yi; + a[78] = xr; + a[79] = xi; + + xr = a[122]; + xi = a[123]; + yr = a[94]; + yi = a[95]; + a[122] = yr; + a[123] = yi; + a[94] = xr; + a[95] = xi; + + xr = a[100]; + xi = a[101]; + yr = a[38]; + yi = a[39]; + a[100] = yr; + a[101] = yi; + a[38] = xr; + a[39] = xi; + + xr = a[108]; + xi = a[109]; + yr = a[54]; + yi = a[55]; + a[108] = yr; + a[109] = yi; + a[54] = xr; + a[55] = xi; + + xr = a[116]; + xi = a[117]; + yr = a[46]; + yi = a[47]; + a[116] = yr; + a[117] = yi; + a[46] = xr; + a[47] = xi; + + xr = a[124]; + xi = a[125]; + yr = a[62]; + yi = a[63]; + a[124] = yr; + a[125] = yi; + a[62] = xr; + a[63] = xi; + + xr = a[110]; + xi = a[111]; + yr = a[118]; + yi = a[119]; + a[110] = yr; + a[111] = yi; + a[118] = xr; + a[119] = xi; +} + +void cft1st_128_mips(float* a) { + float f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14; + int a_ptr, p1_rdft, p2_rdft, count; + const float* first = rdft_wk3ri_first; + const float* second = rdft_wk3ri_second; + + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + // first 8 + "lwc1 %[f0], 0(%[a]) \n\t" + "lwc1 %[f1], 4(%[a]) \n\t" + "lwc1 %[f2], 8(%[a]) \n\t" + "lwc1 %[f3], 12(%[a]) \n\t" + "lwc1 %[f4], 16(%[a]) \n\t" + "lwc1 %[f5], 20(%[a]) \n\t" + "lwc1 %[f6], 24(%[a]) \n\t" + "lwc1 %[f7], 28(%[a]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f8], %[f2] \n\t" + "sub.s %[f2], %[f1], %[f4] \n\t" + "add.s %[f1], %[f1], %[f4] \n\t" + "add.s %[f4], %[f6], %[f3] \n\t" + "sub.s %[f6], %[f6], %[f3] \n\t" + "sub.s %[f3], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "swc1 %[f7], 0(%[a]) \n\t" + "swc1 %[f8], 16(%[a]) \n\t" + "swc1 %[f2], 28(%[a]) \n\t" + "swc1 %[f1], 12(%[a]) \n\t" + "swc1 %[f4], 4(%[a]) \n\t" + "swc1 %[f6], 20(%[a]) \n\t" + "swc1 %[f3], 8(%[a]) \n\t" + "swc1 %[f0], 24(%[a]) \n\t" + // second 8 + "lwc1 %[f0], 32(%[a]) \n\t" + "lwc1 %[f1], 36(%[a]) \n\t" + "lwc1 %[f2], 40(%[a]) \n\t" + "lwc1 %[f3], 44(%[a]) \n\t" + "lwc1 %[f4], 48(%[a]) \n\t" + "lwc1 %[f5], 52(%[a]) \n\t" + "lwc1 %[f6], 56(%[a]) \n\t" + "lwc1 %[f7], 60(%[a]) \n\t" + "add.s %[f8], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f7], %[f4], %[f1] \n\t" + "sub.s %[f4], %[f4], %[f1] \n\t" + "add.s %[f1], %[f3], %[f8] \n\t" + "sub.s %[f3], %[f3], %[f8] \n\t" + "sub.s %[f8], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "add.s %[f5], %[f6], %[f2] \n\t" + "sub.s %[f6], %[f2], %[f6] \n\t" + "lwc1 %[f9], 8(%[rdft_w]) \n\t" + "sub.s %[f2], %[f8], %[f7] \n\t" + "add.s %[f8], %[f8], %[f7] \n\t" + "sub.s %[f7], %[f4], %[f0] \n\t" + "add.s %[f4], %[f4], %[f0] \n\t" + // prepare for loop + "addiu %[a_ptr], %[a], 64 \n\t" + "addiu %[p1_rdft], %[rdft_w], 8 \n\t" + "addiu %[p2_rdft], %[rdft_w], 16 \n\t" + "addiu %[count], $zero, 7 \n\t" + // finish second 8 + "mul.s %[f2], %[f9], %[f2] \n\t" + "mul.s %[f8], %[f9], %[f8] \n\t" + "mul.s %[f7], %[f9], %[f7] \n\t" + "mul.s %[f4], %[f9], %[f4] \n\t" + "swc1 %[f1], 32(%[a]) \n\t" + "swc1 %[f3], 52(%[a]) \n\t" + "swc1 %[f5], 36(%[a]) \n\t" + "swc1 %[f6], 48(%[a]) \n\t" + "swc1 %[f2], 40(%[a]) \n\t" + "swc1 %[f8], 44(%[a]) \n\t" + "swc1 %[f7], 56(%[a]) \n\t" + "swc1 %[f4], 60(%[a]) \n\t" + // loop + "1: \n\t" + "lwc1 %[f0], 0(%[a_ptr]) \n\t" + "lwc1 %[f1], 4(%[a_ptr]) \n\t" + "lwc1 %[f2], 8(%[a_ptr]) \n\t" + "lwc1 %[f3], 12(%[a_ptr]) \n\t" + "lwc1 %[f4], 16(%[a_ptr]) \n\t" + "lwc1 %[f5], 20(%[a_ptr]) \n\t" + "lwc1 %[f6], 24(%[a_ptr]) \n\t" + "lwc1 %[f7], 28(%[a_ptr]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "lwc1 %[f10], 4(%[p1_rdft]) \n\t" + "lwc1 %[f11], 0(%[p2_rdft]) \n\t" + "lwc1 %[f12], 4(%[p2_rdft]) \n\t" + "lwc1 %[f13], 8(%[first]) \n\t" + "lwc1 %[f14], 12(%[first]) \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f8], %[f2] \n\t" + "add.s %[f2], %[f6], %[f3] \n\t" + "sub.s %[f6], %[f6], %[f3] \n\t" + "add.s %[f3], %[f0], %[f5] \n\t" + "sub.s %[f0], %[f0], %[f5] \n\t" + "add.s %[f5], %[f1], %[f4] \n\t" + "sub.s %[f1], %[f1], %[f4] \n\t" + "swc1 %[f7], 0(%[a_ptr]) \n\t" + "swc1 %[f2], 4(%[a_ptr]) \n\t" + "mul.s %[f4], %[f9], %[f8] \n\t" +#if defined(MIPS32_R2_LE) + "mul.s %[f8], %[f10], %[f8] \n\t" + "mul.s %[f7], %[f11], %[f0] \n\t" + "mul.s %[f0], %[f12], %[f0] \n\t" + "mul.s %[f2], %[f13], %[f3] \n\t" + "mul.s %[f3], %[f14], %[f3] \n\t" + "nmsub.s %[f4], %[f4], %[f10], %[f6] \n\t" + "madd.s %[f8], %[f8], %[f9], %[f6] \n\t" + "nmsub.s %[f7], %[f7], %[f12], %[f5] \n\t" + "madd.s %[f0], %[f0], %[f11], %[f5] \n\t" + "nmsub.s %[f2], %[f2], %[f14], %[f1] \n\t" + "madd.s %[f3], %[f3], %[f13], %[f1] \n\t" +#else + "mul.s %[f7], %[f10], %[f6] \n\t" + "mul.s %[f6], %[f9], %[f6] \n\t" + "mul.s %[f8], %[f10], %[f8] \n\t" + "mul.s %[f2], %[f11], %[f0] \n\t" + "mul.s %[f11], %[f11], %[f5] \n\t" + "mul.s %[f5], %[f12], %[f5] \n\t" + "mul.s %[f0], %[f12], %[f0] \n\t" + "mul.s %[f12], %[f13], %[f3] \n\t" + "mul.s %[f13], %[f13], %[f1] \n\t" + "mul.s %[f1], %[f14], %[f1] \n\t" + "mul.s %[f3], %[f14], %[f3] \n\t" + "sub.s %[f4], %[f4], %[f7] \n\t" + "add.s %[f8], %[f6], %[f8] \n\t" + "sub.s %[f7], %[f2], %[f5] \n\t" + "add.s %[f0], %[f11], %[f0] \n\t" + "sub.s %[f2], %[f12], %[f1] \n\t" + "add.s %[f3], %[f13], %[f3] \n\t" +#endif + "swc1 %[f4], 16(%[a_ptr]) \n\t" + "swc1 %[f8], 20(%[a_ptr]) \n\t" + "swc1 %[f7], 8(%[a_ptr]) \n\t" + "swc1 %[f0], 12(%[a_ptr]) \n\t" + "swc1 %[f2], 24(%[a_ptr]) \n\t" + "swc1 %[f3], 28(%[a_ptr]) \n\t" + "lwc1 %[f0], 32(%[a_ptr]) \n\t" + "lwc1 %[f1], 36(%[a_ptr]) \n\t" + "lwc1 %[f2], 40(%[a_ptr]) \n\t" + "lwc1 %[f3], 44(%[a_ptr]) \n\t" + "lwc1 %[f4], 48(%[a_ptr]) \n\t" + "lwc1 %[f5], 52(%[a_ptr]) \n\t" + "lwc1 %[f6], 56(%[a_ptr]) \n\t" + "lwc1 %[f7], 60(%[a_ptr]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "lwc1 %[f11], 8(%[p2_rdft]) \n\t" + "lwc1 %[f12], 12(%[p2_rdft]) \n\t" + "lwc1 %[f13], 8(%[second]) \n\t" + "lwc1 %[f14], 12(%[second]) \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f2], %[f8] \n\t" + "add.s %[f2], %[f6], %[f3] \n\t" + "sub.s %[f6], %[f3], %[f6] \n\t" + "add.s %[f3], %[f0], %[f5] \n\t" + "sub.s %[f0], %[f0], %[f5] \n\t" + "add.s %[f5], %[f1], %[f4] \n\t" + "sub.s %[f1], %[f1], %[f4] \n\t" + "swc1 %[f7], 32(%[a_ptr]) \n\t" + "swc1 %[f2], 36(%[a_ptr]) \n\t" + "mul.s %[f4], %[f10], %[f8] \n\t" +#if defined(MIPS32_R2_LE) + "mul.s %[f10], %[f10], %[f6] \n\t" + "mul.s %[f7], %[f11], %[f0] \n\t" + "mul.s %[f11], %[f11], %[f5] \n\t" + "mul.s %[f2], %[f13], %[f3] \n\t" + "mul.s %[f13], %[f13], %[f1] \n\t" + "madd.s %[f4], %[f4], %[f9], %[f6] \n\t" + "nmsub.s %[f10], %[f10], %[f9], %[f8] \n\t" + "nmsub.s %[f7], %[f7], %[f12], %[f5] \n\t" + "madd.s %[f11], %[f11], %[f12], %[f0] \n\t" + "nmsub.s %[f2], %[f2], %[f14], %[f1] \n\t" + "madd.s %[f13], %[f13], %[f14], %[f3] \n\t" +#else + "mul.s %[f2], %[f9], %[f6] \n\t" + "mul.s %[f10], %[f10], %[f6] \n\t" + "mul.s %[f9], %[f9], %[f8] \n\t" + "mul.s %[f7], %[f11], %[f0] \n\t" + "mul.s %[f8], %[f12], %[f5] \n\t" + "mul.s %[f11], %[f11], %[f5] \n\t" + "mul.s %[f12], %[f12], %[f0] \n\t" + "mul.s %[f5], %[f13], %[f3] \n\t" + "mul.s %[f0], %[f14], %[f1] \n\t" + "mul.s %[f13], %[f13], %[f1] \n\t" + "mul.s %[f14], %[f14], %[f3] \n\t" + "add.s %[f4], %[f4], %[f2] \n\t" + "sub.s %[f10], %[f10], %[f9] \n\t" + "sub.s %[f7], %[f7], %[f8] \n\t" + "add.s %[f11], %[f11], %[f12] \n\t" + "sub.s %[f2], %[f5], %[f0] \n\t" + "add.s %[f13], %[f13], %[f14] \n\t" +#endif + "swc1 %[f4], 48(%[a_ptr]) \n\t" + "swc1 %[f10], 52(%[a_ptr]) \n\t" + "swc1 %[f7], 40(%[a_ptr]) \n\t" + "swc1 %[f11], 44(%[a_ptr]) \n\t" + "swc1 %[f2], 56(%[a_ptr]) \n\t" + "swc1 %[f13], 60(%[a_ptr]) \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f9], 8(%[p1_rdft]) \n\t" + "addiu %[a_ptr], %[a_ptr], 64 \n\t" + "addiu %[p1_rdft], %[p1_rdft], 8 \n\t" + "addiu %[p2_rdft], %[p2_rdft], 16 \n\t" + "addiu %[first], %[first], 8 \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[second], %[second], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), + [f4] "=&f"(f4), [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), + [f8] "=&f"(f8), [f9] "=&f"(f9), [f10] "=&f"(f10), [f11] "=&f"(f11), + [f12] "=&f"(f12), [f13] "=&f"(f13), [f14] "=&f"(f14), + [a_ptr] "=&r"(a_ptr), [p1_rdft] "=&r"(p1_rdft), [first] "+r"(first), + [p2_rdft] "=&r"(p2_rdft), [count] "=&r"(count), [second] "+r"(second) + : [a] "r"(a), [rdft_w] "r"(rdft_w) + : "memory"); +} + +void cftmdl_128_mips(float* a) { + float f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14; + int tmp_a, count; + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmp_a], %[a], 0 \n\t" + "addiu %[count], $zero, 4 \n\t" + "1: \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f0], 0(%[tmp_a]) \n\t" + "lwc1 %[f2], 32(%[tmp_a]) \n\t" + "lwc1 %[f4], 64(%[tmp_a]) \n\t" + "lwc1 %[f6], 96(%[tmp_a]) \n\t" + "lwc1 %[f1], 4(%[tmp_a]) \n\t" + "lwc1 %[f3], 36(%[tmp_a]) \n\t" + "lwc1 %[f5], 68(%[tmp_a]) \n\t" + "lwc1 %[f7], 100(%[tmp_a]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f8], %[f2] \n\t" + "add.s %[f2], %[f1], %[f4] \n\t" + "sub.s %[f1], %[f1], %[f4] \n\t" + "add.s %[f4], %[f6], %[f3] \n\t" + "sub.s %[f6], %[f6], %[f3] \n\t" + "sub.s %[f3], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "swc1 %[f7], 0(%[tmp_a]) \n\t" + "swc1 %[f8], 64(%[tmp_a]) \n\t" + "swc1 %[f2], 36(%[tmp_a]) \n\t" + "swc1 %[f1], 100(%[tmp_a]) \n\t" + "swc1 %[f4], 4(%[tmp_a]) \n\t" + "swc1 %[f6], 68(%[tmp_a]) \n\t" + "swc1 %[f3], 32(%[tmp_a]) \n\t" + "swc1 %[f0], 96(%[tmp_a]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[tmp_a], %[tmp_a], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), + [f4] "=&f"(f4), [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), + [f8] "=&f"(f8), [tmp_a] "=&r"(tmp_a), [count] "=&r"(count) + : [a] "r"(a) + : "memory"); + f9 = rdft_w[2]; + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmp_a], %[a], 128 \n\t" + "addiu %[count], $zero, 4 \n\t" + "1: \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f0], 0(%[tmp_a]) \n\t" + "lwc1 %[f2], 32(%[tmp_a]) \n\t" + "lwc1 %[f5], 68(%[tmp_a]) \n\t" + "lwc1 %[f7], 100(%[tmp_a]) \n\t" + "lwc1 %[f1], 4(%[tmp_a]) \n\t" + "lwc1 %[f3], 36(%[tmp_a]) \n\t" + "lwc1 %[f4], 64(%[tmp_a]) \n\t" + "lwc1 %[f6], 96(%[tmp_a]) \n\t" + "sub.s %[f8], %[f0], %[f2] \n\t" + "add.s %[f0], %[f0], %[f2] \n\t" + "sub.s %[f2], %[f5], %[f7] \n\t" + "add.s %[f5], %[f5], %[f7] \n\t" + "sub.s %[f7], %[f1], %[f3] \n\t" + "add.s %[f1], %[f1], %[f3] \n\t" + "sub.s %[f3], %[f4], %[f6] \n\t" + "add.s %[f4], %[f4], %[f6] \n\t" + "sub.s %[f6], %[f8], %[f2] \n\t" + "add.s %[f8], %[f8], %[f2] \n\t" + "add.s %[f2], %[f5], %[f1] \n\t" + "sub.s %[f5], %[f5], %[f1] \n\t" + "add.s %[f1], %[f3], %[f7] \n\t" + "sub.s %[f3], %[f3], %[f7] \n\t" + "add.s %[f7], %[f0], %[f4] \n\t" + "sub.s %[f0], %[f0], %[f4] \n\t" + "sub.s %[f4], %[f6], %[f1] \n\t" + "add.s %[f6], %[f6], %[f1] \n\t" + "sub.s %[f1], %[f3], %[f8] \n\t" + "add.s %[f3], %[f3], %[f8] \n\t" + "mul.s %[f4], %[f4], %[f9] \n\t" + "mul.s %[f6], %[f6], %[f9] \n\t" + "mul.s %[f1], %[f1], %[f9] \n\t" + "mul.s %[f3], %[f3], %[f9] \n\t" + "swc1 %[f7], 0(%[tmp_a]) \n\t" + "swc1 %[f2], 4(%[tmp_a]) \n\t" + "swc1 %[f5], 64(%[tmp_a]) \n\t" + "swc1 %[f0], 68(%[tmp_a]) \n\t" + "swc1 %[f4], 32(%[tmp_a]) \n\t" + "swc1 %[f6], 36(%[tmp_a]) \n\t" + "swc1 %[f1], 96(%[tmp_a]) \n\t" + "swc1 %[f3], 100(%[tmp_a]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[tmp_a], %[tmp_a], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), + [f4] "=&f"(f4), [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), + [f8] "=&f"(f8), [tmp_a] "=&r"(tmp_a), [count] "=&r"(count) + : [a] "r"(a), [f9] "f"(f9) + : "memory"); + f10 = rdft_w[3]; + f11 = rdft_w[4]; + f12 = rdft_w[5]; + f13 = rdft_wk3ri_first[2]; + f14 = rdft_wk3ri_first[3]; + + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmp_a], %[a], 256 \n\t" + "addiu %[count], $zero, 4 \n\t" + "1: \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f0], 0(%[tmp_a]) \n\t" + "lwc1 %[f2], 32(%[tmp_a]) \n\t" + "lwc1 %[f4], 64(%[tmp_a]) \n\t" + "lwc1 %[f6], 96(%[tmp_a]) \n\t" + "lwc1 %[f1], 4(%[tmp_a]) \n\t" + "lwc1 %[f3], 36(%[tmp_a]) \n\t" + "lwc1 %[f5], 68(%[tmp_a]) \n\t" + "lwc1 %[f7], 100(%[tmp_a]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "sub.s %[f7], %[f8], %[f2] \n\t" + "add.s %[f8], %[f8], %[f2] \n\t" + "add.s %[f2], %[f1], %[f4] \n\t" + "sub.s %[f1], %[f1], %[f4] \n\t" + "sub.s %[f4], %[f6], %[f3] \n\t" + "add.s %[f6], %[f6], %[f3] \n\t" + "sub.s %[f3], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "swc1 %[f8], 0(%[tmp_a]) \n\t" + "swc1 %[f6], 4(%[tmp_a]) \n\t" + "mul.s %[f5], %[f9], %[f7] \n\t" +#if defined(MIPS32_R2_LE) + "mul.s %[f7], %[f10], %[f7] \n\t" + "mul.s %[f8], %[f11], %[f3] \n\t" + "mul.s %[f3], %[f12], %[f3] \n\t" + "mul.s %[f6], %[f13], %[f0] \n\t" + "mul.s %[f0], %[f14], %[f0] \n\t" + "nmsub.s %[f5], %[f5], %[f10], %[f4] \n\t" + "madd.s %[f7], %[f7], %[f9], %[f4] \n\t" + "nmsub.s %[f8], %[f8], %[f12], %[f2] \n\t" + "madd.s %[f3], %[f3], %[f11], %[f2] \n\t" + "nmsub.s %[f6], %[f6], %[f14], %[f1] \n\t" + "madd.s %[f0], %[f0], %[f13], %[f1] \n\t" + "swc1 %[f5], 64(%[tmp_a]) \n\t" + "swc1 %[f7], 68(%[tmp_a]) \n\t" +#else + "mul.s %[f8], %[f10], %[f4] \n\t" + "mul.s %[f4], %[f9], %[f4] \n\t" + "mul.s %[f7], %[f10], %[f7] \n\t" + "mul.s %[f6], %[f11], %[f3] \n\t" + "mul.s %[f3], %[f12], %[f3] \n\t" + "sub.s %[f5], %[f5], %[f8] \n\t" + "mul.s %[f8], %[f12], %[f2] \n\t" + "mul.s %[f2], %[f11], %[f2] \n\t" + "add.s %[f7], %[f4], %[f7] \n\t" + "mul.s %[f4], %[f13], %[f0] \n\t" + "mul.s %[f0], %[f14], %[f0] \n\t" + "sub.s %[f8], %[f6], %[f8] \n\t" + "mul.s %[f6], %[f14], %[f1] \n\t" + "mul.s %[f1], %[f13], %[f1] \n\t" + "add.s %[f3], %[f2], %[f3] \n\t" + "swc1 %[f5], 64(%[tmp_a]) \n\t" + "swc1 %[f7], 68(%[tmp_a]) \n\t" + "sub.s %[f6], %[f4], %[f6] \n\t" + "add.s %[f0], %[f1], %[f0] \n\t" +#endif + "swc1 %[f8], 32(%[tmp_a]) \n\t" + "swc1 %[f3], 36(%[tmp_a]) \n\t" + "swc1 %[f6], 96(%[tmp_a]) \n\t" + "swc1 %[f0], 100(%[tmp_a]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[tmp_a], %[tmp_a], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), + [f4] "=&f"(f4), [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), + [f8] "=&f"(f8), [tmp_a] "=&r"(tmp_a), [count] "=&r"(count) + : [a] "r"(a), [f9] "f"(f9), [f10] "f"(f10), [f11] "f"(f11), + [f12] "f"(f12), [f13] "f"(f13), [f14] "f"(f14) + : "memory"); + f11 = rdft_w[6]; + f12 = rdft_w[7]; + f13 = rdft_wk3ri_second[2]; + f14 = rdft_wk3ri_second[3]; + __asm __volatile( + ".set push " + "\n\t" + ".set noreorder " + "\n\t" + "addiu %[tmp_a], %[a], 384 " + "\n\t" + "addiu %[count], $zero, 4 " + "\n\t" + "1: " + "\n\t" + "addiu %[count], %[count], -1 " + "\n\t" + "lwc1 %[f0], 0(%[tmp_a]) " + "\n\t" + "lwc1 %[f1], 4(%[tmp_a]) " + "\n\t" + "lwc1 %[f2], 32(%[tmp_a]) " + "\n\t" + "lwc1 %[f3], 36(%[tmp_a]) " + "\n\t" + "lwc1 %[f4], 64(%[tmp_a]) " + "\n\t" + "lwc1 %[f5], 68(%[tmp_a]) " + "\n\t" + "lwc1 %[f6], 96(%[tmp_a]) " + "\n\t" + "lwc1 %[f7], 100(%[tmp_a]) " + "\n\t" + "add.s %[f8], %[f0], %[f2] " + "\n\t" + "sub.s %[f0], %[f0], %[f2] " + "\n\t" + "add.s %[f2], %[f4], %[f6] " + "\n\t" + "sub.s %[f4], %[f4], %[f6] " + "\n\t" + "add.s %[f6], %[f1], %[f3] " + "\n\t" + "sub.s %[f1], %[f1], %[f3] " + "\n\t" + "add.s %[f3], %[f5], %[f7] " + "\n\t" + "sub.s %[f5], %[f5], %[f7] " + "\n\t" + "sub.s %[f7], %[f2], %[f8] " + "\n\t" + "add.s %[f2], %[f2], %[f8] " + "\n\t" + "add.s %[f8], %[f1], %[f4] " + "\n\t" + "sub.s %[f1], %[f1], %[f4] " + "\n\t" + "sub.s %[f4], %[f3], %[f6] " + "\n\t" + "add.s %[f3], %[f3], %[f6] " + "\n\t" + "sub.s %[f6], %[f0], %[f5] " + "\n\t" + "add.s %[f0], %[f0], %[f5] " + "\n\t" + "swc1 %[f2], 0(%[tmp_a]) " + "\n\t" + "swc1 %[f3], 4(%[tmp_a]) " + "\n\t" + "mul.s %[f5], %[f10], %[f7] " + "\n\t" +#if defined(MIPS32_R2_LE) + "mul.s %[f7], %[f9], %[f7] " + "\n\t" + "mul.s %[f2], %[f12], %[f8] " + "\n\t" + "mul.s %[f8], %[f11], %[f8] " + "\n\t" + "mul.s %[f3], %[f14], %[f1] " + "\n\t" + "mul.s %[f1], %[f13], %[f1] " + "\n\t" + "madd.s %[f5], %[f5], %[f9], %[f4] " + "\n\t" + "msub.s %[f7], %[f7], %[f10], %[f4] " + "\n\t" + "msub.s %[f2], %[f2], %[f11], %[f6] " + "\n\t" + "madd.s %[f8], %[f8], %[f12], %[f6] " + "\n\t" + "msub.s %[f3], %[f3], %[f13], %[f0] " + "\n\t" + "madd.s %[f1], %[f1], %[f14], %[f0] " + "\n\t" + "swc1 %[f5], 64(%[tmp_a]) " + "\n\t" + "swc1 %[f7], 68(%[tmp_a]) " + "\n\t" +#else + "mul.s %[f2], %[f9], %[f4] " + "\n\t" + "mul.s %[f4], %[f10], %[f4] " + "\n\t" + "mul.s %[f7], %[f9], %[f7] " + "\n\t" + "mul.s %[f3], %[f11], %[f6] " + "\n\t" + "mul.s %[f6], %[f12], %[f6] " + "\n\t" + "add.s %[f5], %[f5], %[f2] " + "\n\t" + "sub.s %[f7], %[f4], %[f7] " + "\n\t" + "mul.s %[f2], %[f12], %[f8] " + "\n\t" + "mul.s %[f8], %[f11], %[f8] " + "\n\t" + "mul.s %[f4], %[f14], %[f1] " + "\n\t" + "mul.s %[f1], %[f13], %[f1] " + "\n\t" + "sub.s %[f2], %[f3], %[f2] " + "\n\t" + "mul.s %[f3], %[f13], %[f0] " + "\n\t" + "mul.s %[f0], %[f14], %[f0] " + "\n\t" + "add.s %[f8], %[f8], %[f6] " + "\n\t" + "swc1 %[f5], 64(%[tmp_a]) " + "\n\t" + "swc1 %[f7], 68(%[tmp_a]) " + "\n\t" + "sub.s %[f3], %[f3], %[f4] " + "\n\t" + "add.s %[f1], %[f1], %[f0] " + "\n\t" +#endif + "swc1 %[f2], 32(%[tmp_a]) " + "\n\t" + "swc1 %[f8], 36(%[tmp_a]) " + "\n\t" + "swc1 %[f3], 96(%[tmp_a]) " + "\n\t" + "swc1 %[f1], 100(%[tmp_a]) " + "\n\t" + "bgtz %[count], 1b " + "\n\t" + " addiu %[tmp_a], %[tmp_a], 8 " + "\n\t" + ".set pop " + "\n\t" + : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), + [f4] "=&f"(f4), [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), + [f8] "=&f"(f8), [tmp_a] "=&r"(tmp_a), [count] "=&r"(count) + : [a] "r"(a), [f9] "f"(f9), [f10] "f"(f10), [f11] "f"(f11), + [f12] "f"(f12), [f13] "f"(f13), [f14] "f"(f14) + : "memory"); +} + +void cftfsub_128_mips(float* a) { + float f0, f1, f2, f3, f4, f5, f6, f7, f8; + int tmp_a, count; + + cft1st_128_mips(a); + cftmdl_128_mips(a); + + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmp_a], %[a], 0 \n\t" + "addiu %[count], $zero, 16 \n\t" + "1: \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f0], 0(%[tmp_a]) \n\t" + "lwc1 %[f2], 128(%[tmp_a]) \n\t" + "lwc1 %[f4], 256(%[tmp_a]) \n\t" + "lwc1 %[f6], 384(%[tmp_a]) \n\t" + "lwc1 %[f1], 4(%[tmp_a]) \n\t" + "lwc1 %[f3], 132(%[tmp_a]) \n\t" + "lwc1 %[f5], 260(%[tmp_a]) \n\t" + "lwc1 %[f7], 388(%[tmp_a]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f1], %[f3] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f8], %[f2] \n\t" + "add.s %[f2], %[f1], %[f4] \n\t" + "sub.s %[f1], %[f1], %[f4] \n\t" + "add.s %[f4], %[f6], %[f3] \n\t" + "sub.s %[f6], %[f6], %[f3] \n\t" + "sub.s %[f3], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "swc1 %[f7], 0(%[tmp_a]) \n\t" + "swc1 %[f8], 256(%[tmp_a]) \n\t" + "swc1 %[f2], 132(%[tmp_a]) \n\t" + "swc1 %[f1], 388(%[tmp_a]) \n\t" + "swc1 %[f4], 4(%[tmp_a]) \n\t" + "swc1 %[f6], 260(%[tmp_a]) \n\t" + "swc1 %[f3], 128(%[tmp_a]) \n\t" + "swc1 %[f0], 384(%[tmp_a]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[tmp_a], %[tmp_a], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), + [f4] "=&f"(f4), [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), + [f8] "=&f"(f8), [tmp_a] "=&r"(tmp_a), [count] "=&r"(count) + : [a] "r"(a) + : "memory"); +} + +void cftbsub_128_mips(float* a) { + float f0, f1, f2, f3, f4, f5, f6, f7, f8; + int tmp_a, count; + + cft1st_128_mips(a); + cftmdl_128_mips(a); + + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[tmp_a], %[a], 0 \n\t" + "addiu %[count], $zero, 16 \n\t" + "1: \n\t" + "addiu %[count], %[count], -1 \n\t" + "lwc1 %[f0], 0(%[tmp_a]) \n\t" + "lwc1 %[f2], 128(%[tmp_a]) \n\t" + "lwc1 %[f4], 256(%[tmp_a]) \n\t" + "lwc1 %[f6], 384(%[tmp_a]) \n\t" + "lwc1 %[f1], 4(%[tmp_a]) \n\t" + "lwc1 %[f3], 132(%[tmp_a]) \n\t" + "lwc1 %[f5], 260(%[tmp_a]) \n\t" + "lwc1 %[f7], 388(%[tmp_a]) \n\t" + "add.s %[f8], %[f0], %[f2] \n\t" + "sub.s %[f0], %[f0], %[f2] \n\t" + "add.s %[f2], %[f4], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "add.s %[f6], %[f1], %[f3] \n\t" + "sub.s %[f1], %[f3], %[f1] \n\t" + "add.s %[f3], %[f5], %[f7] \n\t" + "sub.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f7], %[f8], %[f2] \n\t" + "sub.s %[f8], %[f8], %[f2] \n\t" + "sub.s %[f2], %[f1], %[f4] \n\t" + "add.s %[f1], %[f1], %[f4] \n\t" + "add.s %[f4], %[f3], %[f6] \n\t" + "sub.s %[f6], %[f3], %[f6] \n\t" + "sub.s %[f3], %[f0], %[f5] \n\t" + "add.s %[f0], %[f0], %[f5] \n\t" + "neg.s %[f4], %[f4] \n\t" + "swc1 %[f7], 0(%[tmp_a]) \n\t" + "swc1 %[f8], 256(%[tmp_a]) \n\t" + "swc1 %[f2], 132(%[tmp_a]) \n\t" + "swc1 %[f1], 388(%[tmp_a]) \n\t" + "swc1 %[f6], 260(%[tmp_a]) \n\t" + "swc1 %[f3], 128(%[tmp_a]) \n\t" + "swc1 %[f0], 384(%[tmp_a]) \n\t" + "swc1 %[f4], 4(%[tmp_a]) \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[tmp_a], %[tmp_a], 8 \n\t" + ".set pop \n\t" + : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), + [f4] "=&f"(f4), [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), + [f8] "=&f"(f8), [tmp_a] "=&r"(tmp_a), [count] "=&r"(count) + : [a] "r"(a) + : "memory"); +} + +void rftfsub_128_mips(float* a) { + const float* c = rdft_w + 32; + const float f0 = 0.5f; + float* a1 = &a[2]; + float* a2 = &a[126]; + const float* c1 = &c[1]; + const float* c2 = &c[31]; + float f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15; + int count; + + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "lwc1 %[f6], 0(%[c2]) \n\t" + "lwc1 %[f1], 0(%[a1]) \n\t" + "lwc1 %[f2], 0(%[a2]) \n\t" + "lwc1 %[f3], 4(%[a1]) \n\t" + "lwc1 %[f4], 4(%[a2]) \n\t" + "lwc1 %[f5], 0(%[c1]) \n\t" + "sub.s %[f6], %[f0], %[f6] \n\t" + "sub.s %[f7], %[f1], %[f2] \n\t" + "add.s %[f8], %[f3], %[f4] \n\t" + "addiu %[count], $zero, 15 \n\t" + "mul.s %[f9], %[f6], %[f7] \n\t" + "mul.s %[f6], %[f6], %[f8] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f8], %[f5], %[f8] \n\t" + "mul.s %[f5], %[f5], %[f7] \n\t" + "sub.s %[f9], %[f9], %[f8] \n\t" + "add.s %[f6], %[f6], %[f5] \n\t" +#else + "nmsub.s %[f9], %[f9], %[f5], %[f8] \n\t" + "madd.s %[f6], %[f6], %[f5], %[f7] \n\t" +#endif + "sub.s %[f1], %[f1], %[f9] \n\t" + "add.s %[f2], %[f2], %[f9] \n\t" + "sub.s %[f3], %[f3], %[f6] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "swc1 %[f3], 4(%[a1]) \n\t" + "swc1 %[f4], 4(%[a2]) \n\t" + "addiu %[a1], %[a1], 8 \n\t" + "addiu %[a2], %[a2], -8 \n\t" + "addiu %[c1], %[c1], 4 \n\t" + "addiu %[c2], %[c2], -4 \n\t" + "1: \n\t" + "lwc1 %[f6], 0(%[c2]) \n\t" + "lwc1 %[f1], 0(%[a1]) \n\t" + "lwc1 %[f2], 0(%[a2]) \n\t" + "lwc1 %[f3], 4(%[a1]) \n\t" + "lwc1 %[f4], 4(%[a2]) \n\t" + "lwc1 %[f5], 0(%[c1]) \n\t" + "sub.s %[f6], %[f0], %[f6] \n\t" + "sub.s %[f7], %[f1], %[f2] \n\t" + "add.s %[f8], %[f3], %[f4] \n\t" + "lwc1 %[f10], -4(%[c2]) \n\t" + "lwc1 %[f11], 8(%[a1]) \n\t" + "lwc1 %[f12], -8(%[a2]) \n\t" + "mul.s %[f9], %[f6], %[f7] \n\t" + "mul.s %[f6], %[f6], %[f8] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f8], %[f5], %[f8] \n\t" + "mul.s %[f5], %[f5], %[f7] \n\t" + "lwc1 %[f13], 12(%[a1]) \n\t" + "lwc1 %[f14], -4(%[a2]) \n\t" + "lwc1 %[f15], 4(%[c1]) \n\t" + "sub.s %[f9], %[f9], %[f8] \n\t" + "add.s %[f6], %[f6], %[f5] \n\t" +#else + "lwc1 %[f13], 12(%[a1]) \n\t" + "lwc1 %[f14], -4(%[a2]) \n\t" + "lwc1 %[f15], 4(%[c1]) \n\t" + "nmsub.s %[f9], %[f9], %[f5], %[f8] \n\t" + "madd.s %[f6], %[f6], %[f5], %[f7] \n\t" +#endif + "sub.s %[f10], %[f0], %[f10] \n\t" + "sub.s %[f5], %[f11], %[f12] \n\t" + "add.s %[f7], %[f13], %[f14] \n\t" + "sub.s %[f1], %[f1], %[f9] \n\t" + "add.s %[f2], %[f2], %[f9] \n\t" + "sub.s %[f3], %[f3], %[f6] \n\t" + "mul.s %[f8], %[f10], %[f5] \n\t" + "mul.s %[f10], %[f10], %[f7] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f9], %[f15], %[f7] \n\t" + "mul.s %[f15], %[f15], %[f5] \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "sub.s %[f8], %[f8], %[f9] \n\t" + "add.s %[f10], %[f10], %[f15] \n\t" +#else + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "sub.s %[f4], %[f4], %[f6] \n\t" + "nmsub.s %[f8], %[f8], %[f15], %[f7] \n\t" + "madd.s %[f10], %[f10], %[f15], %[f5] \n\t" +#endif + "swc1 %[f3], 4(%[a1]) \n\t" + "swc1 %[f4], 4(%[a2]) \n\t" + "sub.s %[f11], %[f11], %[f8] \n\t" + "add.s %[f12], %[f12], %[f8] \n\t" + "sub.s %[f13], %[f13], %[f10] \n\t" + "sub.s %[f14], %[f14], %[f10] \n\t" + "addiu %[c2], %[c2], -8 \n\t" + "addiu %[c1], %[c1], 8 \n\t" + "swc1 %[f11], 8(%[a1]) \n\t" + "swc1 %[f12], -8(%[a2]) \n\t" + "swc1 %[f13], 12(%[a1]) \n\t" + "swc1 %[f14], -4(%[a2]) \n\t" + "addiu %[a1], %[a1], 16 \n\t" + "addiu %[count], %[count], -1 \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[a2], %[a2], -16 \n\t" + ".set pop \n\t" + : [a1] "+r"(a1), [a2] "+r"(a2), [c1] "+r"(c1), [c2] "+r"(c2), + [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), [f4] "=&f"(f4), + [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), [f8] "=&f"(f8), + [f9] "=&f"(f9), [f10] "=&f"(f10), [f11] "=&f"(f11), [f12] "=&f"(f12), + [f13] "=&f"(f13), [f14] "=&f"(f14), [f15] "=&f"(f15), + [count] "=&r"(count) + : [f0] "f"(f0) + : "memory"); +} + +void rftbsub_128_mips(float* a) { + const float* c = rdft_w + 32; + const float f0 = 0.5f; + float* a1 = &a[2]; + float* a2 = &a[126]; + const float* c1 = &c[1]; + const float* c2 = &c[31]; + float f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15; + int count; + + a[1] = -a[1]; + a[65] = -a[65]; + + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "lwc1 %[f6], 0(%[c2]) \n\t" + "lwc1 %[f1], 0(%[a1]) \n\t" + "lwc1 %[f2], 0(%[a2]) \n\t" + "lwc1 %[f3], 4(%[a1]) \n\t" + "lwc1 %[f4], 4(%[a2]) \n\t" + "lwc1 %[f5], 0(%[c1]) \n\t" + "sub.s %[f6], %[f0], %[f6] \n\t" + "sub.s %[f7], %[f1], %[f2] \n\t" + "add.s %[f8], %[f3], %[f4] \n\t" + "addiu %[count], $zero, 15 \n\t" + "mul.s %[f9], %[f6], %[f7] \n\t" + "mul.s %[f6], %[f6], %[f8] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f8], %[f5], %[f8] \n\t" + "mul.s %[f5], %[f5], %[f7] \n\t" + "add.s %[f9], %[f9], %[f8] \n\t" + "sub.s %[f6], %[f6], %[f5] \n\t" +#else + "madd.s %[f9], %[f9], %[f5], %[f8] \n\t" + "nmsub.s %[f6], %[f6], %[f5], %[f7] \n\t" +#endif + "sub.s %[f1], %[f1], %[f9] \n\t" + "add.s %[f2], %[f2], %[f9] \n\t" + "sub.s %[f3], %[f6], %[f3] \n\t" + "sub.s %[f4], %[f6], %[f4] \n\t" + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "swc1 %[f3], 4(%[a1]) \n\t" + "swc1 %[f4], 4(%[a2]) \n\t" + "addiu %[a1], %[a1], 8 \n\t" + "addiu %[a2], %[a2], -8 \n\t" + "addiu %[c1], %[c1], 4 \n\t" + "addiu %[c2], %[c2], -4 \n\t" + "1: \n\t" + "lwc1 %[f6], 0(%[c2]) \n\t" + "lwc1 %[f1], 0(%[a1]) \n\t" + "lwc1 %[f2], 0(%[a2]) \n\t" + "lwc1 %[f3], 4(%[a1]) \n\t" + "lwc1 %[f4], 4(%[a2]) \n\t" + "lwc1 %[f5], 0(%[c1]) \n\t" + "sub.s %[f6], %[f0], %[f6] \n\t" + "sub.s %[f7], %[f1], %[f2] \n\t" + "add.s %[f8], %[f3], %[f4] \n\t" + "lwc1 %[f10], -4(%[c2]) \n\t" + "lwc1 %[f11], 8(%[a1]) \n\t" + "lwc1 %[f12], -8(%[a2]) \n\t" + "mul.s %[f9], %[f6], %[f7] \n\t" + "mul.s %[f6], %[f6], %[f8] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f8], %[f5], %[f8] \n\t" + "mul.s %[f5], %[f5], %[f7] \n\t" + "lwc1 %[f13], 12(%[a1]) \n\t" + "lwc1 %[f14], -4(%[a2]) \n\t" + "lwc1 %[f15], 4(%[c1]) \n\t" + "add.s %[f9], %[f9], %[f8] \n\t" + "sub.s %[f6], %[f6], %[f5] \n\t" +#else + "lwc1 %[f13], 12(%[a1]) \n\t" + "lwc1 %[f14], -4(%[a2]) \n\t" + "lwc1 %[f15], 4(%[c1]) \n\t" + "madd.s %[f9], %[f9], %[f5], %[f8] \n\t" + "nmsub.s %[f6], %[f6], %[f5], %[f7] \n\t" +#endif + "sub.s %[f10], %[f0], %[f10] \n\t" + "sub.s %[f5], %[f11], %[f12] \n\t" + "add.s %[f7], %[f13], %[f14] \n\t" + "sub.s %[f1], %[f1], %[f9] \n\t" + "add.s %[f2], %[f2], %[f9] \n\t" + "sub.s %[f3], %[f6], %[f3] \n\t" + "mul.s %[f8], %[f10], %[f5] \n\t" + "mul.s %[f10], %[f10], %[f7] \n\t" +#if !defined(MIPS32_R2_LE) + "mul.s %[f9], %[f15], %[f7] \n\t" + "mul.s %[f15], %[f15], %[f5] \n\t" + "sub.s %[f4], %[f6], %[f4] \n\t" + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "add.s %[f8], %[f8], %[f9] \n\t" + "sub.s %[f10], %[f10], %[f15] \n\t" +#else + "swc1 %[f1], 0(%[a1]) \n\t" + "swc1 %[f2], 0(%[a2]) \n\t" + "sub.s %[f4], %[f6], %[f4] \n\t" + "madd.s %[f8], %[f8], %[f15], %[f7] \n\t" + "nmsub.s %[f10], %[f10], %[f15], %[f5] \n\t" +#endif + "swc1 %[f3], 4(%[a1]) \n\t" + "swc1 %[f4], 4(%[a2]) \n\t" + "sub.s %[f11], %[f11], %[f8] \n\t" + "add.s %[f12], %[f12], %[f8] \n\t" + "sub.s %[f13], %[f10], %[f13] \n\t" + "sub.s %[f14], %[f10], %[f14] \n\t" + "addiu %[c2], %[c2], -8 \n\t" + "addiu %[c1], %[c1], 8 \n\t" + "swc1 %[f11], 8(%[a1]) \n\t" + "swc1 %[f12], -8(%[a2]) \n\t" + "swc1 %[f13], 12(%[a1]) \n\t" + "swc1 %[f14], -4(%[a2]) \n\t" + "addiu %[a1], %[a1], 16 \n\t" + "addiu %[count], %[count], -1 \n\t" + "bgtz %[count], 1b \n\t" + " addiu %[a2], %[a2], -16 \n\t" + ".set pop \n\t" + : [a1] "+r"(a1), [a2] "+r"(a2), [c1] "+r"(c1), [c2] "+r"(c2), + [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), [f4] "=&f"(f4), + [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), [f8] "=&f"(f8), + [f9] "=&f"(f9), [f10] "=&f"(f10), [f11] "=&f"(f11), [f12] "=&f"(f12), + [f13] "=&f"(f13), [f14] "=&f"(f14), [f15] "=&f"(f15), + [count] "=&r"(count) + : [f0] "f"(f0) + : "memory"); +} +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec/aec_rdft_neon.c b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_neon.cc similarity index 94% rename from webrtc/modules/audio_processing/aec/aec_rdft_neon.c rename to webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_neon.cc index 43b6a68..acab972 100644 --- a/webrtc/modules/audio_processing/aec/aec_rdft_neon.c +++ b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_neon.cc @@ -14,15 +14,16 @@ * Based on the sse2 version. */ - -#include "webrtc/modules/audio_processing/aec/aec_rdft.h" - #include -static const ALIGN16_BEG float ALIGN16_END - k_swap_sign[4] = {-1.f, 1.f, -1.f, 1.f}; +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h" -static void cft1st_128_neon(float* a) { +namespace webrtc { + +#if defined(WEBRTC_HAS_NEON) +void cft1st_128_neon(float* a) { const float32x4_t vec_swap_sign = vld1q_f32((float32_t*)k_swap_sign); int j, k2; @@ -71,7 +72,7 @@ static void cft1st_128_neon(float* a) { } } -static void cftmdl_128_neon(float* a) { +void cftmdl_128_neon(float* a) { int j; const int l = 8; const float32x4_t vec_swap_sign = vld1q_f32((float32_t*)k_swap_sign); @@ -185,7 +186,7 @@ __inline static float32x4_t reverse_order_f32x4(float32x4_t in) { return vrev64q_f32(rev); } -static void rftfsub_128_neon(float* a) { +void rftfsub_128_neon(float* a) { const float* c = rdft_w + 32; int j1, j2; const float32x4_t mm_half = vdupq_n_f32(0.5f); @@ -264,7 +265,7 @@ static void rftfsub_128_neon(float* a) { } } -static void rftbsub_128_neon(float* a) { +void rftbsub_128_neon(float* a) { const float* c = rdft_w + 32; int j1, j2; const float32x4_t mm_half = vdupq_n_f32(0.5f); @@ -274,11 +275,11 @@ static void rftbsub_128_neon(float* a) { // Note: commented number are indexes for the first iteration of the loop. for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) { // Load 'wk'. - const float32x4_t c_j1 = vld1q_f32(&c[j1]); // 1, 2, 3, 4, - const float32x4_t c_k1 = vld1q_f32(&c[29 - j1]); // 28, 29, 30, 31, - const float32x4_t wkrt = vsubq_f32(mm_half, c_k1); // 28, 29, 30, 31, - const float32x4_t wkr_ = reverse_order_f32x4(wkrt); // 31, 30, 29, 28, - const float32x4_t wki_ = c_j1; // 1, 2, 3, 4, + const float32x4_t c_j1 = vld1q_f32(&c[j1]); // 1, 2, 3, 4, + const float32x4_t c_k1 = vld1q_f32(&c[29 - j1]); // 28, 29, 30, 31, + const float32x4_t wkrt = vsubq_f32(mm_half, c_k1); // 28, 29, 30, 31, + const float32x4_t wkr_ = reverse_order_f32x4(wkrt); // 31, 30, 29, 28, + const float32x4_t wki_ = c_j1; // 1, 2, 3, 4, // Load and shuffle 'a'. // 2, 4, 6, 8, 3, 5, 7, 9 float32x4x2_t a_j2_p = vld2q_f32(&a[0 + j2]); @@ -345,11 +346,6 @@ static void rftbsub_128_neon(float* a) { } a[65] = -a[65]; } +#endif -void aec_rdft_init_neon(void) { - cft1st_128 = cft1st_128_neon; - cftmdl_128 = cftmdl_128_neon; - rftfsub_128 = rftfsub_128_neon; - rftbsub_128 = rftbsub_128_neon; -} - +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec/aec_rdft_sse2.c b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_sse2.cc similarity index 85% rename from webrtc/modules/audio_processing/aec/aec_rdft_sse2.c rename to webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_sse2.cc index b4e453f..7f0802d 100644 --- a/webrtc/modules/audio_processing/aec/aec_rdft_sse2.c +++ b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_sse2.cc @@ -8,14 +8,33 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/aec/aec_rdft.h" - #include +#include -static const ALIGN16_BEG float ALIGN16_END - k_swap_sign[4] = {-1.f, 1.f, -1.f, 1.f}; +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h" +#include "rtc_base/system/arch.h" -static void cft1st_128_SSE2(float* a) { +namespace webrtc { + +#if defined(WEBRTC_ARCH_X86_FAMILY) + +namespace { +// These intrinsics were unavailable before VS 2008. +// TODO(andrew): move to a common file. +#if defined(_MSC_VER) && _MSC_VER < 1500 +static __inline __m128 _mm_castsi128_ps(__m128i a) { + return *(__m128*)&a; +} +static __inline __m128i _mm_castps_si128(__m128 a) { + return *(__m128i*)&a; +} +#endif + +} // namespace + +void cft1st_128_SSE2(float* a) { const __m128 mm_swap_sign = _mm_load_ps(k_swap_sign); int j, k2; @@ -78,7 +97,7 @@ static void cft1st_128_SSE2(float* a) { } } -static void cftmdl_128_SSE2(float* a) { +void cftmdl_128_SSE2(float* a) { const int l = 8; const __m128 mm_swap_sign = _mm_load_ps(k_swap_sign); int j0; @@ -89,12 +108,12 @@ static void cftmdl_128_SSE2(float* a) { const __m128i a_08 = _mm_loadl_epi64((__m128i*)&a[j0 + 8]); const __m128i a_32 = _mm_loadl_epi64((__m128i*)&a[j0 + 32]); const __m128i a_40 = _mm_loadl_epi64((__m128i*)&a[j0 + 40]); - const __m128 a_00_32 = _mm_shuffle_ps(_mm_castsi128_ps(a_00), - _mm_castsi128_ps(a_32), - _MM_SHUFFLE(1, 0, 1, 0)); - const __m128 a_08_40 = _mm_shuffle_ps(_mm_castsi128_ps(a_08), - _mm_castsi128_ps(a_40), - _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_00_32 = + _mm_shuffle_ps(_mm_castsi128_ps(a_00), _mm_castsi128_ps(a_32), + _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_08_40 = + _mm_shuffle_ps(_mm_castsi128_ps(a_08), _mm_castsi128_ps(a_40), + _MM_SHUFFLE(1, 0, 1, 0)); __m128 x0r0_0i0_0r1_x0i1 = _mm_add_ps(a_00_32, a_08_40); const __m128 x1r0_1i0_1r1_x1i1 = _mm_sub_ps(a_00_32, a_08_40); @@ -102,12 +121,12 @@ static void cftmdl_128_SSE2(float* a) { const __m128i a_24 = _mm_loadl_epi64((__m128i*)&a[j0 + 24]); const __m128i a_48 = _mm_loadl_epi64((__m128i*)&a[j0 + 48]); const __m128i a_56 = _mm_loadl_epi64((__m128i*)&a[j0 + 56]); - const __m128 a_16_48 = _mm_shuffle_ps(_mm_castsi128_ps(a_16), - _mm_castsi128_ps(a_48), - _MM_SHUFFLE(1, 0, 1, 0)); - const __m128 a_24_56 = _mm_shuffle_ps(_mm_castsi128_ps(a_24), - _mm_castsi128_ps(a_56), - _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_16_48 = + _mm_shuffle_ps(_mm_castsi128_ps(a_16), _mm_castsi128_ps(a_48), + _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_24_56 = + _mm_shuffle_ps(_mm_castsi128_ps(a_24), _mm_castsi128_ps(a_56), + _MM_SHUFFLE(1, 0, 1, 0)); const __m128 x2r0_2i0_2r1_x2i1 = _mm_add_ps(a_16_48, a_24_56); const __m128 x3r0_3i0_3r1_x3i1 = _mm_sub_ps(a_16_48, a_24_56); @@ -163,12 +182,12 @@ static void cftmdl_128_SSE2(float* a) { const __m128i a_08 = _mm_loadl_epi64((__m128i*)&a[j0 + 8]); const __m128i a_32 = _mm_loadl_epi64((__m128i*)&a[j0 + 32]); const __m128i a_40 = _mm_loadl_epi64((__m128i*)&a[j0 + 40]); - const __m128 a_00_32 = _mm_shuffle_ps(_mm_castsi128_ps(a_00), - _mm_castsi128_ps(a_32), - _MM_SHUFFLE(1, 0, 1, 0)); - const __m128 a_08_40 = _mm_shuffle_ps(_mm_castsi128_ps(a_08), - _mm_castsi128_ps(a_40), - _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_00_32 = + _mm_shuffle_ps(_mm_castsi128_ps(a_00), _mm_castsi128_ps(a_32), + _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_08_40 = + _mm_shuffle_ps(_mm_castsi128_ps(a_08), _mm_castsi128_ps(a_40), + _MM_SHUFFLE(1, 0, 1, 0)); __m128 x0r0_0i0_0r1_x0i1 = _mm_add_ps(a_00_32, a_08_40); const __m128 x1r0_1i0_1r1_x1i1 = _mm_sub_ps(a_00_32, a_08_40); @@ -176,22 +195,21 @@ static void cftmdl_128_SSE2(float* a) { const __m128i a_24 = _mm_loadl_epi64((__m128i*)&a[j0 + 24]); const __m128i a_48 = _mm_loadl_epi64((__m128i*)&a[j0 + 48]); const __m128i a_56 = _mm_loadl_epi64((__m128i*)&a[j0 + 56]); - const __m128 a_16_48 = _mm_shuffle_ps(_mm_castsi128_ps(a_16), - _mm_castsi128_ps(a_48), - _MM_SHUFFLE(1, 0, 1, 0)); - const __m128 a_24_56 = _mm_shuffle_ps(_mm_castsi128_ps(a_24), - _mm_castsi128_ps(a_56), - _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_16_48 = + _mm_shuffle_ps(_mm_castsi128_ps(a_16), _mm_castsi128_ps(a_48), + _MM_SHUFFLE(1, 0, 1, 0)); + const __m128 a_24_56 = + _mm_shuffle_ps(_mm_castsi128_ps(a_24), _mm_castsi128_ps(a_56), + _MM_SHUFFLE(1, 0, 1, 0)); const __m128 x2r0_2i0_2r1_x2i1 = _mm_add_ps(a_16_48, a_24_56); const __m128 x3r0_3i0_3r1_x3i1 = _mm_sub_ps(a_16_48, a_24_56); const __m128 xx = _mm_add_ps(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); const __m128 xx1 = _mm_sub_ps(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); const __m128 xx2 = _mm_mul_ps(xx1, wk2rv); - const __m128 xx3 = - _mm_mul_ps(wk2iv, - _mm_castsi128_ps(_mm_shuffle_epi32( - _mm_castps_si128(xx1), _MM_SHUFFLE(2, 3, 0, 1)))); + const __m128 xx3 = _mm_mul_ps( + wk2iv, _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(xx1), + _MM_SHUFFLE(2, 3, 0, 1)))); const __m128 xx4 = _mm_add_ps(xx2, xx3); const __m128 x3i0_3r0_3i1_x3r1 = _mm_castsi128_ps(_mm_shuffle_epi32( @@ -202,16 +220,14 @@ static void cftmdl_128_SSE2(float* a) { const __m128 xx10 = _mm_mul_ps(x1_x3_add, wk1rv); const __m128 xx11 = _mm_mul_ps( - wk1iv, - _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(x1_x3_add), - _MM_SHUFFLE(2, 3, 0, 1)))); + wk1iv, _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(x1_x3_add), + _MM_SHUFFLE(2, 3, 0, 1)))); const __m128 xx12 = _mm_add_ps(xx10, xx11); const __m128 xx20 = _mm_mul_ps(x1_x3_sub, wk3rv); const __m128 xx21 = _mm_mul_ps( - wk3iv, - _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(x1_x3_sub), - _MM_SHUFFLE(2, 3, 0, 1)))); + wk3iv, _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(x1_x3_sub), + _MM_SHUFFLE(2, 3, 0, 1)))); const __m128 xx22 = _mm_add_ps(xx20, xx21); _mm_storel_epi64((__m128i*)&a[j0 + 0], _mm_castps_si128(xx)); @@ -237,13 +253,13 @@ static void cftmdl_128_SSE2(float* a) { } } -static void rftfsub_128_SSE2(float* a) { +void rftfsub_128_SSE2(float* a) { const float* c = rdft_w + 32; int j1, j2, k1, k2; float wkr, wki, xr, xi, yr, yi; - static const ALIGN16_BEG float ALIGN16_END - k_half[4] = {0.5f, 0.5f, 0.5f, 0.5f}; + static const ALIGN16_BEG float ALIGN16_END k_half[4] = {0.5f, 0.5f, 0.5f, + 0.5f}; const __m128 mm_half = _mm_load_ps(k_half); // Vectorized code (four at once). @@ -327,13 +343,13 @@ static void rftfsub_128_SSE2(float* a) { } } -static void rftbsub_128_SSE2(float* a) { +void rftbsub_128_SSE2(float* a) { const float* c = rdft_w + 32; int j1, j2, k1, k2; float wkr, wki, xr, xi, yr, yi; - static const ALIGN16_BEG float ALIGN16_END - k_half[4] = {0.5f, 0.5f, 0.5f, 0.5f}; + static const ALIGN16_BEG float ALIGN16_END k_half[4] = {0.5f, 0.5f, 0.5f, + 0.5f}; const __m128 mm_half = _mm_load_ps(k_half); a[1] = -a[1]; @@ -418,10 +434,6 @@ static void rftbsub_128_SSE2(float* a) { } a[65] = -a[65]; } +#endif -void aec_rdft_init_sse2(void) { - cft1st_128 = cft1st_128_SSE2; - cftmdl_128 = cftmdl_128_SSE2; - rftfsub_128 = rftfsub_128_SSE2; - rftbsub_128 = rftbsub_128_SSE2; -} +} // namespace webrtc diff --git a/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h new file mode 100644 index 0000000..6db1dd9 --- /dev/null +++ b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_common.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_COMMON_H_ + +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" + +namespace webrtc { + +// This tables used to be computed at run-time. For example, refer to: +// https://code.google.com/p/webrtc/source/browse/trunk/webrtc/modules/audio_processing/utility/apm_rdft.c?r=6564 +// to see the initialization code. +// Constants shared by all paths (C, SSE2, NEON). +const float rdft_w[64] = { + 1.0000000000f, 0.0000000000f, 0.7071067691f, 0.7071067691f, 0.9238795638f, + 0.3826834559f, 0.3826834559f, 0.9238795638f, 0.9807852507f, 0.1950903237f, + 0.5555702448f, 0.8314695954f, 0.8314695954f, 0.5555702448f, 0.1950903237f, + 0.9807852507f, 0.9951847196f, 0.0980171412f, 0.6343933344f, 0.7730104327f, + 0.8819212914f, 0.4713967443f, 0.2902846634f, 0.9569403529f, 0.9569403529f, + 0.2902846634f, 0.4713967443f, 0.8819212914f, 0.7730104327f, 0.6343933344f, + 0.0980171412f, 0.9951847196f, 0.7071067691f, 0.4993977249f, 0.4975923598f, + 0.4945882559f, 0.4903926253f, 0.4850156307f, 0.4784701765f, 0.4707720280f, + 0.4619397819f, 0.4519946277f, 0.4409606457f, 0.4288643003f, 0.4157347977f, + 0.4016037583f, 0.3865052164f, 0.3704755902f, 0.3535533845f, 0.3357794881f, + 0.3171966672f, 0.2978496552f, 0.2777851224f, 0.2570513785f, 0.2356983721f, + 0.2137775421f, 0.1913417280f, 0.1684449315f, 0.1451423317f, 0.1214900985f, + 0.0975451618f, 0.0733652338f, 0.0490085706f, 0.0245338380f, +}; + +// Constants used by the C and MIPS paths. +const float rdft_wk3ri_first[16] = { + 1.000000000f, 0.000000000f, 0.382683456f, 0.923879564f, + 0.831469536f, 0.555570245f, -0.195090353f, 0.980785251f, + 0.956940353f, 0.290284693f, 0.098017156f, 0.995184720f, + 0.634393334f, 0.773010492f, -0.471396863f, 0.881921172f, +}; +const float rdft_wk3ri_second[16] = { + -0.707106769f, 0.707106769f, -0.923879564f, -0.382683456f, + -0.980785251f, 0.195090353f, -0.555570245f, -0.831469536f, + -0.881921172f, 0.471396863f, -0.773010492f, -0.634393334f, + -0.995184720f, -0.098017156f, -0.290284693f, -0.956940353f, +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_COMMON_H_ diff --git a/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h new file mode 100644 index 0000000..a63d187 --- /dev/null +++ b/webrtc/common_audio/third_party/ooura/fft_size_128/ooura_fft_tables_neon_sse2.h @@ -0,0 +1,98 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_NEON_SSE2_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_NEON_SSE2_H_ + +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" +#include "rtc_base/system/arch.h" + +#ifdef _MSC_VER /* visual c++ */ +#define ALIGN16_BEG __declspec(align(16)) +#define ALIGN16_END +#else /* gcc or icc */ +#define ALIGN16_BEG +#define ALIGN16_END __attribute__((aligned(16))) +#endif + +namespace webrtc { + +// These tables used to be computed at run-time. For example, refer to: +// https://code.google.com/p/webrtc/source/browse/trunk/webrtc/modules/audio_processing/utility/apm_rdft.c?r=6564 +// to see the initialization code. +#if defined(WEBRTC_ARCH_X86_FAMILY) || defined(WEBRTC_HAS_NEON) +// Constants used by SSE2 and NEON but initialized in the C path. +const ALIGN16_BEG float ALIGN16_END k_swap_sign[4] = {-1.f, 1.f, -1.f, 1.f}; + +ALIGN16_BEG const float ALIGN16_END rdft_wk1r[32] = { + 1.000000000f, 1.000000000f, 0.707106769f, 0.707106769f, 0.923879564f, + 0.923879564f, 0.382683456f, 0.382683456f, 0.980785251f, 0.980785251f, + 0.555570245f, 0.555570245f, 0.831469595f, 0.831469595f, 0.195090324f, + 0.195090324f, 0.995184720f, 0.995184720f, 0.634393334f, 0.634393334f, + 0.881921291f, 0.881921291f, 0.290284663f, 0.290284663f, 0.956940353f, + 0.956940353f, 0.471396744f, 0.471396744f, 0.773010433f, 0.773010433f, + 0.098017141f, 0.098017141f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk2r[32] = { + 1.000000000f, 1.000000000f, -0.000000000f, -0.000000000f, 0.707106769f, + 0.707106769f, -0.707106769f, -0.707106769f, 0.923879564f, 0.923879564f, + -0.382683456f, -0.382683456f, 0.382683456f, 0.382683456f, -0.923879564f, + -0.923879564f, 0.980785251f, 0.980785251f, -0.195090324f, -0.195090324f, + 0.555570245f, 0.555570245f, -0.831469595f, -0.831469595f, 0.831469595f, + 0.831469595f, -0.555570245f, -0.555570245f, 0.195090324f, 0.195090324f, + -0.980785251f, -0.980785251f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk3r[32] = { + 1.000000000f, 1.000000000f, -0.707106769f, -0.707106769f, 0.382683456f, + 0.382683456f, -0.923879564f, -0.923879564f, 0.831469536f, 0.831469536f, + -0.980785251f, -0.980785251f, -0.195090353f, -0.195090353f, -0.555570245f, + -0.555570245f, 0.956940353f, 0.956940353f, -0.881921172f, -0.881921172f, + 0.098017156f, 0.098017156f, -0.773010492f, -0.773010492f, 0.634393334f, + 0.634393334f, -0.995184720f, -0.995184720f, -0.471396863f, -0.471396863f, + -0.290284693f, -0.290284693f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk1i[32] = { + -0.000000000f, 0.000000000f, -0.707106769f, 0.707106769f, -0.382683456f, + 0.382683456f, -0.923879564f, 0.923879564f, -0.195090324f, 0.195090324f, + -0.831469595f, 0.831469595f, -0.555570245f, 0.555570245f, -0.980785251f, + 0.980785251f, -0.098017141f, 0.098017141f, -0.773010433f, 0.773010433f, + -0.471396744f, 0.471396744f, -0.956940353f, 0.956940353f, -0.290284663f, + 0.290284663f, -0.881921291f, 0.881921291f, -0.634393334f, 0.634393334f, + -0.995184720f, 0.995184720f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk2i[32] = { + -0.000000000f, 0.000000000f, -1.000000000f, 1.000000000f, -0.707106769f, + 0.707106769f, -0.707106769f, 0.707106769f, -0.382683456f, 0.382683456f, + -0.923879564f, 0.923879564f, -0.923879564f, 0.923879564f, -0.382683456f, + 0.382683456f, -0.195090324f, 0.195090324f, -0.980785251f, 0.980785251f, + -0.831469595f, 0.831469595f, -0.555570245f, 0.555570245f, -0.555570245f, + 0.555570245f, -0.831469595f, 0.831469595f, -0.980785251f, 0.980785251f, + -0.195090324f, 0.195090324f, +}; +ALIGN16_BEG const float ALIGN16_END rdft_wk3i[32] = { + -0.000000000f, 0.000000000f, -0.707106769f, 0.707106769f, -0.923879564f, + 0.923879564f, 0.382683456f, -0.382683456f, -0.555570245f, 0.555570245f, + -0.195090353f, 0.195090353f, -0.980785251f, 0.980785251f, 0.831469536f, + -0.831469536f, -0.290284693f, 0.290284693f, -0.471396863f, 0.471396863f, + -0.995184720f, 0.995184720f, 0.634393334f, -0.634393334f, -0.773010492f, + 0.773010492f, 0.098017156f, -0.098017156f, -0.881921172f, 0.881921172f, + 0.956940353f, -0.956940353f, +}; +ALIGN16_BEG const float ALIGN16_END cftmdl_wk1r[4] = { + 0.707106769f, + 0.707106769f, + 0.707106769f, + -0.707106769f, +}; +#endif + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_OOURA_FFT_TABLES_NEON_SSE2_H_ diff --git a/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.cc b/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.cc new file mode 100644 index 0000000..d2f7c1c --- /dev/null +++ b/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.cc @@ -0,0 +1,866 @@ +/* + * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html + * Copyright Takuya OOURA, 1996-2001 + * + * You may use, copy, modify and distribute this code for any purpose (include + * commercial use) and without fee. Please refer to this package when you modify + * this code. + * + * Changes: + * Trivial type modifications by the WebRTC authors. + */ + +/* +Fast Fourier/Cosine/Sine Transform + dimension :one + data length :power of 2 + decimation :frequency + radix :4, 2 + data :inplace + table :use +functions + cdft: Complex Discrete Fourier Transform + rdft: Real Discrete Fourier Transform + ddct: Discrete Cosine Transform + ddst: Discrete Sine Transform + dfct: Cosine Transform of RDFT (Real Symmetric DFT) + dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) +function prototypes + void cdft(int, int, float *, int *, float *); + void rdft(size_t, int, float *, size_t *, float *); + void ddct(int, int, float *, int *, float *); + void ddst(int, int, float *, int *, float *); + void dfct(int, float *, float *, int *, float *); + void dfst(int, float *, float *, int *, float *); + + +-------- Complex DFT (Discrete Fourier Transform) -------- + [definition] + + X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k + X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k + ip[0] = 0; // first time only + cdft(2*n, 1, a, ip, w); + + ip[0] = 0; // first time only + cdft(2*n, -1, a, ip, w); + [parameters] + 2*n :data length (int) + n >= 1, n = power of 2 + a[0...2*n-1] :input/output data (float *) + input data + a[2*j] = Re(x[j]), + a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) + strictly, + length of ip >= + 2+(1<<(int)(log(n+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + cdft(2*n, -1, a, ip, w); + is + cdft(2*n, 1, a, ip, w); + for (j = 0; j <= 2 * n - 1; j++) { + a[j] *= 1.0 / n; + } + . + + +-------- Real DFT / Inverse of Real DFT -------- + [definition] + RDFT + R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 + I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0 IRDFT (excluding scale) + a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k + ip[0] = 0; // first time only + rdft(n, 1, a, ip, w); + + ip[0] = 0; // first time only + rdft(n, -1, a, ip, w); + [parameters] + n :data length (size_t) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + + output data + a[2*k] = R[k], 0<=k + input data + a[2*j] = R[j], 0<=j= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + rdft(n, 1, a, ip, w); + is + rdft(n, -1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- + [definition] + IDCT (excluding scale) + C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k DCT + C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k + ip[0] = 0; // first time only + ddct(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddct(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + output data + a[k] = C[k], 0<=k= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddct(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddct(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DST (Discrete Sine Transform) / Inverse of DST -------- + [definition] + IDST (excluding scale) + S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k DST + S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0 + ip[0] = 0; // first time only + ddst(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddst(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + + input data + a[j] = A[j], 0 + output data + a[k] = S[k], 0= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddst(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddst(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Cosine Transform of RDFT (Real Symmetric DFT) -------- + [definition] + C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n + [usage] + ip[0] = 0; // first time only + dfct(n, a, t, ip, w); + [parameters] + n :data length - 1 (int) + n >= 2, n = power of 2 + a[0...n] :input/output data (float *) + output data + a[k] = C[k], 0<=k<=n + t[0...n/2] :work area (float *) + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + is + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + for (j = 0; j <= n; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- + [definition] + S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + output data + a[k] = S[k], 0= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + dfst(n, a, t, ip, w); + is + dfst(n, a, t, ip, w); + for (j = 1; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +Appendix : + The cos/sin table is recalculated when the larger table required. + w[] and ip[] are compatible with all routines. +*/ + +#include +#include + +#include "common_audio/third_party/ooura/fft_size_256/fft4g.h" + +namespace webrtc { + +namespace { + +void makewt(size_t nw, size_t* ip, float* w); +void makect(size_t nc, size_t* ip, float* c); +void bitrv2(size_t n, size_t* ip, float* a); +void cftfsub(size_t n, float* a, float* w); +void cftbsub(size_t n, float* a, float* w); +void cft1st(size_t n, float* a, float* w); +void cftmdl(size_t n, size_t l, float* a, float* w); +void rftfsub(size_t n, float* a, size_t nc, float* c); +void rftbsub(size_t n, float* a, size_t nc, float* c); + +/* -------- initializing routines -------- */ + +void makewt(size_t nw, size_t* ip, float* w) { + size_t j, nwh; + float delta, x, y; + + ip[0] = nw; + ip[1] = 1; + if (nw > 2) { + nwh = nw >> 1; + delta = atanf(1.0f) / nwh; + w[0] = 1; + w[1] = 0; + w[nwh] = (float)cos(delta * nwh); + w[nwh + 1] = w[nwh]; + if (nwh > 2) { + for (j = 2; j < nwh; j += 2) { + x = (float)cos(delta * j); + y = (float)sin(delta * j); + w[j] = x; + w[j + 1] = y; + w[nw - j] = y; + w[nw - j + 1] = x; + } + bitrv2(nw, ip + 2, w); + } + } +} + +void makect(size_t nc, size_t* ip, float* c) { + size_t j, nch; + float delta; + + ip[1] = nc; + if (nc > 1) { + nch = nc >> 1; + delta = atanf(1.0f) / nch; + c[0] = (float)cos(delta * nch); + c[nch] = 0.5f * c[0]; + for (j = 1; j < nch; j++) { + c[j] = 0.5f * (float)cos(delta * j); + c[nc - j] = 0.5f * (float)sin(delta * j); + } + } +} + +/* -------- child routines -------- */ + +void bitrv2(size_t n, size_t* ip, float* a) { + size_t j, j1, k, k1, l, m, m2; + float xr, xi, yr, yi; + + ip[0] = 0; + l = n; + m = 1; + while ((m << 3) < l) { + l >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + l; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == l) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + j1 = 2 * k + m2 + ip[k]; + k1 = j1 + m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } else { + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } + } +} + +void cftfsub(size_t n, float* a, float* w) { + size_t j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + } else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = a[j + 1] - a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] += a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + +void cftbsub(size_t n, float* a, float* w) { + size_t j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = -a[j + 1] - a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = -a[j + 1] + a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i + x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i - x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i + x3r; + } + } else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = -a[j + 1] + a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] = -a[j + 1] - a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + +void cft1st(size_t n, float* a, float* w) { + size_t j, k1, k2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[2]; + x0i = a[1] + a[3]; + x1r = a[0] - a[2]; + x1i = a[1] - a[3]; + x2r = a[4] + a[6]; + x2i = a[5] + a[7]; + x3r = a[4] - a[6]; + x3i = a[5] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[2] = x1r - x3i; + a[3] = x1i + x3r; + a[6] = x1r + x3i; + a[7] = x1i - x3r; + wk1r = w[2]; + x0r = a[8] + a[10]; + x0i = a[9] + a[11]; + x1r = a[8] - a[10]; + x1i = a[9] - a[11]; + x2r = a[12] + a[14]; + x2i = a[13] + a[15]; + x3r = a[12] - a[14]; + x3i = a[13] - a[15]; + a[8] = x0r + x2r; + a[9] = x0i + x2i; + a[12] = x2i - x0i; + a[13] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[10] = wk1r * (x0r - x0i); + a[11] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[14] = wk1r * (x0i - x0r); + a[15] = wk1r * (x0i + x0r); + k1 = 0; + for (j = 16; j < n; j += 16) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + x0r = a[j] + a[j + 2]; + x0i = a[j + 1] + a[j + 3]; + x1r = a[j] - a[j + 2]; + x1i = a[j + 1] - a[j + 3]; + x2r = a[j + 4] + a[j + 6]; + x2i = a[j + 5] + a[j + 7]; + x3r = a[j + 4] - a[j + 6]; + x3i = a[j + 5] - a[j + 7]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 4] = wk2r * x0r - wk2i * x0i; + a[j + 5] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 2] = wk1r * x0r - wk1i * x0i; + a[j + 3] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 6] = wk3r * x0r - wk3i * x0i; + a[j + 7] = wk3r * x0i + wk3i * x0r; + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + x0r = a[j + 8] + a[j + 10]; + x0i = a[j + 9] + a[j + 11]; + x1r = a[j + 8] - a[j + 10]; + x1i = a[j + 9] - a[j + 11]; + x2r = a[j + 12] + a[j + 14]; + x2i = a[j + 13] + a[j + 15]; + x3r = a[j + 12] - a[j + 14]; + x3i = a[j + 13] - a[j + 15]; + a[j + 8] = x0r + x2r; + a[j + 9] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 12] = -wk2i * x0r - wk2r * x0i; + a[j + 13] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 10] = wk1r * x0r - wk1i * x0i; + a[j + 11] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 14] = wk3r * x0r - wk3i * x0i; + a[j + 15] = wk3r * x0i + wk3i * x0r; + } +} + +void cftmdl(size_t n, size_t l, float* a, float* w) { + size_t j, j1, j2, j3, k, k1, k2, m, m2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + m = l << 2; + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + wk1r = w[2]; + for (j = m; j < l + m; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x2i - x0i; + a[j2 + 1] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * (x0r - x0i); + a[j1 + 1] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[j3] = wk1r * (x0i - x0r); + a[j3 + 1] = wk1r * (x0i + x0r); + } + k1 = 0; + m2 = 2 * m; + for (k = m2; k < n; k += m2) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + for (j = k; j < l + k; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = wk2r * x0r - wk2i * x0i; + a[j2 + 1] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + for (j = k + m; j < l + (k + m); j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = -wk2i * x0r - wk2r * x0i; + a[j2 + 1] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + } +} + +void rftfsub(size_t n, float* a, size_t nc, float* c) { + size_t j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5f - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + +void rftbsub(size_t n, float* a, size_t nc, float* c) { + size_t j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + a[1] = -a[1]; + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5f - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j] -= yr; + a[j + 1] = yi - a[j + 1]; + a[k] += yr; + a[k + 1] = yi - a[k + 1]; + } + a[m + 1] = -a[m + 1]; +} + +} // namespace + +void WebRtc_rdft(size_t n, int isgn, float* a, size_t* ip, float* w) { + size_t nw, nc; + float xi; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 2)) { + nc = n >> 2; + makect(nc, ip, w + nw); + } + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, w); + } + xi = a[0] - a[1]; + a[0] += a[1]; + a[1] = xi; + } else { + a[1] = 0.5f * (a[0] - a[1]); + a[0] -= a[1]; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } else if (n == 4) { + cftfsub(n, a, w); + } + } +} + +} // namespace webrtc diff --git a/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.h b/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.h new file mode 100644 index 0000000..d41d2c6 --- /dev/null +++ b/webrtc/common_audio/third_party/ooura/fft_size_256/fft4g.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef COMMON_AUDIO_THIRD_PARTY_OOURA_FFT_SIZE_256_FFT4G_H_ +#define COMMON_AUDIO_THIRD_PARTY_OOURA_FFT_SIZE_256_FFT4G_H_ + +namespace webrtc { + +// Refer to fft4g.c for documentation. +void WebRtc_rdft(size_t n, int isgn, float* a, size_t* ip, float* w); + +} // namespace webrtc + +#endif // COMMON_AUDIO_THIRD_PARTY_OOURA_FFT_SIZE_256_FFT4G_H_ diff --git a/webrtc/common_audio/third_party/spl_sqrt_floor/BUILD.gn b/webrtc/common_audio/third_party/spl_sqrt_floor/BUILD.gn new file mode 100644 index 0000000..ac862c6 --- /dev/null +++ b/webrtc/common_audio/third_party/spl_sqrt_floor/BUILD.gn @@ -0,0 +1,24 @@ +# Copyright (c) 2018 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. + +import("../../../webrtc.gni") + +rtc_library("spl_sqrt_floor") { + visibility = [ "../..:common_audio_c" ] + sources = [ "spl_sqrt_floor.h" ] + deps = [] + if (current_cpu == "arm") { + sources += [ "spl_sqrt_floor_arm.S" ] + + deps += [ "../../../rtc_base/system:asm_defines" ] + } else if (current_cpu == "mipsel") { + sources += [ "spl_sqrt_floor_mips.c" ] + } else { + sources += [ "spl_sqrt_floor.c" ] + } +} diff --git a/webrtc/common_audio/third_party/spl_sqrt_floor/LICENSE b/webrtc/common_audio/third_party/spl_sqrt_floor/LICENSE new file mode 100644 index 0000000..fdf17a2 --- /dev/null +++ b/webrtc/common_audio/third_party/spl_sqrt_floor/LICENSE @@ -0,0 +1,27 @@ +/* + * Written by Wilco Dijkstra, 1996. The following email exchange establishes the + * license. + * + * From: Wilco Dijkstra + * Date: Fri, Jun 24, 2011 at 3:20 AM + * Subject: Re: sqrt routine + * To: Kevin Ma + * Hi Kevin, + * Thanks for asking. Those routines are public domain (originally posted to + * comp.sys.arm a long time ago), so you can use them freely for any purpose. + * Cheers, + * Wilco + * + * ----- Original Message ----- + * From: "Kevin Ma" + * To: + * Sent: Thursday, June 23, 2011 11:44 PM + * Subject: Fwd: sqrt routine + * Hi Wilco, + * I saw your sqrt routine from several web sites, including + * http://www.finesse.demon.co.uk/steven/sqrt.html. + * Just wonder if there's any copyright information with your Successive + * approximation routines, or if I can freely use it for any purpose. + * Thanks. + * Kevin + */ diff --git a/webrtc/common_audio/signal_processing/spl_sqrt_floor.c b/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c similarity index 96% rename from webrtc/common_audio/signal_processing/spl_sqrt_floor.c rename to webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c index 370307a..b478a41 100644 --- a/webrtc/common_audio/signal_processing/spl_sqrt_floor.c +++ b/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c @@ -28,7 +28,7 @@ // Minor modifications in code style for WebRTC, 2012. -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h" /* * Algorithm: diff --git a/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h b/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h new file mode 100644 index 0000000..eaa58e3 --- /dev/null +++ b/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 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 + +// +// WebRtcSpl_SqrtFloor(...) +// +// Returns the square root of the input value |value|. The precision of this +// function is rounding down integer precision, i.e., sqrt(8) gives 2 as answer. +// If |value| is a negative number then 0 is returned. +// +// Algorithm: +// +// An iterative 4 cylce/bit routine +// +// Input: +// - value : Value to calculate sqrt of +// +// Return value : Result of the sqrt calculation +// +int32_t WebRtcSpl_SqrtFloor(int32_t value); diff --git a/webrtc/common_audio/signal_processing/spl_sqrt_floor_arm.S b/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_arm.S similarity index 98% rename from webrtc/common_audio/signal_processing/spl_sqrt_floor_arm.S rename to webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_arm.S index 72cd2d9..228e68e 100644 --- a/webrtc/common_audio/signal_processing/spl_sqrt_floor_arm.S +++ b/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_arm.S @@ -32,7 +32,7 @@ @ Output: r0 = INT (SQRT (r0)), precision is 16 bits @ Registers touched: r1, r2 -#include "webrtc/system_wrappers/include/asm_defines.h" +#include "rtc_base/system/asm_defines.h" GLOBAL_FUNCTION WebRtcSpl_SqrtFloor .align 2 diff --git a/webrtc/common_audio/signal_processing/spl_sqrt_floor_mips.c b/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_mips.c similarity index 99% rename from webrtc/common_audio/signal_processing/spl_sqrt_floor_mips.c rename to webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_mips.c index 8716459..04033c1 100644 --- a/webrtc/common_audio/signal_processing/spl_sqrt_floor_mips.c +++ b/webrtc/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_mips.c @@ -29,7 +29,7 @@ // Minor modifications in code style for WebRTC, 2012. // Code optimizations for MIPS, 2013. -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h" /* * Algorithm: diff --git a/webrtc/common_audio/vad/include/vad.h b/webrtc/common_audio/vad/include/vad.h index 087970f..b15275b 100644 --- a/webrtc/common_audio/vad/include/vad.h +++ b/webrtc/common_audio/vad/include/vad.h @@ -8,13 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_VAD_INCLUDE_VAD_H_ -#define WEBRTC_COMMON_AUDIO_VAD_INCLUDE_VAD_H_ +#ifndef COMMON_AUDIO_VAD_INCLUDE_VAD_H_ +#define COMMON_AUDIO_VAD_INCLUDE_VAD_H_ -#include "webrtc/base/checks.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/vad/include/webrtc_vad.h" -#include "webrtc/typedefs.h" +#include + +#include "common_audio/vad/include/webrtc_vad.h" +#include "rtc_base/checks.h" namespace webrtc { @@ -43,8 +43,8 @@ class Vad { }; // Returns a Vad instance that's implemented on top of WebRtcVad. -rtc::scoped_ptr CreateVad(Vad::Aggressiveness aggressiveness); +std::unique_ptr CreateVad(Vad::Aggressiveness aggressiveness); } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_VAD_INCLUDE_VAD_H_ +#endif // COMMON_AUDIO_VAD_INCLUDE_VAD_H_ diff --git a/webrtc/common_audio/vad/include/webrtc_vad.h b/webrtc/common_audio/vad/include/webrtc_vad.h index 91308ee..f5bbadf 100644 --- a/webrtc/common_audio/vad/include/webrtc_vad.h +++ b/webrtc/common_audio/vad/include/webrtc_vad.h @@ -8,17 +8,16 @@ * be found in the AUTHORS file in the root of the source tree. */ - /* - * This header file includes the VAD API calls. Specific function calls are given below. + * This header file includes the VAD API calls. Specific function calls are + * given below. */ -#ifndef WEBRTC_COMMON_AUDIO_VAD_INCLUDE_WEBRTC_VAD_H_ // NOLINT -#define WEBRTC_COMMON_AUDIO_VAD_INCLUDE_WEBRTC_VAD_H_ +#ifndef COMMON_AUDIO_VAD_INCLUDE_WEBRTC_VAD_H_ // NOLINT +#define COMMON_AUDIO_VAD_INCLUDE_WEBRTC_VAD_H_ #include - -#include "webrtc/typedefs.h" +#include typedef struct WebRtcVadInst VadInst; @@ -27,7 +26,7 @@ extern "C" { #endif // Creates an instance to the VAD structure. -VadInst* WebRtcVad_Create(); +VadInst* WebRtcVad_Create(void); // Frees the dynamic memory of a specified VAD instance. // @@ -39,7 +38,7 @@ void WebRtcVad_Free(VadInst* handle); // - handle [i/o] : Instance that should be initialized. // // returns : 0 - (OK), -// -1 - (NULL pointer or Default mode could not be set). +// -1 - (null pointer or Default mode could not be set). int WebRtcVad_Init(VadInst* handle); // Sets the VAD operating mode. A more aggressive (higher mode) VAD is more @@ -51,7 +50,7 @@ int WebRtcVad_Init(VadInst* handle); // - mode [i] : Aggressiveness mode (0, 1, 2, or 3). // // returns : 0 - (OK), -// -1 - (NULL pointer, mode could not be set or the VAD instance +// -1 - (null pointer, mode could not be set or the VAD instance // has not been initialized). int WebRtcVad_set_mode(VadInst* handle, int mode); @@ -67,7 +66,9 @@ int WebRtcVad_set_mode(VadInst* handle, int mode); // returns : 1 - (Active Voice), // 0 - (Non-active Voice), // -1 - (Error) -int WebRtcVad_Process(VadInst* handle, int fs, const int16_t* audio_frame, +int WebRtcVad_Process(VadInst* handle, + int fs, + const int16_t* audio_frame, size_t frame_length); // Checks for valid combinations of |rate| and |frame_length|. We support 10, @@ -83,4 +84,4 @@ int WebRtcVad_ValidRateAndFrameLength(int rate, size_t frame_length); } #endif -#endif // WEBRTC_COMMON_AUDIO_VAD_INCLUDE_WEBRTC_VAD_H_ // NOLINT +#endif // COMMON_AUDIO_VAD_INCLUDE_WEBRTC_VAD_H_ // NOLINT diff --git a/webrtc/common_audio/vad/vad.cc b/webrtc/common_audio/vad/vad.cc index 95a162f..987ed52 100644 --- a/webrtc/common_audio/vad/vad.cc +++ b/webrtc/common_audio/vad/vad.cc @@ -8,9 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/vad/include/vad.h" +#include "common_audio/vad/include/vad.h" -#include "webrtc/base/checks.h" +#include + +#include "common_audio/vad/include/webrtc_vad.h" +#include "rtc_base/checks.h" namespace webrtc { @@ -35,7 +38,7 @@ class VadImpl final : public Vad { case 1: return kActive; default: - RTC_DCHECK(false) << "WebRtcVad_Process returned an error."; + RTC_NOTREACHED() << "WebRtcVad_Process returned an error."; return kError; } } @@ -56,8 +59,8 @@ class VadImpl final : public Vad { } // namespace -rtc::scoped_ptr CreateVad(Vad::Aggressiveness aggressiveness) { - return rtc::scoped_ptr(new VadImpl(aggressiveness)); +std::unique_ptr CreateVad(Vad::Aggressiveness aggressiveness) { + return std::unique_ptr(new VadImpl(aggressiveness)); } } // namespace webrtc diff --git a/webrtc/common_audio/vad/vad_core.c b/webrtc/common_audio/vad/vad_core.c index 51797ee..55927ce 100644 --- a/webrtc/common_audio/vad/vad_core.c +++ b/webrtc/common_audio/vad/vad_core.c @@ -8,13 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/vad/vad_core.h" +#include "common_audio/vad/vad_core.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/common_audio/vad/vad_filterbank.h" -#include "webrtc/common_audio/vad/vad_gmm.h" -#include "webrtc/common_audio/vad/vad_sp.h" -#include "webrtc/typedefs.h" +#include "rtc_base/sanitizer.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/vad/vad_filterbank.h" +#include "common_audio/vad/vad_gmm.h" +#include "common_audio/vad/vad_sp.h" // Spectrum Weighting static const int16_t kSpectrumWeight[kNumChannels] = { 6, 8, 10, 12, 14, 16 }; @@ -110,6 +110,15 @@ static int32_t WeightedAverage(int16_t* data, int16_t offset, return weighted_average; } +// An s16 x s32 -> s32 multiplication that's allowed to overflow. (It's still +// undefined behavior, so not a good idea; this just makes UBSan ignore the +// violation, so that our old code can continue to do what it's always been +// doing.) +static inline int32_t RTC_NO_SANITIZE("signed-integer-overflow") + OverflowingMulS16ByS32ToS32(int16_t a, int32_t b) { + return a * b; +} + // Calculates the probabilities for both speech and background noise using // Gaussian Mixture Models (GMM). A hypothesis-test is performed to decide which // type of signal is most probable. @@ -231,7 +240,7 @@ static int16_t GmmProbability(VadInstT* self, int16_t* features, (int32_t) (log_likelihood_ratio * kSpectrumWeight[channel]); // Local VAD decision. - if ((log_likelihood_ratio << 2) > individualTest) { + if ((log_likelihood_ratio * 4) > individualTest) { vadflag = 1; } @@ -378,7 +387,7 @@ static int16_t GmmProbability(VadInstT* self, int16_t* features, // (Q14 >> 2) * Q12 = Q24. tmp_s16 = (ngprvec[gaussian] + 2) >> 2; - tmp2_s32 = tmp_s16 * tmp1_s32; + tmp2_s32 = OverflowingMulS16ByS32ToS32(tmp_s16, tmp1_s32); // Q20 * approx 0.001 (2^-10=0.0009766), hence, // (Q24 >> 14) = (Q24 >> 4) / 2^10 = Q20. tmp1_s32 = tmp2_s32 >> 14; diff --git a/webrtc/common_audio/vad/vad_core.h b/webrtc/common_audio/vad/vad_core.h index b38c515..e79696c 100644 --- a/webrtc/common_audio/vad/vad_core.h +++ b/webrtc/common_audio/vad/vad_core.h @@ -8,51 +8,46 @@ * be found in the AUTHORS file in the root of the source tree. */ - /* * This header file includes the descriptions of the core VAD calls. */ -#ifndef WEBRTC_COMMON_AUDIO_VAD_VAD_CORE_H_ -#define WEBRTC_COMMON_AUDIO_VAD_VAD_CORE_H_ +#ifndef COMMON_AUDIO_VAD_VAD_CORE_H_ +#define COMMON_AUDIO_VAD_VAD_CORE_H_ -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/typedefs.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" -enum { kNumChannels = 6 }; // Number of frequency bands (named channels). +enum { kNumChannels = 6 }; // Number of frequency bands (named channels). enum { kNumGaussians = 2 }; // Number of Gaussians per channel in the GMM. enum { kTableSize = kNumChannels * kNumGaussians }; enum { kMinEnergy = 10 }; // Minimum energy required to trigger audio signal. -typedef struct VadInstT_ -{ - - int vad; - int32_t downsampling_filter_states[4]; - WebRtcSpl_State48khzTo8khz state_48_to_8; - int16_t noise_means[kTableSize]; - int16_t speech_means[kTableSize]; - int16_t noise_stds[kTableSize]; - int16_t speech_stds[kTableSize]; - // TODO(bjornv): Change to |frame_count|. - int32_t frame_counter; - int16_t over_hang; // Over Hang - int16_t num_of_speech; - // TODO(bjornv): Change to |age_vector|. - int16_t index_vector[16 * kNumChannels]; - int16_t low_value_vector[16 * kNumChannels]; - // TODO(bjornv): Change to |median|. - int16_t mean_value[kNumChannels]; - int16_t upper_state[5]; - int16_t lower_state[5]; - int16_t hp_filter_state[4]; - int16_t over_hang_max_1[3]; - int16_t over_hang_max_2[3]; - int16_t individual[3]; - int16_t total[3]; - - int init_flag; +typedef struct VadInstT_ { + int vad; + int32_t downsampling_filter_states[4]; + WebRtcSpl_State48khzTo8khz state_48_to_8; + int16_t noise_means[kTableSize]; + int16_t speech_means[kTableSize]; + int16_t noise_stds[kTableSize]; + int16_t speech_stds[kTableSize]; + // TODO(bjornv): Change to |frame_count|. + int32_t frame_counter; + int16_t over_hang; // Over Hang + int16_t num_of_speech; + // TODO(bjornv): Change to |age_vector|. + int16_t index_vector[16 * kNumChannels]; + int16_t low_value_vector[16 * kNumChannels]; + // TODO(bjornv): Change to |median|. + int16_t mean_value[kNumChannels]; + int16_t upper_state[5]; + int16_t lower_state[5]; + int16_t hp_filter_state[4]; + int16_t over_hang_max_1[3]; + int16_t over_hang_max_2[3]; + int16_t individual[3]; + int16_t total[3]; + int init_flag; } VadInstT; // Initializes the core VAD component. The default aggressiveness mode is @@ -60,7 +55,7 @@ typedef struct VadInstT_ // // - self [i/o] : Instance that should be initialized // -// returns : 0 (OK), -1 (NULL pointer in or if the default mode can't be +// returns : 0 (OK), -1 (null pointer in or if the default mode can't be // set) int WebRtcVad_InitCore(VadInstT* self); @@ -103,13 +98,17 @@ int WebRtcVad_set_mode_core(VadInstT* self, int mode); * 0 - No active speech * 1-6 - Active speech */ -int WebRtcVad_CalcVad48khz(VadInstT* inst, const int16_t* speech_frame, +int WebRtcVad_CalcVad48khz(VadInstT* inst, + const int16_t* speech_frame, size_t frame_length); -int WebRtcVad_CalcVad32khz(VadInstT* inst, const int16_t* speech_frame, +int WebRtcVad_CalcVad32khz(VadInstT* inst, + const int16_t* speech_frame, size_t frame_length); -int WebRtcVad_CalcVad16khz(VadInstT* inst, const int16_t* speech_frame, +int WebRtcVad_CalcVad16khz(VadInstT* inst, + const int16_t* speech_frame, size_t frame_length); -int WebRtcVad_CalcVad8khz(VadInstT* inst, const int16_t* speech_frame, +int WebRtcVad_CalcVad8khz(VadInstT* inst, + const int16_t* speech_frame, size_t frame_length); -#endif // WEBRTC_COMMON_AUDIO_VAD_VAD_CORE_H_ +#endif // COMMON_AUDIO_VAD_VAD_CORE_H_ diff --git a/webrtc/common_audio/vad/vad_filterbank.c b/webrtc/common_audio/vad/vad_filterbank.c index 8b9df93..1513153 100644 --- a/webrtc/common_audio/vad/vad_filterbank.c +++ b/webrtc/common_audio/vad/vad_filterbank.c @@ -8,12 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/vad/vad_filterbank.h" +#include "common_audio/vad/vad_filterbank.h" -#include - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/typedefs.h" +#include "rtc_base/checks.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" // Constants used in LogOfEnergy(). static const int16_t kLogConst = 24660; // 160*log10(2) in Q9. @@ -92,14 +90,14 @@ static void AllPassFilter(const int16_t* data_in, size_t data_length, size_t i; int16_t tmp16 = 0; int32_t tmp32 = 0; - int32_t state32 = ((int32_t) (*filter_state) << 16); // Q15 + int32_t state32 = ((int32_t) (*filter_state) * (1 << 16)); // Q15 for (i = 0; i < data_length; i++) { tmp32 = state32 + filter_coefficient * *data_in; tmp16 = (int16_t) (tmp32 >> 16); // Q(-1) *data_out++ = tmp16; - state32 = (*data_in << 14) - filter_coefficient * tmp16; // Q14 - state32 <<= 1; // Q15. + state32 = (*data_in * (1 << 14)) - filter_coefficient * tmp16; // Q14 + state32 *= 2; // Q15. data_in += 2; } @@ -160,8 +158,8 @@ static void LogOfEnergy(const int16_t* data_in, size_t data_length, // we eventually will mask out the fractional part. uint32_t energy = 0; - assert(data_in != NULL); - assert(data_length > 0); + RTC_DCHECK(data_in); + RTC_DCHECK_GT(data_length, 0); energy = (uint32_t) WebRtcSpl_Energy((int16_t*) data_in, data_length, &tot_rshifts); @@ -261,8 +259,8 @@ int16_t WebRtcVad_CalculateFeatures(VadInstT* self, const int16_t* data_in, int16_t* hp_out_ptr = hp_120; // [2000 - 4000] Hz. int16_t* lp_out_ptr = lp_120; // [0 - 2000] Hz. - assert(data_length <= 240); - assert(4 < kNumChannels - 1); // Checking maximum |frequency_band|. + RTC_DCHECK_LE(data_length, 240); + RTC_DCHECK_LT(4, kNumChannels - 1); // Checking maximum |frequency_band|. // Split at 2000 Hz and downsample. SplitFilter(in_ptr, data_length, &self->upper_state[frequency_band], diff --git a/webrtc/common_audio/vad/vad_filterbank.h b/webrtc/common_audio/vad/vad_filterbank.h index 42bf3fc..53bbbe1 100644 --- a/webrtc/common_audio/vad/vad_filterbank.h +++ b/webrtc/common_audio/vad/vad_filterbank.h @@ -12,11 +12,10 @@ * This file includes feature calculating functionality used in vad_core.c. */ -#ifndef WEBRTC_COMMON_AUDIO_VAD_VAD_FILTERBANK_H_ -#define WEBRTC_COMMON_AUDIO_VAD_VAD_FILTERBANK_H_ +#ifndef COMMON_AUDIO_VAD_VAD_FILTERBANK_H_ +#define COMMON_AUDIO_VAD_VAD_FILTERBANK_H_ -#include "webrtc/common_audio/vad/vad_core.h" -#include "webrtc/typedefs.h" +#include "common_audio/vad/vad_core.h" // Takes |data_length| samples of |data_in| and calculates the logarithm of the // energy of each of the |kNumChannels| = 6 frequency bands used by the VAD: @@ -38,7 +37,9 @@ // - features [o] : 10 * log10(energy in each frequency band), Q4. // - returns : Total energy of the signal (NOTE! This value is not // exact. It is only used in a comparison.) -int16_t WebRtcVad_CalculateFeatures(VadInstT* self, const int16_t* data_in, - size_t data_length, int16_t* features); +int16_t WebRtcVad_CalculateFeatures(VadInstT* self, + const int16_t* data_in, + size_t data_length, + int16_t* features); -#endif // WEBRTC_COMMON_AUDIO_VAD_VAD_FILTERBANK_H_ +#endif // COMMON_AUDIO_VAD_VAD_FILTERBANK_H_ diff --git a/webrtc/common_audio/vad/vad_gmm.c b/webrtc/common_audio/vad/vad_gmm.c index 4a01440..ddc87b6 100644 --- a/webrtc/common_audio/vad/vad_gmm.c +++ b/webrtc/common_audio/vad/vad_gmm.c @@ -8,10 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/vad/vad_gmm.h" +#include "common_audio/vad/vad_gmm.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/typedefs.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" static const int32_t kCompVar = 22005; static const int16_t kLog2Exp = 5909; // log2(exp(1)) in Q12. diff --git a/webrtc/common_audio/vad/vad_gmm.h b/webrtc/common_audio/vad/vad_gmm.h index 992a156..6b2d11b 100644 --- a/webrtc/common_audio/vad/vad_gmm.h +++ b/webrtc/common_audio/vad/vad_gmm.h @@ -10,10 +10,10 @@ // Gaussian probability calculations internally used in vad_core.c. -#ifndef WEBRTC_COMMON_AUDIO_VAD_VAD_GMM_H_ -#define WEBRTC_COMMON_AUDIO_VAD_VAD_GMM_H_ +#ifndef COMMON_AUDIO_VAD_VAD_GMM_H_ +#define COMMON_AUDIO_VAD_VAD_GMM_H_ -#include "webrtc/typedefs.h" +#include // Calculates the probability for |input|, given that |input| comes from a // normal distribution with mean and standard deviation (|mean|, |std|). @@ -36,4 +36,4 @@ int32_t WebRtcVad_GaussianProbability(int16_t input, int16_t std, int16_t* delta); -#endif // WEBRTC_COMMON_AUDIO_VAD_VAD_GMM_H_ +#endif // COMMON_AUDIO_VAD_VAD_GMM_H_ diff --git a/webrtc/common_audio/vad/vad_sp.c b/webrtc/common_audio/vad/vad_sp.c index a54be17..d710a37 100644 --- a/webrtc/common_audio/vad/vad_sp.c +++ b/webrtc/common_audio/vad/vad_sp.c @@ -8,13 +8,11 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/vad/vad_sp.h" +#include "common_audio/vad/vad_sp.h" -#include - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/common_audio/vad/vad_core.h" -#include "webrtc/typedefs.h" +#include "rtc_base/checks.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/vad/vad_core.h" // Allpass filter coefficients, upper and lower, in Q13. // Upper: 0.64, Lower: 0.17. @@ -72,7 +70,7 @@ int16_t WebRtcVad_FindMinimum(VadInstT* self, int16_t* age = &self->index_vector[offset]; int16_t* smallest_values = &self->low_value_vector[offset]; - assert(channel < kNumChannels); + RTC_DCHECK_LT(channel, kNumChannels); // Each value in |smallest_values| is getting 1 loop older. Update |age|, and // remove old values. @@ -81,7 +79,7 @@ int16_t WebRtcVad_FindMinimum(VadInstT* self, age[i]++; } else { // Too old value. Remove from memory and shift larger values downwards. - for (j = i; j < 16; j++) { + for (j = i; j < 15; j++) { smallest_values[j] = smallest_values[j + 1]; age[j] = age[j + 1]; } diff --git a/webrtc/common_audio/vad/vad_sp.h b/webrtc/common_audio/vad/vad_sp.h index 4d2b02a..ff36760 100644 --- a/webrtc/common_audio/vad/vad_sp.h +++ b/webrtc/common_audio/vad/vad_sp.h @@ -8,14 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ - // This file includes specific signal processing tools used in vad_core.c. -#ifndef WEBRTC_COMMON_AUDIO_VAD_VAD_SP_H_ -#define WEBRTC_COMMON_AUDIO_VAD_VAD_SP_H_ +#ifndef COMMON_AUDIO_VAD_VAD_SP_H_ +#define COMMON_AUDIO_VAD_VAD_SP_H_ -#include "webrtc/common_audio/vad/vad_core.h" -#include "webrtc/typedefs.h" +#include "common_audio/vad/vad_core.h" // Downsamples the signal by a factor 2, eg. 32->16 or 16->8. // @@ -53,4 +51,4 @@ int16_t WebRtcVad_FindMinimum(VadInstT* handle, int16_t feature_value, int channel); -#endif // WEBRTC_COMMON_AUDIO_VAD_VAD_SP_H_ +#endif // COMMON_AUDIO_VAD_VAD_SP_H_ diff --git a/webrtc/common_audio/vad/webrtc_vad.c b/webrtc/common_audio/vad/webrtc_vad.c index 80c8f3c..49e7682 100644 --- a/webrtc/common_audio/vad/webrtc_vad.c +++ b/webrtc/common_audio/vad/webrtc_vad.c @@ -8,14 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/vad/include/webrtc_vad.h" +#include "common_audio/vad/include/webrtc_vad.h" #include #include -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/common_audio/vad/vad_core.h" -#include "webrtc/typedefs.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/vad/vad_core.h" static const int kInitCheck = 42; static const int kValidRates[] = { 8000, 16000, 32000, 48000 }; @@ -25,7 +24,6 @@ static const int kMaxFrameLengthMs = 30; VadInst* WebRtcVad_Create() { VadInstT* self = (VadInstT*)malloc(sizeof(VadInstT)); - WebRtcSpl_Init(); self->init_flag = 0; return (VadInst*)self; diff --git a/webrtc/common_audio/wav_file.cc b/webrtc/common_audio/wav_file.cc index b14b620..e49126f 100644 --- a/webrtc/common_audio/wav_file.cc +++ b/webrtc/common_audio/wav_file.cc @@ -8,175 +8,283 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/common_audio/wav_file.h" +#include "common_audio/wav_file.h" + +#include #include +#include #include -#include +#include +#include -#include "webrtc/base/checks.h" -#include "webrtc/base/safe_conversions.h" -#include "webrtc/common_audio/include/audio_util.h" -#include "webrtc/common_audio/wav_header.h" +#include "common_audio/include/audio_util.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/arch.h" namespace webrtc { +namespace { -// We write 16-bit PCM WAV files. -static const WavFormat kWavFormat = kWavFormatPcm; -static const int kBytesPerSample = 2; +static_assert(std::is_trivially_destructible::value, ""); + +// Checks whether the format is supported or not. +bool FormatSupported(WavFormat format) { + // Only PCM and IEEE Float formats are supported. + return format == WavFormat::kWavFormatPcm || + format == WavFormat::kWavFormatIeeeFloat; +} // Doesn't take ownership of the file handle and won't close it. -class ReadableWavFile : public ReadableWav { +class WavHeaderFileReader : public WavHeaderReader { public: - explicit ReadableWavFile(FILE* file) : file_(file) {} - virtual size_t Read(void* buf, size_t num_bytes) { - return fread(buf, 1, num_bytes, file_); + explicit WavHeaderFileReader(FileWrapper* file) : file_(file) {} + + WavHeaderFileReader(const WavHeaderFileReader&) = delete; + WavHeaderFileReader& operator=(const WavHeaderFileReader&) = delete; + + size_t Read(void* buf, size_t num_bytes) override { + size_t count = file_->Read(buf, num_bytes); + pos_ += count; + return count; } + bool SeekForward(uint32_t num_bytes) override { + bool success = file_->SeekRelative(num_bytes); + if (success) { + pos_ += num_bytes; + } + return success; + } + int64_t GetPosition() override { return pos_; } private: - FILE* file_; + FileWrapper* file_; + int64_t pos_ = 0; }; -std::string WavFile::FormatAsString() const { - std::ostringstream s; - s << "Sample rate: " << sample_rate() << " Hz, Channels: " << num_channels() - << ", Duration: " - << (1.f * num_samples()) / (num_channels() * sample_rate()) << " s"; - return s.str(); -} +constexpr size_t kMaxChunksize = 4096; + +} // namespace WavReader::WavReader(const std::string& filename) - : file_handle_(fopen(filename.c_str(), "rb")) { - RTC_CHECK(file_handle_) << "Could not open wav file for reading."; + : WavReader(FileWrapper::OpenReadOnly(filename)) {} - ReadableWavFile readable(file_handle_); - WavFormat format; - int bytes_per_sample; - RTC_CHECK(ReadWavHeader(&readable, &num_channels_, &sample_rate_, &format, - &bytes_per_sample, &num_samples_)); - num_samples_remaining_ = num_samples_; - RTC_CHECK_EQ(kWavFormat, format); - RTC_CHECK_EQ(kBytesPerSample, bytes_per_sample); +WavReader::WavReader(FileWrapper file) : file_(std::move(file)) { + RTC_CHECK(file_.is_open()) + << "Invalid file. Could not create file handle for wav file."; + + WavHeaderFileReader readable(&file_); + size_t bytes_per_sample; + RTC_CHECK(ReadWavHeader(&readable, &num_channels_, &sample_rate_, &format_, + &bytes_per_sample, &num_samples_in_file_, + &data_start_pos_)); + num_unread_samples_ = num_samples_in_file_; + RTC_CHECK(FormatSupported(format_)) << "Non-implemented wav-format"; } -WavReader::~WavReader() { - Close(); +void WavReader::Reset() { + RTC_CHECK(file_.SeekTo(data_start_pos_)) + << "Failed to set position in the file to WAV data start position"; + num_unread_samples_ = num_samples_in_file_; } -size_t WavReader::ReadSamples(size_t num_samples, int16_t* samples) { +size_t WavReader::ReadSamples(const size_t num_samples, + int16_t* const samples) { #ifndef WEBRTC_ARCH_LITTLE_ENDIAN #error "Need to convert samples to big-endian when reading from WAV file" #endif - // There could be metadata after the audio; ensure we don't read it. - num_samples = std::min(rtc::checked_cast(num_samples), - num_samples_remaining_); - const size_t read = - fread(samples, sizeof(*samples), num_samples, file_handle_); - // If we didn't read what was requested, ensure we've reached the EOF. - RTC_CHECK(read == num_samples || feof(file_handle_)); - RTC_CHECK_LE(read, num_samples_remaining_); - num_samples_remaining_ -= rtc::checked_cast(read); - return read; + + size_t num_samples_left_to_read = num_samples; + size_t next_chunk_start = 0; + while (num_samples_left_to_read > 0 && num_unread_samples_ > 0) { + const size_t chunk_size = std::min( + std::min(kMaxChunksize, num_samples_left_to_read), num_unread_samples_); + size_t num_bytes_read; + size_t num_samples_read; + if (format_ == WavFormat::kWavFormatIeeeFloat) { + std::array samples_to_convert; + num_bytes_read = file_.Read(samples_to_convert.data(), + chunk_size * sizeof(samples_to_convert[0])); + num_samples_read = num_bytes_read / sizeof(samples_to_convert[0]); + + for (size_t j = 0; j < num_samples_read; ++j) { + samples[next_chunk_start + j] = FloatToS16(samples_to_convert[j]); + } + } else { + RTC_CHECK_EQ(format_, WavFormat::kWavFormatPcm); + num_bytes_read = file_.Read(&samples[next_chunk_start], + chunk_size * sizeof(samples[0])); + num_samples_read = num_bytes_read / sizeof(samples[0]); + } + RTC_CHECK(num_samples_read == 0 || (num_bytes_read % num_samples_read) == 0) + << "Corrupt file: file ended in the middle of a sample."; + RTC_CHECK(num_samples_read == chunk_size || file_.ReadEof()) + << "Corrupt file: payload size does not match header."; + + next_chunk_start += num_samples_read; + num_unread_samples_ -= num_samples_read; + num_samples_left_to_read -= num_samples_read; + } + + return num_samples - num_samples_left_to_read; } -size_t WavReader::ReadSamples(size_t num_samples, float* samples) { - static const size_t kChunksize = 4096 / sizeof(uint16_t); - size_t read = 0; - for (size_t i = 0; i < num_samples; i += kChunksize) { - int16_t isamples[kChunksize]; - size_t chunk = std::min(kChunksize, num_samples - i); - chunk = ReadSamples(chunk, isamples); - for (size_t j = 0; j < chunk; ++j) - samples[i + j] = isamples[j]; - read += chunk; +size_t WavReader::ReadSamples(const size_t num_samples, float* const samples) { +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Need to convert samples to big-endian when reading from WAV file" +#endif + + size_t num_samples_left_to_read = num_samples; + size_t next_chunk_start = 0; + while (num_samples_left_to_read > 0 && num_unread_samples_ > 0) { + const size_t chunk_size = std::min( + std::min(kMaxChunksize, num_samples_left_to_read), num_unread_samples_); + size_t num_bytes_read; + size_t num_samples_read; + if (format_ == WavFormat::kWavFormatPcm) { + std::array samples_to_convert; + num_bytes_read = file_.Read(samples_to_convert.data(), + chunk_size * sizeof(samples_to_convert[0])); + num_samples_read = num_bytes_read / sizeof(samples_to_convert[0]); + + for (size_t j = 0; j < num_samples_read; ++j) { + samples[next_chunk_start + j] = + static_cast(samples_to_convert[j]); + } + } else { + RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat); + num_bytes_read = file_.Read(&samples[next_chunk_start], + chunk_size * sizeof(samples[0])); + num_samples_read = num_bytes_read / sizeof(samples[0]); + + for (size_t j = 0; j < num_samples_read; ++j) { + samples[next_chunk_start + j] = + FloatToFloatS16(samples[next_chunk_start + j]); + } + } + RTC_CHECK(num_samples_read == 0 || (num_bytes_read % num_samples_read) == 0) + << "Corrupt file: file ended in the middle of a sample."; + RTC_CHECK(num_samples_read == chunk_size || file_.ReadEof()) + << "Corrupt file: payload size does not match header."; + + next_chunk_start += num_samples_read; + num_unread_samples_ -= num_samples_read; + num_samples_left_to_read -= num_samples_read; } - return read; + + return num_samples - num_samples_left_to_read; } void WavReader::Close() { - RTC_CHECK_EQ(0, fclose(file_handle_)); - file_handle_ = NULL; + file_.Close(); } -WavWriter::WavWriter(const std::string& filename, int sample_rate, - int num_channels) +WavWriter::WavWriter(const std::string& filename, + int sample_rate, + size_t num_channels, + SampleFormat sample_format) + // Unlike plain fopen, OpenWriteOnly takes care of filename utf8 -> + // wchar conversion on windows. + : WavWriter(FileWrapper::OpenWriteOnly(filename), + sample_rate, + num_channels, + sample_format) {} + +WavWriter::WavWriter(FileWrapper file, + int sample_rate, + size_t num_channels, + SampleFormat sample_format) : sample_rate_(sample_rate), num_channels_(num_channels), - num_samples_(0), - file_handle_(fopen(filename.c_str(), "wb")) { - RTC_CHECK(file_handle_) << "Could not open wav file for writing."; - RTC_CHECK(CheckWavParameters(num_channels_, sample_rate_, kWavFormat, - kBytesPerSample, num_samples_)); + num_samples_written_(0), + format_(sample_format == SampleFormat::kInt16 + ? WavFormat::kWavFormatPcm + : WavFormat::kWavFormatIeeeFloat), + file_(std::move(file)) { + // Handle errors from the OpenWriteOnly call in above constructor. + RTC_CHECK(file_.is_open()) << "Invalid file. Could not create wav file."; + + RTC_CHECK(CheckWavParameters(num_channels_, sample_rate_, format_, + num_samples_written_)); // Write a blank placeholder header, since we need to know the total number // of samples before we can fill in the real data. - static const uint8_t blank_header[kWavHeaderSize] = {0}; - RTC_CHECK_EQ(1u, fwrite(blank_header, kWavHeaderSize, 1, file_handle_)); -} - -WavWriter::~WavWriter() { - Close(); + static const uint8_t blank_header[MaxWavHeaderSize()] = {0}; + RTC_CHECK(file_.Write(blank_header, WavHeaderSize(format_))); } void WavWriter::WriteSamples(const int16_t* samples, size_t num_samples) { #ifndef WEBRTC_ARCH_LITTLE_ENDIAN #error "Need to convert samples to little-endian when writing to WAV file" #endif - const size_t written = - fwrite(samples, sizeof(*samples), num_samples, file_handle_); - RTC_CHECK_EQ(num_samples, written); - num_samples_ += static_cast(written); - RTC_CHECK(written <= std::numeric_limits::max() || - num_samples_ >= written); // detect uint32_t overflow + + for (size_t i = 0; i < num_samples; i += kMaxChunksize) { + const size_t num_remaining_samples = num_samples - i; + const size_t num_samples_to_write = + std::min(kMaxChunksize, num_remaining_samples); + + if (format_ == WavFormat::kWavFormatPcm) { + RTC_CHECK( + file_.Write(&samples[i], num_samples_to_write * sizeof(samples[0]))); + } else { + RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat); + std::array converted_samples; + for (size_t j = 0; j < num_samples_to_write; ++j) { + converted_samples[j] = S16ToFloat(samples[i + j]); + } + RTC_CHECK( + file_.Write(converted_samples.data(), + num_samples_to_write * sizeof(converted_samples[0]))); + } + + num_samples_written_ += num_samples_to_write; + RTC_CHECK_GE(num_samples_written_, + num_samples_to_write); // detect size_t overflow + } } void WavWriter::WriteSamples(const float* samples, size_t num_samples) { - static const size_t kChunksize = 4096 / sizeof(uint16_t); - for (size_t i = 0; i < num_samples; i += kChunksize) { - int16_t isamples[kChunksize]; - const size_t chunk = std::min(kChunksize, num_samples - i); - FloatS16ToS16(samples + i, chunk, isamples); - WriteSamples(isamples, chunk); +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Need to convert samples to little-endian when writing to WAV file" +#endif + + for (size_t i = 0; i < num_samples; i += kMaxChunksize) { + const size_t num_remaining_samples = num_samples - i; + const size_t num_samples_to_write = + std::min(kMaxChunksize, num_remaining_samples); + + if (format_ == WavFormat::kWavFormatPcm) { + std::array converted_samples; + for (size_t j = 0; j < num_samples_to_write; ++j) { + converted_samples[j] = FloatS16ToS16(samples[i + j]); + } + RTC_CHECK( + file_.Write(converted_samples.data(), + num_samples_to_write * sizeof(converted_samples[0]))); + } else { + RTC_CHECK_EQ(format_, WavFormat::kWavFormatIeeeFloat); + std::array converted_samples; + for (size_t j = 0; j < num_samples_to_write; ++j) { + converted_samples[j] = FloatS16ToFloat(samples[i + j]); + } + RTC_CHECK( + file_.Write(converted_samples.data(), + num_samples_to_write * sizeof(converted_samples[0]))); + } + + num_samples_written_ += num_samples_to_write; + RTC_CHECK(num_samples_written_ >= + num_samples_to_write); // detect size_t overflow } } void WavWriter::Close() { - RTC_CHECK_EQ(0, fseek(file_handle_, 0, SEEK_SET)); - uint8_t header[kWavHeaderSize]; - WriteWavHeader(header, num_channels_, sample_rate_, kWavFormat, - kBytesPerSample, num_samples_); - RTC_CHECK_EQ(1u, fwrite(header, kWavHeaderSize, 1, file_handle_)); - RTC_CHECK_EQ(0, fclose(file_handle_)); - file_handle_ = NULL; + RTC_CHECK(file_.Rewind()); + std::array header; + size_t header_size; + WriteWavHeader(num_channels_, sample_rate_, format_, num_samples_written_, + header.data(), &header_size); + RTC_CHECK(file_.Write(header.data(), header_size)); + RTC_CHECK(file_.Close()); } } // namespace webrtc - -rtc_WavWriter* rtc_WavOpen(const char* filename, - int sample_rate, - int num_channels) { - return reinterpret_cast( - new webrtc::WavWriter(filename, sample_rate, num_channels)); -} - -void rtc_WavClose(rtc_WavWriter* wf) { - delete reinterpret_cast(wf); -} - -void rtc_WavWriteSamples(rtc_WavWriter* wf, - const float* samples, - size_t num_samples) { - reinterpret_cast(wf)->WriteSamples(samples, num_samples); -} - -int rtc_WavSampleRate(const rtc_WavWriter* wf) { - return reinterpret_cast(wf)->sample_rate(); -} - -int rtc_WavNumChannels(const rtc_WavWriter* wf) { - return reinterpret_cast(wf)->num_channels(); -} - -uint32_t rtc_WavNumSamples(const rtc_WavWriter* wf) { - return reinterpret_cast(wf)->num_samples(); -} diff --git a/webrtc/common_audio/wav_file.h b/webrtc/common_audio/wav_file.h index 42b0618..dda611b 100644 --- a/webrtc/common_audio/wav_file.h +++ b/webrtc/common_audio/wav_file.h @@ -8,60 +8,69 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_WAV_FILE_H_ -#define WEBRTC_COMMON_AUDIO_WAV_FILE_H_ - -#ifdef __cplusplus +#ifndef COMMON_AUDIO_WAV_FILE_H_ +#define COMMON_AUDIO_WAV_FILE_H_ #include + #include #include -#include "webrtc/base/constructormagic.h" +#include "common_audio/wav_header.h" +#include "rtc_base/system/file_wrapper.h" namespace webrtc { -// Interface to provide access to WAV file parameters. +// Interface to provide access WAV file parameters. class WavFile { public: + enum class SampleFormat { kInt16, kFloat }; + virtual ~WavFile() {} virtual int sample_rate() const = 0; - virtual int num_channels() const = 0; - virtual uint32_t num_samples() const = 0; - - // Returns a human-readable string containing the audio format. - std::string FormatAsString() const; + virtual size_t num_channels() const = 0; + virtual size_t num_samples() const = 0; }; -// Simple C++ class for writing 16-bit PCM WAV files. All error handling is -// by calls to RTC_CHECK(), making it unsuitable for anything but debug code. +// Simple C++ class for writing 16-bit integer and 32 bit floating point PCM WAV +// files. All error handling is by calls to RTC_CHECK(), making it unsuitable +// for anything but debug code. class WavWriter final : public WavFile { public: - // Open a new WAV file for writing. - WavWriter(const std::string& filename, int sample_rate, int num_channels); + // Opens a new WAV file for writing. + WavWriter(const std::string& filename, + int sample_rate, + size_t num_channels, + SampleFormat sample_format = SampleFormat::kInt16); + WavWriter(FileWrapper file, + int sample_rate, + size_t num_channels, + SampleFormat sample_format = SampleFormat::kInt16); - // Close the WAV file, after writing its header. - ~WavWriter(); + // Closes the WAV file, after writing its header. + ~WavWriter() { Close(); } + + WavWriter(const WavWriter&) = delete; + WavWriter& operator=(const WavWriter&) = delete; // Write additional samples to the file. Each sample is in the range - // [-32768,32767], and there must be the previously specified number of + // [-32768.0,32767.0], and there must be the previously specified number of // interleaved channels. void WriteSamples(const float* samples, size_t num_samples); void WriteSamples(const int16_t* samples, size_t num_samples); int sample_rate() const override { return sample_rate_; } - int num_channels() const override { return num_channels_; } - uint32_t num_samples() const override { return num_samples_; } + size_t num_channels() const override { return num_channels_; } + size_t num_samples() const override { return num_samples_written_; } private: void Close(); const int sample_rate_; - const int num_channels_; - uint32_t num_samples_; // Total number of samples written to file. - FILE* file_handle_; // Output file, owned by this class - - RTC_DISALLOW_COPY_AND_ASSIGN(WavWriter); + const size_t num_channels_; + size_t num_samples_written_; + WavFormat format_; + FileWrapper file_; }; // Follows the conventions of WavWriter. @@ -69,9 +78,16 @@ class WavReader final : public WavFile { public: // Opens an existing WAV file for reading. explicit WavReader(const std::string& filename); + explicit WavReader(FileWrapper file); // Close the WAV file. - ~WavReader(); + ~WavReader() { Close(); } + + WavReader(const WavReader&) = delete; + WavReader& operator=(const WavReader&) = delete; + + // Resets position to the beginning of the file. + void Reset(); // Returns the number of samples read. If this is less than requested, // verifies that the end of the file was reached. @@ -79,40 +95,21 @@ class WavReader final : public WavFile { size_t ReadSamples(size_t num_samples, int16_t* samples); int sample_rate() const override { return sample_rate_; } - int num_channels() const override { return num_channels_; } - uint32_t num_samples() const override { return num_samples_; } + size_t num_channels() const override { return num_channels_; } + size_t num_samples() const override { return num_samples_in_file_; } private: void Close(); int sample_rate_; - int num_channels_; - uint32_t num_samples_; // Total number of samples in the file. - uint32_t num_samples_remaining_; - FILE* file_handle_; // Input file, owned by this class. - - RTC_DISALLOW_COPY_AND_ASSIGN(WavReader); + size_t num_channels_; + WavFormat format_; + size_t num_samples_in_file_; + size_t num_unread_samples_; + FileWrapper file_; + int64_t + data_start_pos_; // Position in the file immediately after WAV header. }; } // namespace webrtc -extern "C" { -#endif // __cplusplus - -// C wrappers for the WavWriter class. -typedef struct rtc_WavWriter rtc_WavWriter; -rtc_WavWriter* rtc_WavOpen(const char* filename, - int sample_rate, - int num_channels); -void rtc_WavClose(rtc_WavWriter* wf); -void rtc_WavWriteSamples(rtc_WavWriter* wf, - const float* samples, - size_t num_samples); -int rtc_WavSampleRate(const rtc_WavWriter* wf); -int rtc_WavNumChannels(const rtc_WavWriter* wf); -uint32_t rtc_WavNumSamples(const rtc_WavWriter* wf); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // WEBRTC_COMMON_AUDIO_WAV_FILE_H_ +#endif // COMMON_AUDIO_WAV_FILE_H_ diff --git a/webrtc/common_audio/wav_header.cc b/webrtc/common_audio/wav_header.cc index 61cfffe..d3dca90 100644 --- a/webrtc/common_audio/wav_header.cc +++ b/webrtc/common_audio/wav_header.cc @@ -12,28 +12,42 @@ // https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ and // http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html -#include "webrtc/common_audio/wav_header.h" +#include "common_audio/wav_header.h" -#include #include #include #include -#include "webrtc/base/checks.h" -#include "webrtc/common_audio/include/audio_util.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/sanitizer.h" +#include "rtc_base/system/arch.h" namespace webrtc { namespace { +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN +#error "Code not working properly for big endian platforms." +#endif + +#pragma pack(2) struct ChunkHeader { uint32_t ID; uint32_t Size; }; static_assert(sizeof(ChunkHeader) == 8, "ChunkHeader size"); +#pragma pack(2) +struct RiffHeader { + ChunkHeader header; + uint32_t Format; +}; +static_assert(sizeof(RiffHeader) == sizeof(ChunkHeader) + 4, "RiffHeader size"); + // We can't nest this definition in WavHeader, because VS2013 gives an error // on sizeof(WavHeader::fmt): "error C2070: 'unknown': illegal sizeof operand". -struct FmtSubchunk { +#pragma pack(2) +struct FmtPcmSubchunk { ChunkHeader header; uint16_t AudioFormat; uint16_t NumChannels; @@ -42,37 +56,247 @@ struct FmtSubchunk { uint16_t BlockAlign; uint16_t BitsPerSample; }; -static_assert(sizeof(FmtSubchunk) == 24, "FmtSubchunk size"); -const uint32_t kFmtSubchunkSize = sizeof(FmtSubchunk) - sizeof(ChunkHeader); +static_assert(sizeof(FmtPcmSubchunk) == 24, "FmtPcmSubchunk size"); +const uint32_t kFmtPcmSubchunkSize = + sizeof(FmtPcmSubchunk) - sizeof(ChunkHeader); -struct WavHeader { - struct { - ChunkHeader header; - uint32_t Format; - } riff; - FmtSubchunk fmt; +// Pack struct to avoid additional padding bytes. +#pragma pack(2) +struct FmtIeeeFloatSubchunk { + ChunkHeader header; + uint16_t AudioFormat; + uint16_t NumChannels; + uint32_t SampleRate; + uint32_t ByteRate; + uint16_t BlockAlign; + uint16_t BitsPerSample; + uint16_t ExtensionSize; +}; +static_assert(sizeof(FmtIeeeFloatSubchunk) == 26, "FmtIeeeFloatSubchunk size"); +const uint32_t kFmtIeeeFloatSubchunkSize = + sizeof(FmtIeeeFloatSubchunk) - sizeof(ChunkHeader); + +// Simple PCM wav header. It does not include chunks that are not essential to +// read audio samples. +#pragma pack(2) +struct WavHeaderPcm { + WavHeaderPcm(const WavHeaderPcm&) = default; + WavHeaderPcm& operator=(const WavHeaderPcm&) = default; + RiffHeader riff; + FmtPcmSubchunk fmt; struct { ChunkHeader header; } data; }; -static_assert(sizeof(WavHeader) == kWavHeaderSize, "no padding in header"); +static_assert(sizeof(WavHeaderPcm) == kPcmWavHeaderSize, + "no padding in header"); -} // namespace +// IEEE Float Wav header, includes extra chunks necessary for proper non-PCM +// WAV implementation. +#pragma pack(2) +struct WavHeaderIeeeFloat { + WavHeaderIeeeFloat(const WavHeaderIeeeFloat&) = default; + WavHeaderIeeeFloat& operator=(const WavHeaderIeeeFloat&) = default; + RiffHeader riff; + FmtIeeeFloatSubchunk fmt; + struct { + ChunkHeader header; + uint32_t SampleLength; + } fact; + struct { + ChunkHeader header; + } data; +}; +static_assert(sizeof(WavHeaderIeeeFloat) == kIeeeFloatWavHeaderSize, + "no padding in header"); -bool CheckWavParameters(int num_channels, +uint32_t PackFourCC(char a, char b, char c, char d) { + uint32_t packed_value = + static_cast(a) | static_cast(b) << 8 | + static_cast(c) << 16 | static_cast(d) << 24; + return packed_value; +} + +std::string ReadFourCC(uint32_t x) { + return std::string(reinterpret_cast(&x), 4); +} + +uint16_t MapWavFormatToHeaderField(WavFormat format) { + switch (format) { + case WavFormat::kWavFormatPcm: + return 1; + case WavFormat::kWavFormatIeeeFloat: + return 3; + case WavFormat::kWavFormatALaw: + return 6; + case WavFormat::kWavFormatMuLaw: + return 7; + } + RTC_CHECK(false); +} + +WavFormat MapHeaderFieldToWavFormat(uint16_t format_header_value) { + if (format_header_value == 1) { + return WavFormat::kWavFormatPcm; + } + if (format_header_value == 3) { + return WavFormat::kWavFormatIeeeFloat; + } + + RTC_CHECK(false) << "Unsupported WAV format"; +} + +uint32_t RiffChunkSize(size_t bytes_in_payload, size_t header_size) { + return static_cast(bytes_in_payload + header_size - + sizeof(ChunkHeader)); +} + +uint32_t ByteRate(size_t num_channels, + int sample_rate, + size_t bytes_per_sample) { + return static_cast(num_channels * sample_rate * bytes_per_sample); +} + +uint16_t BlockAlign(size_t num_channels, size_t bytes_per_sample) { + return static_cast(num_channels * bytes_per_sample); +} + +// Finds a chunk having the sought ID. If found, then |readable| points to the +// first byte of the sought chunk data. If not found, the end of the file is +// reached. +bool FindWaveChunk(ChunkHeader* chunk_header, + WavHeaderReader* readable, + const std::string sought_chunk_id) { + RTC_DCHECK_EQ(sought_chunk_id.size(), 4); + while (true) { + if (readable->Read(chunk_header, sizeof(*chunk_header)) != + sizeof(*chunk_header)) + return false; // EOF. + if (ReadFourCC(chunk_header->ID) == sought_chunk_id) + return true; // Sought chunk found. + // Ignore current chunk by skipping its payload. + if (!readable->SeekForward(chunk_header->Size)) + return false; // EOF or error. + } +} + +bool ReadFmtChunkData(FmtPcmSubchunk* fmt_subchunk, WavHeaderReader* readable) { + // Reads "fmt " chunk payload. + if (readable->Read(&(fmt_subchunk->AudioFormat), kFmtPcmSubchunkSize) != + kFmtPcmSubchunkSize) + return false; + const uint32_t fmt_size = fmt_subchunk->header.Size; + if (fmt_size != kFmtPcmSubchunkSize) { + // There is an optional two-byte extension field permitted to be present + // with PCM, but which must be zero. + int16_t ext_size; + if (kFmtPcmSubchunkSize + sizeof(ext_size) != fmt_size) + return false; + if (readable->Read(&ext_size, sizeof(ext_size)) != sizeof(ext_size)) + return false; + if (ext_size != 0) + return false; + } + return true; +} + +void WritePcmWavHeader(size_t num_channels, + int sample_rate, + size_t bytes_per_sample, + size_t num_samples, + uint8_t* buf, + size_t* header_size) { + RTC_CHECK(buf); + RTC_CHECK(header_size); + *header_size = kPcmWavHeaderSize; + auto header = rtc::MsanUninitialized({}); + const size_t bytes_in_payload = bytes_per_sample * num_samples; + + header.riff.header.ID = PackFourCC('R', 'I', 'F', 'F'); + header.riff.header.Size = RiffChunkSize(bytes_in_payload, *header_size); + header.riff.Format = PackFourCC('W', 'A', 'V', 'E'); + header.fmt.header.ID = PackFourCC('f', 'm', 't', ' '); + header.fmt.header.Size = kFmtPcmSubchunkSize; + header.fmt.AudioFormat = MapWavFormatToHeaderField(WavFormat::kWavFormatPcm); + header.fmt.NumChannels = static_cast(num_channels); + header.fmt.SampleRate = sample_rate; + header.fmt.ByteRate = ByteRate(num_channels, sample_rate, bytes_per_sample); + header.fmt.BlockAlign = BlockAlign(num_channels, bytes_per_sample); + header.fmt.BitsPerSample = static_cast(8 * bytes_per_sample); + header.data.header.ID = PackFourCC('d', 'a', 't', 'a'); + header.data.header.Size = static_cast(bytes_in_payload); + + // Do an extra copy rather than writing everything to buf directly, since buf + // might not be correctly aligned. + memcpy(buf, &header, *header_size); +} + +void WriteIeeeFloatWavHeader(size_t num_channels, + int sample_rate, + size_t bytes_per_sample, + size_t num_samples, + uint8_t* buf, + size_t* header_size) { + RTC_CHECK(buf); + RTC_CHECK(header_size); + *header_size = kIeeeFloatWavHeaderSize; + auto header = rtc::MsanUninitialized({}); + const size_t bytes_in_payload = bytes_per_sample * num_samples; + + header.riff.header.ID = PackFourCC('R', 'I', 'F', 'F'); + header.riff.header.Size = RiffChunkSize(bytes_in_payload, *header_size); + header.riff.Format = PackFourCC('W', 'A', 'V', 'E'); + header.fmt.header.ID = PackFourCC('f', 'm', 't', ' '); + header.fmt.header.Size = kFmtIeeeFloatSubchunkSize; + header.fmt.AudioFormat = + MapWavFormatToHeaderField(WavFormat::kWavFormatIeeeFloat); + header.fmt.NumChannels = static_cast(num_channels); + header.fmt.SampleRate = sample_rate; + header.fmt.ByteRate = ByteRate(num_channels, sample_rate, bytes_per_sample); + header.fmt.BlockAlign = BlockAlign(num_channels, bytes_per_sample); + header.fmt.BitsPerSample = static_cast(8 * bytes_per_sample); + header.fmt.ExtensionSize = 0; + header.fact.header.ID = PackFourCC('f', 'a', 'c', 't'); + header.fact.header.Size = 4; + header.fact.SampleLength = static_cast(num_channels * num_samples); + header.data.header.ID = PackFourCC('d', 'a', 't', 'a'); + header.data.header.Size = static_cast(bytes_in_payload); + + // Do an extra copy rather than writing everything to buf directly, since buf + // might not be correctly aligned. + memcpy(buf, &header, *header_size); +} + +// Returns the number of bytes per sample for the format. +size_t GetFormatBytesPerSample(WavFormat format) { + switch (format) { + case WavFormat::kWavFormatPcm: + // Other values may be OK, but for now we're conservative. + return 2; + case WavFormat::kWavFormatALaw: + case WavFormat::kWavFormatMuLaw: + return 1; + case WavFormat::kWavFormatIeeeFloat: + return 4; + default: + RTC_CHECK(false); + return 2; + } +} + +bool CheckWavParameters(size_t num_channels, int sample_rate, WavFormat format, - int bytes_per_sample, - uint32_t num_samples) { + size_t bytes_per_sample, + size_t num_samples) { // num_channels, sample_rate, and bytes_per_sample must be positive, must fit // in their respective fields, and their product must fit in the 32-bit // ByteRate field. - if (num_channels <= 0 || sample_rate <= 0 || bytes_per_sample <= 0) + if (num_channels == 0 || sample_rate <= 0 || bytes_per_sample == 0) return false; if (static_cast(sample_rate) > std::numeric_limits::max()) return false; - if (static_cast(num_channels) > - std::numeric_limits::max()) + if (num_channels > std::numeric_limits::max()) return false; if (static_cast(bytes_per_sample) * 8 > std::numeric_limits::max()) @@ -83,26 +307,29 @@ bool CheckWavParameters(int num_channels, // format and bytes_per_sample must agree. switch (format) { - case kWavFormatPcm: + case WavFormat::kWavFormatPcm: // Other values may be OK, but for now we're conservative: if (bytes_per_sample != 1 && bytes_per_sample != 2) return false; break; - case kWavFormatALaw: - case kWavFormatMuLaw: + case WavFormat::kWavFormatALaw: + case WavFormat::kWavFormatMuLaw: if (bytes_per_sample != 1) return false; break; + case WavFormat::kWavFormatIeeeFloat: + if (bytes_per_sample != 4) + return false; + break; default: return false; } // The number of bytes in the file, not counting the first ChunkHeader, must // be less than 2^32; otherwise, the ChunkSize field overflows. - const uint32_t max_samples = - (std::numeric_limits::max() - - (kWavHeaderSize - sizeof(ChunkHeader))) / - bytes_per_sample; + const size_t header_size = kPcmWavHeaderSize - sizeof(ChunkHeader); + const size_t max_samples = + (std::numeric_limits::max() - header_size) / bytes_per_sample; if (num_samples > max_samples) return false; @@ -113,130 +340,102 @@ bool CheckWavParameters(int num_channels, return true; } -#ifdef WEBRTC_ARCH_LITTLE_ENDIAN -static inline void WriteLE16(uint16_t* f, uint16_t x) { *f = x; } -static inline void WriteLE32(uint32_t* f, uint32_t x) { *f = x; } -static inline void WriteFourCC(uint32_t* f, char a, char b, char c, char d) { - *f = static_cast(a) - | static_cast(b) << 8 - | static_cast(c) << 16 - | static_cast(d) << 24; +} // namespace + +bool CheckWavParameters(size_t num_channels, + int sample_rate, + WavFormat format, + size_t num_samples) { + return CheckWavParameters(num_channels, sample_rate, format, + GetFormatBytesPerSample(format), num_samples); } -static inline uint16_t ReadLE16(uint16_t x) { return x; } -static inline uint32_t ReadLE32(uint32_t x) { return x; } -static inline std::string ReadFourCC(uint32_t x) { - return std::string(reinterpret_cast(&x), 4); -} -#else -#error "Write be-to-le conversion functions" -#endif - -static inline uint32_t RiffChunkSize(uint32_t bytes_in_payload) { - return bytes_in_payload + kWavHeaderSize - sizeof(ChunkHeader); -} - -static inline uint32_t ByteRate(int num_channels, int sample_rate, - int bytes_per_sample) { - return static_cast(num_channels) * sample_rate * bytes_per_sample; -} - -static inline uint16_t BlockAlign(int num_channels, int bytes_per_sample) { - return num_channels * bytes_per_sample; -} - -void WriteWavHeader(uint8_t* buf, - int num_channels, +void WriteWavHeader(size_t num_channels, int sample_rate, WavFormat format, - int bytes_per_sample, - uint32_t num_samples) { + size_t num_samples, + uint8_t* buf, + size_t* header_size) { + RTC_CHECK(buf); + RTC_CHECK(header_size); + + const size_t bytes_per_sample = GetFormatBytesPerSample(format); RTC_CHECK(CheckWavParameters(num_channels, sample_rate, format, bytes_per_sample, num_samples)); - - WavHeader header; - const uint32_t bytes_in_payload = bytes_per_sample * num_samples; - - WriteFourCC(&header.riff.header.ID, 'R', 'I', 'F', 'F'); - WriteLE32(&header.riff.header.Size, RiffChunkSize(bytes_in_payload)); - WriteFourCC(&header.riff.Format, 'W', 'A', 'V', 'E'); - - WriteFourCC(&header.fmt.header.ID, 'f', 'm', 't', ' '); - WriteLE32(&header.fmt.header.Size, kFmtSubchunkSize); - WriteLE16(&header.fmt.AudioFormat, format); - WriteLE16(&header.fmt.NumChannels, num_channels); - WriteLE32(&header.fmt.SampleRate, sample_rate); - WriteLE32(&header.fmt.ByteRate, ByteRate(num_channels, sample_rate, - bytes_per_sample)); - WriteLE16(&header.fmt.BlockAlign, BlockAlign(num_channels, bytes_per_sample)); - WriteLE16(&header.fmt.BitsPerSample, 8 * bytes_per_sample); - - WriteFourCC(&header.data.header.ID, 'd', 'a', 't', 'a'); - WriteLE32(&header.data.header.Size, bytes_in_payload); - - // Do an extra copy rather than writing everything to buf directly, since buf - // might not be correctly aligned. - memcpy(buf, &header, kWavHeaderSize); + if (format == WavFormat::kWavFormatPcm) { + WritePcmWavHeader(num_channels, sample_rate, bytes_per_sample, num_samples, + buf, header_size); + } else { + RTC_CHECK_EQ(format, WavFormat::kWavFormatIeeeFloat); + WriteIeeeFloatWavHeader(num_channels, sample_rate, bytes_per_sample, + num_samples, buf, header_size); + } } -bool ReadWavHeader(ReadableWav* readable, - int* num_channels, +bool ReadWavHeader(WavHeaderReader* readable, + size_t* num_channels, int* sample_rate, WavFormat* format, - int* bytes_per_sample, - uint32_t* num_samples) { - WavHeader header; - if (readable->Read(&header, kWavHeaderSize - sizeof(header.data)) != - kWavHeaderSize - sizeof(header.data)) - return false; + size_t* bytes_per_sample, + size_t* num_samples, + int64_t* data_start_pos) { + // Read using the PCM header, even though it might be float Wav file + auto header = rtc::MsanUninitialized({}); - const uint32_t fmt_size = ReadLE32(header.fmt.header.Size); - if (fmt_size != kFmtSubchunkSize) { - // There is an optional two-byte extension field permitted to be present - // with PCM, but which must be zero. - int16_t ext_size; - if (kFmtSubchunkSize + sizeof(ext_size) != fmt_size) - return false; - if (readable->Read(&ext_size, sizeof(ext_size)) != sizeof(ext_size)) - return false; - if (ext_size != 0) - return false; - } - if (readable->Read(&header.data, sizeof(header.data)) != sizeof(header.data)) + // Read RIFF chunk. + if (readable->Read(&header.riff, sizeof(header.riff)) != sizeof(header.riff)) return false; - - // Parse needed fields. - *format = static_cast(ReadLE16(header.fmt.AudioFormat)); - *num_channels = ReadLE16(header.fmt.NumChannels); - *sample_rate = ReadLE32(header.fmt.SampleRate); - *bytes_per_sample = ReadLE16(header.fmt.BitsPerSample) / 8; - const uint32_t bytes_in_payload = ReadLE32(header.data.header.Size); - if (*bytes_per_sample <= 0) - return false; - *num_samples = bytes_in_payload / *bytes_per_sample; - - // Sanity check remaining fields. if (ReadFourCC(header.riff.header.ID) != "RIFF") return false; if (ReadFourCC(header.riff.Format) != "WAVE") return false; - if (ReadFourCC(header.fmt.header.ID) != "fmt ") - return false; - if (ReadFourCC(header.data.header.ID) != "data") - return false; - if (ReadLE32(header.riff.header.Size) < RiffChunkSize(bytes_in_payload)) + // Find "fmt " and "data" chunks. While the official Wave file specification + // does not put requirements on the chunks order, it is uncommon to find the + // "data" chunk before the "fmt " one. The code below fails if this is not the + // case. + if (!FindWaveChunk(&header.fmt.header, readable, "fmt ")) { + RTC_LOG(LS_ERROR) << "Cannot find 'fmt ' chunk."; return false; - if (ReadLE32(header.fmt.ByteRate) != + } + if (!ReadFmtChunkData(&header.fmt, readable)) { + RTC_LOG(LS_ERROR) << "Cannot read 'fmt ' chunk."; + return false; + } + if (!FindWaveChunk(&header.data.header, readable, "data")) { + RTC_LOG(LS_ERROR) << "Cannot find 'data' chunk."; + return false; + } + + // Parse needed fields. + *format = MapHeaderFieldToWavFormat(header.fmt.AudioFormat); + *num_channels = header.fmt.NumChannels; + *sample_rate = header.fmt.SampleRate; + *bytes_per_sample = header.fmt.BitsPerSample / 8; + const size_t bytes_in_payload = header.data.header.Size; + if (*bytes_per_sample == 0) + return false; + *num_samples = bytes_in_payload / *bytes_per_sample; + + const size_t header_size = *format == WavFormat::kWavFormatPcm + ? kPcmWavHeaderSize + : kIeeeFloatWavHeaderSize; + + if (header.riff.header.Size < RiffChunkSize(bytes_in_payload, header_size)) + return false; + if (header.fmt.ByteRate != ByteRate(*num_channels, *sample_rate, *bytes_per_sample)) return false; - if (ReadLE16(header.fmt.BlockAlign) != - BlockAlign(*num_channels, *bytes_per_sample)) + if (header.fmt.BlockAlign != BlockAlign(*num_channels, *bytes_per_sample)) return false; - return CheckWavParameters(*num_channels, *sample_rate, *format, - *bytes_per_sample, *num_samples); + if (!CheckWavParameters(*num_channels, *sample_rate, *format, + *bytes_per_sample, *num_samples)) { + return false; + } + + *data_start_pos = readable->GetPosition(); + return true; } - } // namespace webrtc diff --git a/webrtc/common_audio/wav_header.h b/webrtc/common_audio/wav_header.h index 1a0fd7c..2cccd7d 100644 --- a/webrtc/common_audio/wav_header.h +++ b/webrtc/common_audio/wav_header.h @@ -8,57 +8,83 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_WAV_HEADER_H_ -#define WEBRTC_COMMON_AUDIO_WAV_HEADER_H_ +#ifndef COMMON_AUDIO_WAV_HEADER_H_ +#define COMMON_AUDIO_WAV_HEADER_H_ #include #include +#include + +#include "rtc_base/checks.h" namespace webrtc { -static const size_t kWavHeaderSize = 44; - -class ReadableWav { +// Interface providing header reading functionality. +class WavHeaderReader { public: // Returns the number of bytes read. - size_t virtual Read(void* buf, size_t num_bytes) = 0; - virtual ~ReadableWav() {} + virtual size_t Read(void* buf, size_t num_bytes) = 0; + virtual bool SeekForward(uint32_t num_bytes) = 0; + virtual ~WavHeaderReader() = default; + virtual int64_t GetPosition() = 0; }; -enum WavFormat { - kWavFormatPcm = 1, // PCM, each sample of size bytes_per_sample - kWavFormatALaw = 6, // 8-bit ITU-T G.711 A-law - kWavFormatMuLaw = 7, // 8-bit ITU-T G.711 mu-law +// Possible WAV formats. +enum class WavFormat { + kWavFormatPcm = 1, // PCM, each sample of size bytes_per_sample. + kWavFormatIeeeFloat = 3, // IEEE float. + kWavFormatALaw = 6, // 8-bit ITU-T G.711 A-law. + kWavFormatMuLaw = 7, // 8-bit ITU-T G.711 mu-law. }; +// Header sizes for supported WAV formats. +constexpr size_t kPcmWavHeaderSize = 44; +constexpr size_t kIeeeFloatWavHeaderSize = 58; + +// Returns the size of the WAV header for the specified format. +constexpr size_t WavHeaderSize(WavFormat format) { + if (format == WavFormat::kWavFormatPcm) { + return kPcmWavHeaderSize; + } + RTC_CHECK_EQ(format, WavFormat::kWavFormatIeeeFloat); + return kIeeeFloatWavHeaderSize; +} + +// Returns the maximum size of the supported WAV formats. +constexpr size_t MaxWavHeaderSize() { + return std::max(WavHeaderSize(WavFormat::kWavFormatPcm), + WavHeaderSize(WavFormat::kWavFormatIeeeFloat)); +} + // Return true if the given parameters will make a well-formed WAV header. -bool CheckWavParameters(int num_channels, +bool CheckWavParameters(size_t num_channels, int sample_rate, WavFormat format, - int bytes_per_sample, - uint32_t num_samples); + size_t num_samples); // Write a kWavHeaderSize bytes long WAV header to buf. The payload that // follows the header is supposed to have the specified number of interleaved // channels and contain the specified total number of samples of the specified -// type. CHECKs the input parameters for validity. -void WriteWavHeader(uint8_t* buf, - int num_channels, +// type. The size of the header is returned in header_size. CHECKs the input +// parameters for validity. +void WriteWavHeader(size_t num_channels, int sample_rate, WavFormat format, - int bytes_per_sample, - uint32_t num_samples); + size_t num_samples, + uint8_t* buf, + size_t* header_size); -// Read a WAV header from an implemented ReadableWav and parse the values into -// the provided output parameters. ReadableWav is used because the header can -// be variably sized. Returns false if the header is invalid. -bool ReadWavHeader(ReadableWav* readable, - int* num_channels, +// Read a WAV header from an implemented WavHeaderReader and parse the values +// into the provided output parameters. WavHeaderReader is used because the +// header can be variably sized. Returns false if the header is invalid. +bool ReadWavHeader(WavHeaderReader* readable, + size_t* num_channels, int* sample_rate, WavFormat* format, - int* bytes_per_sample, - uint32_t* num_samples); + size_t* bytes_per_sample, + size_t* num_samples, + int64_t* data_start_pos); } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_WAV_HEADER_H_ +#endif // COMMON_AUDIO_WAV_HEADER_H_ diff --git a/webrtc/common_audio/window_generator.cc b/webrtc/common_audio/window_generator.cc index ab983b7..da5603d 100644 --- a/webrtc/common_audio/window_generator.cc +++ b/webrtc/common_audio/window_generator.cc @@ -10,12 +10,12 @@ #define _USE_MATH_DEFINES -#include "webrtc/common_audio/window_generator.h" +#include "common_audio/window_generator.h" #include #include -#include "webrtc/base/checks.h" +#include "rtc_base/checks.h" using std::complex; @@ -25,12 +25,11 @@ namespace { complex I0(complex x) { complex y = x / 3.75f; y *= y; - return 1.0f + y * ( - 3.5156229f + y * ( - 3.0899424f + y * ( - 1.2067492f + y * ( - 0.2659732f + y * ( - 0.360768e-1f + y * 0.45813e-2f))))); + return 1.0f + y * (3.5156229f + + y * (3.0899424f + + y * (1.2067492f + + y * (0.2659732f + + y * (0.360768e-1f + y * 0.45813e-2f))))); } } // namespace @@ -41,12 +40,13 @@ void WindowGenerator::Hanning(int length, float* window) { RTC_CHECK_GT(length, 1); RTC_CHECK(window != nullptr); for (int i = 0; i < length; ++i) { - window[i] = 0.5f * (1 - cosf(2 * static_cast(M_PI) * i / - (length - 1))); + window[i] = + 0.5f * (1 - cosf(2 * static_cast(M_PI) * i / (length - 1))); } } -void WindowGenerator::KaiserBesselDerived(float alpha, size_t length, +void WindowGenerator::KaiserBesselDerived(float alpha, + size_t length, float* window) { RTC_CHECK_GT(length, 1U); RTC_CHECK(window != nullptr); @@ -69,4 +69,3 @@ void WindowGenerator::KaiserBesselDerived(float alpha, size_t length, } } // namespace webrtc - diff --git a/webrtc/common_audio/window_generator.h b/webrtc/common_audio/window_generator.h index 25dd233..c0a89c4 100644 --- a/webrtc/common_audio/window_generator.h +++ b/webrtc/common_audio/window_generator.h @@ -8,26 +8,24 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_ -#define WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_ +#ifndef COMMON_AUDIO_WINDOW_GENERATOR_H_ +#define COMMON_AUDIO_WINDOW_GENERATOR_H_ #include -#include "webrtc/base/constructormagic.h" - namespace webrtc { // Helper class with generators for various signal transform windows. class WindowGenerator { public: + WindowGenerator() = delete; + WindowGenerator(const WindowGenerator&) = delete; + WindowGenerator& operator=(const WindowGenerator&) = delete; + static void Hanning(int length, float* window); static void KaiserBesselDerived(float alpha, size_t length, float* window); - - private: - RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(WindowGenerator); }; } // namespace webrtc -#endif // WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_ - +#endif // COMMON_AUDIO_WINDOW_GENERATOR_H_ diff --git a/webrtc/common_types.h b/webrtc/common_types.h deleted file mode 100644 index 048485f..0000000 --- a/webrtc/common_types.h +++ /dev/null @@ -1,911 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_COMMON_TYPES_H_ -#define WEBRTC_COMMON_TYPES_H_ - -#include -#include - -#include -#include - -#include "webrtc/typedefs.h" - -#if defined(_MSC_VER) -// Disable "new behavior: elements of array will be default initialized" -// warning. Affects OverUseDetectorOptions. -#pragma warning(disable:4351) -#endif - -#ifdef WEBRTC_EXPORT -#define WEBRTC_DLLEXPORT _declspec(dllexport) -#elif WEBRTC_DLL -#define WEBRTC_DLLEXPORT _declspec(dllimport) -#else -#define WEBRTC_DLLEXPORT -#endif - -#ifndef NULL -#define NULL 0 -#endif - -#define RTP_PAYLOAD_NAME_SIZE 32 - -#if defined(WEBRTC_WIN) || defined(WIN32) -// Compares two strings without regard to case. -#define STR_CASE_CMP(s1, s2) ::_stricmp(s1, s2) -// Compares characters of two strings without regard to case. -#define STR_NCASE_CMP(s1, s2, n) ::_strnicmp(s1, s2, n) -#else -#define STR_CASE_CMP(s1, s2) ::strcasecmp(s1, s2) -#define STR_NCASE_CMP(s1, s2, n) ::strncasecmp(s1, s2, n) -#endif - -namespace webrtc { - -class Config; - -class InStream -{ -public: - // Reads |length| bytes from file to |buf|. Returns the number of bytes read - // or -1 on error. - virtual int Read(void *buf, size_t len) = 0; - virtual int Rewind(); - virtual ~InStream() {} -protected: - InStream() {} -}; - -class OutStream -{ -public: - // Writes |length| bytes from |buf| to file. The actual writing may happen - // some time later. Call Flush() to force a write. - virtual bool Write(const void *buf, size_t len) = 0; - virtual int Rewind(); - virtual ~OutStream() {} -protected: - OutStream() {} -}; - -enum TraceModule -{ - kTraceUndefined = 0, - // not a module, triggered from the engine code - kTraceVoice = 0x0001, - // not a module, triggered from the engine code - kTraceVideo = 0x0002, - // not a module, triggered from the utility code - kTraceUtility = 0x0003, - kTraceRtpRtcp = 0x0004, - kTraceTransport = 0x0005, - kTraceSrtp = 0x0006, - kTraceAudioCoding = 0x0007, - kTraceAudioMixerServer = 0x0008, - kTraceAudioMixerClient = 0x0009, - kTraceFile = 0x000a, - kTraceAudioProcessing = 0x000b, - kTraceVideoCoding = 0x0010, - kTraceVideoMixer = 0x0011, - kTraceAudioDevice = 0x0012, - kTraceVideoRenderer = 0x0014, - kTraceVideoCapture = 0x0015, - kTraceRemoteBitrateEstimator = 0x0017, -}; - -enum TraceLevel -{ - kTraceNone = 0x0000, // no trace - kTraceStateInfo = 0x0001, - kTraceWarning = 0x0002, - kTraceError = 0x0004, - kTraceCritical = 0x0008, - kTraceApiCall = 0x0010, - kTraceDefault = 0x00ff, - - kTraceModuleCall = 0x0020, - kTraceMemory = 0x0100, // memory info - kTraceTimer = 0x0200, // timing info - kTraceStream = 0x0400, // "continuous" stream of data - - // used for debug purposes - kTraceDebug = 0x0800, // debug - kTraceInfo = 0x1000, // debug info - - // Non-verbose level used by LS_INFO of logging.h. Do not use directly. - kTraceTerseInfo = 0x2000, - - kTraceAll = 0xffff -}; - -// External Trace API -class TraceCallback { - public: - virtual void Print(TraceLevel level, const char* message, int length) = 0; - - protected: - virtual ~TraceCallback() {} - TraceCallback() {} -}; - -enum FileFormats -{ - kFileFormatWavFile = 1, - kFileFormatCompressedFile = 2, - kFileFormatPreencodedFile = 4, - kFileFormatPcm16kHzFile = 7, - kFileFormatPcm8kHzFile = 8, - kFileFormatPcm32kHzFile = 9 -}; - -enum ProcessingTypes -{ - kPlaybackPerChannel = 0, - kPlaybackAllChannelsMixed, - kRecordingPerChannel, - kRecordingAllChannelsMixed, - kRecordingPreprocessing -}; - -enum FrameType { - kEmptyFrame = 0, - kAudioFrameSpeech = 1, - kAudioFrameCN = 2, - kVideoFrameKey = 3, - kVideoFrameDelta = 4, -}; - -// Statistics for an RTCP channel -struct RtcpStatistics { - RtcpStatistics() - : fraction_lost(0), - cumulative_lost(0), - extended_max_sequence_number(0), - jitter(0) {} - - uint8_t fraction_lost; - uint32_t cumulative_lost; - uint32_t extended_max_sequence_number; - uint32_t jitter; -}; - -class RtcpStatisticsCallback { - public: - virtual ~RtcpStatisticsCallback() {} - - virtual void StatisticsUpdated(const RtcpStatistics& statistics, - uint32_t ssrc) = 0; - virtual void CNameChanged(const char* cname, uint32_t ssrc) = 0; -}; - -// Statistics for RTCP packet types. -struct RtcpPacketTypeCounter { - RtcpPacketTypeCounter() - : first_packet_time_ms(-1), - nack_packets(0), - fir_packets(0), - pli_packets(0), - nack_requests(0), - unique_nack_requests(0) {} - - void Add(const RtcpPacketTypeCounter& other) { - nack_packets += other.nack_packets; - fir_packets += other.fir_packets; - pli_packets += other.pli_packets; - nack_requests += other.nack_requests; - unique_nack_requests += other.unique_nack_requests; - if (other.first_packet_time_ms != -1 && - (other.first_packet_time_ms < first_packet_time_ms || - first_packet_time_ms == -1)) { - // Use oldest time. - first_packet_time_ms = other.first_packet_time_ms; - } - } - - int64_t TimeSinceFirstPacketInMs(int64_t now_ms) const { - return (first_packet_time_ms == -1) ? -1 : (now_ms - first_packet_time_ms); - } - - int UniqueNackRequestsInPercent() const { - if (nack_requests == 0) { - return 0; - } - return static_cast( - (unique_nack_requests * 100.0f / nack_requests) + 0.5f); - } - - int64_t first_packet_time_ms; // Time when first packet is sent/received. - uint32_t nack_packets; // Number of RTCP NACK packets. - uint32_t fir_packets; // Number of RTCP FIR packets. - uint32_t pli_packets; // Number of RTCP PLI packets. - uint32_t nack_requests; // Number of NACKed RTP packets. - uint32_t unique_nack_requests; // Number of unique NACKed RTP packets. -}; - -class RtcpPacketTypeCounterObserver { - public: - virtual ~RtcpPacketTypeCounterObserver() {} - virtual void RtcpPacketTypesCounterUpdated( - uint32_t ssrc, - const RtcpPacketTypeCounter& packet_counter) = 0; -}; - -// Rate statistics for a stream. -struct BitrateStatistics { - BitrateStatistics() : bitrate_bps(0), packet_rate(0), timestamp_ms(0) {} - - uint32_t bitrate_bps; // Bitrate in bits per second. - uint32_t packet_rate; // Packet rate in packets per second. - uint64_t timestamp_ms; // Ntp timestamp in ms at time of rate estimation. -}; - -// Callback, used to notify an observer whenever new rates have been estimated. -class BitrateStatisticsObserver { - public: - virtual ~BitrateStatisticsObserver() {} - - virtual void Notify(const BitrateStatistics& total_stats, - const BitrateStatistics& retransmit_stats, - uint32_t ssrc) = 0; -}; - -struct FrameCounts { - FrameCounts() : key_frames(0), delta_frames(0) {} - int key_frames; - int delta_frames; -}; - -// Callback, used to notify an observer whenever frame counts have been updated. -class FrameCountObserver { - public: - virtual ~FrameCountObserver() {} - virtual void FrameCountUpdated(const FrameCounts& frame_counts, - uint32_t ssrc) = 0; -}; - -// Callback, used to notify an observer whenever the send-side delay is updated. -class SendSideDelayObserver { - public: - virtual ~SendSideDelayObserver() {} - virtual void SendSideDelayUpdated(int avg_delay_ms, - int max_delay_ms, - uint32_t ssrc) = 0; -}; - -// ================================================================== -// Voice specific types -// ================================================================== - -// Each codec supported can be described by this structure. -struct CodecInst { - int pltype; - char plname[RTP_PAYLOAD_NAME_SIZE]; - int plfreq; - int pacsize; - int channels; - int rate; // bits/sec unlike {start,min,max}Bitrate elsewhere in this file! - - bool operator==(const CodecInst& other) const { - return pltype == other.pltype && - (STR_CASE_CMP(plname, other.plname) == 0) && - plfreq == other.plfreq && - pacsize == other.pacsize && - channels == other.channels && - rate == other.rate; - } - - bool operator!=(const CodecInst& other) const { - return !(*this == other); - } -}; - -// RTP -enum {kRtpCsrcSize = 15}; // RFC 3550 page 13 - -enum RTPDirections -{ - kRtpIncoming = 0, - kRtpOutgoing -}; - -enum PayloadFrequencies -{ - kFreq8000Hz = 8000, - kFreq16000Hz = 16000, - kFreq32000Hz = 32000 -}; - -enum VadModes // degree of bandwidth reduction -{ - kVadConventional = 0, // lowest reduction - kVadAggressiveLow, - kVadAggressiveMid, - kVadAggressiveHigh // highest reduction -}; - -struct NetworkStatistics // NETEQ statistics -{ - // current jitter buffer size in ms - uint16_t currentBufferSize; - // preferred (optimal) buffer size in ms - uint16_t preferredBufferSize; - // adding extra delay due to "peaky jitter" - bool jitterPeaksFound; - // Loss rate (network + late); fraction between 0 and 1, scaled to Q14. - uint16_t currentPacketLossRate; - // Late loss rate; fraction between 0 and 1, scaled to Q14. - uint16_t currentDiscardRate; - // fraction (of original stream) of synthesized audio inserted through - // expansion (in Q14) - uint16_t currentExpandRate; - // fraction (of original stream) of synthesized speech inserted through - // expansion (in Q14) - uint16_t currentSpeechExpandRate; - // fraction of synthesized speech inserted through pre-emptive expansion - // (in Q14) - uint16_t currentPreemptiveRate; - // fraction of data removed through acceleration (in Q14) - uint16_t currentAccelerateRate; - // fraction of data coming from secondary decoding (in Q14) - uint16_t currentSecondaryDecodedRate; - // clock-drift in parts-per-million (negative or positive) - int32_t clockDriftPPM; - // average packet waiting time in the jitter buffer (ms) - int meanWaitingTimeMs; - // median packet waiting time in the jitter buffer (ms) - int medianWaitingTimeMs; - // min packet waiting time in the jitter buffer (ms) - int minWaitingTimeMs; - // max packet waiting time in the jitter buffer (ms) - int maxWaitingTimeMs; - // added samples in off mode due to packet loss - size_t addedSamples; -}; - -// Statistics for calls to AudioCodingModule::PlayoutData10Ms(). -struct AudioDecodingCallStats { - AudioDecodingCallStats() - : calls_to_silence_generator(0), - calls_to_neteq(0), - decoded_normal(0), - decoded_plc(0), - decoded_cng(0), - decoded_plc_cng(0) {} - - int calls_to_silence_generator; // Number of calls where silence generated, - // and NetEq was disengaged from decoding. - int calls_to_neteq; // Number of calls to NetEq. - int decoded_normal; // Number of calls where audio RTP packet decoded. - int decoded_plc; // Number of calls resulted in PLC. - int decoded_cng; // Number of calls where comfort noise generated due to DTX. - int decoded_plc_cng; // Number of calls resulted where PLC faded to CNG. -}; - -typedef struct -{ - int min; // minumum - int max; // maximum - int average; // average -} StatVal; - -typedef struct // All levels are reported in dBm0 -{ - StatVal speech_rx; // long-term speech levels on receiving side - StatVal speech_tx; // long-term speech levels on transmitting side - StatVal noise_rx; // long-term noise/silence levels on receiving side - StatVal noise_tx; // long-term noise/silence levels on transmitting side -} LevelStatistics; - -typedef struct // All levels are reported in dB -{ - StatVal erl; // Echo Return Loss - StatVal erle; // Echo Return Loss Enhancement - StatVal rerl; // RERL = ERL + ERLE - // Echo suppression inside EC at the point just before its NLP - StatVal a_nlp; -} EchoStatistics; - -enum NsModes // type of Noise Suppression -{ - kNsUnchanged = 0, // previously set mode - kNsDefault, // platform default - kNsConference, // conferencing default - kNsLowSuppression, // lowest suppression - kNsModerateSuppression, - kNsHighSuppression, - kNsVeryHighSuppression, // highest suppression -}; - -enum AgcModes // type of Automatic Gain Control -{ - kAgcUnchanged = 0, // previously set mode - kAgcDefault, // platform default - // adaptive mode for use when analog volume control exists (e.g. for - // PC softphone) - kAgcAdaptiveAnalog, - // scaling takes place in the digital domain (e.g. for conference servers - // and embedded devices) - kAgcAdaptiveDigital, - // can be used on embedded devices where the capture signal level - // is predictable - kAgcFixedDigital -}; - -// EC modes -enum EcModes // type of Echo Control -{ - kEcUnchanged = 0, // previously set mode - kEcDefault, // platform default - kEcConference, // conferencing default (aggressive AEC) - kEcAec, // Acoustic Echo Cancellation - kEcAecm, // AEC mobile -}; - -// AECM modes -enum AecmModes // mode of AECM -{ - kAecmQuietEarpieceOrHeadset = 0, - // Quiet earpiece or headset use - kAecmEarpiece, // most earpiece use - kAecmLoudEarpiece, // Loud earpiece or quiet speakerphone use - kAecmSpeakerphone, // most speakerphone use (default) - kAecmLoudSpeakerphone // Loud speakerphone -}; - -// AGC configuration -typedef struct -{ - unsigned short targetLeveldBOv; - unsigned short digitalCompressionGaindB; - bool limiterEnable; -} AgcConfig; // AGC configuration parameters - -enum StereoChannel -{ - kStereoLeft = 0, - kStereoRight, - kStereoBoth -}; - -// Audio device layers -enum AudioLayers -{ - kAudioPlatformDefault = 0, - kAudioWindowsWave = 1, - kAudioWindowsCore = 2, - kAudioLinuxAlsa = 3, - kAudioLinuxPulse = 4 -}; - -// TODO(henrika): to be removed. -enum NetEqModes // NetEQ playout configurations -{ - // Optimized trade-off between low delay and jitter robustness for two-way - // communication. - kNetEqDefault = 0, - // Improved jitter robustness at the cost of increased delay. Can be - // used in one-way communication. - kNetEqStreaming = 1, - // Optimzed for decodability of fax signals rather than for perceived audio - // quality. - kNetEqFax = 2, - // Minimal buffer management. Inserts zeros for lost packets and during - // buffer increases. - kNetEqOff = 3, -}; - -// TODO(henrika): to be removed. -enum OnHoldModes // On Hold direction -{ - kHoldSendAndPlay = 0, // Put both sending and playing in on-hold state. - kHoldSendOnly, // Put only sending in on-hold state. - kHoldPlayOnly // Put only playing in on-hold state. -}; - -// TODO(henrika): to be removed. -enum AmrMode -{ - kRfc3267BwEfficient = 0, - kRfc3267OctetAligned = 1, - kRfc3267FileStorage = 2, -}; - -// ================================================================== -// Video specific types -// ================================================================== - -// Raw video types -enum RawVideoType -{ - kVideoI420 = 0, - kVideoYV12 = 1, - kVideoYUY2 = 2, - kVideoUYVY = 3, - kVideoIYUV = 4, - kVideoARGB = 5, - kVideoRGB24 = 6, - kVideoRGB565 = 7, - kVideoARGB4444 = 8, - kVideoARGB1555 = 9, - kVideoMJPEG = 10, - kVideoNV12 = 11, - kVideoNV21 = 12, - kVideoBGRA = 13, - kVideoUnknown = 99 -}; - -// Video codec -enum { kConfigParameterSize = 128}; -enum { kPayloadNameSize = 32}; -enum { kMaxSimulcastStreams = 4}; -enum { kMaxSpatialLayers = 5 }; -enum { kMaxTemporalStreams = 4}; - -enum VideoCodecComplexity -{ - kComplexityNormal = 0, - kComplexityHigh = 1, - kComplexityHigher = 2, - kComplexityMax = 3 -}; - -enum VideoCodecProfile -{ - kProfileBase = 0x00, - kProfileMain = 0x01 -}; - -enum VP8ResilienceMode { - kResilienceOff, // The stream produced by the encoder requires a - // recovery frame (typically a key frame) to be - // decodable after a packet loss. - kResilientStream, // A stream produced by the encoder is resilient to - // packet losses, but packets within a frame subsequent - // to a loss can't be decoded. - kResilientFrames // Same as kResilientStream but with added resilience - // within a frame. -}; - -// VP8 specific -struct VideoCodecVP8 { - bool pictureLossIndicationOn; - bool feedbackModeOn; - VideoCodecComplexity complexity; - VP8ResilienceMode resilience; - unsigned char numberOfTemporalLayers; - bool denoisingOn; - bool errorConcealmentOn; - bool automaticResizeOn; - bool frameDroppingOn; - int keyFrameInterval; - - bool operator==(const VideoCodecVP8& other) const { - return pictureLossIndicationOn == other.pictureLossIndicationOn && - feedbackModeOn == other.feedbackModeOn && - complexity == other.complexity && - resilience == other.resilience && - numberOfTemporalLayers == other.numberOfTemporalLayers && - denoisingOn == other.denoisingOn && - errorConcealmentOn == other.errorConcealmentOn && - automaticResizeOn == other.automaticResizeOn && - frameDroppingOn == other.frameDroppingOn && - keyFrameInterval == other.keyFrameInterval; - } - - bool operator!=(const VideoCodecVP8& other) const { - return !(*this == other); - } -}; - -// VP9 specific. -struct VideoCodecVP9 { - VideoCodecComplexity complexity; - int resilience; - unsigned char numberOfTemporalLayers; - bool denoisingOn; - bool frameDroppingOn; - int keyFrameInterval; - bool adaptiveQpMode; - bool automaticResizeOn; - unsigned char numberOfSpatialLayers; - bool flexibleMode; -}; - -// H264 specific. -struct VideoCodecH264 { - VideoCodecProfile profile; - bool frameDroppingOn; - int keyFrameInterval; - // These are NULL/0 if not externally negotiated. - const uint8_t* spsData; - size_t spsLen; - const uint8_t* ppsData; - size_t ppsLen; -}; - -// Video codec types -enum VideoCodecType { - kVideoCodecVP8, - kVideoCodecVP9, - kVideoCodecH264, - kVideoCodecI420, - kVideoCodecRED, - kVideoCodecULPFEC, - kVideoCodecGeneric, - kVideoCodecUnknown -}; - -union VideoCodecUnion { - VideoCodecVP8 VP8; - VideoCodecVP9 VP9; - VideoCodecH264 H264; -}; - - -// Simulcast is when the same stream is encoded multiple times with different -// settings such as resolution. -struct SimulcastStream { - unsigned short width; - unsigned short height; - unsigned char numberOfTemporalLayers; - unsigned int maxBitrate; // kilobits/sec. - unsigned int targetBitrate; // kilobits/sec. - unsigned int minBitrate; // kilobits/sec. - unsigned int qpMax; // minimum quality - - bool operator==(const SimulcastStream& other) const { - return width == other.width && - height == other.height && - numberOfTemporalLayers == other.numberOfTemporalLayers && - maxBitrate == other.maxBitrate && - targetBitrate == other.targetBitrate && - minBitrate == other.minBitrate && - qpMax == other.qpMax; - } - - bool operator!=(const SimulcastStream& other) const { - return !(*this == other); - } -}; - -struct SpatialLayer { - int scaling_factor_num; - int scaling_factor_den; - int target_bitrate_bps; - // TODO(ivica): Add max_quantizer and min_quantizer? -}; - -enum VideoCodecMode { - kRealtimeVideo, - kScreensharing -}; - -// Common video codec properties -struct VideoCodec { - VideoCodecType codecType; - char plName[kPayloadNameSize]; - unsigned char plType; - - unsigned short width; - unsigned short height; - - unsigned int startBitrate; // kilobits/sec. - unsigned int maxBitrate; // kilobits/sec. - unsigned int minBitrate; // kilobits/sec. - unsigned int targetBitrate; // kilobits/sec. - - unsigned char maxFramerate; - - VideoCodecUnion codecSpecific; - - unsigned int qpMax; - unsigned char numberOfSimulcastStreams; - SimulcastStream simulcastStream[kMaxSimulcastStreams]; - SpatialLayer spatialLayers[kMaxSpatialLayers]; - - VideoCodecMode mode; - - // When using an external encoder/decoder this allows to pass - // extra options without requiring webrtc to be aware of them. - Config* extra_options; - - bool operator==(const VideoCodec& other) const { - bool ret = codecType == other.codecType && - (STR_CASE_CMP(plName, other.plName) == 0) && - plType == other.plType && - width == other.width && - height == other.height && - startBitrate == other.startBitrate && - maxBitrate == other.maxBitrate && - minBitrate == other.minBitrate && - targetBitrate == other.targetBitrate && - maxFramerate == other.maxFramerate && - qpMax == other.qpMax && - numberOfSimulcastStreams == other.numberOfSimulcastStreams && - mode == other.mode; - if (ret && codecType == kVideoCodecVP8) { - ret &= (codecSpecific.VP8 == other.codecSpecific.VP8); - } - - for (unsigned char i = 0; i < other.numberOfSimulcastStreams && ret; ++i) { - ret &= (simulcastStream[i] == other.simulcastStream[i]); - } - return ret; - } - - bool operator!=(const VideoCodec& other) const { - return !(*this == other); - } -}; - -// Bandwidth over-use detector options. These are used to drive -// experimentation with bandwidth estimation parameters. -// See modules/remote_bitrate_estimator/overuse_detector.h -struct OverUseDetectorOptions { - OverUseDetectorOptions() - : initial_slope(8.0/512.0), - initial_offset(0), - initial_e(), - initial_process_noise(), - initial_avg_noise(0.0), - initial_var_noise(50) { - initial_e[0][0] = 100; - initial_e[1][1] = 1e-1; - initial_e[0][1] = initial_e[1][0] = 0; - initial_process_noise[0] = 1e-13; - initial_process_noise[1] = 1e-2; - } - double initial_slope; - double initial_offset; - double initial_e[2][2]; - double initial_process_noise[2]; - double initial_avg_noise; - double initial_var_noise; -}; - -// This structure will have the information about when packet is actually -// received by socket. -struct PacketTime { - PacketTime() : timestamp(-1), not_before(-1) {} - PacketTime(int64_t timestamp, int64_t not_before) - : timestamp(timestamp), not_before(not_before) { - } - - int64_t timestamp; // Receive time after socket delivers the data. - int64_t not_before; // Earliest possible time the data could have arrived, - // indicating the potential error in the |timestamp| - // value,in case the system is busy. - // For example, the time of the last select() call. - // If unknown, this value will be set to zero. -}; - -struct RTPHeaderExtension { - RTPHeaderExtension(); - - bool hasTransmissionTimeOffset; - int32_t transmissionTimeOffset; - bool hasAbsoluteSendTime; - uint32_t absoluteSendTime; - bool hasTransportSequenceNumber; - uint16_t transportSequenceNumber; - - // Audio Level includes both level in dBov and voiced/unvoiced bit. See: - // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/ - bool hasAudioLevel; - bool voiceActivity; - uint8_t audioLevel; - - // For Coordination of Video Orientation. See - // http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/12.07.00_60/ - // ts_126114v120700p.pdf - bool hasVideoRotation; - uint8_t videoRotation; -}; - -struct RTPHeader { - RTPHeader(); - - bool markerBit; - uint8_t payloadType; - uint16_t sequenceNumber; - uint32_t timestamp; - uint32_t ssrc; - uint8_t numCSRCs; - uint32_t arrOfCSRCs[kRtpCsrcSize]; - size_t paddingLength; - size_t headerLength; - int payload_type_frequency; - RTPHeaderExtension extension; -}; - -struct RtpPacketCounter { - RtpPacketCounter() - : header_bytes(0), - payload_bytes(0), - padding_bytes(0), - packets(0) {} - - void Add(const RtpPacketCounter& other) { - header_bytes += other.header_bytes; - payload_bytes += other.payload_bytes; - padding_bytes += other.padding_bytes; - packets += other.packets; - } - - void AddPacket(size_t packet_length, const RTPHeader& header) { - ++packets; - header_bytes += header.headerLength; - padding_bytes += header.paddingLength; - payload_bytes += - packet_length - (header.headerLength + header.paddingLength); - } - - size_t TotalBytes() const { - return header_bytes + payload_bytes + padding_bytes; - } - - size_t header_bytes; // Number of bytes used by RTP headers. - size_t payload_bytes; // Payload bytes, excluding RTP headers and padding. - size_t padding_bytes; // Number of padding bytes. - uint32_t packets; // Number of packets. -}; - -// Data usage statistics for a (rtp) stream. -struct StreamDataCounters { - StreamDataCounters(); - - void Add(const StreamDataCounters& other) { - transmitted.Add(other.transmitted); - retransmitted.Add(other.retransmitted); - fec.Add(other.fec); - if (other.first_packet_time_ms != -1 && - (other.first_packet_time_ms < first_packet_time_ms || - first_packet_time_ms == -1)) { - // Use oldest time. - first_packet_time_ms = other.first_packet_time_ms; - } - } - - int64_t TimeSinceFirstPacketInMs(int64_t now_ms) const { - return (first_packet_time_ms == -1) ? -1 : (now_ms - first_packet_time_ms); - } - - // Returns the number of bytes corresponding to the actual media payload (i.e. - // RTP headers, padding, retransmissions and fec packets are excluded). - // Note this function does not have meaning for an RTX stream. - size_t MediaPayloadBytes() const { - return transmitted.payload_bytes - retransmitted.payload_bytes - - fec.payload_bytes; - } - - int64_t first_packet_time_ms; // Time when first packet is sent/received. - RtpPacketCounter transmitted; // Number of transmitted packets/bytes. - RtpPacketCounter retransmitted; // Number of retransmitted packets/bytes. - RtpPacketCounter fec; // Number of redundancy packets/bytes. -}; - -// Callback, called whenever byte/packet counts have been updated. -class StreamDataCountersCallback { - public: - virtual ~StreamDataCountersCallback() {} - - virtual void DataCountersUpdated(const StreamDataCounters& counters, - uint32_t ssrc) = 0; -}; - -// RTCP mode to use. Compound mode is described by RFC 4585 and reduced-size -// RTCP mode is described by RFC 5506. -enum class RtcpMode { kOff, kCompound, kReducedSize }; - -} // namespace webrtc - -#endif // WEBRTC_COMMON_TYPES_H_ diff --git a/webrtc/meson.build b/webrtc/meson.build index c166f9b..e7a2dfb 100644 --- a/webrtc/meson.build +++ b/webrtc/meson.build @@ -1,31 +1,11 @@ -webrtc_sources = [ - 'common_types.cc' -] +webrtc_inc = include_directories('.') -webrtc_headers = [ - 'common.h', - 'common_types.h', - 'typedefs.h', -] - -install_headers(webrtc_headers, - subdir: 'webrtc_audio_processing/webrtc' -) - -libwebrtc = static_library('webrtc', - webrtc_sources, - dependencies: common_deps, - include_directories: webrtc_inc, - c_args: common_cflags, - cpp_args: common_cxxflags -) - -webrtc_dep = declare_dependency( - link_with: libwebrtc -) - -subdir('base') -subdir('common_audio') +subdir('rtc_base') +subdir('api') subdir('system_wrappers') +subdir('common_audio') + +subdir('third_party/pffft') +subdir('third_party/rnnoise') subdir('modules') diff --git a/webrtc/modules/BUILD.gn b/webrtc/modules/BUILD.gn new file mode 100644 index 0000000..bb6b7cc --- /dev/null +++ b/webrtc/modules/BUILD.gn @@ -0,0 +1,247 @@ +# 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. + +import("../webrtc.gni") +import("audio_coding/audio_coding.gni") + +group("modules") { + deps = [ + "audio_coding", + "audio_device", + "audio_mixer", + "audio_processing", + "congestion_controller", + "pacing", + "remote_bitrate_estimator", + "rtp_rtcp", + "utility", + "video_coding", + "video_processing", + ] + + if (rtc_desktop_capture_supported) { + deps += [ "desktop_capture" ] + } +} + +rtc_source_set("module_api_public") { + sources = [ "include/module_common_types_public.h" ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_source_set("module_api") { + visibility = [ "*" ] + sources = [ + "include/module.h", + "include/module_common_types.h", + ] +} + +rtc_source_set("module_fec_api") { + visibility = [ "*" ] + sources = [ "include/module_fec_types.h" ] +} + +if (rtc_include_tests) { + modules_tests_resources = [ + "../resources/audio_coding/testfile16kHz.pcm", + "../resources/audio_coding/testfile32kHz.pcm", + "../resources/audio_coding/teststereo32kHz.pcm", + "../resources/foreman_cif.yuv", + ] + + if (is_ios) { + bundle_data("modules_tests_bundle_data") { + testonly = true + sources = modules_tests_resources + outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ] + } + } + + rtc_test("modules_tests") { + testonly = true + + deps = [ + "../test:test_main", + "../test:video_test_common", + "audio_coding:audio_coding_modules_tests", + "rtp_rtcp:rtp_rtcp_modules_tests", + "video_coding:video_coding_modules_tests", + "//testing/gtest", + ] + + if (rtc_desktop_capture_supported) { + deps += [ "desktop_capture:desktop_capture_modules_tests" ] + } + + data = modules_tests_resources + + if (is_android) { + deps += [ + # NOTE(brandtr): Including Java classes seems only to be possible from + # rtc_test targets. Therefore we include this target here, instead of + # in video_coding_modules_tests, where it is actually used. + "../sdk/android:libjingle_peerconnection_java", + "//testing/android/native_test:native_test_native_code", + ] + shard_timeout = 900 + } + + if (is_ios) { + deps += [ ":modules_tests_bundle_data" ] + } + } + + modules_unittests_resources = [ + "../resources/audio_coding/neteq_opus.rtp", + "../resources/audio_coding/neteq_opus_dtx.rtp", + "../resources/audio_coding/neteq_universal_new.rtp", + "../resources/audio_coding/speech_4_channels_48k_one_second.wav", + "../resources/audio_coding/speech_mono_16kHz.pcm", + "../resources/audio_coding/speech_mono_32_48kHz.pcm", + "../resources/audio_coding/testfile16kHz.pcm", + "../resources/audio_coding/testfile32kHz.pcm", + "../resources/audio_coding/testfile_fake_stereo_32kHz.pcm", + "../resources/audio_coding/teststereo32kHz.pcm", + "../resources/audio_device/audio_short16.pcm", + "../resources/audio_device/audio_short44.pcm", + "../resources/audio_device/audio_short48.pcm", + "../resources/audio_processing/agc/agc_audio.pcm", + "../resources/audio_processing/agc/agc_no_circular_buffer.dat", + "../resources/audio_processing/agc/agc_pitch_gain.dat", + "../resources/audio_processing/agc/agc_pitch_lag.dat", + "../resources/audio_processing/agc/agc_spectral_peak.dat", + "../resources/audio_processing/agc/agc_vad.dat", + "../resources/audio_processing/agc/agc_voicing_prob.dat", + "../resources/audio_processing/agc/agc_with_circular_buffer.dat", + "../resources/audio_processing/output_data_fixed.pb", + "../resources/audio_processing/output_data_float.pb", + "../resources/audio_processing/output_data_float_avx2.pb", + "../resources/audio_processing/output_data_mac.pb", + "../resources/audio_processing/transient/ajm-macbook-1-spke16m.pcm", + "../resources/audio_processing/transient/audio16kHz.pcm", + "../resources/audio_processing/transient/audio32kHz.pcm", + "../resources/audio_processing/transient/audio48kHz.pcm", + "../resources/audio_processing/transient/audio8kHz.pcm", + "../resources/audio_processing/transient/detect16kHz.dat", + "../resources/audio_processing/transient/detect32kHz.dat", + "../resources/audio_processing/transient/detect48kHz.dat", + "../resources/audio_processing/transient/detect8kHz.dat", + "../resources/audio_processing/transient/double-utils.dat", + "../resources/audio_processing/transient/float-utils.dat", + "../resources/audio_processing/transient/suppressed16kHz.pcm", + "../resources/audio_processing/transient/suppressed32kHz.pcm", + "../resources/audio_processing/transient/suppressed8kHz.pcm", + "../resources/audio_processing/transient/wpd0.dat", + "../resources/audio_processing/transient/wpd1.dat", + "../resources/audio_processing/transient/wpd2.dat", + "../resources/audio_processing/transient/wpd3.dat", + "../resources/audio_processing/transient/wpd4.dat", + "../resources/audio_processing/transient/wpd5.dat", + "../resources/audio_processing/transient/wpd6.dat", + "../resources/audio_processing/transient/wpd7.dat", + "../resources/deflicker_before_cif_short.yuv", + "../resources/far16_stereo.pcm", + "../resources/far32_stereo.pcm", + "../resources/far44_stereo.pcm", + "../resources/far48_stereo.pcm", + "../resources/far8_stereo.pcm", + "../resources/foremanColorEnhanced_cif_short.yuv", + "../resources/foreman_cif.yuv", + "../resources/foreman_cif_short.yuv", + "../resources/near16_stereo.pcm", + "../resources/near32_stereo.pcm", + "../resources/near44_stereo.pcm", + "../resources/near48_stereo.pcm", + "../resources/near8_stereo.pcm", + "../resources/ref03.aecdump", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_0_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_0_TOF.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_1_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_1_TOF.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_0_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_0_TOF.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_1_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke2_1_TOF.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingDelay1_0_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingDelay1_0_TOF.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingLoss1_0_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingLoss1_0_TOF.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_Multi1_1_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_Multi1_1_TOF.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_0_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_0_TOF.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_1_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyChoke_1_TOF.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyDelay_0_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyDelay_0_TOF.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyLoss_0_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_SteadyLoss_0_TOF.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_UnlimitedSpeed_0_AST.bin", + "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_UnlimitedSpeed_0_TOF.bin", + "../resources/short_mixed_mono_48.dat", + "../resources/short_mixed_mono_48.pcm", + "../resources/short_mixed_mono_48_arm.dat", + "../resources/short_mixed_stereo_48.dat", + "../resources/short_mixed_stereo_48.pcm", + "../resources/voice_engine/audio_tiny48.wav", + ] + if (is_ios) { + bundle_data("modules_unittests_bundle_data") { + testonly = true + sources = modules_unittests_resources + outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ] + } + } + + rtc_test("modules_unittests") { + testonly = true + defines = [] + sources = [ "module_common_types_unittest.cc" ] + + deps = [ + ":module_api", + ":module_api_public", + "../test:test_main", + "../test:test_support", + "audio_coding:audio_coding_unittests", + "audio_device:audio_device_unittests", + "audio_mixer:audio_mixer_unittests", + "audio_processing:audio_processing_unittests", + "audio_processing/aec3:aec3_unittests", + "audio_processing/ns:ns_unittests", + "congestion_controller:congestion_controller_unittests", + "pacing:pacing_unittests", + "remote_bitrate_estimator:remote_bitrate_estimator_unittests", + "rtp_rtcp:rtp_rtcp_unittests", + "utility:utility_unittests", + "video_coding:video_coding_unittests", + "video_processing:video_processing_unittests", + ] + + if (rtc_desktop_capture_supported) { + deps += [ "desktop_capture:desktop_capture_unittests" ] + } + + data = modules_unittests_resources + + if (is_android) { + deps += [ + "../sdk/android:libjingle_peerconnection_java", + "//testing/android/native_test:native_test_support", + ] + shard_timeout = 900 + } + if (is_ios) { + info_plist = "../test/ios/Info.plist" + deps += [ ":modules_unittests_bundle_data" ] + configs += [ "..:common_objc" ] + ldflags = [ "-ObjC" ] + } + } +} diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn index 839a143..cdf7821 100644 --- a/webrtc/modules/audio_coding/BUILD.gn +++ b/webrtc/modules/audio_coding/BUILD.gn @@ -6,287 +6,223 @@ # in the file PATENTS. All contributing project authors may # be found in the AUTHORS file in the root of the source tree. -import("//build/config/arm.gni") -import("../../build/webrtc.gni") - -source_set("rent_a_codec") { - sources = [ - "main/acm2/acm_codec_database.cc", - "main/acm2/acm_codec_database.h", - "main/acm2/rent_a_codec.cc", - "main/acm2/rent_a_codec.h", - ] - configs += [ "../..:common_config" ] - public_configs = [ "../..:common_inherited_config" ] - deps = [ - "../..:webrtc_common", - ] - - defines = [] - if (rtc_include_opus) { - defines += [ "WEBRTC_CODEC_OPUS" ] - } - if (!build_with_mozilla) { - if (current_cpu == "arm") { - defines += [ "WEBRTC_CODEC_ISACFX" ] - } else { - defines += [ "WEBRTC_CODEC_ISAC" ] - } - defines += [ "WEBRTC_CODEC_G722" ] - } - if (!build_with_mozilla && !build_with_chromium) { - defines += [ - "WEBRTC_CODEC_ILBC", - "WEBRTC_CODEC_RED", - ] - } +import("../../webrtc.gni") +import("audio_coding.gni") +if (rtc_enable_protobuf) { + import("//third_party/protobuf/proto_library.gni") } -config("audio_coding_config") { - include_dirs = [ - "main/include", - "../interface", - ] +visibility = [ ":*" ] + +rtc_source_set("audio_coding_module_typedefs") { + visibility += [ "*" ] + sources = [ "include/audio_coding_module_typedefs.h" ] + deps = [ "../../rtc_base:deprecation" ] } -source_set("audio_coding") { +rtc_library("audio_coding") { + visibility += [ "*" ] sources = [ - "main/acm2/acm_common_defs.h", - "main/acm2/acm_receiver.cc", - "main/acm2/acm_receiver.h", - "main/acm2/acm_resampler.cc", - "main/acm2/acm_resampler.h", - "main/acm2/audio_coding_module.cc", - "main/acm2/audio_coding_module_impl.cc", - "main/acm2/audio_coding_module_impl.h", - "main/acm2/call_statistics.cc", - "main/acm2/call_statistics.h", - "main/acm2/codec_manager.cc", - "main/acm2/codec_manager.h", - "main/acm2/codec_owner.cc", - "main/acm2/codec_owner.h", - "main/acm2/initial_delay_manager.cc", - "main/acm2/initial_delay_manager.h", - "main/include/audio_coding_module.h", - "main/include/audio_coding_module_typedefs.h", + "acm2/acm_receiver.cc", + "acm2/acm_receiver.h", + "acm2/acm_remixing.cc", + "acm2/acm_remixing.h", + "acm2/acm_resampler.cc", + "acm2/acm_resampler.h", + "acm2/audio_coding_module.cc", + "acm2/call_statistics.cc", + "acm2/call_statistics.h", + "include/audio_coding_module.h", ] defines = [] - configs += [ "../..:common_config" ] - - public_configs = [ - "../..:common_inherited_config", - ":audio_coding_config", - ] - - if (is_win) { - cflags = [ - # TODO(kjellander): Bug 261: fix this warning. - "/wd4373", # virtual function override. - ] - } - - if (is_clang) { - # Suppress warnings from Chrome's Clang plugins. - # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. - configs -= [ "//build/config/clang:find_bad_constructs" ] - } - deps = [ - ":cng", - ":g711", + ":audio_coding_module_typedefs", + ":default_neteq_factory", ":neteq", - ":pcm16b", - ":rent_a_codec", - "../..:rtc_event_log", - "../..:webrtc_common", + "..:module_api", + "..:module_api_public", + "../../api:array_view", + "../../api:function_view", + "../../api/audio:audio_frame_api", + "../../api/audio_codecs:audio_codecs_api", + "../../api/neteq:neteq_api", "../../common_audio", + "../../common_audio:common_audio_c", + "../../rtc_base:audio_format_to_string", + "../../rtc_base:checks", + "../../rtc_base:deprecation", + "../../rtc_base:rtc_base_approved", + "../../rtc_base/synchronization:mutex", "../../system_wrappers", + "../../system_wrappers:metrics", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", ] - - if (rtc_include_opus) { - defines += [ "WEBRTC_CODEC_OPUS" ] - deps += [ ":webrtc_opus" ] - } - if (!build_with_mozilla) { - if (current_cpu == "arm") { - defines += [ "WEBRTC_CODEC_ISACFX" ] - deps += [ ":isac_fix" ] - } else { - defines += [ "WEBRTC_CODEC_ISAC" ] - deps += [ ":isac" ] - } - defines += [ "WEBRTC_CODEC_G722" ] - deps += [ ":g722" ] - } - if (!build_with_mozilla && !build_with_chromium) { - defines += [ - "WEBRTC_CODEC_ILBC", - "WEBRTC_CODEC_RED", - ] - deps += [ - ":ilbc", - ":red", - ] - } } -source_set("audio_decoder_interface") { +rtc_library("legacy_encoded_audio_frame") { sources = [ - "codecs/audio_decoder.cc", - "codecs/audio_decoder.h", + "codecs/legacy_encoded_audio_frame.cc", + "codecs/legacy_encoded_audio_frame.h", ] - configs += [ "../..:common_config" ] - public_configs = [ "../..:common_inherited_config" ] deps = [ - "../..:webrtc_common", + "../../api:array_view", + "../../api/audio_codecs:audio_codecs_api", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } -source_set("audio_encoder_interface") { +rtc_library("webrtc_cng") { + visibility += webrtc_default_visibility sources = [ - "codecs/audio_encoder.cc", - "codecs/audio_encoder.h", + "codecs/cng/webrtc_cng.cc", + "codecs/cng/webrtc_cng.h", ] - configs += [ "../..:common_config" ] - public_configs = [ "../..:common_inherited_config" ] + deps = [ - "../..:webrtc_common", + "../../api:array_view", + "../../common_audio:common_audio_c", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_conversions", ] } -config("cng_config") { - include_dirs = [ - "../../..", - "codecs/cng/include", - ] -} - -source_set("cng") { +rtc_library("audio_encoder_cng") { + visibility += [ "*" ] sources = [ "codecs/cng/audio_encoder_cng.cc", - "codecs/cng/cng_helpfuns.c", - "codecs/cng/cng_helpfuns.h", - "codecs/cng/include/audio_encoder_cng.h", - "codecs/cng/include/webrtc_cng.h", - "codecs/cng/webrtc_cng.c", - ] - - configs += [ "../..:common_config" ] - - public_configs = [ - "../..:common_inherited_config", - ":cng_config", + "codecs/cng/audio_encoder_cng.h", ] deps = [ + ":webrtc_cng", + "../../api/audio_codecs:audio_codecs_api", + "../../api/units:time_delta", "../../common_audio", - ":audio_encoder_interface", + "../../rtc_base:checks", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } -config("red_config") { - include_dirs = [ "codecs/red" ] -} - -source_set("red") { +rtc_library("red") { + visibility += [ "*" ] sources = [ "codecs/red/audio_encoder_copy_red.cc", "codecs/red/audio_encoder_copy_red.h", ] - configs += [ "../..:common_config" ] - - public_configs = [ - "../..:common_inherited_config", - ":red_config", - ] - deps = [ + "../../api:array_view", + "../../api/audio_codecs:audio_codecs_api", + "../../api/units:time_delta", "../../common_audio", - ":audio_encoder_interface", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } -config("g711_config") { - include_dirs = [ - "../../..", - "codecs/g711/include", - ] -} - -source_set("g711") { +rtc_library("g711") { + visibility += [ "*" ] + poisonous = [ "audio_codecs" ] sources = [ "codecs/g711/audio_decoder_pcm.cc", + "codecs/g711/audio_decoder_pcm.h", "codecs/g711/audio_encoder_pcm.cc", - "codecs/g711/g711.c", - "codecs/g711/g711.h", - "codecs/g711/g711_interface.c", - "codecs/g711/include/audio_decoder_pcm.h", - "codecs/g711/include/audio_encoder_pcm.h", - "codecs/g711/include/g711_interface.h", - ] - - configs += [ "../..:common_config" ] - - public_configs = [ - "../..:common_inherited_config", - ":g711_config", + "codecs/g711/audio_encoder_pcm.h", ] deps = [ - ":audio_encoder_interface", + ":legacy_encoded_audio_frame", + "../../api:array_view", + "../../api/audio_codecs:audio_codecs_api", + "../../api/units:time_delta", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + public_deps = [ ":g711_c" ] # no-presubmit-check TODO(webrtc:8603) } -config("g722_config") { - include_dirs = [ - "../../..", - "codecs/g722/include", +rtc_library("g711_c") { + poisonous = [ "audio_codecs" ] + sources = [ + "codecs/g711/g711_interface.c", + "codecs/g711/g711_interface.h", ] + deps = [ "../third_party/g711:g711_3p" ] } -source_set("g722") { +rtc_library("g722") { + visibility += [ "*" ] + poisonous = [ "audio_codecs" ] sources = [ "codecs/g722/audio_decoder_g722.cc", + "codecs/g722/audio_decoder_g722.h", "codecs/g722/audio_encoder_g722.cc", - "codecs/g722/g722_decode.c", - "codecs/g722/g722_enc_dec.h", - "codecs/g722/g722_encode.c", - "codecs/g722/g722_interface.c", - "codecs/g722/include/audio_decoder_g722.h", - "codecs/g722/include/audio_encoder_g722.h", - "codecs/g722/include/g722_interface.h", - ] - - configs += [ "../..:common_config" ] - - public_configs = [ - "../..:common_inherited_config", - ":g722_config", + "codecs/g722/audio_encoder_g722.h", ] deps = [ - ":audio_encoder_interface", + ":legacy_encoded_audio_frame", + "../../api:array_view", + "../../api/audio_codecs:audio_codecs_api", + "../../api/audio_codecs/g722:audio_encoder_g722_config", + "../../api/units:time_delta", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + public_deps = [ ":g722_c" ] # no-presubmit-check TODO(webrtc:8603) } -config("ilbc_config") { - include_dirs = [ - "../../..", - "codecs/ilbc/include", +rtc_library("g722_c") { + poisonous = [ "audio_codecs" ] + sources = [ + "codecs/g722/g722_interface.c", + "codecs/g722/g722_interface.h", ] + deps = [ "../third_party/g722:g722_3p" ] } -source_set("ilbc") { +rtc_library("ilbc") { + visibility += webrtc_default_visibility + poisonous = [ "audio_codecs" ] + sources = [ + "codecs/ilbc/audio_decoder_ilbc.cc", + "codecs/ilbc/audio_decoder_ilbc.h", + "codecs/ilbc/audio_encoder_ilbc.cc", + "codecs/ilbc/audio_encoder_ilbc.h", + ] + + deps = [ + ":legacy_encoded_audio_frame", + "../../api:array_view", + "../../api/audio_codecs:audio_codecs_api", + "../../api/audio_codecs/ilbc:audio_encoder_ilbc_config", + "../../api/units:time_delta", + "../../common_audio", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + public_deps = [ ":ilbc_c" ] # no-presubmit-check TODO(webrtc:8603) +} + +rtc_library("ilbc_c") { + poisonous = [ "audio_codecs" ] sources = [ "codecs/ilbc/abs_quant.c", "codecs/ilbc/abs_quant.h", "codecs/ilbc/abs_quant_loop.c", "codecs/ilbc/abs_quant_loop.h", - "codecs/ilbc/audio_decoder_ilbc.cc", - "codecs/ilbc/audio_encoder_ilbc.cc", "codecs/ilbc/augmented_cb_corr.c", "codecs/ilbc/augmented_cb_corr.h", "codecs/ilbc/bw_expand.c", @@ -351,9 +287,7 @@ source_set("ilbc") { "codecs/ilbc/hp_output.c", "codecs/ilbc/hp_output.h", "codecs/ilbc/ilbc.c", - "codecs/ilbc/include/audio_decoder_ilbc.h", - "codecs/ilbc/include/audio_encoder_ilbc.h", - "codecs/ilbc/include/ilbc.h", + "codecs/ilbc/ilbc.h", "codecs/ilbc/index_conv_dec.c", "codecs/ilbc/index_conv_dec.h", "codecs/ilbc/index_conv_enc.c", @@ -426,47 +360,93 @@ source_set("ilbc") { "codecs/ilbc/xcorr_coef.h", ] - configs += [ "../..:common_config" ] - - public_configs = [ - "../..:common_inherited_config", - ":ilbc_config", - ] - deps = [ + "../../api/audio_codecs:audio_codecs_api", "../../common_audio", - ":audio_encoder_interface", + "../../common_audio:common_audio_c", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:sanitizer", + "../../rtc_base/system:arch", + "../../rtc_base/system:unused", ] } -source_set("isac_common") { +rtc_source_set("isac_common") { + poisonous = [ "audio_codecs" ] sources = [ + "codecs/isac/audio_decoder_isac_t.h", + "codecs/isac/audio_decoder_isac_t_impl.h", "codecs/isac/audio_encoder_isac_t.h", "codecs/isac/audio_encoder_isac_t_impl.h", - "codecs/isac/locked_bandwidth_info.cc", - "codecs/isac/locked_bandwidth_info.h", ] - public_configs = [ "../..:common_inherited_config" ] + deps = [ + ":isac_bwinfo", + "../../api:scoped_refptr", + "../../api/audio_codecs:audio_codecs_api", + "../../api/units:time_delta", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_minmax", + "../../system_wrappers:field_trial", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } -config("isac_config") { - include_dirs = [ - "../../..", - "codecs/isac/main/include", - ] -} - -source_set("isac") { +rtc_library("isac") { + visibility += [ "*" ] + poisonous = [ "audio_codecs" ] sources = [ "codecs/isac/main/include/audio_decoder_isac.h", "codecs/isac/main/include/audio_encoder_isac.h", + "codecs/isac/main/source/audio_decoder_isac.cc", + "codecs/isac/main/source/audio_encoder_isac.cc", + ] + + deps = [ + ":isac_common", + "../../api/audio_codecs:audio_codecs_api", + ] + public_deps = [ ":isac_c" ] # no-presubmit-check TODO(webrtc:8603) +} + +rtc_source_set("isac_bwinfo") { + sources = [ "codecs/isac/bandwidth_info.h" ] + deps = [] +} + +rtc_library("isac_vad") { + visibility += webrtc_default_visibility + sources = [ + "codecs/isac/main/source/filter_functions.c", + "codecs/isac/main/source/filter_functions.h", + "codecs/isac/main/source/isac_vad.c", + "codecs/isac/main/source/isac_vad.h", + "codecs/isac/main/source/os_specific_inline.h", + "codecs/isac/main/source/pitch_estimator.c", + "codecs/isac/main/source/pitch_estimator.h", + "codecs/isac/main/source/pitch_filter.c", + "codecs/isac/main/source/pitch_filter.h", + "codecs/isac/main/source/settings.h", + "codecs/isac/main/source/structs.h", + ] + deps = [ + ":isac_bwinfo", + "../../rtc_base:compile_assert_c", + "../../rtc_base/system:arch", + "../../rtc_base/system:ignore_warnings", + "../third_party/fft", + ] +} + +rtc_library("isac_c") { + poisonous = [ "audio_codecs" ] + sources = [ "codecs/isac/main/include/isac.h", "codecs/isac/main/source/arith_routines.c", "codecs/isac/main/source/arith_routines.h", "codecs/isac/main/source/arith_routines_hist.c", "codecs/isac/main/source/arith_routines_logist.c", - "codecs/isac/main/source/audio_decoder_isac.cc", - "codecs/isac/main/source/audio_encoder_isac.cc", "codecs/isac/main/source/bandwidth_estimator.c", "codecs/isac/main/source/bandwidth_estimator.h", "codecs/isac/main/source/codec.h", @@ -479,11 +459,6 @@ source_set("isac") { "codecs/isac/main/source/encode_lpc_swb.h", "codecs/isac/main/source/entropy_coding.c", "codecs/isac/main/source/entropy_coding.h", - "codecs/isac/main/source/fft.c", - "codecs/isac/main/source/fft.h", - "codecs/isac/main/source/filter_functions.c", - "codecs/isac/main/source/filterbank_tables.c", - "codecs/isac/main/source/filterbank_tables.h", "codecs/isac/main/source/filterbanks.c", "codecs/isac/main/source/intialize.c", "codecs/isac/main/source/isac.c", @@ -499,48 +474,89 @@ source_set("isac") { "codecs/isac/main/source/lpc_shape_swb16_tables.h", "codecs/isac/main/source/lpc_tables.c", "codecs/isac/main/source/lpc_tables.h", - "codecs/isac/main/source/os_specific_inline.h", - "codecs/isac/main/source/pitch_estimator.c", - "codecs/isac/main/source/pitch_estimator.h", - "codecs/isac/main/source/pitch_filter.c", "codecs/isac/main/source/pitch_gain_tables.c", "codecs/isac/main/source/pitch_gain_tables.h", "codecs/isac/main/source/pitch_lag_tables.c", "codecs/isac/main/source/pitch_lag_tables.h", - "codecs/isac/main/source/settings.h", "codecs/isac/main/source/spectrum_ar_model_tables.c", "codecs/isac/main/source/spectrum_ar_model_tables.h", - "codecs/isac/main/source/structs.h", "codecs/isac/main/source/transform.c", ] - if (is_linux) { + if (is_linux || is_chromeos) { libs = [ "m" ] } - configs += [ "../..:common_config" ] + deps = [ + ":isac_bwinfo", + ":isac_vad", + "../../common_audio", + "../../common_audio:common_audio_c", + "../../rtc_base:checks", + "../../rtc_base:compile_assert_c", + "../../rtc_base:rtc_base_approved", + "../../rtc_base/system:arch", + "../third_party/fft", + ] +} - public_configs = [ - "../..:common_inherited_config", - ":isac_config", +rtc_library("isac_fix") { + visibility += [ "*" ] + poisonous = [ "audio_codecs" ] + sources = [ + "codecs/isac/fix/source/audio_decoder_isacfix.cc", + "codecs/isac/fix/source/audio_encoder_isacfix.cc", ] deps = [ - ":audio_decoder_interface", - ":audio_encoder_interface", ":isac_common", + "../../api/audio_codecs:audio_codecs_api", "../../common_audio", + "../../system_wrappers", + ] + public_deps = [ ":isac_fix_c" ] # no-presubmit-check TODO(webrtc:8603) + + if (rtc_build_with_neon) { + deps += [ ":isac_neon" ] + } +} + +rtc_library("isac_fix_common") { + poisonous = [ "audio_codecs" ] + sources = [ + "codecs/isac/fix/source/codec.h", + "codecs/isac/fix/source/entropy_coding.h", + "codecs/isac/fix/source/fft.c", + "codecs/isac/fix/source/fft.h", + "codecs/isac/fix/source/filterbank_internal.h", + "codecs/isac/fix/source/settings.h", + "codecs/isac/fix/source/structs.h", + "codecs/isac/fix/source/transform_tables.c", + ] + deps = [ + ":isac_bwinfo", + "../../common_audio", + "../../common_audio:common_audio_c", ] } -config("isac_fix_config") { - include_dirs = [ - "../../..", - "codecs/isac/fix/include", - ] +rtc_source_set("isac_fix_c_arm_asm") { + poisonous = [ "audio_codecs" ] + sources = [] + if (current_cpu == "arm" && arm_version >= 7) { + sources += [ + "codecs/isac/fix/source/lattice_armv7.S", + "codecs/isac/fix/source/pitch_filter_armv6.S", + ] + deps = [ + ":isac_fix_common", + "../../rtc_base/system:asm_defines", + ] + } } -source_set("isac_fix") { +rtc_library("isac_fix_c") { + poisonous = [ "audio_codecs" ] sources = [ "codecs/isac/fix/include/audio_decoder_isacfix.h", "codecs/isac/fix/include/audio_encoder_isacfix.h", @@ -549,19 +565,13 @@ source_set("isac_fix") { "codecs/isac/fix/source/arith_routines_hist.c", "codecs/isac/fix/source/arith_routines_logist.c", "codecs/isac/fix/source/arith_routins.h", - "codecs/isac/fix/source/audio_decoder_isacfix.cc", - "codecs/isac/fix/source/audio_encoder_isacfix.cc", "codecs/isac/fix/source/bandwidth_estimator.c", "codecs/isac/fix/source/bandwidth_estimator.h", - "codecs/isac/fix/source/codec.h", "codecs/isac/fix/source/decode.c", "codecs/isac/fix/source/decode_bwe.c", "codecs/isac/fix/source/decode_plc.c", "codecs/isac/fix/source/encode.c", "codecs/isac/fix/source/entropy_coding.c", - "codecs/isac/fix/source/entropy_coding.h", - "codecs/isac/fix/source/fft.c", - "codecs/isac/fix/source/fft.h", "codecs/isac/fix/source/filterbank_tables.c", "codecs/isac/fix/source/filterbank_tables.h", "codecs/isac/fix/source/filterbanks.c", @@ -584,45 +594,44 @@ source_set("isac_fix") { "codecs/isac/fix/source/pitch_gain_tables.h", "codecs/isac/fix/source/pitch_lag_tables.c", "codecs/isac/fix/source/pitch_lag_tables.h", - "codecs/isac/fix/source/settings.h", "codecs/isac/fix/source/spectrum_ar_model_tables.c", "codecs/isac/fix/source/spectrum_ar_model_tables.h", - "codecs/isac/fix/source/structs.h", "codecs/isac/fix/source/transform.c", - "codecs/isac/fix/source/transform_tables.c", - ] - - if (!is_win) { - defines = [ "WEBRTC_LINUX" ] - } - - configs += [ "../..:common_config" ] - - public_configs = [ - "../..:common_inherited_config", - ":isac_fix_config", ] deps = [ - ":audio_encoder_interface", + ":isac_bwinfo", ":isac_common", + "../../api/audio_codecs:audio_codecs_api", "../../common_audio", + "../../common_audio:common_audio_c", + "../../rtc_base:checks", + "../../rtc_base:compile_assert_c", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:sanitizer", "../../system_wrappers", + "../third_party/fft", ] + public_deps = [ ":isac_fix_common" ] # no-presubmit-check TODO(webrtc:8603) + if (rtc_build_with_neon) { deps += [ ":isac_neon" ] + + # TODO(bugs.webrtc.org/9579): Consider moving the usage of NEON from + # pitch_estimator_c.c into the "isac_neon" target and delete this flag: + if (current_cpu != "arm64") { + suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] + cflags = [ "-mfpu=neon" ] + } } if (current_cpu == "arm" && arm_version >= 7) { - sources += [ - "codecs/isac/fix/source/lattice_armv7.S", - "codecs/isac/fix/source/pitch_filter_armv6.S", - ] sources -= [ "codecs/isac/fix/source/lattice_c.c", "codecs/isac/fix/source/pitch_filter_c.c", ] + deps += [ ":isac_fix_c_arm_asm" ] } if (current_cpu == "mipsel") { @@ -651,7 +660,8 @@ source_set("isac_fix") { } if (rtc_build_with_neon) { - source_set("isac_neon") { + rtc_library("isac_neon") { + poisonous = [ "audio_codecs" ] sources = [ "codecs/isac/fix/source/entropy_coding_neon.c", "codecs/isac/fix/source/filterbanks_neon.c", @@ -661,108 +671,259 @@ if (rtc_build_with_neon) { ] if (current_cpu != "arm64") { - # Enable compilation for the NEON instruction set. This is needed - # since //build/config/arm.gni only enables NEON for iOS, not Android. - # This provides the same functionality as webrtc/build/arm_neon.gypi. - configs -= [ "//build/config/compiler:compiler_arm_fpu" ] + # Enable compilation for the NEON instruction set. + suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] cflags = [ "-mfpu=neon" ] } - # Disable LTO on NEON targets due to compiler bug. - # TODO(fdegans): Enable this. See crbug.com/408997. - if (rtc_use_lto) { - cflags -= [ - "-flto", - "-ffat-lto-objects", - ] - } - - configs += [ "../..:common_config" ] - public_configs = [ "../..:common_inherited_config" ] - deps = [ + ":isac_fix_common", "../../common_audio", + "../../common_audio:common_audio_c", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", ] } } -config("pcm16b_config") { - include_dirs = [ - "../../..", - "codecs/pcm16b/include", - ] -} - -source_set("pcm16b") { +rtc_library("pcm16b") { + visibility += [ "*" ] + poisonous = [ "audio_codecs" ] sources = [ "codecs/pcm16b/audio_decoder_pcm16b.cc", + "codecs/pcm16b/audio_decoder_pcm16b.h", "codecs/pcm16b/audio_encoder_pcm16b.cc", - "codecs/pcm16b/include/audio_decoder_pcm16b.h", - "codecs/pcm16b/include/audio_encoder_pcm16b.h", - "codecs/pcm16b/include/pcm16b.h", - "codecs/pcm16b/pcm16b.c", + "codecs/pcm16b/audio_encoder_pcm16b.h", + "codecs/pcm16b/pcm16b_common.cc", + "codecs/pcm16b/pcm16b_common.h", ] deps = [ - ":audio_encoder_interface", ":g711", + ":legacy_encoded_audio_frame", + "../../api:array_view", + "../../api/audio_codecs:audio_codecs_api", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", ] + public_deps = [ ":pcm16b_c" ] # no-presubmit-check TODO(webrtc:8603) +} - configs += [ "../..:common_config" ] - - public_configs = [ - "../..:common_inherited_config", - ":pcm16b_config", +rtc_library("pcm16b_c") { + poisonous = [ "audio_codecs" ] + sources = [ + "codecs/pcm16b/pcm16b.c", + "codecs/pcm16b/pcm16b.h", ] } -config("opus_config") { - include_dirs = [ "../../.." ] +rtc_library("audio_coding_opus_common") { + sources = [ + "codecs/opus/audio_coder_opus_common.cc", + "codecs/opus/audio_coder_opus_common.h", + ] + + deps = [ + "../../api:array_view", + "../../api/audio_codecs:audio_codecs_api", + "../../rtc_base:checks", + "../../rtc_base:stringutils", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } -source_set("webrtc_opus") { +rtc_library("webrtc_opus") { + visibility += webrtc_default_visibility + poisonous = [ "audio_codecs" ] sources = [ "codecs/opus/audio_decoder_opus.cc", + "codecs/opus/audio_decoder_opus.h", "codecs/opus/audio_encoder_opus.cc", - "codecs/opus/include/audio_decoder_opus.h", - "codecs/opus/include/audio_encoder_opus.h", - "codecs/opus/include/opus_interface.h", - "codecs/opus/opus_inst.h", - "codecs/opus/opus_interface.c", + "codecs/opus/audio_encoder_opus.h", ] deps = [ - ":audio_encoder_interface", + ":audio_coding_opus_common", + ":audio_network_adaptor", + "../../api:array_view", + "../../api/audio_codecs:audio_codecs_api", + "../../api/audio_codecs/opus:audio_encoder_opus_config", + "../../common_audio", + "../../rtc_base:checks", + "../../rtc_base:protobuf_utils", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_numerics", + "../../rtc_base:safe_minmax", + "../../system_wrappers:field_trial", ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] + public_deps = # no-presubmit-check TODO(webrtc:8603) + [ ":webrtc_opus_wrapper" ] + + defines = audio_codec_defines if (rtc_build_opus) { - configs += [ "../..:common_config" ] - public_configs = [ "../..:common_inherited_config" ] + public_deps += [ rtc_opus_dir ] # no-presubmit-check TODO(webrtc:8603) + } else if (build_with_mozilla) { + include_dirs = [ "/media/libopus/include" ] + } +} - public_deps = [ - rtc_opus_dir, - ] +rtc_library("webrtc_multiopus") { + visibility += webrtc_default_visibility + poisonous = [ "audio_codecs" ] + sources = [ + "codecs/opus/audio_decoder_multi_channel_opus_impl.cc", + "codecs/opus/audio_decoder_multi_channel_opus_impl.h", + "codecs/opus/audio_encoder_multi_channel_opus_impl.cc", + "codecs/opus/audio_encoder_multi_channel_opus_impl.h", + ] + + deps = [ + ":audio_coding_opus_common", + "../../api/audio_codecs:audio_codecs_api", + "../../api/audio_codecs/opus:audio_decoder_opus_config", + "../../api/audio_codecs/opus:audio_encoder_opus_config", + "../../api/units:time_delta", + "../../rtc_base:checks", + "../../rtc_base:logging", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_minmax", + "../../rtc_base:stringutils", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] + public_deps = # no-presubmit-check TODO(webrtc:8603) + [ ":webrtc_opus_wrapper" ] + + defines = audio_codec_defines + + if (rtc_build_opus) { + public_deps += [ rtc_opus_dir ] + } else if (build_with_mozilla) { + include_dirs = [ "/media/libopus/include" ] + } +} + +rtc_library("webrtc_opus_wrapper") { + poisonous = [ "audio_codecs" ] + sources = [ + "codecs/opus/opus_inst.h", + "codecs/opus/opus_interface.cc", + "codecs/opus/opus_interface.h", + ] + + defines = audio_coding_defines + + if (rtc_build_opus) { + public_deps = [ rtc_opus_dir ] # no-presubmit-check TODO(webrtc:8603) } else if (build_with_mozilla) { include_dirs = [ getenv("DIST") + "/include/opus" ] } -} -config("neteq_config") { - include_dirs = [ - # Need Opus header files for the audio classifier. - "//third_party/opus/src/celt", - "//third_party/opus/src/src", + deps = [ + "../../rtc_base:checks", + "../../rtc_base:ignore_wundef", + "../../rtc_base:rtc_base_approved", + "../../system_wrappers:field_trial", ] } -source_set("neteq") { +if (rtc_enable_protobuf) { + proto_library("ana_debug_dump_proto") { + visibility += webrtc_default_visibility + sources = [ "audio_network_adaptor/debug_dump.proto" ] + link_deps = [ ":ana_config_proto" ] + proto_out_dir = "modules/audio_coding/audio_network_adaptor" + } + proto_library("ana_config_proto") { + visibility += [ "*" ] + sources = [ "audio_network_adaptor/config.proto" ] + proto_out_dir = "modules/audio_coding/audio_network_adaptor" + } +} + +rtc_library("audio_network_adaptor_config") { + visibility += webrtc_default_visibility + sources = [ + "audio_network_adaptor/audio_network_adaptor_config.cc", + "audio_network_adaptor/include/audio_network_adaptor_config.h", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("audio_network_adaptor") { + visibility += webrtc_default_visibility + sources = [ + "audio_network_adaptor/audio_network_adaptor_impl.cc", + "audio_network_adaptor/audio_network_adaptor_impl.h", + "audio_network_adaptor/bitrate_controller.cc", + "audio_network_adaptor/bitrate_controller.h", + "audio_network_adaptor/channel_controller.cc", + "audio_network_adaptor/channel_controller.h", + "audio_network_adaptor/controller.cc", + "audio_network_adaptor/controller.h", + "audio_network_adaptor/controller_manager.cc", + "audio_network_adaptor/controller_manager.h", + "audio_network_adaptor/debug_dump_writer.cc", + "audio_network_adaptor/debug_dump_writer.h", + "audio_network_adaptor/dtx_controller.cc", + "audio_network_adaptor/dtx_controller.h", + "audio_network_adaptor/event_log_writer.cc", + "audio_network_adaptor/event_log_writer.h", + "audio_network_adaptor/fec_controller_plr_based.cc", + "audio_network_adaptor/fec_controller_plr_based.h", + "audio_network_adaptor/frame_length_controller.cc", + "audio_network_adaptor/frame_length_controller.h", + "audio_network_adaptor/frame_length_controller_v2.cc", + "audio_network_adaptor/frame_length_controller_v2.h", + "audio_network_adaptor/include/audio_network_adaptor.h", + "audio_network_adaptor/util/threshold_curve.h", + ] + + public_deps = # no-presubmit-check TODO(webrtc:8603) + [ ":audio_network_adaptor_config" ] + + deps = [ + "../../api/audio_codecs:audio_codecs_api", + "../../api/rtc_event_log", + "../../common_audio", + "../../logging:rtc_event_audio", + "../../rtc_base:checks", + "../../rtc_base:ignore_wundef", + "../../rtc_base:protobuf_utils", + "../../rtc_base:rtc_base_approved", + "../../rtc_base/system:file_wrapper", + "../../system_wrappers", + "../../system_wrappers:field_trial", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/types:optional", + ] + + if (rtc_enable_protobuf) { + deps += [ + ":ana_config_proto", + ":ana_debug_dump_proto", + ] + } +} + +rtc_library("neteq") { + visibility += webrtc_default_visibility sources = [ "neteq/accelerate.cc", "neteq/accelerate.h", - "neteq/audio_classifier.cc", - "neteq/audio_classifier.h", - "neteq/audio_decoder_impl.cc", - "neteq/audio_decoder_impl.h", "neteq/audio_multi_vector.cc", "neteq/audio_multi_vector.h", "neteq/audio_vector.cc", @@ -773,19 +934,14 @@ source_set("neteq") { "neteq/buffer_level_filter.h", "neteq/comfort_noise.cc", "neteq/comfort_noise.h", + "neteq/cross_correlation.cc", + "neteq/cross_correlation.h", "neteq/decision_logic.cc", "neteq/decision_logic.h", - "neteq/decision_logic_fax.cc", - "neteq/decision_logic_fax.h", - "neteq/decision_logic_normal.cc", - "neteq/decision_logic_normal.h", "neteq/decoder_database.cc", "neteq/decoder_database.h", - "neteq/defines.h", "neteq/delay_manager.cc", "neteq/delay_manager.h", - "neteq/delay_peak_detector.cc", - "neteq/delay_peak_detector.h", "neteq/dsp_helper.cc", "neteq/dsp_helper.h", "neteq/dtmf_buffer.cc", @@ -794,28 +950,30 @@ source_set("neteq") { "neteq/dtmf_tone_generator.h", "neteq/expand.cc", "neteq/expand.h", - "neteq/include/neteq.h", + "neteq/expand_uma_logger.cc", + "neteq/expand_uma_logger.h", + "neteq/histogram.cc", + "neteq/histogram.h", "neteq/merge.cc", "neteq/merge.h", - "neteq/nack.cc", - "neteq/nack.h", - "neteq/neteq.cc", + "neteq/nack_tracker.cc", + "neteq/nack_tracker.h", "neteq/neteq_impl.cc", "neteq/neteq_impl.h", "neteq/normal.cc", "neteq/normal.h", + "neteq/packet.cc", + "neteq/packet.h", "neteq/packet_buffer.cc", "neteq/packet_buffer.h", - "neteq/payload_splitter.cc", - "neteq/payload_splitter.h", "neteq/post_decode_vad.cc", "neteq/post_decode_vad.h", "neteq/preemptive_expand.cc", "neteq/preemptive_expand.h", "neteq/random_vector.cc", "neteq/random_vector.h", - "neteq/rtcp.cc", - "neteq/rtcp.h", + "neteq/red_payload_splitter.cc", + "neteq/red_payload_splitter.h", "neteq/statistics_calculator.cc", "neteq/statistics_calculator.h", "neteq/sync_buffer.cc", @@ -826,42 +984,1132 @@ source_set("neteq") { "neteq/timestamp_scaler.h", ] - configs += [ "../..:common_config" ] + deps = [ + ":audio_coding_module_typedefs", + ":webrtc_cng", + "..:module_api", + "..:module_api_public", + "../../api:array_view", + "../../api:rtp_headers", + "../../api:rtp_packet_info", + "../../api:scoped_refptr", + "../../api/audio:audio_frame_api", + "../../api/audio_codecs:audio_codecs_api", + "../../api/neteq:neteq_api", + "../../api/neteq:neteq_controller_api", + "../../api/neteq:tick_timer", + "../../common_audio", + "../../common_audio:common_audio_c", + "../../rtc_base:audio_format_to_string", + "../../rtc_base:checks", + "../../rtc_base:gtest_prod", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_minmax", + "../../rtc_base:sanitizer", + "../../rtc_base/experiments:field_trial_parser", + "../../rtc_base/synchronization:mutex", + "../../system_wrappers", + "../../system_wrappers:field_trial", + "../../system_wrappers:metrics", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} - public_configs = [ - "../..:common_inherited_config", - ":neteq_config", +rtc_source_set("default_neteq_factory") { + visibility += webrtc_default_visibility + sources = [ + "neteq/default_neteq_factory.cc", + "neteq/default_neteq_factory.h", + ] + deps = [ + ":neteq", + "../../api:scoped_refptr", + "../../api/audio_codecs:audio_codecs_api", + "../../api/neteq:default_neteq_controller_factory", + "../../api/neteq:neteq_api", + "../../system_wrappers:system_wrappers", + ] +} + +# Although providing only test support, this target must be outside of the +# rtc_include_tests conditional. The reason is that it supports fuzzer tests +# that ultimately are built and run as a part of the Chromium ecosystem, which +# does not set the rtc_include_tests flag. +rtc_library("neteq_tools_minimal") { + visibility += webrtc_default_visibility + sources = [ + "neteq/tools/audio_sink.cc", + "neteq/tools/audio_sink.h", + "neteq/tools/encode_neteq_input.cc", + "neteq/tools/encode_neteq_input.h", + "neteq/tools/neteq_input.cc", + "neteq/tools/neteq_input.h", + "neteq/tools/neteq_test.cc", + "neteq/tools/neteq_test.h", + "neteq/tools/packet.cc", + "neteq/tools/packet.h", + "neteq/tools/packet_source.cc", + "neteq/tools/packet_source.h", + ] + + deps = [ + ":default_neteq_factory", + ":neteq", + "../../api:neteq_simulator_api", + "../../api:rtp_headers", + "../../api/audio:audio_frame_api", + "../../api/audio_codecs:audio_codecs_api", + "../../api/neteq:custom_neteq_factory", + "../../api/neteq:default_neteq_controller_factory", + "../../api/neteq:neteq_api", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../system_wrappers", + "../rtp_rtcp", + "../rtp_rtcp:rtp_rtcp_format", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + defines = audio_codec_defines +} + +rtc_library("neteq_test_tools") { + visibility += webrtc_default_visibility + testonly = true + sources = [ + "neteq/tools/audio_checksum.h", + "neteq/tools/audio_loop.cc", + "neteq/tools/audio_loop.h", + "neteq/tools/constant_pcm_packet_source.cc", + "neteq/tools/constant_pcm_packet_source.h", + "neteq/tools/initial_packet_inserter_neteq_input.cc", + "neteq/tools/initial_packet_inserter_neteq_input.h", + "neteq/tools/neteq_packet_source_input.cc", + "neteq/tools/neteq_packet_source_input.h", + "neteq/tools/output_audio_file.h", + "neteq/tools/output_wav_file.h", + "neteq/tools/rtp_file_source.cc", + "neteq/tools/rtp_file_source.h", + "neteq/tools/rtp_generator.cc", + "neteq/tools/rtp_generator.h", ] deps = [ - ":audio_decoder_interface", - ":cng", - ":g711", ":pcm16b", - "../..:webrtc_common", + "../../api:array_view", + "../../api:rtp_headers", + "../../common_audio", + "../../rtc_base", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../rtc_base/system:arch", + "../../test:rtp_test_utils", + "../rtp_rtcp", + "../rtp_rtcp:rtp_rtcp_format", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + + public_deps = [ + ":neteq_tools", + ":neteq_tools_minimal", + ] + + if (rtc_enable_protobuf) { + sources += [ + "neteq/tools/neteq_event_log_input.cc", + "neteq/tools/neteq_event_log_input.h", + ] + deps += [ ":rtc_event_log_source" ] + } +} + +rtc_library("neteq_tools") { + visibility += webrtc_default_visibility + sources = [ + "neteq/tools/fake_decode_from_file.cc", + "neteq/tools/fake_decode_from_file.h", + "neteq/tools/neteq_delay_analyzer.cc", + "neteq/tools/neteq_delay_analyzer.h", + "neteq/tools/neteq_replacement_input.cc", + "neteq/tools/neteq_replacement_input.h", + "neteq/tools/neteq_stats_getter.cc", + "neteq/tools/neteq_stats_getter.h", + "neteq/tools/neteq_stats_plotter.cc", + "neteq/tools/neteq_stats_plotter.h", + ] + + deps = [ + "..:module_api_public", + "../../api:array_view", + "../../api/audio_codecs:audio_codecs_api", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../rtp_rtcp", + "../rtp_rtcp:rtp_rtcp_format", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] + + public_deps = [ + ":neteq_input_audio_tools", + ":neteq_tools_minimal", + ] +} + +rtc_library("neteq_input_audio_tools") { + visibility += webrtc_default_visibility + sources = [ + "neteq/tools/input_audio_file.cc", + "neteq/tools/input_audio_file.h", + "neteq/tools/resample_input_audio_file.cc", + "neteq/tools/resample_input_audio_file.h", + ] + + deps = [ + "../../common_audio", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + ] +} + +if (rtc_enable_protobuf) { + rtc_library("rtc_event_log_source") { + testonly = true + + sources = [ + "neteq/tools/rtc_event_log_source.cc", + "neteq/tools/rtc_event_log_source.h", + ] + + deps = [ + ":neteq_tools_minimal", + "../../logging:rtc_event_log_parser", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../rtp_rtcp", + "../rtp_rtcp:rtp_rtcp_format", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + public_deps = # no-presubmit-check TODO(webrtc:8603) + [ "../../logging:rtc_event_log_proto" ] + } + + # Only used for test purpose. Since we want to use it from chromium + # (see audio_coding_modules_tests_shared below), we cannot guard it + # under rtc_include_tests. + proto_library("neteq_unittest_proto") { + testonly = true + sources = [ "neteq/neteq_unittest.proto" ] + proto_out_dir = "modules/audio_coding/neteq" + } +} + +# Allow to re-use some test classes from chromium. +rtc_library("audio_coding_modules_tests_shared") { + testonly = true + visibility = [] + visibility = [ "*" ] + + sources = [ + "neteq/test/neteq_decoding_test.cc", + "neteq/test/neteq_decoding_test.h", + "neteq/test/result_sink.cc", + "neteq/test/result_sink.h", + "test/PCMFile.cc", + "test/PCMFile.h", + "test/TestStereo.cc", + "test/TestStereo.h", + "test/opus_test.cc", + "test/opus_test.h", + ] + + deps = [ + ":audio_coding", + ":audio_coding_module_typedefs", + ":default_neteq_factory", + ":neteq_test_tools", + ":neteq_tools_minimal", + ":webrtc_opus_wrapper", + "..:module_api", + "../../api:rtp_headers", + "../../api/audio:audio_frame_api", + "../../api/audio_codecs:builtin_audio_decoder_factory", + "../../api/audio_codecs:builtin_audio_encoder_factory", + "../../api/neteq:neteq_api", + "../../rtc_base", + "../../rtc_base:checks", + "../../rtc_base:ignore_wundef", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:stringutils", + "../../system_wrappers", + "../../test:fileutils", + "../../test:test_support", + "../rtp_rtcp:rtp_rtcp_format", + "//testing/gtest", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] + defines = audio_coding_defines + + if (rtc_enable_protobuf) { + defines += [ "WEBRTC_NETEQ_UNITTEST_BITEXACT" ] + deps += [ ":neteq_unittest_proto" ] + } +} + +if (rtc_include_tests) { + audio_coding_deps = [ + ":audio_encoder_cng", + ":g711", + ":g722", + ":pcm16b", "../../common_audio", "../../system_wrappers", ] - - defines = [] - - if (rtc_include_opus) { - defines += [ "WEBRTC_CODEC_OPUS" ] - deps += [ ":webrtc_opus" ] + if (rtc_include_ilbc) { + audio_coding_deps += [ ":ilbc" ] } - if (!build_with_mozilla) { - if (current_cpu == "arm") { - defines += [ "WEBRTC_CODEC_ISACFX" ] - deps += [ ":isac_fix" ] - } else { - defines += [ "WEBRTC_CODEC_ISAC" ] - deps += [ ":isac" ] - } - defines += [ "WEBRTC_CODEC_G722" ] - deps += [ ":g722" ] + if (rtc_include_opus) { + audio_coding_deps += [ ":webrtc_opus" ] + } + if (current_cpu == "arm") { + audio_coding_deps += [ ":isac_fix" ] + } else { + audio_coding_deps += [ ":isac" ] } if (!build_with_mozilla && !build_with_chromium) { - defines += [ "WEBRTC_CODEC_ILBC" ] - deps += [ ":ilbc" ] + audio_coding_deps += [ ":red" ] + } + + rtc_source_set("mocks") { + testonly = true + sources = [ + "audio_network_adaptor/mock/mock_audio_network_adaptor.h", + "audio_network_adaptor/mock/mock_controller.h", + "audio_network_adaptor/mock/mock_controller_manager.h", + "audio_network_adaptor/mock/mock_debug_dump_writer.h", + ] + deps = [ + ":audio_network_adaptor", + "../../test:test_support", + ] + } + + group("audio_coding_tests") { + visibility += webrtc_default_visibility + testonly = true + public_deps = [ + ":acm_receive_test", + ":acm_send_test", + ":audio_codec_speed_tests", + ":audio_decoder_unittests", + ":audio_decoder_unittests", + ":g711_test", + ":g722_test", + ":ilbc_test", + ":isac_api_test", + ":isac_fix_test", + ":isac_switch_samprate_test", + ":isac_test", + ":neteq_ilbc_quality_test", + ":neteq_isac_quality_test", + ":neteq_opus_quality_test", + ":neteq_pcm16b_quality_test", + ":neteq_pcmu_quality_test", + ":neteq_speed_test", + ":rtp_analyze", + ":rtp_encode", + ":rtp_jitter", + ":rtpcat", + ":webrtc_opus_fec_test", + ] + if (rtc_enable_protobuf) { + public_deps += [ ":neteq_rtpplay" ] + } + } + + rtc_library("audio_coding_modules_tests") { + testonly = true + visibility += webrtc_default_visibility + + sources = [ + "test/Channel.cc", + "test/Channel.h", + "test/EncodeDecodeTest.cc", + "test/EncodeDecodeTest.h", + "test/PacketLossTest.cc", + "test/PacketLossTest.h", + "test/RTPFile.cc", + "test/RTPFile.h", + "test/TestAllCodecs.cc", + "test/TestAllCodecs.h", + "test/TestRedFec.cc", + "test/TestRedFec.h", + "test/TestVADDTX.cc", + "test/TestVADDTX.h", + "test/Tester.cc", + "test/TwoWayCommunication.cc", + "test/TwoWayCommunication.h", + "test/iSACTest.cc", + "test/iSACTest.h", + "test/target_delay_unittest.cc", + ] + deps = [ + ":audio_coding", + ":audio_coding_module_typedefs", + ":audio_coding_modules_tests_shared", + ":audio_encoder_cng", + ":pcm16b_c", + ":red", + ":webrtc_opus_wrapper", + "..:module_api", + "../../api:rtp_headers", + "../../api/audio:audio_frame_api", + "../../api/audio_codecs:audio_codecs_api", + "../../api/audio_codecs:builtin_audio_decoder_factory", + "../../api/audio_codecs:builtin_audio_encoder_factory", + "../../api/audio_codecs/L16:audio_decoder_L16", + "../../api/audio_codecs/L16:audio_encoder_L16", + "../../api/audio_codecs/g711:audio_decoder_g711", + "../../api/audio_codecs/g711:audio_encoder_g711", + "../../api/audio_codecs/g722:audio_decoder_g722", + "../../api/audio_codecs/g722:audio_encoder_g722", + "../../api/audio_codecs/ilbc:audio_decoder_ilbc", + "../../api/audio_codecs/ilbc:audio_encoder_ilbc", + "../../api/audio_codecs/isac:audio_decoder_isac_float", + "../../api/audio_codecs/isac:audio_encoder_isac_float", + "../../api/audio_codecs/opus:audio_decoder_opus", + "../../api/audio_codecs/opus:audio_encoder_opus", + "../../common_audio", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../rtc_base/synchronization:mutex", + "../../rtc_base/synchronization:rw_lock_wrapper", + "../../system_wrappers", + "../../test:fileutils", + "../../test:test_support", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] + defines = audio_coding_defines + } + + rtc_library("audio_coding_perf_tests") { + testonly = true + visibility += webrtc_default_visibility + + sources = [ + "codecs/opus/opus_complexity_unittest.cc", + "neteq/test/neteq_performance_unittest.cc", + ] + deps = [ + ":neteq_test_support", + ":neteq_test_tools", + "../../api/audio_codecs/opus:audio_encoder_opus", + "../../rtc_base:rtc_base_approved", + "../../system_wrappers", + "../../system_wrappers:field_trial", + "../../test:fileutils", + "../../test:perf_test", + "../../test:test_support", + ] + } + + rtc_library("acm_receive_test") { + testonly = true + sources = [ + "acm2/acm_receive_test.cc", + "acm2/acm_receive_test.h", + ] + + defines = audio_coding_defines + + deps = audio_coding_deps + [ + "../../api:scoped_refptr", + "..:module_api", + ":audio_coding", + "../../api/audio_codecs:audio_codecs_api", + "../../api/audio_codecs:builtin_audio_decoder_factory", + ":neteq_tools", + "../../rtc_base:rtc_base_approved", + "../../test:test_support", + "//testing/gtest", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] + } + + rtc_library("acm_send_test") { + testonly = true + sources = [ + "acm2/acm_send_test.cc", + "acm2/acm_send_test.h", + ] + + defines = audio_coding_defines + + deps = audio_coding_deps + [ + "//third_party/abseil-cpp/absl/strings", + "../../api/audio:audio_frame_api", + "../../rtc_base:checks", + ":audio_coding", + ":neteq_tools", + "../../api/audio_codecs:builtin_audio_decoder_factory", + "../../api/audio_codecs:builtin_audio_encoder_factory", + "../../api/audio_codecs:audio_codecs_api", + "../../rtc_base:rtc_base_approved", + "../../test:test_support", + "//testing/gtest", + ] + } + + audio_decoder_unittests_resources = + [ "../../resources/audio_coding/testfile32kHz.pcm" ] + + if (is_ios) { + bundle_data("audio_decoder_unittests_bundle_data") { + testonly = true + sources = audio_decoder_unittests_resources + outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ] + } + } + + rtc_test("audio_decoder_unittests") { + testonly = true + sources = [ "neteq/audio_decoder_unittest.cc" ] + + defines = neteq_defines + + deps = [ + ":ilbc", + ":isac", + ":isac_fix", + ":neteq", + ":neteq_tools", + "../../test:fileutils", + "../../api/audio_codecs:audio_codecs_api", + "../../api/audio_codecs/opus:audio_encoder_opus", + "../../common_audio", + "../../rtc_base/system:arch", + "../../test:test_main", + "//testing/gtest", + "../../test:test_support", + ] + audio_coding_deps + + data = audio_decoder_unittests_resources + + if (is_android) { + deps += [ "//testing/android/native_test:native_test_native_code" ] + shard_timeout = 900 + } + if (is_ios) { + deps += [ ":audio_decoder_unittests_bundle_data" ] + } + } + + if (rtc_enable_protobuf) { + rtc_library("neteq_test_factory") { + testonly = true + visibility += webrtc_default_visibility + defines = audio_codec_defines + deps = [ + "../../rtc_base:checks", + "../../test:fileutils", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + sources = [ + "neteq/tools/neteq_test_factory.cc", + "neteq/tools/neteq_test_factory.h", + ] + + deps += [ + ":neteq", + ":neteq_test_tools", + "../../api/audio_codecs:builtin_audio_decoder_factory", + "../../api/neteq:neteq_api", + "../../rtc_base:rtc_base_approved", + "../../test:audio_codec_mocks", + "../../test:field_trial", + "../../test:test_support", + ] + } + + rtc_executable("neteq_rtpplay") { + testonly = true + visibility += [ "*" ] + defines = [] + deps = [ + ":neteq_test_factory", + ":neteq_test_tools", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:stringutils", + "../../system_wrappers:field_trial", + "../../test:field_trial", + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/flags:parse", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] + sources = [ "neteq/tools/neteq_rtpplay.cc" ] + } + } + + audio_codec_speed_tests_resources = [ + "//resources/audio_coding/music_stereo_48kHz.pcm", + "//resources/audio_coding/speech_mono_16kHz.pcm", + "//resources/audio_coding/speech_mono_32_48kHz.pcm", + ] + + if (is_ios) { + bundle_data("audio_codec_speed_tests_data") { + testonly = true + sources = audio_codec_speed_tests_resources + outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ] + } + } + + rtc_test("audio_codec_speed_tests") { + testonly = true + defines = [] + deps = [ "../../test:fileutils" ] + sources = [ + "codecs/isac/fix/test/isac_speed_test.cc", + "codecs/opus/opus_speed_test.cc", + "codecs/tools/audio_codec_speed_test.cc", + "codecs/tools/audio_codec_speed_test.h", + ] + + data = audio_codec_speed_tests_resources + + if (is_android) { + deps += [ "//testing/android/native_test:native_test_native_code" ] + shard_timeout = 900 + } + + if (is_ios) { + deps += [ ":audio_codec_speed_tests_data" ] + } + + deps += [ + ":isac_fix", + ":webrtc_opus", + "../../rtc_base:rtc_base_approved", + "../../test:test_main", + "../../test:test_support", + "../audio_processing", + "//testing/gtest", + ] + } + + rtc_library("neteq_test_support") { + testonly = true + sources = [ + "neteq/tools/neteq_performance_test.cc", + "neteq/tools/neteq_performance_test.h", + ] + + deps = [ + ":default_neteq_factory", + ":neteq", + ":neteq_test_tools", + ":pcm16b", + "../../api/audio:audio_frame_api", + "../../api/audio_codecs:audio_codecs_api", + "../../api/audio_codecs:builtin_audio_decoder_factory", + "../../api/neteq:neteq_api", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../system_wrappers", + "../../test:fileutils", + "../../test:test_support", + "//testing/gtest", + ] + } + + rtc_library("neteq_quality_test_support") { + testonly = true + sources = [ + "neteq/tools/neteq_quality_test.cc", + "neteq/tools/neteq_quality_test.h", + ] + + deps = [ + ":default_neteq_factory", + ":neteq", + ":neteq_test_tools", + "../../api/audio_codecs:builtin_audio_decoder_factory", + "../../api/neteq:neteq_api", + "../../rtc_base:checks", + "../../system_wrappers", + "../../test:fileutils", + "../../test:test_support", + "//testing/gtest", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/flags:flag" ] + } + + rtc_executable("rtp_encode") { + testonly = true + + deps = audio_coding_deps + [ + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/flags:parse", + ":audio_coding", + ":audio_encoder_cng", + ":neteq_input_audio_tools", + "../../api/audio:audio_frame_api", + "../../api/audio_codecs/g711:audio_encoder_g711", + "../../api/audio_codecs/L16:audio_encoder_L16", + "../../api/audio_codecs/g722:audio_encoder_g722", + "../../api/audio_codecs/ilbc:audio_encoder_ilbc", + "../../api/audio_codecs/isac:audio_encoder_isac", + "../../api/audio_codecs/opus:audio_encoder_opus", + "../../rtc_base:safe_conversions", + "//third_party/abseil-cpp/absl/memory", + ] + + sources = [ "neteq/tools/rtp_encode.cc" ] + + defines = audio_coding_defines + } + + rtc_executable("rtp_jitter") { + testonly = true + + deps = audio_coding_deps + [ + "../rtp_rtcp:rtp_rtcp_format", + "../../api:array_view", + "../../rtc_base:rtc_base_approved", + ] + + sources = [ "neteq/tools/rtp_jitter.cc" ] + + defines = audio_coding_defines + } + + rtc_executable("rtpcat") { + testonly = true + + sources = [ "neteq/tools/rtpcat.cc" ] + + deps = [ + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../test:rtp_test_utils", + "//testing/gtest", + ] + } + + rtc_executable("rtp_analyze") { + testonly = true + + sources = [ "neteq/tools/rtp_analyze.cc" ] + + deps = [ + ":neteq", + ":neteq_test_tools", + ":pcm16b", + "//testing/gtest", + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/flags:parse", + ] + } + + rtc_executable("neteq_opus_quality_test") { + testonly = true + + sources = [ "neteq/test/neteq_opus_quality_test.cc" ] + + deps = [ + ":neteq", + ":neteq_quality_test_support", + ":neteq_tools", + ":webrtc_opus", + "../../rtc_base:rtc_base_approved", + "../../test:test_main", + "//testing/gtest", + "//third_party/abseil-cpp/absl/flags:flag", + ] + } + + rtc_executable("neteq_speed_test") { + testonly = true + + sources = [ "neteq/test/neteq_speed_test.cc" ] + + deps = [ + ":neteq", + ":neteq_test_support", + "../../rtc_base:checks", + "../../test:test_support", + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/flags:parse", + ] + } + + rtc_executable("neteq_ilbc_quality_test") { + testonly = true + + sources = [ "neteq/test/neteq_ilbc_quality_test.cc" ] + + deps = [ + ":ilbc", + ":neteq", + ":neteq_quality_test_support", + ":neteq_tools", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../test:fileutils", + "../../test:test_main", + "//testing/gtest", + "//third_party/abseil-cpp/absl/flags:flag", + ] + } + + rtc_executable("neteq_isac_quality_test") { + testonly = true + + sources = [ "neteq/test/neteq_isac_quality_test.cc" ] + + deps = [ + ":isac_fix", + ":neteq", + ":neteq_quality_test_support", + "../../rtc_base:rtc_base_approved", + "../../test:test_main", + "//testing/gtest", + "//third_party/abseil-cpp/absl/flags:flag", + ] + } + + rtc_executable("neteq_pcmu_quality_test") { + testonly = true + + sources = [ "neteq/test/neteq_pcmu_quality_test.cc" ] + + deps = [ + ":g711", + ":neteq", + ":neteq_quality_test_support", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../test:fileutils", + "../../test:test_main", + "//testing/gtest", + "//third_party/abseil-cpp/absl/flags:flag", + ] + } + + rtc_executable("neteq_pcm16b_quality_test") { + testonly = true + + sources = [ "neteq/test/neteq_pcm16b_quality_test.cc" ] + + deps = [ + ":neteq", + ":neteq_quality_test_support", + ":pcm16b", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../test:fileutils", + "../../test:test_main", + "//testing/gtest", + "//third_party/abseil-cpp/absl/flags:flag", + ] + } + + rtc_executable("isac_fix_test") { + testonly = true + + sources = [ "codecs/isac/fix/test/kenny.cc" ] + + deps = [ + ":isac_fix", + "../../test:perf_test", + "../../test:test_support", + ] + + data = [ "../../resources/speech_and_misc_wb.pcm" ] + } + + rtc_library("isac_test_util") { + testonly = true + sources = [ + "codecs/isac/main/util/utility.c", + "codecs/isac/main/util/utility.h", + ] + } + + rtc_executable("isac_test") { + testonly = true + + sources = [ "codecs/isac/main/test/simpleKenny.c" ] + + deps = [ + ":isac", + ":isac_test_util", + "../../rtc_base:rtc_base_approved", + ] + } + + rtc_executable("g711_test") { + testonly = true + + sources = [ "codecs/g711/test/testG711.cc" ] + + deps = [ ":g711" ] + } + + rtc_executable("g722_test") { + testonly = true + + sources = [ "codecs/g722/test/testG722.cc" ] + + deps = [ ":g722" ] + } + + rtc_executable("isac_api_test") { + testonly = true + + sources = [ "codecs/isac/main/test/ReleaseTest-API/ReleaseTest-API.cc" ] + + deps = [ + ":isac", + ":isac_test_util", + "../../rtc_base:rtc_base_approved", + ] + } + + rtc_executable("isac_switch_samprate_test") { + testonly = true + + sources = [ "codecs/isac/main/test/SwitchingSampRate/SwitchingSampRate.cc" ] + + deps = [ + ":isac", + ":isac_test_util", + "../../common_audio", + "../../common_audio:common_audio_c", + ] + } + + rtc_executable("ilbc_test") { + testonly = true + + sources = [ "codecs/ilbc/test/iLBC_test.c" ] + + deps = [ ":ilbc" ] + } + + rtc_executable("webrtc_opus_fec_test") { + testonly = true + + sources = [ "codecs/opus/opus_fec_test.cc" ] + + deps = [ + ":webrtc_opus", + "../../common_audio", + "../../rtc_base:rtc_base_approved", + "../../test:fileutils", + "../../test:test_main", + "../../test:test_support", + "//testing/gtest", + ] + } + + rtc_library("audio_coding_unittests") { + testonly = true + visibility += webrtc_default_visibility + + sources = [ + "acm2/acm_receiver_unittest.cc", + "acm2/acm_remixing_unittest.cc", + "acm2/audio_coding_module_unittest.cc", + "acm2/call_statistics_unittest.cc", + "audio_network_adaptor/audio_network_adaptor_impl_unittest.cc", + "audio_network_adaptor/bitrate_controller_unittest.cc", + "audio_network_adaptor/channel_controller_unittest.cc", + "audio_network_adaptor/controller_manager_unittest.cc", + "audio_network_adaptor/dtx_controller_unittest.cc", + "audio_network_adaptor/event_log_writer_unittest.cc", + "audio_network_adaptor/fec_controller_plr_based_unittest.cc", + "audio_network_adaptor/frame_length_controller_unittest.cc", + "audio_network_adaptor/frame_length_controller_v2_unittest.cc", + "audio_network_adaptor/util/threshold_curve_unittest.cc", + "codecs/builtin_audio_decoder_factory_unittest.cc", + "codecs/builtin_audio_encoder_factory_unittest.cc", + "codecs/cng/audio_encoder_cng_unittest.cc", + "codecs/cng/cng_unittest.cc", + "codecs/ilbc/ilbc_unittest.cc", + "codecs/isac/fix/source/filterbanks_unittest.cc", + "codecs/isac/fix/source/filters_unittest.cc", + "codecs/isac/fix/source/lpc_masking_model_unittest.cc", + "codecs/isac/fix/source/transform_unittest.cc", + "codecs/isac/isac_webrtc_api_test.cc", + "codecs/isac/main/source/audio_encoder_isac_unittest.cc", + "codecs/isac/main/source/isac_unittest.cc", + "codecs/legacy_encoded_audio_frame_unittest.cc", + "codecs/opus/audio_decoder_multi_channel_opus_unittest.cc", + "codecs/opus/audio_encoder_multi_channel_opus_unittest.cc", + "codecs/opus/audio_encoder_opus_unittest.cc", + "codecs/opus/opus_bandwidth_unittest.cc", + "codecs/opus/opus_unittest.cc", + "codecs/red/audio_encoder_copy_red_unittest.cc", + "neteq/audio_multi_vector_unittest.cc", + "neteq/audio_vector_unittest.cc", + "neteq/background_noise_unittest.cc", + "neteq/buffer_level_filter_unittest.cc", + "neteq/comfort_noise_unittest.cc", + "neteq/decision_logic_unittest.cc", + "neteq/decoder_database_unittest.cc", + "neteq/delay_manager_unittest.cc", + "neteq/dsp_helper_unittest.cc", + "neteq/dtmf_buffer_unittest.cc", + "neteq/dtmf_tone_generator_unittest.cc", + "neteq/expand_unittest.cc", + "neteq/histogram_unittest.cc", + "neteq/merge_unittest.cc", + "neteq/mock/mock_decoder_database.h", + "neteq/mock/mock_dtmf_buffer.h", + "neteq/mock/mock_dtmf_tone_generator.h", + "neteq/mock/mock_expand.h", + "neteq/mock/mock_histogram.h", + "neteq/mock/mock_neteq_controller.h", + "neteq/mock/mock_packet_buffer.h", + "neteq/mock/mock_red_payload_splitter.h", + "neteq/mock/mock_statistics_calculator.h", + "neteq/nack_tracker_unittest.cc", + "neteq/neteq_decoder_plc_unittest.cc", + "neteq/neteq_impl_unittest.cc", + "neteq/neteq_network_stats_unittest.cc", + "neteq/neteq_stereo_unittest.cc", + "neteq/neteq_unittest.cc", + "neteq/normal_unittest.cc", + "neteq/packet_buffer_unittest.cc", + "neteq/post_decode_vad_unittest.cc", + "neteq/random_vector_unittest.cc", + "neteq/red_payload_splitter_unittest.cc", + "neteq/statistics_calculator_unittest.cc", + "neteq/sync_buffer_unittest.cc", + "neteq/time_stretch_unittest.cc", + "neteq/timestamp_scaler_unittest.cc", + "neteq/tools/input_audio_file_unittest.cc", + "neteq/tools/packet_unittest.cc", + ] + + deps = [ + ":acm_receive_test", + ":acm_send_test", + ":audio_coding", + ":audio_coding_module_typedefs", + ":audio_coding_modules_tests_shared", + ":audio_coding_opus_common", + ":audio_encoder_cng", + ":audio_network_adaptor", + ":default_neteq_factory", + ":g711", + ":ilbc", + ":isac", + ":isac_c", + ":isac_common", + ":isac_fix", + ":legacy_encoded_audio_frame", + ":mocks", + ":neteq", + ":neteq_test_support", + ":neteq_test_tools", + ":pcm16b", + ":red", + ":webrtc_cng", + ":webrtc_opus", + "..:module_api", + "..:module_api_public", + "../../api:array_view", + "../../api/audio:audio_frame_api", + "../../api/audio_codecs:audio_codecs_api", + "../../api/audio_codecs:builtin_audio_decoder_factory", + "../../api/audio_codecs:builtin_audio_encoder_factory", + "../../api/audio_codecs/isac:audio_decoder_isac_fix", + "../../api/audio_codecs/isac:audio_decoder_isac_float", + "../../api/audio_codecs/isac:audio_encoder_isac_fix", + "../../api/audio_codecs/isac:audio_encoder_isac_float", + "../../api/audio_codecs/opus:audio_decoder_multiopus", + "../../api/audio_codecs/opus:audio_decoder_opus", + "../../api/audio_codecs/opus:audio_encoder_multiopus", + "../../api/audio_codecs/opus:audio_encoder_opus", + "../../api/neteq:default_neteq_controller_factory", + "../../api/neteq:neteq_api", + "../../api/neteq:neteq_controller_api", + "../../api/neteq:tick_timer", + "../../api/neteq:tick_timer_unittest", + "../../api/rtc_event_log", + "../../common_audio", + "../../common_audio:common_audio_c", + "../../common_audio:mock_common_audio", + "../../logging:mocks", + "../../logging:rtc_event_audio", + "../../modules/rtp_rtcp:rtp_rtcp_format", + "../../rtc_base", + "../../rtc_base:checks", + "../../rtc_base:ignore_wundef", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_base_tests_utils", + "../../rtc_base:sanitizer", + "../../rtc_base:timeutils", + "../../rtc_base/synchronization:mutex", + "../../rtc_base/system:arch", + "../../system_wrappers", + "../../test:audio_codec_mocks", + "../../test:field_trial", + "../../test:fileutils", + "../../test:rtc_expect_death", + "../../test:rtp_test_utils", + "../../test:test_common", + "../../test:test_support", + "codecs/opus/test", + "codecs/opus/test:test_unittest", + "//testing/gtest", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] + + defines = audio_coding_defines + + if (rtc_enable_protobuf) { + defines += [ "WEBRTC_NETEQ_UNITTEST_BITEXACT" ] + deps += [ + ":ana_config_proto", + ":neteq_unittest_proto", + ] + } } } + +# For backwards compatibility only! Use +# webrtc/api/audio_codecs:audio_codecs_api instead. +# TODO(kwiberg): Remove this. +rtc_source_set("audio_decoder_interface") { + visibility += [ "*" ] + sources = [ "codecs/audio_decoder.h" ] + deps = [ "../../api/audio_codecs:audio_codecs_api" ] +} + +# For backwards compatibility only! Use +# webrtc/api/audio_codecs:audio_codecs_api instead. +# TODO(ossu): Remove this. +rtc_source_set("audio_encoder_interface") { + visibility += [ "*" ] + sources = [ "codecs/audio_encoder.h" ] + deps = [ "../../api/audio_codecs:audio_codecs_api" ] +} diff --git a/webrtc/modules/audio_coding/codecs/audio_decoder.cc b/webrtc/modules/audio_coding/codecs/audio_decoder.cc deleted file mode 100644 index 08d101c..0000000 --- a/webrtc/modules/audio_coding/codecs/audio_decoder.cc +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_coding/codecs/audio_decoder.h" - -#include - -#include "webrtc/base/checks.h" - -namespace webrtc { - -int AudioDecoder::Decode(const uint8_t* encoded, size_t encoded_len, - int sample_rate_hz, size_t max_decoded_bytes, - int16_t* decoded, SpeechType* speech_type) { - int duration = PacketDuration(encoded, encoded_len); - if (duration >= 0 && - duration * Channels() * sizeof(int16_t) > max_decoded_bytes) { - return -1; - } - return DecodeInternal(encoded, encoded_len, sample_rate_hz, decoded, - speech_type); -} - -int AudioDecoder::DecodeRedundant(const uint8_t* encoded, size_t encoded_len, - int sample_rate_hz, size_t max_decoded_bytes, - int16_t* decoded, SpeechType* speech_type) { - int duration = PacketDurationRedundant(encoded, encoded_len); - if (duration >= 0 && - duration * Channels() * sizeof(int16_t) > max_decoded_bytes) { - return -1; - } - return DecodeRedundantInternal(encoded, encoded_len, sample_rate_hz, decoded, - speech_type); -} - -int AudioDecoder::DecodeInternal(const uint8_t* encoded, size_t encoded_len, - int sample_rate_hz, int16_t* decoded, - SpeechType* speech_type) { - return kNotImplemented; -} - -int AudioDecoder::DecodeRedundantInternal(const uint8_t* encoded, - size_t encoded_len, - int sample_rate_hz, int16_t* decoded, - SpeechType* speech_type) { - return DecodeInternal(encoded, encoded_len, sample_rate_hz, decoded, - speech_type); -} - -bool AudioDecoder::HasDecodePlc() const { return false; } - -size_t AudioDecoder::DecodePlc(size_t num_frames, int16_t* decoded) { - return 0; -} - -int AudioDecoder::IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp) { - return 0; -} - -int AudioDecoder::ErrorCode() { return 0; } - -int AudioDecoder::PacketDuration(const uint8_t* encoded, - size_t encoded_len) const { - return kNotImplemented; -} - -int AudioDecoder::PacketDurationRedundant(const uint8_t* encoded, - size_t encoded_len) const { - return kNotImplemented; -} - -bool AudioDecoder::PacketHasFec(const uint8_t* encoded, - size_t encoded_len) const { - return false; -} - -CNG_dec_inst* AudioDecoder::CngDecoderInstance() { - FATAL() << "Not a CNG decoder"; - return NULL; -} - -AudioDecoder::SpeechType AudioDecoder::ConvertSpeechType(int16_t type) { - switch (type) { - case 0: // TODO(hlundin): Both iSAC and Opus return 0 for speech. - case 1: - return kSpeech; - case 2: - return kComfortNoise; - default: - assert(false); - return kSpeech; - } -} - -} // namespace webrtc diff --git a/webrtc/modules/audio_coding/codecs/audio_decoder.h b/webrtc/modules/audio_coding/codecs/audio_decoder.h index 6189be0..b7b15cd 100644 --- a/webrtc/modules/audio_coding/codecs/audio_decoder.h +++ b/webrtc/modules/audio_coding/codecs/audio_decoder.h @@ -8,116 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_INCLUDE_AUDIO_DECODER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_INCLUDE_AUDIO_DECODER_H_ +// This file is for backwards compatibility only! Use +// webrtc/api/audio_codecs/audio_decoder.h instead! +// TODO(kwiberg): Remove it. -#include // NULL +#ifndef MODULES_AUDIO_CODING_CODECS_AUDIO_DECODER_H_ +#define MODULES_AUDIO_CODING_CODECS_AUDIO_DECODER_H_ -#include "webrtc/base/constructormagic.h" -#include "webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h" -#include "webrtc/typedefs.h" +#include "api/audio_codecs/audio_decoder.h" -namespace webrtc { - -// This is the interface class for decoders in NetEQ. Each codec type will have -// and implementation of this class. -class AudioDecoder { - public: - enum SpeechType { - kSpeech = 1, - kComfortNoise = 2 - }; - - // Used by PacketDuration below. Save the value -1 for errors. - enum { kNotImplemented = -2 }; - - AudioDecoder() = default; - virtual ~AudioDecoder() = default; - - // Decodes |encode_len| bytes from |encoded| and writes the result in - // |decoded|. The maximum bytes allowed to be written into |decoded| is - // |max_decoded_bytes|. Returns the total number of samples across all - // channels. If the decoder produced comfort noise, |speech_type| - // is set to kComfortNoise, otherwise it is kSpeech. The desired output - // sample rate is provided in |sample_rate_hz|, which must be valid for the - // codec at hand. - virtual int Decode(const uint8_t* encoded, - size_t encoded_len, - int sample_rate_hz, - size_t max_decoded_bytes, - int16_t* decoded, - SpeechType* speech_type); - - // Same as Decode(), but interfaces to the decoders redundant decode function. - // The default implementation simply calls the regular Decode() method. - virtual int DecodeRedundant(const uint8_t* encoded, - size_t encoded_len, - int sample_rate_hz, - size_t max_decoded_bytes, - int16_t* decoded, - SpeechType* speech_type); - - // Indicates if the decoder implements the DecodePlc method. - virtual bool HasDecodePlc() const; - - // Calls the packet-loss concealment of the decoder to update the state after - // one or several lost packets. The caller has to make sure that the - // memory allocated in |decoded| should accommodate |num_frames| frames. - virtual size_t DecodePlc(size_t num_frames, int16_t* decoded); - - // Resets the decoder state (empty buffers etc.). - virtual void Reset() = 0; - - // Notifies the decoder of an incoming packet to NetEQ. - virtual int IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp); - - // Returns the last error code from the decoder. - virtual int ErrorCode(); - - // Returns the duration in samples-per-channel of the payload in |encoded| - // which is |encoded_len| bytes long. Returns kNotImplemented if no duration - // estimate is available, or -1 in case of an error. - virtual int PacketDuration(const uint8_t* encoded, size_t encoded_len) const; - - // Returns the duration in samples-per-channel of the redandant payload in - // |encoded| which is |encoded_len| bytes long. Returns kNotImplemented if no - // duration estimate is available, or -1 in case of an error. - virtual int PacketDurationRedundant(const uint8_t* encoded, - size_t encoded_len) const; - - // Detects whether a packet has forward error correction. The packet is - // comprised of the samples in |encoded| which is |encoded_len| bytes long. - // Returns true if the packet has FEC and false otherwise. - virtual bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const; - - // If this is a CNG decoder, return the underlying CNG_dec_inst*. If this - // isn't a CNG decoder, don't call this method. - virtual CNG_dec_inst* CngDecoderInstance(); - - virtual size_t Channels() const = 0; - - protected: - static SpeechType ConvertSpeechType(int16_t type); - - virtual int DecodeInternal(const uint8_t* encoded, - size_t encoded_len, - int sample_rate_hz, - int16_t* decoded, - SpeechType* speech_type); - - virtual int DecodeRedundantInternal(const uint8_t* encoded, - size_t encoded_len, - int sample_rate_hz, - int16_t* decoded, - SpeechType* speech_type); - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoder); -}; - -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_INCLUDE_AUDIO_DECODER_H_ +#endif // MODULES_AUDIO_CODING_CODECS_AUDIO_DECODER_H_ diff --git a/webrtc/modules/audio_coding/codecs/audio_encoder.cc b/webrtc/modules/audio_coding/codecs/audio_encoder.cc deleted file mode 100644 index 6d76300..0000000 --- a/webrtc/modules/audio_coding/codecs/audio_encoder.cc +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2014 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 "webrtc/modules/audio_coding/codecs/audio_encoder.h" -#include "webrtc/base/checks.h" - -namespace webrtc { - -AudioEncoder::EncodedInfo::EncodedInfo() = default; - -AudioEncoder::EncodedInfo::~EncodedInfo() = default; - -int AudioEncoder::RtpTimestampRateHz() const { - return SampleRateHz(); -} - -AudioEncoder::EncodedInfo AudioEncoder::Encode(uint32_t rtp_timestamp, - const int16_t* audio, - size_t num_samples_per_channel, - size_t max_encoded_bytes, - uint8_t* encoded) { - RTC_CHECK_EQ(num_samples_per_channel, - static_cast(SampleRateHz() / 100)); - EncodedInfo info = - EncodeInternal(rtp_timestamp, audio, max_encoded_bytes, encoded); - RTC_CHECK_LE(info.encoded_bytes, max_encoded_bytes); - return info; -} - -bool AudioEncoder::SetFec(bool enable) { - return !enable; -} - -bool AudioEncoder::SetDtx(bool enable) { - return !enable; -} - -bool AudioEncoder::SetApplication(Application application) { - return false; -} - -void AudioEncoder::SetMaxPlaybackRate(int frequency_hz) {} - -void AudioEncoder::SetProjectedPacketLossRate(double fraction) {} - -void AudioEncoder::SetTargetBitrate(int target_bps) {} - -} // namespace webrtc diff --git a/webrtc/modules/audio_coding/codecs/audio_encoder.h b/webrtc/modules/audio_coding/codecs/audio_encoder.h index cda9d86..010ae67 100644 --- a/webrtc/modules/audio_coding/codecs/audio_encoder.h +++ b/webrtc/modules/audio_coding/codecs/audio_encoder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * Copyright (c) 2012 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 @@ -8,136 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_AUDIO_ENCODER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_AUDIO_ENCODER_H_ +// This file is for backwards compatibility only! Use +// webrtc/api/audio_codecs/audio_encoder.h instead! +// TODO(ossu): Remove it. -#include -#include +#ifndef MODULES_AUDIO_CODING_CODECS_AUDIO_ENCODER_H_ +#define MODULES_AUDIO_CODING_CODECS_AUDIO_ENCODER_H_ -#include "webrtc/typedefs.h" +#include "api/audio_codecs/audio_encoder.h" -namespace webrtc { - -// This is the interface class for encoders in AudioCoding module. Each codec -// type must have an implementation of this class. -class AudioEncoder { - public: - struct EncodedInfoLeaf { - size_t encoded_bytes = 0; - uint32_t encoded_timestamp = 0; - int payload_type = 0; - bool send_even_if_empty = false; - bool speech = true; - }; - - // This is the main struct for auxiliary encoding information. Each encoded - // packet should be accompanied by one EncodedInfo struct, containing the - // total number of |encoded_bytes|, the |encoded_timestamp| and the - // |payload_type|. If the packet contains redundant encodings, the |redundant| - // vector will be populated with EncodedInfoLeaf structs. Each struct in the - // vector represents one encoding; the order of structs in the vector is the - // same as the order in which the actual payloads are written to the byte - // stream. When EncoderInfoLeaf structs are present in the vector, the main - // struct's |encoded_bytes| will be the sum of all the |encoded_bytes| in the - // vector. - struct EncodedInfo : public EncodedInfoLeaf { - EncodedInfo(); - ~EncodedInfo(); - - std::vector redundant; - }; - - virtual ~AudioEncoder() = default; - - // Returns the maximum number of bytes that can be produced by the encoder - // at each Encode() call. The caller can use the return value to determine - // the size of the buffer that needs to be allocated. This value is allowed - // to depend on encoder parameters like bitrate, frame size etc., so if - // any of these change, the caller of Encode() is responsible for checking - // that the buffer is large enough by calling MaxEncodedBytes() again. - virtual size_t MaxEncodedBytes() const = 0; - - // Returns the input sample rate in Hz and the number of input channels. - // These are constants set at instantiation time. - virtual int SampleRateHz() const = 0; - virtual int NumChannels() const = 0; - - // Returns the rate at which the RTP timestamps are updated. The default - // implementation returns SampleRateHz(). - virtual int RtpTimestampRateHz() const; - - // Returns the number of 10 ms frames the encoder will put in the next - // packet. This value may only change when Encode() outputs a packet; i.e., - // the encoder may vary the number of 10 ms frames from packet to packet, but - // it must decide the length of the next packet no later than when outputting - // the preceding packet. - virtual size_t Num10MsFramesInNextPacket() const = 0; - - // Returns the maximum value that can be returned by - // Num10MsFramesInNextPacket(). - virtual size_t Max10MsFramesInAPacket() const = 0; - - // Returns the current target bitrate in bits/s. The value -1 means that the - // codec adapts the target automatically, and a current target cannot be - // provided. - virtual int GetTargetBitrate() const = 0; - - // Accepts one 10 ms block of input audio (i.e., SampleRateHz() / 100 * - // NumChannels() samples). Multi-channel audio must be sample-interleaved. - // The encoder produces zero or more bytes of output in |encoded| and - // returns additional encoding information. - // The caller is responsible for making sure that |max_encoded_bytes| is - // not smaller than the number of bytes actually produced by the encoder. - // Encode() checks some preconditions, calls EncodeInternal() which does the - // actual work, and then checks some postconditions. - EncodedInfo Encode(uint32_t rtp_timestamp, - const int16_t* audio, - size_t num_samples_per_channel, - size_t max_encoded_bytes, - uint8_t* encoded); - - virtual EncodedInfo EncodeInternal(uint32_t rtp_timestamp, - const int16_t* audio, - size_t max_encoded_bytes, - uint8_t* encoded) = 0; - - // Resets the encoder to its starting state, discarding any input that has - // been fed to the encoder but not yet emitted in a packet. - virtual void Reset() = 0; - - // Enables or disables codec-internal FEC (forward error correction). Returns - // true if the codec was able to comply. The default implementation returns - // true when asked to disable FEC and false when asked to enable it (meaning - // that FEC isn't supported). - virtual bool SetFec(bool enable); - - // Enables or disables codec-internal VAD/DTX. Returns true if the codec was - // able to comply. The default implementation returns true when asked to - // disable DTX and false when asked to enable it (meaning that DTX isn't - // supported). - virtual bool SetDtx(bool enable); - - // Sets the application mode. Returns true if the codec was able to comply. - // The default implementation just returns false. - enum class Application { kSpeech, kAudio }; - virtual bool SetApplication(Application application); - - // Tells the encoder about the highest sample rate the decoder is expected to - // use when decoding the bitstream. The encoder would typically use this - // information to adjust the quality of the encoding. The default - // implementation just returns true. - virtual void SetMaxPlaybackRate(int frequency_hz); - - // Tells the encoder what the projected packet loss rate is. The rate is in - // the range [0.0, 1.0]. The encoder would typically use this information to - // adjust channel coding efforts, such as FEC. The default implementation - // does nothing. - virtual void SetProjectedPacketLossRate(double fraction); - - // Tells the encoder what average bitrate we'd like it to produce. The - // encoder is free to adjust or disregard the given bitrate (the default - // implementation does the latter). - virtual void SetTargetBitrate(int target_bps); -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_AUDIO_ENCODER_H_ +#endif // MODULES_AUDIO_CODING_CODECS_AUDIO_ENCODER_H_ diff --git a/webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h b/webrtc/modules/audio_coding/codecs/cng/webrtc_cng.h similarity index 100% rename from webrtc/modules/audio_coding/codecs/cng/include/webrtc_cng.h rename to webrtc/modules/audio_coding/codecs/cng/webrtc_cng.h diff --git a/webrtc/modules/audio_coding/codecs/isac/audio_decoder_isac_t.h b/webrtc/modules/audio_coding/codecs/isac/audio_decoder_isac_t.h index 845af42..23a3020 100644 --- a/webrtc/modules/audio_coding/codecs/isac/audio_decoder_isac_t.h +++ b/webrtc/modules/audio_coding/codecs/isac/audio_decoder_isac_t.h @@ -8,32 +8,33 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_H_ #include -#include "webrtc/modules/audio_coding/codecs/audio_decoder.h" -#include "webrtc/modules/audio_coding/codecs/isac/locked_bandwidth_info.h" +#include "absl/types/optional.h" +#include "api/audio_codecs/audio_decoder.h" +#include "api/scoped_refptr.h" +#include "rtc_base/constructor_magic.h" namespace webrtc { template class AudioDecoderIsacT final : public AudioDecoder { public: - AudioDecoderIsacT(); - explicit AudioDecoderIsacT(LockedIsacBandwidthInfo* bwinfo); - ~AudioDecoderIsacT() override; + struct Config { + bool IsOk() const; + int sample_rate_hz = 16000; + }; + explicit AudioDecoderIsacT(const Config& config); + virtual ~AudioDecoderIsacT() override; bool HasDecodePlc() const override; size_t DecodePlc(size_t num_frames, int16_t* decoded) override; void Reset() override; - int IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp) override; int ErrorCode() override; + int SampleRateHz() const override; size_t Channels() const override; int DecodeInternal(const uint8_t* encoded, size_t encoded_len, @@ -43,12 +44,11 @@ class AudioDecoderIsacT final : public AudioDecoder { private: typename T::instance_type* isac_state_; - LockedIsacBandwidthInfo* bwinfo_; - int decoder_sample_rate_hz_; + int sample_rate_hz_; RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderIsacT); }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/audio_decoder_isac_t_impl.h b/webrtc/modules/audio_coding/codecs/isac/audio_decoder_isac_t_impl.h index a986bc4..2e43fd3 100644 --- a/webrtc/modules/audio_coding/codecs/isac/audio_decoder_isac_t_impl.h +++ b/webrtc/modules/audio_coding/codecs/isac/audio_decoder_isac_t_impl.h @@ -8,29 +8,26 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_IMPL_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_IMPL_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_IMPL_H_ -#include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h" - -#include "webrtc/base/checks.h" +#include "rtc_base/checks.h" namespace webrtc { template -AudioDecoderIsacT::AudioDecoderIsacT() - : AudioDecoderIsacT(nullptr) {} +bool AudioDecoderIsacT::Config::IsOk() const { + return (sample_rate_hz == 16000 || sample_rate_hz == 32000); +} template -AudioDecoderIsacT::AudioDecoderIsacT(LockedIsacBandwidthInfo* bwinfo) - : bwinfo_(bwinfo), decoder_sample_rate_hz_(-1) { +AudioDecoderIsacT::AudioDecoderIsacT(const Config& config) + : sample_rate_hz_(config.sample_rate_hz) { + RTC_CHECK(config.IsOk()) << "Unsupported sample rate " + << config.sample_rate_hz; RTC_CHECK_EQ(0, T::Create(&isac_state_)); T::DecoderInit(isac_state_); - if (bwinfo_) { - IsacBandwidthInfo bi; - T::GetBandwidthInfo(isac_state_, &bi); - bwinfo_->Set(bi); - } + RTC_CHECK_EQ(0, T::SetDecSampRate(isac_state_, sample_rate_hz_)); } template @@ -44,12 +41,7 @@ int AudioDecoderIsacT::DecodeInternal(const uint8_t* encoded, int sample_rate_hz, int16_t* decoded, SpeechType* speech_type) { - RTC_CHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000) - << "Unsupported sample rate " << sample_rate_hz; - if (sample_rate_hz != decoder_sample_rate_hz_) { - RTC_CHECK_EQ(0, T::SetDecSampRate(isac_state_, sample_rate_hz)); - decoder_sample_rate_hz_ = sample_rate_hz; - } + RTC_CHECK_EQ(sample_rate_hz_, sample_rate_hz); int16_t temp_type = 1; // Default is speech. int ret = T::DecodeInternal(isac_state_, encoded, encoded_len, decoded, &temp_type); @@ -73,25 +65,13 @@ void AudioDecoderIsacT::Reset() { } template -int AudioDecoderIsacT::IncomingPacket(const uint8_t* payload, - size_t payload_len, - uint16_t rtp_sequence_number, - uint32_t rtp_timestamp, - uint32_t arrival_timestamp) { - int ret = T::UpdateBwEstimate(isac_state_, payload, payload_len, - rtp_sequence_number, rtp_timestamp, - arrival_timestamp); - if (bwinfo_) { - IsacBandwidthInfo bwinfo; - T::GetBandwidthInfo(isac_state_, &bwinfo); - bwinfo_->Set(bwinfo); - } - return ret; +int AudioDecoderIsacT::ErrorCode() { + return T::GetErrorCode(isac_state_); } template -int AudioDecoderIsacT::ErrorCode() { - return T::GetErrorCode(isac_state_); +int AudioDecoderIsacT::SampleRateHz() const { + return sample_rate_hz_; } template @@ -101,4 +81,4 @@ size_t AudioDecoderIsacT::Channels() const { } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_IMPL_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_DECODER_ISAC_T_IMPL_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h index b15ad94..d99e9c8 100644 --- a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h +++ b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h @@ -8,18 +8,21 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_H_ +#include #include -#include "webrtc/modules/audio_coding/codecs/audio_encoder.h" -#include "webrtc/modules/audio_coding/codecs/isac/locked_bandwidth_info.h" +#include "absl/types/optional.h" +#include "api/audio_codecs/audio_encoder.h" +#include "api/scoped_refptr.h" +#include "api/units/time_delta.h" +#include "rtc_base/constructor_magic.h" +#include "system_wrappers/include/field_trial.h" namespace webrtc { -struct CodecInst; - template class AudioEncoderIsacT final : public AudioEncoder { public: @@ -29,9 +32,6 @@ class AudioEncoderIsacT final : public AudioEncoder { // - 32000 Hz, 30 ms, 10000-56000 bps (if T has super-wideband support) struct Config { bool IsOk() const; - - LockedIsacBandwidthInfo* bwinfo = nullptr; - int payload_type = 103; int sample_rate_hz = 16000; int frame_size_ms = 30; @@ -39,46 +39,48 @@ class AudioEncoderIsacT final : public AudioEncoder { // rate, in bits/s. int max_payload_size_bytes = -1; int max_bit_rate = -1; - - // If true, the encoder will dynamically adjust frame size and bit rate; - // the configured values are then merely the starting point. - bool adaptive_mode = false; - - // In adaptive mode, prevent adaptive changes to the frame size. (Not used - // in nonadaptive mode.) - bool enforce_frame_size = false; }; explicit AudioEncoderIsacT(const Config& config); - explicit AudioEncoderIsacT(const CodecInst& codec_inst, - LockedIsacBandwidthInfo* bwinfo); ~AudioEncoderIsacT() override; - size_t MaxEncodedBytes() const override; int SampleRateHz() const override; - int NumChannels() const override; + size_t NumChannels() const override; size_t Num10MsFramesInNextPacket() const override; size_t Max10MsFramesInAPacket() const override; int GetTargetBitrate() const override; - EncodedInfo EncodeInternal(uint32_t rtp_timestamp, - const int16_t* audio, - size_t max_encoded_bytes, - uint8_t* encoded) override; + void SetTargetBitrate(int target_bps) override; + void OnReceivedTargetAudioBitrate(int target_bps) override; + void OnReceivedUplinkBandwidth( + int target_audio_bitrate_bps, + absl::optional bwe_period_ms) override; + void OnReceivedUplinkAllocation(BitrateAllocationUpdate update) override; + void OnReceivedOverhead(size_t overhead_bytes_per_packet) override; + EncodedInfo EncodeImpl(uint32_t rtp_timestamp, + rtc::ArrayView audio, + rtc::Buffer* encoded) override; void Reset() override; + absl::optional> GetFrameLengthRange() + const override; private: // This value is taken from STREAM_SIZE_MAX_60 for iSAC float (60 ms) and // STREAM_MAXW16_60MS for iSAC fix (60 ms). static const size_t kSufficientEncodeBufferSizeBytes = 400; - static const int kDefaultBitRate = 32000; + static constexpr int kDefaultBitRate = 32000; + static constexpr int kMinBitrateBps = 10000; + static constexpr int MaxBitrateBps(int sample_rate_hz) { + return sample_rate_hz == 32000 ? 56000 : 32000; + } + + void SetTargetBitrate(int target_bps, bool subtract_per_packet_overhead); // Recreate the iSAC encoder instance with the given settings, and save them. void RecreateEncoderInstance(const Config& config); Config config_; typename T::instance_type* isac_state_ = nullptr; - LockedIsacBandwidthInfo* bwinfo_ = nullptr; // Have we accepted input but not yet emitted it in a packet? bool packet_in_progress_ = false; @@ -89,9 +91,18 @@ class AudioEncoderIsacT final : public AudioEncoder { // Timestamp of the previously encoded packet. uint32_t last_encoded_timestamp_; + // Cache the value of the "WebRTC-SendSideBwe-WithOverhead" field trial. + const bool send_side_bwe_with_overhead_ = + field_trial::IsEnabled("WebRTC-SendSideBwe-WithOverhead"); + + // When we send a packet, expect this many bytes of headers to be added to it. + // Start out with a reasonable default that we can use until we receive a real + // value. + DataSize overhead_per_packet_ = DataSize::Bytes(28); + RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderIsacT); }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h index 279f80d..0bde3f7 100644 --- a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h +++ b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h @@ -8,39 +8,21 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_IMPL_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_IMPL_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_IMPL_H_ -#include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h" - -#include "webrtc/base/checks.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" namespace webrtc { -template -typename AudioEncoderIsacT::Config CreateIsacConfig( - const CodecInst& codec_inst, - LockedIsacBandwidthInfo* bwinfo) { - typename AudioEncoderIsacT::Config config; - config.bwinfo = bwinfo; - config.payload_type = codec_inst.pltype; - config.sample_rate_hz = codec_inst.plfreq; - config.frame_size_ms = - rtc::CheckedDivExact(1000 * codec_inst.pacsize, config.sample_rate_hz); - config.adaptive_mode = (codec_inst.rate == -1); - if (codec_inst.rate != -1) - config.bit_rate = codec_inst.rate; - return config; -} - template bool AudioEncoderIsacT::Config::IsOk() const { if (max_bit_rate < 32000 && max_bit_rate != -1) return false; if (max_payload_size_bytes < 120 && max_payload_size_bytes != -1) return false; - if (adaptive_mode && !bwinfo) - return false; + switch (sample_rate_hz) { case 16000: if (max_bit_rate > 53400) @@ -67,37 +49,26 @@ AudioEncoderIsacT::AudioEncoderIsacT(const Config& config) { RecreateEncoderInstance(config); } -template -AudioEncoderIsacT::AudioEncoderIsacT(const CodecInst& codec_inst, - LockedIsacBandwidthInfo* bwinfo) - : AudioEncoderIsacT(CreateIsacConfig(codec_inst, bwinfo)) {} - template AudioEncoderIsacT::~AudioEncoderIsacT() { RTC_CHECK_EQ(0, T::Free(isac_state_)); } -template -size_t AudioEncoderIsacT::MaxEncodedBytes() const { - return kSufficientEncodeBufferSizeBytes; -} - template int AudioEncoderIsacT::SampleRateHz() const { return T::EncSampRate(isac_state_); } template -int AudioEncoderIsacT::NumChannels() const { +size_t AudioEncoderIsacT::NumChannels() const { return 1; } template size_t AudioEncoderIsacT::Num10MsFramesInNextPacket() const { const int samples_in_next_packet = T::GetNewFrameLen(isac_state_); - return static_cast( - rtc::CheckedDivExact(samples_in_next_packet, - rtc::CheckedDivExact(SampleRateHz(), 100))); + return static_cast(rtc::CheckedDivExact( + samples_in_next_packet, rtc::CheckedDivExact(SampleRateHz(), 100))); } template @@ -107,44 +78,85 @@ size_t AudioEncoderIsacT::Max10MsFramesInAPacket() const { template int AudioEncoderIsacT::GetTargetBitrate() const { - if (config_.adaptive_mode) - return -1; return config_.bit_rate == 0 ? kDefaultBitRate : config_.bit_rate; } template -AudioEncoder::EncodedInfo AudioEncoderIsacT::EncodeInternal( +void AudioEncoderIsacT::SetTargetBitrate(int target_bps) { + // Set target bitrate directly without subtracting per-packet overhead, + // because that's what AudioEncoderOpus does. + SetTargetBitrate(target_bps, + /*subtract_per_packet_overhead=*/false); +} + +template +void AudioEncoderIsacT::OnReceivedTargetAudioBitrate(int target_bps) { + // Set target bitrate directly without subtracting per-packet overhead, + // because that's what AudioEncoderOpus does. + SetTargetBitrate(target_bps, + /*subtract_per_packet_overhead=*/false); +} + +template +void AudioEncoderIsacT::OnReceivedUplinkBandwidth( + int target_audio_bitrate_bps, + absl::optional /*bwe_period_ms*/) { + // Set target bitrate, subtracting the per-packet overhead if + // WebRTC-SendSideBwe-WithOverhead is enabled, because that's what + // AudioEncoderOpus does. + SetTargetBitrate( + target_audio_bitrate_bps, + /*subtract_per_packet_overhead=*/send_side_bwe_with_overhead_); +} + +template +void AudioEncoderIsacT::OnReceivedUplinkAllocation( + BitrateAllocationUpdate update) { + // Set target bitrate, subtracting the per-packet overhead if + // WebRTC-SendSideBwe-WithOverhead is enabled, because that's what + // AudioEncoderOpus does. + SetTargetBitrate( + update.target_bitrate.bps(), + /*subtract_per_packet_overhead=*/send_side_bwe_with_overhead_); +} + +template +void AudioEncoderIsacT::OnReceivedOverhead( + size_t overhead_bytes_per_packet) { + overhead_per_packet_ = DataSize::Bytes(overhead_bytes_per_packet); +} + +template +AudioEncoder::EncodedInfo AudioEncoderIsacT::EncodeImpl( uint32_t rtp_timestamp, - const int16_t* audio, - size_t max_encoded_bytes, - uint8_t* encoded) { + rtc::ArrayView audio, + rtc::Buffer* encoded) { if (!packet_in_progress_) { // Starting a new packet; remember the timestamp for later. packet_in_progress_ = true; packet_timestamp_ = rtp_timestamp; } - if (bwinfo_) { - IsacBandwidthInfo bwinfo = bwinfo_->Get(); - T::SetBandwidthInfo(isac_state_, &bwinfo); - } - int r = T::Encode(isac_state_, audio, encoded); - RTC_CHECK_GE(r, 0) << "Encode failed (error code " - << T::GetErrorCode(isac_state_) << ")"; + size_t encoded_bytes = encoded->AppendData( + kSufficientEncodeBufferSizeBytes, [&](rtc::ArrayView encoded) { + int r = T::Encode(isac_state_, audio.data(), encoded.data()); - // T::Encode doesn't allow us to tell it the size of the output - // buffer. All we can do is check for an overrun after the fact. - RTC_CHECK_LE(static_cast(r), max_encoded_bytes); + RTC_CHECK_GE(r, 0) << "Encode failed (error code " + << T::GetErrorCode(isac_state_) << ")"; - if (r == 0) + return static_cast(r); + }); + + if (encoded_bytes == 0) return EncodedInfo(); // Got enough input to produce a packet. Return the saved timestamp from // the first chunk of input that went into the packet. packet_in_progress_ = false; EncodedInfo info; - info.encoded_bytes = r; + info.encoded_bytes = encoded_bytes; info.encoded_timestamp = packet_timestamp_; info.payload_type = config_.payload_type; + info.encoder_type = CodecType::kIsac; return info; } @@ -153,23 +165,40 @@ void AudioEncoderIsacT::Reset() { RecreateEncoderInstance(config_); } +template +absl::optional> +AudioEncoderIsacT::GetFrameLengthRange() const { + return {{TimeDelta::Millis(config_.frame_size_ms), + TimeDelta::Millis(config_.frame_size_ms)}}; +} + +template +void AudioEncoderIsacT::SetTargetBitrate(int target_bps, + bool subtract_per_packet_overhead) { + if (subtract_per_packet_overhead) { + const DataRate overhead_rate = + overhead_per_packet_ / TimeDelta::Millis(config_.frame_size_ms); + target_bps -= overhead_rate.bps(); + } + target_bps = rtc::SafeClamp(target_bps, kMinBitrateBps, + MaxBitrateBps(config_.sample_rate_hz)); + int result = T::Control(isac_state_, target_bps, config_.frame_size_ms); + RTC_DCHECK_EQ(result, 0); + config_.bit_rate = target_bps; +} + template void AudioEncoderIsacT::RecreateEncoderInstance(const Config& config) { RTC_CHECK(config.IsOk()); packet_in_progress_ = false; - bwinfo_ = config.bwinfo; if (isac_state_) RTC_CHECK_EQ(0, T::Free(isac_state_)); RTC_CHECK_EQ(0, T::Create(&isac_state_)); - RTC_CHECK_EQ(0, T::EncoderInit(isac_state_, config.adaptive_mode ? 0 : 1)); + RTC_CHECK_EQ(0, T::EncoderInit(isac_state_, /*coding_mode=*/1)); RTC_CHECK_EQ(0, T::SetEncSampRate(isac_state_, config.sample_rate_hz)); const int bit_rate = config.bit_rate == 0 ? kDefaultBitRate : config.bit_rate; - if (config.adaptive_mode) { - RTC_CHECK_EQ(0, T::ControlBwe(isac_state_, bit_rate, config.frame_size_ms, - config.enforce_frame_size)); - } else { - RTC_CHECK_EQ(0, T::Control(isac_state_, bit_rate, config.frame_size_ms)); - } + RTC_CHECK_EQ(0, T::Control(isac_state_, bit_rate, config.frame_size_ms)); + if (config.max_payload_size_bytes != -1) RTC_CHECK_EQ( 0, T::SetMaxPayloadSize(isac_state_, config.max_payload_size_bytes)); @@ -187,4 +216,4 @@ void AudioEncoderIsacT::RecreateEncoderInstance(const Config& config) { } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_IMPL_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_AUDIO_ENCODER_ISAC_T_IMPL_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/bandwidth_info.h b/webrtc/modules/audio_coding/codecs/isac/bandwidth_info.h index 1e3f4c9..c3830a5 100644 --- a/webrtc/modules/audio_coding/codecs/isac/bandwidth_info.h +++ b/webrtc/modules/audio_coding/codecs/isac/bandwidth_info.h @@ -8,10 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_BANDWIDTH_INFO_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_BANDWIDTH_INFO_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_BANDWIDTH_INFO_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_BANDWIDTH_INFO_H_ -#include "webrtc/typedefs.h" +#include typedef struct { int in_use; @@ -21,4 +21,4 @@ typedef struct { int16_t jitter_info; } IsacBandwidthInfo; -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_BANDWIDTH_INFO_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_BANDWIDTH_INFO_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h b/webrtc/modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h index dcd4852..fae2f3d 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h @@ -8,15 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_DECODER_ISAC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_DECODER_ISAC_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_DECODER_ISAC_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_DECODER_ISAC_H_ -#include "webrtc/modules/audio_coding/codecs/isac/audio_decoder_isac_t.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/isac_float_type.h" +#include "modules/audio_coding/codecs/isac/audio_decoder_isac_t.h" +#include "modules/audio_coding/codecs/isac/main/source/isac_float_type.h" namespace webrtc { -using AudioDecoderIsac = AudioDecoderIsacT; +using AudioDecoderIsacFloatImpl = AudioDecoderIsacT; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_ENCODER_ISAC_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_ENCODER_ISAC_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h b/webrtc/modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h index cc8665d..dc32bcd 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h @@ -8,15 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_ENCODER_ISAC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_ENCODER_ISAC_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_ENCODER_ISAC_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_ENCODER_ISAC_H_ -#include "webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/isac_float_type.h" +#include "modules/audio_coding/codecs/isac/audio_encoder_isac_t.h" +#include "modules/audio_coding/codecs/isac/main/source/isac_float_type.h" namespace webrtc { -using AudioEncoderIsac = AudioEncoderIsacT; +using AudioEncoderIsacFloatImpl = AudioEncoderIsacT; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_ENCODER_ISAC_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_AUDIO_ENCODER_ISAC_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/include/isac.h b/webrtc/modules/audio_coding/codecs/isac/main/include/isac.h index 327e7f4..3d2caef 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/include/isac.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/include/isac.h @@ -8,717 +8,610 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_ISAC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_ISAC_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_ISAC_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_ISAC_H_ #include -#include "webrtc/modules/audio_coding/codecs/isac/bandwidth_info.h" -#include "webrtc/typedefs.h" +#include "modules/audio_coding/codecs/isac/bandwidth_info.h" -typedef struct WebRtcISACStruct ISACStruct; +typedef struct WebRtcISACStruct ISACStruct; #if defined(__cplusplus) extern "C" { #endif - /****************************************************************************** - * WebRtcIsac_AssignSize(...) - * - * This function returns the size of the ISAC instance, so that the instance - * can be created outside iSAC. - * - * Input: - * - samplingRate : sampling rate of the input/output audio. - * - * Output: - * - sizeinbytes : number of bytes needed to allocate for the - * instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - - int16_t WebRtcIsac_AssignSize( - int* sizeinbytes); - - - /****************************************************************************** - * WebRtcIsac_Assign(...) - * - * This function assignes the memory already created to the ISAC instance. - * - * Input: - * - *ISAC_main_inst : a pointer to the coder instance. - * - samplingRate : sampling rate of the input/output audio. - * - ISAC_inst_Addr : the already allocated memory, where we put the - * iSAC structure. - * - * Return value : 0 - Ok - * -1 - Error - */ - - int16_t WebRtcIsac_Assign( - ISACStruct** ISAC_main_inst, - void* ISAC_inst_Addr); - - - /****************************************************************************** - * WebRtcIsac_Create(...) - * - * This function creates an ISAC instance, which will contain the state - * information for one coding/decoding channel. - * - * Input: - * - *ISAC_main_inst : a pointer to the coder instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - - int16_t WebRtcIsac_Create( - ISACStruct** ISAC_main_inst); - - - /****************************************************************************** - * WebRtcIsac_Free(...) - * - * This function frees the ISAC instance created at the beginning. - * - * Input: - * - ISAC_main_inst : an ISAC instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - - int16_t WebRtcIsac_Free( - ISACStruct* ISAC_main_inst); - - - /****************************************************************************** - * WebRtcIsac_EncoderInit(...) - * - * This function initializes an ISAC instance prior to the encoder calls. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - CodingMode : 0 -> Bit rate and frame length are - * automatically adjusted to available bandwidth - * on transmission channel, just valid if codec - * is created to work in wideband mode. - * 1 -> User sets a frame length and a target bit - * rate which is taken as the maximum - * short-term average bit rate. - * - * Return value : 0 - Ok - * -1 - Error - */ - - int16_t WebRtcIsac_EncoderInit( - ISACStruct* ISAC_main_inst, - int16_t CodingMode); - - - /****************************************************************************** - * WebRtcIsac_Encode(...) - * - * This function encodes 10ms audio blocks and inserts it into a package. - * Input speech length has 160 samples if operating at 16 kHz sampling - * rate, or 320 if operating at 32 kHz sampling rate. The encoder buffers the - * input audio until the whole frame is buffered then proceeds with encoding. - * - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - speechIn : input speech vector. - * - * Output: - * - encoded : the encoded data vector - * - * Return value: - * : >0 - Length (in bytes) of coded data - * : 0 - The buffer didn't reach the chosen - * frame-size so it keeps buffering speech - * samples. - * : -1 - Error - */ - - int WebRtcIsac_Encode( - ISACStruct* ISAC_main_inst, - const int16_t* speechIn, - uint8_t* encoded); - - - /****************************************************************************** - * WebRtcIsac_DecoderInit(...) - * - * This function initializes an ISAC instance prior to the decoder calls. - * - * Input: - * - ISAC_main_inst : ISAC instance. - */ - - void WebRtcIsac_DecoderInit(ISACStruct* ISAC_main_inst); - - /****************************************************************************** - * WebRtcIsac_UpdateBwEstimate(...) - * - * This function updates the estimate of the bandwidth. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s). - * - packet_size : size of the packet. - * - rtp_seq_number : the RTP number of the packet. - * - send_ts : the RTP send timestamp, given in samples - * - arr_ts : the arrival time of the packet (from NetEq) - * in samples. - * - * Return value : 0 - Ok - * -1 - Error - */ - - int16_t WebRtcIsac_UpdateBwEstimate( - ISACStruct* ISAC_main_inst, - const uint8_t* encoded, - size_t packet_size, - uint16_t rtp_seq_number, - uint32_t send_ts, - uint32_t arr_ts); - - - /****************************************************************************** - * WebRtcIsac_Decode(...) - * - * This function decodes an ISAC frame. At 16 kHz sampling rate, the length - * of the output audio could be either 480 or 960 samples, equivalent to - * 30 or 60 ms respectively. At 32 kHz sampling rate, the length of the - * output audio is 960 samples, which is 30 ms. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s). - * - len : bytes in encoded vector. - * - * Output: - * - decoded : The decoded vector. - * - * Return value : >0 - number of samples in decoded vector. - * -1 - Error. - */ - - int WebRtcIsac_Decode( - ISACStruct* ISAC_main_inst, - const uint8_t* encoded, - size_t len, - int16_t* decoded, - int16_t* speechType); - - - /****************************************************************************** - * WebRtcIsac_DecodePlc(...) - * - * This function conducts PLC for ISAC frame(s). Output speech length - * will be a multiple of frames, i.e. multiples of 30 ms audio. Therefore, - * the output is multiple of 480 samples if operating at 16 kHz and multiple - * of 960 if operating at 32 kHz. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - noOfLostFrames : Number of PLC frames to produce. - * - * Output: - * - decoded : The decoded vector. - * - * Return value : Number of samples in decoded PLC vector - */ - - size_t WebRtcIsac_DecodePlc( - ISACStruct* ISAC_main_inst, - int16_t* decoded, - size_t noOfLostFrames); - - - /****************************************************************************** - * WebRtcIsac_Control(...) - * - * This function sets the limit on the short-term average bit-rate and the - * frame length. Should be used only in Instantaneous mode. At 16 kHz sampling - * rate, an average bit-rate between 10000 to 32000 bps is valid and a - * frame-size of 30 or 60 ms is acceptable. At 32 kHz, an average bit-rate - * between 10000 to 56000 is acceptable, and the valid frame-size is 30 ms. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - rate : limit on the short-term average bit rate, - * in bits/second. - * - framesize : frame-size in millisecond. - * - * Return value : 0 - ok - * -1 - Error - */ - - int16_t WebRtcIsac_Control( - ISACStruct* ISAC_main_inst, - int32_t rate, - int framesize); - - void WebRtcIsac_SetInitialBweBottleneck(ISACStruct* ISAC_main_inst, - int bottleneck_bits_per_second); - - /****************************************************************************** - * WebRtcIsac_ControlBwe(...) - * - * This function sets the initial values of bottleneck and frame-size if - * iSAC is used in channel-adaptive mode. Therefore, this API is not - * applicable if the codec is created to operate in super-wideband mode. - * - * Through this API, users can enforce a frame-size for all values of - * bottleneck. Then iSAC will not automatically change the frame-size. - * - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - rateBPS : initial value of bottleneck in bits/second - * 10000 <= rateBPS <= 56000 is accepted - * For default bottleneck set rateBPS = 0 - * - frameSizeMs : number of milliseconds per frame (30 or 60) - * - enforceFrameSize : 1 to enforce the given frame-size through - * out the adaptation process, 0 to let iSAC - * change the frame-size if required. - * - * Return value : 0 - ok - * -1 - Error - */ - - int16_t WebRtcIsac_ControlBwe( - ISACStruct* ISAC_main_inst, - int32_t rateBPS, - int frameSizeMs, - int16_t enforceFrameSize); - - - /****************************************************************************** - * WebRtcIsac_ReadFrameLen(...) - * - * This function returns the length of the frame represented in the packet. - * - * Input: - * - encoded : Encoded bit-stream - * - * Output: - * - frameLength : Length of frame in packet (in samples) - * - */ - - int16_t WebRtcIsac_ReadFrameLen( - ISACStruct* ISAC_main_inst, - const uint8_t* encoded, - int16_t* frameLength); - - - /****************************************************************************** - * WebRtcIsac_version(...) - * - * This function returns the version number. - * - * Output: - * - version : Pointer to character string - * - */ - - void WebRtcIsac_version( - char *version); - - - /****************************************************************************** - * WebRtcIsac_GetErrorCode(...) - * - * This function can be used to check the error code of an iSAC instance. When - * a function returns -1 a error code will be set for that instance. The - * function below extract the code of the last error that occurred in the - * specified instance. - * - * Input: - * - ISAC_main_inst : ISAC instance - * - * Return value : Error code - */ - - int16_t WebRtcIsac_GetErrorCode( - ISACStruct* ISAC_main_inst); - - - /**************************************************************************** - * WebRtcIsac_GetUplinkBw(...) - * - * This function outputs the target bottleneck of the codec. In - * channel-adaptive mode, the target bottleneck is specified through in-band - * signalling retreived by bandwidth estimator. - * In channel-independent, also called instantaneous mode, the target - * bottleneck is provided to the encoder by calling xxx_control(...). If - * xxx_control is never called the default values is returned. The default - * value for bottleneck at 16 kHz encoder sampling rate is 32000 bits/sec, - * and it is 56000 bits/sec for 32 kHz sampling rate. - * Note that the output is the iSAC internal operating bottleneck which might - * differ slightly from the one provided through xxx_control(). - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Output: - * - *bottleneck : bottleneck in bits/sec - * - * Return value : -1 if error happens - * 0 bit-rates computed correctly. - */ - - int16_t WebRtcIsac_GetUplinkBw( - ISACStruct* ISAC_main_inst, - int32_t* bottleneck); - - - /****************************************************************************** - * WebRtcIsac_SetMaxPayloadSize(...) - * - * This function sets a limit for the maximum payload size of iSAC. The same - * value is used both for 30 and 60 ms packets. If the encoder sampling rate - * is 16 kHz the maximum payload size is between 120 and 400 bytes. If the - * encoder sampling rate is 32 kHz the maximum payload size is between 120 - * and 600 bytes. - * - * If an out of range limit is used, the function returns -1, but the closest - * valid value will be applied. - * - * --------------- - * IMPORTANT NOTES - * --------------- - * The size of a packet is limited to the minimum of 'max-payload-size' and - * 'max-rate.' For instance, let's assume the max-payload-size is set to - * 170 bytes, and max-rate is set to 40 kbps. Note that a limit of 40 kbps - * translates to 150 bytes for 30ms frame-size & 300 bytes for 60ms - * frame-size. Then a packet with a frame-size of 30 ms is limited to 150, - * i.e. min(170, 150), and a packet with 60 ms frame-size is limited to - * 170 bytes, i.e. min(170, 300). - * - * Input: - * - ISAC_main_inst : iSAC instance - * - maxPayloadBytes : maximum size of the payload in bytes - * valid values are between 120 and 400 bytes - * if encoder sampling rate is 16 kHz. For - * 32 kHz encoder sampling rate valid values - * are between 120 and 600 bytes. - * - * Return value : 0 if successful - * -1 if error happens - */ - - int16_t WebRtcIsac_SetMaxPayloadSize( - ISACStruct* ISAC_main_inst, - int16_t maxPayloadBytes); - - - /****************************************************************************** - * WebRtcIsac_SetMaxRate(...) - * - * This function sets the maximum rate which the codec may not exceed for - * any signal packet. The maximum rate is defined and payload-size per - * frame-size in bits per second. - * - * The codec has a maximum rate of 53400 bits per second (200 bytes per 30 - * ms) if the encoder sampling rate is 16kHz, and 160 kbps (600 bytes/30 ms) - * if the encoder sampling rate is 32 kHz. - * - * It is possible to set a maximum rate between 32000 and 53400 bits/sec - * in wideband mode, and 32000 to 160000 bits/sec in super-wideband mode. - * - * If an out of range limit is used, the function returns -1, but the closest - * valid value will be applied. - * - * --------------- - * IMPORTANT NOTES - * --------------- - * The size of a packet is limited to the minimum of 'max-payload-size' and - * 'max-rate.' For instance, let's assume the max-payload-size is set to - * 170 bytes, and max-rate is set to 40 kbps. Note that a limit of 40 kbps - * translates to 150 bytes for 30ms frame-size & 300 bytes for 60ms - * frame-size. Then a packet with a frame-size of 30 ms is limited to 150, - * i.e. min(170, 150), and a packet with 60 ms frame-size is limited to - * 170 bytes, min(170, 300). - * - * Input: - * - ISAC_main_inst : iSAC instance - * - maxRate : maximum rate in bits per second, - * valid values are 32000 to 53400 bits/sec in - * wideband mode, and 32000 to 160000 bits/sec in - * super-wideband mode. - * - * Return value : 0 if successful - * -1 if error happens - */ - - int16_t WebRtcIsac_SetMaxRate( - ISACStruct* ISAC_main_inst, - int32_t maxRate); - - - /****************************************************************************** - * WebRtcIsac_DecSampRate() - * Return the sampling rate of the decoded audio. - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Return value : sampling frequency in Hertz. - * - */ - - uint16_t WebRtcIsac_DecSampRate(ISACStruct* ISAC_main_inst); - - - /****************************************************************************** - * WebRtcIsac_EncSampRate() - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Return value : sampling rate in Hertz. - * - */ - - uint16_t WebRtcIsac_EncSampRate(ISACStruct* ISAC_main_inst); - - - /****************************************************************************** - * WebRtcIsac_SetDecSampRate() - * Set the sampling rate of the decoder. Initialization of the decoder WILL - * NOT overwrite the sampling rate of the encoder. The default value is 16 kHz - * which is set when the instance is created. - * - * Input: - * - ISAC_main_inst : iSAC instance - * - sampRate : sampling rate in Hertz. - * - * Return value : 0 if successful - * -1 if failed. - */ - - int16_t WebRtcIsac_SetDecSampRate(ISACStruct* ISAC_main_inst, - uint16_t samp_rate_hz); - - - /****************************************************************************** - * WebRtcIsac_SetEncSampRate() - * Set the sampling rate of the encoder. Initialization of the encoder WILL - * NOT overwrite the sampling rate of the encoder. The default value is 16 kHz - * which is set when the instance is created. The encoding-mode and the - * bottleneck remain unchanged by this call, however, the maximum rate and - * maximum payload-size will reset to their default value. - * - * Input: - * - ISAC_main_inst : iSAC instance - * - sampRate : sampling rate in Hertz. - * - * Return value : 0 if successful - * -1 if failed. - */ - - int16_t WebRtcIsac_SetEncSampRate(ISACStruct* ISAC_main_inst, - uint16_t sample_rate_hz); - - - - /****************************************************************************** - * WebRtcIsac_GetNewBitStream(...) - * - * This function returns encoded data, with the recieved bwe-index in the - * stream. If the rate is set to a value less than bottleneck of codec - * the new bistream will be re-encoded with the given target rate. - * It should always return a complete packet, i.e. only called once - * even for 60 msec frames. - * - * NOTE 1! This function does not write in the ISACStruct, it is not allowed. - * NOTE 2! Currently not implemented for SWB mode. - * NOTE 3! Rates larger than the bottleneck of the codec will be limited - * to the current bottleneck. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - bweIndex : Index of bandwidth estimate to put in new - * bitstream - * - rate : target rate of the transcoder is bits/sec. - * Valid values are the accepted rate in iSAC, - * i.e. 10000 to 56000. - * - isRCU : if the new bit-stream is an RCU stream. - * Note that the rate parameter always indicates - * the target rate of the main payload, regardless - * of 'isRCU' value. - * - * Output: - * - encoded : The encoded data vector - * - * Return value : >0 - Length (in bytes) of coded data - * -1 - Error or called in SWB mode - * NOTE! No error code is written to - * the struct since it is only allowed to read - * the struct. - */ - int16_t WebRtcIsac_GetNewBitStream( - ISACStruct* ISAC_main_inst, - int16_t bweIndex, - int16_t jitterInfo, - int32_t rate, - uint8_t* encoded, - int16_t isRCU); - - - - /**************************************************************************** - * WebRtcIsac_GetDownLinkBwIndex(...) - * - * This function returns index representing the Bandwidth estimate from - * other side to this side. - * - * Input: - * - ISAC_main_inst : iSAC struct - * - * Output: - * - bweIndex : Bandwidth estimate to transmit to other side. - * - */ - - int16_t WebRtcIsac_GetDownLinkBwIndex( - ISACStruct* ISAC_main_inst, - int16_t* bweIndex, - int16_t* jitterInfo); - - - /**************************************************************************** - * WebRtcIsac_UpdateUplinkBw(...) - * - * This function takes an index representing the Bandwidth estimate from - * this side to other side and updates BWE. - * - * Input: - * - ISAC_main_inst : iSAC struct - * - bweIndex : Bandwidth estimate from other side. - * - */ - - int16_t WebRtcIsac_UpdateUplinkBw( - ISACStruct* ISAC_main_inst, - int16_t bweIndex); - - - /**************************************************************************** - * WebRtcIsac_ReadBwIndex(...) - * - * This function returns the index of the Bandwidth estimate from the bitstream. - * - * Input: - * - encoded : Encoded bitstream - * - * Output: - * - frameLength : Length of frame in packet (in samples) - * - bweIndex : Bandwidth estimate in bitstream - * - */ - - int16_t WebRtcIsac_ReadBwIndex( - const uint8_t* encoded, - int16_t* bweIndex); - - - - /******************************************************************************* - * WebRtcIsac_GetNewFrameLen(...) - * - * returns the frame lenght (in samples) of the next packet. In the case of channel-adaptive - * mode, iSAC decides on its frame lenght based on the estimated bottleneck - * this allows a user to prepare for the next packet (at the encoder) - * - * The primary usage is in CE to make the iSAC works in channel-adaptive mode - * - * Input: - * - ISAC_main_inst : iSAC struct - * - * Return Value : frame lenght in samples - * - */ - - int16_t WebRtcIsac_GetNewFrameLen( - ISACStruct* ISAC_main_inst); - - - /**************************************************************************** - * WebRtcIsac_GetRedPayload(...) - * - * Populates "encoded" with the redundant payload of the recently encoded - * frame. This function has to be called once that WebRtcIsac_Encode(...) - * returns a positive value. Regardless of the frame-size this function will - * be called only once after encoding is completed. - * - * Input: - * - ISAC_main_inst : iSAC struct - * - * Output: - * - encoded : the encoded data vector - * - * - * Return value: - * : >0 - Length (in bytes) of coded data - * : -1 - Error - * - * - */ - int16_t WebRtcIsac_GetRedPayload( - ISACStruct* ISAC_main_inst, - uint8_t* encoded); - - - /**************************************************************************** - * WebRtcIsac_DecodeRcu(...) - * - * This function decodes a redundant (RCU) iSAC frame. Function is called in - * NetEq with a stored RCU payload i case of packet loss. Output speech length - * will be a multiple of 480 samples: 480 or 960 samples, - * depending on the framesize (30 or 60 ms). - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC RCU frame(s) - * - len : bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded vector - * -1 - Error - */ - int WebRtcIsac_DecodeRcu( - ISACStruct* ISAC_main_inst, - const uint8_t* encoded, - size_t len, - int16_t* decoded, - int16_t* speechType); - - /* Fills in an IsacBandwidthInfo struct. |inst| should be a decoder. */ - void WebRtcIsac_GetBandwidthInfo(ISACStruct* inst, IsacBandwidthInfo* bwinfo); - - /* Uses the values from an IsacBandwidthInfo struct. |inst| should be an - encoder. */ - void WebRtcIsac_SetBandwidthInfo(ISACStruct* inst, - const IsacBandwidthInfo* bwinfo); - - /* If |inst| is a decoder but not an encoder: tell it what sample rate the - encoder is using, for bandwidth estimation purposes. */ - void WebRtcIsac_SetEncSampRateInDecoder(ISACStruct* inst, int sample_rate_hz); +/****************************************************************************** + * WebRtcIsac_Create(...) + * + * This function creates an ISAC instance, which will contain the state + * information for one coding/decoding channel. + * + * Input: + * - *ISAC_main_inst : a pointer to the coder instance. + * + * Return value : 0 - Ok + * -1 - Error + */ + +int16_t WebRtcIsac_Create(ISACStruct** ISAC_main_inst); + +/****************************************************************************** + * WebRtcIsac_Free(...) + * + * This function frees the ISAC instance created at the beginning. + * + * Input: + * - ISAC_main_inst : an ISAC instance. + * + * Return value : 0 - Ok + * -1 - Error + */ + +int16_t WebRtcIsac_Free(ISACStruct* ISAC_main_inst); + +/****************************************************************************** + * WebRtcIsac_EncoderInit(...) + * + * This function initializes an ISAC instance prior to the encoder calls. + * + * Input: + * - ISAC_main_inst : ISAC instance. + * - CodingMode : 0 -> Bit rate and frame length are + * automatically adjusted to available bandwidth + * on transmission channel, just valid if codec + * is created to work in wideband mode. + * 1 -> User sets a frame length and a target bit + * rate which is taken as the maximum + * short-term average bit rate. + * + * Return value : 0 - Ok + * -1 - Error + */ + +int16_t WebRtcIsac_EncoderInit(ISACStruct* ISAC_main_inst, int16_t CodingMode); + +/****************************************************************************** + * WebRtcIsac_Encode(...) + * + * This function encodes 10ms audio blocks and inserts it into a package. + * Input speech length has 160 samples if operating at 16 kHz sampling + * rate, or 320 if operating at 32 kHz sampling rate. The encoder buffers the + * input audio until the whole frame is buffered then proceeds with encoding. + * + * + * Input: + * - ISAC_main_inst : ISAC instance. + * - speechIn : input speech vector. + * + * Output: + * - encoded : the encoded data vector + * + * Return value: + * : >0 - Length (in bytes) of coded data + * : 0 - The buffer didn't reach the chosen + * frame-size so it keeps buffering speech + * samples. + * : -1 - Error + */ + +int WebRtcIsac_Encode(ISACStruct* ISAC_main_inst, + const int16_t* speechIn, + uint8_t* encoded); + +/****************************************************************************** + * WebRtcIsac_DecoderInit(...) + * + * This function initializes an ISAC instance prior to the decoder calls. + * + * Input: + * - ISAC_main_inst : ISAC instance. + */ + +void WebRtcIsac_DecoderInit(ISACStruct* ISAC_main_inst); + +/****************************************************************************** + * WebRtcIsac_UpdateBwEstimate(...) + * + * This function updates the estimate of the bandwidth. + * + * Input: + * - ISAC_main_inst : ISAC instance. + * - encoded : encoded ISAC frame(s). + * - packet_size : size of the packet. + * - rtp_seq_number : the RTP number of the packet. + * - send_ts : the RTP send timestamp, given in samples + * - arr_ts : the arrival time of the packet (from NetEq) + * in samples. + * + * Return value : 0 - Ok + * -1 - Error + */ + +int16_t WebRtcIsac_UpdateBwEstimate(ISACStruct* ISAC_main_inst, + const uint8_t* encoded, + size_t packet_size, + uint16_t rtp_seq_number, + uint32_t send_ts, + uint32_t arr_ts); + +/****************************************************************************** + * WebRtcIsac_Decode(...) + * + * This function decodes an ISAC frame. At 16 kHz sampling rate, the length + * of the output audio could be either 480 or 960 samples, equivalent to + * 30 or 60 ms respectively. At 32 kHz sampling rate, the length of the + * output audio is 960 samples, which is 30 ms. + * + * Input: + * - ISAC_main_inst : ISAC instance. + * - encoded : encoded ISAC frame(s). + * - len : bytes in encoded vector. + * + * Output: + * - decoded : The decoded vector. + * + * Return value : >0 - number of samples in decoded vector. + * -1 - Error. + */ + +int WebRtcIsac_Decode(ISACStruct* ISAC_main_inst, + const uint8_t* encoded, + size_t len, + int16_t* decoded, + int16_t* speechType); + +/****************************************************************************** + * WebRtcIsac_DecodePlc(...) + * + * This function conducts PLC for ISAC frame(s). Output speech length + * will be a multiple of frames, i.e. multiples of 30 ms audio. Therefore, + * the output is multiple of 480 samples if operating at 16 kHz and multiple + * of 960 if operating at 32 kHz. + * + * Input: + * - ISAC_main_inst : ISAC instance. + * - noOfLostFrames : Number of PLC frames to produce. + * + * Output: + * - decoded : The decoded vector. + * + * Return value : Number of samples in decoded PLC vector + */ + +size_t WebRtcIsac_DecodePlc(ISACStruct* ISAC_main_inst, + int16_t* decoded, + size_t noOfLostFrames); + +/****************************************************************************** + * WebRtcIsac_Control(...) + * + * This function sets the limit on the short-term average bit-rate and the + * frame length. Should be used only in Instantaneous mode. At 16 kHz sampling + * rate, an average bit-rate between 10000 to 32000 bps is valid and a + * frame-size of 30 or 60 ms is acceptable. At 32 kHz, an average bit-rate + * between 10000 to 56000 is acceptable, and the valid frame-size is 30 ms. + * + * Input: + * - ISAC_main_inst : ISAC instance. + * - rate : limit on the short-term average bit rate, + * in bits/second. + * - framesize : frame-size in millisecond. + * + * Return value : 0 - ok + * -1 - Error + */ + +int16_t WebRtcIsac_Control(ISACStruct* ISAC_main_inst, + int32_t rate, + int framesize); + +void WebRtcIsac_SetInitialBweBottleneck(ISACStruct* ISAC_main_inst, + int bottleneck_bits_per_second); + +/****************************************************************************** + * WebRtcIsac_ControlBwe(...) + * + * This function sets the initial values of bottleneck and frame-size if + * iSAC is used in channel-adaptive mode. Therefore, this API is not + * applicable if the codec is created to operate in super-wideband mode. + * + * Through this API, users can enforce a frame-size for all values of + * bottleneck. Then iSAC will not automatically change the frame-size. + * + * + * Input: + * - ISAC_main_inst : ISAC instance. + * - rateBPS : initial value of bottleneck in bits/second + * 10000 <= rateBPS <= 56000 is accepted + * For default bottleneck set rateBPS = 0 + * - frameSizeMs : number of milliseconds per frame (30 or 60) + * - enforceFrameSize : 1 to enforce the given frame-size through + * out the adaptation process, 0 to let iSAC + * change the frame-size if required. + * + * Return value : 0 - ok + * -1 - Error + */ + +int16_t WebRtcIsac_ControlBwe(ISACStruct* ISAC_main_inst, + int32_t rateBPS, + int frameSizeMs, + int16_t enforceFrameSize); + +/****************************************************************************** + * WebRtcIsac_ReadFrameLen(...) + * + * This function returns the length of the frame represented in the packet. + * + * Input: + * - encoded : Encoded bit-stream + * + * Output: + * - frameLength : Length of frame in packet (in samples) + * + */ + +int16_t WebRtcIsac_ReadFrameLen(const ISACStruct* ISAC_main_inst, + const uint8_t* encoded, + int16_t* frameLength); + +/****************************************************************************** + * WebRtcIsac_version(...) + * + * This function returns the version number. + * + * Output: + * - version : Pointer to character string + * + */ + +void WebRtcIsac_version(char* version); + +/****************************************************************************** + * WebRtcIsac_GetErrorCode(...) + * + * This function can be used to check the error code of an iSAC instance. When + * a function returns -1 a error code will be set for that instance. The + * function below extract the code of the last error that occurred in the + * specified instance. + * + * Input: + * - ISAC_main_inst : ISAC instance + * + * Return value : Error code + */ + +int16_t WebRtcIsac_GetErrorCode(ISACStruct* ISAC_main_inst); + +/**************************************************************************** + * WebRtcIsac_GetUplinkBw(...) + * + * This function outputs the target bottleneck of the codec. In + * channel-adaptive mode, the target bottleneck is specified through in-band + * signalling retreived by bandwidth estimator. + * In channel-independent, also called instantaneous mode, the target + * bottleneck is provided to the encoder by calling xxx_control(...). If + * xxx_control is never called the default values is returned. The default + * value for bottleneck at 16 kHz encoder sampling rate is 32000 bits/sec, + * and it is 56000 bits/sec for 32 kHz sampling rate. + * Note that the output is the iSAC internal operating bottleneck which might + * differ slightly from the one provided through xxx_control(). + * + * Input: + * - ISAC_main_inst : iSAC instance + * + * Output: + * - *bottleneck : bottleneck in bits/sec + * + * Return value : -1 if error happens + * 0 bit-rates computed correctly. + */ + +int16_t WebRtcIsac_GetUplinkBw(ISACStruct* ISAC_main_inst, int32_t* bottleneck); + +/****************************************************************************** + * WebRtcIsac_SetMaxPayloadSize(...) + * + * This function sets a limit for the maximum payload size of iSAC. The same + * value is used both for 30 and 60 ms packets. If the encoder sampling rate + * is 16 kHz the maximum payload size is between 120 and 400 bytes. If the + * encoder sampling rate is 32 kHz the maximum payload size is between 120 + * and 600 bytes. + * + * If an out of range limit is used, the function returns -1, but the closest + * valid value will be applied. + * + * --------------- + * IMPORTANT NOTES + * --------------- + * The size of a packet is limited to the minimum of 'max-payload-size' and + * 'max-rate.' For instance, let's assume the max-payload-size is set to + * 170 bytes, and max-rate is set to 40 kbps. Note that a limit of 40 kbps + * translates to 150 bytes for 30ms frame-size & 300 bytes for 60ms + * frame-size. Then a packet with a frame-size of 30 ms is limited to 150, + * i.e. min(170, 150), and a packet with 60 ms frame-size is limited to + * 170 bytes, i.e. min(170, 300). + * + * Input: + * - ISAC_main_inst : iSAC instance + * - maxPayloadBytes : maximum size of the payload in bytes + * valid values are between 120 and 400 bytes + * if encoder sampling rate is 16 kHz. For + * 32 kHz encoder sampling rate valid values + * are between 120 and 600 bytes. + * + * Return value : 0 if successful + * -1 if error happens + */ + +int16_t WebRtcIsac_SetMaxPayloadSize(ISACStruct* ISAC_main_inst, + int16_t maxPayloadBytes); + +/****************************************************************************** + * WebRtcIsac_SetMaxRate(...) + * + * This function sets the maximum rate which the codec may not exceed for + * any signal packet. The maximum rate is defined and payload-size per + * frame-size in bits per second. + * + * The codec has a maximum rate of 53400 bits per second (200 bytes per 30 + * ms) if the encoder sampling rate is 16kHz, and 160 kbps (600 bytes/30 ms) + * if the encoder sampling rate is 32 kHz. + * + * It is possible to set a maximum rate between 32000 and 53400 bits/sec + * in wideband mode, and 32000 to 160000 bits/sec in super-wideband mode. + * + * If an out of range limit is used, the function returns -1, but the closest + * valid value will be applied. + * + * --------------- + * IMPORTANT NOTES + * --------------- + * The size of a packet is limited to the minimum of 'max-payload-size' and + * 'max-rate.' For instance, let's assume the max-payload-size is set to + * 170 bytes, and max-rate is set to 40 kbps. Note that a limit of 40 kbps + * translates to 150 bytes for 30ms frame-size & 300 bytes for 60ms + * frame-size. Then a packet with a frame-size of 30 ms is limited to 150, + * i.e. min(170, 150), and a packet with 60 ms frame-size is limited to + * 170 bytes, min(170, 300). + * + * Input: + * - ISAC_main_inst : iSAC instance + * - maxRate : maximum rate in bits per second, + * valid values are 32000 to 53400 bits/sec in + * wideband mode, and 32000 to 160000 bits/sec in + * super-wideband mode. + * + * Return value : 0 if successful + * -1 if error happens + */ + +int16_t WebRtcIsac_SetMaxRate(ISACStruct* ISAC_main_inst, int32_t maxRate); + +/****************************************************************************** + * WebRtcIsac_DecSampRate() + * Return the sampling rate of the decoded audio. + * + * Input: + * - ISAC_main_inst : iSAC instance + * + * Return value : sampling frequency in Hertz. + * + */ + +uint16_t WebRtcIsac_DecSampRate(ISACStruct* ISAC_main_inst); + +/****************************************************************************** + * WebRtcIsac_EncSampRate() + * + * Input: + * - ISAC_main_inst : iSAC instance + * + * Return value : sampling rate in Hertz. + * + */ + +uint16_t WebRtcIsac_EncSampRate(ISACStruct* ISAC_main_inst); + +/****************************************************************************** + * WebRtcIsac_SetDecSampRate() + * Set the sampling rate of the decoder. Initialization of the decoder WILL + * NOT overwrite the sampling rate of the encoder. The default value is 16 kHz + * which is set when the instance is created. + * + * Input: + * - ISAC_main_inst : iSAC instance + * - sampRate : sampling rate in Hertz. + * + * Return value : 0 if successful + * -1 if failed. + */ + +int16_t WebRtcIsac_SetDecSampRate(ISACStruct* ISAC_main_inst, + uint16_t samp_rate_hz); + +/****************************************************************************** + * WebRtcIsac_SetEncSampRate() + * Set the sampling rate of the encoder. Initialization of the encoder WILL + * NOT overwrite the sampling rate of the encoder. The default value is 16 kHz + * which is set when the instance is created. The encoding-mode and the + * bottleneck remain unchanged by this call, however, the maximum rate and + * maximum payload-size will reset to their default value. + * + * Input: + * - ISAC_main_inst : iSAC instance + * - sampRate : sampling rate in Hertz. + * + * Return value : 0 if successful + * -1 if failed. + */ + +int16_t WebRtcIsac_SetEncSampRate(ISACStruct* ISAC_main_inst, + uint16_t sample_rate_hz); + +/****************************************************************************** + * WebRtcIsac_GetNewBitStream(...) + * + * This function returns encoded data, with the recieved bwe-index in the + * stream. If the rate is set to a value less than bottleneck of codec + * the new bistream will be re-encoded with the given target rate. + * It should always return a complete packet, i.e. only called once + * even for 60 msec frames. + * + * NOTE 1! This function does not write in the ISACStruct, it is not allowed. + * NOTE 2! Currently not implemented for SWB mode. + * NOTE 3! Rates larger than the bottleneck of the codec will be limited + * to the current bottleneck. + * + * Input: + * - ISAC_main_inst : ISAC instance. + * - bweIndex : Index of bandwidth estimate to put in new + * bitstream + * - rate : target rate of the transcoder is bits/sec. + * Valid values are the accepted rate in iSAC, + * i.e. 10000 to 56000. + * - isRCU : if the new bit-stream is an RCU + * stream. Note that the rate parameter always indicates the target rate of the + * main payload, regardless of 'isRCU' value. + * + * Output: + * - encoded : The encoded data vector + * + * Return value : >0 - Length (in bytes) of coded data + * -1 - Error or called in SWB mode + * NOTE! No error code is written to + * the struct since it is only allowed to read + * the struct. + */ +int16_t WebRtcIsac_GetNewBitStream(ISACStruct* ISAC_main_inst, + int16_t bweIndex, + int16_t jitterInfo, + int32_t rate, + uint8_t* encoded, + int16_t isRCU); + +/**************************************************************************** + * WebRtcIsac_GetDownLinkBwIndex(...) + * + * This function returns index representing the Bandwidth estimate from + * other side to this side. + * + * Input: + * - ISAC_main_inst : iSAC struct + * + * Output: + * - bweIndex : Bandwidth estimate to transmit to other side. + * + */ + +int16_t WebRtcIsac_GetDownLinkBwIndex(ISACStruct* ISAC_main_inst, + int16_t* bweIndex, + int16_t* jitterInfo); + +/**************************************************************************** + * WebRtcIsac_UpdateUplinkBw(...) + * + * This function takes an index representing the Bandwidth estimate from + * this side to other side and updates BWE. + * + * Input: + * - ISAC_main_inst : iSAC struct + * - bweIndex : Bandwidth estimate from other side. + * + */ + +int16_t WebRtcIsac_UpdateUplinkBw(ISACStruct* ISAC_main_inst, int16_t bweIndex); + +/**************************************************************************** + * WebRtcIsac_ReadBwIndex(...) + * + * This function returns the index of the Bandwidth estimate from the bitstream. + * + * Input: + * - encoded : Encoded bitstream + * + * Output: + * - frameLength : Length of frame in packet (in samples) + * - bweIndex : Bandwidth estimate in bitstream + * + */ + +int16_t WebRtcIsac_ReadBwIndex(const uint8_t* encoded, int16_t* bweIndex); + +/******************************************************************************* + * WebRtcIsac_GetNewFrameLen(...) + * + * returns the frame lenght (in samples) of the next packet. In the case of + * channel-adaptive mode, iSAC decides on its frame lenght based on the + * estimated bottleneck this allows a user to prepare for the next packet (at + * the encoder) + * + * The primary usage is in CE to make the iSAC works in channel-adaptive mode + * + * Input: + * - ISAC_main_inst : iSAC struct + * + * Return Value : frame lenght in samples + * + */ + +int16_t WebRtcIsac_GetNewFrameLen(ISACStruct* ISAC_main_inst); + +/**************************************************************************** + * WebRtcIsac_GetRedPayload(...) + * + * Populates "encoded" with the redundant payload of the recently encoded + * frame. This function has to be called once that WebRtcIsac_Encode(...) + * returns a positive value. Regardless of the frame-size this function will + * be called only once after encoding is completed. + * + * Input: + * - ISAC_main_inst : iSAC struct + * + * Output: + * - encoded : the encoded data vector + * + * + * Return value: + * : >0 - Length (in bytes) of coded data + * : -1 - Error + * + * + */ +int16_t WebRtcIsac_GetRedPayload(ISACStruct* ISAC_main_inst, uint8_t* encoded); + +/**************************************************************************** + * WebRtcIsac_DecodeRcu(...) + * + * This function decodes a redundant (RCU) iSAC frame. Function is called in + * NetEq with a stored RCU payload i case of packet loss. Output speech length + * will be a multiple of 480 samples: 480 or 960 samples, + * depending on the framesize (30 or 60 ms). + * + * Input: + * - ISAC_main_inst : ISAC instance. + * - encoded : encoded ISAC RCU frame(s) + * - len : bytes in encoded vector + * + * Output: + * - decoded : The decoded vector + * + * Return value : >0 - number of samples in decoded vector + * -1 - Error + */ +int WebRtcIsac_DecodeRcu(ISACStruct* ISAC_main_inst, + const uint8_t* encoded, + size_t len, + int16_t* decoded, + int16_t* speechType); + +/* If |inst| is a decoder but not an encoder: tell it what sample rate the + encoder is using, for bandwidth estimation purposes. */ +void WebRtcIsac_SetEncSampRateInDecoder(ISACStruct* inst, int sample_rate_hz); #if defined(__cplusplus) } #endif - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_ISAC_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INCLUDE_ISAC_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines.c b/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines.c index 5c901bb..9d5c693 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines.c @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "arith_routines.h" -#include "settings.h" +#include "modules/audio_coding/codecs/isac/main/source/arith_routines.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" /* diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines.h b/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines.h index 43ba40e..6e7ea1d 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines.h @@ -15,49 +15,53 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ARITH_ROUTINES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ARITH_ROUTINES_H_ - -#include "structs.h" +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ARITH_ROUTINES_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ARITH_ROUTINES_H_ +#include "modules/audio_coding/codecs/isac/main/source/structs.h" int WebRtcIsac_EncLogisticMulti2( - Bitstr *streamdata, /* in-/output struct containing bitstream */ - int16_t *dataQ7, /* input: data vector */ - const uint16_t *env, /* input: side info vector defining the width of the pdf */ - const int N, /* input: data vector length */ + Bitstr* streamdata, /* in-/output struct containing bitstream */ + int16_t* dataQ7, /* input: data vector */ + const uint16_t* + env, /* input: side info vector defining the width of the pdf */ + const int N, /* input: data vector length */ const int16_t isSWB12kHz); /* if the codec is working in 12kHz bandwidth */ /* returns the number of bytes in the stream */ -int WebRtcIsac_EncTerminate(Bitstr *streamdata); /* in-/output struct containing bitstream */ +int WebRtcIsac_EncTerminate( + Bitstr* streamdata); /* in-/output struct containing bitstream */ /* returns the number of bytes in the stream so far */ int WebRtcIsac_DecLogisticMulti2( - int16_t *data, /* output: data vector */ - Bitstr *streamdata, /* in-/output struct containing bitstream */ - const uint16_t *env, /* input: side info vector defining the width of the pdf */ - const int16_t *dither, /* input: dither vector */ - const int N, /* input: data vector length */ + int16_t* data, /* output: data vector */ + Bitstr* streamdata, /* in-/output struct containing bitstream */ + const uint16_t* + env, /* input: side info vector defining the width of the pdf */ + const int16_t* dither, /* input: dither vector */ + const int N, /* input: data vector length */ const int16_t isSWB12kHz); /* if the codec is working in 12kHz bandwidth */ void WebRtcIsac_EncHistMulti( - Bitstr *streamdata, /* in-/output struct containing bitstream */ - const int *data, /* input: data vector */ - const uint16_t **cdf, /* input: array of cdf arrays */ + Bitstr* streamdata, /* in-/output struct containing bitstream */ + const int* data, /* input: data vector */ + const uint16_t* const* cdf, /* input: array of cdf arrays */ const int N); /* input: data vector length */ int WebRtcIsac_DecHistBisectMulti( - int *data, /* output: data vector */ - Bitstr *streamdata, /* in-/output struct containing bitstream */ - const uint16_t **cdf, /* input: array of cdf arrays */ - const uint16_t *cdf_size, /* input: array of cdf table sizes+1 (power of two: 2^k) */ - const int N); /* input: data vector length */ + int* data, /* output: data vector */ + Bitstr* streamdata, /* in-/output struct containing bitstream */ + const uint16_t* const* cdf, /* input: array of cdf arrays */ + const uint16_t* + cdf_size, /* input: array of cdf table sizes+1 (power of two: 2^k) */ + const int N); /* input: data vector length */ int WebRtcIsac_DecHistOneStepMulti( - int *data, /* output: data vector */ - Bitstr *streamdata, /* in-/output struct containing bitstream */ - const uint16_t **cdf, /* input: array of cdf arrays */ - const uint16_t *init_index,/* input: vector of initial cdf table search entries */ - const int N); /* input: data vector length */ + int* data, /* output: data vector */ + Bitstr* streamdata, /* in-/output struct containing bitstream */ + const uint16_t* const* cdf, /* input: array of cdf arrays */ + const uint16_t* + init_index, /* input: vector of initial cdf table search entries */ + const int N); /* input: data vector length */ -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ARITH_ROUTINES_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ARITH_ROUTINES_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines_hist.c b/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines_hist.c index 63e4928..e948979 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines_hist.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines_hist.c @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "settings.h" -#include "arith_routines.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/source/arith_routines.h" /* @@ -17,7 +17,7 @@ */ void WebRtcIsac_EncHistMulti(Bitstr *streamdata, /* in-/output struct containing bitstream */ const int *data, /* input: data vector */ - const uint16_t **cdf, /* input: array of cdf arrays */ + const uint16_t *const *cdf, /* input: array of cdf arrays */ const int N) /* input: data vector length */ { uint32_t W_lower, W_upper; @@ -84,7 +84,7 @@ void WebRtcIsac_EncHistMulti(Bitstr *streamdata, /* in-/output struct containing */ int WebRtcIsac_DecHistBisectMulti(int *data, /* output: data vector */ Bitstr *streamdata, /* in-/output struct containing bitstream */ - const uint16_t **cdf, /* input: array of cdf arrays */ + const uint16_t *const *cdf, /* input: array of cdf arrays */ const uint16_t *cdf_size, /* input: array of cdf table sizes+1 (power of two: 2^k) */ const int N) /* input: data vector length */ { @@ -192,7 +192,7 @@ int WebRtcIsac_DecHistBisectMulti(int *data, /* output: data vector */ */ int WebRtcIsac_DecHistOneStepMulti(int *data, /* output: data vector */ Bitstr *streamdata, /* in-/output struct containing bitstream */ - const uint16_t **cdf, /* input: array of cdf arrays */ + const uint16_t *const *cdf, /* input: array of cdf arrays */ const uint16_t *init_index, /* input: vector of initial cdf table search entries */ const int N) /* input: data vector length */ { @@ -214,10 +214,10 @@ int WebRtcIsac_DecHistOneStepMulti(int *data, /* output: data vector */ if (streamdata->stream_index == 0) /* first time decoder is called for this stream */ { /* read first word from bytestream */ - streamval = *stream_ptr << 24; - streamval |= *++stream_ptr << 16; - streamval |= *++stream_ptr << 8; - streamval |= *++stream_ptr; + streamval = (uint32_t)(*stream_ptr) << 24; + streamval |= (uint32_t)(*++stream_ptr) << 16; + streamval |= (uint32_t)(*++stream_ptr) << 8; + streamval |= (uint32_t)(*++stream_ptr); } else { streamval = streamdata->streamval; } diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines_logist.c b/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines_logist.c index eeed7ae..777780f 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines_logist.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/arith_routines_logist.c @@ -17,7 +17,7 @@ */ -#include "arith_routines.h" +#include "modules/audio_coding/codecs/isac/main/source/arith_routines.h" @@ -185,11 +185,18 @@ int WebRtcIsac_DecLogisticMulti2( int16_t candQ7; int k; + // Position just past the end of the stream. STREAM_SIZE_MAX_60 instead of + // STREAM_SIZE_MAX (which is the size of the allocated buffer) because that's + // the limit to how much data is filled in. + const uint8_t* const stream_end = streamdata->stream + STREAM_SIZE_MAX_60; + stream_ptr = streamdata->stream + streamdata->stream_index; W_upper = streamdata->W_upper; if (streamdata->stream_index == 0) /* first time decoder is called for this stream */ { /* read first word from bytestream */ + if (stream_ptr + 3 >= stream_end) + return -1; // Would read out of bounds. Malformed input? streamval = *stream_ptr << 24; streamval |= *++stream_ptr << 16; streamval |= *++stream_ptr << 8; @@ -277,6 +284,8 @@ int WebRtcIsac_DecLogisticMulti2( while ( !(W_upper & 0xFF000000) ) /* W_upper < 2^24 */ { /* read next byte from stream */ + if (stream_ptr + 1 >= stream_end) + return -1; // Would read out of bounds. Malformed input? streamval = (streamval << 8) | *++stream_ptr; W_upper <<= 8; } diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/audio_decoder_isac.cc b/webrtc/modules/audio_coding/codecs/isac/main/source/audio_decoder_isac.cc index 8e0603e..b671002 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/audio_decoder_isac.cc +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/audio_decoder_isac.cc @@ -8,9 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h" +#include "modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h" -#include "webrtc/modules/audio_coding/codecs/isac/audio_decoder_isac_t_impl.h" +#include "modules/audio_coding/codecs/isac/audio_decoder_isac_t_impl.h" namespace webrtc { diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac.cc b/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac.cc index 64b9815..b7f2c0b 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac.cc +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac.cc @@ -8,9 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h" +#include "modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h" -#include "webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h" +#include "modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h" namespace webrtc { diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.c b/webrtc/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.c index 82fd053..486cd95 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.c @@ -16,14 +16,14 @@ * */ -#include "bandwidth_estimator.h" -#include "settings.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/include/isac.h" - -#include #include #include +#include "modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/include/isac.h" +#include "rtc_base/checks.h" + /* array of quantization levels for bottle neck info; Matlab code: */ /* sprintf('%4.1ff, ', logspace(log10(5000), log10(40000), 12)) */ static const float kQRateTableWb[12] = @@ -159,7 +159,7 @@ int16_t WebRtcIsac_UpdateBandwidthEstimator( int immediate_set = 0; int num_pkts_expected; - assert(!bwest_str->external_bw_info.in_use); + RTC_DCHECK(!bwest_str->external_bw_info.in_use); // We have to adjust the header-rate if the first packet has a // frame-size different than the initialized value. @@ -514,7 +514,7 @@ int16_t WebRtcIsac_UpdateUplinkBwImpl( int16_t index, enum IsacSamplingRate encoderSamplingFreq) { - assert(!bwest_str->external_bw_info.in_use); + RTC_DCHECK(!bwest_str->external_bw_info.in_use); if((index < 0) || (index > 23)) { @@ -572,7 +572,7 @@ int16_t WebRtcIsac_UpdateUplinkJitter( BwEstimatorstr* bwest_str, int32_t index) { - assert(!bwest_str->external_bw_info.in_use); + RTC_DCHECK(!bwest_str->external_bw_info.in_use); if((index < 0) || (index > 23)) { @@ -711,7 +711,7 @@ int32_t WebRtcIsac_GetDownlinkBandwidth( const BwEstimatorstr *bwest_str) float jitter_sign; float bw_adjust; - assert(!bwest_str->external_bw_info.in_use); + RTC_DCHECK(!bwest_str->external_bw_info.in_use); /* create a value between -1.0 and 1.0 indicating "average sign" of jitter */ jitter_sign = bwest_str->rec_jitter_short_term / @@ -741,7 +741,7 @@ WebRtcIsac_GetDownlinkMaxDelay(const BwEstimatorstr *bwest_str) { int32_t rec_max_delay; - assert(!bwest_str->external_bw_info.in_use); + RTC_DCHECK(!bwest_str->external_bw_info.in_use); rec_max_delay = (int32_t)(bwest_str->rec_max_delay); @@ -759,7 +759,7 @@ WebRtcIsac_GetDownlinkMaxDelay(const BwEstimatorstr *bwest_str) /* Clamp val to the closed interval [min,max]. */ static int32_t clamp(int32_t val, int32_t min, int32_t max) { - assert(min <= max); + RTC_DCHECK_LE(min, max); return val < min ? min : (val > max ? max : val); } @@ -775,24 +775,6 @@ int32_t WebRtcIsac_GetUplinkMaxDelay(const BwEstimatorstr* bwest_str) { : clamp(bwest_str->send_max_delay_avg, MIN_ISAC_MD, MAX_ISAC_MD); } -void WebRtcIsacBw_GetBandwidthInfo(BwEstimatorstr* bwest_str, - enum IsacSamplingRate decoder_sample_rate_hz, - IsacBandwidthInfo* bwinfo) { - assert(!bwest_str->external_bw_info.in_use); - bwinfo->in_use = 1; - bwinfo->send_bw_avg = WebRtcIsac_GetUplinkBandwidth(bwest_str); - bwinfo->send_max_delay_avg = WebRtcIsac_GetUplinkMaxDelay(bwest_str); - WebRtcIsac_GetDownlinkBwJitIndexImpl(bwest_str, &bwinfo->bottleneck_idx, - &bwinfo->jitter_info, - decoder_sample_rate_hz); -} - -void WebRtcIsacBw_SetBandwidthInfo(BwEstimatorstr* bwest_str, - const IsacBandwidthInfo* bwinfo) { - memcpy(&bwest_str->external_bw_info, bwinfo, - sizeof bwest_str->external_bw_info); -} - /* * update long-term average bitrate and amount of data in buffer * returns minimum payload size (bytes) diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h b/webrtc/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h index 0704337..221e65f 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h @@ -16,169 +16,150 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_BANDWIDTH_ESTIMATOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_BANDWIDTH_ESTIMATOR_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_BANDWIDTH_ESTIMATOR_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_BANDWIDTH_ESTIMATOR_H_ -#include "structs.h" -#include "settings.h" +#include +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" -#define MIN_ISAC_BW 10000 -#define MIN_ISAC_BW_LB 10000 -#define MIN_ISAC_BW_UB 25000 +#define MIN_ISAC_BW 10000 +#define MIN_ISAC_BW_LB 10000 +#define MIN_ISAC_BW_UB 25000 -#define MAX_ISAC_BW 56000 -#define MAX_ISAC_BW_UB 32000 -#define MAX_ISAC_BW_LB 32000 +#define MAX_ISAC_BW 56000 +#define MAX_ISAC_BW_UB 32000 +#define MAX_ISAC_BW_LB 32000 -#define MIN_ISAC_MD 5 -#define MAX_ISAC_MD 25 +#define MIN_ISAC_MD 5 +#define MAX_ISAC_MD 25 // assumed header size, in bytes; we don't know the exact number // (header compression may be used) -#define HEADER_SIZE 35 +#define HEADER_SIZE 35 // Initial Frame-Size, in ms, for Wideband & Super-Wideband Mode -#define INIT_FRAME_LEN_WB 60 +#define INIT_FRAME_LEN_WB 60 #define INIT_FRAME_LEN_SWB 30 // Initial Bottleneck Estimate, in bits/sec, for // Wideband & Super-wideband mode -#define INIT_BN_EST_WB 20e3f -#define INIT_BN_EST_SWB 56e3f +#define INIT_BN_EST_WB 20e3f +#define INIT_BN_EST_SWB 56e3f // Initial Header rate (header rate depends on frame-size), // in bits/sec, for Wideband & Super-Wideband mode. -#define INIT_HDR_RATE_WB \ +#define INIT_HDR_RATE_WB \ ((float)HEADER_SIZE * 8.0f * 1000.0f / (float)INIT_FRAME_LEN_WB) -#define INIT_HDR_RATE_SWB \ +#define INIT_HDR_RATE_SWB \ ((float)HEADER_SIZE * 8.0f * 1000.0f / (float)INIT_FRAME_LEN_SWB) // number of packets in a row for a high rate burst -#define BURST_LEN 3 +#define BURST_LEN 3 // ms, max time between two full bursts -#define BURST_INTERVAL 500 +#define BURST_INTERVAL 500 // number of packets in a row for initial high rate burst -#define INIT_BURST_LEN 5 +#define INIT_BURST_LEN 5 // bits/s, rate for the first BURST_LEN packets -#define INIT_RATE_WB INIT_BN_EST_WB -#define INIT_RATE_SWB INIT_BN_EST_SWB - +#define INIT_RATE_WB INIT_BN_EST_WB +#define INIT_RATE_SWB INIT_BN_EST_SWB #if defined(__cplusplus) extern "C" { #endif - /* This function initializes the struct */ - /* to be called before using the struct for anything else */ - /* returns 0 if everything went fine, -1 otherwise */ - int32_t WebRtcIsac_InitBandwidthEstimator( - BwEstimatorstr* bwest_str, - enum IsacSamplingRate encoderSampRate, - enum IsacSamplingRate decoderSampRate); +/* This function initializes the struct */ +/* to be called before using the struct for anything else */ +/* returns 0 if everything went fine, -1 otherwise */ +int32_t WebRtcIsac_InitBandwidthEstimator( + BwEstimatorstr* bwest_str, + enum IsacSamplingRate encoderSampRate, + enum IsacSamplingRate decoderSampRate); - /* This function updates the receiving estimate */ - /* Parameters: */ - /* rtp_number - value from RTP packet, from NetEq */ - /* frame length - length of signal frame in ms, from iSAC decoder */ - /* send_ts - value in RTP header giving send time in samples */ - /* arr_ts - value given by timeGetTime() time of arrival in samples of packet from NetEq */ - /* pksize - size of packet in bytes, from NetEq */ - /* Index - integer (range 0...23) indicating bottle neck & jitter as estimated by other side */ - /* returns 0 if everything went fine, -1 otherwise */ - int16_t WebRtcIsac_UpdateBandwidthEstimator( - BwEstimatorstr* bwest_str, - const uint16_t rtp_number, - const int32_t frame_length, - const uint32_t send_ts, - const uint32_t arr_ts, - const size_t pksize); +/* This function updates the receiving estimate */ +/* Parameters: */ +/* rtp_number - value from RTP packet, from NetEq */ +/* frame length - length of signal frame in ms, from iSAC decoder */ +/* send_ts - value in RTP header giving send time in samples */ +/* arr_ts - value given by timeGetTime() time of arrival in samples of + * packet from NetEq */ +/* pksize - size of packet in bytes, from NetEq */ +/* Index - integer (range 0...23) indicating bottle neck & jitter as + * estimated by other side */ +/* returns 0 if everything went fine, -1 otherwise */ +int16_t WebRtcIsac_UpdateBandwidthEstimator(BwEstimatorstr* bwest_str, + const uint16_t rtp_number, + const int32_t frame_length, + const uint32_t send_ts, + const uint32_t arr_ts, + const size_t pksize); - /* Update receiving estimates. Used when we only receive BWE index, no iSAC data packet. */ - int16_t WebRtcIsac_UpdateUplinkBwImpl( - BwEstimatorstr* bwest_str, - int16_t Index, - enum IsacSamplingRate encoderSamplingFreq); +/* Update receiving estimates. Used when we only receive BWE index, no iSAC data + * packet. */ +int16_t WebRtcIsac_UpdateUplinkBwImpl( + BwEstimatorstr* bwest_str, + int16_t Index, + enum IsacSamplingRate encoderSamplingFreq); - /* Returns the bandwidth/jitter estimation code (integer 0...23) to put in the sending iSAC payload */ - void WebRtcIsac_GetDownlinkBwJitIndexImpl( - BwEstimatorstr* bwest_str, - int16_t* bottleneckIndex, - int16_t* jitterInfo, - enum IsacSamplingRate decoderSamplingFreq); +/* Returns the bandwidth/jitter estimation code (integer 0...23) to put in the + * sending iSAC payload */ +void WebRtcIsac_GetDownlinkBwJitIndexImpl( + BwEstimatorstr* bwest_str, + int16_t* bottleneckIndex, + int16_t* jitterInfo, + enum IsacSamplingRate decoderSamplingFreq); - /* Returns the bandwidth estimation (in bps) */ - int32_t WebRtcIsac_GetDownlinkBandwidth( - const BwEstimatorstr *bwest_str); +/* Returns the bandwidth estimation (in bps) */ +int32_t WebRtcIsac_GetDownlinkBandwidth(const BwEstimatorstr* bwest_str); - /* Returns the max delay (in ms) */ - int32_t WebRtcIsac_GetDownlinkMaxDelay( - const BwEstimatorstr *bwest_str); +/* Returns the max delay (in ms) */ +int32_t WebRtcIsac_GetDownlinkMaxDelay(const BwEstimatorstr* bwest_str); - /* Returns the bandwidth that iSAC should send with in bps */ - int32_t WebRtcIsac_GetUplinkBandwidth(const BwEstimatorstr* bwest_str); +/* Returns the bandwidth that iSAC should send with in bps */ +int32_t WebRtcIsac_GetUplinkBandwidth(const BwEstimatorstr* bwest_str); - /* Returns the max delay value from the other side in ms */ - int32_t WebRtcIsac_GetUplinkMaxDelay( - const BwEstimatorstr *bwest_str); +/* Returns the max delay value from the other side in ms */ +int32_t WebRtcIsac_GetUplinkMaxDelay(const BwEstimatorstr* bwest_str); - /* Fills in an IsacExternalBandwidthInfo struct. */ - void WebRtcIsacBw_GetBandwidthInfo( - BwEstimatorstr* bwest_str, - enum IsacSamplingRate decoder_sample_rate_hz, - IsacBandwidthInfo* bwinfo); +/* + * update amount of data in bottle neck buffer and burst handling + * returns minimum payload size (bytes) + */ +int WebRtcIsac_GetMinBytes( + RateModel* State, + int StreamSize, /* bytes in bitstream */ + const int FrameLen, /* ms per frame */ + const double BottleNeck, /* bottle neck rate; excl headers (bps) */ + const double DelayBuildUp, /* max delay from bottleneck buffering (ms) */ + enum ISACBandwidth bandwidth + /*,int16_t frequentLargePackets*/); - /* Uses the values from an IsacExternalBandwidthInfo struct. */ - void WebRtcIsacBw_SetBandwidthInfo(BwEstimatorstr* bwest_str, - const IsacBandwidthInfo* bwinfo); +/* + * update long-term average bitrate and amount of data in buffer + */ +void WebRtcIsac_UpdateRateModel( + RateModel* State, + int StreamSize, /* bytes in bitstream */ + const int FrameSamples, /* samples per frame */ + const double BottleNeck); /* bottle neck rate; excl headers (bps) */ - /* - * update amount of data in bottle neck buffer and burst handling - * returns minimum payload size (bytes) - */ - int WebRtcIsac_GetMinBytes( - RateModel* State, - int StreamSize, /* bytes in bitstream */ - const int FrameLen, /* ms per frame */ - const double BottleNeck, /* bottle neck rate; excl headers (bps) */ - const double DelayBuildUp, /* max delay from bottleneck buffering (ms) */ - enum ISACBandwidth bandwidth - /*,int16_t frequentLargePackets*/); +void WebRtcIsac_InitRateModel(RateModel* State); - /* - * update long-term average bitrate and amount of data in buffer - */ - void WebRtcIsac_UpdateRateModel( - RateModel* State, - int StreamSize, /* bytes in bitstream */ - const int FrameSamples, /* samples per frame */ - const double BottleNeck); /* bottle neck rate; excl headers (bps) */ +/* Returns the new framelength value (input argument: bottle_neck) */ +int WebRtcIsac_GetNewFrameLength(double bottle_neck, int current_framelength); +/* Returns the new SNR value (input argument: bottle_neck) */ +double WebRtcIsac_GetSnr(double bottle_neck, int new_framelength); - void WebRtcIsac_InitRateModel( - RateModel *State); - - /* Returns the new framelength value (input argument: bottle_neck) */ - int WebRtcIsac_GetNewFrameLength( - double bottle_neck, - int current_framelength); - - /* Returns the new SNR value (input argument: bottle_neck) */ - double WebRtcIsac_GetSnr( - double bottle_neck, - int new_framelength); - - - int16_t WebRtcIsac_UpdateUplinkJitter( - BwEstimatorstr* bwest_str, - int32_t index); +int16_t WebRtcIsac_UpdateUplinkJitter(BwEstimatorstr* bwest_str, int32_t index); #if defined(__cplusplus) } #endif - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_BANDWIDTH_ESTIMATOR_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_BANDWIDTH_ESTIMATOR_H_ \ + */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/codec.h b/webrtc/modules/audio_coding/codecs/isac/main/source/codec.h index 7ef64b5..a7c7ddc 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/codec.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/codec.h @@ -16,18 +16,22 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CODEC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CODEC_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CODEC_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CODEC_H_ -#include "structs.h" +#include +#include "modules/audio_coding/codecs/isac/main/source/structs.h" +#include "modules/third_party/fft/fft.h" void WebRtcIsac_ResetBitstream(Bitstr* bit_stream); -int WebRtcIsac_EstimateBandwidth(BwEstimatorstr* bwest_str, Bitstr* streamdata, +int WebRtcIsac_EstimateBandwidth(BwEstimatorstr* bwest_str, + Bitstr* streamdata, size_t packet_size, uint16_t rtp_seq_number, - uint32_t send_ts, uint32_t arr_ts, + uint32_t send_ts, + uint32_t arr_ts, enum IsacSamplingRate encoderSampRate, enum IsacSamplingRate decoderSampRate); @@ -37,7 +41,8 @@ int WebRtcIsac_DecodeLb(const TransformTables* transform_tables, int16_t* current_framesamples, int16_t isRCUPayload); -int WebRtcIsac_DecodeRcuLb(float* signal_out, ISACLBDecStruct* ISACdec_obj, +int WebRtcIsac_DecodeRcuLb(float* signal_out, + ISACLBDecStruct* ISACdec_obj, int16_t* current_framesamples); int WebRtcIsac_EncodeLb(const TransformTables* transform_tables, @@ -47,15 +52,20 @@ int WebRtcIsac_EncodeLb(const TransformTables* transform_tables, int16_t bottleneckIndex); int WebRtcIsac_EncodeStoredDataLb(const IsacSaveEncoderData* ISACSavedEnc_obj, - Bitstr* ISACBitStr_obj, int BWnumber, + Bitstr* ISACBitStr_obj, + int BWnumber, float scale); int WebRtcIsac_EncodeStoredDataUb( - const ISACUBSaveEncDataStruct* ISACSavedEnc_obj, Bitstr* bitStream, - int32_t jitterInfo, float scale, enum ISACBandwidth bandwidth); + const ISACUBSaveEncDataStruct* ISACSavedEnc_obj, + Bitstr* bitStream, + int32_t jitterInfo, + float scale, + enum ISACBandwidth bandwidth); int16_t WebRtcIsac_GetRedPayloadUb( - const ISACUBSaveEncDataStruct* ISACSavedEncObj, Bitstr* bitStreamObj, + const ISACUBSaveEncDataStruct* ISACSavedEncObj, + Bitstr* bitStreamObj, enum ISACBandwidth bandwidth); /****************************************************************************** @@ -81,7 +91,6 @@ int16_t WebRtcIsac_RateAllocation(int32_t inRateBitPerSec, double* rateUBBitPerSec, enum ISACBandwidth* bandwidthKHz); - /****************************************************************************** * WebRtcIsac_DecodeUb16() * @@ -166,15 +175,8 @@ int WebRtcIsac_EncodeUb12(const TransformTables* transform_tables, void WebRtcIsac_InitMasking(MaskFiltstr* maskdata); -void WebRtcIsac_InitPreFilterbank(PreFiltBankstr* prefiltdata); - void WebRtcIsac_InitPostFilterbank(PostFiltBankstr* postfiltdata); -void WebRtcIsac_InitPitchFilter(PitchFiltstr* pitchfiltdata); - -void WebRtcIsac_InitPitchAnalysis(PitchAnalysisStruct* State); - - /**************************** transform functions ****************************/ void WebRtcIsac_InitTransform(TransformTables* tables); @@ -193,41 +195,29 @@ void WebRtcIsac_Spec2time(const TransformTables* tables, double* outre2, FFTstr* fftstr_obj); -/******************************* filter functions ****************************/ - -void WebRtcIsac_AllPoleFilter(double* InOut, double* Coef, size_t lengthInOut, - int orderCoef); - -void WebRtcIsac_AllZeroFilter(double* In, double* Coef, size_t lengthInOut, - int orderCoef, double* Out); - -void WebRtcIsac_ZeroPoleFilter(double* In, double* ZeroCoef, double* PoleCoef, - size_t lengthInOut, int orderCoef, double* Out); - - /***************************** filterbank functions **************************/ -void WebRtcIsac_SplitAndFilterFloat(float* in, float* LP, float* HP, - double* LP_la, double* HP_la, - PreFiltBankstr* prefiltdata); - - -void WebRtcIsac_FilterAndCombineFloat(float* InLP, float* InHP, float* Out, +void WebRtcIsac_FilterAndCombineFloat(float* InLP, + float* InHP, + float* Out, PostFiltBankstr* postfiltdata); - /************************* normalized lattice filters ************************/ -void WebRtcIsac_NormLatticeFilterMa(int orderCoef, float* stateF, float* stateG, - float* lat_in, double* filtcoeflo, +void WebRtcIsac_NormLatticeFilterMa(int orderCoef, + float* stateF, + float* stateG, + float* lat_in, + double* filtcoeflo, double* lat_out); -void WebRtcIsac_NormLatticeFilterAr(int orderCoef, float* stateF, float* stateG, - double* lat_in, double* lo_filt_coef, +void WebRtcIsac_NormLatticeFilterAr(int orderCoef, + float* stateF, + float* stateG, + double* lat_in, + double* lo_filt_coef, float* lat_out); void WebRtcIsac_Dir2Lat(double* a, int orderCoef, float* sth, float* cth); -void WebRtcIsac_AutoCorr(double* r, const double* x, size_t N, size_t order); - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CODEC_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CODEC_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/crc.c b/webrtc/modules/audio_coding/codecs/isac/main/source/crc.c index ebef595..1bb0827 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/crc.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/crc.c @@ -8,9 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "crc.h" #include -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" + +#include "modules/audio_coding/codecs/isac/main/source/crc.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" #define POLYNOMIAL 0x04c11db7L diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/crc.h b/webrtc/modules/audio_coding/codecs/isac/main/source/crc.h index 09583df..f031019 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/crc.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/crc.h @@ -15,10 +15,10 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CRC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CRC_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CRC_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CRC_H_ -#include "webrtc/typedefs.h" +#include /**************************************************************************** * WebRtcIsac_GetCrc(...) @@ -36,11 +36,6 @@ * -1 - Error */ -int WebRtcIsac_GetCrc( - const int16_t* encoded, - int no_of_word8s, - uint32_t* crc); +int WebRtcIsac_GetCrc(const int16_t* encoded, int no_of_word8s, uint32_t* crc); - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CRC_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CRC_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/decode.c b/webrtc/modules/audio_coding/codecs/isac/main/source/decode.c index e925efb..6e114e4 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/decode.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/decode.c @@ -18,18 +18,17 @@ * */ - -#include "codec.h" -#include "entropy_coding.h" -#include "pitch_estimator.h" -#include "bandwidth_estimator.h" -#include "structs.h" -#include "settings.h" - #include #include #include +#include "modules/audio_coding/codecs/isac/main/source/codec.h" +#include "modules/audio_coding/codecs/isac/main/source/entropy_coding.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_filter.h" /* * function to decode the bitstream diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/decode_bwe.c b/webrtc/modules/audio_coding/codecs/isac/main/source/decode_bwe.c index 019cc89..89d970f 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/decode_bwe.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/decode_bwe.c @@ -8,10 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "structs.h" -#include "bandwidth_estimator.h" -#include "entropy_coding.h" -#include "codec.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" +#include "modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/entropy_coding.h" +#include "modules/audio_coding/codecs/isac/main/source/codec.h" int diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/encode.c b/webrtc/modules/audio_coding/codecs/isac/main/source/encode.c index 3f1912b..bf92d02 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/encode.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/encode.c @@ -21,20 +21,22 @@ #include #include -#include "structs.h" -#include "codec.h" -#include "pitch_estimator.h" -#include "entropy_coding.h" -#include "arith_routines.h" -#include "pitch_gain_tables.h" -#include "pitch_lag_tables.h" -#include "spectrum_ar_model_tables.h" -#include "lpc_tables.h" -#include "lpc_analysis.h" -#include "bandwidth_estimator.h" -#include "lpc_shape_swb12_tables.h" -#include "lpc_shape_swb16_tables.h" -#include "lpc_gain_swb_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" +#include "modules/audio_coding/codecs/isac/main/source/codec.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/entropy_coding.h" +#include "modules/audio_coding/codecs/isac/main/source/arith_routines.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_analysis.h" +#include "modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/isac_vad.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_filter.h" #define UB_LOOKAHEAD 24 diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c b/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c index 12a263d..7b02e64 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c @@ -16,17 +16,15 @@ * */ -#include "encode_lpc_swb.h" - #include #include #include -#include "lpc_gain_swb_tables.h" -#include "lpc_shape_swb12_tables.h" -#include "lpc_shape_swb16_tables.h" -#include "settings.h" -#include "webrtc/typedefs.h" +#include "modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" /****************************************************************************** * WebRtcIsac_RemoveLarMean() diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h b/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h index 3dd2311..8bc3d75 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h @@ -16,12 +16,11 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENCODE_LPC_SWB_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENCODE_LPC_SWB_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENCODE_LPC_SWB_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENCODE_LPC_SWB_H_ -#include "settings.h" -#include "structs.h" -#include "webrtc/typedefs.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" /****************************************************************************** * WebRtcIsac_RemoveLarMean() @@ -39,9 +38,7 @@ * * */ -int16_t WebRtcIsac_RemoveLarMean( - double* lar, - int16_t bandwidth); +int16_t WebRtcIsac_RemoveLarMean(double* lar, int16_t bandwidth); /****************************************************************************** * WebRtcIsac_DecorrelateIntraVec() @@ -59,11 +56,9 @@ int16_t WebRtcIsac_RemoveLarMean( * Output: * -out : decorrelated LAR vectors. */ -int16_t WebRtcIsac_DecorrelateIntraVec( - const double* inLAR, - double* out, - int16_t bandwidth); - +int16_t WebRtcIsac_DecorrelateIntraVec(const double* inLAR, + double* out, + int16_t bandwidth); /****************************************************************************** * WebRtcIsac_DecorrelateInterVec() @@ -82,11 +77,9 @@ int16_t WebRtcIsac_DecorrelateIntraVec( * Output: * -out : decorrelated LAR vectors. */ -int16_t WebRtcIsac_DecorrelateInterVec( - const double* data, - double* out, - int16_t bandwidth); - +int16_t WebRtcIsac_DecorrelateInterVec(const double* data, + double* out, + int16_t bandwidth); /****************************************************************************** * WebRtcIsac_QuantizeUncorrLar() @@ -102,11 +95,7 @@ int16_t WebRtcIsac_DecorrelateInterVec( * -data : quantized version of the input. * -idx : pointer to quantization indices. */ -double WebRtcIsac_QuantizeUncorrLar( - double* data, - int* idx, - int16_t bandwidth); - +double WebRtcIsac_QuantizeUncorrLar(double* data, int* idx, int16_t bandwidth); /****************************************************************************** * WebRtcIsac_CorrelateIntraVec() @@ -121,11 +110,9 @@ double WebRtcIsac_QuantizeUncorrLar( * Output: * -out : correlated parametrs. */ -int16_t WebRtcIsac_CorrelateIntraVec( - const double* data, - double* out, - int16_t bandwidth); - +int16_t WebRtcIsac_CorrelateIntraVec(const double* data, + double* out, + int16_t bandwidth); /****************************************************************************** * WebRtcIsac_CorrelateInterVec() @@ -140,17 +127,15 @@ int16_t WebRtcIsac_CorrelateIntraVec( * Output: * -out : correlated parametrs. */ -int16_t WebRtcIsac_CorrelateInterVec( - const double* data, - double* out, - int16_t bandwidth); - +int16_t WebRtcIsac_CorrelateInterVec(const double* data, + double* out, + int16_t bandwidth); /****************************************************************************** * WebRtcIsac_AddLarMean() * * This is the inverse of WebRtcIsac_RemoveLarMean() - * + * * Input: * -data : pointer to mean-removed LAR:s. * -bandwidth : indicates if the given LAR vectors belong @@ -159,10 +144,7 @@ int16_t WebRtcIsac_CorrelateInterVec( * Output: * -data : pointer to LARs. */ -int16_t WebRtcIsac_AddLarMean( - double* data, - int16_t bandwidth); - +int16_t WebRtcIsac_AddLarMean(double* data, int16_t bandwidth); /****************************************************************************** * WebRtcIsac_DequantizeLpcParam() @@ -177,11 +159,9 @@ int16_t WebRtcIsac_AddLarMean( * Output: * -out : pointer to quantized values. */ -int16_t WebRtcIsac_DequantizeLpcParam( - const int* idx, - double* out, - int16_t bandwidth); - +int16_t WebRtcIsac_DequantizeLpcParam(const int* idx, + double* out, + int16_t bandwidth); /****************************************************************************** * WebRtcIsac_ToLogDomainRemoveMean() @@ -194,9 +174,7 @@ int16_t WebRtcIsac_DequantizeLpcParam( * Output: * -lpcGain : mean-removed in log domain. */ -int16_t WebRtcIsac_ToLogDomainRemoveMean( - double* lpGains); - +int16_t WebRtcIsac_ToLogDomainRemoveMean(double* lpGains); /****************************************************************************** * WebRtcIsac_DecorrelateLPGain() @@ -210,16 +188,13 @@ int16_t WebRtcIsac_ToLogDomainRemoveMean( * Output: * -out : decorrelated parameters. */ -int16_t WebRtcIsac_DecorrelateLPGain( - const double* data, - double* out); - +int16_t WebRtcIsac_DecorrelateLPGain(const double* data, double* out); /****************************************************************************** * WebRtcIsac_QuantizeLpcGain() * * Quantize the decorrelated log-domain gains. - * + * * Input: * -lpcGain : uncorrelated LPC gains. * @@ -227,10 +202,7 @@ int16_t WebRtcIsac_DecorrelateLPGain( * -idx : quantization indices * -lpcGain : quantized value of the inpt. */ -double WebRtcIsac_QuantizeLpcGain( - double* lpGains, - int* idx); - +double WebRtcIsac_QuantizeLpcGain(double* lpGains, int* idx); /****************************************************************************** * WebRtcIsac_DequantizeLpcGain() @@ -243,10 +215,7 @@ double WebRtcIsac_QuantizeLpcGain( * Output: * -lpcGains : quantized values of the given parametes. */ -int16_t WebRtcIsac_DequantizeLpcGain( - const int* idx, - double* lpGains); - +int16_t WebRtcIsac_DequantizeLpcGain(const int* idx, double* lpGains); /****************************************************************************** * WebRtcIsac_CorrelateLpcGain() @@ -259,10 +228,7 @@ int16_t WebRtcIsac_DequantizeLpcGain( * Output: * -out : correlated parameters. */ -int16_t WebRtcIsac_CorrelateLpcGain( - const double* data, - double* out); - +int16_t WebRtcIsac_CorrelateLpcGain(const double* data, double* out); /****************************************************************************** * WebRtcIsac_AddMeanToLinearDomain() @@ -275,8 +241,6 @@ int16_t WebRtcIsac_CorrelateLpcGain( * Output: * -lpcGain : LPC gain in normal domain. */ -int16_t WebRtcIsac_AddMeanToLinearDomain( - double* lpcGains); +int16_t WebRtcIsac_AddMeanToLinearDomain(double* lpcGains); - -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENCODE_LPC_SWB_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENCODE_LPC_SWB_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.c b/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.c index bc65396..6692a51 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.c @@ -17,19 +17,19 @@ */ -#include "entropy_coding.h" -#include "settings.h" -#include "arith_routines.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "spectrum_ar_model_tables.h" -#include "lpc_tables.h" -#include "pitch_gain_tables.h" -#include "pitch_lag_tables.h" -#include "encode_lpc_swb.h" -#include "lpc_shape_swb12_tables.h" -#include "lpc_shape_swb16_tables.h" -#include "lpc_gain_swb_tables.h" -#include "os_specific_inline.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "modules/audio_coding/codecs/isac/main/source/entropy_coding.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/source/arith_routines.h" +#include "modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/os_specific_inline.h" #include #include @@ -42,7 +42,7 @@ static const uint16_t kOneBitEqualProbCdf[3] = { 0, 32768, 65535 }; /* Pointer to cdf array for encoder bandwidth (12 vs 16 kHz) indicator. */ -static const uint16_t* kOneBitEqualProbCdf_ptr[1] = { +static const uint16_t* const kOneBitEqualProbCdf_ptr[1] = { kOneBitEqualProbCdf }; /* @@ -96,7 +96,7 @@ static void FindInvArSpec(const int16_t* ARCoefQ12, const int32_t gainQ10, int32_t* CurveQ16) { int32_t CorrQ11[AR_ORDER + 1]; - int32_t sum, tmpGain; + int64_t sum, tmpGain; int32_t diffQ16[FRAMESAMPLES / 8]; const int16_t* CS_ptrQ9; int k, n; @@ -162,9 +162,9 @@ static void FindInvArSpec(const int16_t* ARCoefQ12, } for (k = 0; k < FRAMESAMPLES / 8; k++) { - CurveQ16[FRAMESAMPLES_QUARTER - 1 - k] = CurveQ16[k] - - (diffQ16[k] << shftVal); - CurveQ16[k] += diffQ16[k] << shftVal; + int32_t diff_q16_shifted = (int32_t)((uint32_t)(diffQ16[k]) << shftVal); + CurveQ16[FRAMESAMPLES_QUARTER - 1 - k] = CurveQ16[k] - diff_q16_shifted; + CurveQ16[k] += diff_q16_shifted; } } @@ -182,13 +182,13 @@ static void GenerateDitherQ7Lb(int16_t* bufQ7, uint32_t seed, /* Fixed-point dither sample between -64 and 64 (Q7). */ /* dither = seed * 128 / 4294967295 */ - dither1_Q7 = (int16_t)(((int)seed + 16777216) >> 25); + dither1_Q7 = (int16_t)(((int32_t)(seed + 16777216)) >> 25); /* New random unsigned int. */ seed = (seed * 196314165) + 907633515; /* Fixed-point dither sample between -64 and 64. */ - dither2_Q7 = (int16_t)(((int)seed + 16777216) >> 25); + dither2_Q7 = (int16_t)(((int32_t)(seed + 16777216)) >> 25); shft = (seed >> 25) & 15; if (shft < 5) { @@ -214,7 +214,7 @@ static void GenerateDitherQ7Lb(int16_t* bufQ7, uint32_t seed, seed = (seed * 196314165) + 907633515; /* Fixed-point dither sample between -64 and 64. */ - dither1_Q7 = (int16_t)(((int)seed + 16777216) >> 25); + dither1_Q7 = (int16_t)(((int32_t)(seed + 16777216)) >> 25); /* Dither sample is placed in either even or odd index. */ shft = (seed >> 25) & 1; /* Either 0 or 1 */ @@ -254,7 +254,7 @@ static void GenerateDitherQ7LbUB( /* Fixed-point dither sample between -64 and 64 (Q7). */ /* bufQ7 = seed * 128 / 4294967295 */ - bufQ7[k] = (int16_t)(((int)seed + 16777216) >> 25); + bufQ7[k] = (int16_t)(((int32_t)(seed + 16777216)) >> 25); /* Scale by 0.35. */ bufQ7[k] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT(bufQ7[k], 2048, 13); @@ -1843,7 +1843,7 @@ static const uint16_t kBwCdf[25] = { 62804, 65535 }; /* pointer to cdf array for estimated bandwidth */ -static const uint16_t* kBwCdfPtr[1] = { kBwCdf }; +static const uint16_t* const kBwCdfPtr[1] = { kBwCdf }; /* initial cdf index for decoder of estimated bandwidth*/ static const uint16_t kBwInitIndex[1] = { 7 }; diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.h b/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.h index d715d86..6c2b8d3 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.h @@ -16,11 +16,11 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENTROPY_CODING_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENTROPY_CODING_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENTROPY_CODING_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENTROPY_CODING_H_ -#include "settings.h" -#include "structs.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" /****************************************************************************** * WebRtcIsac_DecodeSpec() @@ -46,8 +46,11 @@ * Return value : < 0 if an error occures * 0 if succeeded. */ -int WebRtcIsac_DecodeSpec(Bitstr* streamdata, int16_t AvgPitchGain_Q12, - enum ISACBand band, double* fr, double* fi); +int WebRtcIsac_DecodeSpec(Bitstr* streamdata, + int16_t AvgPitchGain_Q12, + enum ISACBand band, + double* fr, + double* fi); /****************************************************************************** * WebRtcIsac_EncodeSpec() @@ -72,24 +75,31 @@ int WebRtcIsac_DecodeSpec(Bitstr* streamdata, int16_t AvgPitchGain_Q12, * Return value : < 0 if an error occures * 0 if succeeded. */ -int WebRtcIsac_EncodeSpec(const int16_t* fr, const int16_t* fi, - int16_t AvgPitchGain_Q12, enum ISACBand band, +int WebRtcIsac_EncodeSpec(const int16_t* fr, + const int16_t* fi, + int16_t AvgPitchGain_Q12, + enum ISACBand band, Bitstr* streamdata); /* decode & dequantize LPC Coef */ int WebRtcIsac_DecodeLpcCoef(Bitstr* streamdata, double* LPCCoef); -int WebRtcIsac_DecodeLpcCoefUB(Bitstr* streamdata, double* lpcVecs, +int WebRtcIsac_DecodeLpcCoefUB(Bitstr* streamdata, + double* lpcVecs, double* percepFilterGains, int16_t bandwidth); -int WebRtcIsac_DecodeLpc(Bitstr* streamdata, double* LPCCoef_lo, +int WebRtcIsac_DecodeLpc(Bitstr* streamdata, + double* LPCCoef_lo, double* LPCCoef_hi); /* quantize & code LPC Coef */ -void WebRtcIsac_EncodeLpcLb(double* LPCCoef_lo, double* LPCCoef_hi, - Bitstr* streamdata, IsacSaveEncoderData* encData); +void WebRtcIsac_EncodeLpcLb(double* LPCCoef_lo, + double* LPCCoef_hi, + Bitstr* streamdata, + IsacSaveEncoderData* encData); -void WebRtcIsac_EncodeLpcGainLb(double* LPCCoef_lo, double* LPCCoef_hi, +void WebRtcIsac_EncodeLpcGainLb(double* LPCCoef_lo, + double* LPCCoef_hi, Bitstr* streamdata, IsacSaveEncoderData* encData); @@ -126,7 +136,8 @@ void WebRtcIsac_EncodeLpcGainLb(double* LPCCoef_lo, double* LPCCoef_hi, * Return value : 0 if encoding is successful, * <0 if failed to encode. */ -int16_t WebRtcIsac_EncodeLpcUB(double* lpcCoeff, Bitstr* streamdata, +int16_t WebRtcIsac_EncodeLpcUB(double* lpcCoeff, + Bitstr* streamdata, double* interpolLPCCoeff, int16_t bandwidth, ISACUBSaveEncDataStruct* encData); @@ -184,9 +195,9 @@ void WebRtcIsac_EncodePitchLag(double* PitchLags, Bitstr* streamdata, IsacSaveEncoderData* encData); -int WebRtcIsac_DecodePitchGain(Bitstr* streamdata, - int16_t* PitchGain_Q12); -int WebRtcIsac_DecodePitchLag(Bitstr* streamdata, int16_t* PitchGain_Q12, +int WebRtcIsac_DecodePitchGain(Bitstr* streamdata, int16_t* PitchGain_Q12); +int WebRtcIsac_DecodePitchLag(Bitstr* streamdata, + int16_t* PitchGain_Q12, double* PitchLag); int WebRtcIsac_DecodeFrameLen(Bitstr* streamdata, int16_t* framelength); @@ -200,10 +211,10 @@ void WebRtcIsac_Poly2Rc(double* a, int N, double* RC); /* Step-up */ void WebRtcIsac_Rc2Poly(double* RC, int N, double* a); -void WebRtcIsac_TranscodeLPCCoef(double* LPCCoef_lo, double* LPCCoef_hi, +void WebRtcIsac_TranscodeLPCCoef(double* LPCCoef_lo, + double* LPCCoef_hi, int* index_g); - /****************************************************************************** * WebRtcIsac_EncodeLpcGainUb() * Encode LPC gains of sub-Frames. @@ -220,10 +231,10 @@ void WebRtcIsac_TranscodeLPCCoef(double* LPCCoef_lo, double* LPCCoef_hi, * - lpcGainIndex : quantization indices for lpc gains, these will * be stored to be used for FEC. */ -void WebRtcIsac_EncodeLpcGainUb(double* lpGains, Bitstr* streamdata, +void WebRtcIsac_EncodeLpcGainUb(double* lpGains, + Bitstr* streamdata, int* lpcGainIndex); - /****************************************************************************** * WebRtcIsac_EncodeLpcGainUb() * Store LPC gains of sub-Frames in 'streamdata'. @@ -239,7 +250,6 @@ void WebRtcIsac_EncodeLpcGainUb(double* lpGains, Bitstr* streamdata, */ void WebRtcIsac_StoreLpcGainUb(double* lpGains, Bitstr* streamdata); - /****************************************************************************** * WebRtcIsac_DecodeLpcGainUb() * Decode the LPC gain of sub-frames. @@ -257,7 +267,6 @@ void WebRtcIsac_StoreLpcGainUb(double* lpGains, Bitstr* streamdata); */ int16_t WebRtcIsac_DecodeLpcGainUb(double* lpGains, Bitstr* streamdata); - /****************************************************************************** * WebRtcIsac_EncodeBandwidth() * Encode if the bandwidth of encoded audio is 0-12 kHz or 0-16 kHz. @@ -277,7 +286,6 @@ int16_t WebRtcIsac_DecodeLpcGainUb(double* lpGains, Bitstr* streamdata); int16_t WebRtcIsac_EncodeBandwidth(enum ISACBandwidth bandwidth, Bitstr* streamData); - /****************************************************************************** * WebRtcIsac_DecodeBandwidth() * Decode the bandwidth of the encoded audio, i.e. if the bandwidth is 0-12 kHz @@ -298,7 +306,6 @@ int16_t WebRtcIsac_EncodeBandwidth(enum ISACBandwidth bandwidth, int16_t WebRtcIsac_DecodeBandwidth(Bitstr* streamData, enum ISACBandwidth* bandwidth); - /****************************************************************************** * WebRtcIsac_EncodeJitterInfo() * Decode the jitter information. @@ -316,9 +323,7 @@ int16_t WebRtcIsac_DecodeBandwidth(Bitstr* streamData, * Return value : 0 if succeeded. * <0 if failed. */ -int16_t WebRtcIsac_EncodeJitterInfo(int32_t jitterIndex, - Bitstr* streamData); - +int16_t WebRtcIsac_EncodeJitterInfo(int32_t jitterIndex, Bitstr* streamData); /****************************************************************************** * WebRtcIsac_DecodeJitterInfo() @@ -337,7 +342,6 @@ int16_t WebRtcIsac_EncodeJitterInfo(int32_t jitterIndex, * Return value : 0 if succeeded. * <0 if failed. */ -int16_t WebRtcIsac_DecodeJitterInfo(Bitstr* streamData, - int32_t* jitterInfo); +int16_t WebRtcIsac_DecodeJitterInfo(Bitstr* streamData, int32_t* jitterInfo); -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENTROPY_CODING_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENTROPY_CODING_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c b/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c index d47eb1f..a4f297c 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/filter_functions.c @@ -13,16 +13,14 @@ #ifdef WEBRTC_ANDROID #include #endif -#include "pitch_estimator.h" -#include "lpc_analysis.h" -#include "codec.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/isac_vad.h" - -void WebRtcIsac_AllPoleFilter(double* InOut, - double* Coef, - size_t lengthInOut, - int orderCoef) { +static void WebRtcIsac_AllPoleFilter(double* InOut, + double* Coef, + size_t lengthInOut, + int orderCoef) { /* the state of filter is assumed to be in InOut[-1] to InOut[-orderCoef] */ double scal; double sum; @@ -55,12 +53,11 @@ void WebRtcIsac_AllPoleFilter(double* InOut, } } - -void WebRtcIsac_AllZeroFilter(double* In, - double* Coef, - size_t lengthInOut, - int orderCoef, - double* Out) { +static void WebRtcIsac_AllZeroFilter(double* In, + double* Coef, + size_t lengthInOut, + int orderCoef, + double* Out) { /* the state of filter is assumed to be in In[-1] to In[-orderCoef] */ size_t n; @@ -80,13 +77,12 @@ void WebRtcIsac_AllZeroFilter(double* In, } } - -void WebRtcIsac_ZeroPoleFilter(double* In, - double* ZeroCoef, - double* PoleCoef, - size_t lengthInOut, - int orderCoef, - double* Out) { +static void WebRtcIsac_ZeroPoleFilter(double* In, + double* ZeroCoef, + double* PoleCoef, + size_t lengthInOut, + int orderCoef, + double* Out) { /* the state of the zero section is assumed to be in In[-1] to In[-orderCoef] */ /* the state of the pole section is assumed to be in Out[-1] to Out[-orderCoef] */ @@ -115,8 +111,10 @@ void WebRtcIsac_AutoCorr(double* r, const double* x, size_t N, size_t order) { } - -void WebRtcIsac_BwExpand(double* out, double* in, double coef, size_t length) { +static void WebRtcIsac_BwExpand(double* out, + double* in, + double coef, + size_t length) { size_t i; double chirp; @@ -195,69 +193,3 @@ void WebRtcIsac_WeightingFilter(const double* in, memcpy(weiout, weoutbuf+PITCH_WLPCORDER, sizeof(double) * PITCH_FRAME_LEN); memcpy(whiout, whoutbuf+PITCH_WLPCORDER, sizeof(double) * PITCH_FRAME_LEN); } - - -static const double APupper[ALLPASSSECTIONS] = {0.0347, 0.3826}; -static const double APlower[ALLPASSSECTIONS] = {0.1544, 0.744}; - - -void WebRtcIsac_AllpassFilterForDec(double* InOut, - const double* APSectionFactors, - size_t lengthInOut, - double* FilterState) { - //This performs all-pass filtering--a series of first order all-pass sections are used - //to filter the input in a cascade manner. - size_t n,j; - double temp; - for (j=0; jINLABUFx arrays - each of length QLOOKAHEAD. - The remaining FRAMESAMPLES_HALF-QLOOKAHEAD samples are based - on the first FRAMESAMPLES_HALF-QLOOKAHEAD samples of the input - array in[]. - HP: a FRAMESAMPLES_HALF array of high-pass filtered samples that - have been phase equalized. The first QLOOKAHEAD samples are - based on the samples in the two prefiltdata->INLABUFx arrays - each of length QLOOKAHEAD. - The remaining FRAMESAMPLES_HALF-QLOOKAHEAD samples are based - on the first FRAMESAMPLES_HALF-QLOOKAHEAD samples of the input - array in[]. - - LP_la: a FRAMESAMPLES_HALF array of low-pass filtered samples. - These samples are not phase equalized. They are computed - from the samples in the in[] array. - HP_la: a FRAMESAMPLES_HALF array of high-pass filtered samples - that are not phase equalized. They are computed from - the in[] vector. - prefiltdata: this input data structure's filterbank state and - lookahead sample buffers are updated for the next - encoding iteration. -*/ -void WebRtcIsac_SplitAndFilterFloat(float *pin, float *LP, float *HP, - double *LP_la, double *HP_la, - PreFiltBankstr *prefiltdata) -{ - int k,n; - float CompositeAPFilterState[NUMBEROFCOMPOSITEAPSECTIONS]; - float ForTransform_CompositeAPFilterState[NUMBEROFCOMPOSITEAPSECTIONS]; - float ForTransform_CompositeAPFilterState2[NUMBEROFCOMPOSITEAPSECTIONS]; - float tempinoutvec[FRAMESAMPLES+MAX_AR_MODEL_ORDER]; - float tempin_ch1[FRAMESAMPLES+MAX_AR_MODEL_ORDER]; - float tempin_ch2[FRAMESAMPLES+MAX_AR_MODEL_ORDER]; - float in[FRAMESAMPLES]; - float ftmp; - - - /* High pass filter */ - - for (k=0;kHPstates_float[0] + - kHpStCoefInFloat[3] * prefiltdata->HPstates_float[1]; - ftmp = pin[k] - kHpStCoefInFloat[0] * prefiltdata->HPstates_float[0] - - kHpStCoefInFloat[1] * prefiltdata->HPstates_float[1]; - prefiltdata->HPstates_float[1] = prefiltdata->HPstates_float[0]; - prefiltdata->HPstates_float[0] = ftmp; - } - - /* - % backwards all-pass filtering to obtain zero-phase - [tmp1(N2+LA:-1:LA+1, 1), state1] = filter(Q.coef, Q.coef(end:-1:1), in(N:-2:2)); - tmp1(LA:-1:1) = filter(Q.coef, Q.coef(end:-1:1), Q.LookAheadBuf1, state1); - Q.LookAheadBuf1 = in(N:-2:N-2*LA+2); - */ - /*Backwards all-pass filter the odd samples of the input (upper channel) - to eventually obtain zero phase. The composite all-pass filter (comprised of both - the upper and lower channel all-pass filsters in series) is used for the - filtering. */ - - /* First Channel */ - - /*initial state of composite filter is zero */ - for (k=0;kINLABUF1_float, - WebRtcIsac_kCompositeApFactorsFloat, QLOOKAHEAD, - NUMBEROFCOMPOSITEAPSECTIONS, CompositeAPFilterState); - - /* save the output, but write it in forward order */ - /* write the lookahead samples for the next encoding iteration. Every other - sample at the end of the input frame is written in reverse order for the - lookahead length. Exported in the prefiltdata structure. */ - for (k=0;kINLABUF1_float[k]; - prefiltdata->INLABUF1_float[k]=in[FRAMESAMPLES-1-2*k]; - } - - /* Second Channel. This is exactly like the first channel, except that the - even samples are now filtered instead (lower channel). */ - for (k=0;kINLABUF2_float, - WebRtcIsac_kCompositeApFactorsFloat, QLOOKAHEAD,NUMBEROFCOMPOSITEAPSECTIONS, - CompositeAPFilterState); - - for (k=0;kINLABUF2_float[k]; - prefiltdata->INLABUF2_float[k]=in[FRAMESAMPLES-2-2*k]; - } - - /* Transform filter states from backward to forward */ - /*At this point, each of the states of the backwards composite filters for the - two channels are transformed into forward filtering states for the corresponding - forward channel filters. Each channel's forward filtering state from the previous - encoding iteration is added to the transformed state to get a proper forward state */ - - /* So the existing NUMBEROFCOMPOSITEAPSECTIONS x 1 (4x1) state vector is multiplied by a - NUMBEROFCHANNELAPSECTIONSxNUMBEROFCOMPOSITEAPSECTIONS (2x4) transform matrix to get the - new state that is added to the previous 2x1 input state */ - - for (k=0;kINSTAT1_float[k] += ForTransform_CompositeAPFilterState[n]* - WebRtcIsac_kTransform1Float[k*NUMBEROFCHANNELAPSECTIONS+n]; - prefiltdata->INSTAT2_float[k] += ForTransform_CompositeAPFilterState2[n]* - WebRtcIsac_kTransform2Float[k*NUMBEROFCHANNELAPSECTIONS+n]; - } - } - - /*obtain polyphase components by forward all-pass filtering through each channel */ - /* the backward filtered samples are now forward filtered with the corresponding channel filters */ - /* The all pass filtering automatically updates the filter states which are exported in the - prefiltdata structure */ - WebRtcIsac_AllPassFilter2Float(tempin_ch1,WebRtcIsac_kUpperApFactorsFloat, - FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, prefiltdata->INSTAT1_float); - WebRtcIsac_AllPassFilter2Float(tempin_ch2,WebRtcIsac_kLowerApFactorsFloat, - FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, prefiltdata->INSTAT2_float); - - /* Now Construct low-pass and high-pass signals as combinations of polyphase components */ - for (k=0; kINSTATLA1_float); - WebRtcIsac_AllPassFilter2Float(tempin_ch2,WebRtcIsac_kLowerApFactorsFloat, - FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, prefiltdata->INSTATLA2_float); - - for (k=0; k +#include "modules/audio_coding/codecs/isac/main/source/structs.h" +#include "modules/audio_coding/codecs/isac/main/source/codec.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" + void WebRtcIsac_InitMasking(MaskFiltstr *maskdata) { int k; @@ -43,39 +43,6 @@ void WebRtcIsac_InitMasking(MaskFiltstr *maskdata) { return; } -void WebRtcIsac_InitPreFilterbank(PreFiltBankstr *prefiltdata) -{ - int k; - - for (k = 0; k < QLOOKAHEAD; k++) { - prefiltdata->INLABUF1[k] = 0; - prefiltdata->INLABUF2[k] = 0; - - prefiltdata->INLABUF1_float[k] = 0; - prefiltdata->INLABUF2_float[k] = 0; - } - for (k = 0; k < 2*(QORDER-1); k++) { - prefiltdata->INSTAT1[k] = 0; - prefiltdata->INSTAT2[k] = 0; - prefiltdata->INSTATLA1[k] = 0; - prefiltdata->INSTATLA2[k] = 0; - - prefiltdata->INSTAT1_float[k] = 0; - prefiltdata->INSTAT2_float[k] = 0; - prefiltdata->INSTATLA1_float[k] = 0; - prefiltdata->INSTATLA2_float[k] = 0; - } - - /* High pass filter states */ - prefiltdata->HPstates[0] = 0.0; - prefiltdata->HPstates[1] = 0.0; - - prefiltdata->HPstates_float[0] = 0.0f; - prefiltdata->HPstates_float[1] = 0.0f; - - return; -} - void WebRtcIsac_InitPostFilterbank(PostFiltBankstr *postfiltdata) { int k; @@ -103,69 +70,3 @@ void WebRtcIsac_InitPostFilterbank(PostFiltBankstr *postfiltdata) return; } - - -void WebRtcIsac_InitPitchFilter(PitchFiltstr *pitchfiltdata) -{ - int k; - - for (k = 0; k < PITCH_BUFFSIZE; k++) { - pitchfiltdata->ubuf[k] = 0.0; - } - pitchfiltdata->ystate[0] = 0.0; - for (k = 1; k < (PITCH_DAMPORDER); k++) { - pitchfiltdata->ystate[k] = 0.0; - } - pitchfiltdata->oldlagp[0] = 50.0; - pitchfiltdata->oldgainp[0] = 0.0; -} - -void WebRtcIsac_InitWeightingFilter(WeightFiltstr *wfdata) -{ - int k; - double t, dtmp, dtmp2, denum, denum2; - - for (k=0;kbuffer[k]=0.0; - - for (k=0;kistate[k]=0.0; - wfdata->weostate[k]=0.0; - wfdata->whostate[k]=0.0; - } - - /* next part should be in Matlab, writing to a global table */ - t = 0.5; - denum = 1.0 / ((double) PITCH_WLPCWINLEN); - denum2 = denum * denum; - for (k=0;kwindow[k] = dtmp2 * dtmp2; - t++; - } -} - -/* clear all buffers */ -void WebRtcIsac_InitPitchAnalysis(PitchAnalysisStruct *State) -{ - int k; - - for (k = 0; k < PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2; k++) - State->dec_buffer[k] = 0.0; - for (k = 0; k < 2*ALLPASSSECTIONS+1; k++) - State->decimator_state[k] = 0.0; - for (k = 0; k < 2; k++) - State->hp_state[k] = 0.0; - for (k = 0; k < QLOOKAHEAD; k++) - State->whitened_buf[k] = 0.0; - for (k = 0; k < QLOOKAHEAD; k++) - State->inbuf[k] = 0.0; - - WebRtcIsac_InitPitchFilter(&(State->PFstr_wght)); - - WebRtcIsac_InitPitchFilter(&(State->PFstr)); - - WebRtcIsac_InitWeightingFilter(&(State->Wghtstr)); -} diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/isac.c b/webrtc/modules/audio_coding/codecs/isac/main/source/isac.c index 875e7ac..73f132c 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/isac.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/isac.c @@ -15,22 +15,24 @@ * */ -#include "webrtc/modules/audio_coding/codecs/isac/main/include/isac.h" +#include "modules/audio_coding/codecs/isac/main/include/isac.h" -#include #include #include #include #include -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/codec.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/crc.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/entropy_coding.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/structs.h" +#include "rtc_base/checks.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/codec.h" +#include "modules/audio_coding/codecs/isac/main/source/crc.h" +#include "modules/audio_coding/codecs/isac/main/source/entropy_coding.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/os_specific_inline.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" +#include "modules/audio_coding/codecs/isac/main/source/isac_vad.h" +#include "rtc_base/system/arch.h" #define BIT_MASK_DEC_INIT 0x0001 #define BIT_MASK_ENC_INIT 0x0002 @@ -204,62 +206,6 @@ static void GetSendBandwidthInfo(ISACMainStruct* instISAC, } -/**************************************************************************** - * WebRtcIsac_AssignSize(...) - * - * This function returns the size of the ISAC instance, so that the instance - * can be created out side iSAC. - * - * Output: - * - sizeinbytes : number of bytes needed to allocate for the - * instance. - * - * Return value : 0 - Ok - * -1 - Error - */ -int16_t WebRtcIsac_AssignSize(int* sizeInBytes) { - *sizeInBytes = sizeof(ISACMainStruct) * 2 / sizeof(int16_t); - return 0; -} - - -/**************************************************************************** - * WebRtcIsac_Assign(...) - * - * This function assigns the memory already created to the ISAC instance. - * - * Input: - * - ISAC_main_inst : address of the pointer to the coder instance. - * - instISAC_Addr : the already allocated memory, where we put the - * iSAC structure. - * - * Return value : 0 - Ok - * -1 - Error - */ -int16_t WebRtcIsac_Assign(ISACStruct** ISAC_main_inst, - void* instISAC_Addr) { - if (instISAC_Addr != NULL) { - ISACMainStruct* instISAC = (ISACMainStruct*)instISAC_Addr; - instISAC->errorCode = 0; - instISAC->initFlag = 0; - - /* Assign the address. */ - *ISAC_main_inst = (ISACStruct*)instISAC_Addr; - - /* Default is wideband. */ - instISAC->encoderSamplingRateKHz = kIsacWideband; - instISAC->decoderSamplingRateKHz = kIsacWideband; - instISAC->bandwidthKHz = isac8kHz; - instISAC->in_sample_rate_hz = 16000; - - WebRtcIsac_InitTransform(&instISAC->transform_tables); - return 0; - } else { - return -1; - } -} - - /**************************************************************************** * WebRtcIsac_Create(...) * @@ -1253,10 +1199,23 @@ static int Decode(ISACStruct* ISAC_main_inst, return -1; } + if (numDecodedBytesUB < 0) { + instISAC->errorCode = numDecodedBytesUB; + return -1; + } + if (numDecodedBytesLB + numDecodedBytesUB > lenEncodedBytes) { + // We have supposedly decoded more bytes than we were given. Likely + // caused by bad input data. + instISAC->errorCode = ISAC_LENGTH_MISMATCH; + return -1; + } + /* It might be less due to garbage. */ if ((numDecodedBytesUB != lenNextStream) && - (numDecodedBytesUB != (lenNextStream - - encoded[numDecodedBytesLB + 1 + numDecodedBytesUB]))) { + (numDecodedBytesLB + 1 + numDecodedBytesUB >= lenEncodedBytes || + numDecodedBytesUB != + (lenNextStream - + encoded[numDecodedBytesLB + 1 + numDecodedBytesUB]))) { instISAC->errorCode = ISAC_LENGTH_MISMATCH; return -1; } @@ -1539,8 +1498,8 @@ int16_t WebRtcIsac_Control(ISACStruct* ISAC_main_inst, void WebRtcIsac_SetInitialBweBottleneck(ISACStruct* ISAC_main_inst, int bottleneck_bits_per_second) { ISACMainStruct* instISAC = (ISACMainStruct*)ISAC_main_inst; - assert(bottleneck_bits_per_second >= 10000 && - bottleneck_bits_per_second <= 32000); + RTC_DCHECK_GE(bottleneck_bits_per_second, 10000); + RTC_DCHECK_LE(bottleneck_bits_per_second, 32000); instISAC->bwestimator_obj.send_bw_avg = (float)bottleneck_bits_per_second; } @@ -1760,7 +1719,7 @@ int16_t WebRtcIsac_ReadBwIndex(const uint8_t* encoded, * - frameLength : Length of frame in packet (in samples) * */ -int16_t WebRtcIsac_ReadFrameLen(ISACStruct* ISAC_main_inst, +int16_t WebRtcIsac_ReadFrameLen(const ISACStruct* ISAC_main_inst, const uint8_t* encoded, int16_t* frameLength) { Bitstr streamdata; @@ -2338,26 +2297,11 @@ uint16_t WebRtcIsac_DecSampRate(ISACStruct* ISAC_main_inst) { return instISAC->decoderSamplingRateKHz == kIsacWideband ? 16000 : 32000; } -void WebRtcIsac_GetBandwidthInfo(ISACStruct* inst, - IsacBandwidthInfo* bwinfo) { - ISACMainStruct* instISAC = (ISACMainStruct*)inst; - assert(instISAC->initFlag & BIT_MASK_DEC_INIT); - WebRtcIsacBw_GetBandwidthInfo(&instISAC->bwestimator_obj, - instISAC->decoderSamplingRateKHz, bwinfo); -} - -void WebRtcIsac_SetBandwidthInfo(ISACStruct* inst, - const IsacBandwidthInfo* bwinfo) { - ISACMainStruct* instISAC = (ISACMainStruct*)inst; - assert(instISAC->initFlag & BIT_MASK_ENC_INIT); - WebRtcIsacBw_SetBandwidthInfo(&instISAC->bwestimator_obj, bwinfo); -} - void WebRtcIsac_SetEncSampRateInDecoder(ISACStruct* inst, int sample_rate_hz) { ISACMainStruct* instISAC = (ISACMainStruct*)inst; - assert(instISAC->initFlag & BIT_MASK_DEC_INIT); - assert(!(instISAC->initFlag & BIT_MASK_ENC_INIT)); - assert(sample_rate_hz == 16000 || sample_rate_hz == 32000); + RTC_DCHECK_NE(0, instISAC->initFlag & BIT_MASK_DEC_INIT); + RTC_DCHECK(!(instISAC->initFlag & BIT_MASK_ENC_INIT)); + RTC_DCHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000); instISAC->encoderSamplingRateKHz = sample_rate_hz / 1000; } diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/isac_float_type.h b/webrtc/modules/audio_coding/codecs/isac/main/source/isac_float_type.h index e150d39..511bc97 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/isac_float_type.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/isac_float_type.h @@ -8,10 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_FLOAT_TYPE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_FLOAT_TYPE_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_FLOAT_TYPE_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_FLOAT_TYPE_H_ -#include "webrtc/modules/audio_coding/codecs/isac/main/include/isac.h" +#include "modules/audio_coding/codecs/isac/main/include/isac.h" namespace webrtc { @@ -64,10 +64,6 @@ struct IsacFloat { static inline int16_t Free(instance_type* inst) { return WebRtcIsac_Free(inst); } - static inline void GetBandwidthInfo(instance_type* inst, - IsacBandwidthInfo* bwinfo) { - WebRtcIsac_GetBandwidthInfo(inst, bwinfo); - } static inline int16_t GetErrorCode(instance_type* inst) { return WebRtcIsac_GetErrorCode(inst); } @@ -75,10 +71,6 @@ struct IsacFloat { static inline int16_t GetNewFrameLen(instance_type* inst) { return WebRtcIsac_GetNewFrameLen(inst); } - static inline void SetBandwidthInfo(instance_type* inst, - const IsacBandwidthInfo* bwinfo) { - WebRtcIsac_SetBandwidthInfo(inst, bwinfo); - } static inline int16_t SetDecSampRate(instance_type* inst, uint16_t sample_rate_hz) { return WebRtcIsac_SetDecSampRate(inst, sample_rate_hz); @@ -95,15 +87,6 @@ struct IsacFloat { int bottleneck_bits_per_second) { WebRtcIsac_SetInitialBweBottleneck(inst, bottleneck_bits_per_second); } - static inline int16_t UpdateBwEstimate(instance_type* inst, - const uint8_t* encoded, - size_t packet_size, - uint16_t rtp_seq_number, - uint32_t send_ts, - uint32_t arr_ts) { - return WebRtcIsac_UpdateBwEstimate(inst, encoded, packet_size, - rtp_seq_number, send_ts, arr_ts); - } static inline int16_t SetMaxPayloadSize(instance_type* inst, int16_t max_payload_size_bytes) { return WebRtcIsac_SetMaxPayloadSize(inst, max_payload_size_bytes); @@ -114,4 +97,4 @@ struct IsacFloat { }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_FLOAT_TYPE_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_FLOAT_TYPE_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.c b/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.c new file mode 100644 index 0000000..57cf0c3 --- /dev/null +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.c @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2018 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 "modules/audio_coding/codecs/isac/main/source/isac_vad.h" + +#include + +void WebRtcIsac_InitPitchFilter(PitchFiltstr* pitchfiltdata) { + int k; + + for (k = 0; k < PITCH_BUFFSIZE; k++) { + pitchfiltdata->ubuf[k] = 0.0; + } + pitchfiltdata->ystate[0] = 0.0; + for (k = 1; k < (PITCH_DAMPORDER); k++) { + pitchfiltdata->ystate[k] = 0.0; + } + pitchfiltdata->oldlagp[0] = 50.0; + pitchfiltdata->oldgainp[0] = 0.0; +} + +static void WebRtcIsac_InitWeightingFilter(WeightFiltstr* wfdata) { + int k; + double t, dtmp, dtmp2, denum, denum2; + + for (k = 0; k < PITCH_WLPCBUFLEN; k++) + wfdata->buffer[k] = 0.0; + + for (k = 0; k < PITCH_WLPCORDER; k++) { + wfdata->istate[k] = 0.0; + wfdata->weostate[k] = 0.0; + wfdata->whostate[k] = 0.0; + } + + /* next part should be in Matlab, writing to a global table */ + t = 0.5; + denum = 1.0 / ((double)PITCH_WLPCWINLEN); + denum2 = denum * denum; + for (k = 0; k < PITCH_WLPCWINLEN; k++) { + dtmp = PITCH_WLPCASYM * t * denum + (1 - PITCH_WLPCASYM) * t * t * denum2; + dtmp *= 3.14159265; + dtmp2 = sin(dtmp); + wfdata->window[k] = dtmp2 * dtmp2; + t++; + } +} + +void WebRtcIsac_InitPitchAnalysis(PitchAnalysisStruct* State) { + int k; + + for (k = 0; k < PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + PITCH_MAX_LAG / 2 - + PITCH_FRAME_LEN / 2 + 2; + k++) + State->dec_buffer[k] = 0.0; + for (k = 0; k < 2 * ALLPASSSECTIONS + 1; k++) + State->decimator_state[k] = 0.0; + for (k = 0; k < 2; k++) + State->hp_state[k] = 0.0; + for (k = 0; k < QLOOKAHEAD; k++) + State->whitened_buf[k] = 0.0; + for (k = 0; k < QLOOKAHEAD; k++) + State->inbuf[k] = 0.0; + + WebRtcIsac_InitPitchFilter(&(State->PFstr_wght)); + + WebRtcIsac_InitPitchFilter(&(State->PFstr)); + + WebRtcIsac_InitWeightingFilter(&(State->Wghtstr)); +} + +void WebRtcIsac_InitPreFilterbank(PreFiltBankstr* prefiltdata) { + int k; + + for (k = 0; k < QLOOKAHEAD; k++) { + prefiltdata->INLABUF1[k] = 0; + prefiltdata->INLABUF2[k] = 0; + + prefiltdata->INLABUF1_float[k] = 0; + prefiltdata->INLABUF2_float[k] = 0; + } + for (k = 0; k < 2 * (QORDER - 1); k++) { + prefiltdata->INSTAT1[k] = 0; + prefiltdata->INSTAT2[k] = 0; + prefiltdata->INSTATLA1[k] = 0; + prefiltdata->INSTATLA2[k] = 0; + + prefiltdata->INSTAT1_float[k] = 0; + prefiltdata->INSTAT2_float[k] = 0; + prefiltdata->INSTATLA1_float[k] = 0; + prefiltdata->INSTATLA2_float[k] = 0; + } + + /* High pass filter states */ + prefiltdata->HPstates[0] = 0.0; + prefiltdata->HPstates[1] = 0.0; + + prefiltdata->HPstates_float[0] = 0.0f; + prefiltdata->HPstates_float[1] = 0.0f; + + return; +} + +double WebRtcIsac_LevDurb(double* a, double* k, double* r, size_t order) { + const double LEVINSON_EPS = 1.0e-10; + + double sum, alpha; + size_t m, m_h, i; + alpha = 0; // warning -DH + a[0] = 1.0; + if (r[0] < LEVINSON_EPS) { /* if r[0] <= 0, set LPC coeff. to zero */ + for (i = 0; i < order; i++) { + k[i] = 0; + a[i + 1] = 0; + } + } else { + a[1] = k[0] = -r[1] / r[0]; + alpha = r[0] + r[1] * k[0]; + for (m = 1; m < order; m++) { + sum = r[m + 1]; + for (i = 0; i < m; i++) { + sum += a[i + 1] * r[m - i]; + } + k[m] = -sum / alpha; + alpha += k[m] * sum; + m_h = (m + 1) >> 1; + for (i = 0; i < m_h; i++) { + sum = a[i + 1] + k[m] * a[m - i]; + a[m - i] += k[m] * a[i + 1]; + a[i + 1] = sum; + } + a[m + 1] = k[m]; + } + } + return alpha; +} + +/* The upper channel all-pass filter factors */ +const float WebRtcIsac_kUpperApFactorsFloat[2] = {0.03470000000000f, + 0.38260000000000f}; + +/* The lower channel all-pass filter factors */ +const float WebRtcIsac_kLowerApFactorsFloat[2] = {0.15440000000000f, + 0.74400000000000f}; + +/* This function performs all-pass filtering--a series of first order all-pass + * sections are used to filter the input in a cascade manner. + * The input is overwritten!! + */ +void WebRtcIsac_AllPassFilter2Float(float* InOut, + const float* APSectionFactors, + int lengthInOut, + int NumberOfSections, + float* FilterState) { + int n, j; + float temp; + for (j = 0; j < NumberOfSections; j++) { + for (n = 0; n < lengthInOut; n++) { + temp = FilterState[j] + APSectionFactors[j] * InOut[n]; + FilterState[j] = -APSectionFactors[j] * temp + InOut[n]; + InOut[n] = temp; + } + } +} + +/* The number of composite all-pass filter factors */ +#define NUMBEROFCOMPOSITEAPSECTIONS 4 + +/* Function WebRtcIsac_SplitAndFilter + * This function creates low-pass and high-pass decimated versions of part of + the input signal, and part of the signal in the input 'lookahead buffer'. + + INPUTS: + in: a length FRAMESAMPLES array of input samples + prefiltdata: input data structure containing the filterbank states + and lookahead samples from the previous encoding + iteration. + OUTPUTS: + LP: a FRAMESAMPLES_HALF array of low-pass filtered samples that + have been phase equalized. The first QLOOKAHEAD samples are + based on the samples in the two prefiltdata->INLABUFx arrays + each of length QLOOKAHEAD. + The remaining FRAMESAMPLES_HALF-QLOOKAHEAD samples are based + on the first FRAMESAMPLES_HALF-QLOOKAHEAD samples of the input + array in[]. + HP: a FRAMESAMPLES_HALF array of high-pass filtered samples that + have been phase equalized. The first QLOOKAHEAD samples are + based on the samples in the two prefiltdata->INLABUFx arrays + each of length QLOOKAHEAD. + The remaining FRAMESAMPLES_HALF-QLOOKAHEAD samples are based + on the first FRAMESAMPLES_HALF-QLOOKAHEAD samples of the input + array in[]. + + LP_la: a FRAMESAMPLES_HALF array of low-pass filtered samples. + These samples are not phase equalized. They are computed + from the samples in the in[] array. + HP_la: a FRAMESAMPLES_HALF array of high-pass filtered samples + that are not phase equalized. They are computed from + the in[] vector. + prefiltdata: this input data structure's filterbank state and + lookahead sample buffers are updated for the next + encoding iteration. +*/ +void WebRtcIsac_SplitAndFilterFloat(float* pin, + float* LP, + float* HP, + double* LP_la, + double* HP_la, + PreFiltBankstr* prefiltdata) { + int k, n; + float CompositeAPFilterState[NUMBEROFCOMPOSITEAPSECTIONS]; + float ForTransform_CompositeAPFilterState[NUMBEROFCOMPOSITEAPSECTIONS]; + float ForTransform_CompositeAPFilterState2[NUMBEROFCOMPOSITEAPSECTIONS]; + float tempinoutvec[FRAMESAMPLES + MAX_AR_MODEL_ORDER]; + float tempin_ch1[FRAMESAMPLES + MAX_AR_MODEL_ORDER]; + float tempin_ch2[FRAMESAMPLES + MAX_AR_MODEL_ORDER]; + float in[FRAMESAMPLES]; + float ftmp; + + /* HPstcoeff_in = {a1, a2, b1 - b0 * a1, b2 - b0 * a2}; */ + static const float kHpStCoefInFloat[4] = { + -1.94895953203325f, 0.94984516000000f, -0.05101826139794f, + 0.05015484000000f}; + + /* The composite all-pass filter factors */ + static const float WebRtcIsac_kCompositeApFactorsFloat[4] = { + 0.03470000000000f, 0.15440000000000f, 0.38260000000000f, + 0.74400000000000f}; + + // The matrix for transforming the backward composite state to upper channel + // state. + static const float WebRtcIsac_kTransform1Float[8] = { + -0.00158678506084f, 0.00127157815343f, -0.00104805672709f, + 0.00084837248079f, 0.00134467983258f, -0.00107756549387f, + 0.00088814793277f, -0.00071893072525f}; + + // The matrix for transforming the backward composite state to lower channel + // state. + static const float WebRtcIsac_kTransform2Float[8] = { + -0.00170686041697f, 0.00136780109829f, -0.00112736532350f, + 0.00091257055385f, 0.00103094281812f, -0.00082615076557f, + 0.00068092756088f, -0.00055119165484f}; + + /* High pass filter */ + + for (k = 0; k < FRAMESAMPLES; k++) { + in[k] = pin[k] + kHpStCoefInFloat[2] * prefiltdata->HPstates_float[0] + + kHpStCoefInFloat[3] * prefiltdata->HPstates_float[1]; + ftmp = pin[k] - kHpStCoefInFloat[0] * prefiltdata->HPstates_float[0] - + kHpStCoefInFloat[1] * prefiltdata->HPstates_float[1]; + prefiltdata->HPstates_float[1] = prefiltdata->HPstates_float[0]; + prefiltdata->HPstates_float[0] = ftmp; + } + + /* First Channel */ + + /*initial state of composite filter is zero */ + for (k = 0; k < NUMBEROFCOMPOSITEAPSECTIONS; k++) { + CompositeAPFilterState[k] = 0.0; + } + /* put every other sample of input into a temporary vector in reverse + * (backward) order*/ + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + tempinoutvec[k] = in[FRAMESAMPLES - 1 - 2 * k]; + } + + /* now all-pass filter the backwards vector. Output values overwrite the + * input vector. */ + WebRtcIsac_AllPassFilter2Float( + tempinoutvec, WebRtcIsac_kCompositeApFactorsFloat, FRAMESAMPLES_HALF, + NUMBEROFCOMPOSITEAPSECTIONS, CompositeAPFilterState); + + /* save the backwards filtered output for later forward filtering, + but write it in forward order*/ + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + tempin_ch1[FRAMESAMPLES_HALF + QLOOKAHEAD - 1 - k] = tempinoutvec[k]; + } + + /* save the backwards filter state becaue it will be transformed + later into a forward state */ + for (k = 0; k < NUMBEROFCOMPOSITEAPSECTIONS; k++) { + ForTransform_CompositeAPFilterState[k] = CompositeAPFilterState[k]; + } + + /* now backwards filter the samples in the lookahead buffer. The samples were + placed there in the encoding of the previous frame. The output samples + overwrite the input samples */ + WebRtcIsac_AllPassFilter2Float( + prefiltdata->INLABUF1_float, WebRtcIsac_kCompositeApFactorsFloat, + QLOOKAHEAD, NUMBEROFCOMPOSITEAPSECTIONS, CompositeAPFilterState); + + /* save the output, but write it in forward order */ + /* write the lookahead samples for the next encoding iteration. Every other + sample at the end of the input frame is written in reverse order for the + lookahead length. Exported in the prefiltdata structure. */ + for (k = 0; k < QLOOKAHEAD; k++) { + tempin_ch1[QLOOKAHEAD - 1 - k] = prefiltdata->INLABUF1_float[k]; + prefiltdata->INLABUF1_float[k] = in[FRAMESAMPLES - 1 - 2 * k]; + } + + /* Second Channel. This is exactly like the first channel, except that the + even samples are now filtered instead (lower channel). */ + for (k = 0; k < NUMBEROFCOMPOSITEAPSECTIONS; k++) { + CompositeAPFilterState[k] = 0.0; + } + + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + tempinoutvec[k] = in[FRAMESAMPLES - 2 - 2 * k]; + } + + WebRtcIsac_AllPassFilter2Float( + tempinoutvec, WebRtcIsac_kCompositeApFactorsFloat, FRAMESAMPLES_HALF, + NUMBEROFCOMPOSITEAPSECTIONS, CompositeAPFilterState); + + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + tempin_ch2[FRAMESAMPLES_HALF + QLOOKAHEAD - 1 - k] = tempinoutvec[k]; + } + + for (k = 0; k < NUMBEROFCOMPOSITEAPSECTIONS; k++) { + ForTransform_CompositeAPFilterState2[k] = CompositeAPFilterState[k]; + } + + WebRtcIsac_AllPassFilter2Float( + prefiltdata->INLABUF2_float, WebRtcIsac_kCompositeApFactorsFloat, + QLOOKAHEAD, NUMBEROFCOMPOSITEAPSECTIONS, CompositeAPFilterState); + + for (k = 0; k < QLOOKAHEAD; k++) { + tempin_ch2[QLOOKAHEAD - 1 - k] = prefiltdata->INLABUF2_float[k]; + prefiltdata->INLABUF2_float[k] = in[FRAMESAMPLES - 2 - 2 * k]; + } + + /* Transform filter states from backward to forward */ + /*At this point, each of the states of the backwards composite filters for the + two channels are transformed into forward filtering states for the + corresponding forward channel filters. Each channel's forward filtering + state from the previous + encoding iteration is added to the transformed state to get a proper forward + state */ + + /* So the existing NUMBEROFCOMPOSITEAPSECTIONS x 1 (4x1) state vector is + multiplied by a NUMBEROFCHANNELAPSECTIONSxNUMBEROFCOMPOSITEAPSECTIONS (2x4) + transform matrix to get the new state that is added to the previous 2x1 + input state */ + + for (k = 0; k < NUMBEROFCHANNELAPSECTIONS; k++) { /* k is row variable */ + for (n = 0; n < NUMBEROFCOMPOSITEAPSECTIONS; + n++) { /* n is column variable */ + prefiltdata->INSTAT1_float[k] += + ForTransform_CompositeAPFilterState[n] * + WebRtcIsac_kTransform1Float[k * NUMBEROFCHANNELAPSECTIONS + n]; + prefiltdata->INSTAT2_float[k] += + ForTransform_CompositeAPFilterState2[n] * + WebRtcIsac_kTransform2Float[k * NUMBEROFCHANNELAPSECTIONS + n]; + } + } + + /*obtain polyphase components by forward all-pass filtering through each + * channel */ + /* the backward filtered samples are now forward filtered with the + * corresponding channel filters */ + /* The all pass filtering automatically updates the filter states which are + exported in the prefiltdata structure */ + WebRtcIsac_AllPassFilter2Float(tempin_ch1, WebRtcIsac_kUpperApFactorsFloat, + FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, + prefiltdata->INSTAT1_float); + WebRtcIsac_AllPassFilter2Float(tempin_ch2, WebRtcIsac_kLowerApFactorsFloat, + FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, + prefiltdata->INSTAT2_float); + + /* Now Construct low-pass and high-pass signals as combinations of polyphase + * components */ + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + LP[k] = 0.5f * (tempin_ch1[k] + tempin_ch2[k]); /* low pass signal*/ + HP[k] = 0.5f * (tempin_ch1[k] - tempin_ch2[k]); /* high pass signal*/ + } + + /* Lookahead LP and HP signals */ + /* now create low pass and high pass signals of the input vector. However, no + backwards filtering is performed, and hence no phase equalization is + involved. Also, the input contains some samples that are lookahead samples. + The high pass and low pass signals that are created are used outside this + function for analysis (not encoding) purposes */ + + /* set up input */ + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + tempin_ch1[k] = in[2 * k + 1]; + tempin_ch2[k] = in[2 * k]; + } + + /* the input filter states are passed in and updated by the all-pass filtering + routine and exported in the prefiltdata structure*/ + WebRtcIsac_AllPassFilter2Float(tempin_ch1, WebRtcIsac_kUpperApFactorsFloat, + FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, + prefiltdata->INSTATLA1_float); + WebRtcIsac_AllPassFilter2Float(tempin_ch2, WebRtcIsac_kLowerApFactorsFloat, + FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, + prefiltdata->INSTATLA2_float); + + for (k = 0; k < FRAMESAMPLES_HALF; k++) { + LP_la[k] = (float)(0.5f * (tempin_ch1[k] + tempin_ch2[k])); /*low pass */ + HP_la[k] = (double)(0.5f * (tempin_ch1[k] - tempin_ch2[k])); /* high pass */ + } +} diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.h b/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.h new file mode 100644 index 0000000..1aecfc4 --- /dev/null +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/isac_vad.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_VAD_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_VAD_H_ + +#include + +#include "modules/audio_coding/codecs/isac/main/source/structs.h" + +void WebRtcIsac_InitPitchFilter(PitchFiltstr* pitchfiltdata); +void WebRtcIsac_InitPitchAnalysis(PitchAnalysisStruct* state); +void WebRtcIsac_InitPreFilterbank(PreFiltBankstr* prefiltdata); + +double WebRtcIsac_LevDurb(double* a, double* k, double* r, size_t order); + +/* The number of all-pass filter factors in an upper or lower channel*/ +#define NUMBEROFCHANNELAPSECTIONS 2 + +/* The upper channel all-pass filter factors */ +extern const float WebRtcIsac_kUpperApFactorsFloat[2]; + +/* The lower channel all-pass filter factors */ +extern const float WebRtcIsac_kLowerApFactorsFloat[2]; + +void WebRtcIsac_AllPassFilter2Float(float* InOut, + const float* APSectionFactors, + int lengthInOut, + int NumberOfSections, + float* FilterState); +void WebRtcIsac_SplitAndFilterFloat(float* in, + float* LP, + float* HP, + double* LP_la, + double* HP_la, + PreFiltBankstr* prefiltdata); + +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ISAC_VAD_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/lattice.c b/webrtc/modules/audio_coding/codecs/isac/main/source/lattice.c index eabe708..d9d2d65 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/lattice.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/lattice.c @@ -14,8 +14,6 @@ * contains the normalized lattice filter routines (MA and AR) for iSAC codec * */ -#include "settings.h" -#include "codec.h" #include #include @@ -24,6 +22,9 @@ #include #endif +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/source/codec.h" + /* filter the signal using normalized lattice filter */ /* MA filter */ void WebRtcIsac_NormLatticeFilterMa(int orderCoef, diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_analysis.c b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_analysis.c index 60fc25b..0fda73b 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_analysis.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_analysis.c @@ -8,16 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "lpc_analysis.h" -#include "settings.h" -#include "codec.h" -#include "entropy_coding.h" - #include #include -#define LEVINSON_EPS 1.0e-10 - +#include "modules/audio_coding/codecs/isac/main/source/lpc_analysis.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/source/codec.h" +#include "modules/audio_coding/codecs/isac/main/source/entropy_coding.h" +#include "modules/audio_coding/codecs/isac/main/source/filter_functions.h" +#include "modules/audio_coding/codecs/isac/main/source/isac_vad.h" /* window */ /* Matlab generation code: @@ -75,45 +74,10 @@ static const double kLpcCorrWindow[WINLEN] = { 0.00155690, 0.00124918, 0.00094895, 0.00066112, 0.00039320, 0.00015881 }; -double WebRtcIsac_LevDurb(double *a, double *k, double *r, size_t order) -{ - - double sum, alpha; - size_t m, m_h, i; - alpha = 0; //warning -DH - a[0] = 1.0; - if (r[0] < LEVINSON_EPS) { /* if r[0] <= 0, set LPC coeff. to zero */ - for (i = 0; i < order; i++) { - k[i] = 0; - a[i+1] = 0; - } - } else { - a[1] = k[0] = -r[1]/r[0]; - alpha = r[0] + r[1] * k[0]; - for (m = 1; m < order; m++){ - sum = r[m + 1]; - for (i = 0; i < m; i++){ - sum += a[i+1] * r[m - i]; - } - k[m] = -sum / alpha; - alpha += k[m] * sum; - m_h = (m + 1) >> 1; - for (i = 0; i < m_h; i++){ - sum = a[i+1] + k[m] * a[m - i]; - a[m - i] += k[m] * a[i+1]; - a[i+1] = sum; - } - a[m+1] = k[m]; - } - } - return alpha; -} - - -//was static before, but didn't work with MEX file -void WebRtcIsac_GetVars(const double *input, const int16_t *pitchGains_Q12, - double *oldEnergy, double *varscale) -{ +static void WebRtcIsac_GetVars(const double* input, + const int16_t* pitchGains_Q12, + double* oldEnergy, + double* varscale) { double nrg[4], chng, pg; int k; @@ -162,12 +126,9 @@ void WebRtcIsac_GetVars(const double *input, const int16_t *pitchGains_Q12, *oldEnergy = nrg[3]; } -void -WebRtcIsac_GetVarsUB( - const double* input, - double* oldEnergy, - double* varscale) -{ +static void WebRtcIsac_GetVarsUB(const double* input, + double* oldEnergy, + double* varscale) { double nrg[4], chng; int k; diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_analysis.h b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_analysis.h index 8dfe383..5503e2d 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_analysis.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_analysis.h @@ -15,36 +15,32 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_ANALYSIS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_ANALYSIS_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_ANALYSIS_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_ANALYSIS_H_ -#include "settings.h" -#include "structs.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" -double WebRtcIsac_LevDurb(double *a, double *k, double *r, size_t order); +void WebRtcIsac_GetLpcCoefLb(double* inLo, + double* inHi, + MaskFiltstr* maskdata, + double signal_noise_ratio, + const int16_t* pitchGains_Q12, + double* lo_coeff, + double* hi_coeff); -void WebRtcIsac_GetVars(const double *input, const int16_t *pitchGains_Q12, - double *oldEnergy, double *varscale); +void WebRtcIsac_GetLpcGain(double signal_noise_ratio, + const double* filtCoeffVecs, + int numVecs, + double* gain, + double corrLo[][UB_LPC_ORDER + 1], + const double* varscale); -void WebRtcIsac_GetLpcCoefLb(double *inLo, double *inHi, MaskFiltstr *maskdata, - double signal_noise_ratio, const int16_t *pitchGains_Q12, - double *lo_coeff, double *hi_coeff); +void WebRtcIsac_GetLpcCoefUb(double* inSignal, + MaskFiltstr* maskdata, + double* lpCoeff, + double corr[][UB_LPC_ORDER + 1], + double* varscale, + int16_t bandwidth); - -void WebRtcIsac_GetLpcGain( - double signal_noise_ratio, - const double* filtCoeffVecs, - int numVecs, - double* gain, - double corrLo[][UB_LPC_ORDER + 1], - const double* varscale); - -void WebRtcIsac_GetLpcCoefUb( - double* inSignal, - MaskFiltstr* maskdata, - double* lpCoeff, - double corr[][UB_LPC_ORDER + 1], - double* varscale, - int16_t bandwidth); - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_ANALYIS_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_ANALYIS_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c index 5cc6c11..6707540 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c @@ -16,9 +16,8 @@ * */ -#include "lpc_gain_swb_tables.h" -#include "settings.h" -#include "webrtc/typedefs.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" const double WebRtcIsac_kQSizeLpcGain = 0.100000; diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h index c163f4a..39c4a24 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h @@ -16,11 +16,12 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_GAIN_SWB_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_GAIN_SWB_TABLES_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_GAIN_SWB_TABLES_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_GAIN_SWB_TABLES_H_ -#include "settings.h" -#include "webrtc/typedefs.h" +#include + +#include "modules/audio_coding/codecs/isac/main/source/settings.h" extern const double WebRtcIsac_kQSizeLpcGain; @@ -46,4 +47,4 @@ extern const uint16_t* WebRtcIsac_kLpcGainCdfMat[SUBFRAMES]; extern const double WebRtcIsac_kLpcGainDecorrMat[SUBFRAMES][SUBFRAMES]; -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_GAIN_SWB_TABLES_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_GAIN_SWB_TABLES_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c index 599b89d..e3600a7 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c @@ -16,9 +16,8 @@ * */ -#include "lpc_shape_swb12_tables.h" -#include "settings.h" -#include "webrtc/typedefs.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" /* * Mean value of LAR diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h index 256f1d4..7448a1e 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h @@ -16,32 +16,33 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB12_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB12_TABLES_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB12_TABLES_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB12_TABLES_H_ -#include "settings.h" -#include "webrtc/typedefs.h" +#include + +#include "modules/audio_coding/codecs/isac/main/source/settings.h" extern const double WebRtcIsac_kMeanLarUb12[UB_LPC_ORDER]; extern const double WebRtcIsac_kMeanLpcGain; -extern const double WebRtcIsac_kIntraVecDecorrMatUb12[UB_LPC_ORDER][UB_LPC_ORDER]; +extern const double WebRtcIsac_kIntraVecDecorrMatUb12[UB_LPC_ORDER] + [UB_LPC_ORDER]; -extern const double WebRtcIsac_kInterVecDecorrMatUb12 -[UB_LPC_VEC_PER_FRAME][UB_LPC_VEC_PER_FRAME]; +extern const double WebRtcIsac_kInterVecDecorrMatUb12[UB_LPC_VEC_PER_FRAME] + [UB_LPC_VEC_PER_FRAME]; extern const double WebRtcIsac_kLpcShapeQStepSizeUb12; -extern const double WebRtcIsac_kLpcShapeLeftRecPointUb12 -[UB_LPC_ORDER*UB_LPC_VEC_PER_FRAME]; +extern const double + WebRtcIsac_kLpcShapeLeftRecPointUb12[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME]; +extern const int16_t + WebRtcIsac_kLpcShapeNumRecPointUb12[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME]; -extern const int16_t WebRtcIsac_kLpcShapeNumRecPointUb12 -[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME]; - -extern const uint16_t WebRtcIsac_kLpcShapeEntropySearchUb12 -[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME]; +extern const uint16_t + WebRtcIsac_kLpcShapeEntropySearchUb12[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME]; extern const uint16_t WebRtcIsac_kLpcShapeCdfVec0Ub12[14]; @@ -59,7 +60,7 @@ extern const uint16_t WebRtcIsac_kLpcShapeCdfVec6Ub12[33]; extern const uint16_t WebRtcIsac_kLpcShapeCdfVec7Ub12[49]; -extern const uint16_t* WebRtcIsac_kLpcShapeCdfMatUb12 -[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME]; +extern const uint16_t* + WebRtcIsac_kLpcShapeCdfMatUb12[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME]; -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB12_TABLES_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB12_TABLES_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c index 6176d2c..59617fd 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c @@ -16,9 +16,8 @@ * */ -#include "lpc_shape_swb16_tables.h" -#include "settings.h" -#include "webrtc/typedefs.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" /* * Mean value of LAR diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h index 3e1bdf7..51101db 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h @@ -16,18 +16,20 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB16_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB16_TABLES_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB16_TABLES_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB16_TABLES_H_ -#include "settings.h" -#include "webrtc/typedefs.h" +#include + +#include "modules/audio_coding/codecs/isac/main/source/settings.h" extern const double WebRtcIsac_kMeanLarUb16[UB_LPC_ORDER]; -extern const double WebRtcIsac_kIintraVecDecorrMatUb16[UB_LPC_ORDER][UB_LPC_ORDER]; +extern const double WebRtcIsac_kIintraVecDecorrMatUb16[UB_LPC_ORDER] + [UB_LPC_ORDER]; -extern const double WebRtcIsac_kInterVecDecorrMatUb16 -[UB16_LPC_VEC_PER_FRAME][UB16_LPC_VEC_PER_FRAME]; +extern const double WebRtcIsac_kInterVecDecorrMatUb16[UB16_LPC_VEC_PER_FRAME] + [UB16_LPC_VEC_PER_FRAME]; extern const uint16_t WebRtcIsac_kLpcShapeCdfVec01Ub16[14]; @@ -61,18 +63,19 @@ extern const uint16_t WebRtcIsac_kLpcShapeCdfVec01Ub165[34]; extern const uint16_t WebRtcIsac_kLpcShapeCdfVec01Ub166[71]; -extern const uint16_t* WebRtcIsac_kLpcShapeCdfMatUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; +extern const uint16_t* + WebRtcIsac_kLpcShapeCdfMatUb16[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; -extern const double WebRtcIsac_kLpcShapeLeftRecPointUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; +extern const double + WebRtcIsac_kLpcShapeLeftRecPointUb16[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; -extern const int16_t WebRtcIsac_kLpcShapeNumRecPointUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; +extern const int16_t + WebRtcIsac_kLpcShapeNumRecPointUb16[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; -extern const uint16_t WebRtcIsac_kLpcShapeEntropySearchUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; +extern const uint16_t + WebRtcIsac_kLpcShapeEntropySearchUb16[UB_LPC_ORDER * + UB16_LPC_VEC_PER_FRAME]; extern const double WebRtcIsac_kLpcShapeQStepSizeUb16; -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB16_TABLES_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB16_TABLES_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_tables.c b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_tables.c index 909809b..461b92e 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_tables.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_tables.c @@ -10,8 +10,8 @@ /* coding tables for the KLT coefficients */ -#include "lpc_tables.h" -#include "settings.h" +#include "modules/audio_coding/codecs/isac/main/source/lpc_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" /* cdf array for model indicator */ const uint16_t WebRtcIsac_kQKltModelCdf[4] = { diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_tables.h b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_tables.h index 51f6316..56ff22c 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_tables.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/lpc_tables.h @@ -15,34 +15,33 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_TABLES_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_TABLES_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_TABLES_H_ -#include "structs.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" -#include "settings.h" +#define KLT_STEPSIZE 1.00000000 +#define KLT_NUM_AVG_GAIN 0 +#define KLT_NUM_AVG_SHAPE 0 +#define KLT_NUM_MODELS 3 +#define LPC_GAIN_SCALE 4.000f +#define LPC_LOBAND_SCALE 2.100f +#define LPC_LOBAND_ORDER ORDERLO +#define LPC_HIBAND_SCALE 0.450f +#define LPC_HIBAND_ORDER ORDERHI +#define LPC_GAIN_ORDER 2 -#define KLT_STEPSIZE 1.00000000 -#define KLT_NUM_AVG_GAIN 0 -#define KLT_NUM_AVG_SHAPE 0 -#define KLT_NUM_MODELS 3 -#define LPC_GAIN_SCALE 4.000f -#define LPC_LOBAND_SCALE 2.100f -#define LPC_LOBAND_ORDER ORDERLO -#define LPC_HIBAND_SCALE 0.450f -#define LPC_HIBAND_ORDER ORDERHI -#define LPC_GAIN_ORDER 2 +#define LPC_SHAPE_ORDER (LPC_LOBAND_ORDER + LPC_HIBAND_ORDER) -#define LPC_SHAPE_ORDER (LPC_LOBAND_ORDER + LPC_HIBAND_ORDER) - -#define KLT_ORDER_GAIN (LPC_GAIN_ORDER * SUBFRAMES) -#define KLT_ORDER_SHAPE (LPC_SHAPE_ORDER * SUBFRAMES) +#define KLT_ORDER_GAIN (LPC_GAIN_ORDER * SUBFRAMES) +#define KLT_ORDER_SHAPE (LPC_SHAPE_ORDER * SUBFRAMES) /* cdf array for model indicator */ -extern const uint16_t WebRtcIsac_kQKltModelCdf[KLT_NUM_MODELS+1]; +extern const uint16_t WebRtcIsac_kQKltModelCdf[KLT_NUM_MODELS + 1]; /* pointer to cdf array for model indicator */ -extern const uint16_t *WebRtcIsac_kQKltModelCdfPtr[1]; +extern const uint16_t* WebRtcIsac_kQKltModelCdfPtr[1]; /* initial cdf index for decoder of model indicator */ extern const uint16_t WebRtcIsac_kQKltModelInitIndex[1]; @@ -78,9 +77,9 @@ extern const uint16_t WebRtcIsac_kQKltCdfGain[404]; extern const uint16_t WebRtcIsac_kQKltCdfShape[686]; /* pointers to cdf tables for quantizer indices */ -extern const uint16_t *WebRtcIsac_kQKltCdfPtrGain[12]; +extern const uint16_t* WebRtcIsac_kQKltCdfPtrGain[12]; -extern const uint16_t *WebRtcIsac_kQKltCdfPtrShape[108]; +extern const uint16_t* WebRtcIsac_kQKltCdfPtrShape[108]; /* left KLT transforms */ extern const double WebRtcIsac_kKltT1Gain[4]; @@ -97,4 +96,4 @@ extern const double WebRtcIsac_kLpcMeansGain[12]; extern const double WebRtcIsac_kLpcMeansShape[108]; -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_TABLES_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_TABLES_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h b/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h index 2b446e9..fe9afa4 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h @@ -8,12 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_OS_SPECIFIC_INLINE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_OS_SPECIFIC_INLINE_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_OS_SPECIFIC_INLINE_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_OS_SPECIFIC_INLINE_H_ #include -#include "webrtc/typedefs.h" + +#include "rtc_base/system/arch.h" #if defined(WEBRTC_POSIX) #define WebRtcIsac_lrint lrint @@ -24,11 +24,12 @@ static __inline long int WebRtcIsac_lrint(double x_dbl) { __asm { fld x_dbl fistp x_int - }; + } + ; return x_int; } -#else // Do a slow but correct implementation of lrint +#else // Do a slow but correct implementation of lrint static __inline long int WebRtcIsac_lrint(double x_dbl) { long int x_int; @@ -38,4 +39,4 @@ static __inline long int WebRtcIsac_lrint(double x_dbl) { #endif -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_OS_SPECIFIC_INLINE_H_ +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_OS_SPECIFIC_INLINE_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c index 090b94c..8a19ac1 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "pitch_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" #include #include @@ -17,6 +17,10 @@ #include #endif +#include "modules/audio_coding/codecs/isac/main/source/filter_functions.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_filter.h" +#include "rtc_base/system/ignore_warnings.h" + static const double kInterpolWin[8] = {-0.00067556028640, 0.02184247643159, -0.12203175715679, 0.60086484101160, 0.60086484101160, -0.12203175715679, 0.02184247643159, -0.00067556028640}; @@ -122,13 +126,56 @@ static void PCorr(const double *in, double *outcorr) } } +static void WebRtcIsac_AllpassFilterForDec(double* InOut, + const double* APSectionFactors, + size_t lengthInOut, + double* FilterState) { + // This performs all-pass filtering--a series of first order all-pass + // sections are used to filter the input in a cascade manner. + size_t n, j; + double temp; + for (j = 0; j < ALLPASSSECTIONS; j++) { + for (n = 0; n < lengthInOut; n += 2) { + temp = InOut[n]; // store input + InOut[n] = FilterState[j] + APSectionFactors[j] * temp; + FilterState[j] = -APSectionFactors[j] * InOut[n] + temp; + } + } +} -void WebRtcIsac_InitializePitch(const double *in, - const double old_lag, - const double old_gain, - PitchAnalysisStruct *State, - double *lags) -{ +static void WebRtcIsac_DecimateAllpass( + const double* in, + double* state_in, // array of size: 2*ALLPASSSECTIONS+1 + size_t N, // number of input samples + double* out) { // array of size N/2 + + static const double APupper[ALLPASSSECTIONS] = {0.0347, 0.3826}; + static const double APlower[ALLPASSSECTIONS] = {0.1544, 0.744}; + + size_t n; + double data_vec[PITCH_FRAME_LEN]; + + /* copy input */ + memcpy(data_vec + 1, in, sizeof(double) * (N - 1)); + + data_vec[0] = state_in[2 * ALLPASSSECTIONS]; // the z^(-1) state + state_in[2 * ALLPASSSECTIONS] = in[N - 1]; + + WebRtcIsac_AllpassFilterForDec(data_vec + 1, APupper, N, state_in); + WebRtcIsac_AllpassFilterForDec(data_vec, APlower, N, + state_in + ALLPASSSECTIONS); + + for (n = 0; n < N / 2; n++) + out[n] = data_vec[2 * n] + data_vec[2 * n + 1]; +} + +RTC_PUSH_IGNORING_WFRAME_LARGER_THAN() + +static void WebRtcIsac_InitializePitch(const double* in, + const double old_lag, + const double old_gain, + PitchAnalysisStruct* State, + double* lags) { double buf_dec[PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2+2]; double ratio, log_lag, gain_bias; double bias; @@ -449,7 +496,7 @@ void WebRtcIsac_InitializePitch(const double *in, } } - +RTC_POP_IGNORING_WFRAME_LARGER_THAN() /* create weighting matrix by orthogonalizing a basis of polynomials of increasing order * t = (0:4)'; @@ -464,6 +511,29 @@ static const double kWeight[5][5] = { { 0.01714285714286, 0.05142857142857, -0.05714285714286, -0.30857142857143, 0.29714285714286} }; +/* second order high-pass filter */ +static void WebRtcIsac_Highpass(const double* in, + double* out, + double* state, + size_t N) { + /* create high-pass filter ocefficients + * z = 0.998 * exp(j*2*pi*35/8000); + * p = 0.94 * exp(j*2*pi*140/8000); + * HP_b = [1, -2*real(z), abs(z)^2]; + * HP_a = [1, -2*real(p), abs(p)^2]; */ + static const double a_coef[2] = { 1.86864659625574, -0.88360000000000}; + static const double b_coef[2] = {-1.99524591718270, 0.99600400000000}; + + size_t k; + + for (k=0; kinbuf[k] = inbuf[k + PITCH_FRAME_LEN]; } + +RTC_POP_IGNORING_WFRAME_LARGER_THAN() diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h index 6fb02b3..4ab78c2 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h @@ -15,61 +15,18 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ -#include "structs.h" +#include +#include "modules/audio_coding/codecs/isac/main/source/structs.h" +void WebRtcIsac_PitchAnalysis( + const double* in, /* PITCH_FRAME_LEN samples */ + double* out, /* PITCH_FRAME_LEN+QLOOKAHEAD samples */ + PitchAnalysisStruct* State, + double* lags, + double* gains); -void WebRtcIsac_PitchAnalysis(const double *in, /* PITCH_FRAME_LEN samples */ - double *out, /* PITCH_FRAME_LEN+QLOOKAHEAD samples */ - PitchAnalysisStruct *State, - double *lags, - double *gains); - -void WebRtcIsac_InitializePitch(const double *in, - const double old_lag, - const double old_gain, - PitchAnalysisStruct *State, - double *lags); - -void WebRtcIsac_PitchfilterPre(double *indat, - double *outdat, - PitchFiltstr *pfp, - double *lags, - double *gains); - -void WebRtcIsac_PitchfilterPost(double *indat, - double *outdat, - PitchFiltstr *pfp, - double *lags, - double *gains); - -void WebRtcIsac_PitchfilterPre_la(double *indat, - double *outdat, - PitchFiltstr *pfp, - double *lags, - double *gains); - -void WebRtcIsac_PitchfilterPre_gains(double *indat, - double *outdat, - double out_dG[][PITCH_FRAME_LEN + QLOOKAHEAD], - PitchFiltstr *pfp, - double *lags, - double *gains); - -void WebRtcIsac_WeightingFilter(const double *in, double *weiout, double *whiout, WeightFiltstr *wfdata); - -void WebRtcIsac_Highpass(const double *in, - double *out, - double *state, - size_t N); - -void WebRtcIsac_DecimateAllpass(const double *in, - double *state_in, /* array of size: - * 2*ALLPASSSECTIONS+1 */ - size_t N, /* number of input samples */ - double *out); /* array of size N/2 */ - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.c b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.c index f03d230..61cd533 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.c @@ -8,13 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "pitch_estimator.h" - #include #include #include -#include "os_specific_inline.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/os_specific_inline.h" +#include "rtc_base/compile_assert_c.h" /* * We are implementing the following filters; @@ -275,6 +275,11 @@ static void FilterFrame(const double* in_data, PitchFiltstr* filter_state, /* Copy states to local variables. */ memcpy(filter_parameters.buffer, filter_state->ubuf, sizeof(filter_state->ubuf)); + RTC_COMPILE_ASSERT(sizeof(filter_parameters.buffer) >= + sizeof(filter_state->ubuf)); + memset(filter_parameters.buffer + + sizeof(filter_state->ubuf) / sizeof(filter_state->ubuf[0]), + 0, sizeof(filter_parameters.buffer) - sizeof(filter_state->ubuf)); memcpy(filter_parameters.damper_state, filter_state->ystate, sizeof(filter_state->ystate)); diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.h b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.h new file mode 100644 index 0000000..9a232de --- /dev/null +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_filter.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_FILTER_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_FILTER_H_ + +#include "modules/audio_coding/codecs/isac/main/source/structs.h" + +void WebRtcIsac_PitchfilterPre(double* indat, + double* outdat, + PitchFiltstr* pfp, + double* lags, + double* gains); + +void WebRtcIsac_PitchfilterPost(double* indat, + double* outdat, + PitchFiltstr* pfp, + double* lags, + double* gains); + +void WebRtcIsac_PitchfilterPre_la(double* indat, + double* outdat, + PitchFiltstr* pfp, + double* lags, + double* gains); + +void WebRtcIsac_PitchfilterPre_gains( + double* indat, + double* outdat, + double out_dG[][PITCH_FRAME_LEN + QLOOKAHEAD], + PitchFiltstr* pfp, + double* lags, + double* gains); + +#endif // MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_FILTER_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.c b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.c index 947d3e7..080432c 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.c @@ -8,9 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "pitch_gain_tables.h" - -#include "settings.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" /* header file for coding tables for the pitch filter side-info in the entropy coder */ /********************* Pitch Filter Gain Coefficient Tables ************************/ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h index 8d708ce..145fd4e 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h @@ -11,17 +11,20 @@ /* * pitch_gain_tables.h * - * This file contains tables for the pitch filter side-info in the entropy coder. + * This file contains tables for the pitch filter side-info in the entropy + * coder. * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_GAIN_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_GAIN_TABLES_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_GAIN_TABLES_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_GAIN_TABLES_H_ -#include "webrtc/typedefs.h" +#include -/* header file for coding tables for the pitch filter side-info in the entropy coder */ -/********************* Pitch Filter Gain Coefficient Tables ************************/ +/* header file for coding tables for the pitch filter side-info in the entropy + * coder */ +/********************* Pitch Filter Gain Coefficient Tables + * ************************/ /* cdf for quantized pitch filter gains */ extern const uint16_t WebRtcIsac_kQPitchGainCdf[255]; @@ -42,4 +45,4 @@ extern const int16_t WebRtcIsac_kQMeanGain4Q12[144]; /* size of cdf table */ extern const uint16_t WebRtcIsac_kQCdfTableSizeGain[1]; -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_GAIN_TABLES_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_GAIN_TABLES_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.c b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.c index f845a22..57d1202 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.c @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "pitch_lag_tables.h" -#include "settings.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" /* header file for coding tables for the pitch filter side-info in the entropy coder */ /********************* Pitch Filter Gain Coefficient Tables ************************/ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h index 01989f0..b48e358 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h @@ -11,16 +11,20 @@ /* * pitch_lag_tables.h * - * This file contains tables for the pitch filter side-info in the entropy coder. + * This file contains tables for the pitch filter side-info in the entropy + * coder. * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_LAG_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_LAG_TABLES_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_LAG_TABLES_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_LAG_TABLES_H_ -#include "webrtc/typedefs.h" -/* header file for coding tables for the pitch filter side-info in the entropy coder */ -/********************* Pitch Filter Lag Coefficient Tables ************************/ +#include + +/* header file for coding tables for the pitch filter side-info in the entropy + * coder */ +/********************* Pitch Filter Lag Coefficient Tables + * ************************/ /* tables for use with small pitch gain */ @@ -30,7 +34,7 @@ extern const uint16_t WebRtcIsac_kQPitchLagCdf2Lo[20]; extern const uint16_t WebRtcIsac_kQPitchLagCdf3Lo[2]; extern const uint16_t WebRtcIsac_kQPitchLagCdf4Lo[10]; -extern const uint16_t *WebRtcIsac_kQPitchLagCdfPtrLo[4]; +extern const uint16_t* WebRtcIsac_kQPitchLagCdfPtrLo[4]; /* size of first cdf table */ extern const uint16_t WebRtcIsac_kQPitchLagCdfSizeLo[1]; @@ -49,7 +53,6 @@ extern const double WebRtcIsac_kQMeanLag4Lo[9]; extern const double WebRtcIsac_kQPitchLagStepsizeLo; - /* tables for use with medium pitch gain */ /* cdfs for quantized pitch lags */ @@ -58,7 +61,7 @@ extern const uint16_t WebRtcIsac_kQPitchLagCdf2Mid[36]; extern const uint16_t WebRtcIsac_kQPitchLagCdf3Mid[2]; extern const uint16_t WebRtcIsac_kQPitchLagCdf4Mid[20]; -extern const uint16_t *WebRtcIsac_kQPitchLagCdfPtrMid[4]; +extern const uint16_t* WebRtcIsac_kQPitchLagCdfPtrMid[4]; /* size of first cdf table */ extern const uint16_t WebRtcIsac_kQPitchLagCdfSizeMid[1]; @@ -77,7 +80,6 @@ extern const double WebRtcIsac_kQMeanLag4Mid[19]; extern const double WebRtcIsac_kQPitchLagStepsizeMid; - /* tables for use with large pitch gain */ /* cdfs for quantized pitch lags */ @@ -86,7 +88,7 @@ extern const uint16_t WebRtcIsac_kQPitchLagCdf2Hi[68]; extern const uint16_t WebRtcIsac_kQPitchLagCdf3Hi[2]; extern const uint16_t WebRtcIsac_kQPitchLagCdf4Hi[35]; -extern const uint16_t *WebRtcIsac_kQPitchLagCdfPtrHi[4]; +extern const uint16_t* WebRtcIsac_kQPitchLagCdfPtrHi[4]; /* size of first cdf table */ extern const uint16_t WebRtcIsac_kQPitchLagCdfSizeHi[1]; @@ -111,4 +113,4 @@ extern const double WebRtcIsac_kTransform[4][4]; /* transpose transform matrix */ extern const double WebRtcIsac_kTransformTranspose[4][4]; -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_LAG_TABLES_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_LAG_TABLES_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/settings.h b/webrtc/modules/audio_coding/codecs/isac/main/source/settings.h index 31a8065..abce90c 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/settings.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/settings.h @@ -15,191 +15,182 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ /* sampling frequency (Hz) */ -#define FS 16000 +#define FS 16000 /* number of samples per frame (either 320 (20ms), 480 (30ms) or 960 (60ms)) */ -#define INITIAL_FRAMESAMPLES 960 - - -#define MAXFFTSIZE 2048 -#define NFACTOR 11 - - +#define INITIAL_FRAMESAMPLES 960 /* do not modify the following; this will have to be modified if we * have a 20ms framesize option */ /**********************************************************************/ /* miliseconds */ -#define FRAMESIZE 30 +#define FRAMESIZE 30 /* number of samples per frame processed in the encoder, 480 */ -#define FRAMESAMPLES 480 /* ((FRAMESIZE*FS)/1000) */ -#define FRAMESAMPLES_HALF 240 -#define FRAMESAMPLES_QUARTER 120 +#define FRAMESAMPLES 480 /* ((FRAMESIZE*FS)/1000) */ +#define FRAMESAMPLES_HALF 240 +#define FRAMESAMPLES_QUARTER 120 /**********************************************************************/ - - /* max number of samples per frame (= 60 ms frame) */ -#define MAX_FRAMESAMPLES 960 -#define MAX_SWBFRAMESAMPLES (MAX_FRAMESAMPLES * 2) +#define MAX_FRAMESAMPLES 960 +#define MAX_SWBFRAMESAMPLES (MAX_FRAMESAMPLES * 2) /* number of samples per 10ms frame */ -#define FRAMESAMPLES_10ms ((10*FS)/1000) -#define SWBFRAMESAMPLES_10ms (FRAMESAMPLES_10ms * 2) +#define FRAMESAMPLES_10ms ((10 * FS) / 1000) +#define SWBFRAMESAMPLES_10ms (FRAMESAMPLES_10ms * 2) /* number of samples in 30 ms frame */ -#define FRAMESAMPLES_30ms 480 +#define FRAMESAMPLES_30ms 480 /* number of subframes */ -#define SUBFRAMES 6 +#define SUBFRAMES 6 /* length of a subframe */ -#define UPDATE 80 +#define UPDATE 80 /* length of half a subframe (low/high band) */ -#define HALF_SUBFRAMELEN (UPDATE/2) +#define HALF_SUBFRAMELEN (UPDATE / 2) /* samples of look ahead (in a half-band, so actually * half the samples of look ahead @ FS) */ -#define QLOOKAHEAD 24 /* 3 ms */ +#define QLOOKAHEAD 24 /* 3 ms */ /* order of AR model in spectral entropy coder */ -#define AR_ORDER 6 +#define AR_ORDER 6 /* order of LP model in spectral entropy coder */ -#define LP_ORDER 0 +#define LP_ORDER 0 /* window length (masking analysis) */ -#define WINLEN 256 +#define WINLEN 256 /* order of low-band pole filter used to approximate masking curve */ -#define ORDERLO 12 +#define ORDERLO 12 /* order of hi-band pole filter used to approximate masking curve */ -#define ORDERHI 6 - -#define UB_LPC_ORDER 4 -#define UB_LPC_VEC_PER_FRAME 2 -#define UB16_LPC_VEC_PER_FRAME 4 -#define UB_ACTIVE_SUBFRAMES 2 -#define UB_MAX_LPC_ORDER 6 -#define UB_INTERPOL_SEGMENTS 1 -#define UB16_INTERPOL_SEGMENTS 3 -#define LB_TOTAL_DELAY_SAMPLES 48 -enum ISACBandwidth {isac8kHz = 8, isac12kHz = 12, isac16kHz = 16}; -enum ISACBand {kIsacLowerBand = 0, kIsacUpperBand12 = 1, kIsacUpperBand16 = 2}; -enum IsacSamplingRate {kIsacWideband = 16, kIsacSuperWideband = 32}; -#define UB_LPC_GAIN_DIM SUBFRAMES -#define FB_STATE_SIZE_WORD32 6 +#define ORDERHI 6 +#define UB_LPC_ORDER 4 +#define UB_LPC_VEC_PER_FRAME 2 +#define UB16_LPC_VEC_PER_FRAME 4 +#define UB_ACTIVE_SUBFRAMES 2 +#define UB_MAX_LPC_ORDER 6 +#define UB_INTERPOL_SEGMENTS 1 +#define UB16_INTERPOL_SEGMENTS 3 +#define LB_TOTAL_DELAY_SAMPLES 48 +enum ISACBandwidth { isac8kHz = 8, isac12kHz = 12, isac16kHz = 16 }; +enum ISACBand { + kIsacLowerBand = 0, + kIsacUpperBand12 = 1, + kIsacUpperBand16 = 2 +}; +enum IsacSamplingRate { kIsacWideband = 16, kIsacSuperWideband = 32 }; +#define UB_LPC_GAIN_DIM SUBFRAMES +#define FB_STATE_SIZE_WORD32 6 /* order for post_filter_bank */ -#define POSTQORDER 3 +#define POSTQORDER 3 /* order for pre-filterbank */ -#define QORDER 3 +#define QORDER 3 /* another order */ -#define QORDER_ALL (POSTQORDER+QORDER-1) +#define QORDER_ALL (POSTQORDER + QORDER - 1) /* for decimator */ -#define ALLPASSSECTIONS 2 - +#define ALLPASSSECTIONS 2 /* array size for byte stream in number of bytes. */ /* The old maximum size still needed for the decoding */ -#define STREAM_SIZE_MAX 600 -#define STREAM_SIZE_MAX_30 200 /* 200 bytes=53.4 kbps @ 30 ms.framelength */ -#define STREAM_SIZE_MAX_60 400 /* 400 bytes=53.4 kbps @ 60 ms.framelength */ +#define STREAM_SIZE_MAX 600 +#define STREAM_SIZE_MAX_30 200 /* 200 bytes=53.4 kbps @ 30 ms.framelength */ +#define STREAM_SIZE_MAX_60 400 /* 400 bytes=53.4 kbps @ 60 ms.framelength */ /* storage size for bit counts */ -#define BIT_COUNTER_SIZE 30 +#define BIT_COUNTER_SIZE 30 /* maximum order of any AR model or filter */ -#define MAX_AR_MODEL_ORDER 12//50 - +#define MAX_AR_MODEL_ORDER 12 // 50 /* For pitch analysis */ -#define PITCH_FRAME_LEN (FRAMESAMPLES_HALF) /* 30 ms */ -#define PITCH_MAX_LAG 140 /* 57 Hz */ -#define PITCH_MIN_LAG 20 /* 400 Hz */ -#define PITCH_MAX_GAIN 0.45 -#define PITCH_MAX_GAIN_06 0.27 /* PITCH_MAX_GAIN*0.6 */ -#define PITCH_MAX_GAIN_Q12 1843 -#define PITCH_LAG_SPAN2 (PITCH_MAX_LAG/2-PITCH_MIN_LAG/2+5) -#define PITCH_CORR_LEN2 60 /* 15 ms */ -#define PITCH_CORR_STEP2 (PITCH_FRAME_LEN/4) -#define PITCH_BW 11 /* half the band width of correlation surface */ -#define PITCH_SUBFRAMES 4 -#define PITCH_GRAN_PER_SUBFRAME 5 -#define PITCH_SUBFRAME_LEN (PITCH_FRAME_LEN/PITCH_SUBFRAMES) -#define PITCH_UPDATE (PITCH_SUBFRAME_LEN/PITCH_GRAN_PER_SUBFRAME) +#define PITCH_FRAME_LEN (FRAMESAMPLES_HALF) /* 30 ms */ +#define PITCH_MAX_LAG 140 /* 57 Hz */ +#define PITCH_MIN_LAG 20 /* 400 Hz */ +#define PITCH_MAX_GAIN 0.45 +#define PITCH_MAX_GAIN_06 0.27 /* PITCH_MAX_GAIN*0.6 */ +#define PITCH_MAX_GAIN_Q12 1843 +#define PITCH_LAG_SPAN2 (PITCH_MAX_LAG / 2 - PITCH_MIN_LAG / 2 + 5) +#define PITCH_CORR_LEN2 60 /* 15 ms */ +#define PITCH_CORR_STEP2 (PITCH_FRAME_LEN / 4) +#define PITCH_BW 11 /* half the band width of correlation surface */ +#define PITCH_SUBFRAMES 4 +#define PITCH_GRAN_PER_SUBFRAME 5 +#define PITCH_SUBFRAME_LEN (PITCH_FRAME_LEN / PITCH_SUBFRAMES) +#define PITCH_UPDATE (PITCH_SUBFRAME_LEN / PITCH_GRAN_PER_SUBFRAME) /* maximum number of peaks to be examined in correlation surface */ -#define PITCH_MAX_NUM_PEAKS 10 -#define PITCH_PEAK_DECAY 0.85 +#define PITCH_MAX_NUM_PEAKS 10 +#define PITCH_PEAK_DECAY 0.85 /* For weighting filter */ -#define PITCH_WLPCORDER 6 -#define PITCH_WLPCWINLEN PITCH_FRAME_LEN -#define PITCH_WLPCASYM 0.3 /* asymmetry parameter */ -#define PITCH_WLPCBUFLEN PITCH_WLPCWINLEN +#define PITCH_WLPCORDER 6 +#define PITCH_WLPCWINLEN PITCH_FRAME_LEN +#define PITCH_WLPCASYM 0.3 /* asymmetry parameter */ +#define PITCH_WLPCBUFLEN PITCH_WLPCWINLEN /* For pitch filter */ /* Extra 50 for fraction and LP filters */ -#define PITCH_BUFFSIZE (PITCH_MAX_LAG + 50) -#define PITCH_INTBUFFSIZE (PITCH_FRAME_LEN+PITCH_BUFFSIZE) +#define PITCH_BUFFSIZE (PITCH_MAX_LAG + 50) +#define PITCH_INTBUFFSIZE (PITCH_FRAME_LEN + PITCH_BUFFSIZE) /* Max rel. step for interpolation */ -#define PITCH_UPSTEP 1.5 +#define PITCH_UPSTEP 1.5 /* Max rel. step for interpolation */ -#define PITCH_DOWNSTEP 0.67 -#define PITCH_FRACS 8 -#define PITCH_FRACORDER 9 -#define PITCH_DAMPORDER 5 -#define PITCH_FILTDELAY 1.5f +#define PITCH_DOWNSTEP 0.67 +#define PITCH_FRACS 8 +#define PITCH_FRACORDER 9 +#define PITCH_DAMPORDER 5 +#define PITCH_FILTDELAY 1.5f /* stepsize for quantization of the pitch Gain */ -#define PITCH_GAIN_STEPSIZE 0.125 - - +#define PITCH_GAIN_STEPSIZE 0.125 /* Order of high pass filter */ -#define HPORDER 2 +#define HPORDER 2 /* some mathematical constants */ /* log2(exp) */ -#define LOG2EXP 1.44269504088896 -#define PI 3.14159265358979 +#define LOG2EXP 1.44269504088896 +#define PI 3.14159265358979 /* Maximum number of iterations allowed to limit payload size */ -#define MAX_PAYLOAD_LIMIT_ITERATION 5 +#define MAX_PAYLOAD_LIMIT_ITERATION 5 /* Redundant Coding */ -#define RCU_BOTTLENECK_BPS 16000 -#define RCU_TRANSCODING_SCALE 0.40f -#define RCU_TRANSCODING_SCALE_INVERSE 2.5f +#define RCU_BOTTLENECK_BPS 16000 +#define RCU_TRANSCODING_SCALE 0.40f +#define RCU_TRANSCODING_SCALE_INVERSE 2.5f -#define RCU_TRANSCODING_SCALE_UB 0.50f -#define RCU_TRANSCODING_SCALE_UB_INVERSE 2.0f +#define RCU_TRANSCODING_SCALE_UB 0.50f +#define RCU_TRANSCODING_SCALE_UB_INVERSE 2.0f /* Define Error codes */ /* 6000 General */ -#define ISAC_MEMORY_ALLOCATION_FAILED 6010 -#define ISAC_MODE_MISMATCH 6020 -#define ISAC_DISALLOWED_BOTTLENECK 6030 -#define ISAC_DISALLOWED_FRAME_LENGTH 6040 -#define ISAC_UNSUPPORTED_SAMPLING_FREQUENCY 6050 +#define ISAC_MEMORY_ALLOCATION_FAILED 6010 +#define ISAC_MODE_MISMATCH 6020 +#define ISAC_DISALLOWED_BOTTLENECK 6030 +#define ISAC_DISALLOWED_FRAME_LENGTH 6040 +#define ISAC_UNSUPPORTED_SAMPLING_FREQUENCY 6050 /* 6200 Bandwidth estimator */ -#define ISAC_RANGE_ERROR_BW_ESTIMATOR 6240 +#define ISAC_RANGE_ERROR_BW_ESTIMATOR 6240 /* 6400 Encoder */ -#define ISAC_ENCODER_NOT_INITIATED 6410 -#define ISAC_DISALLOWED_CODING_MODE 6420 -#define ISAC_DISALLOWED_FRAME_MODE_ENCODER 6430 -#define ISAC_DISALLOWED_BITSTREAM_LENGTH 6440 -#define ISAC_PAYLOAD_LARGER_THAN_LIMIT 6450 -#define ISAC_DISALLOWED_ENCODER_BANDWIDTH 6460 +#define ISAC_ENCODER_NOT_INITIATED 6410 +#define ISAC_DISALLOWED_CODING_MODE 6420 +#define ISAC_DISALLOWED_FRAME_MODE_ENCODER 6430 +#define ISAC_DISALLOWED_BITSTREAM_LENGTH 6440 +#define ISAC_PAYLOAD_LARGER_THAN_LIMIT 6450 +#define ISAC_DISALLOWED_ENCODER_BANDWIDTH 6460 /* 6600 Decoder */ -#define ISAC_DECODER_NOT_INITIATED 6610 -#define ISAC_EMPTY_PACKET 6620 -#define ISAC_DISALLOWED_FRAME_MODE_DECODER 6630 -#define ISAC_RANGE_ERROR_DECODE_FRAME_LENGTH 6640 -#define ISAC_RANGE_ERROR_DECODE_BANDWIDTH 6650 -#define ISAC_RANGE_ERROR_DECODE_PITCH_GAIN 6660 -#define ISAC_RANGE_ERROR_DECODE_PITCH_LAG 6670 -#define ISAC_RANGE_ERROR_DECODE_LPC 6680 -#define ISAC_RANGE_ERROR_DECODE_SPECTRUM 6690 -#define ISAC_LENGTH_MISMATCH 6730 -#define ISAC_RANGE_ERROR_DECODE_BANDWITH 6740 -#define ISAC_DISALLOWED_BANDWIDTH_MODE_DECODER 6750 -#define ISAC_DISALLOWED_LPC_MODEL 6760 +#define ISAC_DECODER_NOT_INITIATED 6610 +#define ISAC_EMPTY_PACKET 6620 +#define ISAC_DISALLOWED_FRAME_MODE_DECODER 6630 +#define ISAC_RANGE_ERROR_DECODE_FRAME_LENGTH 6640 +#define ISAC_RANGE_ERROR_DECODE_BANDWIDTH 6650 +#define ISAC_RANGE_ERROR_DECODE_PITCH_GAIN 6660 +#define ISAC_RANGE_ERROR_DECODE_PITCH_LAG 6670 +#define ISAC_RANGE_ERROR_DECODE_LPC 6680 +#define ISAC_RANGE_ERROR_DECODE_SPECTRUM 6690 +#define ISAC_LENGTH_MISMATCH 6730 +#define ISAC_RANGE_ERROR_DECODE_BANDWITH 6740 +#define ISAC_DISALLOWED_BANDWIDTH_MODE_DECODER 6750 +#define ISAC_DISALLOWED_LPC_MODEL 6760 /* 6800 Call setup formats */ -#define ISAC_INCOMPATIBLE_FORMATS 6810 +#define ISAC_INCOMPATIBLE_FORMATS 6810 -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.c b/webrtc/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.c index 0f6d889..839d5d4 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.c @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "spectrum_ar_model_tables.h" -#include "settings.h" +#include "modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" /********************* AR Coefficient Tables ************************/ /* cdf for quantized reflection coefficient 1 */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.h b/webrtc/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.h index 989cb36..d272be0 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.h @@ -11,15 +11,15 @@ /* * spectrum_ar_model_tables.h * - * This file contains definitions of tables with AR coefficients, + * This file contains definitions of tables with AR coefficients, * Gain coefficients and cosine tables. * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ -#include "structs.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" #define NUM_AR_RC_QUANT_BAUNDARY 12 @@ -45,15 +45,15 @@ extern const uint16_t WebRtcIsac_kQArRc6Cdf[NUM_AR_RC_QUANT_BAUNDARY]; /* quantization boundary levels for reflection coefficients */ extern const int16_t WebRtcIsac_kQArBoundaryLevels[NUM_AR_RC_QUANT_BAUNDARY]; -/* initial indices for AR reflection coefficient quantizer and cdf table search */ +/* initial indices for AR reflection coefficient quantizer and cdf table search + */ extern const uint16_t WebRtcIsac_kQArRcInitIndex[AR_ORDER]; /* pointers to AR cdf tables */ -extern const uint16_t *WebRtcIsac_kQArRcCdfPtr[AR_ORDER]; +extern const uint16_t* WebRtcIsac_kQArRcCdfPtr[AR_ORDER]; /* pointers to AR representation levels tables */ -extern const int16_t *WebRtcIsac_kQArRcLevelsPtr[AR_ORDER]; - +extern const int16_t* WebRtcIsac_kQArRcLevelsPtr[AR_ORDER]; /******************** GAIN Coefficient Tables ***********************/ /* cdf for Gain coefficient */ @@ -66,7 +66,7 @@ extern const int32_t WebRtcIsac_kQGain2Levels[18]; extern const int32_t WebRtcIsac_kQGain2BoundaryLevels[19]; /* pointer to Gain cdf table */ -extern const uint16_t *WebRtcIsac_kQGainCdf_ptr[1]; +extern const uint16_t* WebRtcIsac_kQGainCdf_ptr[1]; /* Gain initial index for gain quantizer and cdf table search */ extern const uint16_t WebRtcIsac_kQGainInitIndex[1]; @@ -75,4 +75,5 @@ extern const uint16_t WebRtcIsac_kQGainInitIndex[1]; /* Cosine table */ extern const int16_t WebRtcIsac_kCos[6][60]; -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ \ + */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/structs.h b/webrtc/modules/audio_coding/codecs/isac/main/source/structs.h index a2cdca2..6861ca4 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/structs.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/structs.h @@ -15,187 +15,174 @@ * */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ +#ifndef MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ +#define MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ -#include "webrtc/modules/audio_coding/codecs/isac/bandwidth_info.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/include/isac.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/settings.h" -#include "webrtc/typedefs.h" +#include "modules/audio_coding/codecs/isac/bandwidth_info.h" +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/third_party/fft/fft.h" typedef struct Bitstreamstruct { - - uint8_t stream[STREAM_SIZE_MAX]; - uint32_t W_upper; - uint32_t streamval; - uint32_t stream_index; + uint8_t stream[STREAM_SIZE_MAX]; + uint32_t W_upper; + uint32_t streamval; + uint32_t stream_index; } Bitstr; typedef struct { + double DataBufferLo[WINLEN]; + double DataBufferHi[WINLEN]; - double DataBufferLo[WINLEN]; - double DataBufferHi[WINLEN]; + double CorrBufLo[ORDERLO + 1]; + double CorrBufHi[ORDERHI + 1]; - double CorrBufLo[ORDERLO+1]; - double CorrBufHi[ORDERHI+1]; + float PreStateLoF[ORDERLO + 1]; + float PreStateLoG[ORDERLO + 1]; + float PreStateHiF[ORDERHI + 1]; + float PreStateHiG[ORDERHI + 1]; + float PostStateLoF[ORDERLO + 1]; + float PostStateLoG[ORDERLO + 1]; + float PostStateHiF[ORDERHI + 1]; + float PostStateHiG[ORDERHI + 1]; - float PreStateLoF[ORDERLO+1]; - float PreStateLoG[ORDERLO+1]; - float PreStateHiF[ORDERHI+1]; - float PreStateHiG[ORDERHI+1]; - float PostStateLoF[ORDERLO+1]; - float PostStateLoG[ORDERLO+1]; - float PostStateHiF[ORDERHI+1]; - float PostStateHiG[ORDERHI+1]; - - double OldEnergy; + double OldEnergy; } MaskFiltstr; - typedef struct { + // state vectors for each of the two analysis filters + double INSTAT1[2 * (QORDER - 1)]; + double INSTAT2[2 * (QORDER - 1)]; + double INSTATLA1[2 * (QORDER - 1)]; + double INSTATLA2[2 * (QORDER - 1)]; + double INLABUF1[QLOOKAHEAD]; + double INLABUF2[QLOOKAHEAD]; - //state vectors for each of the two analysis filters - double INSTAT1[2*(QORDER-1)]; - double INSTAT2[2*(QORDER-1)]; - double INSTATLA1[2*(QORDER-1)]; - double INSTATLA2[2*(QORDER-1)]; - double INLABUF1[QLOOKAHEAD]; - double INLABUF2[QLOOKAHEAD]; - - float INSTAT1_float[2*(QORDER-1)]; - float INSTAT2_float[2*(QORDER-1)]; - float INSTATLA1_float[2*(QORDER-1)]; - float INSTATLA2_float[2*(QORDER-1)]; - float INLABUF1_float[QLOOKAHEAD]; - float INLABUF2_float[QLOOKAHEAD]; + float INSTAT1_float[2 * (QORDER - 1)]; + float INSTAT2_float[2 * (QORDER - 1)]; + float INSTATLA1_float[2 * (QORDER - 1)]; + float INSTATLA2_float[2 * (QORDER - 1)]; + float INLABUF1_float[QLOOKAHEAD]; + float INLABUF2_float[QLOOKAHEAD]; /* High pass filter */ - double HPstates[HPORDER]; - float HPstates_float[HPORDER]; + double HPstates[HPORDER]; + float HPstates_float[HPORDER]; } PreFiltBankstr; - typedef struct { - - //state vectors for each of the two analysis filters - double STATE_0_LOWER[2*POSTQORDER]; - double STATE_0_UPPER[2*POSTQORDER]; + // state vectors for each of the two analysis filters + double STATE_0_LOWER[2 * POSTQORDER]; + double STATE_0_UPPER[2 * POSTQORDER]; /* High pass filter */ - double HPstates1[HPORDER]; - double HPstates2[HPORDER]; + double HPstates1[HPORDER]; + double HPstates2[HPORDER]; - float STATE_0_LOWER_float[2*POSTQORDER]; - float STATE_0_UPPER_float[2*POSTQORDER]; + float STATE_0_LOWER_float[2 * POSTQORDER]; + float STATE_0_UPPER_float[2 * POSTQORDER]; - float HPstates1_float[HPORDER]; - float HPstates2_float[HPORDER]; + float HPstates1_float[HPORDER]; + float HPstates2_float[HPORDER]; } PostFiltBankstr; typedef struct { + // data buffer for pitch filter + double ubuf[PITCH_BUFFSIZE]; - //data buffer for pitch filter - double ubuf[PITCH_BUFFSIZE]; + // low pass state vector + double ystate[PITCH_DAMPORDER]; - //low pass state vector - double ystate[PITCH_DAMPORDER]; - - //old lag and gain - double oldlagp[1]; - double oldgainp[1]; + // old lag and gain + double oldlagp[1]; + double oldgainp[1]; } PitchFiltstr; typedef struct { + // data buffer + double buffer[PITCH_WLPCBUFLEN]; - //data buffer - double buffer[PITCH_WLPCBUFLEN]; + // state vectors + double istate[PITCH_WLPCORDER]; + double weostate[PITCH_WLPCORDER]; + double whostate[PITCH_WLPCORDER]; - //state vectors - double istate[PITCH_WLPCORDER]; - double weostate[PITCH_WLPCORDER]; - double whostate[PITCH_WLPCORDER]; - - //LPC window -> should be a global array because constant - double window[PITCH_WLPCWINLEN]; + // LPC window -> should be a global array because constant + double window[PITCH_WLPCWINLEN]; } WeightFiltstr; typedef struct { + // for inital estimator + double dec_buffer[PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + PITCH_MAX_LAG / 2 - + PITCH_FRAME_LEN / 2 + 2]; + double decimator_state[2 * ALLPASSSECTIONS + 1]; + double hp_state[2]; - //for inital estimator - double dec_buffer[PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + - PITCH_MAX_LAG/2 - PITCH_FRAME_LEN/2+2]; - double decimator_state[2*ALLPASSSECTIONS+1]; - double hp_state[2]; + double whitened_buf[QLOOKAHEAD]; - double whitened_buf[QLOOKAHEAD]; + double inbuf[QLOOKAHEAD]; - double inbuf[QLOOKAHEAD]; - - PitchFiltstr PFstr_wght; - PitchFiltstr PFstr; + PitchFiltstr PFstr_wght; + PitchFiltstr PFstr; WeightFiltstr Wghtstr; } PitchAnalysisStruct; - - /* Have instance of struct together with other iSAC structs */ typedef struct { - /* Previous frame length (in ms) */ - int32_t prev_frame_length; + int32_t prev_frame_length; /* Previous RTP timestamp from received packet (in samples relative beginning) */ - int32_t prev_rec_rtp_number; + int32_t prev_rec_rtp_number; /* Send timestamp for previous packet (in ms using timeGetTime()) */ - uint32_t prev_rec_send_ts; + uint32_t prev_rec_send_ts; /* Arrival time for previous packet (in ms using timeGetTime()) */ - uint32_t prev_rec_arr_ts; + uint32_t prev_rec_arr_ts; /* rate of previous packet, derived from RTP timestamps (in bits/s) */ - float prev_rec_rtp_rate; + float prev_rec_rtp_rate; /* Time sinse the last update of the BN estimate (in ms) */ - uint32_t last_update_ts; + uint32_t last_update_ts; /* Time sinse the last reduction (in ms) */ - uint32_t last_reduction_ts; + uint32_t last_reduction_ts; /* How many times the estimate was update in the beginning */ - int32_t count_tot_updates_rec; + int32_t count_tot_updates_rec; /* The estimated bottle neck rate from there to here (in bits/s) */ - int32_t rec_bw; - float rec_bw_inv; - float rec_bw_avg; - float rec_bw_avg_Q; + int32_t rec_bw; + float rec_bw_inv; + float rec_bw_avg; + float rec_bw_avg_Q; /* The estimated mean absolute jitter value, as seen on this side (in ms) */ - float rec_jitter; - float rec_jitter_short_term; - float rec_jitter_short_term_abs; - float rec_max_delay; - float rec_max_delay_avg_Q; + float rec_jitter; + float rec_jitter_short_term; + float rec_jitter_short_term_abs; + float rec_max_delay; + float rec_max_delay_avg_Q; /* (assumed) bitrate for headers (bps) */ - float rec_header_rate; + float rec_header_rate; /* The estimated bottle neck rate from here to there (in bits/s) */ - float send_bw_avg; + float send_bw_avg; /* The estimated mean absolute jitter value, as seen on the other siee (in ms) */ - float send_max_delay_avg; + float send_max_delay_avg; // number of packets received since last update int num_pkts_rec; @@ -218,72 +205,54 @@ typedef struct { int change_to_WB; - uint32_t senderTimestamp; - uint32_t receiverTimestamp; - //enum IsacSamplingRate incomingStreamSampFreq; - uint16_t numConsecLatePkts; - float consecLatency; - int16_t inWaitLatePkts; + uint32_t senderTimestamp; + uint32_t receiverTimestamp; + // enum IsacSamplingRate incomingStreamSampFreq; + uint16_t numConsecLatePkts; + float consecLatency; + int16_t inWaitLatePkts; IsacBandwidthInfo external_bw_info; } BwEstimatorstr; - typedef struct { - /* boolean, flags if previous packet exceeded B.N. */ - int PrevExceed; + int PrevExceed; /* ms */ - int ExceedAgo; + int ExceedAgo; /* packets left to send in current burst */ - int BurstCounter; + int BurstCounter; /* packets */ - int InitCounter; + int InitCounter; /* ms remaining in buffer when next packet will be sent */ double StillBuffered; } RateModel; - -typedef struct { - - unsigned int SpaceAlloced; - unsigned int MaxPermAlloced; - double Tmp0[MAXFFTSIZE]; - double Tmp1[MAXFFTSIZE]; - double Tmp2[MAXFFTSIZE]; - double Tmp3[MAXFFTSIZE]; - int Perm[MAXFFTSIZE]; - int factor [NFACTOR]; - -} FFTstr; - - /* The following strutc is used to store data from encoding, to make it fast and easy to construct a new bitstream with a different Bandwidth estimate. All values (except framelength and minBytes) is double size to handle 60 ms of data. */ typedef struct { - /* Used to keep track of if it is first or second part of 60 msec packet */ - int startIdx; + int startIdx; /* Frame length in samples */ int16_t framelength; /* Pitch Gain */ - int pitchGain_index[2]; + int pitchGain_index[2]; /* Pitch Lag */ - double meanGain[2]; - int pitchIndex[PITCH_SUBFRAMES*2]; + double meanGain[2]; + int pitchIndex[PITCH_SUBFRAMES * 2]; /* LPC */ - int LPCindex_s[108*2]; /* KLT_ORDER_SHAPE = 108 */ - int LPCindex_g[12*2]; /* KLT_ORDER_GAIN = 12 */ - double LPCcoeffs_lo[(ORDERLO+1)*SUBFRAMES*2]; - double LPCcoeffs_hi[(ORDERHI+1)*SUBFRAMES*2]; + int LPCindex_s[108 * 2]; /* KLT_ORDER_SHAPE = 108 */ + int LPCindex_g[12 * 2]; /* KLT_ORDER_GAIN = 12 */ + double LPCcoeffs_lo[(ORDERLO + 1) * SUBFRAMES * 2]; + double LPCcoeffs_hi[(ORDERHI + 1) * SUBFRAMES * 2]; /* Encode Spec */ int16_t fre[FRAMESAMPLES]; @@ -291,125 +260,109 @@ typedef struct { int16_t AvgPitchGain[2]; /* Used in adaptive mode only */ - int minBytes; + int minBytes; } IsacSaveEncoderData; - typedef struct { + int indexLPCShape[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; + double lpcGain[SUBFRAMES << 1]; + int lpcGainIndex[SUBFRAMES << 1]; - int indexLPCShape[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; - double lpcGain[SUBFRAMES<<1]; - int lpcGainIndex[SUBFRAMES<<1]; - - Bitstr bitStreamObj; + Bitstr bitStreamObj; int16_t realFFT[FRAMESAMPLES_HALF]; int16_t imagFFT[FRAMESAMPLES_HALF]; } ISACUBSaveEncDataStruct; - - typedef struct { - - Bitstr bitstr_obj; - MaskFiltstr maskfiltstr_obj; - PreFiltBankstr prefiltbankstr_obj; - PitchFiltstr pitchfiltstr_obj; + Bitstr bitstr_obj; + MaskFiltstr maskfiltstr_obj; + PreFiltBankstr prefiltbankstr_obj; + PitchFiltstr pitchfiltstr_obj; PitchAnalysisStruct pitchanalysisstr_obj; - FFTstr fftstr_obj; + FFTstr fftstr_obj; IsacSaveEncoderData SaveEnc_obj; - int buffer_index; - int16_t current_framesamples; + int buffer_index; + int16_t current_framesamples; - float data_buffer_float[FRAMESAMPLES_30ms]; + float data_buffer_float[FRAMESAMPLES_30ms]; - int frame_nb; - double bottleneck; - int16_t new_framelength; - double s2nr; + int frame_nb; + double bottleneck; + int16_t new_framelength; + double s2nr; /* Maximum allowed number of bits for a 30 msec packet */ - int16_t payloadLimitBytes30; + int16_t payloadLimitBytes30; /* Maximum allowed number of bits for a 30 msec packet */ - int16_t payloadLimitBytes60; + int16_t payloadLimitBytes60; /* Maximum allowed number of bits for both 30 and 60 msec packet */ - int16_t maxPayloadBytes; + int16_t maxPayloadBytes; /* Maximum allowed rate in bytes per 30 msec packet */ - int16_t maxRateInBytes; + int16_t maxRateInBytes; /*--- - If set to 1 iSAC will not addapt the frame-size, if used in + If set to 1 iSAC will not adapt the frame-size, if used in channel-adaptive mode. The initial value will be used for all rates. ---*/ - int16_t enforceFrameSize; + int16_t enforceFrameSize; /*----- This records the BWE index the encoder injected into the bit-stream. It will be used in RCU. The same BWE index of main payload will be in - the redundant payload. We can not retrive it from BWE because it is + the redundant payload. We can not retrieve it from BWE because it is a recursive procedure (WebRtcIsac_GetDownlinkBwJitIndexImpl) and has to be called only once per each encode. -----*/ - int16_t lastBWIdx; + int16_t lastBWIdx; } ISACLBEncStruct; typedef struct { - - Bitstr bitstr_obj; - MaskFiltstr maskfiltstr_obj; - PreFiltBankstr prefiltbankstr_obj; - FFTstr fftstr_obj; + Bitstr bitstr_obj; + MaskFiltstr maskfiltstr_obj; + PreFiltBankstr prefiltbankstr_obj; + FFTstr fftstr_obj; ISACUBSaveEncDataStruct SaveEnc_obj; - int buffer_index; - float data_buffer_float[MAX_FRAMESAMPLES + - LB_TOTAL_DELAY_SAMPLES]; - double bottleneck; + int buffer_index; + float data_buffer_float[MAX_FRAMESAMPLES + LB_TOTAL_DELAY_SAMPLES]; + double bottleneck; /* Maximum allowed number of bits for a 30 msec packet */ - //int16_t payloadLimitBytes30; + // int16_t payloadLimitBytes30; /* Maximum allowed number of bits for both 30 and 60 msec packet */ - //int16_t maxPayloadBytes; - int16_t maxPayloadSizeBytes; + // int16_t maxPayloadBytes; + int16_t maxPayloadSizeBytes; - double lastLPCVec[UB_LPC_ORDER]; - int16_t numBytesUsed; - int16_t lastJitterInfo; + double lastLPCVec[UB_LPC_ORDER]; + int16_t numBytesUsed; + int16_t lastJitterInfo; } ISACUBEncStruct; - - typedef struct { - - Bitstr bitstr_obj; - MaskFiltstr maskfiltstr_obj; + Bitstr bitstr_obj; + MaskFiltstr maskfiltstr_obj; PostFiltBankstr postfiltbankstr_obj; - PitchFiltstr pitchfiltstr_obj; - FFTstr fftstr_obj; + PitchFiltstr pitchfiltstr_obj; + FFTstr fftstr_obj; } ISACLBDecStruct; typedef struct { - - Bitstr bitstr_obj; - MaskFiltstr maskfiltstr_obj; + Bitstr bitstr_obj; + MaskFiltstr maskfiltstr_obj; PostFiltBankstr postfiltbankstr_obj; - FFTstr fftstr_obj; + FFTstr fftstr_obj; } ISACUBDecStruct; - - typedef struct { - ISACLBEncStruct ISACencLB_obj; ISACLBDecStruct ISACdecLB_obj; } ISACLBStruct; - typedef struct { - ISACUBEncStruct ISACencUB_obj; ISACUBDecStruct ISACdecUB_obj; } ISACUBStruct; @@ -421,14 +374,14 @@ typedef struct { */ typedef struct { /* 6 lower-band & 6 upper-band */ - double loFiltGain[SUBFRAMES]; - double hiFiltGain[SUBFRAMES]; + double loFiltGain[SUBFRAMES]; + double hiFiltGain[SUBFRAMES]; /* Upper boundary of interval W */ uint32_t W_upper; uint32_t streamval; /* Index to the current position in bytestream */ uint32_t stream_index; - uint8_t stream[3]; + uint8_t stream[3]; } transcode_obj; typedef struct { @@ -444,46 +397,46 @@ typedef struct { typedef struct { // lower-band codec instance - ISACLBStruct instLB; + ISACLBStruct instLB; // upper-band codec instance - ISACUBStruct instUB; + ISACUBStruct instUB; // Bandwidth Estimator and model for the rate. - BwEstimatorstr bwestimator_obj; - RateModel rate_data_obj; - double MaxDelay; + BwEstimatorstr bwestimator_obj; + RateModel rate_data_obj; + double MaxDelay; /* 0 = adaptive; 1 = instantaneous */ - int16_t codingMode; + int16_t codingMode; // overall bottleneck of the codec - int32_t bottleneck; + int32_t bottleneck; // QMF Filter state - int32_t analysisFBState1[FB_STATE_SIZE_WORD32]; - int32_t analysisFBState2[FB_STATE_SIZE_WORD32]; - int32_t synthesisFBState1[FB_STATE_SIZE_WORD32]; - int32_t synthesisFBState2[FB_STATE_SIZE_WORD32]; + int32_t analysisFBState1[FB_STATE_SIZE_WORD32]; + int32_t analysisFBState2[FB_STATE_SIZE_WORD32]; + int32_t synthesisFBState1[FB_STATE_SIZE_WORD32]; + int32_t synthesisFBState2[FB_STATE_SIZE_WORD32]; // Error Code - int16_t errorCode; + int16_t errorCode; // bandwidth of the encoded audio 8, 12 or 16 kHz - enum ISACBandwidth bandwidthKHz; + enum ISACBandwidth bandwidthKHz; // Sampling rate of audio, encoder and decode, 8 or 16 kHz enum IsacSamplingRate encoderSamplingRateKHz; enum IsacSamplingRate decoderSamplingRateKHz; // Flag to keep track of initializations, lower & upper-band // encoder and decoder. - int16_t initFlag; + int16_t initFlag; // Flag to to indicate signal bandwidth switch - int16_t resetFlag_8kHz; + int16_t resetFlag_8kHz; // Maximum allowed rate, measured in Bytes per 30 ms. - int16_t maxRateBytesPer30Ms; + int16_t maxRateBytesPer30Ms; // Maximum allowed payload-size, measured in Bytes. - int16_t maxPayloadSizeBytes; + int16_t maxPayloadSizeBytes; /* The expected sampling rate of the input signal. Valid values are 16000 * and 32000. This is not the operation sampling rate of the codec. */ uint16_t in_sample_rate_hz; @@ -492,4 +445,4 @@ typedef struct { TransformTables transform_tables; } ISACMainStruct; -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ */ +#endif /* MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/transform.c b/webrtc/modules/audio_coding/codecs/isac/main/source/transform.c index 8992897..082ad94 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/transform.c +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/transform.c @@ -8,12 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "settings.h" -#include "fft.h" -#include "codec.h" -#include "os_specific_inline.h" #include +#include "modules/audio_coding/codecs/isac/main/source/settings.h" +#include "modules/audio_coding/codecs/isac/main/source/codec.h" +#include "modules/audio_coding/codecs/isac/main/source/os_specific_inline.h" +#include "modules/third_party/fft/fft.h" + void WebRtcIsac_InitTransform(TransformTables* tables) { int k; double fact, phase; diff --git a/webrtc/modules/audio_coding/meson.build b/webrtc/modules/audio_coding/meson.build index fb46df8..983d3fb 100644 --- a/webrtc/modules/audio_coding/meson.build +++ b/webrtc/modules/audio_coding/meson.build @@ -2,12 +2,21 @@ webrtc_audio_coding_sources = [ 'codecs/isac/main/source/arith_routines.c', 'codecs/isac/main/source/arith_routines_hist.c', 'codecs/isac/main/source/arith_routines_logist.c', + 'codecs/isac/main/source/audio_decoder_isac.cc', + 'codecs/isac/main/source/audio_encoder_isac.cc', + 'codecs/isac/main/source/bandwidth_estimator.c', + 'codecs/isac/main/source/crc.c', + 'codecs/isac/main/source/decode_bwe.c', + 'codecs/isac/main/source/decode.c', + 'codecs/isac/main/source/encode.c', 'codecs/isac/main/source/encode_lpc_swb.c', 'codecs/isac/main/source/entropy_coding.c', - 'codecs/isac/main/source/filter_functions.c', 'codecs/isac/main/source/filterbanks.c', - 'codecs/isac/main/source/filterbank_tables.c', + 'codecs/isac/main/source/filter_functions.c', 'codecs/isac/main/source/intialize.c', + 'codecs/isac/main/source/isac.c', + 'codecs/isac/main/source/isac_vad.c', + 'codecs/isac/main/source/lattice.c', 'codecs/isac/main/source/lpc_analysis.c', 'codecs/isac/main/source/lpc_gain_swb_tables.c', 'codecs/isac/main/source/lpc_shape_swb12_tables.c', @@ -18,24 +27,12 @@ webrtc_audio_coding_sources = [ 'codecs/isac/main/source/pitch_gain_tables.c', 'codecs/isac/main/source/pitch_lag_tables.c', 'codecs/isac/main/source/spectrum_ar_model_tables.c', - 'codecs/audio_decoder.cc', - 'codecs/audio_encoder.cc', - 'codecs/isac/main/source/audio_decoder_isac.cc', - 'codecs/isac/main/source/audio_encoder_isac.cc', - 'codecs/isac/main/source/bandwidth_estimator.c', - 'codecs/isac/main/source/crc.c', - 'codecs/isac/main/source/decode.c', - 'codecs/isac/main/source/decode_bwe.c', - 'codecs/isac/main/source/encode.c', - 'codecs/isac/main/source/fft.c', - 'codecs/isac/main/source/isac.c', - 'codecs/isac/main/source/lattice.c', 'codecs/isac/main/source/transform.c', ] libwebrtc_audio_coding = library('webrtc_audio_coding', webrtc_audio_coding_sources, - dependencies: [base_dep, common_audio_dep] + common_deps, + dependencies: [base_dep, api_dep, common_audio_dep, system_wrappers_dep, fft_dep] + common_deps, include_directories: webrtc_inc, c_args: common_cflags, cpp_args: common_cxxflags, @@ -50,13 +47,13 @@ webrtc_audio_coding_dep = declare_dependency( ) install_headers(['codecs/isac/bandwidth_info.h'], - subdir: 'webrtc_audio_processing/webrtc/modules/audio_coding/codecs/isac' + subdir: 'webrtc_audio_processing/modules/audio_coding/codecs/isac' ) install_headers(['codecs/isac/main/source/settings.h'], - subdir: 'webrtc_audio_processing/webrtc/modules/audio_coding/codecs/isac/main/source' + subdir: 'webrtc_audio_processing/modules/audio_coding/codecs/isac/main/source' ) install_headers(['codecs/isac/main/include/isac.h'], - subdir: 'webrtc_audio_processing/webrtc/modules/audio_coding/codecs/isac/main/include' + subdir: 'webrtc_audio_processing/modules/audio_coding/codecs/isac/main/include' ) diff --git a/webrtc/modules/audio_processing/BUILD.gn b/webrtc/modules/audio_processing/BUILD.gn index 2d0c602..dbb1882 100644 --- a/webrtc/modules/audio_processing/BUILD.gn +++ b/webrtc/modules/audio_processing/BUILD.gn @@ -6,281 +6,606 @@ # in the file PATENTS. All contributing project authors may # be found in the AUTHORS file in the root of the source tree. -import("//build/config/arm.gni") -import("//third_party/protobuf/proto_library.gni") -import("../../build/webrtc.gni") - -declare_args() { - # Outputs some low-level debug files. - aec_debug_dump = false - - # Disables the usual mode where we trust the reported system delay - # values the AEC receives. The corresponding define is set appropriately - # in the code, but it can be force-enabled here for testing. - aec_untrusted_delay_for_testing = false +import("../../webrtc.gni") +if (rtc_enable_protobuf) { + import("//third_party/protobuf/proto_library.gni") } -source_set("audio_processing") { +config("apm_debug_dump") { + if (apm_debug_dump) { + defines = [ "WEBRTC_APM_DEBUG_DUMP=1" ] + } else { + defines = [ "WEBRTC_APM_DEBUG_DUMP=0" ] + } +} + +rtc_library("config") { + visibility = [ ":*" ] + sources = [ + "include/config.cc", + "include/config.h", + ] + deps = [ "../../rtc_base/system:rtc_export" ] +} + +rtc_library("api") { + visibility = [ "*" ] + sources = [ + "include/audio_processing.cc", + "include/audio_processing.h", + ] + deps = [ + ":audio_frame_view", + ":audio_processing_statistics", + ":config", + "../../api:array_view", + "../../api:scoped_refptr", + "../../api/audio:aec3_config", + "../../api/audio:audio_frame_api", + "../../api/audio:echo_control", + "../../rtc_base:deprecation", + "../../rtc_base:rtc_base_approved", + "../../rtc_base/system:arch", + "../../rtc_base/system:file_wrapper", + "../../rtc_base/system:rtc_export", + "agc:gain_control_interface", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("audio_frame_proxies") { + visibility = [ "*" ] + sources = [ + "include/audio_frame_proxies.cc", + "include/audio_frame_proxies.h", + ] + deps = [ + ":api", + ":audio_frame_view", + "../../api/audio:audio_frame_api", + ] +} + +rtc_library("audio_buffer") { + visibility = [ "*" ] + + configs += [ ":apm_debug_dump" ] + sources = [ - "aec/aec_core.c", - "aec/aec_core.h", - "aec/aec_core_internal.h", - "aec/aec_rdft.c", - "aec/aec_rdft.h", - "aec/aec_resampler.c", - "aec/aec_resampler.h", - "aec/echo_cancellation.c", - "aec/echo_cancellation_internal.h", - "aec/include/echo_cancellation.h", - "aecm/aecm_core.c", - "aecm/aecm_core.h", - "aecm/echo_control_mobile.c", - "aecm/include/echo_control_mobile.h", - "agc/agc.cc", - "agc/agc.h", - "agc/agc_manager_direct.cc", - "agc/agc_manager_direct.h", - "agc/gain_map_internal.h", - "agc/histogram.cc", - "agc/histogram.h", - "agc/legacy/analog_agc.c", - "agc/legacy/analog_agc.h", - "agc/legacy/digital_agc.c", - "agc/legacy/digital_agc.h", - "agc/legacy/gain_control.h", - "agc/utility.cc", - "agc/utility.h", "audio_buffer.cc", "audio_buffer.h", - "audio_processing_impl.cc", - "audio_processing_impl.h", - "beamformer/array_util.cc", - "beamformer/array_util.h", - "beamformer/beamformer.h", - "beamformer/complex_matrix.h", - "beamformer/covariance_matrix_generator.cc", - "beamformer/covariance_matrix_generator.h", - "beamformer/matrix.h", - "beamformer/nonlinear_beamformer.cc", - "beamformer/nonlinear_beamformer.h", - "common.h", - "echo_cancellation_impl.cc", - "echo_cancellation_impl.h", - "echo_control_mobile_impl.cc", - "echo_control_mobile_impl.h", - "gain_control_impl.cc", - "gain_control_impl.h", - "high_pass_filter_impl.cc", - "high_pass_filter_impl.h", - "include/audio_processing.h", - "intelligibility/intelligibility_enhancer.cc", - "intelligibility/intelligibility_enhancer.h", - "intelligibility/intelligibility_utils.cc", - "intelligibility/intelligibility_utils.h", - "level_estimator_impl.cc", - "level_estimator_impl.h", - "logging/aec_logging.h", - "logging/aec_logging_file_handling.cc", - "logging/aec_logging_file_handling.h", - "noise_suppression_impl.cc", - "noise_suppression_impl.h", - "processing_component.cc", - "processing_component.h", - "rms_level.cc", - "rms_level.h", "splitting_filter.cc", "splitting_filter.h", "three_band_filter_bank.cc", "three_band_filter_bank.h", - "transient/common.h", - "transient/daubechies_8_wavelet_coeffs.h", - "transient/dyadic_decimator.h", - "transient/moving_moments.cc", - "transient/moving_moments.h", - "transient/transient_detector.cc", - "transient/transient_detector.h", - "transient/transient_suppressor.cc", - "transient/transient_suppressor.h", - "transient/wpd_node.cc", - "transient/wpd_node.h", - "transient/wpd_tree.cc", - "transient/wpd_tree.h", - "typing_detection.cc", - "typing_detection.h", - "utility/delay_estimator.c", - "utility/delay_estimator.h", - "utility/delay_estimator_internal.h", - "utility/delay_estimator_wrapper.c", - "utility/delay_estimator_wrapper.h", - "vad/common.h", - "vad/gmm.cc", - "vad/gmm.h", - "vad/noise_gmm_tables.h", - "vad/pitch_based_vad.cc", - "vad/pitch_based_vad.h", - "vad/pitch_internal.cc", - "vad/pitch_internal.h", - "vad/pole_zero_filter.cc", - "vad/pole_zero_filter.h", - "vad/standalone_vad.cc", - "vad/standalone_vad.h", - "vad/vad_audio_proc.cc", - "vad/vad_audio_proc.h", - "vad/vad_audio_proc_internal.h", - "vad/vad_circular_buffer.cc", - "vad/vad_circular_buffer.h", - "vad/voice_activity_detector.cc", - "vad/voice_activity_detector.h", - "vad/voice_gmm_tables.h", - "voice_detection_impl.cc", - "voice_detection_impl.h", ] - configs += [ "../..:common_config" ] - public_configs = [ "../..:common_inherited_config" ] + defines = [] + + deps = [ + ":api", + "../../api:array_view", + "../../common_audio", + "../../common_audio:common_audio_c", + "../../rtc_base:checks", + ] +} + +rtc_library("high_pass_filter") { + visibility = [ "*" ] + + sources = [ + "high_pass_filter.cc", + "high_pass_filter.h", + ] + + defines = [] + + deps = [ + ":audio_buffer", + "../../api:array_view", + "../../rtc_base:checks", + "utility:cascaded_biquad_filter", + ] +} + +rtc_source_set("aec_dump_interface") { + visibility = [ "*" ] + sources = [ + "include/aec_dump.cc", + "include/aec_dump.h", + ] + + deps = [ + ":api", + ":audio_frame_view", + "../../rtc_base:deprecation", + ] +} + +rtc_library("audio_processing") { + visibility = [ "*" ] + configs += [ ":apm_debug_dump" ] + sources = [ + "audio_processing_builder_impl.cc", + "audio_processing_impl.cc", + "audio_processing_impl.h", + "common.h", + "echo_control_mobile_impl.cc", + "echo_control_mobile_impl.h", + "echo_detector/circular_buffer.cc", + "echo_detector/circular_buffer.h", + "echo_detector/mean_variance_estimator.cc", + "echo_detector/mean_variance_estimator.h", + "echo_detector/moving_max.cc", + "echo_detector/moving_max.h", + "echo_detector/normalized_covariance_estimator.cc", + "echo_detector/normalized_covariance_estimator.h", + "gain_control_impl.cc", + "gain_control_impl.h", + "gain_controller2.cc", + "gain_controller2.h", + "level_estimator.cc", + "level_estimator.h", + "render_queue_item_verifier.h", + "residual_echo_detector.cc", + "residual_echo_detector.h", + "typing_detection.cc", + "typing_detection.h", + ] defines = [] deps = [ - "../..:webrtc_common", - "../audio_coding:isac", + ":aec_dump_interface", + ":api", + ":apm_logging", + ":audio_buffer", + ":audio_frame_proxies", + ":audio_frame_view", + ":audio_processing_statistics", + ":config", + ":high_pass_filter", + ":optionally_built_submodule_creators", + ":rms_level", + ":voice_detection", + "../../api:array_view", + "../../api:function_view", + "../../api/audio:aec3_config", + "../../api/audio:audio_frame_api", + "../../api/audio:echo_control", + "../../audio/utility:audio_frame_operations", + "../../common_audio:common_audio_c", + "../../common_audio/third_party/ooura:fft_size_256", + "../../rtc_base:checks", + "../../rtc_base:deprecation", + "../../rtc_base:gtest_prod", + "../../rtc_base:ignore_wundef", + "../../rtc_base:refcount", + "../../rtc_base:safe_minmax", + "../../rtc_base:sanitizer", + "../../rtc_base/synchronization:mutex", + "../../rtc_base/system:rtc_export", + "../../system_wrappers", + "../../system_wrappers:field_trial", + "../../system_wrappers:metrics", + "aec3", + "aec_dump:aec_dump", + "aecm:aecm_core", + "agc", + "agc:gain_control_interface", + "agc:legacy_agc", + "agc2:adaptive_digital", + "agc2:fixed_digital", + "agc2:gain_applier", + "ns", + "transient:transient_suppressor_api", + "vad", ] - - if (aec_debug_dump) { - defines += [ "WEBRTC_AEC_DEBUG_DUMP" ] - } - - if (aec_untrusted_delay_for_testing) { - defines += [ "WEBRTC_UNTRUSTED_DELAY" ] - } - - if (rtc_enable_protobuf) { - defines += [ "WEBRTC_AUDIOPROC_DEBUG_DUMP" ] - deps += [ ":audioproc_debug_proto" ] - } - - if (rtc_prefer_fixed_point) { - defines += [ "WEBRTC_NS_FIXED" ] - sources += [ - "ns/include/noise_suppression_x.h", - "ns/noise_suppression_x.c", - "ns/nsx_core.c", - "ns/nsx_core.h", - "ns/nsx_defines.h", - ] - if (current_cpu == "mipsel") { - sources += [ "ns/nsx_core_mips.c" ] - } else { - sources += [ "ns/nsx_core_c.c" ] - } - } else { - defines += [ "WEBRTC_NS_FLOAT" ] - sources += [ - "ns/defines.h", - "ns/include/noise_suppression.h", - "ns/noise_suppression.c", - "ns/ns_core.c", - "ns/ns_core.h", - "ns/windows_private.h", - ] - } - - if (current_cpu == "x86" || current_cpu == "x64") { - deps += [ ":audio_processing_sse2" ] - } - - if (rtc_build_with_neon) { - deps += [ ":audio_processing_neon" ] - } - - if (current_cpu == "mipsel") { - sources += [ "aecm/aecm_core_mips.c" ] - if (mips_float_abi == "hard") { - sources += [ - "aec/aec_core_mips.c", - "aec/aec_rdft_mips.c", - ] - } - } else { - sources += [ "aecm/aecm_core_c.c" ] - } - - if (is_win) { - cflags = [ - # TODO(jschuh): Bug 1348: fix this warning. - "/wd4267", # size_t to int truncations - ] - } - - if (is_clang) { - # Suppress warnings from Chrome's Clang plugins. - # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. - configs -= [ "//build/config/clang:find_bad_constructs" ] - } + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] deps += [ - "../../base:rtc_base_approved", "../../common_audio", + "../../common_audio:fir_filter", + "../../common_audio:fir_filter_factory", + "../../rtc_base:rtc_base_approved", "../../system_wrappers", ] + + if (rtc_enable_protobuf) { + deps += [ "aec_dump:aec_dump_impl" ] + } else { + deps += [ "aec_dump:null_aec_dump_factory" ] + } +} + +rtc_library("voice_detection") { + sources = [ + "voice_detection.cc", + "voice_detection.h", + ] + deps = [ + ":api", + ":audio_buffer", + "../../api/audio:audio_frame_api", + "../../common_audio:common_audio_c", + "../../rtc_base:checks", + ] +} + +rtc_library("optionally_built_submodule_creators") { + sources = [ + "optionally_built_submodule_creators.cc", + "optionally_built_submodule_creators.h", + ] + deps = [ + "transient:transient_suppressor_api", + "transient:transient_suppressor_impl", + ] +} + +rtc_source_set("rms_level") { + visibility = [ "*" ] + sources = [ + "rms_level.cc", + "rms_level.h", + ] + deps = [ + "../../api:array_view", + "../../rtc_base:checks", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("audio_processing_statistics") { + visibility = [ "*" ] + sources = [ + "include/audio_processing_statistics.cc", + "include/audio_processing_statistics.h", + ] + deps = [ "../../rtc_base/system:rtc_export" ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_source_set("audio_frame_view") { + sources = [ "include/audio_frame_view.h" ] + deps = [ "../../api:array_view" ] } if (rtc_enable_protobuf) { proto_library("audioproc_debug_proto") { - sources = [ - "debug.proto", - ] + sources = [ "debug.proto" ] - proto_out_dir = "webrtc/audio_processing" + proto_out_dir = "modules/audio_processing" } } -if (current_cpu == "x86" || current_cpu == "x64") { - source_set("audio_processing_sse2") { - sources = [ - "aec/aec_core_sse2.c", - "aec/aec_rdft_sse2.c", - ] - - if (is_posix) { - cflags = [ "-msse2" ] - } - - configs += [ "../..:common_config" ] - public_configs = [ "../..:common_inherited_config" ] - } +rtc_library("apm_logging") { + configs += [ ":apm_debug_dump" ] + sources = [ + "logging/apm_data_dumper.cc", + "logging/apm_data_dumper.h", + ] + deps = [ + "../../api:array_view", + "../../common_audio", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + ] + defines = [] } -if (rtc_build_with_neon) { - source_set("audio_processing_neon") { - sources = [ - "aec/aec_core_neon.c", - "aec/aec_rdft_neon.c", - "aecm/aecm_core_neon.c", - "ns/nsx_core_neon.c", +if (rtc_include_tests) { + rtc_source_set("mocks") { + testonly = true + sources = [ "include/mock_audio_processing.h" ] + deps = [ + ":aec_dump_interface", + ":api", + ":audio_buffer", + ":audio_processing", + ":audio_processing_statistics", + "../../test:test_support", + ] + } + + group("audio_processing_tests") { + testonly = true + deps = [ + ":audioproc_test_utils", + "transient:click_annotate", + "transient:transient_suppression_test", ] - if (current_cpu != "arm64") { - # Enable compilation for the NEON instruction set. This is needed - # since //build/config/arm.gni only enables NEON for iOS, not Android. - # This provides the same functionality as webrtc/build/arm_neon.gypi. - configs -= [ "//build/config/compiler:compiler_arm_fpu" ] - cflags = [ "-mfpu=neon" ] + if (rtc_enable_protobuf) { + deps += [ + ":audioproc_unittest_proto", + "aec_dump:aec_dump_unittests", + "test/conversational_speech", + "test/py_quality_assessment", + ] + } + } + + rtc_library("audio_processing_unittests") { + testonly = true + + configs += [ ":apm_debug_dump" ] + sources = [ + "audio_buffer_unittest.cc", + "audio_frame_view_unittest.cc", + "config_unittest.cc", + "echo_control_mobile_unittest.cc", + "gain_controller2_unittest.cc", + "splitting_filter_unittest.cc", + "test/fake_recording_device_unittest.cc", + ] + + deps = [ + ":analog_mic_simulation", + ":api", + ":apm_logging", + ":audio_buffer", + ":audio_frame_view", + ":audio_processing", + ":audioproc_test_utils", + ":config", + ":high_pass_filter", + ":mocks", + ":voice_detection", + "../../api:array_view", + "../../api:scoped_refptr", + "../../api/audio:aec3_config", + "../../api/audio:aec3_factory", + "../../common_audio", + "../../common_audio:common_audio_c", + "../../rtc_base", + "../../rtc_base:checks", + "../../rtc_base:gtest_prod", + "../../rtc_base:ignore_wundef", + "../../rtc_base:protobuf_utils", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_base_tests_utils", + "../../rtc_base:safe_minmax", + "../../rtc_base:task_queue_for_test", + "../../rtc_base/synchronization:mutex", + "../../rtc_base/system:arch", + "../../rtc_base/system:file_wrapper", + "../../system_wrappers", + "../../test:fileutils", + "../../test:rtc_expect_death", + "../../test:test_support", + "../audio_coding:neteq_input_audio_tools", + "aec_dump:mock_aec_dump_unittests", + "agc:agc_unittests", + "agc2:adaptive_digital_unittests", + "agc2:biquad_filter_unittests", + "agc2:fixed_digital_unittests", + "agc2:noise_estimator_unittests", + "agc2:rnn_vad_with_level_unittests", + "agc2:test_utils", + "agc2/rnn_vad:unittests", + "test/conversational_speech:unittest", + "transient:transient_suppression_unittests", + "utility:legacy_delay_estimator_unittest", + "utility:pffft_wrapper_unittest", + "vad:vad_unittests", + "//testing/gtest", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + + defines = [] + + if (rtc_prefer_fixed_point) { + defines += [ "WEBRTC_AUDIOPROC_FIXED_PROFILE" ] + } else { + defines += [ "WEBRTC_AUDIOPROC_FLOAT_PROFILE" ] } - # Disable LTO on NEON targets due to compiler bug. - # TODO(fdegans): Enable this. See crbug.com/408997. - if (rtc_use_lto) { - cflags -= [ - "-flto", - "-ffat-lto-objects", + if (rtc_enable_protobuf) { + defines += [ "WEBRTC_AUDIOPROC_DEBUG_DUMP" ] + deps += [ + ":audioproc_debug_proto", + ":audioproc_protobuf_utils", + ":audioproc_test_utils", + ":audioproc_unittest_proto", + ":optionally_built_submodule_creators", + ":rms_level", + ":runtime_settings_protobuf_utils", + "../../api/audio:audio_frame_api", + "../../api/audio:echo_control", + "../../rtc_base:rtc_base_tests_utils", + "../../rtc_base:rtc_task_queue", + "aec_dump", + "aec_dump:aec_dump_unittests", + ] + absl_deps += [ "//third_party/abseil-cpp/absl/flags:flag" ] + sources += [ + "audio_processing_impl_locking_unittest.cc", + "audio_processing_impl_unittest.cc", + "audio_processing_unittest.cc", + "echo_control_mobile_bit_exact_unittest.cc", + "echo_detector/circular_buffer_unittest.cc", + "echo_detector/mean_variance_estimator_unittest.cc", + "echo_detector/moving_max_unittest.cc", + "echo_detector/normalized_covariance_estimator_unittest.cc", + "gain_control_unittest.cc", + "high_pass_filter_unittest.cc", + "level_estimator_unittest.cc", + "residual_echo_detector_unittest.cc", + "rms_level_unittest.cc", + "test/debug_dump_replayer.cc", + "test/debug_dump_replayer.h", + "test/debug_dump_test.cc", + "test/echo_canceller_test_tools.cc", + "test/echo_canceller_test_tools.h", + "test/echo_canceller_test_tools_unittest.cc", + "test/echo_control_mock.h", + "test/test_utils.h", + "voice_detection_unittest.cc", + ] + } + } + + rtc_library("audio_processing_perf_tests") { + testonly = true + configs += [ ":apm_debug_dump" ] + + sources = [ "audio_processing_performance_unittest.cc" ] + deps = [ + ":audio_processing", + ":audioproc_test_utils", + "../../api:array_view", + "../../rtc_base:protobuf_utils", + "../../rtc_base:rtc_base_approved", + "../../system_wrappers", + "../../test:perf_test", + "../../test:test_support", + ] + } + + rtc_library("analog_mic_simulation") { + sources = [ + "test/fake_recording_device.cc", + "test/fake_recording_device.h", + ] + deps = [ + "../../api:array_view", + "../../api/audio:audio_frame_api", + "../../common_audio", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_minmax", + "agc:gain_map", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + } + + if (rtc_enable_protobuf) { + rtc_library("audioproc_f_impl") { + testonly = true + configs += [ ":apm_debug_dump" ] + sources = [ + "test/aec_dump_based_simulator.cc", + "test/aec_dump_based_simulator.h", + "test/api_call_statistics.cc", + "test/api_call_statistics.h", + "test/audio_processing_simulator.cc", + "test/audio_processing_simulator.h", + "test/audioproc_float_impl.cc", + "test/audioproc_float_impl.h", + "test/wav_based_simulator.cc", + "test/wav_based_simulator.h", + ] + + deps = [ + ":analog_mic_simulation", + ":api", + ":apm_logging", + ":audio_processing", + ":audioproc_debug_proto", + ":audioproc_protobuf_utils", + ":audioproc_test_utils", + ":runtime_settings_protobuf_utils", + "../../api/audio:aec3_config_json", + "../../api/audio:aec3_factory", + "../../common_audio", + "../../rtc_base:checks", + "../../rtc_base:ignore_wundef", + "../../rtc_base:protobuf_utils", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_json", + "../../rtc_base:task_queue_for_test", + "../../rtc_base/system:file_wrapper", + "../../system_wrappers", + "../../system_wrappers:field_trial", + "../../test:test_support", + "aec_dump", + "aec_dump:aec_dump_impl", + "//testing/gtest", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/flags:parse", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] + } # audioproc_f_impl + } + + if (rtc_enable_protobuf) { + proto_library("audioproc_unittest_proto") { + sources = [ "test/unittest.proto" ] + proto_out_dir = "modules/audio_processing/test" + } + + rtc_library("audioproc_protobuf_utils") { + sources = [ + "test/protobuf_utils.cc", + "test/protobuf_utils.h", + ] + + deps = [ + ":audioproc_debug_proto", + "../../rtc_base:checks", + "../../rtc_base:ignore_wundef", + "../../rtc_base:protobuf_utils", + "../../rtc_base:rtc_base_approved", + "../../rtc_base/system:arch", ] } - configs += [ "../..:common_config" ] - public_configs = [ "../..:common_inherited_config" ] + rtc_library("runtime_settings_protobuf_utils") { + testonly = true + sources = [ + "test/runtime_setting_util.cc", + "test/runtime_setting_util.h", + ] - deps = [ - "../../common_audio", - ] + deps = [ + ":api", + ":audioproc_debug_proto", + ":audioproc_protobuf_utils", + "../../rtc_base:checks", + ] + } } } + +rtc_library("audioproc_test_utils") { + visibility = [ "*" ] + testonly = true + sources = [ + "test/audio_buffer_tools.cc", + "test/audio_buffer_tools.h", + "test/audio_processing_builder_for_testing.cc", + "test/audio_processing_builder_for_testing.h", + "test/bitexactness_tools.cc", + "test/bitexactness_tools.h", + "test/performance_timer.cc", + "test/performance_timer.h", + "test/simulator_buffers.cc", + "test/simulator_buffers.h", + "test/test_utils.cc", + "test/test_utils.h", + ] + + configs += [ ":apm_debug_dump" ] + + deps = [ + ":api", + ":audio_buffer", + ":audio_processing", + "../../api:array_view", + "../../api/audio:audio_frame_api", + "../../common_audio", + "../../rtc_base:checks", + "../../rtc_base:rtc_base_approved", + "../../rtc_base/system:arch", + "../../system_wrappers", + "../../test:fileutils", + "../../test:test_support", + "../audio_coding:neteq_input_audio_tools", + "//testing/gtest", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} diff --git a/webrtc/modules/audio_processing/aec/aec_common.h b/webrtc/modules/audio_processing/aec/aec_common.h deleted file mode 100644 index 1e24ca9..0000000 --- a/webrtc/modules/audio_processing/aec/aec_common.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_COMMON_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_COMMON_H_ - -#include "webrtc/typedefs.h" - -#ifdef _MSC_VER /* visual c++ */ -#define ALIGN16_BEG __declspec(align(16)) -#define ALIGN16_END -#else /* gcc or icc */ -#define ALIGN16_BEG -#define ALIGN16_END __attribute__((aligned(16))) -#endif - -extern ALIGN16_BEG const float ALIGN16_END WebRtcAec_sqrtHanning[65]; -extern ALIGN16_BEG const float ALIGN16_END WebRtcAec_weightCurve[65]; -extern ALIGN16_BEG const float ALIGN16_END WebRtcAec_overDriveCurve[65]; -extern const float WebRtcAec_kExtendedSmoothingCoefficients[2][2]; -extern const float WebRtcAec_kNormalSmoothingCoefficients[2][2]; -extern const float WebRtcAec_kMinFarendPSD; - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_COMMON_H_ - diff --git a/webrtc/modules/audio_processing/aec/aec_core.c b/webrtc/modules/audio_processing/aec/aec_core.c deleted file mode 100644 index f8eed32..0000000 --- a/webrtc/modules/audio_processing/aec/aec_core.c +++ /dev/null @@ -1,1929 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -/* - * The core AEC algorithm, which is presented with time-aligned signals. - */ - -#include "webrtc/modules/audio_processing/aec/aec_core.h" - -#ifdef WEBRTC_AEC_DEBUG_DUMP -#include -#endif - -#include -#include -#include // size_t -#include -#include - -#include "webrtc/common_audio/ring_buffer.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/aec/aec_common.h" -#include "webrtc/modules/audio_processing/aec/aec_core_internal.h" -#include "webrtc/modules/audio_processing/aec/aec_rdft.h" -#include "webrtc/modules/audio_processing/logging/aec_logging.h" -#include "webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h" -#include "webrtc/system_wrappers/include/cpu_features_wrapper.h" -#include "webrtc/typedefs.h" - - -// Buffer size (samples) -static const size_t kBufSizePartitions = 250; // 1 second of audio in 16 kHz. - -// Metrics -static const int subCountLen = 4; -static const int countLen = 50; -static const int kDelayMetricsAggregationWindow = 1250; // 5 seconds at 16 kHz. - -// Quantities to control H band scaling for SWB input -static const int flagHbandCn = 1; // flag for adding comfort noise in H band -static const float cnScaleHband = - (float)0.4; // scale for comfort noise in H band -// Initial bin for averaging nlp gain in low band -static const int freqAvgIc = PART_LEN / 2; - -// Matlab code to produce table: -// win = sqrt(hanning(63)); win = [0 ; win(1:32)]; -// fprintf(1, '\t%.14f, %.14f, %.14f,\n', win); -ALIGN16_BEG const float ALIGN16_END WebRtcAec_sqrtHanning[65] = { - 0.00000000000000f, 0.02454122852291f, 0.04906767432742f, 0.07356456359967f, - 0.09801714032956f, 0.12241067519922f, 0.14673047445536f, 0.17096188876030f, - 0.19509032201613f, 0.21910124015687f, 0.24298017990326f, 0.26671275747490f, - 0.29028467725446f, 0.31368174039889f, 0.33688985339222f, 0.35989503653499f, - 0.38268343236509f, 0.40524131400499f, 0.42755509343028f, 0.44961132965461f, - 0.47139673682600f, 0.49289819222978f, 0.51410274419322f, 0.53499761988710f, - 0.55557023301960f, 0.57580819141785f, 0.59569930449243f, 0.61523159058063f, - 0.63439328416365f, 0.65317284295378f, 0.67155895484702f, 0.68954054473707f, - 0.70710678118655f, 0.72424708295147f, 0.74095112535496f, 0.75720884650648f, - 0.77301045336274f, 0.78834642762661f, 0.80320753148064f, 0.81758481315158f, - 0.83146961230255f, 0.84485356524971f, 0.85772861000027f, 0.87008699110871f, - 0.88192126434835f, 0.89322430119552f, 0.90398929312344f, 0.91420975570353f, - 0.92387953251129f, 0.93299279883474f, 0.94154406518302f, 0.94952818059304f, - 0.95694033573221f, 0.96377606579544f, 0.97003125319454f, 0.97570213003853f, - 0.98078528040323f, 0.98527764238894f, 0.98917650996478f, 0.99247953459871f, - 0.99518472667220f, 0.99729045667869f, 0.99879545620517f, 0.99969881869620f, - 1.00000000000000f}; - -// Matlab code to produce table: -// weightCurve = [0 ; 0.3 * sqrt(linspace(0,1,64))' + 0.1]; -// fprintf(1, '\t%.4f, %.4f, %.4f, %.4f, %.4f, %.4f,\n', weightCurve); -ALIGN16_BEG const float ALIGN16_END WebRtcAec_weightCurve[65] = { - 0.0000f, 0.1000f, 0.1378f, 0.1535f, 0.1655f, 0.1756f, 0.1845f, 0.1926f, - 0.2000f, 0.2069f, 0.2134f, 0.2195f, 0.2254f, 0.2309f, 0.2363f, 0.2414f, - 0.2464f, 0.2512f, 0.2558f, 0.2604f, 0.2648f, 0.2690f, 0.2732f, 0.2773f, - 0.2813f, 0.2852f, 0.2890f, 0.2927f, 0.2964f, 0.3000f, 0.3035f, 0.3070f, - 0.3104f, 0.3138f, 0.3171f, 0.3204f, 0.3236f, 0.3268f, 0.3299f, 0.3330f, - 0.3360f, 0.3390f, 0.3420f, 0.3449f, 0.3478f, 0.3507f, 0.3535f, 0.3563f, - 0.3591f, 0.3619f, 0.3646f, 0.3673f, 0.3699f, 0.3726f, 0.3752f, 0.3777f, - 0.3803f, 0.3828f, 0.3854f, 0.3878f, 0.3903f, 0.3928f, 0.3952f, 0.3976f, - 0.4000f}; - -// Matlab code to produce table: -// overDriveCurve = [sqrt(linspace(0,1,65))' + 1]; -// fprintf(1, '\t%.4f, %.4f, %.4f, %.4f, %.4f, %.4f,\n', overDriveCurve); -ALIGN16_BEG const float ALIGN16_END WebRtcAec_overDriveCurve[65] = { - 1.0000f, 1.1250f, 1.1768f, 1.2165f, 1.2500f, 1.2795f, 1.3062f, 1.3307f, - 1.3536f, 1.3750f, 1.3953f, 1.4146f, 1.4330f, 1.4507f, 1.4677f, 1.4841f, - 1.5000f, 1.5154f, 1.5303f, 1.5449f, 1.5590f, 1.5728f, 1.5863f, 1.5995f, - 1.6124f, 1.6250f, 1.6374f, 1.6495f, 1.6614f, 1.6731f, 1.6847f, 1.6960f, - 1.7071f, 1.7181f, 1.7289f, 1.7395f, 1.7500f, 1.7603f, 1.7706f, 1.7806f, - 1.7906f, 1.8004f, 1.8101f, 1.8197f, 1.8292f, 1.8385f, 1.8478f, 1.8570f, - 1.8660f, 1.8750f, 1.8839f, 1.8927f, 1.9014f, 1.9100f, 1.9186f, 1.9270f, - 1.9354f, 1.9437f, 1.9520f, 1.9601f, 1.9682f, 1.9763f, 1.9843f, 1.9922f, - 2.0000f}; - -// Delay Agnostic AEC parameters, still under development and may change. -static const float kDelayQualityThresholdMax = 0.07f; -static const float kDelayQualityThresholdMin = 0.01f; -static const int kInitialShiftOffset = 5; -#if !defined(WEBRTC_ANDROID) -static const int kDelayCorrectionStart = 1500; // 10 ms chunks -#endif - -// Target suppression levels for nlp modes. -// log{0.001, 0.00001, 0.00000001} -static const float kTargetSupp[3] = {-6.9f, -11.5f, -18.4f}; - -// Two sets of parameters, one for the extended filter mode. -static const float kExtendedMinOverDrive[3] = {3.0f, 6.0f, 15.0f}; -static const float kNormalMinOverDrive[3] = {1.0f, 2.0f, 5.0f}; -const float WebRtcAec_kExtendedSmoothingCoefficients[2][2] = {{0.9f, 0.1f}, - {0.92f, 0.08f}}; -const float WebRtcAec_kNormalSmoothingCoefficients[2][2] = {{0.9f, 0.1f}, - {0.93f, 0.07f}}; - -// Number of partitions forming the NLP's "preferred" bands. -enum { - kPrefBandSize = 24 -}; - -#ifdef WEBRTC_AEC_DEBUG_DUMP -extern int webrtc_aec_instance_count; -#endif - -WebRtcAecFilterFar WebRtcAec_FilterFar; -WebRtcAecScaleErrorSignal WebRtcAec_ScaleErrorSignal; -WebRtcAecFilterAdaptation WebRtcAec_FilterAdaptation; -WebRtcAecOverdriveAndSuppress WebRtcAec_OverdriveAndSuppress; -WebRtcAecComfortNoise WebRtcAec_ComfortNoise; -WebRtcAecSubBandCoherence WebRtcAec_SubbandCoherence; - -__inline static float MulRe(float aRe, float aIm, float bRe, float bIm) { - return aRe * bRe - aIm * bIm; -} - -__inline static float MulIm(float aRe, float aIm, float bRe, float bIm) { - return aRe * bIm + aIm * bRe; -} - -static int CmpFloat(const void* a, const void* b) { - const float* da = (const float*)a; - const float* db = (const float*)b; - - return (*da > *db) - (*da < *db); -} - -static void FilterFar(AecCore* aec, float yf[2][PART_LEN1]) { - int i; - for (i = 0; i < aec->num_partitions; i++) { - int j; - int xPos = (i + aec->xfBufBlockPos) * PART_LEN1; - int pos = i * PART_LEN1; - // Check for wrap - if (i + aec->xfBufBlockPos >= aec->num_partitions) { - xPos -= aec->num_partitions * (PART_LEN1); - } - - for (j = 0; j < PART_LEN1; j++) { - yf[0][j] += MulRe(aec->xfBuf[0][xPos + j], - aec->xfBuf[1][xPos + j], - aec->wfBuf[0][pos + j], - aec->wfBuf[1][pos + j]); - yf[1][j] += MulIm(aec->xfBuf[0][xPos + j], - aec->xfBuf[1][xPos + j], - aec->wfBuf[0][pos + j], - aec->wfBuf[1][pos + j]); - } - } -} - -static void ScaleErrorSignal(AecCore* aec, float ef[2][PART_LEN1]) { - const float mu = aec->extended_filter_enabled ? kExtendedMu : aec->normal_mu; - const float error_threshold = aec->extended_filter_enabled - ? kExtendedErrorThreshold - : aec->normal_error_threshold; - int i; - float abs_ef; - for (i = 0; i < (PART_LEN1); i++) { - ef[0][i] /= (aec->xPow[i] + 1e-10f); - ef[1][i] /= (aec->xPow[i] + 1e-10f); - abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]); - - if (abs_ef > error_threshold) { - abs_ef = error_threshold / (abs_ef + 1e-10f); - ef[0][i] *= abs_ef; - ef[1][i] *= abs_ef; - } - - // Stepsize factor - ef[0][i] *= mu; - ef[1][i] *= mu; - } -} - -// Time-unconstrined filter adaptation. -// TODO(andrew): consider for a low-complexity mode. -// static void FilterAdaptationUnconstrained(AecCore* aec, float *fft, -// float ef[2][PART_LEN1]) { -// int i, j; -// for (i = 0; i < aec->num_partitions; i++) { -// int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1); -// int pos; -// // Check for wrap -// if (i + aec->xfBufBlockPos >= aec->num_partitions) { -// xPos -= aec->num_partitions * PART_LEN1; -// } -// -// pos = i * PART_LEN1; -// -// for (j = 0; j < PART_LEN1; j++) { -// aec->wfBuf[0][pos + j] += MulRe(aec->xfBuf[0][xPos + j], -// -aec->xfBuf[1][xPos + j], -// ef[0][j], ef[1][j]); -// aec->wfBuf[1][pos + j] += MulIm(aec->xfBuf[0][xPos + j], -// -aec->xfBuf[1][xPos + j], -// ef[0][j], ef[1][j]); -// } -// } -//} - -static void FilterAdaptation(AecCore* aec, float* fft, float ef[2][PART_LEN1]) { - int i, j; - for (i = 0; i < aec->num_partitions; i++) { - int xPos = (i + aec->xfBufBlockPos) * (PART_LEN1); - int pos; - // Check for wrap - if (i + aec->xfBufBlockPos >= aec->num_partitions) { - xPos -= aec->num_partitions * PART_LEN1; - } - - pos = i * PART_LEN1; - - for (j = 0; j < PART_LEN; j++) { - - fft[2 * j] = MulRe(aec->xfBuf[0][xPos + j], - -aec->xfBuf[1][xPos + j], - ef[0][j], - ef[1][j]); - fft[2 * j + 1] = MulIm(aec->xfBuf[0][xPos + j], - -aec->xfBuf[1][xPos + j], - ef[0][j], - ef[1][j]); - } - fft[1] = MulRe(aec->xfBuf[0][xPos + PART_LEN], - -aec->xfBuf[1][xPos + PART_LEN], - ef[0][PART_LEN], - ef[1][PART_LEN]); - - aec_rdft_inverse_128(fft); - memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN); - - // fft scaling - { - float scale = 2.0f / PART_LEN2; - for (j = 0; j < PART_LEN; j++) { - fft[j] *= scale; - } - } - aec_rdft_forward_128(fft); - - aec->wfBuf[0][pos] += fft[0]; - aec->wfBuf[0][pos + PART_LEN] += fft[1]; - - for (j = 1; j < PART_LEN; j++) { - aec->wfBuf[0][pos + j] += fft[2 * j]; - aec->wfBuf[1][pos + j] += fft[2 * j + 1]; - } - } -} - -static void OverdriveAndSuppress(AecCore* aec, - float hNl[PART_LEN1], - const float hNlFb, - float efw[2][PART_LEN1]) { - int i; - for (i = 0; i < PART_LEN1; i++) { - // Weight subbands - if (hNl[i] > hNlFb) { - hNl[i] = WebRtcAec_weightCurve[i] * hNlFb + - (1 - WebRtcAec_weightCurve[i]) * hNl[i]; - } - hNl[i] = powf(hNl[i], aec->overDriveSm * WebRtcAec_overDriveCurve[i]); - - // Suppress error signal - efw[0][i] *= hNl[i]; - efw[1][i] *= hNl[i]; - - // Ooura fft returns incorrect sign on imaginary component. It matters here - // because we are making an additive change with comfort noise. - efw[1][i] *= -1; - } -} - -static int PartitionDelay(const AecCore* aec) { - // Measures the energy in each filter partition and returns the partition with - // highest energy. - // TODO(bjornv): Spread computational cost by computing one partition per - // block? - float wfEnMax = 0; - int i; - int delay = 0; - - for (i = 0; i < aec->num_partitions; i++) { - int j; - int pos = i * PART_LEN1; - float wfEn = 0; - for (j = 0; j < PART_LEN1; j++) { - wfEn += aec->wfBuf[0][pos + j] * aec->wfBuf[0][pos + j] + - aec->wfBuf[1][pos + j] * aec->wfBuf[1][pos + j]; - } - - if (wfEn > wfEnMax) { - wfEnMax = wfEn; - delay = i; - } - } - return delay; -} - -// Threshold to protect against the ill-effects of a zero far-end. -const float WebRtcAec_kMinFarendPSD = 15; - -// Updates the following smoothed Power Spectral Densities (PSD): -// - sd : near-end -// - se : residual echo -// - sx : far-end -// - sde : cross-PSD of near-end and residual echo -// - sxd : cross-PSD of near-end and far-end -// -// In addition to updating the PSDs, also the filter diverge state is determined -// upon actions are taken. -static void SmoothedPSD(AecCore* aec, - float efw[2][PART_LEN1], - float dfw[2][PART_LEN1], - float xfw[2][PART_LEN1]) { - // Power estimate smoothing coefficients. - const float* ptrGCoh = aec->extended_filter_enabled - ? WebRtcAec_kExtendedSmoothingCoefficients[aec->mult - 1] - : WebRtcAec_kNormalSmoothingCoefficients[aec->mult - 1]; - int i; - float sdSum = 0, seSum = 0; - - for (i = 0; i < PART_LEN1; i++) { - aec->sd[i] = ptrGCoh[0] * aec->sd[i] + - ptrGCoh[1] * (dfw[0][i] * dfw[0][i] + dfw[1][i] * dfw[1][i]); - aec->se[i] = ptrGCoh[0] * aec->se[i] + - ptrGCoh[1] * (efw[0][i] * efw[0][i] + efw[1][i] * efw[1][i]); - // We threshold here to protect against the ill-effects of a zero farend. - // The threshold is not arbitrarily chosen, but balances protection and - // adverse interaction with the algorithm's tuning. - // TODO(bjornv): investigate further why this is so sensitive. - aec->sx[i] = - ptrGCoh[0] * aec->sx[i] + - ptrGCoh[1] * WEBRTC_SPL_MAX( - xfw[0][i] * xfw[0][i] + xfw[1][i] * xfw[1][i], - WebRtcAec_kMinFarendPSD); - - aec->sde[i][0] = - ptrGCoh[0] * aec->sde[i][0] + - ptrGCoh[1] * (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); - aec->sde[i][1] = - ptrGCoh[0] * aec->sde[i][1] + - ptrGCoh[1] * (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); - - aec->sxd[i][0] = - ptrGCoh[0] * aec->sxd[i][0] + - ptrGCoh[1] * (dfw[0][i] * xfw[0][i] + dfw[1][i] * xfw[1][i]); - aec->sxd[i][1] = - ptrGCoh[0] * aec->sxd[i][1] + - ptrGCoh[1] * (dfw[0][i] * xfw[1][i] - dfw[1][i] * xfw[0][i]); - - sdSum += aec->sd[i]; - seSum += aec->se[i]; - } - - // Divergent filter safeguard. - aec->divergeState = (aec->divergeState ? 1.05f : 1.0f) * seSum > sdSum; - - if (aec->divergeState) - memcpy(efw, dfw, sizeof(efw[0][0]) * 2 * PART_LEN1); - - // Reset if error is significantly larger than nearend (13 dB). - if (!aec->extended_filter_enabled && seSum > (19.95f * sdSum)) - memset(aec->wfBuf, 0, sizeof(aec->wfBuf)); -} - -// Window time domain data to be used by the fft. -__inline static void WindowData(float* x_windowed, const float* x) { - int i; - for (i = 0; i < PART_LEN; i++) { - x_windowed[i] = x[i] * WebRtcAec_sqrtHanning[i]; - x_windowed[PART_LEN + i] = - x[PART_LEN + i] * WebRtcAec_sqrtHanning[PART_LEN - i]; - } -} - -// Puts fft output data into a complex valued array. -__inline static void StoreAsComplex(const float* data, - float data_complex[2][PART_LEN1]) { - int i; - data_complex[0][0] = data[0]; - data_complex[1][0] = 0; - for (i = 1; i < PART_LEN; i++) { - data_complex[0][i] = data[2 * i]; - data_complex[1][i] = data[2 * i + 1]; - } - data_complex[0][PART_LEN] = data[1]; - data_complex[1][PART_LEN] = 0; -} - -static void SubbandCoherence(AecCore* aec, - float efw[2][PART_LEN1], - float xfw[2][PART_LEN1], - float* fft, - float* cohde, - float* cohxd) { - float dfw[2][PART_LEN1]; - int i; - - if (aec->delayEstCtr == 0) - aec->delayIdx = PartitionDelay(aec); - - // Use delayed far. - memcpy(xfw, - aec->xfwBuf + aec->delayIdx * PART_LEN1, - sizeof(xfw[0][0]) * 2 * PART_LEN1); - - // Windowed near fft - WindowData(fft, aec->dBuf); - aec_rdft_forward_128(fft); - StoreAsComplex(fft, dfw); - - // Windowed error fft - WindowData(fft, aec->eBuf); - aec_rdft_forward_128(fft); - StoreAsComplex(fft, efw); - - SmoothedPSD(aec, efw, dfw, xfw); - - // Subband coherence - for (i = 0; i < PART_LEN1; i++) { - cohde[i] = - (aec->sde[i][0] * aec->sde[i][0] + aec->sde[i][1] * aec->sde[i][1]) / - (aec->sd[i] * aec->se[i] + 1e-10f); - cohxd[i] = - (aec->sxd[i][0] * aec->sxd[i][0] + aec->sxd[i][1] * aec->sxd[i][1]) / - (aec->sx[i] * aec->sd[i] + 1e-10f); - } -} - -static void GetHighbandGain(const float* lambda, float* nlpGainHband) { - int i; - - nlpGainHband[0] = (float)0.0; - for (i = freqAvgIc; i < PART_LEN1 - 1; i++) { - nlpGainHband[0] += lambda[i]; - } - nlpGainHband[0] /= (float)(PART_LEN1 - 1 - freqAvgIc); -} - -static void ComfortNoise(AecCore* aec, - float efw[2][PART_LEN1], - complex_t* comfortNoiseHband, - const float* noisePow, - const float* lambda) { - int i, num; - float rand[PART_LEN]; - float noise, noiseAvg, tmp, tmpAvg; - int16_t randW16[PART_LEN]; - complex_t u[PART_LEN1]; - - const float pi2 = 6.28318530717959f; - - // Generate a uniform random array on [0 1] - WebRtcSpl_RandUArray(randW16, PART_LEN, &aec->seed); - for (i = 0; i < PART_LEN; i++) { - rand[i] = ((float)randW16[i]) / 32768; - } - - // Reject LF noise - u[0][0] = 0; - u[0][1] = 0; - for (i = 1; i < PART_LEN1; i++) { - tmp = pi2 * rand[i - 1]; - - noise = sqrtf(noisePow[i]); - u[i][0] = noise * cosf(tmp); - u[i][1] = -noise * sinf(tmp); - } - u[PART_LEN][1] = 0; - - for (i = 0; i < PART_LEN1; i++) { - // This is the proper weighting to match the background noise power - tmp = sqrtf(WEBRTC_SPL_MAX(1 - lambda[i] * lambda[i], 0)); - // tmp = 1 - lambda[i]; - efw[0][i] += tmp * u[i][0]; - efw[1][i] += tmp * u[i][1]; - } - - // For H band comfort noise - // TODO: don't compute noise and "tmp" twice. Use the previous results. - noiseAvg = 0.0; - tmpAvg = 0.0; - num = 0; - if (aec->num_bands > 1 && flagHbandCn == 1) { - - // average noise scale - // average over second half of freq spectrum (i.e., 4->8khz) - // TODO: we shouldn't need num. We know how many elements we're summing. - for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { - num++; - noiseAvg += sqrtf(noisePow[i]); - } - noiseAvg /= (float)num; - - // average nlp scale - // average over second half of freq spectrum (i.e., 4->8khz) - // TODO: we shouldn't need num. We know how many elements we're summing. - num = 0; - for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { - num++; - tmpAvg += sqrtf(WEBRTC_SPL_MAX(1 - lambda[i] * lambda[i], 0)); - } - tmpAvg /= (float)num; - - // Use average noise for H band - // TODO: we should probably have a new random vector here. - // Reject LF noise - u[0][0] = 0; - u[0][1] = 0; - for (i = 1; i < PART_LEN1; i++) { - tmp = pi2 * rand[i - 1]; - - // Use average noise for H band - u[i][0] = noiseAvg * (float)cos(tmp); - u[i][1] = -noiseAvg * (float)sin(tmp); - } - u[PART_LEN][1] = 0; - - for (i = 0; i < PART_LEN1; i++) { - // Use average NLP weight for H band - comfortNoiseHband[i][0] = tmpAvg * u[i][0]; - comfortNoiseHband[i][1] = tmpAvg * u[i][1]; - } - } -} - -static void InitLevel(PowerLevel* level) { - const float kBigFloat = 1E17f; - - level->averagelevel = 0; - level->framelevel = 0; - level->minlevel = kBigFloat; - level->frsum = 0; - level->sfrsum = 0; - level->frcounter = 0; - level->sfrcounter = 0; -} - -static void InitStats(Stats* stats) { - stats->instant = kOffsetLevel; - stats->average = kOffsetLevel; - stats->max = kOffsetLevel; - stats->min = kOffsetLevel * (-1); - stats->sum = 0; - stats->hisum = 0; - stats->himean = kOffsetLevel; - stats->counter = 0; - stats->hicounter = 0; -} - -static void InitMetrics(AecCore* self) { - self->stateCounter = 0; - InitLevel(&self->farlevel); - InitLevel(&self->nearlevel); - InitLevel(&self->linoutlevel); - InitLevel(&self->nlpoutlevel); - - InitStats(&self->erl); - InitStats(&self->erle); - InitStats(&self->aNlp); - InitStats(&self->rerl); -} - -static void UpdateLevel(PowerLevel* level, float in[2][PART_LEN1]) { - // Do the energy calculation in the frequency domain. The FFT is performed on - // a segment of PART_LEN2 samples due to overlap, but we only want the energy - // of half that data (the last PART_LEN samples). Parseval's relation states - // that the energy is preserved according to - // - // \sum_{n=0}^{N-1} |x(n)|^2 = 1/N * \sum_{n=0}^{N-1} |X(n)|^2 - // = ENERGY, - // - // where N = PART_LEN2. Since we are only interested in calculating the energy - // for the last PART_LEN samples we approximate by calculating ENERGY and - // divide by 2, - // - // \sum_{n=N/2}^{N-1} |x(n)|^2 ~= ENERGY / 2 - // - // Since we deal with real valued time domain signals we only store frequency - // bins [0, PART_LEN], which is what |in| consists of. To calculate ENERGY we - // need to add the contribution from the missing part in - // [PART_LEN+1, PART_LEN2-1]. These values are, up to a phase shift, identical - // with the values in [1, PART_LEN-1], hence multiply those values by 2. This - // is the values in the for loop below, but multiplication by 2 and division - // by 2 cancel. - - // TODO(bjornv): Investigate reusing energy calculations performed at other - // places in the code. - int k = 1; - // Imaginary parts are zero at end points and left out of the calculation. - float energy = (in[0][0] * in[0][0]) / 2; - energy += (in[0][PART_LEN] * in[0][PART_LEN]) / 2; - - for (k = 1; k < PART_LEN; k++) { - energy += (in[0][k] * in[0][k] + in[1][k] * in[1][k]); - } - energy /= PART_LEN2; - - level->sfrsum += energy; - level->sfrcounter++; - - if (level->sfrcounter > subCountLen) { - level->framelevel = level->sfrsum / (subCountLen * PART_LEN); - level->sfrsum = 0; - level->sfrcounter = 0; - if (level->framelevel > 0) { - if (level->framelevel < level->minlevel) { - level->minlevel = level->framelevel; // New minimum. - } else { - level->minlevel *= (1 + 0.001f); // Small increase. - } - } - level->frcounter++; - level->frsum += level->framelevel; - if (level->frcounter > countLen) { - level->averagelevel = level->frsum / countLen; - level->frsum = 0; - level->frcounter = 0; - } - } -} - -static void UpdateMetrics(AecCore* aec) { - float dtmp, dtmp2; - - const float actThresholdNoisy = 8.0f; - const float actThresholdClean = 40.0f; - const float safety = 0.99995f; - const float noisyPower = 300000.0f; - - float actThreshold; - float echo, suppressedEcho; - - if (aec->echoState) { // Check if echo is likely present - aec->stateCounter++; - } - - if (aec->farlevel.frcounter == 0) { - - if (aec->farlevel.minlevel < noisyPower) { - actThreshold = actThresholdClean; - } else { - actThreshold = actThresholdNoisy; - } - - if ((aec->stateCounter > (0.5f * countLen * subCountLen)) && - (aec->farlevel.sfrcounter == 0) - - // Estimate in active far-end segments only - && - (aec->farlevel.averagelevel > - (actThreshold * aec->farlevel.minlevel))) { - - // Subtract noise power - echo = aec->nearlevel.averagelevel - safety * aec->nearlevel.minlevel; - - // ERL - dtmp = 10 * (float)log10(aec->farlevel.averagelevel / - aec->nearlevel.averagelevel + - 1e-10f); - dtmp2 = 10 * (float)log10(aec->farlevel.averagelevel / echo + 1e-10f); - - aec->erl.instant = dtmp; - if (dtmp > aec->erl.max) { - aec->erl.max = dtmp; - } - - if (dtmp < aec->erl.min) { - aec->erl.min = dtmp; - } - - aec->erl.counter++; - aec->erl.sum += dtmp; - aec->erl.average = aec->erl.sum / aec->erl.counter; - - // Upper mean - if (dtmp > aec->erl.average) { - aec->erl.hicounter++; - aec->erl.hisum += dtmp; - aec->erl.himean = aec->erl.hisum / aec->erl.hicounter; - } - - // A_NLP - dtmp = 10 * (float)log10(aec->nearlevel.averagelevel / - (2 * aec->linoutlevel.averagelevel) + - 1e-10f); - - // subtract noise power - suppressedEcho = 2 * (aec->linoutlevel.averagelevel - - safety * aec->linoutlevel.minlevel); - - dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f); - - aec->aNlp.instant = dtmp2; - if (dtmp > aec->aNlp.max) { - aec->aNlp.max = dtmp; - } - - if (dtmp < aec->aNlp.min) { - aec->aNlp.min = dtmp; - } - - aec->aNlp.counter++; - aec->aNlp.sum += dtmp; - aec->aNlp.average = aec->aNlp.sum / aec->aNlp.counter; - - // Upper mean - if (dtmp > aec->aNlp.average) { - aec->aNlp.hicounter++; - aec->aNlp.hisum += dtmp; - aec->aNlp.himean = aec->aNlp.hisum / aec->aNlp.hicounter; - } - - // ERLE - - // subtract noise power - suppressedEcho = 2 * (aec->nlpoutlevel.averagelevel - - safety * aec->nlpoutlevel.minlevel); - - dtmp = 10 * (float)log10(aec->nearlevel.averagelevel / - (2 * aec->nlpoutlevel.averagelevel) + - 1e-10f); - dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f); - - dtmp = dtmp2; - aec->erle.instant = dtmp; - if (dtmp > aec->erle.max) { - aec->erle.max = dtmp; - } - - if (dtmp < aec->erle.min) { - aec->erle.min = dtmp; - } - - aec->erle.counter++; - aec->erle.sum += dtmp; - aec->erle.average = aec->erle.sum / aec->erle.counter; - - // Upper mean - if (dtmp > aec->erle.average) { - aec->erle.hicounter++; - aec->erle.hisum += dtmp; - aec->erle.himean = aec->erle.hisum / aec->erle.hicounter; - } - } - - aec->stateCounter = 0; - } -} - -static void UpdateDelayMetrics(AecCore* self) { - int i = 0; - int delay_values = 0; - int median = 0; - int lookahead = WebRtc_lookahead(self->delay_estimator); - const int kMsPerBlock = PART_LEN / (self->mult * 8); - int64_t l1_norm = 0; - - if (self->num_delay_values == 0) { - // We have no new delay value data. Even though -1 is a valid |median| in - // the sense that we allow negative values, it will practically never be - // used since multiples of |kMsPerBlock| will always be returned. - // We therefore use -1 to indicate in the logs that the delay estimator was - // not able to estimate the delay. - self->delay_median = -1; - self->delay_std = -1; - self->fraction_poor_delays = -1; - return; - } - - // Start value for median count down. - delay_values = self->num_delay_values >> 1; - // Get median of delay values since last update. - for (i = 0; i < kHistorySizeBlocks; i++) { - delay_values -= self->delay_histogram[i]; - if (delay_values < 0) { - median = i; - break; - } - } - // Account for lookahead. - self->delay_median = (median - lookahead) * kMsPerBlock; - - // Calculate the L1 norm, with median value as central moment. - for (i = 0; i < kHistorySizeBlocks; i++) { - l1_norm += abs(i - median) * self->delay_histogram[i]; - } - self->delay_std = (int)((l1_norm + self->num_delay_values / 2) / - self->num_delay_values) * kMsPerBlock; - - // Determine fraction of delays that are out of bounds, that is, either - // negative (anti-causal system) or larger than the AEC filter length. - { - int num_delays_out_of_bounds = self->num_delay_values; - const int histogram_length = sizeof(self->delay_histogram) / - sizeof(self->delay_histogram[0]); - for (i = lookahead; i < lookahead + self->num_partitions; ++i) { - if (i < histogram_length) - num_delays_out_of_bounds -= self->delay_histogram[i]; - } - self->fraction_poor_delays = (float)num_delays_out_of_bounds / - self->num_delay_values; - } - - // Reset histogram. - memset(self->delay_histogram, 0, sizeof(self->delay_histogram)); - self->num_delay_values = 0; - - return; -} - -static void TimeToFrequency(float time_data[PART_LEN2], - float freq_data[2][PART_LEN1], - int window) { - int i = 0; - - // TODO(bjornv): Should we have a different function/wrapper for windowed FFT? - if (window) { - for (i = 0; i < PART_LEN; i++) { - time_data[i] *= WebRtcAec_sqrtHanning[i]; - time_data[PART_LEN + i] *= WebRtcAec_sqrtHanning[PART_LEN - i]; - } - } - - aec_rdft_forward_128(time_data); - // Reorder. - freq_data[1][0] = 0; - freq_data[1][PART_LEN] = 0; - freq_data[0][0] = time_data[0]; - freq_data[0][PART_LEN] = time_data[1]; - for (i = 1; i < PART_LEN; i++) { - freq_data[0][i] = time_data[2 * i]; - freq_data[1][i] = time_data[2 * i + 1]; - } -} - -static int MoveFarReadPtrWithoutSystemDelayUpdate(AecCore* self, int elements) { - WebRtc_MoveReadPtr(self->far_buf_windowed, elements); -#ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_MoveReadPtr(self->far_time_buf, elements); -#endif - return WebRtc_MoveReadPtr(self->far_buf, elements); -} - -static int SignalBasedDelayCorrection(AecCore* self) { - int delay_correction = 0; - int last_delay = -2; - assert(self != NULL); -#if !defined(WEBRTC_ANDROID) - // On desktops, turn on correction after |kDelayCorrectionStart| frames. This - // is to let the delay estimation get a chance to converge. Also, if the - // playout audio volume is low (or even muted) the delay estimation can return - // a very large delay, which will break the AEC if it is applied. - if (self->frame_count < kDelayCorrectionStart) { - return 0; - } -#endif - - // 1. Check for non-negative delay estimate. Note that the estimates we get - // from the delay estimation are not compensated for lookahead. Hence, a - // negative |last_delay| is an invalid one. - // 2. Verify that there is a delay change. In addition, only allow a change - // if the delay is outside a certain region taking the AEC filter length - // into account. - // TODO(bjornv): Investigate if we can remove the non-zero delay change check. - // 3. Only allow delay correction if the delay estimation quality exceeds - // |delay_quality_threshold|. - // 4. Finally, verify that the proposed |delay_correction| is feasible by - // comparing with the size of the far-end buffer. - last_delay = WebRtc_last_delay(self->delay_estimator); - if ((last_delay >= 0) && - (last_delay != self->previous_delay) && - (WebRtc_last_delay_quality(self->delay_estimator) > - self->delay_quality_threshold)) { - int delay = last_delay - WebRtc_lookahead(self->delay_estimator); - // Allow for a slack in the actual delay, defined by a |lower_bound| and an - // |upper_bound|. The adaptive echo cancellation filter is currently - // |num_partitions| (of 64 samples) long. If the delay estimate is negative - // or at least 3/4 of the filter length we open up for correction. - const int lower_bound = 0; - const int upper_bound = self->num_partitions * 3 / 4; - const int do_correction = delay <= lower_bound || delay > upper_bound; - if (do_correction == 1) { - int available_read = (int)WebRtc_available_read(self->far_buf); - // With |shift_offset| we gradually rely on the delay estimates. For - // positive delays we reduce the correction by |shift_offset| to lower the - // risk of pushing the AEC into a non causal state. For negative delays - // we rely on the values up to a rounding error, hence compensate by 1 - // element to make sure to push the delay into the causal region. - delay_correction = -delay; - delay_correction += delay > self->shift_offset ? self->shift_offset : 1; - self->shift_offset--; - self->shift_offset = (self->shift_offset <= 1 ? 1 : self->shift_offset); - if (delay_correction > available_read - self->mult - 1) { - // There is not enough data in the buffer to perform this shift. Hence, - // we do not rely on the delay estimate and do nothing. - delay_correction = 0; - } else { - self->previous_delay = last_delay; - ++self->delay_correction_count; - } - } - } - // Update the |delay_quality_threshold| once we have our first delay - // correction. - if (self->delay_correction_count > 0) { - float delay_quality = WebRtc_last_delay_quality(self->delay_estimator); - delay_quality = (delay_quality > kDelayQualityThresholdMax ? - kDelayQualityThresholdMax : delay_quality); - self->delay_quality_threshold = - (delay_quality > self->delay_quality_threshold ? delay_quality : - self->delay_quality_threshold); - } - return delay_correction; -} - -static void NonLinearProcessing(AecCore* aec, - float* output, - float* const* outputH) { - float efw[2][PART_LEN1], xfw[2][PART_LEN1]; - complex_t comfortNoiseHband[PART_LEN1]; - float fft[PART_LEN2]; - float scale, dtmp; - float nlpGainHband; - int i; - size_t j; - - // Coherence and non-linear filter - float cohde[PART_LEN1], cohxd[PART_LEN1]; - float hNlDeAvg, hNlXdAvg; - float hNl[PART_LEN1]; - float hNlPref[kPrefBandSize]; - float hNlFb = 0, hNlFbLow = 0; - const float prefBandQuant = 0.75f, prefBandQuantLow = 0.5f; - const int prefBandSize = kPrefBandSize / aec->mult; - const int minPrefBand = 4 / aec->mult; - // Power estimate smoothing coefficients. - const float* min_overdrive = aec->extended_filter_enabled - ? kExtendedMinOverDrive - : kNormalMinOverDrive; - - // Filter energy - const int delayEstInterval = 10 * aec->mult; - - float* xfw_ptr = NULL; - - aec->delayEstCtr++; - if (aec->delayEstCtr == delayEstInterval) { - aec->delayEstCtr = 0; - } - - // initialize comfort noise for H band - memset(comfortNoiseHband, 0, sizeof(comfortNoiseHband)); - nlpGainHband = (float)0.0; - dtmp = (float)0.0; - - // We should always have at least one element stored in |far_buf|. - assert(WebRtc_available_read(aec->far_buf_windowed) > 0); - // NLP - WebRtc_ReadBuffer(aec->far_buf_windowed, (void**)&xfw_ptr, &xfw[0][0], 1); - - // TODO(bjornv): Investigate if we can reuse |far_buf_windowed| instead of - // |xfwBuf|. - // Buffer far. - memcpy(aec->xfwBuf, xfw_ptr, sizeof(float) * 2 * PART_LEN1); - - WebRtcAec_SubbandCoherence(aec, efw, xfw, fft, cohde, cohxd); - - hNlXdAvg = 0; - for (i = minPrefBand; i < prefBandSize + minPrefBand; i++) { - hNlXdAvg += cohxd[i]; - } - hNlXdAvg /= prefBandSize; - hNlXdAvg = 1 - hNlXdAvg; - - hNlDeAvg = 0; - for (i = minPrefBand; i < prefBandSize + minPrefBand; i++) { - hNlDeAvg += cohde[i]; - } - hNlDeAvg /= prefBandSize; - - if (hNlXdAvg < 0.75f && hNlXdAvg < aec->hNlXdAvgMin) { - aec->hNlXdAvgMin = hNlXdAvg; - } - - if (hNlDeAvg > 0.98f && hNlXdAvg > 0.9f) { - aec->stNearState = 1; - } else if (hNlDeAvg < 0.95f || hNlXdAvg < 0.8f) { - aec->stNearState = 0; - } - - if (aec->hNlXdAvgMin == 1) { - aec->echoState = 0; - aec->overDrive = min_overdrive[aec->nlp_mode]; - - if (aec->stNearState == 1) { - memcpy(hNl, cohde, sizeof(hNl)); - hNlFb = hNlDeAvg; - hNlFbLow = hNlDeAvg; - } else { - for (i = 0; i < PART_LEN1; i++) { - hNl[i] = 1 - cohxd[i]; - } - hNlFb = hNlXdAvg; - hNlFbLow = hNlXdAvg; - } - } else { - - if (aec->stNearState == 1) { - aec->echoState = 0; - memcpy(hNl, cohde, sizeof(hNl)); - hNlFb = hNlDeAvg; - hNlFbLow = hNlDeAvg; - } else { - aec->echoState = 1; - for (i = 0; i < PART_LEN1; i++) { - hNl[i] = WEBRTC_SPL_MIN(cohde[i], 1 - cohxd[i]); - } - - // Select an order statistic from the preferred bands. - // TODO: Using quicksort now, but a selection algorithm may be preferred. - memcpy(hNlPref, &hNl[minPrefBand], sizeof(float) * prefBandSize); - qsort(hNlPref, prefBandSize, sizeof(float), CmpFloat); - hNlFb = hNlPref[(int)floor(prefBandQuant * (prefBandSize - 1))]; - hNlFbLow = hNlPref[(int)floor(prefBandQuantLow * (prefBandSize - 1))]; - } - } - - // Track the local filter minimum to determine suppression overdrive. - if (hNlFbLow < 0.6f && hNlFbLow < aec->hNlFbLocalMin) { - aec->hNlFbLocalMin = hNlFbLow; - aec->hNlFbMin = hNlFbLow; - aec->hNlNewMin = 1; - aec->hNlMinCtr = 0; - } - aec->hNlFbLocalMin = - WEBRTC_SPL_MIN(aec->hNlFbLocalMin + 0.0008f / aec->mult, 1); - aec->hNlXdAvgMin = WEBRTC_SPL_MIN(aec->hNlXdAvgMin + 0.0006f / aec->mult, 1); - - if (aec->hNlNewMin == 1) { - aec->hNlMinCtr++; - } - if (aec->hNlMinCtr == 2) { - aec->hNlNewMin = 0; - aec->hNlMinCtr = 0; - aec->overDrive = - WEBRTC_SPL_MAX(kTargetSupp[aec->nlp_mode] / - ((float)log(aec->hNlFbMin + 1e-10f) + 1e-10f), - min_overdrive[aec->nlp_mode]); - } - - // Smooth the overdrive. - if (aec->overDrive < aec->overDriveSm) { - aec->overDriveSm = 0.99f * aec->overDriveSm + 0.01f * aec->overDrive; - } else { - aec->overDriveSm = 0.9f * aec->overDriveSm + 0.1f * aec->overDrive; - } - - WebRtcAec_OverdriveAndSuppress(aec, hNl, hNlFb, efw); - - // Add comfort noise. - WebRtcAec_ComfortNoise(aec, efw, comfortNoiseHband, aec->noisePow, hNl); - - // TODO(bjornv): Investigate how to take the windowing below into account if - // needed. - if (aec->metricsMode == 1) { - // Note that we have a scaling by two in the time domain |eBuf|. - // In addition the time domain signal is windowed before transformation, - // losing half the energy on the average. We take care of the first - // scaling only in UpdateMetrics(). - UpdateLevel(&aec->nlpoutlevel, efw); - } - // Inverse error fft. - fft[0] = efw[0][0]; - fft[1] = efw[0][PART_LEN]; - for (i = 1; i < PART_LEN; i++) { - fft[2 * i] = efw[0][i]; - // Sign change required by Ooura fft. - fft[2 * i + 1] = -efw[1][i]; - } - aec_rdft_inverse_128(fft); - - // Overlap and add to obtain output. - scale = 2.0f / PART_LEN2; - for (i = 0; i < PART_LEN; i++) { - fft[i] *= scale; // fft scaling - fft[i] = fft[i] * WebRtcAec_sqrtHanning[i] + aec->outBuf[i]; - - fft[PART_LEN + i] *= scale; // fft scaling - aec->outBuf[i] = fft[PART_LEN + i] * WebRtcAec_sqrtHanning[PART_LEN - i]; - - // Saturate output to keep it in the allowed range. - output[i] = WEBRTC_SPL_SAT( - WEBRTC_SPL_WORD16_MAX, fft[i], WEBRTC_SPL_WORD16_MIN); - } - - // For H band - if (aec->num_bands > 1) { - - // H band gain - // average nlp over low band: average over second half of freq spectrum - // (4->8khz) - GetHighbandGain(hNl, &nlpGainHband); - - // Inverse comfort_noise - if (flagHbandCn == 1) { - fft[0] = comfortNoiseHband[0][0]; - fft[1] = comfortNoiseHband[PART_LEN][0]; - for (i = 1; i < PART_LEN; i++) { - fft[2 * i] = comfortNoiseHband[i][0]; - fft[2 * i + 1] = comfortNoiseHband[i][1]; - } - aec_rdft_inverse_128(fft); - scale = 2.0f / PART_LEN2; - } - - // compute gain factor - for (j = 0; j < aec->num_bands - 1; ++j) { - for (i = 0; i < PART_LEN; i++) { - dtmp = aec->dBufH[j][i]; - dtmp = dtmp * nlpGainHband; // for variable gain - - // add some comfort noise where Hband is attenuated - if (flagHbandCn == 1 && j == 0) { - fft[i] *= scale; // fft scaling - dtmp += cnScaleHband * fft[i]; - } - - // Saturate output to keep it in the allowed range. - outputH[j][i] = WEBRTC_SPL_SAT( - WEBRTC_SPL_WORD16_MAX, dtmp, WEBRTC_SPL_WORD16_MIN); - } - } - } - - // Copy the current block to the old position. - memcpy(aec->dBuf, aec->dBuf + PART_LEN, sizeof(float) * PART_LEN); - memcpy(aec->eBuf, aec->eBuf + PART_LEN, sizeof(float) * PART_LEN); - - // Copy the current block to the old position for H band - for (j = 0; j < aec->num_bands - 1; ++j) { - memcpy(aec->dBufH[j], aec->dBufH[j] + PART_LEN, sizeof(float) * PART_LEN); - } - - memmove(aec->xfwBuf + PART_LEN1, - aec->xfwBuf, - sizeof(aec->xfwBuf) - sizeof(complex_t) * PART_LEN1); -} - -static void ProcessBlock(AecCore* aec) { - size_t i; - float y[PART_LEN], e[PART_LEN]; - float scale; - - float fft[PART_LEN2]; - float xf[2][PART_LEN1], yf[2][PART_LEN1], ef[2][PART_LEN1]; - float df[2][PART_LEN1]; - float far_spectrum = 0.0f; - float near_spectrum = 0.0f; - float abs_far_spectrum[PART_LEN1]; - float abs_near_spectrum[PART_LEN1]; - - const float gPow[2] = {0.9f, 0.1f}; - - // Noise estimate constants. - const int noiseInitBlocks = 500 * aec->mult; - const float step = 0.1f; - const float ramp = 1.0002f; - const float gInitNoise[2] = {0.999f, 0.001f}; - - float nearend[PART_LEN]; - float* nearend_ptr = NULL; - float output[PART_LEN]; - float outputH[NUM_HIGH_BANDS_MAX][PART_LEN]; - float* outputH_ptr[NUM_HIGH_BANDS_MAX]; - for (i = 0; i < NUM_HIGH_BANDS_MAX; ++i) { - outputH_ptr[i] = outputH[i]; - } - - float* xf_ptr = NULL; - - // Concatenate old and new nearend blocks. - for (i = 0; i < aec->num_bands - 1; ++i) { - WebRtc_ReadBuffer(aec->nearFrBufH[i], - (void**)&nearend_ptr, - nearend, - PART_LEN); - memcpy(aec->dBufH[i] + PART_LEN, nearend_ptr, sizeof(nearend)); - } - WebRtc_ReadBuffer(aec->nearFrBuf, (void**)&nearend_ptr, nearend, PART_LEN); - memcpy(aec->dBuf + PART_LEN, nearend_ptr, sizeof(nearend)); - - // ---------- Ooura fft ---------- - -#ifdef WEBRTC_AEC_DEBUG_DUMP - { - float farend[PART_LEN]; - float* farend_ptr = NULL; - WebRtc_ReadBuffer(aec->far_time_buf, (void**)&farend_ptr, farend, 1); - RTC_AEC_DEBUG_WAV_WRITE(aec->farFile, farend_ptr, PART_LEN); - RTC_AEC_DEBUG_WAV_WRITE(aec->nearFile, nearend_ptr, PART_LEN); - } -#endif - - // We should always have at least one element stored in |far_buf|. - assert(WebRtc_available_read(aec->far_buf) > 0); - WebRtc_ReadBuffer(aec->far_buf, (void**)&xf_ptr, &xf[0][0], 1); - - // Near fft - memcpy(fft, aec->dBuf, sizeof(float) * PART_LEN2); - TimeToFrequency(fft, df, 0); - - // Power smoothing - for (i = 0; i < PART_LEN1; i++) { - far_spectrum = (xf_ptr[i] * xf_ptr[i]) + - (xf_ptr[PART_LEN1 + i] * xf_ptr[PART_LEN1 + i]); - aec->xPow[i] = - gPow[0] * aec->xPow[i] + gPow[1] * aec->num_partitions * far_spectrum; - // Calculate absolute spectra - abs_far_spectrum[i] = sqrtf(far_spectrum); - - near_spectrum = df[0][i] * df[0][i] + df[1][i] * df[1][i]; - aec->dPow[i] = gPow[0] * aec->dPow[i] + gPow[1] * near_spectrum; - // Calculate absolute spectra - abs_near_spectrum[i] = sqrtf(near_spectrum); - } - - // Estimate noise power. Wait until dPow is more stable. - if (aec->noiseEstCtr > 50) { - for (i = 0; i < PART_LEN1; i++) { - if (aec->dPow[i] < aec->dMinPow[i]) { - aec->dMinPow[i] = - (aec->dPow[i] + step * (aec->dMinPow[i] - aec->dPow[i])) * ramp; - } else { - aec->dMinPow[i] *= ramp; - } - } - } - - // Smooth increasing noise power from zero at the start, - // to avoid a sudden burst of comfort noise. - if (aec->noiseEstCtr < noiseInitBlocks) { - aec->noiseEstCtr++; - for (i = 0; i < PART_LEN1; i++) { - if (aec->dMinPow[i] > aec->dInitMinPow[i]) { - aec->dInitMinPow[i] = gInitNoise[0] * aec->dInitMinPow[i] + - gInitNoise[1] * aec->dMinPow[i]; - } else { - aec->dInitMinPow[i] = aec->dMinPow[i]; - } - } - aec->noisePow = aec->dInitMinPow; - } else { - aec->noisePow = aec->dMinPow; - } - - // Block wise delay estimation used for logging - if (aec->delay_logging_enabled) { - if (WebRtc_AddFarSpectrumFloat( - aec->delay_estimator_farend, abs_far_spectrum, PART_LEN1) == 0) { - int delay_estimate = WebRtc_DelayEstimatorProcessFloat( - aec->delay_estimator, abs_near_spectrum, PART_LEN1); - if (delay_estimate >= 0) { - // Update delay estimate buffer. - aec->delay_histogram[delay_estimate]++; - aec->num_delay_values++; - } - if (aec->delay_metrics_delivered == 1 && - aec->num_delay_values >= kDelayMetricsAggregationWindow) { - UpdateDelayMetrics(aec); - } - } - } - - // Update the xfBuf block position. - aec->xfBufBlockPos--; - if (aec->xfBufBlockPos == -1) { - aec->xfBufBlockPos = aec->num_partitions - 1; - } - - // Buffer xf - memcpy(aec->xfBuf[0] + aec->xfBufBlockPos * PART_LEN1, - xf_ptr, - sizeof(float) * PART_LEN1); - memcpy(aec->xfBuf[1] + aec->xfBufBlockPos * PART_LEN1, - &xf_ptr[PART_LEN1], - sizeof(float) * PART_LEN1); - - memset(yf, 0, sizeof(yf)); - - // Filter far - WebRtcAec_FilterFar(aec, yf); - - // Inverse fft to obtain echo estimate and error. - fft[0] = yf[0][0]; - fft[1] = yf[0][PART_LEN]; - for (i = 1; i < PART_LEN; i++) { - fft[2 * i] = yf[0][i]; - fft[2 * i + 1] = yf[1][i]; - } - aec_rdft_inverse_128(fft); - - scale = 2.0f / PART_LEN2; - for (i = 0; i < PART_LEN; i++) { - y[i] = fft[PART_LEN + i] * scale; // fft scaling - } - - for (i = 0; i < PART_LEN; i++) { - e[i] = nearend_ptr[i] - y[i]; - } - - // Error fft - memcpy(aec->eBuf + PART_LEN, e, sizeof(float) * PART_LEN); - memset(fft, 0, sizeof(float) * PART_LEN); - memcpy(fft + PART_LEN, e, sizeof(float) * PART_LEN); - // TODO(bjornv): Change to use TimeToFrequency(). - aec_rdft_forward_128(fft); - - ef[1][0] = 0; - ef[1][PART_LEN] = 0; - ef[0][0] = fft[0]; - ef[0][PART_LEN] = fft[1]; - for (i = 1; i < PART_LEN; i++) { - ef[0][i] = fft[2 * i]; - ef[1][i] = fft[2 * i + 1]; - } - - RTC_AEC_DEBUG_RAW_WRITE(aec->e_fft_file, - &ef[0][0], - sizeof(ef[0][0]) * PART_LEN1 * 2); - - if (aec->metricsMode == 1) { - // Note that the first PART_LEN samples in fft (before transformation) are - // zero. Hence, the scaling by two in UpdateLevel() should not be - // performed. That scaling is taken care of in UpdateMetrics() instead. - UpdateLevel(&aec->linoutlevel, ef); - } - - // Scale error signal inversely with far power. - WebRtcAec_ScaleErrorSignal(aec, ef); - WebRtcAec_FilterAdaptation(aec, fft, ef); - NonLinearProcessing(aec, output, outputH_ptr); - - if (aec->metricsMode == 1) { - // Update power levels and echo metrics - UpdateLevel(&aec->farlevel, (float(*)[PART_LEN1])xf_ptr); - UpdateLevel(&aec->nearlevel, df); - UpdateMetrics(aec); - } - - // Store the output block. - WebRtc_WriteBuffer(aec->outFrBuf, output, PART_LEN); - // For high bands - for (i = 0; i < aec->num_bands - 1; ++i) { - WebRtc_WriteBuffer(aec->outFrBufH[i], outputH[i], PART_LEN); - } - - RTC_AEC_DEBUG_WAV_WRITE(aec->outLinearFile, e, PART_LEN); - RTC_AEC_DEBUG_WAV_WRITE(aec->outFile, output, PART_LEN); -} - -AecCore* WebRtcAec_CreateAec() { - int i; - AecCore* aec = malloc(sizeof(AecCore)); - if (!aec) { - return NULL; - } - - aec->nearFrBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(float)); - if (!aec->nearFrBuf) { - WebRtcAec_FreeAec(aec); - return NULL; - } - - aec->outFrBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(float)); - if (!aec->outFrBuf) { - WebRtcAec_FreeAec(aec); - return NULL; - } - - for (i = 0; i < NUM_HIGH_BANDS_MAX; ++i) { - aec->nearFrBufH[i] = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, - sizeof(float)); - if (!aec->nearFrBufH[i]) { - WebRtcAec_FreeAec(aec); - return NULL; - } - aec->outFrBufH[i] = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, - sizeof(float)); - if (!aec->outFrBufH[i]) { - WebRtcAec_FreeAec(aec); - return NULL; - } - } - - // Create far-end buffers. - aec->far_buf = - WebRtc_CreateBuffer(kBufSizePartitions, sizeof(float) * 2 * PART_LEN1); - if (!aec->far_buf) { - WebRtcAec_FreeAec(aec); - return NULL; - } - aec->far_buf_windowed = - WebRtc_CreateBuffer(kBufSizePartitions, sizeof(float) * 2 * PART_LEN1); - if (!aec->far_buf_windowed) { - WebRtcAec_FreeAec(aec); - return NULL; - } -#ifdef WEBRTC_AEC_DEBUG_DUMP - aec->instance_index = webrtc_aec_instance_count; - aec->far_time_buf = - WebRtc_CreateBuffer(kBufSizePartitions, sizeof(float) * PART_LEN); - if (!aec->far_time_buf) { - WebRtcAec_FreeAec(aec); - return NULL; - } - aec->farFile = aec->nearFile = aec->outFile = aec->outLinearFile = NULL; - aec->debug_dump_count = 0; -#endif - aec->delay_estimator_farend = - WebRtc_CreateDelayEstimatorFarend(PART_LEN1, kHistorySizeBlocks); - if (aec->delay_estimator_farend == NULL) { - WebRtcAec_FreeAec(aec); - return NULL; - } - // We create the delay_estimator with the same amount of maximum lookahead as - // the delay history size (kHistorySizeBlocks) for symmetry reasons. - aec->delay_estimator = WebRtc_CreateDelayEstimator( - aec->delay_estimator_farend, kHistorySizeBlocks); - if (aec->delay_estimator == NULL) { - WebRtcAec_FreeAec(aec); - return NULL; - } -#ifdef WEBRTC_ANDROID - aec->delay_agnostic_enabled = 1; // DA-AEC enabled by default. - // DA-AEC assumes the system is causal from the beginning and will self adjust - // the lookahead when shifting is required. - WebRtc_set_lookahead(aec->delay_estimator, 0); -#else - aec->delay_agnostic_enabled = 0; - WebRtc_set_lookahead(aec->delay_estimator, kLookaheadBlocks); -#endif - aec->extended_filter_enabled = 0; - - // Assembly optimization - WebRtcAec_FilterFar = FilterFar; - WebRtcAec_ScaleErrorSignal = ScaleErrorSignal; - WebRtcAec_FilterAdaptation = FilterAdaptation; - WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppress; - WebRtcAec_ComfortNoise = ComfortNoise; - WebRtcAec_SubbandCoherence = SubbandCoherence; - -#if defined(WEBRTC_ARCH_X86_FAMILY) - if (WebRtc_GetCPUInfo(kSSE2)) { - WebRtcAec_InitAec_SSE2(); - } -#endif - -#if defined(MIPS_FPU_LE) - WebRtcAec_InitAec_mips(); -#endif - -#if defined(WEBRTC_HAS_NEON) - WebRtcAec_InitAec_neon(); -#elif defined(WEBRTC_DETECT_NEON) - if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) { - WebRtcAec_InitAec_neon(); - } -#endif - - aec_rdft_init(); - - return aec; -} - -void WebRtcAec_FreeAec(AecCore* aec) { - int i; - if (aec == NULL) { - return; - } - - WebRtc_FreeBuffer(aec->nearFrBuf); - WebRtc_FreeBuffer(aec->outFrBuf); - - for (i = 0; i < NUM_HIGH_BANDS_MAX; ++i) { - WebRtc_FreeBuffer(aec->nearFrBufH[i]); - WebRtc_FreeBuffer(aec->outFrBufH[i]); - } - - WebRtc_FreeBuffer(aec->far_buf); - WebRtc_FreeBuffer(aec->far_buf_windowed); -#ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_FreeBuffer(aec->far_time_buf); -#endif - RTC_AEC_DEBUG_WAV_CLOSE(aec->farFile); - RTC_AEC_DEBUG_WAV_CLOSE(aec->nearFile); - RTC_AEC_DEBUG_WAV_CLOSE(aec->outFile); - RTC_AEC_DEBUG_WAV_CLOSE(aec->outLinearFile); - RTC_AEC_DEBUG_RAW_CLOSE(aec->e_fft_file); - - WebRtc_FreeDelayEstimator(aec->delay_estimator); - WebRtc_FreeDelayEstimatorFarend(aec->delay_estimator_farend); - - free(aec); -} - -int WebRtcAec_InitAec(AecCore* aec, int sampFreq) { - int i; - - aec->sampFreq = sampFreq; - - if (sampFreq == 8000) { - aec->normal_mu = 0.6f; - aec->normal_error_threshold = 2e-6f; - aec->num_bands = 1; - } else { - aec->normal_mu = 0.5f; - aec->normal_error_threshold = 1.5e-6f; - aec->num_bands = (size_t)(sampFreq / 16000); - } - - WebRtc_InitBuffer(aec->nearFrBuf); - WebRtc_InitBuffer(aec->outFrBuf); - for (i = 0; i < NUM_HIGH_BANDS_MAX; ++i) { - WebRtc_InitBuffer(aec->nearFrBufH[i]); - WebRtc_InitBuffer(aec->outFrBufH[i]); - } - - // Initialize far-end buffers. - WebRtc_InitBuffer(aec->far_buf); - WebRtc_InitBuffer(aec->far_buf_windowed); -#ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_InitBuffer(aec->far_time_buf); - { - int process_rate = sampFreq > 16000 ? 16000 : sampFreq; - RTC_AEC_DEBUG_WAV_REOPEN("aec_far", aec->instance_index, - aec->debug_dump_count, process_rate, - &aec->farFile ); - RTC_AEC_DEBUG_WAV_REOPEN("aec_near", aec->instance_index, - aec->debug_dump_count, process_rate, - &aec->nearFile); - RTC_AEC_DEBUG_WAV_REOPEN("aec_out", aec->instance_index, - aec->debug_dump_count, process_rate, - &aec->outFile ); - RTC_AEC_DEBUG_WAV_REOPEN("aec_out_linear", aec->instance_index, - aec->debug_dump_count, process_rate, - &aec->outLinearFile); - } - - RTC_AEC_DEBUG_RAW_OPEN("aec_e_fft", - aec->debug_dump_count, - &aec->e_fft_file); - - ++aec->debug_dump_count; -#endif - aec->system_delay = 0; - - if (WebRtc_InitDelayEstimatorFarend(aec->delay_estimator_farend) != 0) { - return -1; - } - if (WebRtc_InitDelayEstimator(aec->delay_estimator) != 0) { - return -1; - } - aec->delay_logging_enabled = 0; - aec->delay_metrics_delivered = 0; - memset(aec->delay_histogram, 0, sizeof(aec->delay_histogram)); - aec->num_delay_values = 0; - aec->delay_median = -1; - aec->delay_std = -1; - aec->fraction_poor_delays = -1.0f; - - aec->signal_delay_correction = 0; - aec->previous_delay = -2; // (-2): Uninitialized. - aec->delay_correction_count = 0; - aec->shift_offset = kInitialShiftOffset; - aec->delay_quality_threshold = kDelayQualityThresholdMin; - - aec->num_partitions = kNormalNumPartitions; - - // Update the delay estimator with filter length. We use half the - // |num_partitions| to take the echo path into account. In practice we say - // that the echo has a duration of maximum half |num_partitions|, which is not - // true, but serves as a crude measure. - WebRtc_set_allowed_offset(aec->delay_estimator, aec->num_partitions / 2); - // TODO(bjornv): I currently hard coded the enable. Once we've established - // that AECM has no performance regression, robust_validation will be enabled - // all the time and the APIs to turn it on/off will be removed. Hence, remove - // this line then. - WebRtc_enable_robust_validation(aec->delay_estimator, 1); - aec->frame_count = 0; - - // Default target suppression mode. - aec->nlp_mode = 1; - - // Sampling frequency multiplier w.r.t. 8 kHz. - // In case of multiple bands we process the lower band in 16 kHz, hence the - // multiplier is always 2. - if (aec->num_bands > 1) { - aec->mult = 2; - } else { - aec->mult = (short)aec->sampFreq / 8000; - } - - aec->farBufWritePos = 0; - aec->farBufReadPos = 0; - - aec->inSamples = 0; - aec->outSamples = 0; - aec->knownDelay = 0; - - // Initialize buffers - memset(aec->dBuf, 0, sizeof(aec->dBuf)); - memset(aec->eBuf, 0, sizeof(aec->eBuf)); - // For H bands - for (i = 0; i < NUM_HIGH_BANDS_MAX; ++i) { - memset(aec->dBufH[i], 0, sizeof(aec->dBufH[i])); - } - - memset(aec->xPow, 0, sizeof(aec->xPow)); - memset(aec->dPow, 0, sizeof(aec->dPow)); - memset(aec->dInitMinPow, 0, sizeof(aec->dInitMinPow)); - aec->noisePow = aec->dInitMinPow; - aec->noiseEstCtr = 0; - - // Initial comfort noise power - for (i = 0; i < PART_LEN1; i++) { - aec->dMinPow[i] = 1.0e6f; - } - - // Holds the last block written to - aec->xfBufBlockPos = 0; - // TODO: Investigate need for these initializations. Deleting them doesn't - // change the output at all and yields 0.4% overall speedup. - memset(aec->xfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); - memset(aec->wfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); - memset(aec->sde, 0, sizeof(complex_t) * PART_LEN1); - memset(aec->sxd, 0, sizeof(complex_t) * PART_LEN1); - memset( - aec->xfwBuf, 0, sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); - memset(aec->se, 0, sizeof(float) * PART_LEN1); - - // To prevent numerical instability in the first block. - for (i = 0; i < PART_LEN1; i++) { - aec->sd[i] = 1; - } - for (i = 0; i < PART_LEN1; i++) { - aec->sx[i] = 1; - } - - memset(aec->hNs, 0, sizeof(aec->hNs)); - memset(aec->outBuf, 0, sizeof(float) * PART_LEN); - - aec->hNlFbMin = 1; - aec->hNlFbLocalMin = 1; - aec->hNlXdAvgMin = 1; - aec->hNlNewMin = 0; - aec->hNlMinCtr = 0; - aec->overDrive = 2; - aec->overDriveSm = 2; - aec->delayIdx = 0; - aec->stNearState = 0; - aec->echoState = 0; - aec->divergeState = 0; - - aec->seed = 777; - aec->delayEstCtr = 0; - - // Metrics disabled by default - aec->metricsMode = 0; - InitMetrics(aec); - - return 0; -} - -void WebRtcAec_BufferFarendPartition(AecCore* aec, const float* farend) { - float fft[PART_LEN2]; - float xf[2][PART_LEN1]; - - // Check if the buffer is full, and in that case flush the oldest data. - if (WebRtc_available_write(aec->far_buf) < 1) { - WebRtcAec_MoveFarReadPtr(aec, 1); - } - // Convert far-end partition to the frequency domain without windowing. - memcpy(fft, farend, sizeof(float) * PART_LEN2); - TimeToFrequency(fft, xf, 0); - WebRtc_WriteBuffer(aec->far_buf, &xf[0][0], 1); - - // Convert far-end partition to the frequency domain with windowing. - memcpy(fft, farend, sizeof(float) * PART_LEN2); - TimeToFrequency(fft, xf, 1); - WebRtc_WriteBuffer(aec->far_buf_windowed, &xf[0][0], 1); -} - -int WebRtcAec_MoveFarReadPtr(AecCore* aec, int elements) { - int elements_moved = MoveFarReadPtrWithoutSystemDelayUpdate(aec, elements); - aec->system_delay -= elements_moved * PART_LEN; - return elements_moved; -} - -void WebRtcAec_ProcessFrames(AecCore* aec, - const float* const* nearend, - size_t num_bands, - size_t num_samples, - int knownDelay, - float* const* out) { - size_t i, j; - int out_elements = 0; - - aec->frame_count++; - // For each frame the process is as follows: - // 1) If the system_delay indicates on being too small for processing a - // frame we stuff the buffer with enough data for 10 ms. - // 2 a) Adjust the buffer to the system delay, by moving the read pointer. - // b) Apply signal based delay correction, if we have detected poor AEC - // performance. - // 3) TODO(bjornv): Investigate if we need to add this: - // If we can't move read pointer due to buffer size limitations we - // flush/stuff the buffer. - // 4) Process as many partitions as possible. - // 5) Update the |system_delay| with respect to a full frame of FRAME_LEN - // samples. Even though we will have data left to process (we work with - // partitions) we consider updating a whole frame, since that's the - // amount of data we input and output in audio_processing. - // 6) Update the outputs. - - // The AEC has two different delay estimation algorithms built in. The - // first relies on delay input values from the user and the amount of - // shifted buffer elements is controlled by |knownDelay|. This delay will - // give a guess on how much we need to shift far-end buffers to align with - // the near-end signal. The other delay estimation algorithm uses the - // far- and near-end signals to find the offset between them. This one - // (called "signal delay") is then used to fine tune the alignment, or - // simply compensate for errors in the system based one. - // Note that the two algorithms operate independently. Currently, we only - // allow one algorithm to be turned on. - - assert(aec->num_bands == num_bands); - - for (j = 0; j < num_samples; j+= FRAME_LEN) { - // TODO(bjornv): Change the near-end buffer handling to be the same as for - // far-end, that is, with a near_pre_buf. - // Buffer the near-end frame. - WebRtc_WriteBuffer(aec->nearFrBuf, &nearend[0][j], FRAME_LEN); - // For H band - for (i = 1; i < num_bands; ++i) { - WebRtc_WriteBuffer(aec->nearFrBufH[i - 1], &nearend[i][j], FRAME_LEN); - } - - // 1) At most we process |aec->mult|+1 partitions in 10 ms. Make sure we - // have enough far-end data for that by stuffing the buffer if the - // |system_delay| indicates others. - if (aec->system_delay < FRAME_LEN) { - // We don't have enough data so we rewind 10 ms. - WebRtcAec_MoveFarReadPtr(aec, -(aec->mult + 1)); - } - - if (!aec->delay_agnostic_enabled) { - // 2 a) Compensate for a possible change in the system delay. - - // TODO(bjornv): Investigate how we should round the delay difference; - // right now we know that incoming |knownDelay| is underestimated when - // it's less than |aec->knownDelay|. We therefore, round (-32) in that - // direction. In the other direction, we don't have this situation, but - // might flush one partition too little. This can cause non-causality, - // which should be investigated. Maybe, allow for a non-symmetric - // rounding, like -16. - int move_elements = (aec->knownDelay - knownDelay - 32) / PART_LEN; - int moved_elements = - MoveFarReadPtrWithoutSystemDelayUpdate(aec, move_elements); - aec->knownDelay -= moved_elements * PART_LEN; - } else { - // 2 b) Apply signal based delay correction. - int move_elements = SignalBasedDelayCorrection(aec); - int moved_elements = - MoveFarReadPtrWithoutSystemDelayUpdate(aec, move_elements); - int far_near_buffer_diff = WebRtc_available_read(aec->far_buf) - - WebRtc_available_read(aec->nearFrBuf) / PART_LEN; - WebRtc_SoftResetDelayEstimator(aec->delay_estimator, moved_elements); - WebRtc_SoftResetDelayEstimatorFarend(aec->delay_estimator_farend, - moved_elements); - aec->signal_delay_correction += moved_elements; - // If we rely on reported system delay values only, a buffer underrun here - // can never occur since we've taken care of that in 1) above. Here, we - // apply signal based delay correction and can therefore end up with - // buffer underruns since the delay estimation can be wrong. We therefore - // stuff the buffer with enough elements if needed. - if (far_near_buffer_diff < 0) { - WebRtcAec_MoveFarReadPtr(aec, far_near_buffer_diff); - } - } - - // 4) Process as many blocks as possible. - while (WebRtc_available_read(aec->nearFrBuf) >= PART_LEN) { - ProcessBlock(aec); - } - - // 5) Update system delay with respect to the entire frame. - aec->system_delay -= FRAME_LEN; - - // 6) Update output frame. - // Stuff the out buffer if we have less than a frame to output. - // This should only happen for the first frame. - out_elements = (int)WebRtc_available_read(aec->outFrBuf); - if (out_elements < FRAME_LEN) { - WebRtc_MoveReadPtr(aec->outFrBuf, out_elements - FRAME_LEN); - for (i = 0; i < num_bands - 1; ++i) { - WebRtc_MoveReadPtr(aec->outFrBufH[i], out_elements - FRAME_LEN); - } - } - // Obtain an output frame. - WebRtc_ReadBuffer(aec->outFrBuf, NULL, &out[0][j], FRAME_LEN); - // For H bands. - for (i = 1; i < num_bands; ++i) { - WebRtc_ReadBuffer(aec->outFrBufH[i - 1], NULL, &out[i][j], FRAME_LEN); - } - } -} - -int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std, - float* fraction_poor_delays) { - assert(self != NULL); - assert(median != NULL); - assert(std != NULL); - - if (self->delay_logging_enabled == 0) { - // Logging disabled. - return -1; - } - - if (self->delay_metrics_delivered == 0) { - UpdateDelayMetrics(self); - self->delay_metrics_delivered = 1; - } - *median = self->delay_median; - *std = self->delay_std; - *fraction_poor_delays = self->fraction_poor_delays; - - return 0; -} - -int WebRtcAec_echo_state(AecCore* self) { return self->echoState; } - -void WebRtcAec_GetEchoStats(AecCore* self, - Stats* erl, - Stats* erle, - Stats* a_nlp) { - assert(erl != NULL); - assert(erle != NULL); - assert(a_nlp != NULL); - *erl = self->erl; - *erle = self->erle; - *a_nlp = self->aNlp; -} - -#ifdef WEBRTC_AEC_DEBUG_DUMP -void* WebRtcAec_far_time_buf(AecCore* self) { return self->far_time_buf; } -#endif - -void WebRtcAec_SetConfigCore(AecCore* self, - int nlp_mode, - int metrics_mode, - int delay_logging) { - assert(nlp_mode >= 0 && nlp_mode < 3); - self->nlp_mode = nlp_mode; - self->metricsMode = metrics_mode; - if (self->metricsMode) { - InitMetrics(self); - } - // Turn on delay logging if it is either set explicitly or if delay agnostic - // AEC is enabled (which requires delay estimates). - self->delay_logging_enabled = delay_logging || self->delay_agnostic_enabled; - if (self->delay_logging_enabled) { - memset(self->delay_histogram, 0, sizeof(self->delay_histogram)); - } -} - -void WebRtcAec_enable_delay_agnostic(AecCore* self, int enable) { - self->delay_agnostic_enabled = enable; -} - -int WebRtcAec_delay_agnostic_enabled(AecCore* self) { - return self->delay_agnostic_enabled; -} - -void WebRtcAec_enable_extended_filter(AecCore* self, int enable) { - self->extended_filter_enabled = enable; - self->num_partitions = enable ? kExtendedNumPartitions : kNormalNumPartitions; - // Update the delay estimator with filter length. See InitAEC() for details. - WebRtc_set_allowed_offset(self->delay_estimator, self->num_partitions / 2); -} - -int WebRtcAec_extended_filter_enabled(AecCore* self) { - return self->extended_filter_enabled; -} - -int WebRtcAec_system_delay(AecCore* self) { return self->system_delay; } - -void WebRtcAec_SetSystemDelay(AecCore* self, int delay) { - assert(delay >= 0); - self->system_delay = delay; -} diff --git a/webrtc/modules/audio_processing/aec/aec_core.h b/webrtc/modules/audio_processing/aec/aec_core.h deleted file mode 100644 index 241f077..0000000 --- a/webrtc/modules/audio_processing/aec/aec_core.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -/* - * Specifies the interface for the AEC core. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_H_ - -#include - -#include "webrtc/typedefs.h" - -#define FRAME_LEN 80 -#define PART_LEN 64 // Length of partition -#define PART_LEN1 (PART_LEN + 1) // Unique fft coefficients -#define PART_LEN2 (PART_LEN * 2) // Length of partition * 2 -#define NUM_HIGH_BANDS_MAX 2 // Max number of high bands - -typedef float complex_t[2]; -// For performance reasons, some arrays of complex numbers are replaced by twice -// as long arrays of float, all the real parts followed by all the imaginary -// ones (complex_t[SIZE] -> float[2][SIZE]). This allows SIMD optimizations and -// is better than two arrays (one for the real parts and one for the imaginary -// parts) as this other way would require two pointers instead of one and cause -// extra register spilling. This also allows the offsets to be calculated at -// compile time. - -// Metrics -enum { - kOffsetLevel = -100 -}; - -typedef struct Stats { - float instant; - float average; - float min; - float max; - float sum; - float hisum; - float himean; - int counter; - int hicounter; -} Stats; - -typedef struct AecCore AecCore; - -AecCore* WebRtcAec_CreateAec(); // Returns NULL on error. -void WebRtcAec_FreeAec(AecCore* aec); -int WebRtcAec_InitAec(AecCore* aec, int sampFreq); -void WebRtcAec_InitAec_SSE2(void); -#if defined(MIPS_FPU_LE) -void WebRtcAec_InitAec_mips(void); -#endif -#if defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON) -void WebRtcAec_InitAec_neon(void); -#endif - -void WebRtcAec_BufferFarendPartition(AecCore* aec, const float* farend); -void WebRtcAec_ProcessFrames(AecCore* aec, - const float* const* nearend, - size_t num_bands, - size_t num_samples, - int knownDelay, - float* const* out); - -// A helper function to call WebRtc_MoveReadPtr() for all far-end buffers. -// Returns the number of elements moved, and adjusts |system_delay| by the -// corresponding amount in ms. -int WebRtcAec_MoveFarReadPtr(AecCore* aec, int elements); - -// Calculates the median, standard deviation and amount of poor values among the -// delay estimates aggregated up to the first call to the function. After that -// first call the metrics are aggregated and updated every second. With poor -// values we mean values that most likely will cause the AEC to perform poorly. -// TODO(bjornv): Consider changing tests and tools to handle constant -// constant aggregation window throughout the session instead. -int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std, - float* fraction_poor_delays); - -// Returns the echo state (1: echo, 0: no echo). -int WebRtcAec_echo_state(AecCore* self); - -// Gets statistics of the echo metrics ERL, ERLE, A_NLP. -void WebRtcAec_GetEchoStats(AecCore* self, - Stats* erl, - Stats* erle, - Stats* a_nlp); -#ifdef WEBRTC_AEC_DEBUG_DUMP -void* WebRtcAec_far_time_buf(AecCore* self); -#endif - -// Sets local configuration modes. -void WebRtcAec_SetConfigCore(AecCore* self, - int nlp_mode, - int metrics_mode, - int delay_logging); - -// Non-zero enables, zero disables. -void WebRtcAec_enable_delay_agnostic(AecCore* self, int enable); - -// Returns non-zero if delay agnostic (i.e., signal based delay estimation) is -// enabled and zero if disabled. -int WebRtcAec_delay_agnostic_enabled(AecCore* self); - -// Enables or disables extended filter mode. Non-zero enables, zero disables. -void WebRtcAec_enable_extended_filter(AecCore* self, int enable); - -// Returns non-zero if extended filter mode is enabled and zero if disabled. -int WebRtcAec_extended_filter_enabled(AecCore* self); - -// Returns the current |system_delay|, i.e., the buffered difference between -// far-end and near-end. -int WebRtcAec_system_delay(AecCore* self); - -// Sets the |system_delay| to |value|. Note that if the value is changed -// improperly, there can be a performance regression. So it should be used with -// care. -void WebRtcAec_SetSystemDelay(AecCore* self, int delay); - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_H_ diff --git a/webrtc/modules/audio_processing/aec/aec_core_internal.h b/webrtc/modules/audio_processing/aec/aec_core_internal.h deleted file mode 100644 index 2de0283..0000000 --- a/webrtc/modules/audio_processing/aec/aec_core_internal.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2013 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_ - -#include "webrtc/common_audio/ring_buffer.h" -#include "webrtc/common_audio/wav_file.h" -#include "webrtc/modules/audio_processing/aec/aec_common.h" -#include "webrtc/modules/audio_processing/aec/aec_core.h" -#include "webrtc/typedefs.h" - -// Number of partitions for the extended filter mode. The first one is an enum -// to be used in array declarations, as it represents the maximum filter length. -enum { - kExtendedNumPartitions = 32 -}; -static const int kNormalNumPartitions = 12; - -// Delay estimator constants, used for logging and delay compensation if -// if reported delays are disabled. -enum { - kLookaheadBlocks = 15 -}; -enum { - // 500 ms for 16 kHz which is equivalent with the limit of reported delays. - kHistorySizeBlocks = 125 -}; - -// Extended filter adaptation parameters. -// TODO(ajm): No narrowband tuning yet. -static const float kExtendedMu = 0.4f; -static const float kExtendedErrorThreshold = 1.0e-6f; - -typedef struct PowerLevel { - float sfrsum; - int sfrcounter; - float framelevel; - float frsum; - int frcounter; - float minlevel; - float averagelevel; -} PowerLevel; - -struct AecCore { - int farBufWritePos, farBufReadPos; - - int knownDelay; - int inSamples, outSamples; - int delayEstCtr; - - RingBuffer* nearFrBuf; - RingBuffer* outFrBuf; - - RingBuffer* nearFrBufH[NUM_HIGH_BANDS_MAX]; - RingBuffer* outFrBufH[NUM_HIGH_BANDS_MAX]; - - float dBuf[PART_LEN2]; // nearend - float eBuf[PART_LEN2]; // error - - float dBufH[NUM_HIGH_BANDS_MAX][PART_LEN2]; // nearend - - float xPow[PART_LEN1]; - float dPow[PART_LEN1]; - float dMinPow[PART_LEN1]; - float dInitMinPow[PART_LEN1]; - float* noisePow; - - float xfBuf[2][kExtendedNumPartitions * PART_LEN1]; // farend fft buffer - float wfBuf[2][kExtendedNumPartitions * PART_LEN1]; // filter fft - complex_t sde[PART_LEN1]; // cross-psd of nearend and error - complex_t sxd[PART_LEN1]; // cross-psd of farend and nearend - // Farend windowed fft buffer. - complex_t xfwBuf[kExtendedNumPartitions * PART_LEN1]; - - float sx[PART_LEN1], sd[PART_LEN1], se[PART_LEN1]; // far, near, error psd - float hNs[PART_LEN1]; - float hNlFbMin, hNlFbLocalMin; - float hNlXdAvgMin; - int hNlNewMin, hNlMinCtr; - float overDrive, overDriveSm; - int nlp_mode; - float outBuf[PART_LEN]; - int delayIdx; - - short stNearState, echoState; - short divergeState; - - int xfBufBlockPos; - - RingBuffer* far_buf; - RingBuffer* far_buf_windowed; - int system_delay; // Current system delay buffered in AEC. - - int mult; // sampling frequency multiple - int sampFreq; - size_t num_bands; - uint32_t seed; - - float normal_mu; // stepsize - float normal_error_threshold; // error threshold - - int noiseEstCtr; - - PowerLevel farlevel; - PowerLevel nearlevel; - PowerLevel linoutlevel; - PowerLevel nlpoutlevel; - - int metricsMode; - int stateCounter; - Stats erl; - Stats erle; - Stats aNlp; - Stats rerl; - - // Quantities to control H band scaling for SWB input - int freq_avg_ic; // initial bin for averaging nlp gain - int flag_Hband_cn; // for comfort noise - float cn_scale_Hband; // scale for comfort noise in H band - - int delay_metrics_delivered; - int delay_histogram[kHistorySizeBlocks]; - int num_delay_values; - int delay_median; - int delay_std; - float fraction_poor_delays; - int delay_logging_enabled; - void* delay_estimator_farend; - void* delay_estimator; - // Variables associated with delay correction through signal based delay - // estimation feedback. - int signal_delay_correction; - int previous_delay; - int delay_correction_count; - int shift_offset; - float delay_quality_threshold; - int frame_count; - - // 0 = delay agnostic mode (signal based delay correction) disabled. - // Otherwise enabled. - int delay_agnostic_enabled; - // 1 = extended filter mode enabled, 0 = disabled. - int extended_filter_enabled; - // Runtime selection of number of filter partitions. - int num_partitions; - -#ifdef WEBRTC_AEC_DEBUG_DUMP - // Sequence number of this AEC instance, so that different instances can - // choose different dump file names. - int instance_index; - - // Number of times we've restarted dumping; used to pick new dump file names - // each time. - int debug_dump_count; - - RingBuffer* far_time_buf; - rtc_WavWriter* farFile; - rtc_WavWriter* nearFile; - rtc_WavWriter* outFile; - rtc_WavWriter* outLinearFile; - FILE* e_fft_file; -#endif -}; - -typedef void (*WebRtcAecFilterFar)(AecCore* aec, float yf[2][PART_LEN1]); -extern WebRtcAecFilterFar WebRtcAec_FilterFar; -typedef void (*WebRtcAecScaleErrorSignal)(AecCore* aec, float ef[2][PART_LEN1]); -extern WebRtcAecScaleErrorSignal WebRtcAec_ScaleErrorSignal; -typedef void (*WebRtcAecFilterAdaptation)(AecCore* aec, - float* fft, - float ef[2][PART_LEN1]); -extern WebRtcAecFilterAdaptation WebRtcAec_FilterAdaptation; -typedef void (*WebRtcAecOverdriveAndSuppress)(AecCore* aec, - float hNl[PART_LEN1], - const float hNlFb, - float efw[2][PART_LEN1]); -extern WebRtcAecOverdriveAndSuppress WebRtcAec_OverdriveAndSuppress; - -typedef void (*WebRtcAecComfortNoise)(AecCore* aec, - float efw[2][PART_LEN1], - complex_t* comfortNoiseHband, - const float* noisePow, - const float* lambda); -extern WebRtcAecComfortNoise WebRtcAec_ComfortNoise; - -typedef void (*WebRtcAecSubBandCoherence)(AecCore* aec, - float efw[2][PART_LEN1], - float xfw[2][PART_LEN1], - float* fft, - float* cohde, - float* cohxd); -extern WebRtcAecSubBandCoherence WebRtcAec_SubbandCoherence; - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_INTERNAL_H_ diff --git a/webrtc/modules/audio_processing/aec/aec_core_mips.c b/webrtc/modules/audio_processing/aec/aec_core_mips.c deleted file mode 100644 index bb33087..0000000 --- a/webrtc/modules/audio_processing/aec/aec_core_mips.c +++ /dev/null @@ -1,774 +0,0 @@ -/* - * Copyright (c) 2013 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. - */ - -/* - * The core AEC algorithm, which is presented with time-aligned signals. - */ - -#include "webrtc/modules/audio_processing/aec/aec_core.h" - -#include - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/aec/aec_core_internal.h" -#include "webrtc/modules/audio_processing/aec/aec_rdft.h" - -static const int flagHbandCn = 1; // flag for adding comfort noise in H band -extern const float WebRtcAec_weightCurve[65]; -extern const float WebRtcAec_overDriveCurve[65]; - -void WebRtcAec_ComfortNoise_mips(AecCore* aec, - float efw[2][PART_LEN1], - complex_t* comfortNoiseHband, - const float* noisePow, - const float* lambda) { - int i, num; - float rand[PART_LEN]; - float noise, noiseAvg, tmp, tmpAvg; - int16_t randW16[PART_LEN]; - complex_t u[PART_LEN1]; - - const float pi2 = 6.28318530717959f; - const float pi2t = pi2 / 32768; - - // Generate a uniform random array on [0 1] - WebRtcSpl_RandUArray(randW16, PART_LEN, &aec->seed); - - int16_t* randWptr = randW16; - float randTemp, randTemp2, randTemp3, randTemp4; - int32_t tmp1s, tmp2s, tmp3s, tmp4s; - - for (i = 0; i < PART_LEN; i+=4) { - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "lh %[tmp1s], 0(%[randWptr]) \n\t" - "lh %[tmp2s], 2(%[randWptr]) \n\t" - "lh %[tmp3s], 4(%[randWptr]) \n\t" - "lh %[tmp4s], 6(%[randWptr]) \n\t" - "mtc1 %[tmp1s], %[randTemp] \n\t" - "mtc1 %[tmp2s], %[randTemp2] \n\t" - "mtc1 %[tmp3s], %[randTemp3] \n\t" - "mtc1 %[tmp4s], %[randTemp4] \n\t" - "cvt.s.w %[randTemp], %[randTemp] \n\t" - "cvt.s.w %[randTemp2], %[randTemp2] \n\t" - "cvt.s.w %[randTemp3], %[randTemp3] \n\t" - "cvt.s.w %[randTemp4], %[randTemp4] \n\t" - "addiu %[randWptr], %[randWptr], 8 \n\t" - "mul.s %[randTemp], %[randTemp], %[pi2t] \n\t" - "mul.s %[randTemp2], %[randTemp2], %[pi2t] \n\t" - "mul.s %[randTemp3], %[randTemp3], %[pi2t] \n\t" - "mul.s %[randTemp4], %[randTemp4], %[pi2t] \n\t" - ".set pop \n\t" - : [randWptr] "+r" (randWptr), [randTemp] "=&f" (randTemp), - [randTemp2] "=&f" (randTemp2), [randTemp3] "=&f" (randTemp3), - [randTemp4] "=&f" (randTemp4), [tmp1s] "=&r" (tmp1s), - [tmp2s] "=&r" (tmp2s), [tmp3s] "=&r" (tmp3s), - [tmp4s] "=&r" (tmp4s) - : [pi2t] "f" (pi2t) - : "memory" - ); - - u[i+1][0] = cosf(randTemp); - u[i+1][1] = sinf(randTemp); - u[i+2][0] = cosf(randTemp2); - u[i+2][1] = sinf(randTemp2); - u[i+3][0] = cosf(randTemp3); - u[i+3][1] = sinf(randTemp3); - u[i+4][0] = cosf(randTemp4); - u[i+4][1] = sinf(randTemp4); - } - - // Reject LF noise - float* u_ptr = &u[1][0]; - float noise2, noise3, noise4; - float tmp1f, tmp2f, tmp3f, tmp4f, tmp5f, tmp6f, tmp7f, tmp8f; - - u[0][0] = 0; - u[0][1] = 0; - for (i = 1; i < PART_LEN1; i+=4) { - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "lwc1 %[noise], 4(%[noisePow]) \n\t" - "lwc1 %[noise2], 8(%[noisePow]) \n\t" - "lwc1 %[noise3], 12(%[noisePow]) \n\t" - "lwc1 %[noise4], 16(%[noisePow]) \n\t" - "sqrt.s %[noise], %[noise] \n\t" - "sqrt.s %[noise2], %[noise2] \n\t" - "sqrt.s %[noise3], %[noise3] \n\t" - "sqrt.s %[noise4], %[noise4] \n\t" - "lwc1 %[tmp1f], 0(%[u_ptr]) \n\t" - "lwc1 %[tmp2f], 4(%[u_ptr]) \n\t" - "lwc1 %[tmp3f], 8(%[u_ptr]) \n\t" - "lwc1 %[tmp4f], 12(%[u_ptr]) \n\t" - "lwc1 %[tmp5f], 16(%[u_ptr]) \n\t" - "lwc1 %[tmp6f], 20(%[u_ptr]) \n\t" - "lwc1 %[tmp7f], 24(%[u_ptr]) \n\t" - "lwc1 %[tmp8f], 28(%[u_ptr]) \n\t" - "addiu %[noisePow], %[noisePow], 16 \n\t" - "mul.s %[tmp1f], %[tmp1f], %[noise] \n\t" - "mul.s %[tmp2f], %[tmp2f], %[noise] \n\t" - "mul.s %[tmp3f], %[tmp3f], %[noise2] \n\t" - "mul.s %[tmp4f], %[tmp4f], %[noise2] \n\t" - "mul.s %[tmp5f], %[tmp5f], %[noise3] \n\t" - "mul.s %[tmp6f], %[tmp6f], %[noise3] \n\t" - "swc1 %[tmp1f], 0(%[u_ptr]) \n\t" - "swc1 %[tmp3f], 8(%[u_ptr]) \n\t" - "mul.s %[tmp8f], %[tmp8f], %[noise4] \n\t" - "mul.s %[tmp7f], %[tmp7f], %[noise4] \n\t" - "neg.s %[tmp2f] \n\t" - "neg.s %[tmp4f] \n\t" - "neg.s %[tmp6f] \n\t" - "neg.s %[tmp8f] \n\t" - "swc1 %[tmp5f], 16(%[u_ptr]) \n\t" - "swc1 %[tmp7f], 24(%[u_ptr]) \n\t" - "swc1 %[tmp2f], 4(%[u_ptr]) \n\t" - "swc1 %[tmp4f], 12(%[u_ptr]) \n\t" - "swc1 %[tmp6f], 20(%[u_ptr]) \n\t" - "swc1 %[tmp8f], 28(%[u_ptr]) \n\t" - "addiu %[u_ptr], %[u_ptr], 32 \n\t" - ".set pop \n\t" - : [u_ptr] "+r" (u_ptr), [noisePow] "+r" (noisePow), - [noise] "=&f" (noise), [noise2] "=&f" (noise2), - [noise3] "=&f" (noise3), [noise4] "=&f" (noise4), - [tmp1f] "=&f" (tmp1f), [tmp2f] "=&f" (tmp2f), - [tmp3f] "=&f" (tmp3f), [tmp4f] "=&f" (tmp4f), - [tmp5f] "=&f" (tmp5f), [tmp6f] "=&f" (tmp6f), - [tmp7f] "=&f" (tmp7f), [tmp8f] "=&f" (tmp8f) - : - : "memory" - ); - } - u[PART_LEN][1] = 0; - noisePow -= PART_LEN; - - u_ptr = &u[0][0]; - float* u_ptr_end = &u[PART_LEN][0]; - float* efw_ptr_0 = &efw[0][0]; - float* efw_ptr_1 = &efw[1][0]; - float tmp9f, tmp10f; - const float tmp1c = 1.0; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "1: \n\t" - "lwc1 %[tmp1f], 0(%[lambda]) \n\t" - "lwc1 %[tmp6f], 4(%[lambda]) \n\t" - "addiu %[lambda], %[lambda], 8 \n\t" - "c.lt.s %[tmp1f], %[tmp1c] \n\t" - "bc1f 4f \n\t" - " nop \n\t" - "c.lt.s %[tmp6f], %[tmp1c] \n\t" - "bc1f 3f \n\t" - " nop \n\t" - "2: \n\t" - "mul.s %[tmp1f], %[tmp1f], %[tmp1f] \n\t" - "mul.s %[tmp6f], %[tmp6f], %[tmp6f] \n\t" - "sub.s %[tmp1f], %[tmp1c], %[tmp1f] \n\t" - "sub.s %[tmp6f], %[tmp1c], %[tmp6f] \n\t" - "sqrt.s %[tmp1f], %[tmp1f] \n\t" - "sqrt.s %[tmp6f], %[tmp6f] \n\t" - "lwc1 %[tmp2f], 0(%[efw_ptr_0]) \n\t" - "lwc1 %[tmp3f], 0(%[u_ptr]) \n\t" - "lwc1 %[tmp7f], 4(%[efw_ptr_0]) \n\t" - "lwc1 %[tmp8f], 8(%[u_ptr]) \n\t" - "lwc1 %[tmp4f], 0(%[efw_ptr_1]) \n\t" - "lwc1 %[tmp5f], 4(%[u_ptr]) \n\t" - "lwc1 %[tmp9f], 4(%[efw_ptr_1]) \n\t" - "lwc1 %[tmp10f], 12(%[u_ptr]) \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[tmp3f], %[tmp1f], %[tmp3f] \n\t" - "add.s %[tmp2f], %[tmp2f], %[tmp3f] \n\t" - "mul.s %[tmp3f], %[tmp1f], %[tmp5f] \n\t" - "add.s %[tmp4f], %[tmp4f], %[tmp3f] \n\t" - "mul.s %[tmp3f], %[tmp6f], %[tmp8f] \n\t" - "add.s %[tmp7f], %[tmp7f], %[tmp3f] \n\t" - "mul.s %[tmp3f], %[tmp6f], %[tmp10f] \n\t" - "add.s %[tmp9f], %[tmp9f], %[tmp3f] \n\t" -#else // #if !defined(MIPS32_R2_LE) - "madd.s %[tmp2f], %[tmp2f], %[tmp1f], %[tmp3f] \n\t" - "madd.s %[tmp4f], %[tmp4f], %[tmp1f], %[tmp5f] \n\t" - "madd.s %[tmp7f], %[tmp7f], %[tmp6f], %[tmp8f] \n\t" - "madd.s %[tmp9f], %[tmp9f], %[tmp6f], %[tmp10f] \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "swc1 %[tmp2f], 0(%[efw_ptr_0]) \n\t" - "swc1 %[tmp4f], 0(%[efw_ptr_1]) \n\t" - "swc1 %[tmp7f], 4(%[efw_ptr_0]) \n\t" - "b 5f \n\t" - " swc1 %[tmp9f], 4(%[efw_ptr_1]) \n\t" - "3: \n\t" - "mul.s %[tmp1f], %[tmp1f], %[tmp1f] \n\t" - "sub.s %[tmp1f], %[tmp1c], %[tmp1f] \n\t" - "sqrt.s %[tmp1f], %[tmp1f] \n\t" - "lwc1 %[tmp2f], 0(%[efw_ptr_0]) \n\t" - "lwc1 %[tmp3f], 0(%[u_ptr]) \n\t" - "lwc1 %[tmp4f], 0(%[efw_ptr_1]) \n\t" - "lwc1 %[tmp5f], 4(%[u_ptr]) \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[tmp3f], %[tmp1f], %[tmp3f] \n\t" - "add.s %[tmp2f], %[tmp2f], %[tmp3f] \n\t" - "mul.s %[tmp3f], %[tmp1f], %[tmp5f] \n\t" - "add.s %[tmp4f], %[tmp4f], %[tmp3f] \n\t" -#else // #if !defined(MIPS32_R2_LE) - "madd.s %[tmp2f], %[tmp2f], %[tmp1f], %[tmp3f] \n\t" - "madd.s %[tmp4f], %[tmp4f], %[tmp1f], %[tmp5f] \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "swc1 %[tmp2f], 0(%[efw_ptr_0]) \n\t" - "b 5f \n\t" - " swc1 %[tmp4f], 0(%[efw_ptr_1]) \n\t" - "4: \n\t" - "c.lt.s %[tmp6f], %[tmp1c] \n\t" - "bc1f 5f \n\t" - " nop \n\t" - "mul.s %[tmp6f], %[tmp6f], %[tmp6f] \n\t" - "sub.s %[tmp6f], %[tmp1c], %[tmp6f] \n\t" - "sqrt.s %[tmp6f], %[tmp6f] \n\t" - "lwc1 %[tmp7f], 4(%[efw_ptr_0]) \n\t" - "lwc1 %[tmp8f], 8(%[u_ptr]) \n\t" - "lwc1 %[tmp9f], 4(%[efw_ptr_1]) \n\t" - "lwc1 %[tmp10f], 12(%[u_ptr]) \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[tmp3f], %[tmp6f], %[tmp8f] \n\t" - "add.s %[tmp7f], %[tmp7f], %[tmp3f] \n\t" - "mul.s %[tmp3f], %[tmp6f], %[tmp10f] \n\t" - "add.s %[tmp9f], %[tmp9f], %[tmp3f] \n\t" -#else // #if !defined(MIPS32_R2_LE) - "madd.s %[tmp7f], %[tmp7f], %[tmp6f], %[tmp8f] \n\t" - "madd.s %[tmp9f], %[tmp9f], %[tmp6f], %[tmp10f] \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "swc1 %[tmp7f], 4(%[efw_ptr_0]) \n\t" - "swc1 %[tmp9f], 4(%[efw_ptr_1]) \n\t" - "5: \n\t" - "addiu %[u_ptr], %[u_ptr], 16 \n\t" - "addiu %[efw_ptr_0], %[efw_ptr_0], 8 \n\t" - "bne %[u_ptr], %[u_ptr_end], 1b \n\t" - " addiu %[efw_ptr_1], %[efw_ptr_1], 8 \n\t" - ".set pop \n\t" - : [lambda] "+r" (lambda), [u_ptr] "+r" (u_ptr), - [efw_ptr_0] "+r" (efw_ptr_0), [efw_ptr_1] "+r" (efw_ptr_1), - [tmp1f] "=&f" (tmp1f), [tmp2f] "=&f" (tmp2f), [tmp3f] "=&f" (tmp3f), - [tmp4f] "=&f" (tmp4f), [tmp5f] "=&f" (tmp5f), - [tmp6f] "=&f" (tmp6f), [tmp7f] "=&f" (tmp7f), [tmp8f] "=&f" (tmp8f), - [tmp9f] "=&f" (tmp9f), [tmp10f] "=&f" (tmp10f) - : [tmp1c] "f" (tmp1c), [u_ptr_end] "r" (u_ptr_end) - : "memory" - ); - - lambda -= PART_LEN; - tmp = sqrtf(WEBRTC_SPL_MAX(1 - lambda[PART_LEN] * lambda[PART_LEN], 0)); - //tmp = 1 - lambda[i]; - efw[0][PART_LEN] += tmp * u[PART_LEN][0]; - efw[1][PART_LEN] += tmp * u[PART_LEN][1]; - - // For H band comfort noise - // TODO: don't compute noise and "tmp" twice. Use the previous results. - noiseAvg = 0.0; - tmpAvg = 0.0; - num = 0; - if ((aec->sampFreq == 32000 || aec->sampFreq == 48000) && flagHbandCn == 1) { - for (i = 0; i < PART_LEN; i++) { - rand[i] = ((float)randW16[i]) / 32768; - } - - // average noise scale - // average over second half of freq spectrum (i.e., 4->8khz) - // TODO: we shouldn't need num. We know how many elements we're summing. - for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { - num++; - noiseAvg += sqrtf(noisePow[i]); - } - noiseAvg /= (float)num; - - // average nlp scale - // average over second half of freq spectrum (i.e., 4->8khz) - // TODO: we shouldn't need num. We know how many elements we're summing. - num = 0; - for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { - num++; - tmpAvg += sqrtf(WEBRTC_SPL_MAX(1 - lambda[i] * lambda[i], 0)); - } - tmpAvg /= (float)num; - - // Use average noise for H band - // TODO: we should probably have a new random vector here. - // Reject LF noise - u[0][0] = 0; - u[0][1] = 0; - for (i = 1; i < PART_LEN1; i++) { - tmp = pi2 * rand[i - 1]; - - // Use average noise for H band - u[i][0] = noiseAvg * (float)cos(tmp); - u[i][1] = -noiseAvg * (float)sin(tmp); - } - u[PART_LEN][1] = 0; - - for (i = 0; i < PART_LEN1; i++) { - // Use average NLP weight for H band - comfortNoiseHband[i][0] = tmpAvg * u[i][0]; - comfortNoiseHband[i][1] = tmpAvg * u[i][1]; - } - } -} - -void WebRtcAec_FilterFar_mips(AecCore* aec, float yf[2][PART_LEN1]) { - int i; - for (i = 0; i < aec->num_partitions; i++) { - int xPos = (i + aec->xfBufBlockPos) * PART_LEN1; - int pos = i * PART_LEN1; - // Check for wrap - if (i + aec->xfBufBlockPos >= aec->num_partitions) { - xPos -= aec->num_partitions * (PART_LEN1); - } - float* yf0 = yf[0]; - float* yf1 = yf[1]; - float* aRe = aec->xfBuf[0] + xPos; - float* aIm = aec->xfBuf[1] + xPos; - float* bRe = aec->wfBuf[0] + pos; - float* bIm = aec->wfBuf[1] + pos; - float f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13; - int len = PART_LEN1 >> 1; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "1: \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[bRe]) \n\t" - "lwc1 %[f2], 0(%[bIm]) \n\t" - "lwc1 %[f3], 0(%[aIm]) \n\t" - "lwc1 %[f4], 4(%[aRe]) \n\t" - "lwc1 %[f5], 4(%[bRe]) \n\t" - "lwc1 %[f6], 4(%[bIm]) \n\t" - "mul.s %[f8], %[f0], %[f1] \n\t" - "mul.s %[f0], %[f0], %[f2] \n\t" - "mul.s %[f9], %[f4], %[f5] \n\t" - "mul.s %[f4], %[f4], %[f6] \n\t" - "lwc1 %[f7], 4(%[aIm]) \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f12], %[f2], %[f3] \n\t" - "mul.s %[f1], %[f3], %[f1] \n\t" - "mul.s %[f11], %[f6], %[f7] \n\t" - "addiu %[aRe], %[aRe], 8 \n\t" - "addiu %[aIm], %[aIm], 8 \n\t" - "addiu %[len], %[len], -1 \n\t" - "sub.s %[f8], %[f8], %[f12] \n\t" - "mul.s %[f12], %[f7], %[f5] \n\t" - "lwc1 %[f2], 0(%[yf0]) \n\t" - "add.s %[f1], %[f0], %[f1] \n\t" - "lwc1 %[f3], 0(%[yf1]) \n\t" - "sub.s %[f9], %[f9], %[f11] \n\t" - "lwc1 %[f6], 4(%[yf0]) \n\t" - "add.s %[f4], %[f4], %[f12] \n\t" -#else // #if !defined(MIPS32_R2_LE) - "addiu %[aRe], %[aRe], 8 \n\t" - "addiu %[aIm], %[aIm], 8 \n\t" - "addiu %[len], %[len], -1 \n\t" - "nmsub.s %[f8], %[f8], %[f2], %[f3] \n\t" - "lwc1 %[f2], 0(%[yf0]) \n\t" - "madd.s %[f1], %[f0], %[f3], %[f1] \n\t" - "lwc1 %[f3], 0(%[yf1]) \n\t" - "nmsub.s %[f9], %[f9], %[f6], %[f7] \n\t" - "lwc1 %[f6], 4(%[yf0]) \n\t" - "madd.s %[f4], %[f4], %[f7], %[f5] \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "lwc1 %[f5], 4(%[yf1]) \n\t" - "add.s %[f2], %[f2], %[f8] \n\t" - "addiu %[bRe], %[bRe], 8 \n\t" - "addiu %[bIm], %[bIm], 8 \n\t" - "add.s %[f3], %[f3], %[f1] \n\t" - "add.s %[f6], %[f6], %[f9] \n\t" - "add.s %[f5], %[f5], %[f4] \n\t" - "swc1 %[f2], 0(%[yf0]) \n\t" - "swc1 %[f3], 0(%[yf1]) \n\t" - "swc1 %[f6], 4(%[yf0]) \n\t" - "swc1 %[f5], 4(%[yf1]) \n\t" - "addiu %[yf0], %[yf0], 8 \n\t" - "bgtz %[len], 1b \n\t" - " addiu %[yf1], %[yf1], 8 \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[bRe]) \n\t" - "lwc1 %[f2], 0(%[bIm]) \n\t" - "lwc1 %[f3], 0(%[aIm]) \n\t" - "mul.s %[f8], %[f0], %[f1] \n\t" - "mul.s %[f0], %[f0], %[f2] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f12], %[f2], %[f3] \n\t" - "mul.s %[f1], %[f3], %[f1] \n\t" - "sub.s %[f8], %[f8], %[f12] \n\t" - "lwc1 %[f2], 0(%[yf0]) \n\t" - "add.s %[f1], %[f0], %[f1] \n\t" - "lwc1 %[f3], 0(%[yf1]) \n\t" -#else // #if !defined(MIPS32_R2_LE) - "nmsub.s %[f8], %[f8], %[f2], %[f3] \n\t" - "lwc1 %[f2], 0(%[yf0]) \n\t" - "madd.s %[f1], %[f0], %[f3], %[f1] \n\t" - "lwc1 %[f3], 0(%[yf1]) \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "add.s %[f2], %[f2], %[f8] \n\t" - "add.s %[f3], %[f3], %[f1] \n\t" - "swc1 %[f2], 0(%[yf0]) \n\t" - "swc1 %[f3], 0(%[yf1]) \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), - [f3] "=&f" (f3), [f4] "=&f" (f4), [f5] "=&f" (f5), - [f6] "=&f" (f6), [f7] "=&f" (f7), [f8] "=&f" (f8), - [f9] "=&f" (f9), [f10] "=&f" (f10), [f11] "=&f" (f11), - [f12] "=&f" (f12), [f13] "=&f" (f13), [aRe] "+r" (aRe), - [aIm] "+r" (aIm), [bRe] "+r" (bRe), [bIm] "+r" (bIm), - [yf0] "+r" (yf0), [yf1] "+r" (yf1), [len] "+r" (len) - : - : "memory" - ); - } -} - -void WebRtcAec_FilterAdaptation_mips(AecCore* aec, - float* fft, - float ef[2][PART_LEN1]) { - int i; - for (i = 0; i < aec->num_partitions; i++) { - int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1); - int pos; - // Check for wrap - if (i + aec->xfBufBlockPos >= aec->num_partitions) { - xPos -= aec->num_partitions * PART_LEN1; - } - - pos = i * PART_LEN1; - float* aRe = aec->xfBuf[0] + xPos; - float* aIm = aec->xfBuf[1] + xPos; - float* bRe = ef[0]; - float* bIm = ef[1]; - float* fft_tmp; - - float f0, f1, f2, f3, f4, f5, f6 ,f7, f8, f9, f10, f11, f12; - int len = PART_LEN >> 1; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[fft_tmp], %[fft], 0 \n\t" - "1: \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[bRe]) \n\t" - "lwc1 %[f2], 0(%[bIm]) \n\t" - "lwc1 %[f4], 4(%[aRe]) \n\t" - "lwc1 %[f5], 4(%[bRe]) \n\t" - "lwc1 %[f6], 4(%[bIm]) \n\t" - "addiu %[aRe], %[aRe], 8 \n\t" - "addiu %[bRe], %[bRe], 8 \n\t" - "mul.s %[f8], %[f0], %[f1] \n\t" - "mul.s %[f0], %[f0], %[f2] \n\t" - "lwc1 %[f3], 0(%[aIm]) \n\t" - "mul.s %[f9], %[f4], %[f5] \n\t" - "lwc1 %[f7], 4(%[aIm]) \n\t" - "mul.s %[f4], %[f4], %[f6] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f10], %[f3], %[f2] \n\t" - "mul.s %[f1], %[f3], %[f1] \n\t" - "mul.s %[f11], %[f7], %[f6] \n\t" - "mul.s %[f5], %[f7], %[f5] \n\t" - "addiu %[aIm], %[aIm], 8 \n\t" - "addiu %[bIm], %[bIm], 8 \n\t" - "addiu %[len], %[len], -1 \n\t" - "add.s %[f8], %[f8], %[f10] \n\t" - "sub.s %[f1], %[f0], %[f1] \n\t" - "add.s %[f9], %[f9], %[f11] \n\t" - "sub.s %[f5], %[f4], %[f5] \n\t" -#else // #if !defined(MIPS32_R2_LE) - "addiu %[aIm], %[aIm], 8 \n\t" - "addiu %[bIm], %[bIm], 8 \n\t" - "addiu %[len], %[len], -1 \n\t" - "madd.s %[f8], %[f8], %[f3], %[f2] \n\t" - "nmsub.s %[f1], %[f0], %[f3], %[f1] \n\t" - "madd.s %[f9], %[f9], %[f7], %[f6] \n\t" - "nmsub.s %[f5], %[f4], %[f7], %[f5] \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "swc1 %[f8], 0(%[fft_tmp]) \n\t" - "swc1 %[f1], 4(%[fft_tmp]) \n\t" - "swc1 %[f9], 8(%[fft_tmp]) \n\t" - "swc1 %[f5], 12(%[fft_tmp]) \n\t" - "bgtz %[len], 1b \n\t" - " addiu %[fft_tmp], %[fft_tmp], 16 \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[bRe]) \n\t" - "lwc1 %[f2], 0(%[bIm]) \n\t" - "lwc1 %[f3], 0(%[aIm]) \n\t" - "mul.s %[f8], %[f0], %[f1] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f10], %[f3], %[f2] \n\t" - "add.s %[f8], %[f8], %[f10] \n\t" -#else // #if !defined(MIPS32_R2_LE) - "madd.s %[f8], %[f8], %[f3], %[f2] \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "swc1 %[f8], 4(%[fft]) \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), - [f3] "=&f" (f3), [f4] "=&f" (f4), [f5] "=&f" (f5), - [f6] "=&f" (f6), [f7] "=&f" (f7), [f8] "=&f" (f8), - [f9] "=&f" (f9), [f10] "=&f" (f10), [f11] "=&f" (f11), - [f12] "=&f" (f12), [aRe] "+r" (aRe), [aIm] "+r" (aIm), - [bRe] "+r" (bRe), [bIm] "+r" (bIm), [fft_tmp] "=&r" (fft_tmp), - [len] "+r" (len) - : [fft] "r" (fft) - : "memory" - ); - - aec_rdft_inverse_128(fft); - memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN); - - // fft scaling - { - float scale = 2.0f / PART_LEN2; - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[fft_tmp], %[fft], 0 \n\t" - "addiu %[len], $zero, 8 \n\t" - "1: \n\t" - "addiu %[len], %[len], -1 \n\t" - "lwc1 %[f0], 0(%[fft_tmp]) \n\t" - "lwc1 %[f1], 4(%[fft_tmp]) \n\t" - "lwc1 %[f2], 8(%[fft_tmp]) \n\t" - "lwc1 %[f3], 12(%[fft_tmp]) \n\t" - "mul.s %[f0], %[f0], %[scale] \n\t" - "mul.s %[f1], %[f1], %[scale] \n\t" - "mul.s %[f2], %[f2], %[scale] \n\t" - "mul.s %[f3], %[f3], %[scale] \n\t" - "lwc1 %[f4], 16(%[fft_tmp]) \n\t" - "lwc1 %[f5], 20(%[fft_tmp]) \n\t" - "lwc1 %[f6], 24(%[fft_tmp]) \n\t" - "lwc1 %[f7], 28(%[fft_tmp]) \n\t" - "mul.s %[f4], %[f4], %[scale] \n\t" - "mul.s %[f5], %[f5], %[scale] \n\t" - "mul.s %[f6], %[f6], %[scale] \n\t" - "mul.s %[f7], %[f7], %[scale] \n\t" - "swc1 %[f0], 0(%[fft_tmp]) \n\t" - "swc1 %[f1], 4(%[fft_tmp]) \n\t" - "swc1 %[f2], 8(%[fft_tmp]) \n\t" - "swc1 %[f3], 12(%[fft_tmp]) \n\t" - "swc1 %[f4], 16(%[fft_tmp]) \n\t" - "swc1 %[f5], 20(%[fft_tmp]) \n\t" - "swc1 %[f6], 24(%[fft_tmp]) \n\t" - "swc1 %[f7], 28(%[fft_tmp]) \n\t" - "bgtz %[len], 1b \n\t" - " addiu %[fft_tmp], %[fft_tmp], 32 \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), - [f3] "=&f" (f3), [f4] "=&f" (f4), [f5] "=&f" (f5), - [f6] "=&f" (f6), [f7] "=&f" (f7), [len] "=&r" (len), - [fft_tmp] "=&r" (fft_tmp) - : [scale] "f" (scale), [fft] "r" (fft) - : "memory" - ); - } - aec_rdft_forward_128(fft); - aRe = aec->wfBuf[0] + pos; - aIm = aec->wfBuf[1] + pos; - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[fft_tmp], %[fft], 0 \n\t" - "addiu %[len], $zero, 31 \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[fft_tmp]) \n\t" - "lwc1 %[f2], 256(%[aRe]) \n\t" - "lwc1 %[f3], 4(%[fft_tmp]) \n\t" - "lwc1 %[f4], 4(%[aRe]) \n\t" - "lwc1 %[f5], 8(%[fft_tmp]) \n\t" - "lwc1 %[f6], 4(%[aIm]) \n\t" - "lwc1 %[f7], 12(%[fft_tmp]) \n\t" - "add.s %[f0], %[f0], %[f1] \n\t" - "add.s %[f2], %[f2], %[f3] \n\t" - "add.s %[f4], %[f4], %[f5] \n\t" - "add.s %[f6], %[f6], %[f7] \n\t" - "addiu %[fft_tmp], %[fft_tmp], 16 \n\t" - "swc1 %[f0], 0(%[aRe]) \n\t" - "swc1 %[f2], 256(%[aRe]) \n\t" - "swc1 %[f4], 4(%[aRe]) \n\t" - "addiu %[aRe], %[aRe], 8 \n\t" - "swc1 %[f6], 4(%[aIm]) \n\t" - "addiu %[aIm], %[aIm], 8 \n\t" - "1: \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[fft_tmp]) \n\t" - "lwc1 %[f2], 0(%[aIm]) \n\t" - "lwc1 %[f3], 4(%[fft_tmp]) \n\t" - "lwc1 %[f4], 4(%[aRe]) \n\t" - "lwc1 %[f5], 8(%[fft_tmp]) \n\t" - "lwc1 %[f6], 4(%[aIm]) \n\t" - "lwc1 %[f7], 12(%[fft_tmp]) \n\t" - "add.s %[f0], %[f0], %[f1] \n\t" - "add.s %[f2], %[f2], %[f3] \n\t" - "add.s %[f4], %[f4], %[f5] \n\t" - "add.s %[f6], %[f6], %[f7] \n\t" - "addiu %[len], %[len], -1 \n\t" - "addiu %[fft_tmp], %[fft_tmp], 16 \n\t" - "swc1 %[f0], 0(%[aRe]) \n\t" - "swc1 %[f2], 0(%[aIm]) \n\t" - "swc1 %[f4], 4(%[aRe]) \n\t" - "addiu %[aRe], %[aRe], 8 \n\t" - "swc1 %[f6], 4(%[aIm]) \n\t" - "bgtz %[len], 1b \n\t" - " addiu %[aIm], %[aIm], 8 \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), - [f3] "=&f" (f3), [f4] "=&f" (f4), [f5] "=&f" (f5), - [f6] "=&f" (f6), [f7] "=&f" (f7), [len] "=&r" (len), - [fft_tmp] "=&r" (fft_tmp), [aRe] "+r" (aRe), [aIm] "+r" (aIm) - : [fft] "r" (fft) - : "memory" - ); - } -} - -void WebRtcAec_OverdriveAndSuppress_mips(AecCore* aec, - float hNl[PART_LEN1], - const float hNlFb, - float efw[2][PART_LEN1]) { - int i; - const float one = 1.0; - float* p_hNl; - float* p_efw0; - float* p_efw1; - float* p_WebRtcAec_wC; - float temp1, temp2, temp3, temp4; - - p_hNl = &hNl[0]; - p_efw0 = &efw[0][0]; - p_efw1 = &efw[1][0]; - p_WebRtcAec_wC = (float*)&WebRtcAec_weightCurve[0]; - - for (i = 0; i < PART_LEN1; i++) { - // Weight subbands - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "lwc1 %[temp1], 0(%[p_hNl]) \n\t" - "lwc1 %[temp2], 0(%[p_wC]) \n\t" - "c.lt.s %[hNlFb], %[temp1] \n\t" - "bc1f 1f \n\t" - " mul.s %[temp3], %[temp2], %[hNlFb] \n\t" - "sub.s %[temp4], %[one], %[temp2] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[temp1], %[temp1], %[temp4] \n\t" - "add.s %[temp1], %[temp3], %[temp1] \n\t" -#else // #if !defined(MIPS32_R2_LE) - "madd.s %[temp1], %[temp3], %[temp1], %[temp4] \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "swc1 %[temp1], 0(%[p_hNl]) \n\t" - "1: \n\t" - "addiu %[p_wC], %[p_wC], 4 \n\t" - ".set pop \n\t" - : [temp1] "=&f" (temp1), [temp2] "=&f" (temp2), [temp3] "=&f" (temp3), - [temp4] "=&f" (temp4), [p_wC] "+r" (p_WebRtcAec_wC) - : [hNlFb] "f" (hNlFb), [one] "f" (one), [p_hNl] "r" (p_hNl) - : "memory" - ); - - hNl[i] = powf(hNl[i], aec->overDriveSm * WebRtcAec_overDriveCurve[i]); - - __asm __volatile ( - "lwc1 %[temp1], 0(%[p_hNl]) \n\t" - "lwc1 %[temp3], 0(%[p_efw1]) \n\t" - "lwc1 %[temp2], 0(%[p_efw0]) \n\t" - "addiu %[p_hNl], %[p_hNl], 4 \n\t" - "mul.s %[temp3], %[temp3], %[temp1] \n\t" - "mul.s %[temp2], %[temp2], %[temp1] \n\t" - "addiu %[p_efw0], %[p_efw0], 4 \n\t" - "addiu %[p_efw1], %[p_efw1], 4 \n\t" - "neg.s %[temp4], %[temp3] \n\t" - "swc1 %[temp2], -4(%[p_efw0]) \n\t" - "swc1 %[temp4], -4(%[p_efw1]) \n\t" - : [temp1] "=&f" (temp1), [temp2] "=&f" (temp2), [temp3] "=&f" (temp3), - [temp4] "=&f" (temp4), [p_efw0] "+r" (p_efw0), [p_efw1] "+r" (p_efw1), - [p_hNl] "+r" (p_hNl) - : - : "memory" - ); - } -} - -void WebRtcAec_ScaleErrorSignal_mips(AecCore* aec, float ef[2][PART_LEN1]) { - const float mu = aec->extended_filter_enabled ? kExtendedMu : aec->normal_mu; - const float error_threshold = aec->extended_filter_enabled - ? kExtendedErrorThreshold - : aec->normal_error_threshold; - int len = (PART_LEN1); - float* ef0 = ef[0]; - float* ef1 = ef[1]; - float* xPow = aec->xPow; - float fac1 = 1e-10f; - float err_th2 = error_threshold * error_threshold; - float f0, f1, f2; -#if !defined(MIPS32_R2_LE) - float f3; -#endif - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "1: \n\t" - "lwc1 %[f0], 0(%[xPow]) \n\t" - "lwc1 %[f1], 0(%[ef0]) \n\t" - "lwc1 %[f2], 0(%[ef1]) \n\t" - "add.s %[f0], %[f0], %[fac1] \n\t" - "div.s %[f1], %[f1], %[f0] \n\t" - "div.s %[f2], %[f2], %[f0] \n\t" - "mul.s %[f0], %[f1], %[f1] \n\t" -#if defined(MIPS32_R2_LE) - "madd.s %[f0], %[f0], %[f2], %[f2] \n\t" -#else - "mul.s %[f3], %[f2], %[f2] \n\t" - "add.s %[f0], %[f0], %[f3] \n\t" -#endif - "c.le.s %[f0], %[err_th2] \n\t" - "nop \n\t" - "bc1t 2f \n\t" - " nop \n\t" - "sqrt.s %[f0], %[f0] \n\t" - "add.s %[f0], %[f0], %[fac1] \n\t" - "div.s %[f0], %[err_th], %[f0] \n\t" - "mul.s %[f1], %[f1], %[f0] \n\t" - "mul.s %[f2], %[f2], %[f0] \n\t" - "2: \n\t" - "mul.s %[f1], %[f1], %[mu] \n\t" - "mul.s %[f2], %[f2], %[mu] \n\t" - "swc1 %[f1], 0(%[ef0]) \n\t" - "swc1 %[f2], 0(%[ef1]) \n\t" - "addiu %[len], %[len], -1 \n\t" - "addiu %[xPow], %[xPow], 4 \n\t" - "addiu %[ef0], %[ef0], 4 \n\t" - "bgtz %[len], 1b \n\t" - " addiu %[ef1], %[ef1], 4 \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), -#if !defined(MIPS32_R2_LE) - [f3] "=&f" (f3), -#endif - [xPow] "+r" (xPow), [ef0] "+r" (ef0), [ef1] "+r" (ef1), - [len] "+r" (len) - : [fac1] "f" (fac1), [err_th2] "f" (err_th2), [mu] "f" (mu), - [err_th] "f" (error_threshold) - : "memory" - ); -} - -void WebRtcAec_InitAec_mips(void) { - WebRtcAec_FilterFar = WebRtcAec_FilterFar_mips; - WebRtcAec_FilterAdaptation = WebRtcAec_FilterAdaptation_mips; - WebRtcAec_ScaleErrorSignal = WebRtcAec_ScaleErrorSignal_mips; - WebRtcAec_ComfortNoise = WebRtcAec_ComfortNoise_mips; - WebRtcAec_OverdriveAndSuppress = WebRtcAec_OverdriveAndSuppress_mips; -} - diff --git a/webrtc/modules/audio_processing/aec/aec_core_neon.c b/webrtc/modules/audio_processing/aec/aec_core_neon.c deleted file mode 100644 index 9a677aa..0000000 --- a/webrtc/modules/audio_processing/aec/aec_core_neon.c +++ /dev/null @@ -1,736 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -/* - * The core AEC algorithm, neon version of speed-critical functions. - * - * Based on aec_core_sse2.c. - */ - -#include -#include -#include // memset - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/aec/aec_common.h" -#include "webrtc/modules/audio_processing/aec/aec_core_internal.h" -#include "webrtc/modules/audio_processing/aec/aec_rdft.h" - -enum { kShiftExponentIntoTopMantissa = 8 }; -enum { kFloatExponentShift = 23 }; - -__inline static float MulRe(float aRe, float aIm, float bRe, float bIm) { - return aRe * bRe - aIm * bIm; -} - -__inline static float MulIm(float aRe, float aIm, float bRe, float bIm) { - return aRe * bIm + aIm * bRe; -} - -static void FilterFarNEON(AecCore* aec, float yf[2][PART_LEN1]) { - int i; - const int num_partitions = aec->num_partitions; - for (i = 0; i < num_partitions; i++) { - int j; - int xPos = (i + aec->xfBufBlockPos) * PART_LEN1; - int pos = i * PART_LEN1; - // Check for wrap - if (i + aec->xfBufBlockPos >= num_partitions) { - xPos -= num_partitions * PART_LEN1; - } - - // vectorized code (four at once) - for (j = 0; j + 3 < PART_LEN1; j += 4) { - const float32x4_t xfBuf_re = vld1q_f32(&aec->xfBuf[0][xPos + j]); - const float32x4_t xfBuf_im = vld1q_f32(&aec->xfBuf[1][xPos + j]); - const float32x4_t wfBuf_re = vld1q_f32(&aec->wfBuf[0][pos + j]); - const float32x4_t wfBuf_im = vld1q_f32(&aec->wfBuf[1][pos + j]); - const float32x4_t yf_re = vld1q_f32(&yf[0][j]); - const float32x4_t yf_im = vld1q_f32(&yf[1][j]); - const float32x4_t a = vmulq_f32(xfBuf_re, wfBuf_re); - const float32x4_t e = vmlsq_f32(a, xfBuf_im, wfBuf_im); - const float32x4_t c = vmulq_f32(xfBuf_re, wfBuf_im); - const float32x4_t f = vmlaq_f32(c, xfBuf_im, wfBuf_re); - const float32x4_t g = vaddq_f32(yf_re, e); - const float32x4_t h = vaddq_f32(yf_im, f); - vst1q_f32(&yf[0][j], g); - vst1q_f32(&yf[1][j], h); - } - // scalar code for the remaining items. - for (; j < PART_LEN1; j++) { - yf[0][j] += MulRe(aec->xfBuf[0][xPos + j], - aec->xfBuf[1][xPos + j], - aec->wfBuf[0][pos + j], - aec->wfBuf[1][pos + j]); - yf[1][j] += MulIm(aec->xfBuf[0][xPos + j], - aec->xfBuf[1][xPos + j], - aec->wfBuf[0][pos + j], - aec->wfBuf[1][pos + j]); - } - } -} - -// ARM64's arm_neon.h has already defined vdivq_f32 vsqrtq_f32. -#if !defined (WEBRTC_ARCH_ARM64) -static float32x4_t vdivq_f32(float32x4_t a, float32x4_t b) { - int i; - float32x4_t x = vrecpeq_f32(b); - // from arm documentation - // The Newton-Raphson iteration: - // x[n+1] = x[n] * (2 - d * x[n]) - // converges to (1/d) if x0 is the result of VRECPE applied to d. - // - // Note: The precision did not improve after 2 iterations. - for (i = 0; i < 2; i++) { - x = vmulq_f32(vrecpsq_f32(b, x), x); - } - // a/b = a*(1/b) - return vmulq_f32(a, x); -} - -static float32x4_t vsqrtq_f32(float32x4_t s) { - int i; - float32x4_t x = vrsqrteq_f32(s); - - // Code to handle sqrt(0). - // If the input to sqrtf() is zero, a zero will be returned. - // If the input to vrsqrteq_f32() is zero, positive infinity is returned. - const uint32x4_t vec_p_inf = vdupq_n_u32(0x7F800000); - // check for divide by zero - const uint32x4_t div_by_zero = vceqq_u32(vec_p_inf, vreinterpretq_u32_f32(x)); - // zero out the positive infinity results - x = vreinterpretq_f32_u32(vandq_u32(vmvnq_u32(div_by_zero), - vreinterpretq_u32_f32(x))); - // from arm documentation - // The Newton-Raphson iteration: - // x[n+1] = x[n] * (3 - d * (x[n] * x[n])) / 2) - // converges to (1/√d) if x0 is the result of VRSQRTE applied to d. - // - // Note: The precision did not improve after 2 iterations. - for (i = 0; i < 2; i++) { - x = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x, x), s), x); - } - // sqrt(s) = s * 1/sqrt(s) - return vmulq_f32(s, x);; -} -#endif // WEBRTC_ARCH_ARM64 - -static void ScaleErrorSignalNEON(AecCore* aec, float ef[2][PART_LEN1]) { - const float mu = aec->extended_filter_enabled ? kExtendedMu : aec->normal_mu; - const float error_threshold = aec->extended_filter_enabled ? - kExtendedErrorThreshold : aec->normal_error_threshold; - const float32x4_t k1e_10f = vdupq_n_f32(1e-10f); - const float32x4_t kMu = vmovq_n_f32(mu); - const float32x4_t kThresh = vmovq_n_f32(error_threshold); - int i; - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const float32x4_t xPow = vld1q_f32(&aec->xPow[i]); - const float32x4_t ef_re_base = vld1q_f32(&ef[0][i]); - const float32x4_t ef_im_base = vld1q_f32(&ef[1][i]); - const float32x4_t xPowPlus = vaddq_f32(xPow, k1e_10f); - float32x4_t ef_re = vdivq_f32(ef_re_base, xPowPlus); - float32x4_t ef_im = vdivq_f32(ef_im_base, xPowPlus); - const float32x4_t ef_re2 = vmulq_f32(ef_re, ef_re); - const float32x4_t ef_sum2 = vmlaq_f32(ef_re2, ef_im, ef_im); - const float32x4_t absEf = vsqrtq_f32(ef_sum2); - const uint32x4_t bigger = vcgtq_f32(absEf, kThresh); - const float32x4_t absEfPlus = vaddq_f32(absEf, k1e_10f); - const float32x4_t absEfInv = vdivq_f32(kThresh, absEfPlus); - uint32x4_t ef_re_if = vreinterpretq_u32_f32(vmulq_f32(ef_re, absEfInv)); - uint32x4_t ef_im_if = vreinterpretq_u32_f32(vmulq_f32(ef_im, absEfInv)); - uint32x4_t ef_re_u32 = vandq_u32(vmvnq_u32(bigger), - vreinterpretq_u32_f32(ef_re)); - uint32x4_t ef_im_u32 = vandq_u32(vmvnq_u32(bigger), - vreinterpretq_u32_f32(ef_im)); - ef_re_if = vandq_u32(bigger, ef_re_if); - ef_im_if = vandq_u32(bigger, ef_im_if); - ef_re_u32 = vorrq_u32(ef_re_u32, ef_re_if); - ef_im_u32 = vorrq_u32(ef_im_u32, ef_im_if); - ef_re = vmulq_f32(vreinterpretq_f32_u32(ef_re_u32), kMu); - ef_im = vmulq_f32(vreinterpretq_f32_u32(ef_im_u32), kMu); - vst1q_f32(&ef[0][i], ef_re); - vst1q_f32(&ef[1][i], ef_im); - } - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - float abs_ef; - ef[0][i] /= (aec->xPow[i] + 1e-10f); - ef[1][i] /= (aec->xPow[i] + 1e-10f); - abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]); - - if (abs_ef > error_threshold) { - abs_ef = error_threshold / (abs_ef + 1e-10f); - ef[0][i] *= abs_ef; - ef[1][i] *= abs_ef; - } - - // Stepsize factor - ef[0][i] *= mu; - ef[1][i] *= mu; - } -} - -static void FilterAdaptationNEON(AecCore* aec, - float* fft, - float ef[2][PART_LEN1]) { - int i; - const int num_partitions = aec->num_partitions; - for (i = 0; i < num_partitions; i++) { - int xPos = (i + aec->xfBufBlockPos) * PART_LEN1; - int pos = i * PART_LEN1; - int j; - // Check for wrap - if (i + aec->xfBufBlockPos >= num_partitions) { - xPos -= num_partitions * PART_LEN1; - } - - // Process the whole array... - for (j = 0; j < PART_LEN; j += 4) { - // Load xfBuf and ef. - const float32x4_t xfBuf_re = vld1q_f32(&aec->xfBuf[0][xPos + j]); - const float32x4_t xfBuf_im = vld1q_f32(&aec->xfBuf[1][xPos + j]); - const float32x4_t ef_re = vld1q_f32(&ef[0][j]); - const float32x4_t ef_im = vld1q_f32(&ef[1][j]); - // Calculate the product of conjugate(xfBuf) by ef. - // re(conjugate(a) * b) = aRe * bRe + aIm * bIm - // im(conjugate(a) * b)= aRe * bIm - aIm * bRe - const float32x4_t a = vmulq_f32(xfBuf_re, ef_re); - const float32x4_t e = vmlaq_f32(a, xfBuf_im, ef_im); - const float32x4_t c = vmulq_f32(xfBuf_re, ef_im); - const float32x4_t f = vmlsq_f32(c, xfBuf_im, ef_re); - // Interleave real and imaginary parts. - const float32x4x2_t g_n_h = vzipq_f32(e, f); - // Store - vst1q_f32(&fft[2 * j + 0], g_n_h.val[0]); - vst1q_f32(&fft[2 * j + 4], g_n_h.val[1]); - } - // ... and fixup the first imaginary entry. - fft[1] = MulRe(aec->xfBuf[0][xPos + PART_LEN], - -aec->xfBuf[1][xPos + PART_LEN], - ef[0][PART_LEN], - ef[1][PART_LEN]); - - aec_rdft_inverse_128(fft); - memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN); - - // fft scaling - { - const float scale = 2.0f / PART_LEN2; - const float32x4_t scale_ps = vmovq_n_f32(scale); - for (j = 0; j < PART_LEN; j += 4) { - const float32x4_t fft_ps = vld1q_f32(&fft[j]); - const float32x4_t fft_scale = vmulq_f32(fft_ps, scale_ps); - vst1q_f32(&fft[j], fft_scale); - } - } - aec_rdft_forward_128(fft); - - { - const float wt1 = aec->wfBuf[1][pos]; - aec->wfBuf[0][pos + PART_LEN] += fft[1]; - for (j = 0; j < PART_LEN; j += 4) { - float32x4_t wtBuf_re = vld1q_f32(&aec->wfBuf[0][pos + j]); - float32x4_t wtBuf_im = vld1q_f32(&aec->wfBuf[1][pos + j]); - const float32x4_t fft0 = vld1q_f32(&fft[2 * j + 0]); - const float32x4_t fft4 = vld1q_f32(&fft[2 * j + 4]); - const float32x4x2_t fft_re_im = vuzpq_f32(fft0, fft4); - wtBuf_re = vaddq_f32(wtBuf_re, fft_re_im.val[0]); - wtBuf_im = vaddq_f32(wtBuf_im, fft_re_im.val[1]); - - vst1q_f32(&aec->wfBuf[0][pos + j], wtBuf_re); - vst1q_f32(&aec->wfBuf[1][pos + j], wtBuf_im); - } - aec->wfBuf[1][pos] = wt1; - } - } -} - -static float32x4_t vpowq_f32(float32x4_t a, float32x4_t b) { - // a^b = exp2(b * log2(a)) - // exp2(x) and log2(x) are calculated using polynomial approximations. - float32x4_t log2_a, b_log2_a, a_exp_b; - - // Calculate log2(x), x = a. - { - // To calculate log2(x), we decompose x like this: - // x = y * 2^n - // n is an integer - // y is in the [1.0, 2.0) range - // - // log2(x) = log2(y) + n - // n can be evaluated by playing with float representation. - // log2(y) in a small range can be approximated, this code uses an order - // five polynomial approximation. The coefficients have been - // estimated with the Remez algorithm and the resulting - // polynomial has a maximum relative error of 0.00086%. - - // Compute n. - // This is done by masking the exponent, shifting it into the top bit of - // the mantissa, putting eight into the biased exponent (to shift/ - // compensate the fact that the exponent has been shifted in the top/ - // fractional part and finally getting rid of the implicit leading one - // from the mantissa by substracting it out. - const uint32x4_t vec_float_exponent_mask = vdupq_n_u32(0x7F800000); - const uint32x4_t vec_eight_biased_exponent = vdupq_n_u32(0x43800000); - const uint32x4_t vec_implicit_leading_one = vdupq_n_u32(0x43BF8000); - const uint32x4_t two_n = vandq_u32(vreinterpretq_u32_f32(a), - vec_float_exponent_mask); - const uint32x4_t n_1 = vshrq_n_u32(two_n, kShiftExponentIntoTopMantissa); - const uint32x4_t n_0 = vorrq_u32(n_1, vec_eight_biased_exponent); - const float32x4_t n = - vsubq_f32(vreinterpretq_f32_u32(n_0), - vreinterpretq_f32_u32(vec_implicit_leading_one)); - // Compute y. - const uint32x4_t vec_mantissa_mask = vdupq_n_u32(0x007FFFFF); - const uint32x4_t vec_zero_biased_exponent_is_one = vdupq_n_u32(0x3F800000); - const uint32x4_t mantissa = vandq_u32(vreinterpretq_u32_f32(a), - vec_mantissa_mask); - const float32x4_t y = - vreinterpretq_f32_u32(vorrq_u32(mantissa, - vec_zero_biased_exponent_is_one)); - // Approximate log2(y) ~= (y - 1) * pol5(y). - // pol5(y) = C5 * y^5 + C4 * y^4 + C3 * y^3 + C2 * y^2 + C1 * y + C0 - const float32x4_t C5 = vdupq_n_f32(-3.4436006e-2f); - const float32x4_t C4 = vdupq_n_f32(3.1821337e-1f); - const float32x4_t C3 = vdupq_n_f32(-1.2315303f); - const float32x4_t C2 = vdupq_n_f32(2.5988452f); - const float32x4_t C1 = vdupq_n_f32(-3.3241990f); - const float32x4_t C0 = vdupq_n_f32(3.1157899f); - float32x4_t pol5_y = C5; - pol5_y = vmlaq_f32(C4, y, pol5_y); - pol5_y = vmlaq_f32(C3, y, pol5_y); - pol5_y = vmlaq_f32(C2, y, pol5_y); - pol5_y = vmlaq_f32(C1, y, pol5_y); - pol5_y = vmlaq_f32(C0, y, pol5_y); - const float32x4_t y_minus_one = - vsubq_f32(y, vreinterpretq_f32_u32(vec_zero_biased_exponent_is_one)); - const float32x4_t log2_y = vmulq_f32(y_minus_one, pol5_y); - - // Combine parts. - log2_a = vaddq_f32(n, log2_y); - } - - // b * log2(a) - b_log2_a = vmulq_f32(b, log2_a); - - // Calculate exp2(x), x = b * log2(a). - { - // To calculate 2^x, we decompose x like this: - // x = n + y - // n is an integer, the value of x - 0.5 rounded down, therefore - // y is in the [0.5, 1.5) range - // - // 2^x = 2^n * 2^y - // 2^n can be evaluated by playing with float representation. - // 2^y in a small range can be approximated, this code uses an order two - // polynomial approximation. The coefficients have been estimated - // with the Remez algorithm and the resulting polynomial has a - // maximum relative error of 0.17%. - // To avoid over/underflow, we reduce the range of input to ]-127, 129]. - const float32x4_t max_input = vdupq_n_f32(129.f); - const float32x4_t min_input = vdupq_n_f32(-126.99999f); - const float32x4_t x_min = vminq_f32(b_log2_a, max_input); - const float32x4_t x_max = vmaxq_f32(x_min, min_input); - // Compute n. - const float32x4_t half = vdupq_n_f32(0.5f); - const float32x4_t x_minus_half = vsubq_f32(x_max, half); - const int32x4_t x_minus_half_floor = vcvtq_s32_f32(x_minus_half); - - // Compute 2^n. - const int32x4_t float_exponent_bias = vdupq_n_s32(127); - const int32x4_t two_n_exponent = - vaddq_s32(x_minus_half_floor, float_exponent_bias); - const float32x4_t two_n = - vreinterpretq_f32_s32(vshlq_n_s32(two_n_exponent, kFloatExponentShift)); - // Compute y. - const float32x4_t y = vsubq_f32(x_max, vcvtq_f32_s32(x_minus_half_floor)); - - // Approximate 2^y ~= C2 * y^2 + C1 * y + C0. - const float32x4_t C2 = vdupq_n_f32(3.3718944e-1f); - const float32x4_t C1 = vdupq_n_f32(6.5763628e-1f); - const float32x4_t C0 = vdupq_n_f32(1.0017247f); - float32x4_t exp2_y = C2; - exp2_y = vmlaq_f32(C1, y, exp2_y); - exp2_y = vmlaq_f32(C0, y, exp2_y); - - // Combine parts. - a_exp_b = vmulq_f32(exp2_y, two_n); - } - - return a_exp_b; -} - -static void OverdriveAndSuppressNEON(AecCore* aec, - float hNl[PART_LEN1], - const float hNlFb, - float efw[2][PART_LEN1]) { - int i; - const float32x4_t vec_hNlFb = vmovq_n_f32(hNlFb); - const float32x4_t vec_one = vdupq_n_f32(1.0f); - const float32x4_t vec_minus_one = vdupq_n_f32(-1.0f); - const float32x4_t vec_overDriveSm = vmovq_n_f32(aec->overDriveSm); - - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i += 4) { - // Weight subbands - float32x4_t vec_hNl = vld1q_f32(&hNl[i]); - const float32x4_t vec_weightCurve = vld1q_f32(&WebRtcAec_weightCurve[i]); - const uint32x4_t bigger = vcgtq_f32(vec_hNl, vec_hNlFb); - const float32x4_t vec_weightCurve_hNlFb = vmulq_f32(vec_weightCurve, - vec_hNlFb); - const float32x4_t vec_one_weightCurve = vsubq_f32(vec_one, vec_weightCurve); - const float32x4_t vec_one_weightCurve_hNl = vmulq_f32(vec_one_weightCurve, - vec_hNl); - const uint32x4_t vec_if0 = vandq_u32(vmvnq_u32(bigger), - vreinterpretq_u32_f32(vec_hNl)); - const float32x4_t vec_one_weightCurve_add = - vaddq_f32(vec_weightCurve_hNlFb, vec_one_weightCurve_hNl); - const uint32x4_t vec_if1 = - vandq_u32(bigger, vreinterpretq_u32_f32(vec_one_weightCurve_add)); - - vec_hNl = vreinterpretq_f32_u32(vorrq_u32(vec_if0, vec_if1)); - - { - const float32x4_t vec_overDriveCurve = - vld1q_f32(&WebRtcAec_overDriveCurve[i]); - const float32x4_t vec_overDriveSm_overDriveCurve = - vmulq_f32(vec_overDriveSm, vec_overDriveCurve); - vec_hNl = vpowq_f32(vec_hNl, vec_overDriveSm_overDriveCurve); - vst1q_f32(&hNl[i], vec_hNl); - } - - // Suppress error signal - { - float32x4_t vec_efw_re = vld1q_f32(&efw[0][i]); - float32x4_t vec_efw_im = vld1q_f32(&efw[1][i]); - vec_efw_re = vmulq_f32(vec_efw_re, vec_hNl); - vec_efw_im = vmulq_f32(vec_efw_im, vec_hNl); - - // Ooura fft returns incorrect sign on imaginary component. It matters - // here because we are making an additive change with comfort noise. - vec_efw_im = vmulq_f32(vec_efw_im, vec_minus_one); - vst1q_f32(&efw[0][i], vec_efw_re); - vst1q_f32(&efw[1][i], vec_efw_im); - } - } - - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - // Weight subbands - if (hNl[i] > hNlFb) { - hNl[i] = WebRtcAec_weightCurve[i] * hNlFb + - (1 - WebRtcAec_weightCurve[i]) * hNl[i]; - } - - hNl[i] = powf(hNl[i], aec->overDriveSm * WebRtcAec_overDriveCurve[i]); - - // Suppress error signal - efw[0][i] *= hNl[i]; - efw[1][i] *= hNl[i]; - - // Ooura fft returns incorrect sign on imaginary component. It matters - // here because we are making an additive change with comfort noise. - efw[1][i] *= -1; - } -} - -static int PartitionDelay(const AecCore* aec) { - // Measures the energy in each filter partition and returns the partition with - // highest energy. - // TODO(bjornv): Spread computational cost by computing one partition per - // block? - float wfEnMax = 0; - int i; - int delay = 0; - - for (i = 0; i < aec->num_partitions; i++) { - int j; - int pos = i * PART_LEN1; - float wfEn = 0; - float32x4_t vec_wfEn = vdupq_n_f32(0.0f); - // vectorized code (four at once) - for (j = 0; j + 3 < PART_LEN1; j += 4) { - const float32x4_t vec_wfBuf0 = vld1q_f32(&aec->wfBuf[0][pos + j]); - const float32x4_t vec_wfBuf1 = vld1q_f32(&aec->wfBuf[1][pos + j]); - vec_wfEn = vmlaq_f32(vec_wfEn, vec_wfBuf0, vec_wfBuf0); - vec_wfEn = vmlaq_f32(vec_wfEn, vec_wfBuf1, vec_wfBuf1); - } - { - float32x2_t vec_total; - // A B C D - vec_total = vpadd_f32(vget_low_f32(vec_wfEn), vget_high_f32(vec_wfEn)); - // A+B C+D - vec_total = vpadd_f32(vec_total, vec_total); - // A+B+C+D A+B+C+D - wfEn = vget_lane_f32(vec_total, 0); - } - - // scalar code for the remaining items. - for (; j < PART_LEN1; j++) { - wfEn += aec->wfBuf[0][pos + j] * aec->wfBuf[0][pos + j] + - aec->wfBuf[1][pos + j] * aec->wfBuf[1][pos + j]; - } - - if (wfEn > wfEnMax) { - wfEnMax = wfEn; - delay = i; - } - } - return delay; -} - -// Updates the following smoothed Power Spectral Densities (PSD): -// - sd : near-end -// - se : residual echo -// - sx : far-end -// - sde : cross-PSD of near-end and residual echo -// - sxd : cross-PSD of near-end and far-end -// -// In addition to updating the PSDs, also the filter diverge state is determined -// upon actions are taken. -static void SmoothedPSD(AecCore* aec, - float efw[2][PART_LEN1], - float dfw[2][PART_LEN1], - float xfw[2][PART_LEN1]) { - // Power estimate smoothing coefficients. - const float* ptrGCoh = aec->extended_filter_enabled - ? WebRtcAec_kExtendedSmoothingCoefficients[aec->mult - 1] - : WebRtcAec_kNormalSmoothingCoefficients[aec->mult - 1]; - int i; - float sdSum = 0, seSum = 0; - const float32x4_t vec_15 = vdupq_n_f32(WebRtcAec_kMinFarendPSD); - float32x4_t vec_sdSum = vdupq_n_f32(0.0f); - float32x4_t vec_seSum = vdupq_n_f32(0.0f); - - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const float32x4_t vec_dfw0 = vld1q_f32(&dfw[0][i]); - const float32x4_t vec_dfw1 = vld1q_f32(&dfw[1][i]); - const float32x4_t vec_efw0 = vld1q_f32(&efw[0][i]); - const float32x4_t vec_efw1 = vld1q_f32(&efw[1][i]); - const float32x4_t vec_xfw0 = vld1q_f32(&xfw[0][i]); - const float32x4_t vec_xfw1 = vld1q_f32(&xfw[1][i]); - float32x4_t vec_sd = vmulq_n_f32(vld1q_f32(&aec->sd[i]), ptrGCoh[0]); - float32x4_t vec_se = vmulq_n_f32(vld1q_f32(&aec->se[i]), ptrGCoh[0]); - float32x4_t vec_sx = vmulq_n_f32(vld1q_f32(&aec->sx[i]), ptrGCoh[0]); - float32x4_t vec_dfw_sumsq = vmulq_f32(vec_dfw0, vec_dfw0); - float32x4_t vec_efw_sumsq = vmulq_f32(vec_efw0, vec_efw0); - float32x4_t vec_xfw_sumsq = vmulq_f32(vec_xfw0, vec_xfw0); - - vec_dfw_sumsq = vmlaq_f32(vec_dfw_sumsq, vec_dfw1, vec_dfw1); - vec_efw_sumsq = vmlaq_f32(vec_efw_sumsq, vec_efw1, vec_efw1); - vec_xfw_sumsq = vmlaq_f32(vec_xfw_sumsq, vec_xfw1, vec_xfw1); - vec_xfw_sumsq = vmaxq_f32(vec_xfw_sumsq, vec_15); - vec_sd = vmlaq_n_f32(vec_sd, vec_dfw_sumsq, ptrGCoh[1]); - vec_se = vmlaq_n_f32(vec_se, vec_efw_sumsq, ptrGCoh[1]); - vec_sx = vmlaq_n_f32(vec_sx, vec_xfw_sumsq, ptrGCoh[1]); - - vst1q_f32(&aec->sd[i], vec_sd); - vst1q_f32(&aec->se[i], vec_se); - vst1q_f32(&aec->sx[i], vec_sx); - - { - float32x4x2_t vec_sde = vld2q_f32(&aec->sde[i][0]); - float32x4_t vec_dfwefw0011 = vmulq_f32(vec_dfw0, vec_efw0); - float32x4_t vec_dfwefw0110 = vmulq_f32(vec_dfw0, vec_efw1); - vec_sde.val[0] = vmulq_n_f32(vec_sde.val[0], ptrGCoh[0]); - vec_sde.val[1] = vmulq_n_f32(vec_sde.val[1], ptrGCoh[0]); - vec_dfwefw0011 = vmlaq_f32(vec_dfwefw0011, vec_dfw1, vec_efw1); - vec_dfwefw0110 = vmlsq_f32(vec_dfwefw0110, vec_dfw1, vec_efw0); - vec_sde.val[0] = vmlaq_n_f32(vec_sde.val[0], vec_dfwefw0011, ptrGCoh[1]); - vec_sde.val[1] = vmlaq_n_f32(vec_sde.val[1], vec_dfwefw0110, ptrGCoh[1]); - vst2q_f32(&aec->sde[i][0], vec_sde); - } - - { - float32x4x2_t vec_sxd = vld2q_f32(&aec->sxd[i][0]); - float32x4_t vec_dfwxfw0011 = vmulq_f32(vec_dfw0, vec_xfw0); - float32x4_t vec_dfwxfw0110 = vmulq_f32(vec_dfw0, vec_xfw1); - vec_sxd.val[0] = vmulq_n_f32(vec_sxd.val[0], ptrGCoh[0]); - vec_sxd.val[1] = vmulq_n_f32(vec_sxd.val[1], ptrGCoh[0]); - vec_dfwxfw0011 = vmlaq_f32(vec_dfwxfw0011, vec_dfw1, vec_xfw1); - vec_dfwxfw0110 = vmlsq_f32(vec_dfwxfw0110, vec_dfw1, vec_xfw0); - vec_sxd.val[0] = vmlaq_n_f32(vec_sxd.val[0], vec_dfwxfw0011, ptrGCoh[1]); - vec_sxd.val[1] = vmlaq_n_f32(vec_sxd.val[1], vec_dfwxfw0110, ptrGCoh[1]); - vst2q_f32(&aec->sxd[i][0], vec_sxd); - } - - vec_sdSum = vaddq_f32(vec_sdSum, vec_sd); - vec_seSum = vaddq_f32(vec_seSum, vec_se); - } - { - float32x2_t vec_sdSum_total; - float32x2_t vec_seSum_total; - // A B C D - vec_sdSum_total = vpadd_f32(vget_low_f32(vec_sdSum), - vget_high_f32(vec_sdSum)); - vec_seSum_total = vpadd_f32(vget_low_f32(vec_seSum), - vget_high_f32(vec_seSum)); - // A+B C+D - vec_sdSum_total = vpadd_f32(vec_sdSum_total, vec_sdSum_total); - vec_seSum_total = vpadd_f32(vec_seSum_total, vec_seSum_total); - // A+B+C+D A+B+C+D - sdSum = vget_lane_f32(vec_sdSum_total, 0); - seSum = vget_lane_f32(vec_seSum_total, 0); - } - - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - aec->sd[i] = ptrGCoh[0] * aec->sd[i] + - ptrGCoh[1] * (dfw[0][i] * dfw[0][i] + dfw[1][i] * dfw[1][i]); - aec->se[i] = ptrGCoh[0] * aec->se[i] + - ptrGCoh[1] * (efw[0][i] * efw[0][i] + efw[1][i] * efw[1][i]); - // We threshold here to protect against the ill-effects of a zero farend. - // The threshold is not arbitrarily chosen, but balances protection and - // adverse interaction with the algorithm's tuning. - // TODO(bjornv): investigate further why this is so sensitive. - aec->sx[i] = - ptrGCoh[0] * aec->sx[i] + - ptrGCoh[1] * WEBRTC_SPL_MAX( - xfw[0][i] * xfw[0][i] + xfw[1][i] * xfw[1][i], - WebRtcAec_kMinFarendPSD); - - aec->sde[i][0] = - ptrGCoh[0] * aec->sde[i][0] + - ptrGCoh[1] * (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); - aec->sde[i][1] = - ptrGCoh[0] * aec->sde[i][1] + - ptrGCoh[1] * (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); - - aec->sxd[i][0] = - ptrGCoh[0] * aec->sxd[i][0] + - ptrGCoh[1] * (dfw[0][i] * xfw[0][i] + dfw[1][i] * xfw[1][i]); - aec->sxd[i][1] = - ptrGCoh[0] * aec->sxd[i][1] + - ptrGCoh[1] * (dfw[0][i] * xfw[1][i] - dfw[1][i] * xfw[0][i]); - - sdSum += aec->sd[i]; - seSum += aec->se[i]; - } - - // Divergent filter safeguard. - aec->divergeState = (aec->divergeState ? 1.05f : 1.0f) * seSum > sdSum; - - if (aec->divergeState) - memcpy(efw, dfw, sizeof(efw[0][0]) * 2 * PART_LEN1); - - // Reset if error is significantly larger than nearend (13 dB). - if (!aec->extended_filter_enabled && seSum > (19.95f * sdSum)) - memset(aec->wfBuf, 0, sizeof(aec->wfBuf)); -} - -// Window time domain data to be used by the fft. -__inline static void WindowData(float* x_windowed, const float* x) { - int i; - for (i = 0; i < PART_LEN; i += 4) { - const float32x4_t vec_Buf1 = vld1q_f32(&x[i]); - const float32x4_t vec_Buf2 = vld1q_f32(&x[PART_LEN + i]); - const float32x4_t vec_sqrtHanning = vld1q_f32(&WebRtcAec_sqrtHanning[i]); - // A B C D - float32x4_t vec_sqrtHanning_rev = - vld1q_f32(&WebRtcAec_sqrtHanning[PART_LEN - i - 3]); - // B A D C - vec_sqrtHanning_rev = vrev64q_f32(vec_sqrtHanning_rev); - // D C B A - vec_sqrtHanning_rev = vcombine_f32(vget_high_f32(vec_sqrtHanning_rev), - vget_low_f32(vec_sqrtHanning_rev)); - vst1q_f32(&x_windowed[i], vmulq_f32(vec_Buf1, vec_sqrtHanning)); - vst1q_f32(&x_windowed[PART_LEN + i], - vmulq_f32(vec_Buf2, vec_sqrtHanning_rev)); - } -} - -// Puts fft output data into a complex valued array. -__inline static void StoreAsComplex(const float* data, - float data_complex[2][PART_LEN1]) { - int i; - for (i = 0; i < PART_LEN; i += 4) { - const float32x4x2_t vec_data = vld2q_f32(&data[2 * i]); - vst1q_f32(&data_complex[0][i], vec_data.val[0]); - vst1q_f32(&data_complex[1][i], vec_data.val[1]); - } - // fix beginning/end values - data_complex[1][0] = 0; - data_complex[1][PART_LEN] = 0; - data_complex[0][0] = data[0]; - data_complex[0][PART_LEN] = data[1]; -} - -static void SubbandCoherenceNEON(AecCore* aec, - float efw[2][PART_LEN1], - float xfw[2][PART_LEN1], - float* fft, - float* cohde, - float* cohxd) { - float dfw[2][PART_LEN1]; - int i; - - if (aec->delayEstCtr == 0) - aec->delayIdx = PartitionDelay(aec); - - // Use delayed far. - memcpy(xfw, - aec->xfwBuf + aec->delayIdx * PART_LEN1, - sizeof(xfw[0][0]) * 2 * PART_LEN1); - - // Windowed near fft - WindowData(fft, aec->dBuf); - aec_rdft_forward_128(fft); - StoreAsComplex(fft, dfw); - - // Windowed error fft - WindowData(fft, aec->eBuf); - aec_rdft_forward_128(fft); - StoreAsComplex(fft, efw); - - SmoothedPSD(aec, efw, dfw, xfw); - - { - const float32x4_t vec_1eminus10 = vdupq_n_f32(1e-10f); - - // Subband coherence - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const float32x4_t vec_sd = vld1q_f32(&aec->sd[i]); - const float32x4_t vec_se = vld1q_f32(&aec->se[i]); - const float32x4_t vec_sx = vld1q_f32(&aec->sx[i]); - const float32x4_t vec_sdse = vmlaq_f32(vec_1eminus10, vec_sd, vec_se); - const float32x4_t vec_sdsx = vmlaq_f32(vec_1eminus10, vec_sd, vec_sx); - float32x4x2_t vec_sde = vld2q_f32(&aec->sde[i][0]); - float32x4x2_t vec_sxd = vld2q_f32(&aec->sxd[i][0]); - float32x4_t vec_cohde = vmulq_f32(vec_sde.val[0], vec_sde.val[0]); - float32x4_t vec_cohxd = vmulq_f32(vec_sxd.val[0], vec_sxd.val[0]); - vec_cohde = vmlaq_f32(vec_cohde, vec_sde.val[1], vec_sde.val[1]); - vec_cohde = vdivq_f32(vec_cohde, vec_sdse); - vec_cohxd = vmlaq_f32(vec_cohxd, vec_sxd.val[1], vec_sxd.val[1]); - vec_cohxd = vdivq_f32(vec_cohxd, vec_sdsx); - - vst1q_f32(&cohde[i], vec_cohde); - vst1q_f32(&cohxd[i], vec_cohxd); - } - } - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - cohde[i] = - (aec->sde[i][0] * aec->sde[i][0] + aec->sde[i][1] * aec->sde[i][1]) / - (aec->sd[i] * aec->se[i] + 1e-10f); - cohxd[i] = - (aec->sxd[i][0] * aec->sxd[i][0] + aec->sxd[i][1] * aec->sxd[i][1]) / - (aec->sx[i] * aec->sd[i] + 1e-10f); - } -} - -void WebRtcAec_InitAec_neon(void) { - WebRtcAec_FilterFar = FilterFarNEON; - WebRtcAec_ScaleErrorSignal = ScaleErrorSignalNEON; - WebRtcAec_FilterAdaptation = FilterAdaptationNEON; - WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppressNEON; - WebRtcAec_SubbandCoherence = SubbandCoherenceNEON; -} - diff --git a/webrtc/modules/audio_processing/aec/aec_core_sse2.c b/webrtc/modules/audio_processing/aec/aec_core_sse2.c deleted file mode 100644 index b1bffcb..0000000 --- a/webrtc/modules/audio_processing/aec/aec_core_sse2.c +++ /dev/null @@ -1,731 +0,0 @@ -/* - * 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. - */ - -/* - * The core AEC algorithm, SSE2 version of speed-critical functions. - */ - -#include -#include -#include // memset - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/aec/aec_common.h" -#include "webrtc/modules/audio_processing/aec/aec_core_internal.h" -#include "webrtc/modules/audio_processing/aec/aec_rdft.h" - -__inline static float MulRe(float aRe, float aIm, float bRe, float bIm) { - return aRe * bRe - aIm * bIm; -} - -__inline static float MulIm(float aRe, float aIm, float bRe, float bIm) { - return aRe * bIm + aIm * bRe; -} - -static void FilterFarSSE2(AecCore* aec, float yf[2][PART_LEN1]) { - int i; - const int num_partitions = aec->num_partitions; - for (i = 0; i < num_partitions; i++) { - int j; - int xPos = (i + aec->xfBufBlockPos) * PART_LEN1; - int pos = i * PART_LEN1; - // Check for wrap - if (i + aec->xfBufBlockPos >= num_partitions) { - xPos -= num_partitions * (PART_LEN1); - } - - // vectorized code (four at once) - for (j = 0; j + 3 < PART_LEN1; j += 4) { - const __m128 xfBuf_re = _mm_loadu_ps(&aec->xfBuf[0][xPos + j]); - const __m128 xfBuf_im = _mm_loadu_ps(&aec->xfBuf[1][xPos + j]); - const __m128 wfBuf_re = _mm_loadu_ps(&aec->wfBuf[0][pos + j]); - const __m128 wfBuf_im = _mm_loadu_ps(&aec->wfBuf[1][pos + j]); - const __m128 yf_re = _mm_loadu_ps(&yf[0][j]); - const __m128 yf_im = _mm_loadu_ps(&yf[1][j]); - const __m128 a = _mm_mul_ps(xfBuf_re, wfBuf_re); - const __m128 b = _mm_mul_ps(xfBuf_im, wfBuf_im); - const __m128 c = _mm_mul_ps(xfBuf_re, wfBuf_im); - const __m128 d = _mm_mul_ps(xfBuf_im, wfBuf_re); - const __m128 e = _mm_sub_ps(a, b); - const __m128 f = _mm_add_ps(c, d); - const __m128 g = _mm_add_ps(yf_re, e); - const __m128 h = _mm_add_ps(yf_im, f); - _mm_storeu_ps(&yf[0][j], g); - _mm_storeu_ps(&yf[1][j], h); - } - // scalar code for the remaining items. - for (; j < PART_LEN1; j++) { - yf[0][j] += MulRe(aec->xfBuf[0][xPos + j], - aec->xfBuf[1][xPos + j], - aec->wfBuf[0][pos + j], - aec->wfBuf[1][pos + j]); - yf[1][j] += MulIm(aec->xfBuf[0][xPos + j], - aec->xfBuf[1][xPos + j], - aec->wfBuf[0][pos + j], - aec->wfBuf[1][pos + j]); - } - } -} - -static void ScaleErrorSignalSSE2(AecCore* aec, float ef[2][PART_LEN1]) { - const __m128 k1e_10f = _mm_set1_ps(1e-10f); - const __m128 kMu = aec->extended_filter_enabled ? _mm_set1_ps(kExtendedMu) - : _mm_set1_ps(aec->normal_mu); - const __m128 kThresh = aec->extended_filter_enabled - ? _mm_set1_ps(kExtendedErrorThreshold) - : _mm_set1_ps(aec->normal_error_threshold); - - int i; - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const __m128 xPow = _mm_loadu_ps(&aec->xPow[i]); - const __m128 ef_re_base = _mm_loadu_ps(&ef[0][i]); - const __m128 ef_im_base = _mm_loadu_ps(&ef[1][i]); - - const __m128 xPowPlus = _mm_add_ps(xPow, k1e_10f); - __m128 ef_re = _mm_div_ps(ef_re_base, xPowPlus); - __m128 ef_im = _mm_div_ps(ef_im_base, xPowPlus); - const __m128 ef_re2 = _mm_mul_ps(ef_re, ef_re); - const __m128 ef_im2 = _mm_mul_ps(ef_im, ef_im); - const __m128 ef_sum2 = _mm_add_ps(ef_re2, ef_im2); - const __m128 absEf = _mm_sqrt_ps(ef_sum2); - const __m128 bigger = _mm_cmpgt_ps(absEf, kThresh); - __m128 absEfPlus = _mm_add_ps(absEf, k1e_10f); - const __m128 absEfInv = _mm_div_ps(kThresh, absEfPlus); - __m128 ef_re_if = _mm_mul_ps(ef_re, absEfInv); - __m128 ef_im_if = _mm_mul_ps(ef_im, absEfInv); - ef_re_if = _mm_and_ps(bigger, ef_re_if); - ef_im_if = _mm_and_ps(bigger, ef_im_if); - ef_re = _mm_andnot_ps(bigger, ef_re); - ef_im = _mm_andnot_ps(bigger, ef_im); - ef_re = _mm_or_ps(ef_re, ef_re_if); - ef_im = _mm_or_ps(ef_im, ef_im_if); - ef_re = _mm_mul_ps(ef_re, kMu); - ef_im = _mm_mul_ps(ef_im, kMu); - - _mm_storeu_ps(&ef[0][i], ef_re); - _mm_storeu_ps(&ef[1][i], ef_im); - } - // scalar code for the remaining items. - { - const float mu = - aec->extended_filter_enabled ? kExtendedMu : aec->normal_mu; - const float error_threshold = aec->extended_filter_enabled - ? kExtendedErrorThreshold - : aec->normal_error_threshold; - for (; i < (PART_LEN1); i++) { - float abs_ef; - ef[0][i] /= (aec->xPow[i] + 1e-10f); - ef[1][i] /= (aec->xPow[i] + 1e-10f); - abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]); - - if (abs_ef > error_threshold) { - abs_ef = error_threshold / (abs_ef + 1e-10f); - ef[0][i] *= abs_ef; - ef[1][i] *= abs_ef; - } - - // Stepsize factor - ef[0][i] *= mu; - ef[1][i] *= mu; - } - } -} - -static void FilterAdaptationSSE2(AecCore* aec, - float* fft, - float ef[2][PART_LEN1]) { - int i, j; - const int num_partitions = aec->num_partitions; - for (i = 0; i < num_partitions; i++) { - int xPos = (i + aec->xfBufBlockPos) * (PART_LEN1); - int pos = i * PART_LEN1; - // Check for wrap - if (i + aec->xfBufBlockPos >= num_partitions) { - xPos -= num_partitions * PART_LEN1; - } - - // Process the whole array... - for (j = 0; j < PART_LEN; j += 4) { - // Load xfBuf and ef. - const __m128 xfBuf_re = _mm_loadu_ps(&aec->xfBuf[0][xPos + j]); - const __m128 xfBuf_im = _mm_loadu_ps(&aec->xfBuf[1][xPos + j]); - const __m128 ef_re = _mm_loadu_ps(&ef[0][j]); - const __m128 ef_im = _mm_loadu_ps(&ef[1][j]); - // Calculate the product of conjugate(xfBuf) by ef. - // re(conjugate(a) * b) = aRe * bRe + aIm * bIm - // im(conjugate(a) * b)= aRe * bIm - aIm * bRe - const __m128 a = _mm_mul_ps(xfBuf_re, ef_re); - const __m128 b = _mm_mul_ps(xfBuf_im, ef_im); - const __m128 c = _mm_mul_ps(xfBuf_re, ef_im); - const __m128 d = _mm_mul_ps(xfBuf_im, ef_re); - const __m128 e = _mm_add_ps(a, b); - const __m128 f = _mm_sub_ps(c, d); - // Interleave real and imaginary parts. - const __m128 g = _mm_unpacklo_ps(e, f); - const __m128 h = _mm_unpackhi_ps(e, f); - // Store - _mm_storeu_ps(&fft[2 * j + 0], g); - _mm_storeu_ps(&fft[2 * j + 4], h); - } - // ... and fixup the first imaginary entry. - fft[1] = MulRe(aec->xfBuf[0][xPos + PART_LEN], - -aec->xfBuf[1][xPos + PART_LEN], - ef[0][PART_LEN], - ef[1][PART_LEN]); - - aec_rdft_inverse_128(fft); - memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN); - - // fft scaling - { - float scale = 2.0f / PART_LEN2; - const __m128 scale_ps = _mm_load_ps1(&scale); - for (j = 0; j < PART_LEN; j += 4) { - const __m128 fft_ps = _mm_loadu_ps(&fft[j]); - const __m128 fft_scale = _mm_mul_ps(fft_ps, scale_ps); - _mm_storeu_ps(&fft[j], fft_scale); - } - } - aec_rdft_forward_128(fft); - - { - float wt1 = aec->wfBuf[1][pos]; - aec->wfBuf[0][pos + PART_LEN] += fft[1]; - for (j = 0; j < PART_LEN; j += 4) { - __m128 wtBuf_re = _mm_loadu_ps(&aec->wfBuf[0][pos + j]); - __m128 wtBuf_im = _mm_loadu_ps(&aec->wfBuf[1][pos + j]); - const __m128 fft0 = _mm_loadu_ps(&fft[2 * j + 0]); - const __m128 fft4 = _mm_loadu_ps(&fft[2 * j + 4]); - const __m128 fft_re = - _mm_shuffle_ps(fft0, fft4, _MM_SHUFFLE(2, 0, 2, 0)); - const __m128 fft_im = - _mm_shuffle_ps(fft0, fft4, _MM_SHUFFLE(3, 1, 3, 1)); - wtBuf_re = _mm_add_ps(wtBuf_re, fft_re); - wtBuf_im = _mm_add_ps(wtBuf_im, fft_im); - _mm_storeu_ps(&aec->wfBuf[0][pos + j], wtBuf_re); - _mm_storeu_ps(&aec->wfBuf[1][pos + j], wtBuf_im); - } - aec->wfBuf[1][pos] = wt1; - } - } -} - -static __m128 mm_pow_ps(__m128 a, __m128 b) { - // a^b = exp2(b * log2(a)) - // exp2(x) and log2(x) are calculated using polynomial approximations. - __m128 log2_a, b_log2_a, a_exp_b; - - // Calculate log2(x), x = a. - { - // To calculate log2(x), we decompose x like this: - // x = y * 2^n - // n is an integer - // y is in the [1.0, 2.0) range - // - // log2(x) = log2(y) + n - // n can be evaluated by playing with float representation. - // log2(y) in a small range can be approximated, this code uses an order - // five polynomial approximation. The coefficients have been - // estimated with the Remez algorithm and the resulting - // polynomial has a maximum relative error of 0.00086%. - - // Compute n. - // This is done by masking the exponent, shifting it into the top bit of - // the mantissa, putting eight into the biased exponent (to shift/ - // compensate the fact that the exponent has been shifted in the top/ - // fractional part and finally getting rid of the implicit leading one - // from the mantissa by substracting it out. - static const ALIGN16_BEG int float_exponent_mask[4] ALIGN16_END = { - 0x7F800000, 0x7F800000, 0x7F800000, 0x7F800000}; - static const ALIGN16_BEG int eight_biased_exponent[4] ALIGN16_END = { - 0x43800000, 0x43800000, 0x43800000, 0x43800000}; - static const ALIGN16_BEG int implicit_leading_one[4] ALIGN16_END = { - 0x43BF8000, 0x43BF8000, 0x43BF8000, 0x43BF8000}; - static const int shift_exponent_into_top_mantissa = 8; - const __m128 two_n = _mm_and_ps(a, *((__m128*)float_exponent_mask)); - const __m128 n_1 = _mm_castsi128_ps(_mm_srli_epi32( - _mm_castps_si128(two_n), shift_exponent_into_top_mantissa)); - const __m128 n_0 = _mm_or_ps(n_1, *((__m128*)eight_biased_exponent)); - const __m128 n = _mm_sub_ps(n_0, *((__m128*)implicit_leading_one)); - - // Compute y. - static const ALIGN16_BEG int mantissa_mask[4] ALIGN16_END = { - 0x007FFFFF, 0x007FFFFF, 0x007FFFFF, 0x007FFFFF}; - static const ALIGN16_BEG int zero_biased_exponent_is_one[4] ALIGN16_END = { - 0x3F800000, 0x3F800000, 0x3F800000, 0x3F800000}; - const __m128 mantissa = _mm_and_ps(a, *((__m128*)mantissa_mask)); - const __m128 y = - _mm_or_ps(mantissa, *((__m128*)zero_biased_exponent_is_one)); - - // Approximate log2(y) ~= (y - 1) * pol5(y). - // pol5(y) = C5 * y^5 + C4 * y^4 + C3 * y^3 + C2 * y^2 + C1 * y + C0 - static const ALIGN16_BEG float ALIGN16_END C5[4] = { - -3.4436006e-2f, -3.4436006e-2f, -3.4436006e-2f, -3.4436006e-2f}; - static const ALIGN16_BEG float ALIGN16_END - C4[4] = {3.1821337e-1f, 3.1821337e-1f, 3.1821337e-1f, 3.1821337e-1f}; - static const ALIGN16_BEG float ALIGN16_END - C3[4] = {-1.2315303f, -1.2315303f, -1.2315303f, -1.2315303f}; - static const ALIGN16_BEG float ALIGN16_END - C2[4] = {2.5988452f, 2.5988452f, 2.5988452f, 2.5988452f}; - static const ALIGN16_BEG float ALIGN16_END - C1[4] = {-3.3241990f, -3.3241990f, -3.3241990f, -3.3241990f}; - static const ALIGN16_BEG float ALIGN16_END - C0[4] = {3.1157899f, 3.1157899f, 3.1157899f, 3.1157899f}; - const __m128 pol5_y_0 = _mm_mul_ps(y, *((__m128*)C5)); - const __m128 pol5_y_1 = _mm_add_ps(pol5_y_0, *((__m128*)C4)); - const __m128 pol5_y_2 = _mm_mul_ps(pol5_y_1, y); - const __m128 pol5_y_3 = _mm_add_ps(pol5_y_2, *((__m128*)C3)); - const __m128 pol5_y_4 = _mm_mul_ps(pol5_y_3, y); - const __m128 pol5_y_5 = _mm_add_ps(pol5_y_4, *((__m128*)C2)); - const __m128 pol5_y_6 = _mm_mul_ps(pol5_y_5, y); - const __m128 pol5_y_7 = _mm_add_ps(pol5_y_6, *((__m128*)C1)); - const __m128 pol5_y_8 = _mm_mul_ps(pol5_y_7, y); - const __m128 pol5_y = _mm_add_ps(pol5_y_8, *((__m128*)C0)); - const __m128 y_minus_one = - _mm_sub_ps(y, *((__m128*)zero_biased_exponent_is_one)); - const __m128 log2_y = _mm_mul_ps(y_minus_one, pol5_y); - - // Combine parts. - log2_a = _mm_add_ps(n, log2_y); - } - - // b * log2(a) - b_log2_a = _mm_mul_ps(b, log2_a); - - // Calculate exp2(x), x = b * log2(a). - { - // To calculate 2^x, we decompose x like this: - // x = n + y - // n is an integer, the value of x - 0.5 rounded down, therefore - // y is in the [0.5, 1.5) range - // - // 2^x = 2^n * 2^y - // 2^n can be evaluated by playing with float representation. - // 2^y in a small range can be approximated, this code uses an order two - // polynomial approximation. The coefficients have been estimated - // with the Remez algorithm and the resulting polynomial has a - // maximum relative error of 0.17%. - - // To avoid over/underflow, we reduce the range of input to ]-127, 129]. - static const ALIGN16_BEG float max_input[4] ALIGN16_END = {129.f, 129.f, - 129.f, 129.f}; - static const ALIGN16_BEG float min_input[4] ALIGN16_END = { - -126.99999f, -126.99999f, -126.99999f, -126.99999f}; - const __m128 x_min = _mm_min_ps(b_log2_a, *((__m128*)max_input)); - const __m128 x_max = _mm_max_ps(x_min, *((__m128*)min_input)); - // Compute n. - static const ALIGN16_BEG float half[4] ALIGN16_END = {0.5f, 0.5f, - 0.5f, 0.5f}; - const __m128 x_minus_half = _mm_sub_ps(x_max, *((__m128*)half)); - const __m128i x_minus_half_floor = _mm_cvtps_epi32(x_minus_half); - // Compute 2^n. - static const ALIGN16_BEG int float_exponent_bias[4] ALIGN16_END = { - 127, 127, 127, 127}; - static const int float_exponent_shift = 23; - const __m128i two_n_exponent = - _mm_add_epi32(x_minus_half_floor, *((__m128i*)float_exponent_bias)); - const __m128 two_n = - _mm_castsi128_ps(_mm_slli_epi32(two_n_exponent, float_exponent_shift)); - // Compute y. - const __m128 y = _mm_sub_ps(x_max, _mm_cvtepi32_ps(x_minus_half_floor)); - // Approximate 2^y ~= C2 * y^2 + C1 * y + C0. - static const ALIGN16_BEG float C2[4] ALIGN16_END = { - 3.3718944e-1f, 3.3718944e-1f, 3.3718944e-1f, 3.3718944e-1f}; - static const ALIGN16_BEG float C1[4] ALIGN16_END = { - 6.5763628e-1f, 6.5763628e-1f, 6.5763628e-1f, 6.5763628e-1f}; - static const ALIGN16_BEG float C0[4] ALIGN16_END = {1.0017247f, 1.0017247f, - 1.0017247f, 1.0017247f}; - const __m128 exp2_y_0 = _mm_mul_ps(y, *((__m128*)C2)); - const __m128 exp2_y_1 = _mm_add_ps(exp2_y_0, *((__m128*)C1)); - const __m128 exp2_y_2 = _mm_mul_ps(exp2_y_1, y); - const __m128 exp2_y = _mm_add_ps(exp2_y_2, *((__m128*)C0)); - - // Combine parts. - a_exp_b = _mm_mul_ps(exp2_y, two_n); - } - return a_exp_b; -} - -static void OverdriveAndSuppressSSE2(AecCore* aec, - float hNl[PART_LEN1], - const float hNlFb, - float efw[2][PART_LEN1]) { - int i; - const __m128 vec_hNlFb = _mm_set1_ps(hNlFb); - const __m128 vec_one = _mm_set1_ps(1.0f); - const __m128 vec_minus_one = _mm_set1_ps(-1.0f); - const __m128 vec_overDriveSm = _mm_set1_ps(aec->overDriveSm); - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i += 4) { - // Weight subbands - __m128 vec_hNl = _mm_loadu_ps(&hNl[i]); - const __m128 vec_weightCurve = _mm_loadu_ps(&WebRtcAec_weightCurve[i]); - const __m128 bigger = _mm_cmpgt_ps(vec_hNl, vec_hNlFb); - const __m128 vec_weightCurve_hNlFb = _mm_mul_ps(vec_weightCurve, vec_hNlFb); - const __m128 vec_one_weightCurve = _mm_sub_ps(vec_one, vec_weightCurve); - const __m128 vec_one_weightCurve_hNl = - _mm_mul_ps(vec_one_weightCurve, vec_hNl); - const __m128 vec_if0 = _mm_andnot_ps(bigger, vec_hNl); - const __m128 vec_if1 = _mm_and_ps( - bigger, _mm_add_ps(vec_weightCurve_hNlFb, vec_one_weightCurve_hNl)); - vec_hNl = _mm_or_ps(vec_if0, vec_if1); - - { - const __m128 vec_overDriveCurve = - _mm_loadu_ps(&WebRtcAec_overDriveCurve[i]); - const __m128 vec_overDriveSm_overDriveCurve = - _mm_mul_ps(vec_overDriveSm, vec_overDriveCurve); - vec_hNl = mm_pow_ps(vec_hNl, vec_overDriveSm_overDriveCurve); - _mm_storeu_ps(&hNl[i], vec_hNl); - } - - // Suppress error signal - { - __m128 vec_efw_re = _mm_loadu_ps(&efw[0][i]); - __m128 vec_efw_im = _mm_loadu_ps(&efw[1][i]); - vec_efw_re = _mm_mul_ps(vec_efw_re, vec_hNl); - vec_efw_im = _mm_mul_ps(vec_efw_im, vec_hNl); - - // Ooura fft returns incorrect sign on imaginary component. It matters - // here because we are making an additive change with comfort noise. - vec_efw_im = _mm_mul_ps(vec_efw_im, vec_minus_one); - _mm_storeu_ps(&efw[0][i], vec_efw_re); - _mm_storeu_ps(&efw[1][i], vec_efw_im); - } - } - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - // Weight subbands - if (hNl[i] > hNlFb) { - hNl[i] = WebRtcAec_weightCurve[i] * hNlFb + - (1 - WebRtcAec_weightCurve[i]) * hNl[i]; - } - hNl[i] = powf(hNl[i], aec->overDriveSm * WebRtcAec_overDriveCurve[i]); - - // Suppress error signal - efw[0][i] *= hNl[i]; - efw[1][i] *= hNl[i]; - - // Ooura fft returns incorrect sign on imaginary component. It matters - // here because we are making an additive change with comfort noise. - efw[1][i] *= -1; - } -} - -__inline static void _mm_add_ps_4x1(__m128 sum, float *dst) { - // A+B C+D - sum = _mm_add_ps(sum, _mm_shuffle_ps(sum, sum, _MM_SHUFFLE(0, 0, 3, 2))); - // A+B+C+D A+B+C+D - sum = _mm_add_ps(sum, _mm_shuffle_ps(sum, sum, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dst, sum); -} -static int PartitionDelay(const AecCore* aec) { - // Measures the energy in each filter partition and returns the partition with - // highest energy. - // TODO(bjornv): Spread computational cost by computing one partition per - // block? - float wfEnMax = 0; - int i; - int delay = 0; - - for (i = 0; i < aec->num_partitions; i++) { - int j; - int pos = i * PART_LEN1; - float wfEn = 0; - __m128 vec_wfEn = _mm_set1_ps(0.0f); - // vectorized code (four at once) - for (j = 0; j + 3 < PART_LEN1; j += 4) { - const __m128 vec_wfBuf0 = _mm_loadu_ps(&aec->wfBuf[0][pos + j]); - const __m128 vec_wfBuf1 = _mm_loadu_ps(&aec->wfBuf[1][pos + j]); - vec_wfEn = _mm_add_ps(vec_wfEn, _mm_mul_ps(vec_wfBuf0, vec_wfBuf0)); - vec_wfEn = _mm_add_ps(vec_wfEn, _mm_mul_ps(vec_wfBuf1, vec_wfBuf1)); - } - _mm_add_ps_4x1(vec_wfEn, &wfEn); - - // scalar code for the remaining items. - for (; j < PART_LEN1; j++) { - wfEn += aec->wfBuf[0][pos + j] * aec->wfBuf[0][pos + j] + - aec->wfBuf[1][pos + j] * aec->wfBuf[1][pos + j]; - } - - if (wfEn > wfEnMax) { - wfEnMax = wfEn; - delay = i; - } - } - return delay; -} - -// Updates the following smoothed Power Spectral Densities (PSD): -// - sd : near-end -// - se : residual echo -// - sx : far-end -// - sde : cross-PSD of near-end and residual echo -// - sxd : cross-PSD of near-end and far-end -// -// In addition to updating the PSDs, also the filter diverge state is determined -// upon actions are taken. -static void SmoothedPSD(AecCore* aec, - float efw[2][PART_LEN1], - float dfw[2][PART_LEN1], - float xfw[2][PART_LEN1]) { - // Power estimate smoothing coefficients. - const float* ptrGCoh = aec->extended_filter_enabled - ? WebRtcAec_kExtendedSmoothingCoefficients[aec->mult - 1] - : WebRtcAec_kNormalSmoothingCoefficients[aec->mult - 1]; - int i; - float sdSum = 0, seSum = 0; - const __m128 vec_15 = _mm_set1_ps(WebRtcAec_kMinFarendPSD); - const __m128 vec_GCoh0 = _mm_set1_ps(ptrGCoh[0]); - const __m128 vec_GCoh1 = _mm_set1_ps(ptrGCoh[1]); - __m128 vec_sdSum = _mm_set1_ps(0.0f); - __m128 vec_seSum = _mm_set1_ps(0.0f); - - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const __m128 vec_dfw0 = _mm_loadu_ps(&dfw[0][i]); - const __m128 vec_dfw1 = _mm_loadu_ps(&dfw[1][i]); - const __m128 vec_efw0 = _mm_loadu_ps(&efw[0][i]); - const __m128 vec_efw1 = _mm_loadu_ps(&efw[1][i]); - const __m128 vec_xfw0 = _mm_loadu_ps(&xfw[0][i]); - const __m128 vec_xfw1 = _mm_loadu_ps(&xfw[1][i]); - __m128 vec_sd = _mm_mul_ps(_mm_loadu_ps(&aec->sd[i]), vec_GCoh0); - __m128 vec_se = _mm_mul_ps(_mm_loadu_ps(&aec->se[i]), vec_GCoh0); - __m128 vec_sx = _mm_mul_ps(_mm_loadu_ps(&aec->sx[i]), vec_GCoh0); - __m128 vec_dfw_sumsq = _mm_mul_ps(vec_dfw0, vec_dfw0); - __m128 vec_efw_sumsq = _mm_mul_ps(vec_efw0, vec_efw0); - __m128 vec_xfw_sumsq = _mm_mul_ps(vec_xfw0, vec_xfw0); - vec_dfw_sumsq = _mm_add_ps(vec_dfw_sumsq, _mm_mul_ps(vec_dfw1, vec_dfw1)); - vec_efw_sumsq = _mm_add_ps(vec_efw_sumsq, _mm_mul_ps(vec_efw1, vec_efw1)); - vec_xfw_sumsq = _mm_add_ps(vec_xfw_sumsq, _mm_mul_ps(vec_xfw1, vec_xfw1)); - vec_xfw_sumsq = _mm_max_ps(vec_xfw_sumsq, vec_15); - vec_sd = _mm_add_ps(vec_sd, _mm_mul_ps(vec_dfw_sumsq, vec_GCoh1)); - vec_se = _mm_add_ps(vec_se, _mm_mul_ps(vec_efw_sumsq, vec_GCoh1)); - vec_sx = _mm_add_ps(vec_sx, _mm_mul_ps(vec_xfw_sumsq, vec_GCoh1)); - _mm_storeu_ps(&aec->sd[i], vec_sd); - _mm_storeu_ps(&aec->se[i], vec_se); - _mm_storeu_ps(&aec->sx[i], vec_sx); - - { - const __m128 vec_3210 = _mm_loadu_ps(&aec->sde[i][0]); - const __m128 vec_7654 = _mm_loadu_ps(&aec->sde[i + 2][0]); - __m128 vec_a = _mm_shuffle_ps(vec_3210, vec_7654, - _MM_SHUFFLE(2, 0, 2, 0)); - __m128 vec_b = _mm_shuffle_ps(vec_3210, vec_7654, - _MM_SHUFFLE(3, 1, 3, 1)); - __m128 vec_dfwefw0011 = _mm_mul_ps(vec_dfw0, vec_efw0); - __m128 vec_dfwefw0110 = _mm_mul_ps(vec_dfw0, vec_efw1); - vec_a = _mm_mul_ps(vec_a, vec_GCoh0); - vec_b = _mm_mul_ps(vec_b, vec_GCoh0); - vec_dfwefw0011 = _mm_add_ps(vec_dfwefw0011, - _mm_mul_ps(vec_dfw1, vec_efw1)); - vec_dfwefw0110 = _mm_sub_ps(vec_dfwefw0110, - _mm_mul_ps(vec_dfw1, vec_efw0)); - vec_a = _mm_add_ps(vec_a, _mm_mul_ps(vec_dfwefw0011, vec_GCoh1)); - vec_b = _mm_add_ps(vec_b, _mm_mul_ps(vec_dfwefw0110, vec_GCoh1)); - _mm_storeu_ps(&aec->sde[i][0], _mm_unpacklo_ps(vec_a, vec_b)); - _mm_storeu_ps(&aec->sde[i + 2][0], _mm_unpackhi_ps(vec_a, vec_b)); - } - - { - const __m128 vec_3210 = _mm_loadu_ps(&aec->sxd[i][0]); - const __m128 vec_7654 = _mm_loadu_ps(&aec->sxd[i + 2][0]); - __m128 vec_a = _mm_shuffle_ps(vec_3210, vec_7654, - _MM_SHUFFLE(2, 0, 2, 0)); - __m128 vec_b = _mm_shuffle_ps(vec_3210, vec_7654, - _MM_SHUFFLE(3, 1, 3, 1)); - __m128 vec_dfwxfw0011 = _mm_mul_ps(vec_dfw0, vec_xfw0); - __m128 vec_dfwxfw0110 = _mm_mul_ps(vec_dfw0, vec_xfw1); - vec_a = _mm_mul_ps(vec_a, vec_GCoh0); - vec_b = _mm_mul_ps(vec_b, vec_GCoh0); - vec_dfwxfw0011 = _mm_add_ps(vec_dfwxfw0011, - _mm_mul_ps(vec_dfw1, vec_xfw1)); - vec_dfwxfw0110 = _mm_sub_ps(vec_dfwxfw0110, - _mm_mul_ps(vec_dfw1, vec_xfw0)); - vec_a = _mm_add_ps(vec_a, _mm_mul_ps(vec_dfwxfw0011, vec_GCoh1)); - vec_b = _mm_add_ps(vec_b, _mm_mul_ps(vec_dfwxfw0110, vec_GCoh1)); - _mm_storeu_ps(&aec->sxd[i][0], _mm_unpacklo_ps(vec_a, vec_b)); - _mm_storeu_ps(&aec->sxd[i + 2][0], _mm_unpackhi_ps(vec_a, vec_b)); - } - - vec_sdSum = _mm_add_ps(vec_sdSum, vec_sd); - vec_seSum = _mm_add_ps(vec_seSum, vec_se); - } - - _mm_add_ps_4x1(vec_sdSum, &sdSum); - _mm_add_ps_4x1(vec_seSum, &seSum); - - for (; i < PART_LEN1; i++) { - aec->sd[i] = ptrGCoh[0] * aec->sd[i] + - ptrGCoh[1] * (dfw[0][i] * dfw[0][i] + dfw[1][i] * dfw[1][i]); - aec->se[i] = ptrGCoh[0] * aec->se[i] + - ptrGCoh[1] * (efw[0][i] * efw[0][i] + efw[1][i] * efw[1][i]); - // We threshold here to protect against the ill-effects of a zero farend. - // The threshold is not arbitrarily chosen, but balances protection and - // adverse interaction with the algorithm's tuning. - // TODO(bjornv): investigate further why this is so sensitive. - aec->sx[i] = - ptrGCoh[0] * aec->sx[i] + - ptrGCoh[1] * WEBRTC_SPL_MAX( - xfw[0][i] * xfw[0][i] + xfw[1][i] * xfw[1][i], - WebRtcAec_kMinFarendPSD); - - aec->sde[i][0] = - ptrGCoh[0] * aec->sde[i][0] + - ptrGCoh[1] * (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); - aec->sde[i][1] = - ptrGCoh[0] * aec->sde[i][1] + - ptrGCoh[1] * (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); - - aec->sxd[i][0] = - ptrGCoh[0] * aec->sxd[i][0] + - ptrGCoh[1] * (dfw[0][i] * xfw[0][i] + dfw[1][i] * xfw[1][i]); - aec->sxd[i][1] = - ptrGCoh[0] * aec->sxd[i][1] + - ptrGCoh[1] * (dfw[0][i] * xfw[1][i] - dfw[1][i] * xfw[0][i]); - - sdSum += aec->sd[i]; - seSum += aec->se[i]; - } - - // Divergent filter safeguard. - aec->divergeState = (aec->divergeState ? 1.05f : 1.0f) * seSum > sdSum; - - if (aec->divergeState) - memcpy(efw, dfw, sizeof(efw[0][0]) * 2 * PART_LEN1); - - // Reset if error is significantly larger than nearend (13 dB). - if (!aec->extended_filter_enabled && seSum > (19.95f * sdSum)) - memset(aec->wfBuf, 0, sizeof(aec->wfBuf)); -} - -// Window time domain data to be used by the fft. -__inline static void WindowData(float* x_windowed, const float* x) { - int i; - for (i = 0; i < PART_LEN; i += 4) { - const __m128 vec_Buf1 = _mm_loadu_ps(&x[i]); - const __m128 vec_Buf2 = _mm_loadu_ps(&x[PART_LEN + i]); - const __m128 vec_sqrtHanning = _mm_load_ps(&WebRtcAec_sqrtHanning[i]); - // A B C D - __m128 vec_sqrtHanning_rev = - _mm_loadu_ps(&WebRtcAec_sqrtHanning[PART_LEN - i - 3]); - // D C B A - vec_sqrtHanning_rev = - _mm_shuffle_ps(vec_sqrtHanning_rev, vec_sqrtHanning_rev, - _MM_SHUFFLE(0, 1, 2, 3)); - _mm_storeu_ps(&x_windowed[i], _mm_mul_ps(vec_Buf1, vec_sqrtHanning)); - _mm_storeu_ps(&x_windowed[PART_LEN + i], - _mm_mul_ps(vec_Buf2, vec_sqrtHanning_rev)); - } -} - -// Puts fft output data into a complex valued array. -__inline static void StoreAsComplex(const float* data, - float data_complex[2][PART_LEN1]) { - int i; - for (i = 0; i < PART_LEN; i += 4) { - const __m128 vec_fft0 = _mm_loadu_ps(&data[2 * i]); - const __m128 vec_fft4 = _mm_loadu_ps(&data[2 * i + 4]); - const __m128 vec_a = _mm_shuffle_ps(vec_fft0, vec_fft4, - _MM_SHUFFLE(2, 0, 2, 0)); - const __m128 vec_b = _mm_shuffle_ps(vec_fft0, vec_fft4, - _MM_SHUFFLE(3, 1, 3, 1)); - _mm_storeu_ps(&data_complex[0][i], vec_a); - _mm_storeu_ps(&data_complex[1][i], vec_b); - } - // fix beginning/end values - data_complex[1][0] = 0; - data_complex[1][PART_LEN] = 0; - data_complex[0][0] = data[0]; - data_complex[0][PART_LEN] = data[1]; -} - -static void SubbandCoherenceSSE2(AecCore* aec, - float efw[2][PART_LEN1], - float xfw[2][PART_LEN1], - float* fft, - float* cohde, - float* cohxd) { - float dfw[2][PART_LEN1]; - int i; - - if (aec->delayEstCtr == 0) - aec->delayIdx = PartitionDelay(aec); - - // Use delayed far. - memcpy(xfw, - aec->xfwBuf + aec->delayIdx * PART_LEN1, - sizeof(xfw[0][0]) * 2 * PART_LEN1); - - // Windowed near fft - WindowData(fft, aec->dBuf); - aec_rdft_forward_128(fft); - StoreAsComplex(fft, dfw); - - // Windowed error fft - WindowData(fft, aec->eBuf); - aec_rdft_forward_128(fft); - StoreAsComplex(fft, efw); - - SmoothedPSD(aec, efw, dfw, xfw); - - { - const __m128 vec_1eminus10 = _mm_set1_ps(1e-10f); - - // Subband coherence - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const __m128 vec_sd = _mm_loadu_ps(&aec->sd[i]); - const __m128 vec_se = _mm_loadu_ps(&aec->se[i]); - const __m128 vec_sx = _mm_loadu_ps(&aec->sx[i]); - const __m128 vec_sdse = _mm_add_ps(vec_1eminus10, - _mm_mul_ps(vec_sd, vec_se)); - const __m128 vec_sdsx = _mm_add_ps(vec_1eminus10, - _mm_mul_ps(vec_sd, vec_sx)); - const __m128 vec_sde_3210 = _mm_loadu_ps(&aec->sde[i][0]); - const __m128 vec_sde_7654 = _mm_loadu_ps(&aec->sde[i + 2][0]); - const __m128 vec_sxd_3210 = _mm_loadu_ps(&aec->sxd[i][0]); - const __m128 vec_sxd_7654 = _mm_loadu_ps(&aec->sxd[i + 2][0]); - const __m128 vec_sde_0 = _mm_shuffle_ps(vec_sde_3210, vec_sde_7654, - _MM_SHUFFLE(2, 0, 2, 0)); - const __m128 vec_sde_1 = _mm_shuffle_ps(vec_sde_3210, vec_sde_7654, - _MM_SHUFFLE(3, 1, 3, 1)); - const __m128 vec_sxd_0 = _mm_shuffle_ps(vec_sxd_3210, vec_sxd_7654, - _MM_SHUFFLE(2, 0, 2, 0)); - const __m128 vec_sxd_1 = _mm_shuffle_ps(vec_sxd_3210, vec_sxd_7654, - _MM_SHUFFLE(3, 1, 3, 1)); - __m128 vec_cohde = _mm_mul_ps(vec_sde_0, vec_sde_0); - __m128 vec_cohxd = _mm_mul_ps(vec_sxd_0, vec_sxd_0); - vec_cohde = _mm_add_ps(vec_cohde, _mm_mul_ps(vec_sde_1, vec_sde_1)); - vec_cohde = _mm_div_ps(vec_cohde, vec_sdse); - vec_cohxd = _mm_add_ps(vec_cohxd, _mm_mul_ps(vec_sxd_1, vec_sxd_1)); - vec_cohxd = _mm_div_ps(vec_cohxd, vec_sdsx); - _mm_storeu_ps(&cohde[i], vec_cohde); - _mm_storeu_ps(&cohxd[i], vec_cohxd); - } - - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - cohde[i] = - (aec->sde[i][0] * aec->sde[i][0] + aec->sde[i][1] * aec->sde[i][1]) / - (aec->sd[i] * aec->se[i] + 1e-10f); - cohxd[i] = - (aec->sxd[i][0] * aec->sxd[i][0] + aec->sxd[i][1] * aec->sxd[i][1]) / - (aec->sx[i] * aec->sd[i] + 1e-10f); - } - } -} - -void WebRtcAec_InitAec_SSE2(void) { - WebRtcAec_FilterFar = FilterFarSSE2; - WebRtcAec_ScaleErrorSignal = ScaleErrorSignalSSE2; - WebRtcAec_FilterAdaptation = FilterAdaptationSSE2; - WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppressSSE2; - WebRtcAec_SubbandCoherence = SubbandCoherenceSSE2; -} diff --git a/webrtc/modules/audio_processing/aec/aec_rdft.h b/webrtc/modules/audio_processing/aec/aec_rdft.h deleted file mode 100644 index 18eb7a5..0000000 --- a/webrtc/modules/audio_processing/aec/aec_rdft.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_RDFT_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_RDFT_H_ - -#include "webrtc/modules/audio_processing/aec/aec_common.h" - -// These intrinsics were unavailable before VS 2008. -// TODO(andrew): move to a common file. -#if defined(_MSC_VER) && _MSC_VER < 1500 -#include -static __inline __m128 _mm_castsi128_ps(__m128i a) { return *(__m128*)&a; } -static __inline __m128i _mm_castps_si128(__m128 a) { return *(__m128i*)&a; } -#endif - -// Constants shared by all paths (C, SSE2, NEON). -extern const float rdft_w[64]; -// Constants used by the C path. -extern const float rdft_wk3ri_first[16]; -extern const float rdft_wk3ri_second[16]; -// Constants used by SSE2 and NEON but initialized in the C path. -extern ALIGN16_BEG const float ALIGN16_END rdft_wk1r[32]; -extern ALIGN16_BEG const float ALIGN16_END rdft_wk2r[32]; -extern ALIGN16_BEG const float ALIGN16_END rdft_wk3r[32]; -extern ALIGN16_BEG const float ALIGN16_END rdft_wk1i[32]; -extern ALIGN16_BEG const float ALIGN16_END rdft_wk2i[32]; -extern ALIGN16_BEG const float ALIGN16_END rdft_wk3i[32]; -extern ALIGN16_BEG const float ALIGN16_END cftmdl_wk1r[4]; - -// code path selection function pointers -typedef void (*RftSub128)(float* a); -extern RftSub128 rftfsub_128; -extern RftSub128 rftbsub_128; -extern RftSub128 cft1st_128; -extern RftSub128 cftmdl_128; -extern RftSub128 cftfsub_128; -extern RftSub128 cftbsub_128; -extern RftSub128 bitrv2_128; - -// entry points -void aec_rdft_init(void); -void aec_rdft_init_sse2(void); -void aec_rdft_forward_128(float* a); -void aec_rdft_inverse_128(float* a); - -#if defined(MIPS_FPU_LE) -void aec_rdft_init_mips(void); -#endif -#if defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON) -void aec_rdft_init_neon(void); -#endif - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_RDFT_H_ diff --git a/webrtc/modules/audio_processing/aec/aec_rdft_mips.c b/webrtc/modules/audio_processing/aec/aec_rdft_mips.c deleted file mode 100644 index 7e64e65..0000000 --- a/webrtc/modules/audio_processing/aec/aec_rdft_mips.c +++ /dev/null @@ -1,1187 +0,0 @@ -/* - * Copyright (c) 2014 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 "webrtc/modules/audio_processing/aec/aec_rdft.h" -#include "webrtc/typedefs.h" - -static void bitrv2_128_mips(float* a) { - // n is 128 - float xr, xi, yr, yi; - - xr = a[8]; - xi = a[9]; - yr = a[16]; - yi = a[17]; - a[8] = yr; - a[9] = yi; - a[16] = xr; - a[17] = xi; - - xr = a[64]; - xi = a[65]; - yr = a[2]; - yi = a[3]; - a[64] = yr; - a[65] = yi; - a[2] = xr; - a[3] = xi; - - xr = a[72]; - xi = a[73]; - yr = a[18]; - yi = a[19]; - a[72] = yr; - a[73] = yi; - a[18] = xr; - a[19] = xi; - - xr = a[80]; - xi = a[81]; - yr = a[10]; - yi = a[11]; - a[80] = yr; - a[81] = yi; - a[10] = xr; - a[11] = xi; - - xr = a[88]; - xi = a[89]; - yr = a[26]; - yi = a[27]; - a[88] = yr; - a[89] = yi; - a[26] = xr; - a[27] = xi; - - xr = a[74]; - xi = a[75]; - yr = a[82]; - yi = a[83]; - a[74] = yr; - a[75] = yi; - a[82] = xr; - a[83] = xi; - - xr = a[32]; - xi = a[33]; - yr = a[4]; - yi = a[5]; - a[32] = yr; - a[33] = yi; - a[4] = xr; - a[5] = xi; - - xr = a[40]; - xi = a[41]; - yr = a[20]; - yi = a[21]; - a[40] = yr; - a[41] = yi; - a[20] = xr; - a[21] = xi; - - xr = a[48]; - xi = a[49]; - yr = a[12]; - yi = a[13]; - a[48] = yr; - a[49] = yi; - a[12] = xr; - a[13] = xi; - - xr = a[56]; - xi = a[57]; - yr = a[28]; - yi = a[29]; - a[56] = yr; - a[57] = yi; - a[28] = xr; - a[29] = xi; - - xr = a[34]; - xi = a[35]; - yr = a[68]; - yi = a[69]; - a[34] = yr; - a[35] = yi; - a[68] = xr; - a[69] = xi; - - xr = a[42]; - xi = a[43]; - yr = a[84]; - yi = a[85]; - a[42] = yr; - a[43] = yi; - a[84] = xr; - a[85] = xi; - - xr = a[50]; - xi = a[51]; - yr = a[76]; - yi = a[77]; - a[50] = yr; - a[51] = yi; - a[76] = xr; - a[77] = xi; - - xr = a[58]; - xi = a[59]; - yr = a[92]; - yi = a[93]; - a[58] = yr; - a[59] = yi; - a[92] = xr; - a[93] = xi; - - xr = a[44]; - xi = a[45]; - yr = a[52]; - yi = a[53]; - a[44] = yr; - a[45] = yi; - a[52] = xr; - a[53] = xi; - - xr = a[96]; - xi = a[97]; - yr = a[6]; - yi = a[7]; - a[96] = yr; - a[97] = yi; - a[6] = xr; - a[7] = xi; - - xr = a[104]; - xi = a[105]; - yr = a[22]; - yi = a[23]; - a[104] = yr; - a[105] = yi; - a[22] = xr; - a[23] = xi; - - xr = a[112]; - xi = a[113]; - yr = a[14]; - yi = a[15]; - a[112] = yr; - a[113] = yi; - a[14] = xr; - a[15] = xi; - - xr = a[120]; - xi = a[121]; - yr = a[30]; - yi = a[31]; - a[120] = yr; - a[121] = yi; - a[30] = xr; - a[31] = xi; - - xr = a[98]; - xi = a[99]; - yr = a[70]; - yi = a[71]; - a[98] = yr; - a[99] = yi; - a[70] = xr; - a[71] = xi; - - xr = a[106]; - xi = a[107]; - yr = a[86]; - yi = a[87]; - a[106] = yr; - a[107] = yi; - a[86] = xr; - a[87] = xi; - - xr = a[114]; - xi = a[115]; - yr = a[78]; - yi = a[79]; - a[114] = yr; - a[115] = yi; - a[78] = xr; - a[79] = xi; - - xr = a[122]; - xi = a[123]; - yr = a[94]; - yi = a[95]; - a[122] = yr; - a[123] = yi; - a[94] = xr; - a[95] = xi; - - xr = a[100]; - xi = a[101]; - yr = a[38]; - yi = a[39]; - a[100] = yr; - a[101] = yi; - a[38] = xr; - a[39] = xi; - - xr = a[108]; - xi = a[109]; - yr = a[54]; - yi = a[55]; - a[108] = yr; - a[109] = yi; - a[54] = xr; - a[55] = xi; - - xr = a[116]; - xi = a[117]; - yr = a[46]; - yi = a[47]; - a[116] = yr; - a[117] = yi; - a[46] = xr; - a[47] = xi; - - xr = a[124]; - xi = a[125]; - yr = a[62]; - yi = a[63]; - a[124] = yr; - a[125] = yi; - a[62] = xr; - a[63] = xi; - - xr = a[110]; - xi = a[111]; - yr = a[118]; - yi = a[119]; - a[110] = yr; - a[111] = yi; - a[118] = xr; - a[119] = xi; -} - -static void cft1st_128_mips(float* a) { - float f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14; - int a_ptr, p1_rdft, p2_rdft, count; - const float* first = rdft_wk3ri_first; - const float* second = rdft_wk3ri_second; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - // first 8 - "lwc1 %[f0], 0(%[a]) \n\t" - "lwc1 %[f1], 4(%[a]) \n\t" - "lwc1 %[f2], 8(%[a]) \n\t" - "lwc1 %[f3], 12(%[a]) \n\t" - "lwc1 %[f4], 16(%[a]) \n\t" - "lwc1 %[f5], 20(%[a]) \n\t" - "lwc1 %[f6], 24(%[a]) \n\t" - "lwc1 %[f7], 28(%[a]) \n\t" - "add.s %[f8], %[f0], %[f2] \n\t" - "sub.s %[f0], %[f0], %[f2] \n\t" - "add.s %[f2], %[f4], %[f6] \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "add.s %[f6], %[f1], %[f3] \n\t" - "sub.s %[f1], %[f1], %[f3] \n\t" - "add.s %[f3], %[f5], %[f7] \n\t" - "sub.s %[f5], %[f5], %[f7] \n\t" - "add.s %[f7], %[f8], %[f2] \n\t" - "sub.s %[f8], %[f8], %[f2] \n\t" - "sub.s %[f2], %[f1], %[f4] \n\t" - "add.s %[f1], %[f1], %[f4] \n\t" - "add.s %[f4], %[f6], %[f3] \n\t" - "sub.s %[f6], %[f6], %[f3] \n\t" - "sub.s %[f3], %[f0], %[f5] \n\t" - "add.s %[f0], %[f0], %[f5] \n\t" - "swc1 %[f7], 0(%[a]) \n\t" - "swc1 %[f8], 16(%[a]) \n\t" - "swc1 %[f2], 28(%[a]) \n\t" - "swc1 %[f1], 12(%[a]) \n\t" - "swc1 %[f4], 4(%[a]) \n\t" - "swc1 %[f6], 20(%[a]) \n\t" - "swc1 %[f3], 8(%[a]) \n\t" - "swc1 %[f0], 24(%[a]) \n\t" - // second 8 - "lwc1 %[f0], 32(%[a]) \n\t" - "lwc1 %[f1], 36(%[a]) \n\t" - "lwc1 %[f2], 40(%[a]) \n\t" - "lwc1 %[f3], 44(%[a]) \n\t" - "lwc1 %[f4], 48(%[a]) \n\t" - "lwc1 %[f5], 52(%[a]) \n\t" - "lwc1 %[f6], 56(%[a]) \n\t" - "lwc1 %[f7], 60(%[a]) \n\t" - "add.s %[f8], %[f4], %[f6] \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "add.s %[f6], %[f1], %[f3] \n\t" - "sub.s %[f1], %[f1], %[f3] \n\t" - "add.s %[f3], %[f0], %[f2] \n\t" - "sub.s %[f0], %[f0], %[f2] \n\t" - "add.s %[f2], %[f5], %[f7] \n\t" - "sub.s %[f5], %[f5], %[f7] \n\t" - "add.s %[f7], %[f4], %[f1] \n\t" - "sub.s %[f4], %[f4], %[f1] \n\t" - "add.s %[f1], %[f3], %[f8] \n\t" - "sub.s %[f3], %[f3], %[f8] \n\t" - "sub.s %[f8], %[f0], %[f5] \n\t" - "add.s %[f0], %[f0], %[f5] \n\t" - "add.s %[f5], %[f6], %[f2] \n\t" - "sub.s %[f6], %[f2], %[f6] \n\t" - "lwc1 %[f9], 8(%[rdft_w]) \n\t" - "sub.s %[f2], %[f8], %[f7] \n\t" - "add.s %[f8], %[f8], %[f7] \n\t" - "sub.s %[f7], %[f4], %[f0] \n\t" - "add.s %[f4], %[f4], %[f0] \n\t" - // prepare for loop - "addiu %[a_ptr], %[a], 64 \n\t" - "addiu %[p1_rdft], %[rdft_w], 8 \n\t" - "addiu %[p2_rdft], %[rdft_w], 16 \n\t" - "addiu %[count], $zero, 7 \n\t" - // finish second 8 - "mul.s %[f2], %[f9], %[f2] \n\t" - "mul.s %[f8], %[f9], %[f8] \n\t" - "mul.s %[f7], %[f9], %[f7] \n\t" - "mul.s %[f4], %[f9], %[f4] \n\t" - "swc1 %[f1], 32(%[a]) \n\t" - "swc1 %[f3], 52(%[a]) \n\t" - "swc1 %[f5], 36(%[a]) \n\t" - "swc1 %[f6], 48(%[a]) \n\t" - "swc1 %[f2], 40(%[a]) \n\t" - "swc1 %[f8], 44(%[a]) \n\t" - "swc1 %[f7], 56(%[a]) \n\t" - "swc1 %[f4], 60(%[a]) \n\t" - // loop - "1: \n\t" - "lwc1 %[f0], 0(%[a_ptr]) \n\t" - "lwc1 %[f1], 4(%[a_ptr]) \n\t" - "lwc1 %[f2], 8(%[a_ptr]) \n\t" - "lwc1 %[f3], 12(%[a_ptr]) \n\t" - "lwc1 %[f4], 16(%[a_ptr]) \n\t" - "lwc1 %[f5], 20(%[a_ptr]) \n\t" - "lwc1 %[f6], 24(%[a_ptr]) \n\t" - "lwc1 %[f7], 28(%[a_ptr]) \n\t" - "add.s %[f8], %[f0], %[f2] \n\t" - "sub.s %[f0], %[f0], %[f2] \n\t" - "add.s %[f2], %[f4], %[f6] \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "add.s %[f6], %[f1], %[f3] \n\t" - "sub.s %[f1], %[f1], %[f3] \n\t" - "add.s %[f3], %[f5], %[f7] \n\t" - "sub.s %[f5], %[f5], %[f7] \n\t" - "lwc1 %[f10], 4(%[p1_rdft]) \n\t" - "lwc1 %[f11], 0(%[p2_rdft]) \n\t" - "lwc1 %[f12], 4(%[p2_rdft]) \n\t" - "lwc1 %[f13], 8(%[first]) \n\t" - "lwc1 %[f14], 12(%[first]) \n\t" - "add.s %[f7], %[f8], %[f2] \n\t" - "sub.s %[f8], %[f8], %[f2] \n\t" - "add.s %[f2], %[f6], %[f3] \n\t" - "sub.s %[f6], %[f6], %[f3] \n\t" - "add.s %[f3], %[f0], %[f5] \n\t" - "sub.s %[f0], %[f0], %[f5] \n\t" - "add.s %[f5], %[f1], %[f4] \n\t" - "sub.s %[f1], %[f1], %[f4] \n\t" - "swc1 %[f7], 0(%[a_ptr]) \n\t" - "swc1 %[f2], 4(%[a_ptr]) \n\t" - "mul.s %[f4], %[f9], %[f8] \n\t" -#if defined(MIPS32_R2_LE) - "mul.s %[f8], %[f10], %[f8] \n\t" - "mul.s %[f7], %[f11], %[f0] \n\t" - "mul.s %[f0], %[f12], %[f0] \n\t" - "mul.s %[f2], %[f13], %[f3] \n\t" - "mul.s %[f3], %[f14], %[f3] \n\t" - "nmsub.s %[f4], %[f4], %[f10], %[f6] \n\t" - "madd.s %[f8], %[f8], %[f9], %[f6] \n\t" - "nmsub.s %[f7], %[f7], %[f12], %[f5] \n\t" - "madd.s %[f0], %[f0], %[f11], %[f5] \n\t" - "nmsub.s %[f2], %[f2], %[f14], %[f1] \n\t" - "madd.s %[f3], %[f3], %[f13], %[f1] \n\t" -#else - "mul.s %[f7], %[f10], %[f6] \n\t" - "mul.s %[f6], %[f9], %[f6] \n\t" - "mul.s %[f8], %[f10], %[f8] \n\t" - "mul.s %[f2], %[f11], %[f0] \n\t" - "mul.s %[f11], %[f11], %[f5] \n\t" - "mul.s %[f5], %[f12], %[f5] \n\t" - "mul.s %[f0], %[f12], %[f0] \n\t" - "mul.s %[f12], %[f13], %[f3] \n\t" - "mul.s %[f13], %[f13], %[f1] \n\t" - "mul.s %[f1], %[f14], %[f1] \n\t" - "mul.s %[f3], %[f14], %[f3] \n\t" - "sub.s %[f4], %[f4], %[f7] \n\t" - "add.s %[f8], %[f6], %[f8] \n\t" - "sub.s %[f7], %[f2], %[f5] \n\t" - "add.s %[f0], %[f11], %[f0] \n\t" - "sub.s %[f2], %[f12], %[f1] \n\t" - "add.s %[f3], %[f13], %[f3] \n\t" -#endif - "swc1 %[f4], 16(%[a_ptr]) \n\t" - "swc1 %[f8], 20(%[a_ptr]) \n\t" - "swc1 %[f7], 8(%[a_ptr]) \n\t" - "swc1 %[f0], 12(%[a_ptr]) \n\t" - "swc1 %[f2], 24(%[a_ptr]) \n\t" - "swc1 %[f3], 28(%[a_ptr]) \n\t" - "lwc1 %[f0], 32(%[a_ptr]) \n\t" - "lwc1 %[f1], 36(%[a_ptr]) \n\t" - "lwc1 %[f2], 40(%[a_ptr]) \n\t" - "lwc1 %[f3], 44(%[a_ptr]) \n\t" - "lwc1 %[f4], 48(%[a_ptr]) \n\t" - "lwc1 %[f5], 52(%[a_ptr]) \n\t" - "lwc1 %[f6], 56(%[a_ptr]) \n\t" - "lwc1 %[f7], 60(%[a_ptr]) \n\t" - "add.s %[f8], %[f0], %[f2] \n\t" - "sub.s %[f0], %[f0], %[f2] \n\t" - "add.s %[f2], %[f4], %[f6] \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "add.s %[f6], %[f1], %[f3] \n\t" - "sub.s %[f1], %[f1], %[f3] \n\t" - "add.s %[f3], %[f5], %[f7] \n\t" - "sub.s %[f5], %[f5], %[f7] \n\t" - "lwc1 %[f11], 8(%[p2_rdft]) \n\t" - "lwc1 %[f12], 12(%[p2_rdft]) \n\t" - "lwc1 %[f13], 8(%[second]) \n\t" - "lwc1 %[f14], 12(%[second]) \n\t" - "add.s %[f7], %[f8], %[f2] \n\t" - "sub.s %[f8], %[f2], %[f8] \n\t" - "add.s %[f2], %[f6], %[f3] \n\t" - "sub.s %[f6], %[f3], %[f6] \n\t" - "add.s %[f3], %[f0], %[f5] \n\t" - "sub.s %[f0], %[f0], %[f5] \n\t" - "add.s %[f5], %[f1], %[f4] \n\t" - "sub.s %[f1], %[f1], %[f4] \n\t" - "swc1 %[f7], 32(%[a_ptr]) \n\t" - "swc1 %[f2], 36(%[a_ptr]) \n\t" - "mul.s %[f4], %[f10], %[f8] \n\t" -#if defined(MIPS32_R2_LE) - "mul.s %[f10], %[f10], %[f6] \n\t" - "mul.s %[f7], %[f11], %[f0] \n\t" - "mul.s %[f11], %[f11], %[f5] \n\t" - "mul.s %[f2], %[f13], %[f3] \n\t" - "mul.s %[f13], %[f13], %[f1] \n\t" - "madd.s %[f4], %[f4], %[f9], %[f6] \n\t" - "nmsub.s %[f10], %[f10], %[f9], %[f8] \n\t" - "nmsub.s %[f7], %[f7], %[f12], %[f5] \n\t" - "madd.s %[f11], %[f11], %[f12], %[f0] \n\t" - "nmsub.s %[f2], %[f2], %[f14], %[f1] \n\t" - "madd.s %[f13], %[f13], %[f14], %[f3] \n\t" -#else - "mul.s %[f2], %[f9], %[f6] \n\t" - "mul.s %[f10], %[f10], %[f6] \n\t" - "mul.s %[f9], %[f9], %[f8] \n\t" - "mul.s %[f7], %[f11], %[f0] \n\t" - "mul.s %[f8], %[f12], %[f5] \n\t" - "mul.s %[f11], %[f11], %[f5] \n\t" - "mul.s %[f12], %[f12], %[f0] \n\t" - "mul.s %[f5], %[f13], %[f3] \n\t" - "mul.s %[f0], %[f14], %[f1] \n\t" - "mul.s %[f13], %[f13], %[f1] \n\t" - "mul.s %[f14], %[f14], %[f3] \n\t" - "add.s %[f4], %[f4], %[f2] \n\t" - "sub.s %[f10], %[f10], %[f9] \n\t" - "sub.s %[f7], %[f7], %[f8] \n\t" - "add.s %[f11], %[f11], %[f12] \n\t" - "sub.s %[f2], %[f5], %[f0] \n\t" - "add.s %[f13], %[f13], %[f14] \n\t" -#endif - "swc1 %[f4], 48(%[a_ptr]) \n\t" - "swc1 %[f10], 52(%[a_ptr]) \n\t" - "swc1 %[f7], 40(%[a_ptr]) \n\t" - "swc1 %[f11], 44(%[a_ptr]) \n\t" - "swc1 %[f2], 56(%[a_ptr]) \n\t" - "swc1 %[f13], 60(%[a_ptr]) \n\t" - "addiu %[count], %[count], -1 \n\t" - "lwc1 %[f9], 8(%[p1_rdft]) \n\t" - "addiu %[a_ptr], %[a_ptr], 64 \n\t" - "addiu %[p1_rdft], %[p1_rdft], 8 \n\t" - "addiu %[p2_rdft], %[p2_rdft], 16 \n\t" - "addiu %[first], %[first], 8 \n\t" - "bgtz %[count], 1b \n\t" - " addiu %[second], %[second], 8 \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), - [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), - [f8] "=&f" (f8), [f9] "=&f" (f9), [f10] "=&f" (f10), [f11] "=&f" (f11), - [f12] "=&f" (f12), [f13] "=&f" (f13), [f14] "=&f" (f14), - [a_ptr] "=&r" (a_ptr), [p1_rdft] "=&r" (p1_rdft), [first] "+r" (first), - [p2_rdft] "=&r" (p2_rdft), [count] "=&r" (count), [second] "+r" (second) - : [a] "r" (a), [rdft_w] "r" (rdft_w) - : "memory" - ); -} - -static void cftmdl_128_mips(float* a) { - float f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14; - int tmp_a, count; - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[tmp_a], %[a], 0 \n\t" - "addiu %[count], $zero, 4 \n\t" - "1: \n\t" - "addiu %[count], %[count], -1 \n\t" - "lwc1 %[f0], 0(%[tmp_a]) \n\t" - "lwc1 %[f2], 32(%[tmp_a]) \n\t" - "lwc1 %[f4], 64(%[tmp_a]) \n\t" - "lwc1 %[f6], 96(%[tmp_a]) \n\t" - "lwc1 %[f1], 4(%[tmp_a]) \n\t" - "lwc1 %[f3], 36(%[tmp_a]) \n\t" - "lwc1 %[f5], 68(%[tmp_a]) \n\t" - "lwc1 %[f7], 100(%[tmp_a]) \n\t" - "add.s %[f8], %[f0], %[f2] \n\t" - "sub.s %[f0], %[f0], %[f2] \n\t" - "add.s %[f2], %[f4], %[f6] \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "add.s %[f6], %[f1], %[f3] \n\t" - "sub.s %[f1], %[f1], %[f3] \n\t" - "add.s %[f3], %[f5], %[f7] \n\t" - "sub.s %[f5], %[f5], %[f7] \n\t" - "add.s %[f7], %[f8], %[f2] \n\t" - "sub.s %[f8], %[f8], %[f2] \n\t" - "add.s %[f2], %[f1], %[f4] \n\t" - "sub.s %[f1], %[f1], %[f4] \n\t" - "add.s %[f4], %[f6], %[f3] \n\t" - "sub.s %[f6], %[f6], %[f3] \n\t" - "sub.s %[f3], %[f0], %[f5] \n\t" - "add.s %[f0], %[f0], %[f5] \n\t" - "swc1 %[f7], 0(%[tmp_a]) \n\t" - "swc1 %[f8], 64(%[tmp_a]) \n\t" - "swc1 %[f2], 36(%[tmp_a]) \n\t" - "swc1 %[f1], 100(%[tmp_a]) \n\t" - "swc1 %[f4], 4(%[tmp_a]) \n\t" - "swc1 %[f6], 68(%[tmp_a]) \n\t" - "swc1 %[f3], 32(%[tmp_a]) \n\t" - "swc1 %[f0], 96(%[tmp_a]) \n\t" - "bgtz %[count], 1b \n\t" - " addiu %[tmp_a], %[tmp_a], 8 \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), - [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), - [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), [count] "=&r" (count) - : [a] "r" (a) - : "memory" - ); - f9 = rdft_w[2]; - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[tmp_a], %[a], 128 \n\t" - "addiu %[count], $zero, 4 \n\t" - "1: \n\t" - "addiu %[count], %[count], -1 \n\t" - "lwc1 %[f0], 0(%[tmp_a]) \n\t" - "lwc1 %[f2], 32(%[tmp_a]) \n\t" - "lwc1 %[f5], 68(%[tmp_a]) \n\t" - "lwc1 %[f7], 100(%[tmp_a]) \n\t" - "lwc1 %[f1], 4(%[tmp_a]) \n\t" - "lwc1 %[f3], 36(%[tmp_a]) \n\t" - "lwc1 %[f4], 64(%[tmp_a]) \n\t" - "lwc1 %[f6], 96(%[tmp_a]) \n\t" - "sub.s %[f8], %[f0], %[f2] \n\t" - "add.s %[f0], %[f0], %[f2] \n\t" - "sub.s %[f2], %[f5], %[f7] \n\t" - "add.s %[f5], %[f5], %[f7] \n\t" - "sub.s %[f7], %[f1], %[f3] \n\t" - "add.s %[f1], %[f1], %[f3] \n\t" - "sub.s %[f3], %[f4], %[f6] \n\t" - "add.s %[f4], %[f4], %[f6] \n\t" - "sub.s %[f6], %[f8], %[f2] \n\t" - "add.s %[f8], %[f8], %[f2] \n\t" - "add.s %[f2], %[f5], %[f1] \n\t" - "sub.s %[f5], %[f5], %[f1] \n\t" - "add.s %[f1], %[f3], %[f7] \n\t" - "sub.s %[f3], %[f3], %[f7] \n\t" - "add.s %[f7], %[f0], %[f4] \n\t" - "sub.s %[f0], %[f0], %[f4] \n\t" - "sub.s %[f4], %[f6], %[f1] \n\t" - "add.s %[f6], %[f6], %[f1] \n\t" - "sub.s %[f1], %[f3], %[f8] \n\t" - "add.s %[f3], %[f3], %[f8] \n\t" - "mul.s %[f4], %[f4], %[f9] \n\t" - "mul.s %[f6], %[f6], %[f9] \n\t" - "mul.s %[f1], %[f1], %[f9] \n\t" - "mul.s %[f3], %[f3], %[f9] \n\t" - "swc1 %[f7], 0(%[tmp_a]) \n\t" - "swc1 %[f2], 4(%[tmp_a]) \n\t" - "swc1 %[f5], 64(%[tmp_a]) \n\t" - "swc1 %[f0], 68(%[tmp_a]) \n\t" - "swc1 %[f4], 32(%[tmp_a]) \n\t" - "swc1 %[f6], 36(%[tmp_a]) \n\t" - "swc1 %[f1], 96(%[tmp_a]) \n\t" - "swc1 %[f3], 100(%[tmp_a]) \n\t" - "bgtz %[count], 1b \n\t" - " addiu %[tmp_a], %[tmp_a], 8 \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), - [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), - [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), [count] "=&r" (count) - : [a] "r" (a), [f9] "f" (f9) - : "memory" - ); - f10 = rdft_w[3]; - f11 = rdft_w[4]; - f12 = rdft_w[5]; - f13 = rdft_wk3ri_first[2]; - f14 = rdft_wk3ri_first[3]; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[tmp_a], %[a], 256 \n\t" - "addiu %[count], $zero, 4 \n\t" - "1: \n\t" - "addiu %[count], %[count], -1 \n\t" - "lwc1 %[f0], 0(%[tmp_a]) \n\t" - "lwc1 %[f2], 32(%[tmp_a]) \n\t" - "lwc1 %[f4], 64(%[tmp_a]) \n\t" - "lwc1 %[f6], 96(%[tmp_a]) \n\t" - "lwc1 %[f1], 4(%[tmp_a]) \n\t" - "lwc1 %[f3], 36(%[tmp_a]) \n\t" - "lwc1 %[f5], 68(%[tmp_a]) \n\t" - "lwc1 %[f7], 100(%[tmp_a]) \n\t" - "add.s %[f8], %[f0], %[f2] \n\t" - "sub.s %[f0], %[f0], %[f2] \n\t" - "add.s %[f2], %[f4], %[f6] \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "add.s %[f6], %[f1], %[f3] \n\t" - "sub.s %[f1], %[f1], %[f3] \n\t" - "add.s %[f3], %[f5], %[f7] \n\t" - "sub.s %[f5], %[f5], %[f7] \n\t" - "sub.s %[f7], %[f8], %[f2] \n\t" - "add.s %[f8], %[f8], %[f2] \n\t" - "add.s %[f2], %[f1], %[f4] \n\t" - "sub.s %[f1], %[f1], %[f4] \n\t" - "sub.s %[f4], %[f6], %[f3] \n\t" - "add.s %[f6], %[f6], %[f3] \n\t" - "sub.s %[f3], %[f0], %[f5] \n\t" - "add.s %[f0], %[f0], %[f5] \n\t" - "swc1 %[f8], 0(%[tmp_a]) \n\t" - "swc1 %[f6], 4(%[tmp_a]) \n\t" - "mul.s %[f5], %[f9], %[f7] \n\t" -#if defined(MIPS32_R2_LE) - "mul.s %[f7], %[f10], %[f7] \n\t" - "mul.s %[f8], %[f11], %[f3] \n\t" - "mul.s %[f3], %[f12], %[f3] \n\t" - "mul.s %[f6], %[f13], %[f0] \n\t" - "mul.s %[f0], %[f14], %[f0] \n\t" - "nmsub.s %[f5], %[f5], %[f10], %[f4] \n\t" - "madd.s %[f7], %[f7], %[f9], %[f4] \n\t" - "nmsub.s %[f8], %[f8], %[f12], %[f2] \n\t" - "madd.s %[f3], %[f3], %[f11], %[f2] \n\t" - "nmsub.s %[f6], %[f6], %[f14], %[f1] \n\t" - "madd.s %[f0], %[f0], %[f13], %[f1] \n\t" - "swc1 %[f5], 64(%[tmp_a]) \n\t" - "swc1 %[f7], 68(%[tmp_a]) \n\t" -#else - "mul.s %[f8], %[f10], %[f4] \n\t" - "mul.s %[f4], %[f9], %[f4] \n\t" - "mul.s %[f7], %[f10], %[f7] \n\t" - "mul.s %[f6], %[f11], %[f3] \n\t" - "mul.s %[f3], %[f12], %[f3] \n\t" - "sub.s %[f5], %[f5], %[f8] \n\t" - "mul.s %[f8], %[f12], %[f2] \n\t" - "mul.s %[f2], %[f11], %[f2] \n\t" - "add.s %[f7], %[f4], %[f7] \n\t" - "mul.s %[f4], %[f13], %[f0] \n\t" - "mul.s %[f0], %[f14], %[f0] \n\t" - "sub.s %[f8], %[f6], %[f8] \n\t" - "mul.s %[f6], %[f14], %[f1] \n\t" - "mul.s %[f1], %[f13], %[f1] \n\t" - "add.s %[f3], %[f2], %[f3] \n\t" - "swc1 %[f5], 64(%[tmp_a]) \n\t" - "swc1 %[f7], 68(%[tmp_a]) \n\t" - "sub.s %[f6], %[f4], %[f6] \n\t" - "add.s %[f0], %[f1], %[f0] \n\t" -#endif - "swc1 %[f8], 32(%[tmp_a]) \n\t" - "swc1 %[f3], 36(%[tmp_a]) \n\t" - "swc1 %[f6], 96(%[tmp_a]) \n\t" - "swc1 %[f0], 100(%[tmp_a]) \n\t" - "bgtz %[count], 1b \n\t" - " addiu %[tmp_a], %[tmp_a], 8 \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), - [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), - [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), [count] "=&r" (count) - : [a] "r" (a), [f9] "f" (f9), [f10] "f" (f10), [f11] "f" (f11), - [f12] "f" (f12), [f13] "f" (f13), [f14] "f" (f14) - : "memory" - ); - f11 = rdft_w[6]; - f12 = rdft_w[7]; - f13 = rdft_wk3ri_second[2]; - f14 = rdft_wk3ri_second[3]; - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[tmp_a], %[a], 384 \n\t" - "addiu %[count], $zero, 4 \n\t" - "1: \n\t" - "addiu %[count], %[count], -1 \n\t" - "lwc1 %[f0], 0(%[tmp_a]) \n\t" - "lwc1 %[f1], 4(%[tmp_a]) \n\t" - "lwc1 %[f2], 32(%[tmp_a]) \n\t" - "lwc1 %[f3], 36(%[tmp_a]) \n\t" - "lwc1 %[f4], 64(%[tmp_a]) \n\t" - "lwc1 %[f5], 68(%[tmp_a]) \n\t" - "lwc1 %[f6], 96(%[tmp_a]) \n\t" - "lwc1 %[f7], 100(%[tmp_a]) \n\t" - "add.s %[f8], %[f0], %[f2] \n\t" - "sub.s %[f0], %[f0], %[f2] \n\t" - "add.s %[f2], %[f4], %[f6] \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "add.s %[f6], %[f1], %[f3] \n\t" - "sub.s %[f1], %[f1], %[f3] \n\t" - "add.s %[f3], %[f5], %[f7] \n\t" - "sub.s %[f5], %[f5], %[f7] \n\t" - "sub.s %[f7], %[f2], %[f8] \n\t" - "add.s %[f2], %[f2], %[f8] \n\t" - "add.s %[f8], %[f1], %[f4] \n\t" - "sub.s %[f1], %[f1], %[f4] \n\t" - "sub.s %[f4], %[f3], %[f6] \n\t" - "add.s %[f3], %[f3], %[f6] \n\t" - "sub.s %[f6], %[f0], %[f5] \n\t" - "add.s %[f0], %[f0], %[f5] \n\t" - "swc1 %[f2], 0(%[tmp_a]) \n\t" - "swc1 %[f3], 4(%[tmp_a]) \n\t" - "mul.s %[f5], %[f10], %[f7] \n\t" -#if defined(MIPS32_R2_LE) - "mul.s %[f7], %[f9], %[f7] \n\t" - "mul.s %[f2], %[f12], %[f8] \n\t" - "mul.s %[f8], %[f11], %[f8] \n\t" - "mul.s %[f3], %[f14], %[f1] \n\t" - "mul.s %[f1], %[f13], %[f1] \n\t" - "madd.s %[f5], %[f5], %[f9], %[f4] \n\t" - "msub.s %[f7], %[f7], %[f10], %[f4] \n\t" - "msub.s %[f2], %[f2], %[f11], %[f6] \n\t" - "madd.s %[f8], %[f8], %[f12], %[f6] \n\t" - "msub.s %[f3], %[f3], %[f13], %[f0] \n\t" - "madd.s %[f1], %[f1], %[f14], %[f0] \n\t" - "swc1 %[f5], 64(%[tmp_a]) \n\t" - "swc1 %[f7], 68(%[tmp_a]) \n\t" -#else - "mul.s %[f2], %[f9], %[f4] \n\t" - "mul.s %[f4], %[f10], %[f4] \n\t" - "mul.s %[f7], %[f9], %[f7] \n\t" - "mul.s %[f3], %[f11], %[f6] \n\t" - "mul.s %[f6], %[f12], %[f6] \n\t" - "add.s %[f5], %[f5], %[f2] \n\t" - "sub.s %[f7], %[f4], %[f7] \n\t" - "mul.s %[f2], %[f12], %[f8] \n\t" - "mul.s %[f8], %[f11], %[f8] \n\t" - "mul.s %[f4], %[f14], %[f1] \n\t" - "mul.s %[f1], %[f13], %[f1] \n\t" - "sub.s %[f2], %[f3], %[f2] \n\t" - "mul.s %[f3], %[f13], %[f0] \n\t" - "mul.s %[f0], %[f14], %[f0] \n\t" - "add.s %[f8], %[f8], %[f6] \n\t" - "swc1 %[f5], 64(%[tmp_a]) \n\t" - "swc1 %[f7], 68(%[tmp_a]) \n\t" - "sub.s %[f3], %[f3], %[f4] \n\t" - "add.s %[f1], %[f1], %[f0] \n\t" -#endif - "swc1 %[f2], 32(%[tmp_a]) \n\t" - "swc1 %[f8], 36(%[tmp_a]) \n\t" - "swc1 %[f3], 96(%[tmp_a]) \n\t" - "swc1 %[f1], 100(%[tmp_a]) \n\t" - "bgtz %[count], 1b \n\t" - " addiu %[tmp_a], %[tmp_a], 8 \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), - [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), - [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), [count] "=&r" (count) - : [a] "r" (a), [f9] "f" (f9), [f10] "f" (f10), [f11] "f" (f11), - [f12] "f" (f12), [f13] "f" (f13), [f14] "f" (f14) - : "memory" - ); -} - -static void cftfsub_128_mips(float* a) { - float f0, f1, f2, f3, f4, f5, f6, f7, f8; - int tmp_a, count; - - cft1st_128(a); - cftmdl_128(a); - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[tmp_a], %[a], 0 \n\t" - "addiu %[count], $zero, 16 \n\t" - "1: \n\t" - "addiu %[count], %[count], -1 \n\t" - "lwc1 %[f0], 0(%[tmp_a]) \n\t" - "lwc1 %[f2], 128(%[tmp_a]) \n\t" - "lwc1 %[f4], 256(%[tmp_a]) \n\t" - "lwc1 %[f6], 384(%[tmp_a]) \n\t" - "lwc1 %[f1], 4(%[tmp_a]) \n\t" - "lwc1 %[f3], 132(%[tmp_a]) \n\t" - "lwc1 %[f5], 260(%[tmp_a]) \n\t" - "lwc1 %[f7], 388(%[tmp_a]) \n\t" - "add.s %[f8], %[f0], %[f2] \n\t" - "sub.s %[f0], %[f0], %[f2] \n\t" - "add.s %[f2], %[f4], %[f6] \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "add.s %[f6], %[f1], %[f3] \n\t" - "sub.s %[f1], %[f1], %[f3] \n\t" - "add.s %[f3], %[f5], %[f7] \n\t" - "sub.s %[f5], %[f5], %[f7] \n\t" - "add.s %[f7], %[f8], %[f2] \n\t" - "sub.s %[f8], %[f8], %[f2] \n\t" - "add.s %[f2], %[f1], %[f4] \n\t" - "sub.s %[f1], %[f1], %[f4] \n\t" - "add.s %[f4], %[f6], %[f3] \n\t" - "sub.s %[f6], %[f6], %[f3] \n\t" - "sub.s %[f3], %[f0], %[f5] \n\t" - "add.s %[f0], %[f0], %[f5] \n\t" - "swc1 %[f7], 0(%[tmp_a]) \n\t" - "swc1 %[f8], 256(%[tmp_a]) \n\t" - "swc1 %[f2], 132(%[tmp_a]) \n\t" - "swc1 %[f1], 388(%[tmp_a]) \n\t" - "swc1 %[f4], 4(%[tmp_a]) \n\t" - "swc1 %[f6], 260(%[tmp_a]) \n\t" - "swc1 %[f3], 128(%[tmp_a]) \n\t" - "swc1 %[f0], 384(%[tmp_a]) \n\t" - "bgtz %[count], 1b \n\t" - " addiu %[tmp_a], %[tmp_a], 8 \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), - [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), - [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), - [count] "=&r" (count) - : [a] "r" (a) - : "memory" - ); -} - -static void cftbsub_128_mips(float* a) { - float f0, f1, f2, f3, f4, f5, f6, f7, f8; - int tmp_a, count; - - cft1st_128(a); - cftmdl_128(a); - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[tmp_a], %[a], 0 \n\t" - "addiu %[count], $zero, 16 \n\t" - "1: \n\t" - "addiu %[count], %[count], -1 \n\t" - "lwc1 %[f0], 0(%[tmp_a]) \n\t" - "lwc1 %[f2], 128(%[tmp_a]) \n\t" - "lwc1 %[f4], 256(%[tmp_a]) \n\t" - "lwc1 %[f6], 384(%[tmp_a]) \n\t" - "lwc1 %[f1], 4(%[tmp_a]) \n\t" - "lwc1 %[f3], 132(%[tmp_a]) \n\t" - "lwc1 %[f5], 260(%[tmp_a]) \n\t" - "lwc1 %[f7], 388(%[tmp_a]) \n\t" - "add.s %[f8], %[f0], %[f2] \n\t" - "sub.s %[f0], %[f0], %[f2] \n\t" - "add.s %[f2], %[f4], %[f6] \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "add.s %[f6], %[f1], %[f3] \n\t" - "sub.s %[f1], %[f3], %[f1] \n\t" - "add.s %[f3], %[f5], %[f7] \n\t" - "sub.s %[f5], %[f5], %[f7] \n\t" - "add.s %[f7], %[f8], %[f2] \n\t" - "sub.s %[f8], %[f8], %[f2] \n\t" - "sub.s %[f2], %[f1], %[f4] \n\t" - "add.s %[f1], %[f1], %[f4] \n\t" - "add.s %[f4], %[f3], %[f6] \n\t" - "sub.s %[f6], %[f3], %[f6] \n\t" - "sub.s %[f3], %[f0], %[f5] \n\t" - "add.s %[f0], %[f0], %[f5] \n\t" - "neg.s %[f4], %[f4] \n\t" - "swc1 %[f7], 0(%[tmp_a]) \n\t" - "swc1 %[f8], 256(%[tmp_a]) \n\t" - "swc1 %[f2], 132(%[tmp_a]) \n\t" - "swc1 %[f1], 388(%[tmp_a]) \n\t" - "swc1 %[f6], 260(%[tmp_a]) \n\t" - "swc1 %[f3], 128(%[tmp_a]) \n\t" - "swc1 %[f0], 384(%[tmp_a]) \n\t" - "swc1 %[f4], 4(%[tmp_a]) \n\t" - "bgtz %[count], 1b \n\t" - " addiu %[tmp_a], %[tmp_a], 8 \n\t" - ".set pop \n\t" - : [f0] "=&f" (f0), [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), - [f4] "=&f" (f4), [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), - [f8] "=&f" (f8), [tmp_a] "=&r" (tmp_a), [count] "=&r" (count) - : [a] "r" (a) - : "memory" - ); -} - -static void rftfsub_128_mips(float* a) { - const float* c = rdft_w + 32; - const float f0 = 0.5f; - float* a1 = &a[2]; - float* a2 = &a[126]; - const float* c1 = &c[1]; - const float* c2 = &c[31]; - float f1, f2, f3 ,f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15; - int count; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "lwc1 %[f6], 0(%[c2]) \n\t" - "lwc1 %[f1], 0(%[a1]) \n\t" - "lwc1 %[f2], 0(%[a2]) \n\t" - "lwc1 %[f3], 4(%[a1]) \n\t" - "lwc1 %[f4], 4(%[a2]) \n\t" - "lwc1 %[f5], 0(%[c1]) \n\t" - "sub.s %[f6], %[f0], %[f6] \n\t" - "sub.s %[f7], %[f1], %[f2] \n\t" - "add.s %[f8], %[f3], %[f4] \n\t" - "addiu %[count], $zero, 15 \n\t" - "mul.s %[f9], %[f6], %[f7] \n\t" - "mul.s %[f6], %[f6], %[f8] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f8], %[f5], %[f8] \n\t" - "mul.s %[f5], %[f5], %[f7] \n\t" - "sub.s %[f9], %[f9], %[f8] \n\t" - "add.s %[f6], %[f6], %[f5] \n\t" -#else - "nmsub.s %[f9], %[f9], %[f5], %[f8] \n\t" - "madd.s %[f6], %[f6], %[f5], %[f7] \n\t" -#endif - "sub.s %[f1], %[f1], %[f9] \n\t" - "add.s %[f2], %[f2], %[f9] \n\t" - "sub.s %[f3], %[f3], %[f6] \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "swc1 %[f1], 0(%[a1]) \n\t" - "swc1 %[f2], 0(%[a2]) \n\t" - "swc1 %[f3], 4(%[a1]) \n\t" - "swc1 %[f4], 4(%[a2]) \n\t" - "addiu %[a1], %[a1], 8 \n\t" - "addiu %[a2], %[a2], -8 \n\t" - "addiu %[c1], %[c1], 4 \n\t" - "addiu %[c2], %[c2], -4 \n\t" - "1: \n\t" - "lwc1 %[f6], 0(%[c2]) \n\t" - "lwc1 %[f1], 0(%[a1]) \n\t" - "lwc1 %[f2], 0(%[a2]) \n\t" - "lwc1 %[f3], 4(%[a1]) \n\t" - "lwc1 %[f4], 4(%[a2]) \n\t" - "lwc1 %[f5], 0(%[c1]) \n\t" - "sub.s %[f6], %[f0], %[f6] \n\t" - "sub.s %[f7], %[f1], %[f2] \n\t" - "add.s %[f8], %[f3], %[f4] \n\t" - "lwc1 %[f10], -4(%[c2]) \n\t" - "lwc1 %[f11], 8(%[a1]) \n\t" - "lwc1 %[f12], -8(%[a2]) \n\t" - "mul.s %[f9], %[f6], %[f7] \n\t" - "mul.s %[f6], %[f6], %[f8] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f8], %[f5], %[f8] \n\t" - "mul.s %[f5], %[f5], %[f7] \n\t" - "lwc1 %[f13], 12(%[a1]) \n\t" - "lwc1 %[f14], -4(%[a2]) \n\t" - "lwc1 %[f15], 4(%[c1]) \n\t" - "sub.s %[f9], %[f9], %[f8] \n\t" - "add.s %[f6], %[f6], %[f5] \n\t" -#else - "lwc1 %[f13], 12(%[a1]) \n\t" - "lwc1 %[f14], -4(%[a2]) \n\t" - "lwc1 %[f15], 4(%[c1]) \n\t" - "nmsub.s %[f9], %[f9], %[f5], %[f8] \n\t" - "madd.s %[f6], %[f6], %[f5], %[f7] \n\t" -#endif - "sub.s %[f10], %[f0], %[f10] \n\t" - "sub.s %[f5], %[f11], %[f12] \n\t" - "add.s %[f7], %[f13], %[f14] \n\t" - "sub.s %[f1], %[f1], %[f9] \n\t" - "add.s %[f2], %[f2], %[f9] \n\t" - "sub.s %[f3], %[f3], %[f6] \n\t" - "mul.s %[f8], %[f10], %[f5] \n\t" - "mul.s %[f10], %[f10], %[f7] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f9], %[f15], %[f7] \n\t" - "mul.s %[f15], %[f15], %[f5] \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "swc1 %[f1], 0(%[a1]) \n\t" - "swc1 %[f2], 0(%[a2]) \n\t" - "sub.s %[f8], %[f8], %[f9] \n\t" - "add.s %[f10], %[f10], %[f15] \n\t" -#else - "swc1 %[f1], 0(%[a1]) \n\t" - "swc1 %[f2], 0(%[a2]) \n\t" - "sub.s %[f4], %[f4], %[f6] \n\t" - "nmsub.s %[f8], %[f8], %[f15], %[f7] \n\t" - "madd.s %[f10], %[f10], %[f15], %[f5] \n\t" -#endif - "swc1 %[f3], 4(%[a1]) \n\t" - "swc1 %[f4], 4(%[a2]) \n\t" - "sub.s %[f11], %[f11], %[f8] \n\t" - "add.s %[f12], %[f12], %[f8] \n\t" - "sub.s %[f13], %[f13], %[f10] \n\t" - "sub.s %[f14], %[f14], %[f10] \n\t" - "addiu %[c2], %[c2], -8 \n\t" - "addiu %[c1], %[c1], 8 \n\t" - "swc1 %[f11], 8(%[a1]) \n\t" - "swc1 %[f12], -8(%[a2]) \n\t" - "swc1 %[f13], 12(%[a1]) \n\t" - "swc1 %[f14], -4(%[a2]) \n\t" - "addiu %[a1], %[a1], 16 \n\t" - "addiu %[count], %[count], -1 \n\t" - "bgtz %[count], 1b \n\t" - " addiu %[a2], %[a2], -16 \n\t" - ".set pop \n\t" - : [a1] "+r" (a1), [a2] "+r" (a2), [c1] "+r" (c1), [c2] "+r" (c2), - [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), [f4] "=&f" (f4), - [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), [f8] "=&f" (f8), - [f9] "=&f" (f9), [f10] "=&f" (f10), [f11] "=&f" (f11), [f12] "=&f" (f12), - [f13] "=&f" (f13), [f14] "=&f" (f14), [f15] "=&f" (f15), - [count] "=&r" (count) - : [f0] "f" (f0) - : "memory" - ); -} - -static void rftbsub_128_mips(float* a) { - const float *c = rdft_w + 32; - const float f0 = 0.5f; - float* a1 = &a[2]; - float* a2 = &a[126]; - const float* c1 = &c[1]; - const float* c2 = &c[31]; - float f1, f2, f3 ,f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15; - int count; - - a[1] = -a[1]; - a[65] = -a[65]; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "lwc1 %[f6], 0(%[c2]) \n\t" - "lwc1 %[f1], 0(%[a1]) \n\t" - "lwc1 %[f2], 0(%[a2]) \n\t" - "lwc1 %[f3], 4(%[a1]) \n\t" - "lwc1 %[f4], 4(%[a2]) \n\t" - "lwc1 %[f5], 0(%[c1]) \n\t" - "sub.s %[f6], %[f0], %[f6] \n\t" - "sub.s %[f7], %[f1], %[f2] \n\t" - "add.s %[f8], %[f3], %[f4] \n\t" - "addiu %[count], $zero, 15 \n\t" - "mul.s %[f9], %[f6], %[f7] \n\t" - "mul.s %[f6], %[f6], %[f8] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f8], %[f5], %[f8] \n\t" - "mul.s %[f5], %[f5], %[f7] \n\t" - "add.s %[f9], %[f9], %[f8] \n\t" - "sub.s %[f6], %[f6], %[f5] \n\t" -#else - "madd.s %[f9], %[f9], %[f5], %[f8] \n\t" - "nmsub.s %[f6], %[f6], %[f5], %[f7] \n\t" -#endif - "sub.s %[f1], %[f1], %[f9] \n\t" - "add.s %[f2], %[f2], %[f9] \n\t" - "sub.s %[f3], %[f6], %[f3] \n\t" - "sub.s %[f4], %[f6], %[f4] \n\t" - "swc1 %[f1], 0(%[a1]) \n\t" - "swc1 %[f2], 0(%[a2]) \n\t" - "swc1 %[f3], 4(%[a1]) \n\t" - "swc1 %[f4], 4(%[a2]) \n\t" - "addiu %[a1], %[a1], 8 \n\t" - "addiu %[a2], %[a2], -8 \n\t" - "addiu %[c1], %[c1], 4 \n\t" - "addiu %[c2], %[c2], -4 \n\t" - "1: \n\t" - "lwc1 %[f6], 0(%[c2]) \n\t" - "lwc1 %[f1], 0(%[a1]) \n\t" - "lwc1 %[f2], 0(%[a2]) \n\t" - "lwc1 %[f3], 4(%[a1]) \n\t" - "lwc1 %[f4], 4(%[a2]) \n\t" - "lwc1 %[f5], 0(%[c1]) \n\t" - "sub.s %[f6], %[f0], %[f6] \n\t" - "sub.s %[f7], %[f1], %[f2] \n\t" - "add.s %[f8], %[f3], %[f4] \n\t" - "lwc1 %[f10], -4(%[c2]) \n\t" - "lwc1 %[f11], 8(%[a1]) \n\t" - "lwc1 %[f12], -8(%[a2]) \n\t" - "mul.s %[f9], %[f6], %[f7] \n\t" - "mul.s %[f6], %[f6], %[f8] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f8], %[f5], %[f8] \n\t" - "mul.s %[f5], %[f5], %[f7] \n\t" - "lwc1 %[f13], 12(%[a1]) \n\t" - "lwc1 %[f14], -4(%[a2]) \n\t" - "lwc1 %[f15], 4(%[c1]) \n\t" - "add.s %[f9], %[f9], %[f8] \n\t" - "sub.s %[f6], %[f6], %[f5] \n\t" -#else - "lwc1 %[f13], 12(%[a1]) \n\t" - "lwc1 %[f14], -4(%[a2]) \n\t" - "lwc1 %[f15], 4(%[c1]) \n\t" - "madd.s %[f9], %[f9], %[f5], %[f8] \n\t" - "nmsub.s %[f6], %[f6], %[f5], %[f7] \n\t" -#endif - "sub.s %[f10], %[f0], %[f10] \n\t" - "sub.s %[f5], %[f11], %[f12] \n\t" - "add.s %[f7], %[f13], %[f14] \n\t" - "sub.s %[f1], %[f1], %[f9] \n\t" - "add.s %[f2], %[f2], %[f9] \n\t" - "sub.s %[f3], %[f6], %[f3] \n\t" - "mul.s %[f8], %[f10], %[f5] \n\t" - "mul.s %[f10], %[f10], %[f7] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f9], %[f15], %[f7] \n\t" - "mul.s %[f15], %[f15], %[f5] \n\t" - "sub.s %[f4], %[f6], %[f4] \n\t" - "swc1 %[f1], 0(%[a1]) \n\t" - "swc1 %[f2], 0(%[a2]) \n\t" - "add.s %[f8], %[f8], %[f9] \n\t" - "sub.s %[f10], %[f10], %[f15] \n\t" -#else - "swc1 %[f1], 0(%[a1]) \n\t" - "swc1 %[f2], 0(%[a2]) \n\t" - "sub.s %[f4], %[f6], %[f4] \n\t" - "madd.s %[f8], %[f8], %[f15], %[f7] \n\t" - "nmsub.s %[f10], %[f10], %[f15], %[f5] \n\t" -#endif - "swc1 %[f3], 4(%[a1]) \n\t" - "swc1 %[f4], 4(%[a2]) \n\t" - "sub.s %[f11], %[f11], %[f8] \n\t" - "add.s %[f12], %[f12], %[f8] \n\t" - "sub.s %[f13], %[f10], %[f13] \n\t" - "sub.s %[f14], %[f10], %[f14] \n\t" - "addiu %[c2], %[c2], -8 \n\t" - "addiu %[c1], %[c1], 8 \n\t" - "swc1 %[f11], 8(%[a1]) \n\t" - "swc1 %[f12], -8(%[a2]) \n\t" - "swc1 %[f13], 12(%[a1]) \n\t" - "swc1 %[f14], -4(%[a2]) \n\t" - "addiu %[a1], %[a1], 16 \n\t" - "addiu %[count], %[count], -1 \n\t" - "bgtz %[count], 1b \n\t" - " addiu %[a2], %[a2], -16 \n\t" - ".set pop \n\t" - : [a1] "+r" (a1), [a2] "+r" (a2), [c1] "+r" (c1), [c2] "+r" (c2), - [f1] "=&f" (f1), [f2] "=&f" (f2), [f3] "=&f" (f3), [f4] "=&f" (f4), - [f5] "=&f" (f5), [f6] "=&f" (f6), [f7] "=&f" (f7), [f8] "=&f" (f8), - [f9] "=&f" (f9), [f10] "=&f" (f10), [f11] "=&f" (f11), [f12] "=&f" (f12), - [f13] "=&f" (f13), [f14] "=&f" (f14), [f15] "=&f" (f15), - [count] "=&r" (count) - : [f0] "f" (f0) - : "memory" - ); -} - -void aec_rdft_init_mips(void) { - cft1st_128 = cft1st_128_mips; - cftmdl_128 = cftmdl_128_mips; - rftfsub_128 = rftfsub_128_mips; - rftbsub_128 = rftbsub_128_mips; - cftfsub_128 = cftfsub_128_mips; - cftbsub_128 = cftbsub_128_mips; - bitrv2_128 = bitrv2_128_mips; -} diff --git a/webrtc/modules/audio_processing/aec/aec_resampler.c b/webrtc/modules/audio_processing/aec/aec_resampler.c deleted file mode 100644 index 99c39ef..0000000 --- a/webrtc/modules/audio_processing/aec/aec_resampler.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -/* Resamples a signal to an arbitrary rate. Used by the AEC to compensate for - * clock skew by resampling the farend signal. - */ - -#include "webrtc/modules/audio_processing/aec/aec_resampler.h" - -#include -#include -#include -#include - -#include "webrtc/modules/audio_processing/aec/aec_core.h" - -enum { - kEstimateLengthFrames = 400 -}; - -typedef struct { - float buffer[kResamplerBufferSize]; - float position; - - int deviceSampleRateHz; - int skewData[kEstimateLengthFrames]; - int skewDataIndex; - float skewEstimate; -} AecResampler; - -static int EstimateSkew(const int* rawSkew, - int size, - int absLimit, - float* skewEst); - -void* WebRtcAec_CreateResampler() { - return malloc(sizeof(AecResampler)); -} - -int WebRtcAec_InitResampler(void* resampInst, int deviceSampleRateHz) { - AecResampler* obj = (AecResampler*)resampInst; - memset(obj->buffer, 0, sizeof(obj->buffer)); - obj->position = 0.0; - - obj->deviceSampleRateHz = deviceSampleRateHz; - memset(obj->skewData, 0, sizeof(obj->skewData)); - obj->skewDataIndex = 0; - obj->skewEstimate = 0.0; - - return 0; -} - -void WebRtcAec_FreeResampler(void* resampInst) { - AecResampler* obj = (AecResampler*)resampInst; - free(obj); -} - -void WebRtcAec_ResampleLinear(void* resampInst, - const float* inspeech, - size_t size, - float skew, - float* outspeech, - size_t* size_out) { - AecResampler* obj = (AecResampler*)resampInst; - - float* y; - float be, tnew; - size_t tn, mm; - - assert(size <= 2 * FRAME_LEN); - assert(resampInst != NULL); - assert(inspeech != NULL); - assert(outspeech != NULL); - assert(size_out != NULL); - - // Add new frame data in lookahead - memcpy(&obj->buffer[FRAME_LEN + kResamplingDelay], - inspeech, - size * sizeof(inspeech[0])); - - // Sample rate ratio - be = 1 + skew; - - // Loop over input frame - mm = 0; - y = &obj->buffer[FRAME_LEN]; // Point at current frame - - tnew = be * mm + obj->position; - tn = (size_t)tnew; - - while (tn < size) { - - // Interpolation - outspeech[mm] = y[tn] + (tnew - tn) * (y[tn + 1] - y[tn]); - mm++; - - tnew = be * mm + obj->position; - tn = (int)tnew; - } - - *size_out = mm; - obj->position += (*size_out) * be - size; - - // Shift buffer - memmove(obj->buffer, - &obj->buffer[size], - (kResamplerBufferSize - size) * sizeof(obj->buffer[0])); -} - -int WebRtcAec_GetSkew(void* resampInst, int rawSkew, float* skewEst) { - AecResampler* obj = (AecResampler*)resampInst; - int err = 0; - - if (obj->skewDataIndex < kEstimateLengthFrames) { - obj->skewData[obj->skewDataIndex] = rawSkew; - obj->skewDataIndex++; - } else if (obj->skewDataIndex == kEstimateLengthFrames) { - err = EstimateSkew( - obj->skewData, kEstimateLengthFrames, obj->deviceSampleRateHz, skewEst); - obj->skewEstimate = *skewEst; - obj->skewDataIndex++; - } else { - *skewEst = obj->skewEstimate; - } - - return err; -} - -int EstimateSkew(const int* rawSkew, - int size, - int deviceSampleRateHz, - float* skewEst) { - const int absLimitOuter = (int)(0.04f * deviceSampleRateHz); - const int absLimitInner = (int)(0.0025f * deviceSampleRateHz); - int i = 0; - int n = 0; - float rawAvg = 0; - float err = 0; - float rawAbsDev = 0; - int upperLimit = 0; - int lowerLimit = 0; - float cumSum = 0; - float x = 0; - float x2 = 0; - float y = 0; - float xy = 0; - float xAvg = 0; - float denom = 0; - float skew = 0; - - *skewEst = 0; // Set in case of error below. - for (i = 0; i < size; i++) { - if ((rawSkew[i] < absLimitOuter && rawSkew[i] > -absLimitOuter)) { - n++; - rawAvg += rawSkew[i]; - } - } - - if (n == 0) { - return -1; - } - assert(n > 0); - rawAvg /= n; - - for (i = 0; i < size; i++) { - if ((rawSkew[i] < absLimitOuter && rawSkew[i] > -absLimitOuter)) { - err = rawSkew[i] - rawAvg; - rawAbsDev += err >= 0 ? err : -err; - } - } - assert(n > 0); - rawAbsDev /= n; - upperLimit = (int)(rawAvg + 5 * rawAbsDev + 1); // +1 for ceiling. - lowerLimit = (int)(rawAvg - 5 * rawAbsDev - 1); // -1 for floor. - - n = 0; - for (i = 0; i < size; i++) { - if ((rawSkew[i] < absLimitInner && rawSkew[i] > -absLimitInner) || - (rawSkew[i] < upperLimit && rawSkew[i] > lowerLimit)) { - n++; - cumSum += rawSkew[i]; - x += n; - x2 += n * n; - y += cumSum; - xy += n * cumSum; - } - } - - if (n == 0) { - return -1; - } - assert(n > 0); - xAvg = x / n; - denom = x2 - xAvg * x; - - if (denom != 0) { - skew = (xy - xAvg * y) / denom; - } - - *skewEst = skew; - return 0; -} diff --git a/webrtc/modules/audio_processing/aec/aec_resampler.h b/webrtc/modules/audio_processing/aec/aec_resampler.h deleted file mode 100644 index a5002c1..0000000 --- a/webrtc/modules/audio_processing/aec/aec_resampler.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_ - -#include "webrtc/modules/audio_processing/aec/aec_core.h" - -enum { - kResamplingDelay = 1 -}; -enum { - kResamplerBufferSize = FRAME_LEN * 4 -}; - -// Unless otherwise specified, functions return 0 on success and -1 on error. -void* WebRtcAec_CreateResampler(); // Returns NULL on error. -int WebRtcAec_InitResampler(void* resampInst, int deviceSampleRateHz); -void WebRtcAec_FreeResampler(void* resampInst); - -// Estimates skew from raw measurement. -int WebRtcAec_GetSkew(void* resampInst, int rawSkew, float* skewEst); - -// Resamples input using linear interpolation. -void WebRtcAec_ResampleLinear(void* resampInst, - const float* inspeech, - size_t size, - float skew, - float* outspeech, - size_t* size_out); - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_ diff --git a/webrtc/modules/audio_processing/aec/echo_cancellation.c b/webrtc/modules/audio_processing/aec/echo_cancellation.c deleted file mode 100644 index 0f5cd31..0000000 --- a/webrtc/modules/audio_processing/aec/echo_cancellation.c +++ /dev/null @@ -1,923 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -/* - * Contains the API functions for the AEC. - */ -#include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h" - -#include -#ifdef WEBRTC_AEC_DEBUG_DUMP -#include -#endif -#include -#include - -#include "webrtc/common_audio/ring_buffer.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/aec/aec_core.h" -#include "webrtc/modules/audio_processing/aec/aec_resampler.h" -#include "webrtc/modules/audio_processing/aec/echo_cancellation_internal.h" -#include "webrtc/typedefs.h" - -// Measured delays [ms] -// Device Chrome GTP -// MacBook Air 10 -// MacBook Retina 10 100 -// MacPro 30? -// -// Win7 Desktop 70 80? -// Win7 T430s 110 -// Win8 T420s 70 -// -// Daisy 50 -// Pixel (w/ preproc?) 240 -// Pixel (w/o preproc?) 110 110 - -// The extended filter mode gives us the flexibility to ignore the system's -// reported delays. We do this for platforms which we believe provide results -// which are incompatible with the AEC's expectations. Based on measurements -// (some provided above) we set a conservative (i.e. lower than measured) -// fixed delay. -// -// WEBRTC_UNTRUSTED_DELAY will only have an impact when |extended_filter_mode| -// is enabled. See the note along with |DelayCorrection| in -// echo_cancellation_impl.h for more details on the mode. -// -// Justification: -// Chromium/Mac: Here, the true latency is so low (~10-20 ms), that it plays -// havoc with the AEC's buffering. To avoid this, we set a fixed delay of 20 ms -// and then compensate by rewinding by 10 ms (in wideband) through -// kDelayDiffOffsetSamples. This trick does not seem to work for larger rewind -// values, but fortunately this is sufficient. -// -// Chromium/Linux(ChromeOS): The values we get on this platform don't correspond -// well to reality. The variance doesn't match the AEC's buffer changes, and the -// bulk values tend to be too low. However, the range across different hardware -// appears to be too large to choose a single value. -// -// GTP/Linux(ChromeOS): TBD, but for the moment we will trust the values. -#if defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_MAC) -#define WEBRTC_UNTRUSTED_DELAY -#endif - -#if defined(WEBRTC_UNTRUSTED_DELAY) && defined(WEBRTC_MAC) -static const int kDelayDiffOffsetSamples = -160; -#else -// Not enabled for now. -static const int kDelayDiffOffsetSamples = 0; -#endif - -#if defined(WEBRTC_MAC) -static const int kFixedDelayMs = 20; -#else -static const int kFixedDelayMs = 50; -#endif -#if !defined(WEBRTC_UNTRUSTED_DELAY) -static const int kMinTrustedDelayMs = 20; -#endif -static const int kMaxTrustedDelayMs = 500; - -// Maximum length of resampled signal. Must be an integer multiple of frames -// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN -// The factor of 2 handles wb, and the + 1 is as a safety margin -// TODO(bjornv): Replace with kResamplerBufferSize -#define MAX_RESAMP_LEN (5 * FRAME_LEN) - -static const int kMaxBufSizeStart = 62; // In partitions -static const int sampMsNb = 8; // samples per ms in nb -static const int initCheck = 42; - -#ifdef WEBRTC_AEC_DEBUG_DUMP -int webrtc_aec_instance_count = 0; -#endif - -// Estimates delay to set the position of the far-end buffer read pointer -// (controlled by knownDelay) -static void EstBufDelayNormal(Aec* aecInst); -static void EstBufDelayExtended(Aec* aecInst); -static int ProcessNormal(Aec* self, - const float* const* near, - size_t num_bands, - float* const* out, - size_t num_samples, - int16_t reported_delay_ms, - int32_t skew); -static void ProcessExtended(Aec* self, - const float* const* near, - size_t num_bands, - float* const* out, - size_t num_samples, - int16_t reported_delay_ms, - int32_t skew); - -void* WebRtcAec_Create() { - Aec* aecpc = malloc(sizeof(Aec)); - - if (!aecpc) { - return NULL; - } - - aecpc->aec = WebRtcAec_CreateAec(); - if (!aecpc->aec) { - WebRtcAec_Free(aecpc); - return NULL; - } - aecpc->resampler = WebRtcAec_CreateResampler(); - if (!aecpc->resampler) { - WebRtcAec_Free(aecpc); - return NULL; - } - // Create far-end pre-buffer. The buffer size has to be large enough for - // largest possible drift compensation (kResamplerBufferSize) + "almost" an - // FFT buffer (PART_LEN2 - 1). - aecpc->far_pre_buf = - WebRtc_CreateBuffer(PART_LEN2 + kResamplerBufferSize, sizeof(float)); - if (!aecpc->far_pre_buf) { - WebRtcAec_Free(aecpc); - return NULL; - } - - aecpc->initFlag = 0; - aecpc->lastError = 0; - -#ifdef WEBRTC_AEC_DEBUG_DUMP - { - char filename[64]; - sprintf(filename, "aec_buf%d.dat", webrtc_aec_instance_count); - aecpc->bufFile = fopen(filename, "wb"); - sprintf(filename, "aec_skew%d.dat", webrtc_aec_instance_count); - aecpc->skewFile = fopen(filename, "wb"); - sprintf(filename, "aec_delay%d.dat", webrtc_aec_instance_count); - aecpc->delayFile = fopen(filename, "wb"); - webrtc_aec_instance_count++; - } -#endif - - return aecpc; -} - -void WebRtcAec_Free(void* aecInst) { - Aec* aecpc = aecInst; - - if (aecpc == NULL) { - return; - } - - WebRtc_FreeBuffer(aecpc->far_pre_buf); - -#ifdef WEBRTC_AEC_DEBUG_DUMP - fclose(aecpc->bufFile); - fclose(aecpc->skewFile); - fclose(aecpc->delayFile); -#endif - - WebRtcAec_FreeAec(aecpc->aec); - WebRtcAec_FreeResampler(aecpc->resampler); - free(aecpc); -} - -int32_t WebRtcAec_Init(void* aecInst, int32_t sampFreq, int32_t scSampFreq) { - Aec* aecpc = aecInst; - AecConfig aecConfig; - - if (sampFreq != 8000 && - sampFreq != 16000 && - sampFreq != 32000 && - sampFreq != 48000) { - aecpc->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - aecpc->sampFreq = sampFreq; - - if (scSampFreq < 1 || scSampFreq > 96000) { - aecpc->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - aecpc->scSampFreq = scSampFreq; - - // Initialize echo canceller core - if (WebRtcAec_InitAec(aecpc->aec, aecpc->sampFreq) == -1) { - aecpc->lastError = AEC_UNSPECIFIED_ERROR; - return -1; - } - - if (WebRtcAec_InitResampler(aecpc->resampler, aecpc->scSampFreq) == -1) { - aecpc->lastError = AEC_UNSPECIFIED_ERROR; - return -1; - } - - WebRtc_InitBuffer(aecpc->far_pre_buf); - WebRtc_MoveReadPtr(aecpc->far_pre_buf, -PART_LEN); // Start overlap. - - aecpc->initFlag = initCheck; // indicates that initialization has been done - - if (aecpc->sampFreq == 32000 || aecpc->sampFreq == 48000) { - aecpc->splitSampFreq = 16000; - } else { - aecpc->splitSampFreq = sampFreq; - } - - aecpc->delayCtr = 0; - aecpc->sampFactor = (aecpc->scSampFreq * 1.0f) / aecpc->splitSampFreq; - // Sampling frequency multiplier (SWB is processed as 160 frame size). - aecpc->rate_factor = aecpc->splitSampFreq / 8000; - - aecpc->sum = 0; - aecpc->counter = 0; - aecpc->checkBuffSize = 1; - aecpc->firstVal = 0; - - // We skip the startup_phase completely (setting to 0) if DA-AEC is enabled, - // but not extended_filter mode. - aecpc->startup_phase = WebRtcAec_extended_filter_enabled(aecpc->aec) || - !WebRtcAec_delay_agnostic_enabled(aecpc->aec); - aecpc->bufSizeStart = 0; - aecpc->checkBufSizeCtr = 0; - aecpc->msInSndCardBuf = 0; - aecpc->filtDelay = -1; // -1 indicates an initialized state. - aecpc->timeForDelayChange = 0; - aecpc->knownDelay = 0; - aecpc->lastDelayDiff = 0; - - aecpc->skewFrCtr = 0; - aecpc->resample = kAecFalse; - aecpc->highSkewCtr = 0; - aecpc->skew = 0; - - aecpc->farend_started = 0; - - // Default settings. - aecConfig.nlpMode = kAecNlpModerate; - aecConfig.skewMode = kAecFalse; - aecConfig.metricsMode = kAecFalse; - aecConfig.delay_logging = kAecFalse; - - if (WebRtcAec_set_config(aecpc, aecConfig) == -1) { - aecpc->lastError = AEC_UNSPECIFIED_ERROR; - return -1; - } - - return 0; -} - -// only buffer L band for farend -int32_t WebRtcAec_BufferFarend(void* aecInst, - const float* farend, - size_t nrOfSamples) { - Aec* aecpc = aecInst; - size_t newNrOfSamples = nrOfSamples; - float new_farend[MAX_RESAMP_LEN]; - const float* farend_ptr = farend; - - if (farend == NULL) { - aecpc->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - - if (aecpc->initFlag != initCheck) { - aecpc->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - - // number of samples == 160 for SWB input - if (nrOfSamples != 80 && nrOfSamples != 160) { - aecpc->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - - if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) { - // Resample and get a new number of samples - WebRtcAec_ResampleLinear(aecpc->resampler, - farend, - nrOfSamples, - aecpc->skew, - new_farend, - &newNrOfSamples); - farend_ptr = new_farend; - } - - aecpc->farend_started = 1; - WebRtcAec_SetSystemDelay( - aecpc->aec, WebRtcAec_system_delay(aecpc->aec) + (int)newNrOfSamples); - - // Write the time-domain data to |far_pre_buf|. - WebRtc_WriteBuffer(aecpc->far_pre_buf, farend_ptr, newNrOfSamples); - - // Transform to frequency domain if we have enough data. - while (WebRtc_available_read(aecpc->far_pre_buf) >= PART_LEN2) { - // We have enough data to pass to the FFT, hence read PART_LEN2 samples. - { - float* ptmp = NULL; - float tmp[PART_LEN2]; - WebRtc_ReadBuffer(aecpc->far_pre_buf, (void**)&ptmp, tmp, PART_LEN2); - WebRtcAec_BufferFarendPartition(aecpc->aec, ptmp); -#ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_WriteBuffer( - WebRtcAec_far_time_buf(aecpc->aec), &ptmp[PART_LEN], 1); -#endif - } - - // Rewind |far_pre_buf| PART_LEN samples for overlap before continuing. - WebRtc_MoveReadPtr(aecpc->far_pre_buf, -PART_LEN); - } - - return 0; -} - -int32_t WebRtcAec_Process(void* aecInst, - const float* const* nearend, - size_t num_bands, - float* const* out, - size_t nrOfSamples, - int16_t msInSndCardBuf, - int32_t skew) { - Aec* aecpc = aecInst; - int32_t retVal = 0; - - if (out == NULL) { - aecpc->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - - if (aecpc->initFlag != initCheck) { - aecpc->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - - // number of samples == 160 for SWB input - if (nrOfSamples != 80 && nrOfSamples != 160) { - aecpc->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - - if (msInSndCardBuf < 0) { - msInSndCardBuf = 0; - aecpc->lastError = AEC_BAD_PARAMETER_WARNING; - retVal = -1; - } else if (msInSndCardBuf > kMaxTrustedDelayMs) { - // The clamping is now done in ProcessExtended/Normal(). - aecpc->lastError = AEC_BAD_PARAMETER_WARNING; - retVal = -1; - } - - // This returns the value of aec->extended_filter_enabled. - if (WebRtcAec_extended_filter_enabled(aecpc->aec)) { - ProcessExtended(aecpc, - nearend, - num_bands, - out, - nrOfSamples, - msInSndCardBuf, - skew); - } else { - if (ProcessNormal(aecpc, - nearend, - num_bands, - out, - nrOfSamples, - msInSndCardBuf, - skew) != 0) { - retVal = -1; - } - } - -#ifdef WEBRTC_AEC_DEBUG_DUMP - { - int16_t far_buf_size_ms = (int16_t)(WebRtcAec_system_delay(aecpc->aec) / - (sampMsNb * aecpc->rate_factor)); - (void)fwrite(&far_buf_size_ms, 2, 1, aecpc->bufFile); - (void)fwrite( - &aecpc->knownDelay, sizeof(aecpc->knownDelay), 1, aecpc->delayFile); - } -#endif - - return retVal; -} - -int WebRtcAec_set_config(void* handle, AecConfig config) { - Aec* self = (Aec*)handle; - if (self->initFlag != initCheck) { - self->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - - if (config.skewMode != kAecFalse && config.skewMode != kAecTrue) { - self->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - self->skewMode = config.skewMode; - - if (config.nlpMode != kAecNlpConservative && - config.nlpMode != kAecNlpModerate && - config.nlpMode != kAecNlpAggressive) { - self->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - - if (config.metricsMode != kAecFalse && config.metricsMode != kAecTrue) { - self->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - - if (config.delay_logging != kAecFalse && config.delay_logging != kAecTrue) { - self->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - - WebRtcAec_SetConfigCore( - self->aec, config.nlpMode, config.metricsMode, config.delay_logging); - return 0; -} - -int WebRtcAec_get_echo_status(void* handle, int* status) { - Aec* self = (Aec*)handle; - if (status == NULL) { - self->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - if (self->initFlag != initCheck) { - self->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - - *status = WebRtcAec_echo_state(self->aec); - - return 0; -} - -int WebRtcAec_GetMetrics(void* handle, AecMetrics* metrics) { - const float kUpWeight = 0.7f; - float dtmp; - int stmp; - Aec* self = (Aec*)handle; - Stats erl; - Stats erle; - Stats a_nlp; - - if (handle == NULL) { - return -1; - } - if (metrics == NULL) { - self->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - if (self->initFlag != initCheck) { - self->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - - WebRtcAec_GetEchoStats(self->aec, &erl, &erle, &a_nlp); - - // ERL - metrics->erl.instant = (int)erl.instant; - - if ((erl.himean > kOffsetLevel) && (erl.average > kOffsetLevel)) { - // Use a mix between regular average and upper part average. - dtmp = kUpWeight * erl.himean + (1 - kUpWeight) * erl.average; - metrics->erl.average = (int)dtmp; - } else { - metrics->erl.average = kOffsetLevel; - } - - metrics->erl.max = (int)erl.max; - - if (erl.min < (kOffsetLevel * (-1))) { - metrics->erl.min = (int)erl.min; - } else { - metrics->erl.min = kOffsetLevel; - } - - // ERLE - metrics->erle.instant = (int)erle.instant; - - if ((erle.himean > kOffsetLevel) && (erle.average > kOffsetLevel)) { - // Use a mix between regular average and upper part average. - dtmp = kUpWeight * erle.himean + (1 - kUpWeight) * erle.average; - metrics->erle.average = (int)dtmp; - } else { - metrics->erle.average = kOffsetLevel; - } - - metrics->erle.max = (int)erle.max; - - if (erle.min < (kOffsetLevel * (-1))) { - metrics->erle.min = (int)erle.min; - } else { - metrics->erle.min = kOffsetLevel; - } - - // RERL - if ((metrics->erl.average > kOffsetLevel) && - (metrics->erle.average > kOffsetLevel)) { - stmp = metrics->erl.average + metrics->erle.average; - } else { - stmp = kOffsetLevel; - } - metrics->rerl.average = stmp; - - // No other statistics needed, but returned for completeness. - metrics->rerl.instant = stmp; - metrics->rerl.max = stmp; - metrics->rerl.min = stmp; - - // A_NLP - metrics->aNlp.instant = (int)a_nlp.instant; - - if ((a_nlp.himean > kOffsetLevel) && (a_nlp.average > kOffsetLevel)) { - // Use a mix between regular average and upper part average. - dtmp = kUpWeight * a_nlp.himean + (1 - kUpWeight) * a_nlp.average; - metrics->aNlp.average = (int)dtmp; - } else { - metrics->aNlp.average = kOffsetLevel; - } - - metrics->aNlp.max = (int)a_nlp.max; - - if (a_nlp.min < (kOffsetLevel * (-1))) { - metrics->aNlp.min = (int)a_nlp.min; - } else { - metrics->aNlp.min = kOffsetLevel; - } - - return 0; -} - -int WebRtcAec_GetDelayMetrics(void* handle, - int* median, - int* std, - float* fraction_poor_delays) { - Aec* self = handle; - if (median == NULL) { - self->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - if (std == NULL) { - self->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - if (self->initFlag != initCheck) { - self->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - if (WebRtcAec_GetDelayMetricsCore(self->aec, median, std, - fraction_poor_delays) == - -1) { - // Logging disabled. - self->lastError = AEC_UNSUPPORTED_FUNCTION_ERROR; - return -1; - } - - return 0; -} - -int32_t WebRtcAec_get_error_code(void* aecInst) { - Aec* aecpc = aecInst; - return aecpc->lastError; -} - -AecCore* WebRtcAec_aec_core(void* handle) { - if (!handle) { - return NULL; - } - return ((Aec*)handle)->aec; -} - -static int ProcessNormal(Aec* aecpc, - const float* const* nearend, - size_t num_bands, - float* const* out, - size_t nrOfSamples, - int16_t msInSndCardBuf, - int32_t skew) { - int retVal = 0; - size_t i; - size_t nBlocks10ms; - // Limit resampling to doubling/halving of signal - const float minSkewEst = -0.5f; - const float maxSkewEst = 1.0f; - - msInSndCardBuf = - msInSndCardBuf > kMaxTrustedDelayMs ? kMaxTrustedDelayMs : msInSndCardBuf; - // TODO(andrew): we need to investigate if this +10 is really wanted. - msInSndCardBuf += 10; - aecpc->msInSndCardBuf = msInSndCardBuf; - - if (aecpc->skewMode == kAecTrue) { - if (aecpc->skewFrCtr < 25) { - aecpc->skewFrCtr++; - } else { - retVal = WebRtcAec_GetSkew(aecpc->resampler, skew, &aecpc->skew); - if (retVal == -1) { - aecpc->skew = 0; - aecpc->lastError = AEC_BAD_PARAMETER_WARNING; - } - - aecpc->skew /= aecpc->sampFactor * nrOfSamples; - - if (aecpc->skew < 1.0e-3 && aecpc->skew > -1.0e-3) { - aecpc->resample = kAecFalse; - } else { - aecpc->resample = kAecTrue; - } - - if (aecpc->skew < minSkewEst) { - aecpc->skew = minSkewEst; - } else if (aecpc->skew > maxSkewEst) { - aecpc->skew = maxSkewEst; - } - -#ifdef WEBRTC_AEC_DEBUG_DUMP - (void)fwrite(&aecpc->skew, sizeof(aecpc->skew), 1, aecpc->skewFile); -#endif - } - } - - nBlocks10ms = nrOfSamples / (FRAME_LEN * aecpc->rate_factor); - - if (aecpc->startup_phase) { - for (i = 0; i < num_bands; ++i) { - // Only needed if they don't already point to the same place. - if (nearend[i] != out[i]) { - memcpy(out[i], nearend[i], sizeof(nearend[i][0]) * nrOfSamples); - } - } - - // The AEC is in the start up mode - // AEC is disabled until the system delay is OK - - // Mechanism to ensure that the system delay is reasonably stable. - if (aecpc->checkBuffSize) { - aecpc->checkBufSizeCtr++; - // Before we fill up the far-end buffer we require the system delay - // to be stable (+/-8 ms) compared to the first value. This - // comparison is made during the following 6 consecutive 10 ms - // blocks. If it seems to be stable then we start to fill up the - // far-end buffer. - if (aecpc->counter == 0) { - aecpc->firstVal = aecpc->msInSndCardBuf; - aecpc->sum = 0; - } - - if (abs(aecpc->firstVal - aecpc->msInSndCardBuf) < - WEBRTC_SPL_MAX(0.2 * aecpc->msInSndCardBuf, sampMsNb)) { - aecpc->sum += aecpc->msInSndCardBuf; - aecpc->counter++; - } else { - aecpc->counter = 0; - } - - if (aecpc->counter * nBlocks10ms >= 6) { - // The far-end buffer size is determined in partitions of - // PART_LEN samples. Use 75% of the average value of the system - // delay as buffer size to start with. - aecpc->bufSizeStart = - WEBRTC_SPL_MIN((3 * aecpc->sum * aecpc->rate_factor * 8) / - (4 * aecpc->counter * PART_LEN), - kMaxBufSizeStart); - // Buffer size has now been determined. - aecpc->checkBuffSize = 0; - } - - if (aecpc->checkBufSizeCtr * nBlocks10ms > 50) { - // For really bad systems, don't disable the echo canceller for - // more than 0.5 sec. - aecpc->bufSizeStart = WEBRTC_SPL_MIN( - (aecpc->msInSndCardBuf * aecpc->rate_factor * 3) / 40, - kMaxBufSizeStart); - aecpc->checkBuffSize = 0; - } - } - - // If |checkBuffSize| changed in the if-statement above. - if (!aecpc->checkBuffSize) { - // The system delay is now reasonably stable (or has been unstable - // for too long). When the far-end buffer is filled with - // approximately the same amount of data as reported by the system - // we end the startup phase. - int overhead_elements = - WebRtcAec_system_delay(aecpc->aec) / PART_LEN - aecpc->bufSizeStart; - if (overhead_elements == 0) { - // Enable the AEC - aecpc->startup_phase = 0; - } else if (overhead_elements > 0) { - // TODO(bjornv): Do we need a check on how much we actually - // moved the read pointer? It should always be possible to move - // the pointer |overhead_elements| since we have only added data - // to the buffer and no delay compensation nor AEC processing - // has been done. - WebRtcAec_MoveFarReadPtr(aecpc->aec, overhead_elements); - - // Enable the AEC - aecpc->startup_phase = 0; - } - } - } else { - // AEC is enabled. - EstBufDelayNormal(aecpc); - - // Call the AEC. - // TODO(bjornv): Re-structure such that we don't have to pass - // |aecpc->knownDelay| as input. Change name to something like - // |system_buffer_diff|. - WebRtcAec_ProcessFrames(aecpc->aec, - nearend, - num_bands, - nrOfSamples, - aecpc->knownDelay, - out); - } - - return retVal; -} - -static void ProcessExtended(Aec* self, - const float* const* near, - size_t num_bands, - float* const* out, - size_t num_samples, - int16_t reported_delay_ms, - int32_t skew) { - size_t i; - const int delay_diff_offset = kDelayDiffOffsetSamples; -#if defined(WEBRTC_UNTRUSTED_DELAY) - reported_delay_ms = kFixedDelayMs; -#else - // This is the usual mode where we trust the reported system delay values. - // Due to the longer filter, we no longer add 10 ms to the reported delay - // to reduce chance of non-causality. Instead we apply a minimum here to avoid - // issues with the read pointer jumping around needlessly. - reported_delay_ms = reported_delay_ms < kMinTrustedDelayMs - ? kMinTrustedDelayMs - : reported_delay_ms; - // If the reported delay appears to be bogus, we attempt to recover by using - // the measured fixed delay values. We use >= here because higher layers - // may already clamp to this maximum value, and we would otherwise not - // detect it here. - reported_delay_ms = reported_delay_ms >= kMaxTrustedDelayMs - ? kFixedDelayMs - : reported_delay_ms; -#endif - self->msInSndCardBuf = reported_delay_ms; - - if (!self->farend_started) { - for (i = 0; i < num_bands; ++i) { - // Only needed if they don't already point to the same place. - if (near[i] != out[i]) { - memcpy(out[i], near[i], sizeof(near[i][0]) * num_samples); - } - } - return; - } - if (self->startup_phase) { - // In the extended mode, there isn't a startup "phase", just a special - // action on the first frame. In the trusted delay case, we'll take the - // current reported delay, unless it's less then our conservative - // measurement. - int startup_size_ms = - reported_delay_ms < kFixedDelayMs ? kFixedDelayMs : reported_delay_ms; -#if defined(WEBRTC_ANDROID) - int target_delay = startup_size_ms * self->rate_factor * 8; -#else - // To avoid putting the AEC in a non-causal state we're being slightly - // conservative and scale by 2. On Android we use a fixed delay and - // therefore there is no need to scale the target_delay. - int target_delay = startup_size_ms * self->rate_factor * 8 / 2; -#endif - int overhead_elements = - (WebRtcAec_system_delay(self->aec) - target_delay) / PART_LEN; - WebRtcAec_MoveFarReadPtr(self->aec, overhead_elements); - self->startup_phase = 0; - } - - EstBufDelayExtended(self); - - { - // |delay_diff_offset| gives us the option to manually rewind the delay on - // very low delay platforms which can't be expressed purely through - // |reported_delay_ms|. - const int adjusted_known_delay = - WEBRTC_SPL_MAX(0, self->knownDelay + delay_diff_offset); - - WebRtcAec_ProcessFrames(self->aec, - near, - num_bands, - num_samples, - adjusted_known_delay, - out); - } -} - -static void EstBufDelayNormal(Aec* aecpc) { - int nSampSndCard = aecpc->msInSndCardBuf * sampMsNb * aecpc->rate_factor; - int current_delay = nSampSndCard - WebRtcAec_system_delay(aecpc->aec); - int delay_difference = 0; - - // Before we proceed with the delay estimate filtering we: - // 1) Compensate for the frame that will be read. - // 2) Compensate for drift resampling. - // 3) Compensate for non-causality if needed, since the estimated delay can't - // be negative. - - // 1) Compensating for the frame(s) that will be read/processed. - current_delay += FRAME_LEN * aecpc->rate_factor; - - // 2) Account for resampling frame delay. - if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) { - current_delay -= kResamplingDelay; - } - - // 3) Compensate for non-causality, if needed, by flushing one block. - if (current_delay < PART_LEN) { - current_delay += WebRtcAec_MoveFarReadPtr(aecpc->aec, 1) * PART_LEN; - } - - // We use -1 to signal an initialized state in the "extended" implementation; - // compensate for that. - aecpc->filtDelay = aecpc->filtDelay < 0 ? 0 : aecpc->filtDelay; - aecpc->filtDelay = - WEBRTC_SPL_MAX(0, (short)(0.8 * aecpc->filtDelay + 0.2 * current_delay)); - - delay_difference = aecpc->filtDelay - aecpc->knownDelay; - if (delay_difference > 224) { - if (aecpc->lastDelayDiff < 96) { - aecpc->timeForDelayChange = 0; - } else { - aecpc->timeForDelayChange++; - } - } else if (delay_difference < 96 && aecpc->knownDelay > 0) { - if (aecpc->lastDelayDiff > 224) { - aecpc->timeForDelayChange = 0; - } else { - aecpc->timeForDelayChange++; - } - } else { - aecpc->timeForDelayChange = 0; - } - aecpc->lastDelayDiff = delay_difference; - - if (aecpc->timeForDelayChange > 25) { - aecpc->knownDelay = WEBRTC_SPL_MAX((int)aecpc->filtDelay - 160, 0); - } -} - -static void EstBufDelayExtended(Aec* self) { - int reported_delay = self->msInSndCardBuf * sampMsNb * self->rate_factor; - int current_delay = reported_delay - WebRtcAec_system_delay(self->aec); - int delay_difference = 0; - - // Before we proceed with the delay estimate filtering we: - // 1) Compensate for the frame that will be read. - // 2) Compensate for drift resampling. - // 3) Compensate for non-causality if needed, since the estimated delay can't - // be negative. - - // 1) Compensating for the frame(s) that will be read/processed. - current_delay += FRAME_LEN * self->rate_factor; - - // 2) Account for resampling frame delay. - if (self->skewMode == kAecTrue && self->resample == kAecTrue) { - current_delay -= kResamplingDelay; - } - - // 3) Compensate for non-causality, if needed, by flushing two blocks. - if (current_delay < PART_LEN) { - current_delay += WebRtcAec_MoveFarReadPtr(self->aec, 2) * PART_LEN; - } - - if (self->filtDelay == -1) { - self->filtDelay = WEBRTC_SPL_MAX(0, 0.5 * current_delay); - } else { - self->filtDelay = WEBRTC_SPL_MAX( - 0, (short)(0.95 * self->filtDelay + 0.05 * current_delay)); - } - - delay_difference = self->filtDelay - self->knownDelay; - if (delay_difference > 384) { - if (self->lastDelayDiff < 128) { - self->timeForDelayChange = 0; - } else { - self->timeForDelayChange++; - } - } else if (delay_difference < 128 && self->knownDelay > 0) { - if (self->lastDelayDiff > 384) { - self->timeForDelayChange = 0; - } else { - self->timeForDelayChange++; - } - } else { - self->timeForDelayChange = 0; - } - self->lastDelayDiff = delay_difference; - - if (self->timeForDelayChange > 25) { - self->knownDelay = WEBRTC_SPL_MAX((int)self->filtDelay - 256, 0); - } -} diff --git a/webrtc/modules/audio_processing/aec/echo_cancellation_internal.h b/webrtc/modules/audio_processing/aec/echo_cancellation_internal.h deleted file mode 100644 index 95a6cf3..0000000 --- a/webrtc/modules/audio_processing/aec/echo_cancellation_internal.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_ECHO_CANCELLATION_INTERNAL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_ECHO_CANCELLATION_INTERNAL_H_ - -#include "webrtc/common_audio/ring_buffer.h" -#include "webrtc/modules/audio_processing/aec/aec_core.h" - -typedef struct { - int delayCtr; - int sampFreq; - int splitSampFreq; - int scSampFreq; - float sampFactor; // scSampRate / sampFreq - short skewMode; - int bufSizeStart; - int knownDelay; - int rate_factor; - - short initFlag; // indicates if AEC has been initialized - - // Variables used for averaging far end buffer size - short counter; - int sum; - short firstVal; - short checkBufSizeCtr; - - // Variables used for delay shifts - short msInSndCardBuf; - short filtDelay; // Filtered delay estimate. - int timeForDelayChange; - int startup_phase; - int checkBuffSize; - short lastDelayDiff; - -#ifdef WEBRTC_AEC_DEBUG_DUMP - FILE* bufFile; - FILE* delayFile; - FILE* skewFile; -#endif - - // Structures - void* resampler; - - int skewFrCtr; - int resample; // if the skew is small enough we don't resample - int highSkewCtr; - float skew; - - RingBuffer* far_pre_buf; // Time domain far-end pre-buffer. - - int lastError; - - int farend_started; - - AecCore* aec; -} Aec; - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_ECHO_CANCELLATION_INTERNAL_H_ diff --git a/webrtc/modules/audio_processing/aec/include/echo_cancellation.h b/webrtc/modules/audio_processing/aec/include/echo_cancellation.h deleted file mode 100644 index a340cf8..0000000 --- a/webrtc/modules/audio_processing/aec/include/echo_cancellation.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_INCLUDE_ECHO_CANCELLATION_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_INCLUDE_ECHO_CANCELLATION_H_ - -#include - -#include "webrtc/typedefs.h" - -// Errors -#define AEC_UNSPECIFIED_ERROR 12000 -#define AEC_UNSUPPORTED_FUNCTION_ERROR 12001 -#define AEC_UNINITIALIZED_ERROR 12002 -#define AEC_NULL_POINTER_ERROR 12003 -#define AEC_BAD_PARAMETER_ERROR 12004 - -// Warnings -#define AEC_BAD_PARAMETER_WARNING 12050 - -enum { - kAecNlpConservative = 0, - kAecNlpModerate, - kAecNlpAggressive -}; - -enum { - kAecFalse = 0, - kAecTrue -}; - -typedef struct { - int16_t nlpMode; // default kAecNlpModerate - int16_t skewMode; // default kAecFalse - int16_t metricsMode; // default kAecFalse - int delay_logging; // default kAecFalse - // float realSkew; -} AecConfig; - -typedef struct { - int instant; - int average; - int max; - int min; -} AecLevel; - -typedef struct { - AecLevel rerl; - AecLevel erl; - AecLevel erle; - AecLevel aNlp; -} AecMetrics; - -struct AecCore; - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Allocates the memory needed by the AEC. The memory needs to be initialized - * separately using the WebRtcAec_Init() function. Returns a pointer to the - * object or NULL on error. - */ -void* WebRtcAec_Create(); - -/* - * This function releases the memory allocated by WebRtcAec_Create(). - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecInst Pointer to the AEC instance - */ -void WebRtcAec_Free(void* aecInst); - -/* - * Initializes an AEC instance. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecInst Pointer to the AEC instance - * int32_t sampFreq Sampling frequency of data - * int32_t scSampFreq Soundcard sampling frequency - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 0: OK - * -1: error - */ -int32_t WebRtcAec_Init(void* aecInst, int32_t sampFreq, int32_t scSampFreq); - -/* - * Inserts an 80 or 160 sample block of data into the farend buffer. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecInst Pointer to the AEC instance - * const float* farend In buffer containing one frame of - * farend signal for L band - * int16_t nrOfSamples Number of samples in farend buffer - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 0: OK - * -1: error - */ -int32_t WebRtcAec_BufferFarend(void* aecInst, - const float* farend, - size_t nrOfSamples); - -/* - * Runs the echo canceller on an 80 or 160 sample blocks of data. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecInst Pointer to the AEC instance - * float* const* nearend In buffer containing one frame of - * nearend+echo signal for each band - * int num_bands Number of bands in nearend buffer - * int16_t nrOfSamples Number of samples in nearend buffer - * int16_t msInSndCardBuf Delay estimate for sound card and - * system buffers - * int16_t skew Difference between number of samples played - * and recorded at the soundcard (for clock skew - * compensation) - * - * Outputs Description - * ------------------------------------------------------------------- - * float* const* out Out buffer, one frame of processed nearend - * for each band - * int32_t return 0: OK - * -1: error - */ -int32_t WebRtcAec_Process(void* aecInst, - const float* const* nearend, - size_t num_bands, - float* const* out, - size_t nrOfSamples, - int16_t msInSndCardBuf, - int32_t skew); - -/* - * This function enables the user to set certain parameters on-the-fly. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* handle Pointer to the AEC instance - * AecConfig config Config instance that contains all - * properties to be set - * - * Outputs Description - * ------------------------------------------------------------------- - * int return 0: OK - * -1: error - */ -int WebRtcAec_set_config(void* handle, AecConfig config); - -/* - * Gets the current echo status of the nearend signal. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* handle Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * int* status 0: Almost certainly nearend single-talk - * 1: Might not be neared single-talk - * int return 0: OK - * -1: error - */ -int WebRtcAec_get_echo_status(void* handle, int* status); - -/* - * Gets the current echo metrics for the session. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* handle Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * AecMetrics* metrics Struct which will be filled out with the - * current echo metrics. - * int return 0: OK - * -1: error - */ -int WebRtcAec_GetMetrics(void* handle, AecMetrics* metrics); - -/* - * Gets the current delay metrics for the session. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* handle Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * int* median Delay median value. - * int* std Delay standard deviation. - * float* fraction_poor_delays Fraction of the delay estimates that may - * cause the AEC to perform poorly. - * - * int return 0: OK - * -1: error - */ -int WebRtcAec_GetDelayMetrics(void* handle, - int* median, - int* std, - float* fraction_poor_delays); - -/* - * Gets the last error code. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecInst Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 11000-11100: error code - */ -int32_t WebRtcAec_get_error_code(void* aecInst); - -// Returns a pointer to the low level AEC handle. -// -// Input: -// - handle : Pointer to the AEC instance. -// -// Return value: -// - AecCore pointer : NULL for error. -// -struct AecCore* WebRtcAec_aec_core(void* handle); - -#ifdef __cplusplus -} -#endif -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_INCLUDE_ECHO_CANCELLATION_H_ diff --git a/webrtc/modules/audio_processing/aec3/BUILD.gn b/webrtc/modules/audio_processing/aec3/BUILD.gn new file mode 100644 index 0000000..c98fa4c --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/BUILD.gn @@ -0,0 +1,367 @@ +# Copyright (c) 2018 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. + +import("../../../webrtc.gni") + +rtc_library("aec3") { + visibility = [ "*" ] + configs += [ "..:apm_debug_dump" ] + sources = [ + "adaptive_fir_filter.cc", + "adaptive_fir_filter_erl.cc", + "aec3_common.cc", + "aec3_fft.cc", + "aec_state.cc", + "aec_state.h", + "alignment_mixer.cc", + "alignment_mixer.h", + "api_call_jitter_metrics.cc", + "api_call_jitter_metrics.h", + "block_buffer.cc", + "block_delay_buffer.cc", + "block_delay_buffer.h", + "block_framer.cc", + "block_framer.h", + "block_processor.cc", + "block_processor.h", + "block_processor_metrics.cc", + "block_processor_metrics.h", + "clockdrift_detector.cc", + "clockdrift_detector.h", + "coarse_filter_update_gain.cc", + "coarse_filter_update_gain.h", + "comfort_noise_generator.cc", + "comfort_noise_generator.h", + "decimator.cc", + "decimator.h", + "delay_estimate.h", + "dominant_nearend_detector.cc", + "dominant_nearend_detector.h", + "downsampled_render_buffer.cc", + "downsampled_render_buffer.h", + "echo_audibility.cc", + "echo_audibility.h", + "echo_canceller3.cc", + "echo_canceller3.h", + "echo_path_delay_estimator.cc", + "echo_path_delay_estimator.h", + "echo_path_variability.cc", + "echo_path_variability.h", + "echo_remover.cc", + "echo_remover.h", + "echo_remover_metrics.cc", + "echo_remover_metrics.h", + "erl_estimator.cc", + "erl_estimator.h", + "erle_estimator.cc", + "erle_estimator.h", + "fft_buffer.cc", + "filter_analyzer.cc", + "filter_analyzer.h", + "frame_blocker.cc", + "frame_blocker.h", + "fullband_erle_estimator.cc", + "fullband_erle_estimator.h", + "matched_filter.cc", + "matched_filter_lag_aggregator.cc", + "matched_filter_lag_aggregator.h", + "moving_average.cc", + "moving_average.h", + "nearend_detector.h", + "refined_filter_update_gain.cc", + "refined_filter_update_gain.h", + "render_buffer.cc", + "render_delay_buffer.cc", + "render_delay_buffer.h", + "render_delay_controller.cc", + "render_delay_controller.h", + "render_delay_controller_metrics.cc", + "render_delay_controller_metrics.h", + "render_signal_analyzer.cc", + "render_signal_analyzer.h", + "residual_echo_estimator.cc", + "residual_echo_estimator.h", + "reverb_decay_estimator.cc", + "reverb_decay_estimator.h", + "reverb_frequency_response.cc", + "reverb_frequency_response.h", + "reverb_model.cc", + "reverb_model.h", + "reverb_model_estimator.cc", + "reverb_model_estimator.h", + "signal_dependent_erle_estimator.cc", + "signal_dependent_erle_estimator.h", + "spectrum_buffer.cc", + "stationarity_estimator.cc", + "stationarity_estimator.h", + "subband_erle_estimator.cc", + "subband_erle_estimator.h", + "subband_nearend_detector.cc", + "subband_nearend_detector.h", + "subtractor.cc", + "subtractor.h", + "subtractor_output.cc", + "subtractor_output.h", + "subtractor_output_analyzer.cc", + "subtractor_output_analyzer.h", + "suppression_filter.cc", + "suppression_filter.h", + "suppression_gain.cc", + "suppression_gain.h", + "transparent_mode.cc", + "transparent_mode.h", + ] + + defines = [] + if (rtc_build_with_neon && current_cpu != "arm64") { + suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] + cflags = [ "-mfpu=neon" ] + } + + deps = [ + ":adaptive_fir_filter", + ":adaptive_fir_filter_erl", + ":aec3_common", + ":aec3_fft", + ":fft_data", + ":matched_filter", + ":render_buffer", + ":vector_math", + "..:apm_logging", + "..:audio_buffer", + "..:high_pass_filter", + "../../../api:array_view", + "../../../api/audio:aec3_config", + "../../../api/audio:echo_control", + "../../../common_audio:common_audio_c", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_minmax", + "../../../rtc_base/experiments:field_trial_parser", + "../../../rtc_base/system:arch", + "../../../system_wrappers", + "../../../system_wrappers:field_trial", + "../../../system_wrappers:metrics", + "../utility:cascaded_biquad_filter", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + + if (current_cpu == "x86" || current_cpu == "x64") { + deps += [ ":aec3_avx2" ] + } +} + +rtc_source_set("aec3_common") { + sources = [ "aec3_common.h" ] +} + +rtc_source_set("aec3_fft") { + sources = [ "aec3_fft.h" ] + deps = [ + ":aec3_common", + ":fft_data", + "../../../api:array_view", + "../../../common_audio/third_party/ooura:fft_size_128", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base/system:arch", + ] +} + +rtc_source_set("render_buffer") { + sources = [ + "block_buffer.h", + "fft_buffer.h", + "render_buffer.h", + "spectrum_buffer.h", + ] + deps = [ + ":aec3_common", + ":fft_data", + "../../../api:array_view", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base/system:arch", + ] +} + +rtc_source_set("adaptive_fir_filter") { + sources = [ "adaptive_fir_filter.h" ] + deps = [ + ":aec3_common", + ":aec3_fft", + ":fft_data", + ":render_buffer", + "..:apm_logging", + "../../../api:array_view", + "../../../rtc_base/system:arch", + ] +} + +rtc_source_set("adaptive_fir_filter_erl") { + sources = [ "adaptive_fir_filter_erl.h" ] + deps = [ + ":aec3_common", + "../../../api:array_view", + "../../../rtc_base/system:arch", + ] +} + +rtc_source_set("matched_filter") { + sources = [ "matched_filter.h" ] + deps = [ + ":aec3_common", + "../../../api:array_view", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base/system:arch", + ] +} + +rtc_source_set("vector_math") { + sources = [ "vector_math.h" ] + deps = [ + ":aec3_common", + "../../../api:array_view", + "../../../rtc_base:checks", + "../../../rtc_base/system:arch", + ] +} + +rtc_source_set("fft_data") { + sources = [ "fft_data.h" ] + deps = [ + ":aec3_common", + "../../../api:array_view", + "../../../rtc_base/system:arch", + ] +} + +if (current_cpu == "x86" || current_cpu == "x64") { + rtc_library("aec3_avx2") { + configs += [ "..:apm_debug_dump" ] + sources = [ + "adaptive_fir_filter_avx2.cc", + "adaptive_fir_filter_erl_avx2.cc", + "fft_data_avx2.cc", + "matched_filter_avx2.cc", + "vector_math_avx2.cc", + ] + + if (is_win) { + cflags = [ "/arch:AVX2" ] + } else { + cflags = [ + "-mavx2", + "-mfma", + ] + } + + deps = [ + ":adaptive_fir_filter", + ":adaptive_fir_filter_erl", + ":fft_data", + ":matched_filter", + ":vector_math", + "../../../api:array_view", + "../../../rtc_base:checks", + ] + } +} + +if (rtc_include_tests) { + rtc_library("aec3_unittests") { + testonly = true + + configs += [ "..:apm_debug_dump" ] + sources = [ + "mock/mock_block_processor.cc", + "mock/mock_block_processor.h", + "mock/mock_echo_remover.cc", + "mock/mock_echo_remover.h", + "mock/mock_render_delay_buffer.cc", + "mock/mock_render_delay_buffer.h", + "mock/mock_render_delay_controller.cc", + "mock/mock_render_delay_controller.h", + ] + + deps = [ + ":adaptive_fir_filter", + ":adaptive_fir_filter_erl", + ":aec3", + ":aec3_common", + ":aec3_fft", + ":fft_data", + ":matched_filter", + ":render_buffer", + ":vector_math", + "..:apm_logging", + "..:audio_buffer", + "..:audio_processing", + "..:audio_processing_unittests", + "..:high_pass_filter", + "../../../api:array_view", + "../../../api/audio:aec3_config", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_minmax", + "../../../rtc_base/system:arch", + "../../../system_wrappers", + "../../../test:field_trial", + "../../../test:test_support", + "../utility:cascaded_biquad_filter", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + + defines = [] + + if (rtc_enable_protobuf) { + sources += [ + "adaptive_fir_filter_erl_unittest.cc", + "adaptive_fir_filter_unittest.cc", + "aec3_fft_unittest.cc", + "aec_state_unittest.cc", + "alignment_mixer_unittest.cc", + "api_call_jitter_metrics_unittest.cc", + "block_delay_buffer_unittest.cc", + "block_framer_unittest.cc", + "block_processor_metrics_unittest.cc", + "block_processor_unittest.cc", + "clockdrift_detector_unittest.cc", + "coarse_filter_update_gain_unittest.cc", + "comfort_noise_generator_unittest.cc", + "decimator_unittest.cc", + "echo_canceller3_unittest.cc", + "echo_path_delay_estimator_unittest.cc", + "echo_path_variability_unittest.cc", + "echo_remover_metrics_unittest.cc", + "echo_remover_unittest.cc", + "erl_estimator_unittest.cc", + "erle_estimator_unittest.cc", + "fft_data_unittest.cc", + "filter_analyzer_unittest.cc", + "frame_blocker_unittest.cc", + "matched_filter_lag_aggregator_unittest.cc", + "matched_filter_unittest.cc", + "moving_average_unittest.cc", + "refined_filter_update_gain_unittest.cc", + "render_buffer_unittest.cc", + "render_delay_buffer_unittest.cc", + "render_delay_controller_metrics_unittest.cc", + "render_delay_controller_unittest.cc", + "render_signal_analyzer_unittest.cc", + "residual_echo_estimator_unittest.cc", + "reverb_model_estimator_unittest.cc", + "signal_dependent_erle_estimator_unittest.cc", + "subtractor_unittest.cc", + "suppression_filter_unittest.cc", + "suppression_gain_unittest.cc", + "vector_math_unittest.cc", + ] + } + } +} diff --git a/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc new file mode 100644 index 0000000..bf3a780 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc @@ -0,0 +1,740 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/adaptive_fir_filter.h" + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_HAS_NEON) +#include +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif +#include + +#include +#include + +#include "modules/audio_processing/aec3/fft_data.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace aec3 { + +// Computes and stores the frequency response of the filter. +void ComputeFrequencyResponse( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2) { + for (auto& H2_ch : *H2) { + H2_ch.fill(0.f); + } + + const size_t num_render_channels = H[0].size(); + RTC_DCHECK_EQ(H.size(), H2->capacity()); + for (size_t p = 0; p < num_partitions; ++p) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, (*H2)[p].size()); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + for (size_t j = 0; j < kFftLengthBy2Plus1; ++j) { + float tmp = + H[p][ch].re[j] * H[p][ch].re[j] + H[p][ch].im[j] * H[p][ch].im[j]; + (*H2)[p][j] = std::max((*H2)[p][j], tmp); + } + } + } +} + +#if defined(WEBRTC_HAS_NEON) +// Computes and stores the frequency response of the filter. +void ComputeFrequencyResponse_Neon( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2) { + for (auto& H2_ch : *H2) { + H2_ch.fill(0.f); + } + + const size_t num_render_channels = H[0].size(); + RTC_DCHECK_EQ(H.size(), H2->capacity()); + for (size_t p = 0; p < num_partitions; ++p) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, (*H2)[p].size()); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + for (size_t j = 0; j < kFftLengthBy2; j += 4) { + const float32x4_t re = vld1q_f32(&H[p][ch].re[j]); + const float32x4_t im = vld1q_f32(&H[p][ch].im[j]); + float32x4_t H2_new = vmulq_f32(re, re); + H2_new = vmlaq_f32(H2_new, im, im); + float32x4_t H2_p_j = vld1q_f32(&(*H2)[p][j]); + H2_p_j = vmaxq_f32(H2_p_j, H2_new); + vst1q_f32(&(*H2)[p][j], H2_p_j); + } + float H2_new = H[p][ch].re[kFftLengthBy2] * H[p][ch].re[kFftLengthBy2] + + H[p][ch].im[kFftLengthBy2] * H[p][ch].im[kFftLengthBy2]; + (*H2)[p][kFftLengthBy2] = std::max((*H2)[p][kFftLengthBy2], H2_new); + } + } +} +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) +// Computes and stores the frequency response of the filter. +void ComputeFrequencyResponse_Sse2( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2) { + for (auto& H2_ch : *H2) { + H2_ch.fill(0.f); + } + + const size_t num_render_channels = H[0].size(); + RTC_DCHECK_EQ(H.size(), H2->capacity()); + // constexpr __mmmask8 kMaxMask = static_cast<__mmmask8>(256u); + for (size_t p = 0; p < num_partitions; ++p) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, (*H2)[p].size()); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + for (size_t j = 0; j < kFftLengthBy2; j += 4) { + const __m128 re = _mm_loadu_ps(&H[p][ch].re[j]); + const __m128 re2 = _mm_mul_ps(re, re); + const __m128 im = _mm_loadu_ps(&H[p][ch].im[j]); + const __m128 im2 = _mm_mul_ps(im, im); + const __m128 H2_new = _mm_add_ps(re2, im2); + __m128 H2_k_j = _mm_loadu_ps(&(*H2)[p][j]); + H2_k_j = _mm_max_ps(H2_k_j, H2_new); + _mm_storeu_ps(&(*H2)[p][j], H2_k_j); + } + float H2_new = H[p][ch].re[kFftLengthBy2] * H[p][ch].re[kFftLengthBy2] + + H[p][ch].im[kFftLengthBy2] * H[p][ch].im[kFftLengthBy2]; + (*H2)[p][kFftLengthBy2] = std::max((*H2)[p][kFftLengthBy2], H2_new); + } + } +} +#endif + +// Adapts the filter partitions as H(t+1)=H(t)+G(t)*conj(X(t)). +void AdaptPartitions(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H) { + rtc::ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + size_t index = render_buffer.Position(); + const size_t num_render_channels = render_buffer_data[index].size(); + for (size_t p = 0; p < num_partitions; ++p) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& X_p_ch = render_buffer_data[index][ch]; + FftData& H_p_ch = (*H)[p][ch]; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + H_p_ch.re[k] += X_p_ch.re[k] * G.re[k] + X_p_ch.im[k] * G.im[k]; + H_p_ch.im[k] += X_p_ch.re[k] * G.im[k] - X_p_ch.im[k] * G.re[k]; + } + } + index = index < (render_buffer_data.size() - 1) ? index + 1 : 0; + } +} + +#if defined(WEBRTC_HAS_NEON) +// Adapts the filter partitions. (Neon variant) +void AdaptPartitions_Neon(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H) { + rtc::ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumFourBinBands = kFftLengthBy2 / 4; + + size_t X_partition = render_buffer.Position(); + size_t limit = lim1; + size_t p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + for (size_t k = 0, n = 0; n < kNumFourBinBands; ++n, k += 4) { + const float32x4_t G_re = vld1q_f32(&G.re[k]); + const float32x4_t G_im = vld1q_f32(&G.im[k]); + const float32x4_t X_re = vld1q_f32(&X.re[k]); + const float32x4_t X_im = vld1q_f32(&X.im[k]); + const float32x4_t H_re = vld1q_f32(&H_p_ch.re[k]); + const float32x4_t H_im = vld1q_f32(&H_p_ch.im[k]); + const float32x4_t a = vmulq_f32(X_re, G_re); + const float32x4_t e = vmlaq_f32(a, X_im, G_im); + const float32x4_t c = vmulq_f32(X_re, G_im); + const float32x4_t f = vmlsq_f32(c, X_im, G_re); + const float32x4_t g = vaddq_f32(H_re, e); + const float32x4_t h = vaddq_f32(H_im, f); + vst1q_f32(&H_p_ch.re[k], g); + vst1q_f32(&H_p_ch.im[k], h); + } + } + } + + X_partition = 0; + limit = lim2; + } while (p < lim2); + + X_partition = render_buffer.Position(); + limit = lim1; + p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + + H_p_ch.re[kFftLengthBy2] += X.re[kFftLengthBy2] * G.re[kFftLengthBy2] + + X.im[kFftLengthBy2] * G.im[kFftLengthBy2]; + H_p_ch.im[kFftLengthBy2] += X.re[kFftLengthBy2] * G.im[kFftLengthBy2] - + X.im[kFftLengthBy2] * G.re[kFftLengthBy2]; + } + } + X_partition = 0; + limit = lim2; + } while (p < lim2); +} +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) +// Adapts the filter partitions. (SSE2 variant) +void AdaptPartitions_Sse2(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H) { + rtc::ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumFourBinBands = kFftLengthBy2 / 4; + + size_t X_partition = render_buffer.Position(); + size_t limit = lim1; + size_t p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + + for (size_t k = 0, n = 0; n < kNumFourBinBands; ++n, k += 4) { + const __m128 G_re = _mm_loadu_ps(&G.re[k]); + const __m128 G_im = _mm_loadu_ps(&G.im[k]); + const __m128 X_re = _mm_loadu_ps(&X.re[k]); + const __m128 X_im = _mm_loadu_ps(&X.im[k]); + const __m128 H_re = _mm_loadu_ps(&H_p_ch.re[k]); + const __m128 H_im = _mm_loadu_ps(&H_p_ch.im[k]); + const __m128 a = _mm_mul_ps(X_re, G_re); + const __m128 b = _mm_mul_ps(X_im, G_im); + const __m128 c = _mm_mul_ps(X_re, G_im); + const __m128 d = _mm_mul_ps(X_im, G_re); + const __m128 e = _mm_add_ps(a, b); + const __m128 f = _mm_sub_ps(c, d); + const __m128 g = _mm_add_ps(H_re, e); + const __m128 h = _mm_add_ps(H_im, f); + _mm_storeu_ps(&H_p_ch.re[k], g); + _mm_storeu_ps(&H_p_ch.im[k], h); + } + } + } + X_partition = 0; + limit = lim2; + } while (p < lim2); + + X_partition = render_buffer.Position(); + limit = lim1; + p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + + H_p_ch.re[kFftLengthBy2] += X.re[kFftLengthBy2] * G.re[kFftLengthBy2] + + X.im[kFftLengthBy2] * G.im[kFftLengthBy2]; + H_p_ch.im[kFftLengthBy2] += X.re[kFftLengthBy2] * G.im[kFftLengthBy2] - + X.im[kFftLengthBy2] * G.re[kFftLengthBy2]; + } + } + + X_partition = 0; + limit = lim2; + } while (p < lim2); +} +#endif + +// Produces the filter output. +void ApplyFilter(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S) { + S->re.fill(0.f); + S->im.fill(0.f); + + rtc::ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + size_t index = render_buffer.Position(); + const size_t num_render_channels = render_buffer_data[index].size(); + for (size_t p = 0; p < num_partitions; ++p) { + RTC_DCHECK_EQ(num_render_channels, H[p].size()); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& X_p_ch = render_buffer_data[index][ch]; + const FftData& H_p_ch = H[p][ch]; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + S->re[k] += X_p_ch.re[k] * H_p_ch.re[k] - X_p_ch.im[k] * H_p_ch.im[k]; + S->im[k] += X_p_ch.re[k] * H_p_ch.im[k] + X_p_ch.im[k] * H_p_ch.re[k]; + } + } + index = index < (render_buffer_data.size() - 1) ? index + 1 : 0; + } +} + +#if defined(WEBRTC_HAS_NEON) +// Produces the filter output (Neon variant). +void ApplyFilter_Neon(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S) { + // const RenderBuffer& render_buffer, + // rtc::ArrayView H, + // FftData* S) { + RTC_DCHECK_GE(H.size(), H.size() - 1); + S->Clear(); + + rtc::ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumFourBinBands = kFftLengthBy2 / 4; + + size_t X_partition = render_buffer.Position(); + size_t p = 0; + size_t limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + for (size_t k = 0, n = 0; n < kNumFourBinBands; ++n, k += 4) { + const float32x4_t X_re = vld1q_f32(&X.re[k]); + const float32x4_t X_im = vld1q_f32(&X.im[k]); + const float32x4_t H_re = vld1q_f32(&H_p_ch.re[k]); + const float32x4_t H_im = vld1q_f32(&H_p_ch.im[k]); + const float32x4_t S_re = vld1q_f32(&S->re[k]); + const float32x4_t S_im = vld1q_f32(&S->im[k]); + const float32x4_t a = vmulq_f32(X_re, H_re); + const float32x4_t e = vmlsq_f32(a, X_im, H_im); + const float32x4_t c = vmulq_f32(X_re, H_im); + const float32x4_t f = vmlaq_f32(c, X_im, H_re); + const float32x4_t g = vaddq_f32(S_re, e); + const float32x4_t h = vaddq_f32(S_im, f); + vst1q_f32(&S->re[k], g); + vst1q_f32(&S->im[k], h); + } + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); + + X_partition = render_buffer.Position(); + p = 0; + limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + S->re[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2] - + X.im[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2]; + S->im[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2] + + X.im[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2]; + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); +} +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) +// Produces the filter output (SSE2 variant). +void ApplyFilter_Sse2(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S) { + // const RenderBuffer& render_buffer, + // rtc::ArrayView H, + // FftData* S) { + RTC_DCHECK_GE(H.size(), H.size() - 1); + S->re.fill(0.f); + S->im.fill(0.f); + + rtc::ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumFourBinBands = kFftLengthBy2 / 4; + + size_t X_partition = render_buffer.Position(); + size_t p = 0; + size_t limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + for (size_t k = 0, n = 0; n < kNumFourBinBands; ++n, k += 4) { + const __m128 X_re = _mm_loadu_ps(&X.re[k]); + const __m128 X_im = _mm_loadu_ps(&X.im[k]); + const __m128 H_re = _mm_loadu_ps(&H_p_ch.re[k]); + const __m128 H_im = _mm_loadu_ps(&H_p_ch.im[k]); + const __m128 S_re = _mm_loadu_ps(&S->re[k]); + const __m128 S_im = _mm_loadu_ps(&S->im[k]); + const __m128 a = _mm_mul_ps(X_re, H_re); + const __m128 b = _mm_mul_ps(X_im, H_im); + const __m128 c = _mm_mul_ps(X_re, H_im); + const __m128 d = _mm_mul_ps(X_im, H_re); + const __m128 e = _mm_sub_ps(a, b); + const __m128 f = _mm_add_ps(c, d); + const __m128 g = _mm_add_ps(S_re, e); + const __m128 h = _mm_add_ps(S_im, f); + _mm_storeu_ps(&S->re[k], g); + _mm_storeu_ps(&S->im[k], h); + } + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); + + X_partition = render_buffer.Position(); + p = 0; + limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + S->re[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2] - + X.im[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2]; + S->im[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2] + + X.im[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2]; + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); +} +#endif + +} // namespace aec3 + +namespace { + +// Ensures that the newly added filter partitions after a size increase are set +// to zero. +void ZeroFilter(size_t old_size, + size_t new_size, + std::vector>* H) { + RTC_DCHECK_GE(H->size(), old_size); + RTC_DCHECK_GE(H->size(), new_size); + + for (size_t p = old_size; p < new_size; ++p) { + RTC_DCHECK_EQ((*H)[p].size(), (*H)[0].size()); + for (size_t ch = 0; ch < (*H)[0].size(); ++ch) { + (*H)[p][ch].Clear(); + } + } +} + +} // namespace + +AdaptiveFirFilter::AdaptiveFirFilter(size_t max_size_partitions, + size_t initial_size_partitions, + size_t size_change_duration_blocks, + size_t num_render_channels, + Aec3Optimization optimization, + ApmDataDumper* data_dumper) + : data_dumper_(data_dumper), + fft_(), + optimization_(optimization), + num_render_channels_(num_render_channels), + max_size_partitions_(max_size_partitions), + size_change_duration_blocks_( + static_cast(size_change_duration_blocks)), + current_size_partitions_(initial_size_partitions), + target_size_partitions_(initial_size_partitions), + old_target_size_partitions_(initial_size_partitions), + H_(max_size_partitions_, std::vector(num_render_channels_)) { + RTC_DCHECK(data_dumper_); + RTC_DCHECK_GE(max_size_partitions, initial_size_partitions); + + RTC_DCHECK_LT(0, size_change_duration_blocks_); + one_by_size_change_duration_blocks_ = 1.f / size_change_duration_blocks_; + + ZeroFilter(0, max_size_partitions_, &H_); + + SetSizePartitions(current_size_partitions_, true); +} + +AdaptiveFirFilter::~AdaptiveFirFilter() = default; + +void AdaptiveFirFilter::HandleEchoPathChange() { + // TODO(peah): Check the value and purpose of the code below. + ZeroFilter(current_size_partitions_, max_size_partitions_, &H_); +} + +void AdaptiveFirFilter::SetSizePartitions(size_t size, bool immediate_effect) { + RTC_DCHECK_EQ(max_size_partitions_, H_.capacity()); + RTC_DCHECK_LE(size, max_size_partitions_); + + target_size_partitions_ = std::min(max_size_partitions_, size); + if (immediate_effect) { + size_t old_size_partitions_ = current_size_partitions_; + current_size_partitions_ = old_target_size_partitions_ = + target_size_partitions_; + ZeroFilter(old_size_partitions_, current_size_partitions_, &H_); + + partition_to_constrain_ = + std::min(partition_to_constrain_, current_size_partitions_ - 1); + size_change_counter_ = 0; + } else { + size_change_counter_ = size_change_duration_blocks_; + } +} + +void AdaptiveFirFilter::UpdateSize() { + RTC_DCHECK_GE(size_change_duration_blocks_, size_change_counter_); + size_t old_size_partitions_ = current_size_partitions_; + if (size_change_counter_ > 0) { + --size_change_counter_; + + auto average = [](float from, float to, float from_weight) { + return from * from_weight + to * (1.f - from_weight); + }; + + float change_factor = + size_change_counter_ * one_by_size_change_duration_blocks_; + + current_size_partitions_ = average(old_target_size_partitions_, + target_size_partitions_, change_factor); + + partition_to_constrain_ = + std::min(partition_to_constrain_, current_size_partitions_ - 1); + } else { + current_size_partitions_ = old_target_size_partitions_ = + target_size_partitions_; + } + ZeroFilter(old_size_partitions_, current_size_partitions_, &H_); + RTC_DCHECK_LE(0, size_change_counter_); +} + +void AdaptiveFirFilter::Filter(const RenderBuffer& render_buffer, + FftData* S) const { + RTC_DCHECK(S); + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: + aec3::ApplyFilter_Sse2(render_buffer, current_size_partitions_, H_, S); + break; + case Aec3Optimization::kAvx2: + aec3::ApplyFilter_Avx2(render_buffer, current_size_partitions_, H_, S); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: + aec3::ApplyFilter_Neon(render_buffer, current_size_partitions_, H_, S); + break; +#endif + default: + aec3::ApplyFilter(render_buffer, current_size_partitions_, H_, S); + } +} + +void AdaptiveFirFilter::Adapt(const RenderBuffer& render_buffer, + const FftData& G) { + // Adapt the filter and update the filter size. + AdaptAndUpdateSize(render_buffer, G); + + // Constrain the filter partitions in a cyclic manner. + Constrain(); +} + +void AdaptiveFirFilter::Adapt(const RenderBuffer& render_buffer, + const FftData& G, + std::vector* impulse_response) { + // Adapt the filter and update the filter size. + AdaptAndUpdateSize(render_buffer, G); + + // Constrain the filter partitions in a cyclic manner. + ConstrainAndUpdateImpulseResponse(impulse_response); +} + +void AdaptiveFirFilter::ComputeFrequencyResponse( + std::vector>* H2) const { + RTC_DCHECK_GE(max_size_partitions_, H2->capacity()); + + H2->resize(current_size_partitions_); + + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: + aec3::ComputeFrequencyResponse_Sse2(current_size_partitions_, H_, H2); + break; + case Aec3Optimization::kAvx2: + aec3::ComputeFrequencyResponse_Avx2(current_size_partitions_, H_, H2); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: + aec3::ComputeFrequencyResponse_Neon(current_size_partitions_, H_, H2); + break; +#endif + default: + aec3::ComputeFrequencyResponse(current_size_partitions_, H_, H2); + } +} + +void AdaptiveFirFilter::AdaptAndUpdateSize(const RenderBuffer& render_buffer, + const FftData& G) { + // Update the filter size if needed. + UpdateSize(); + + // Adapt the filter. + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: + aec3::AdaptPartitions_Sse2(render_buffer, G, current_size_partitions_, + &H_); + break; + case Aec3Optimization::kAvx2: + aec3::AdaptPartitions_Avx2(render_buffer, G, current_size_partitions_, + &H_); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: + aec3::AdaptPartitions_Neon(render_buffer, G, current_size_partitions_, + &H_); + break; +#endif + default: + aec3::AdaptPartitions(render_buffer, G, current_size_partitions_, &H_); + } +} + +// Constrains the partition of the frequency domain filter to be limited in +// time via setting the relevant time-domain coefficients to zero and updates +// the corresponding values in an externally stored impulse response estimate. +void AdaptiveFirFilter::ConstrainAndUpdateImpulseResponse( + std::vector* impulse_response) { + RTC_DCHECK_EQ(GetTimeDomainLength(max_size_partitions_), + impulse_response->capacity()); + impulse_response->resize(GetTimeDomainLength(current_size_partitions_)); + std::array h; + impulse_response->resize(GetTimeDomainLength(current_size_partitions_)); + std::fill( + impulse_response->begin() + partition_to_constrain_ * kFftLengthBy2, + impulse_response->begin() + (partition_to_constrain_ + 1) * kFftLengthBy2, + 0.f); + + for (size_t ch = 0; ch < num_render_channels_; ++ch) { + fft_.Ifft(H_[partition_to_constrain_][ch], &h); + + static constexpr float kScale = 1.0f / kFftLengthBy2; + std::for_each(h.begin(), h.begin() + kFftLengthBy2, + [](float& a) { a *= kScale; }); + std::fill(h.begin() + kFftLengthBy2, h.end(), 0.f); + + if (ch == 0) { + std::copy( + h.begin(), h.begin() + kFftLengthBy2, + impulse_response->begin() + partition_to_constrain_ * kFftLengthBy2); + } else { + for (size_t k = 0, j = partition_to_constrain_ * kFftLengthBy2; + k < kFftLengthBy2; ++k, ++j) { + if (fabsf((*impulse_response)[j]) < fabsf(h[k])) { + (*impulse_response)[j] = h[k]; + } + } + } + + fft_.Fft(&h, &H_[partition_to_constrain_][ch]); + } + + partition_to_constrain_ = + partition_to_constrain_ < (current_size_partitions_ - 1) + ? partition_to_constrain_ + 1 + : 0; +} + +// Constrains the a partiton of the frequency domain filter to be limited in +// time via setting the relevant time-domain coefficients to zero. +void AdaptiveFirFilter::Constrain() { + std::array h; + for (size_t ch = 0; ch < num_render_channels_; ++ch) { + fft_.Ifft(H_[partition_to_constrain_][ch], &h); + + static constexpr float kScale = 1.0f / kFftLengthBy2; + std::for_each(h.begin(), h.begin() + kFftLengthBy2, + [](float& a) { a *= kScale; }); + std::fill(h.begin() + kFftLengthBy2, h.end(), 0.f); + + fft_.Fft(&h, &H_[partition_to_constrain_][ch]); + } + + partition_to_constrain_ = + partition_to_constrain_ < (current_size_partitions_ - 1) + ? partition_to_constrain_ + 1 + : 0; +} + +void AdaptiveFirFilter::ScaleFilter(float factor) { + for (auto& H_p : H_) { + for (auto& H_p_ch : H_p) { + for (auto& re : H_p_ch.re) { + re *= factor; + } + for (auto& im : H_p_ch.im) { + im *= factor; + } + } + } +} + +// Set the filter coefficients. +void AdaptiveFirFilter::SetFilter(size_t num_partitions, + const std::vector>& H) { + const size_t min_num_partitions = + std::min(current_size_partitions_, num_partitions); + for (size_t p = 0; p < min_num_partitions; ++p) { + RTC_DCHECK_EQ(H_[p].size(), H[p].size()); + RTC_DCHECK_EQ(num_render_channels_, H_[p].size()); + + for (size_t ch = 0; ch < num_render_channels_; ++ch) { + std::copy(H[p][ch].re.begin(), H[p][ch].re.end(), H_[p][ch].re.begin()); + std::copy(H[p][ch].im.begin(), H[p][ch].im.end(), H_[p][ch].im.begin()); + } + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h new file mode 100644 index 0000000..7597709 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec3_fft.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace aec3 { +// Computes and stores the frequency response of the filter. +void ComputeFrequencyResponse( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2); +#if defined(WEBRTC_HAS_NEON) +void ComputeFrequencyResponse_Neon( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2); +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +void ComputeFrequencyResponse_Sse2( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2); + +void ComputeFrequencyResponse_Avx2( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2); +#endif + +// Adapts the filter partitions. +void AdaptPartitions(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H); +#if defined(WEBRTC_HAS_NEON) +void AdaptPartitions_Neon(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H); +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +void AdaptPartitions_Sse2(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H); + +void AdaptPartitions_Avx2(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H); +#endif + +// Produces the filter output. +void ApplyFilter(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S); +#if defined(WEBRTC_HAS_NEON) +void ApplyFilter_Neon(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S); +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +void ApplyFilter_Sse2(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S); + +void ApplyFilter_Avx2(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S); +#endif + +} // namespace aec3 + +// Provides a frequency domain adaptive filter functionality. +class AdaptiveFirFilter { + public: + AdaptiveFirFilter(size_t max_size_partitions, + size_t initial_size_partitions, + size_t size_change_duration_blocks, + size_t num_render_channels, + Aec3Optimization optimization, + ApmDataDumper* data_dumper); + + ~AdaptiveFirFilter(); + + AdaptiveFirFilter(const AdaptiveFirFilter&) = delete; + AdaptiveFirFilter& operator=(const AdaptiveFirFilter&) = delete; + + // Produces the output of the filter. + void Filter(const RenderBuffer& render_buffer, FftData* S) const; + + // Adapts the filter and updates an externally stored impulse response + // estimate. + void Adapt(const RenderBuffer& render_buffer, + const FftData& G, + std::vector* impulse_response); + + // Adapts the filter. + void Adapt(const RenderBuffer& render_buffer, const FftData& G); + + // Receives reports that known echo path changes have occured and adjusts + // the filter adaptation accordingly. + void HandleEchoPathChange(); + + // Returns the filter size. + size_t SizePartitions() const { return current_size_partitions_; } + + // Sets the filter size. + void SetSizePartitions(size_t size, bool immediate_effect); + + // Computes the frequency responses for the filter partitions. + void ComputeFrequencyResponse( + std::vector>* H2) const; + + // Returns the maximum number of partitions for the filter. + size_t max_filter_size_partitions() const { return max_size_partitions_; } + + void DumpFilter(const char* name_frequency_domain) { + for (size_t p = 0; p < max_size_partitions_; ++p) { + data_dumper_->DumpRaw(name_frequency_domain, H_[p][0].re); + data_dumper_->DumpRaw(name_frequency_domain, H_[p][0].im); + } + } + + // Scale the filter impulse response and spectrum by a factor. + void ScaleFilter(float factor); + + // Set the filter coefficients. + void SetFilter(size_t num_partitions, + const std::vector>& H); + + // Gets the filter coefficients. + const std::vector>& GetFilter() const { return H_; } + + private: + // Adapts the filter and updates the filter size. + void AdaptAndUpdateSize(const RenderBuffer& render_buffer, const FftData& G); + + // Constrain the filter partitions in a cyclic manner. + void Constrain(); + // Constrains the filter in a cyclic manner and updates the corresponding + // values in the supplied impulse response. + void ConstrainAndUpdateImpulseResponse(std::vector* impulse_response); + + // Gradually Updates the current filter size towards the target size. + void UpdateSize(); + + ApmDataDumper* const data_dumper_; + const Aec3Fft fft_; + const Aec3Optimization optimization_; + const size_t num_render_channels_; + const size_t max_size_partitions_; + const int size_change_duration_blocks_; + float one_by_size_change_duration_blocks_; + size_t current_size_partitions_; + size_t target_size_partitions_; + size_t old_target_size_partitions_; + int size_change_counter_ = 0; + std::vector> H_; + size_t partition_to_constrain_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_H_ diff --git a/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc new file mode 100644 index 0000000..245b45a --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2020 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 "modules/audio_processing/aec3/adaptive_fir_filter.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace aec3 { + +// Computes and stores the frequency response of the filter. +void ComputeFrequencyResponse_Avx2( + size_t num_partitions, + const std::vector>& H, + std::vector>* H2) { + for (auto& H2_ch : *H2) { + H2_ch.fill(0.f); + } + + const size_t num_render_channels = H[0].size(); + RTC_DCHECK_EQ(H.size(), H2->capacity()); + for (size_t p = 0; p < num_partitions; ++p) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, (*H2)[p].size()); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + for (size_t j = 0; j < kFftLengthBy2; j += 8) { + __m256 re = _mm256_loadu_ps(&H[p][ch].re[j]); + __m256 re2 = _mm256_mul_ps(re, re); + __m256 im = _mm256_loadu_ps(&H[p][ch].im[j]); + re2 = _mm256_fmadd_ps(im, im, re2); + __m256 H2_k_j = _mm256_loadu_ps(&(*H2)[p][j]); + H2_k_j = _mm256_max_ps(H2_k_j, re2); + _mm256_storeu_ps(&(*H2)[p][j], H2_k_j); + } + float H2_new = H[p][ch].re[kFftLengthBy2] * H[p][ch].re[kFftLengthBy2] + + H[p][ch].im[kFftLengthBy2] * H[p][ch].im[kFftLengthBy2]; + (*H2)[p][kFftLengthBy2] = std::max((*H2)[p][kFftLengthBy2], H2_new); + } + } +} + +// Adapts the filter partitions. +void AdaptPartitions_Avx2(const RenderBuffer& render_buffer, + const FftData& G, + size_t num_partitions, + std::vector>* H) { + rtc::ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumEightBinBands = kFftLengthBy2 / 8; + + size_t X_partition = render_buffer.Position(); + size_t limit = lim1; + size_t p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + + for (size_t k = 0, n = 0; n < kNumEightBinBands; ++n, k += 8) { + const __m256 G_re = _mm256_loadu_ps(&G.re[k]); + const __m256 G_im = _mm256_loadu_ps(&G.im[k]); + const __m256 X_re = _mm256_loadu_ps(&X.re[k]); + const __m256 X_im = _mm256_loadu_ps(&X.im[k]); + const __m256 H_re = _mm256_loadu_ps(&H_p_ch.re[k]); + const __m256 H_im = _mm256_loadu_ps(&H_p_ch.im[k]); + const __m256 a = _mm256_mul_ps(X_re, G_re); + const __m256 b = _mm256_mul_ps(X_im, G_im); + const __m256 c = _mm256_mul_ps(X_re, G_im); + const __m256 d = _mm256_mul_ps(X_im, G_re); + const __m256 e = _mm256_add_ps(a, b); + const __m256 f = _mm256_sub_ps(c, d); + const __m256 g = _mm256_add_ps(H_re, e); + const __m256 h = _mm256_add_ps(H_im, f); + _mm256_storeu_ps(&H_p_ch.re[k], g); + _mm256_storeu_ps(&H_p_ch.im[k], h); + } + } + } + X_partition = 0; + limit = lim2; + } while (p < lim2); + + X_partition = render_buffer.Position(); + limit = lim1; + p = 0; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + FftData& H_p_ch = (*H)[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + + H_p_ch.re[kFftLengthBy2] += X.re[kFftLengthBy2] * G.re[kFftLengthBy2] + + X.im[kFftLengthBy2] * G.im[kFftLengthBy2]; + H_p_ch.im[kFftLengthBy2] += X.re[kFftLengthBy2] * G.im[kFftLengthBy2] - + X.im[kFftLengthBy2] * G.re[kFftLengthBy2]; + } + } + + X_partition = 0; + limit = lim2; + } while (p < lim2); +} + +// Produces the filter output (AVX2 variant). +void ApplyFilter_Avx2(const RenderBuffer& render_buffer, + size_t num_partitions, + const std::vector>& H, + FftData* S) { + RTC_DCHECK_GE(H.size(), H.size() - 1); + S->re.fill(0.f); + S->im.fill(0.f); + + rtc::ArrayView> render_buffer_data = + render_buffer.GetFftBuffer(); + const size_t num_render_channels = render_buffer_data[0].size(); + const size_t lim1 = std::min( + render_buffer_data.size() - render_buffer.Position(), num_partitions); + const size_t lim2 = num_partitions; + constexpr size_t kNumEightBinBands = kFftLengthBy2 / 8; + + size_t X_partition = render_buffer.Position(); + size_t p = 0; + size_t limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + for (size_t k = 0, n = 0; n < kNumEightBinBands; ++n, k += 8) { + const __m256 X_re = _mm256_loadu_ps(&X.re[k]); + const __m256 X_im = _mm256_loadu_ps(&X.im[k]); + const __m256 H_re = _mm256_loadu_ps(&H_p_ch.re[k]); + const __m256 H_im = _mm256_loadu_ps(&H_p_ch.im[k]); + const __m256 S_re = _mm256_loadu_ps(&S->re[k]); + const __m256 S_im = _mm256_loadu_ps(&S->im[k]); + const __m256 a = _mm256_mul_ps(X_re, H_re); + const __m256 b = _mm256_mul_ps(X_im, H_im); + const __m256 c = _mm256_mul_ps(X_re, H_im); + const __m256 d = _mm256_mul_ps(X_im, H_re); + const __m256 e = _mm256_sub_ps(a, b); + const __m256 f = _mm256_add_ps(c, d); + const __m256 g = _mm256_add_ps(S_re, e); + const __m256 h = _mm256_add_ps(S_im, f); + _mm256_storeu_ps(&S->re[k], g); + _mm256_storeu_ps(&S->im[k], h); + } + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); + + X_partition = render_buffer.Position(); + p = 0; + limit = lim1; + do { + for (; p < limit; ++p, ++X_partition) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const FftData& H_p_ch = H[p][ch]; + const FftData& X = render_buffer_data[X_partition][ch]; + S->re[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2] - + X.im[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2]; + S->im[kFftLengthBy2] += X.re[kFftLengthBy2] * H_p_ch.im[kFftLengthBy2] + + X.im[kFftLengthBy2] * H_p_ch.re[kFftLengthBy2]; + } + } + limit = lim2; + X_partition = 0; + } while (p < lim2); +} + +} // namespace aec3 +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc new file mode 100644 index 0000000..45b8813 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/aec3/adaptive_fir_filter_erl.h" + +#include +#include + +#if defined(WEBRTC_HAS_NEON) +#include +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif + +namespace webrtc { + +namespace aec3 { + +// Computes and stores the echo return loss estimate of the filter, which is the +// sum of the partition frequency responses. +void ErlComputer(const std::vector>& H2, + rtc::ArrayView erl) { + std::fill(erl.begin(), erl.end(), 0.f); + for (auto& H2_j : H2) { + std::transform(H2_j.begin(), H2_j.end(), erl.begin(), erl.begin(), + std::plus()); + } +} + +#if defined(WEBRTC_HAS_NEON) +// Computes and stores the echo return loss estimate of the filter, which is the +// sum of the partition frequency responses. +void ErlComputer_NEON( + const std::vector>& H2, + rtc::ArrayView erl) { + std::fill(erl.begin(), erl.end(), 0.f); + for (auto& H2_j : H2) { + for (size_t k = 0; k < kFftLengthBy2; k += 4) { + const float32x4_t H2_j_k = vld1q_f32(&H2_j[k]); + float32x4_t erl_k = vld1q_f32(&erl[k]); + erl_k = vaddq_f32(erl_k, H2_j_k); + vst1q_f32(&erl[k], erl_k); + } + erl[kFftLengthBy2] += H2_j[kFftLengthBy2]; + } +} +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) +// Computes and stores the echo return loss estimate of the filter, which is the +// sum of the partition frequency responses. +void ErlComputer_SSE2( + const std::vector>& H2, + rtc::ArrayView erl) { + std::fill(erl.begin(), erl.end(), 0.f); + for (auto& H2_j : H2) { + for (size_t k = 0; k < kFftLengthBy2; k += 4) { + const __m128 H2_j_k = _mm_loadu_ps(&H2_j[k]); + __m128 erl_k = _mm_loadu_ps(&erl[k]); + erl_k = _mm_add_ps(erl_k, H2_j_k); + _mm_storeu_ps(&erl[k], erl_k); + } + erl[kFftLengthBy2] += H2_j[kFftLengthBy2]; + } +} +#endif + +} // namespace aec3 + +void ComputeErl(const Aec3Optimization& optimization, + const std::vector>& H2, + rtc::ArrayView erl) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, erl.size()); + // Update the frequency response and echo return loss for the filter. + switch (optimization) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: + aec3::ErlComputer_SSE2(H2, erl); + break; + case Aec3Optimization::kAvx2: + aec3::ErlComputer_AVX2(H2, erl); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: + aec3::ErlComputer_NEON(H2, erl); + break; +#endif + default: + aec3::ErlComputer(H2, erl); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.h b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.h new file mode 100644 index 0000000..4ac13b1 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_ERL_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_ERL_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace aec3 { + +// Computes and stores the echo return loss estimate of the filter, which is the +// sum of the partition frequency responses. +void ErlComputer(const std::vector>& H2, + rtc::ArrayView erl); +#if defined(WEBRTC_HAS_NEON) +void ErlComputer_NEON( + const std::vector>& H2, + rtc::ArrayView erl); +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +void ErlComputer_SSE2( + const std::vector>& H2, + rtc::ArrayView erl); + +void ErlComputer_AVX2( + const std::vector>& H2, + rtc::ArrayView erl); +#endif + +} // namespace aec3 + +// Computes the echo return loss based on a frequency response. +void ComputeErl(const Aec3Optimization& optimization, + const std::vector>& H2, + rtc::ArrayView erl); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ADAPTIVE_FIR_FILTER_ERL_H_ diff --git a/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl_avx2.cc b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl_avx2.cc new file mode 100644 index 0000000..5fe7514 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_erl_avx2.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 "modules/audio_processing/aec3/adaptive_fir_filter_erl.h" + +#include + +namespace webrtc { + +namespace aec3 { + +// Computes and stores the echo return loss estimate of the filter, which is the +// sum of the partition frequency responses. +void ErlComputer_AVX2( + const std::vector>& H2, + rtc::ArrayView erl) { + std::fill(erl.begin(), erl.end(), 0.f); + for (auto& H2_j : H2) { + for (size_t k = 0; k < kFftLengthBy2; k += 8) { + const __m256 H2_j_k = _mm256_loadu_ps(&H2_j[k]); + __m256 erl_k = _mm256_loadu_ps(&erl[k]); + erl_k = _mm256_add_ps(erl_k, H2_j_k); + _mm256_storeu_ps(&erl[k], erl_k); + } + erl[kFftLengthBy2] += H2_j[kFftLengthBy2]; + } +} + +} // namespace aec3 +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/aec3_common.cc b/webrtc/modules/audio_processing/aec3/aec3_common.cc new file mode 100644 index 0000000..7bd8d62 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/aec3_common.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/aec3_common.h" + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" + +namespace webrtc { + +Aec3Optimization DetectOptimization() { +#if defined(WEBRTC_ARCH_X86_FAMILY) + if (GetCPUInfo(kAVX2) != 0) { + return Aec3Optimization::kAvx2; + } else if (GetCPUInfo(kSSE2) != 0) { + return Aec3Optimization::kSse2; + } +#endif + +#if defined(WEBRTC_HAS_NEON) + return Aec3Optimization::kNeon; +#endif + + return Aec3Optimization::kNone; +} + +float FastApproxLog2f(const float in) { + RTC_DCHECK_GT(in, .0f); + // Read and interpret float as uint32_t and then cast to float. + // This is done to extract the exponent (bits 30 - 23). + // "Right shift" of the exponent is then performed by multiplying + // with the constant (1/2^23). Finally, we subtract a constant to + // remove the bias (https://en.wikipedia.org/wiki/Exponent_bias). + union { + float dummy; + uint32_t a; + } x = {in}; + float out = x.a; + out *= 1.1920929e-7f; // 1/2^23 + out -= 126.942695f; // Remove bias. + return out; +} + +float Log2TodB(const float in_log2) { + return 3.0102999566398121 * in_log2; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/aec3_common.h b/webrtc/modules/audio_processing/aec3/aec3_common.h new file mode 100644 index 0000000..a7e3121 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/aec3_common.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_AEC3_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_AEC3_COMMON_H_ + +#include + +namespace webrtc { + +#ifdef _MSC_VER /* visual c++ */ +#define ALIGN16_BEG __declspec(align(16)) +#define ALIGN16_END +#else /* gcc or icc */ +#define ALIGN16_BEG +#define ALIGN16_END __attribute__((aligned(16))) +#endif + +enum class Aec3Optimization { kNone, kSse2, kAvx2, kNeon }; + +constexpr int kNumBlocksPerSecond = 250; + +constexpr int kMetricsReportingIntervalBlocks = 10 * kNumBlocksPerSecond; +constexpr int kMetricsComputationBlocks = 7; +constexpr int kMetricsCollectionBlocks = + kMetricsReportingIntervalBlocks - kMetricsComputationBlocks; + +constexpr size_t kFftLengthBy2 = 64; +constexpr size_t kFftLengthBy2Plus1 = kFftLengthBy2 + 1; +constexpr size_t kFftLengthBy2Minus1 = kFftLengthBy2 - 1; +constexpr size_t kFftLength = 2 * kFftLengthBy2; +constexpr size_t kFftLengthBy2Log2 = 6; + +constexpr int kRenderTransferQueueSizeFrames = 100; + +constexpr size_t kMaxNumBands = 3; +constexpr size_t kFrameSize = 160; +constexpr size_t kSubFrameLength = kFrameSize / 2; + +constexpr size_t kBlockSize = kFftLengthBy2; +constexpr size_t kBlockSizeLog2 = kFftLengthBy2Log2; + +constexpr size_t kExtendedBlockSize = 2 * kFftLengthBy2; +constexpr size_t kMatchedFilterWindowSizeSubBlocks = 32; +constexpr size_t kMatchedFilterAlignmentShiftSizeSubBlocks = + kMatchedFilterWindowSizeSubBlocks * 3 / 4; + +// TODO(peah): Integrate this with how it is done inside audio_processing_impl. +constexpr size_t NumBandsForRate(int sample_rate_hz) { + return static_cast(sample_rate_hz / 16000); +} + +constexpr bool ValidFullBandRate(int sample_rate_hz) { + return sample_rate_hz == 16000 || sample_rate_hz == 32000 || + sample_rate_hz == 48000; +} + +constexpr int GetTimeDomainLength(int filter_length_blocks) { + return filter_length_blocks * kFftLengthBy2; +} + +constexpr size_t GetDownSampledBufferSize(size_t down_sampling_factor, + size_t num_matched_filters) { + return kBlockSize / down_sampling_factor * + (kMatchedFilterAlignmentShiftSizeSubBlocks * num_matched_filters + + kMatchedFilterWindowSizeSubBlocks + 1); +} + +constexpr size_t GetRenderDelayBufferSize(size_t down_sampling_factor, + size_t num_matched_filters, + size_t filter_length_blocks) { + return GetDownSampledBufferSize(down_sampling_factor, num_matched_filters) / + (kBlockSize / down_sampling_factor) + + filter_length_blocks + 1; +} + +// Detects what kind of optimizations to use for the code. +Aec3Optimization DetectOptimization(); + +// Computes the log2 of the input in a fast an approximate manner. +float FastApproxLog2f(const float in); + +// Returns dB from a power quantity expressed in log2. +float Log2TodB(const float in_log2); + +static_assert(1 << kBlockSizeLog2 == kBlockSize, + "Proper number of shifts for blocksize"); + +static_assert(1 << kFftLengthBy2Log2 == kFftLengthBy2, + "Proper number of shifts for the fft length"); + +static_assert(1 == NumBandsForRate(16000), "Number of bands for 16 kHz"); +static_assert(2 == NumBandsForRate(32000), "Number of bands for 32 kHz"); +static_assert(3 == NumBandsForRate(48000), "Number of bands for 48 kHz"); + +static_assert(ValidFullBandRate(16000), + "Test that 16 kHz is a valid sample rate"); +static_assert(ValidFullBandRate(32000), + "Test that 32 kHz is a valid sample rate"); +static_assert(ValidFullBandRate(48000), + "Test that 48 kHz is a valid sample rate"); +static_assert(!ValidFullBandRate(8001), + "Test that 8001 Hz is not a valid sample rate"); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_AEC3_COMMON_H_ diff --git a/webrtc/modules/audio_processing/aec3/aec3_fft.cc b/webrtc/modules/audio_processing/aec3/aec3_fft.cc new file mode 100644 index 0000000..8dfa183 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/aec3_fft.cc @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/aec3_fft.h" + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "system_wrappers/include/cpu_features_wrapper.h" + +namespace webrtc { + +namespace { + +const float kHanning64[kFftLengthBy2] = { + 0.f, 0.00248461f, 0.00991376f, 0.0222136f, 0.03926189f, + 0.06088921f, 0.08688061f, 0.11697778f, 0.15088159f, 0.1882551f, + 0.22872687f, 0.27189467f, 0.31732949f, 0.36457977f, 0.41317591f, + 0.46263495f, 0.51246535f, 0.56217185f, 0.61126047f, 0.65924333f, + 0.70564355f, 0.75f, 0.79187184f, 0.83084292f, 0.86652594f, + 0.89856625f, 0.92664544f, 0.95048443f, 0.96984631f, 0.98453864f, + 0.99441541f, 0.99937846f, 0.99937846f, 0.99441541f, 0.98453864f, + 0.96984631f, 0.95048443f, 0.92664544f, 0.89856625f, 0.86652594f, + 0.83084292f, 0.79187184f, 0.75f, 0.70564355f, 0.65924333f, + 0.61126047f, 0.56217185f, 0.51246535f, 0.46263495f, 0.41317591f, + 0.36457977f, 0.31732949f, 0.27189467f, 0.22872687f, 0.1882551f, + 0.15088159f, 0.11697778f, 0.08688061f, 0.06088921f, 0.03926189f, + 0.0222136f, 0.00991376f, 0.00248461f, 0.f}; + +// Hanning window from Matlab command win = sqrt(hanning(128)). +const float kSqrtHanning128[kFftLength] = { + 0.00000000000000f, 0.02454122852291f, 0.04906767432742f, 0.07356456359967f, + 0.09801714032956f, 0.12241067519922f, 0.14673047445536f, 0.17096188876030f, + 0.19509032201613f, 0.21910124015687f, 0.24298017990326f, 0.26671275747490f, + 0.29028467725446f, 0.31368174039889f, 0.33688985339222f, 0.35989503653499f, + 0.38268343236509f, 0.40524131400499f, 0.42755509343028f, 0.44961132965461f, + 0.47139673682600f, 0.49289819222978f, 0.51410274419322f, 0.53499761988710f, + 0.55557023301960f, 0.57580819141785f, 0.59569930449243f, 0.61523159058063f, + 0.63439328416365f, 0.65317284295378f, 0.67155895484702f, 0.68954054473707f, + 0.70710678118655f, 0.72424708295147f, 0.74095112535496f, 0.75720884650648f, + 0.77301045336274f, 0.78834642762661f, 0.80320753148064f, 0.81758481315158f, + 0.83146961230255f, 0.84485356524971f, 0.85772861000027f, 0.87008699110871f, + 0.88192126434835f, 0.89322430119552f, 0.90398929312344f, 0.91420975570353f, + 0.92387953251129f, 0.93299279883474f, 0.94154406518302f, 0.94952818059304f, + 0.95694033573221f, 0.96377606579544f, 0.97003125319454f, 0.97570213003853f, + 0.98078528040323f, 0.98527764238894f, 0.98917650996478f, 0.99247953459871f, + 0.99518472667220f, 0.99729045667869f, 0.99879545620517f, 0.99969881869620f, + 1.00000000000000f, 0.99969881869620f, 0.99879545620517f, 0.99729045667869f, + 0.99518472667220f, 0.99247953459871f, 0.98917650996478f, 0.98527764238894f, + 0.98078528040323f, 0.97570213003853f, 0.97003125319454f, 0.96377606579544f, + 0.95694033573221f, 0.94952818059304f, 0.94154406518302f, 0.93299279883474f, + 0.92387953251129f, 0.91420975570353f, 0.90398929312344f, 0.89322430119552f, + 0.88192126434835f, 0.87008699110871f, 0.85772861000027f, 0.84485356524971f, + 0.83146961230255f, 0.81758481315158f, 0.80320753148064f, 0.78834642762661f, + 0.77301045336274f, 0.75720884650648f, 0.74095112535496f, 0.72424708295147f, + 0.70710678118655f, 0.68954054473707f, 0.67155895484702f, 0.65317284295378f, + 0.63439328416365f, 0.61523159058063f, 0.59569930449243f, 0.57580819141785f, + 0.55557023301960f, 0.53499761988710f, 0.51410274419322f, 0.49289819222978f, + 0.47139673682600f, 0.44961132965461f, 0.42755509343028f, 0.40524131400499f, + 0.38268343236509f, 0.35989503653499f, 0.33688985339222f, 0.31368174039889f, + 0.29028467725446f, 0.26671275747490f, 0.24298017990326f, 0.21910124015687f, + 0.19509032201613f, 0.17096188876030f, 0.14673047445536f, 0.12241067519922f, + 0.09801714032956f, 0.07356456359967f, 0.04906767432742f, 0.02454122852291f}; + +bool IsSse2Available() { +#if defined(WEBRTC_ARCH_X86_FAMILY) + return GetCPUInfo(kSSE2) != 0; +#else + return false; +#endif +} + +} // namespace + +Aec3Fft::Aec3Fft() : ooura_fft_(IsSse2Available()) {} + +// TODO(peah): Change x to be std::array once the rest of the code allows this. +void Aec3Fft::ZeroPaddedFft(rtc::ArrayView x, + Window window, + FftData* X) const { + RTC_DCHECK(X); + RTC_DCHECK_EQ(kFftLengthBy2, x.size()); + std::array fft; + std::fill(fft.begin(), fft.begin() + kFftLengthBy2, 0.f); + switch (window) { + case Window::kRectangular: + std::copy(x.begin(), x.end(), fft.begin() + kFftLengthBy2); + break; + case Window::kHanning: + std::transform(x.begin(), x.end(), std::begin(kHanning64), + fft.begin() + kFftLengthBy2, + [](float a, float b) { return a * b; }); + break; + case Window::kSqrtHanning: + RTC_NOTREACHED(); + break; + default: + RTC_NOTREACHED(); + } + + Fft(&fft, X); +} + +void Aec3Fft::PaddedFft(rtc::ArrayView x, + rtc::ArrayView x_old, + Window window, + FftData* X) const { + RTC_DCHECK(X); + RTC_DCHECK_EQ(kFftLengthBy2, x.size()); + RTC_DCHECK_EQ(kFftLengthBy2, x_old.size()); + std::array fft; + + switch (window) { + case Window::kRectangular: + std::copy(x_old.begin(), x_old.end(), fft.begin()); + std::copy(x.begin(), x.end(), fft.begin() + x_old.size()); + break; + case Window::kHanning: + RTC_NOTREACHED(); + break; + case Window::kSqrtHanning: + std::transform(x_old.begin(), x_old.end(), std::begin(kSqrtHanning128), + fft.begin(), std::multiplies()); + std::transform(x.begin(), x.end(), + std::begin(kSqrtHanning128) + x_old.size(), + fft.begin() + x_old.size(), std::multiplies()); + break; + default: + RTC_NOTREACHED(); + } + + Fft(&fft, X); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/aec3_fft.h b/webrtc/modules/audio_processing/aec3/aec3_fft.h new file mode 100644 index 0000000..6f7fbe4 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/aec3_fft.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_AEC3_FFT_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_AEC3_FFT_H_ + +#include + +#include "api/array_view.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "rtc_base/checks.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +// Wrapper class that provides 128 point real valued FFT functionality with the +// FftData type. +class Aec3Fft { + public: + enum class Window { kRectangular, kHanning, kSqrtHanning }; + + Aec3Fft(); + + // Computes the FFT. Note that both the input and output are modified. + void Fft(std::array* x, FftData* X) const { + RTC_DCHECK(x); + RTC_DCHECK(X); + ooura_fft_.Fft(x->data()); + X->CopyFromPackedArray(*x); + } + // Computes the inverse Fft. + void Ifft(const FftData& X, std::array* x) const { + RTC_DCHECK(x); + X.CopyToPackedArray(x); + ooura_fft_.InverseFft(x->data()); + } + + // Windows the input using a Hanning window, and then adds padding of + // kFftLengthBy2 initial zeros before computing the Fft. + void ZeroPaddedFft(rtc::ArrayView x, + Window window, + FftData* X) const; + + // Concatenates the kFftLengthBy2 values long x and x_old before computing the + // Fft. After that, x is copied to x_old. + void PaddedFft(rtc::ArrayView x, + rtc::ArrayView x_old, + FftData* X) const { + PaddedFft(x, x_old, Window::kRectangular, X); + } + + // Padded Fft using a time-domain window. + void PaddedFft(rtc::ArrayView x, + rtc::ArrayView x_old, + Window window, + FftData* X) const; + + private: + const OouraFft ooura_fft_; + + RTC_DISALLOW_COPY_AND_ASSIGN(Aec3Fft); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_AEC3_FFT_H_ diff --git a/webrtc/modules/audio_processing/aec3/aec_state.cc b/webrtc/modules/audio_processing/aec3/aec_state.cc new file mode 100644 index 0000000..df56c3a --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/aec_state.cc @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/aec_state.h" + +#include + +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" +#include "system_wrappers/include/field_trial.h" + +namespace webrtc { +namespace { + +bool DeactivateInitialStateResetAtEchoPathChange() { + return field_trial::IsEnabled( + "WebRTC-Aec3DeactivateInitialStateResetKillSwitch"); +} + +bool FullResetAtEchoPathChange() { + return !field_trial::IsEnabled("WebRTC-Aec3AecStateFullResetKillSwitch"); +} + +bool SubtractorAnalyzerResetAtEchoPathChange() { + return !field_trial::IsEnabled( + "WebRTC-Aec3AecStateSubtractorAnalyzerResetKillSwitch"); +} + +void ComputeAvgRenderReverb( + const SpectrumBuffer& spectrum_buffer, + int delay_blocks, + float reverb_decay, + ReverbModel* reverb_model, + rtc::ArrayView reverb_power_spectrum) { + RTC_DCHECK(reverb_model); + const size_t num_render_channels = spectrum_buffer.buffer[0].size(); + int idx_at_delay = + spectrum_buffer.OffsetIndex(spectrum_buffer.read, delay_blocks); + int idx_past = spectrum_buffer.IncIndex(idx_at_delay); + + std::array X2_data; + rtc::ArrayView X2; + if (num_render_channels > 1) { + auto average_channels = + [](size_t num_render_channels, + rtc::ArrayView> + spectrum_band_0, + rtc::ArrayView render_power) { + std::fill(render_power.begin(), render_power.end(), 0.f); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + render_power[k] += spectrum_band_0[ch][k]; + } + } + const float normalizer = 1.f / num_render_channels; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + render_power[k] *= normalizer; + } + }; + average_channels(num_render_channels, spectrum_buffer.buffer[idx_past], + X2_data); + reverb_model->UpdateReverbNoFreqShaping( + X2_data, /*power_spectrum_scaling=*/1.0f, reverb_decay); + + average_channels(num_render_channels, spectrum_buffer.buffer[idx_at_delay], + X2_data); + X2 = X2_data; + } else { + reverb_model->UpdateReverbNoFreqShaping( + spectrum_buffer.buffer[idx_past][/*channel=*/0], + /*power_spectrum_scaling=*/1.0f, reverb_decay); + + X2 = spectrum_buffer.buffer[idx_at_delay][/*channel=*/0]; + } + + rtc::ArrayView reverb_power = + reverb_model->reverb(); + for (size_t k = 0; k < X2.size(); ++k) { + reverb_power_spectrum[k] = X2[k] + reverb_power[k]; + } +} + +} // namespace + +int AecState::instance_count_ = 0; + +void AecState::GetResidualEchoScaling( + rtc::ArrayView residual_scaling) const { + bool filter_has_had_time_to_converge; + if (config_.filter.conservative_initial_phase) { + filter_has_had_time_to_converge = + strong_not_saturated_render_blocks_ >= 1.5f * kNumBlocksPerSecond; + } else { + filter_has_had_time_to_converge = + strong_not_saturated_render_blocks_ >= 0.8f * kNumBlocksPerSecond; + } + echo_audibility_.GetResidualEchoScaling(filter_has_had_time_to_converge, + residual_scaling); +} + +absl::optional AecState::ErleUncertainty() const { + if (SaturatedEcho()) { + return 1.f; + } + + return absl::nullopt; +} + +AecState::AecState(const EchoCanceller3Config& config, + size_t num_capture_channels) + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + config_(config), + num_capture_channels_(num_capture_channels), + deactivate_initial_state_reset_at_echo_path_change_( + DeactivateInitialStateResetAtEchoPathChange()), + full_reset_at_echo_path_change_(FullResetAtEchoPathChange()), + subtractor_analyzer_reset_at_echo_path_change_( + SubtractorAnalyzerResetAtEchoPathChange()), + initial_state_(config_), + delay_state_(config_, num_capture_channels_), + transparent_state_(TransparentMode::Create(config_)), + filter_quality_state_(config_, num_capture_channels_), + erl_estimator_(2 * kNumBlocksPerSecond), + erle_estimator_(2 * kNumBlocksPerSecond, config_, num_capture_channels_), + filter_analyzer_(config_, num_capture_channels_), + echo_audibility_( + config_.echo_audibility.use_stationarity_properties_at_init), + reverb_model_estimator_(config_, num_capture_channels_), + subtractor_output_analyzer_(num_capture_channels_) {} + +AecState::~AecState() = default; + +void AecState::HandleEchoPathChange( + const EchoPathVariability& echo_path_variability) { + const auto full_reset = [&]() { + filter_analyzer_.Reset(); + capture_signal_saturation_ = false; + strong_not_saturated_render_blocks_ = 0; + blocks_with_active_render_ = 0; + if (!deactivate_initial_state_reset_at_echo_path_change_) { + initial_state_.Reset(); + } + if (transparent_state_) { + transparent_state_->Reset(); + } + erle_estimator_.Reset(true); + erl_estimator_.Reset(); + filter_quality_state_.Reset(); + }; + + // TODO(peah): Refine the reset scheme according to the type of gain and + // delay adjustment. + + if (full_reset_at_echo_path_change_ && + echo_path_variability.delay_change != + EchoPathVariability::DelayAdjustment::kNone) { + full_reset(); + } else if (echo_path_variability.gain_change) { + erle_estimator_.Reset(false); + } + if (subtractor_analyzer_reset_at_echo_path_change_) { + subtractor_output_analyzer_.HandleEchoPathChange(); + } +} + +void AecState::Update( + const absl::optional& external_delay, + rtc::ArrayView>> + adaptive_filter_frequency_responses, + rtc::ArrayView> adaptive_filter_impulse_responses, + const RenderBuffer& render_buffer, + rtc::ArrayView> E2_refined, + rtc::ArrayView> Y2, + rtc::ArrayView subtractor_output) { + RTC_DCHECK_EQ(num_capture_channels_, Y2.size()); + RTC_DCHECK_EQ(num_capture_channels_, subtractor_output.size()); + RTC_DCHECK_EQ(num_capture_channels_, + adaptive_filter_frequency_responses.size()); + RTC_DCHECK_EQ(num_capture_channels_, + adaptive_filter_impulse_responses.size()); + + // Analyze the filter outputs and filters. + bool any_filter_converged; + bool all_filters_diverged; + subtractor_output_analyzer_.Update(subtractor_output, &any_filter_converged, + &all_filters_diverged); + + bool any_filter_consistent; + float max_echo_path_gain; + filter_analyzer_.Update(adaptive_filter_impulse_responses, render_buffer, + &any_filter_consistent, &max_echo_path_gain); + + // Estimate the direct path delay of the filter. + if (config_.filter.use_linear_filter) { + delay_state_.Update(filter_analyzer_.FilterDelaysBlocks(), external_delay, + strong_not_saturated_render_blocks_); + } + + const std::vector>& aligned_render_block = + render_buffer.Block(-delay_state_.MinDirectPathFilterDelay())[0]; + + // Update render counters. + bool active_render = false; + for (size_t ch = 0; ch < aligned_render_block.size(); ++ch) { + const float render_energy = std::inner_product( + aligned_render_block[ch].begin(), aligned_render_block[ch].end(), + aligned_render_block[ch].begin(), 0.f); + if (render_energy > (config_.render_levels.active_render_limit * + config_.render_levels.active_render_limit) * + kFftLengthBy2) { + active_render = true; + break; + } + } + blocks_with_active_render_ += active_render ? 1 : 0; + strong_not_saturated_render_blocks_ += + active_render && !SaturatedCapture() ? 1 : 0; + + std::array avg_render_spectrum_with_reverb; + + ComputeAvgRenderReverb(render_buffer.GetSpectrumBuffer(), + delay_state_.MinDirectPathFilterDelay(), ReverbDecay(), + &avg_render_reverb_, avg_render_spectrum_with_reverb); + + if (config_.echo_audibility.use_stationarity_properties) { + // Update the echo audibility evaluator. + echo_audibility_.Update(render_buffer, avg_render_reverb_.reverb(), + delay_state_.MinDirectPathFilterDelay(), + delay_state_.ExternalDelayReported()); + } + + // Update the ERL and ERLE measures. + if (initial_state_.TransitionTriggered()) { + erle_estimator_.Reset(false); + } + + erle_estimator_.Update(render_buffer, adaptive_filter_frequency_responses, + avg_render_spectrum_with_reverb, Y2, E2_refined, + subtractor_output_analyzer_.ConvergedFilters()); + + erl_estimator_.Update( + subtractor_output_analyzer_.ConvergedFilters(), + render_buffer.Spectrum(delay_state_.MinDirectPathFilterDelay()), Y2); + + // Detect and flag echo saturation. + if (config_.ep_strength.echo_can_saturate) { + saturation_detector_.Update(aligned_render_block, SaturatedCapture(), + UsableLinearEstimate(), subtractor_output, + max_echo_path_gain); + } else { + RTC_DCHECK(!saturation_detector_.SaturatedEcho()); + } + + // Update the decision on whether to use the initial state parameter set. + initial_state_.Update(active_render, SaturatedCapture()); + + // Detect whether the transparent mode should be activated. + if (transparent_state_) { + transparent_state_->Update(delay_state_.MinDirectPathFilterDelay(), + any_filter_consistent, any_filter_converged, + all_filters_diverged, active_render, + SaturatedCapture()); + } + + // Analyze the quality of the filter. + filter_quality_state_.Update(active_render, TransparentModeActive(), + SaturatedCapture(), external_delay, + any_filter_converged); + + // Update the reverb estimate. + const bool stationary_block = + config_.echo_audibility.use_stationarity_properties && + echo_audibility_.IsBlockStationary(); + + reverb_model_estimator_.Update( + filter_analyzer_.GetAdjustedFilters(), + adaptive_filter_frequency_responses, + erle_estimator_.GetInstLinearQualityEstimates(), + delay_state_.DirectPathFilterDelays(), + filter_quality_state_.UsableLinearFilterOutputs(), stationary_block); + + erle_estimator_.Dump(data_dumper_); + reverb_model_estimator_.Dump(data_dumper_.get()); + data_dumper_->DumpRaw("aec3_active_render", active_render); + data_dumper_->DumpRaw("aec3_erl", Erl()); + data_dumper_->DumpRaw("aec3_erl_time_domain", ErlTimeDomain()); + data_dumper_->DumpRaw("aec3_erle", Erle()[0]); + data_dumper_->DumpRaw("aec3_usable_linear_estimate", UsableLinearEstimate()); + data_dumper_->DumpRaw("aec3_transparent_mode", TransparentModeActive()); + data_dumper_->DumpRaw("aec3_filter_delay", + filter_analyzer_.MinFilterDelayBlocks()); + + data_dumper_->DumpRaw("aec3_any_filter_consistent", any_filter_consistent); + data_dumper_->DumpRaw("aec3_initial_state", + initial_state_.InitialStateActive()); + data_dumper_->DumpRaw("aec3_capture_saturation", SaturatedCapture()); + data_dumper_->DumpRaw("aec3_echo_saturation", SaturatedEcho()); + data_dumper_->DumpRaw("aec3_any_filter_converged", any_filter_converged); + data_dumper_->DumpRaw("aec3_all_filters_diverged", all_filters_diverged); + + data_dumper_->DumpRaw("aec3_external_delay_avaliable", + external_delay ? 1 : 0); + data_dumper_->DumpRaw("aec3_filter_tail_freq_resp_est", + GetReverbFrequencyResponse()); +} + +AecState::InitialState::InitialState(const EchoCanceller3Config& config) + : conservative_initial_phase_(config.filter.conservative_initial_phase), + initial_state_seconds_(config.filter.initial_state_seconds) { + Reset(); +} +void AecState::InitialState::InitialState::Reset() { + initial_state_ = true; + strong_not_saturated_render_blocks_ = 0; +} +void AecState::InitialState::InitialState::Update(bool active_render, + bool saturated_capture) { + strong_not_saturated_render_blocks_ += + active_render && !saturated_capture ? 1 : 0; + + // Flag whether the initial state is still active. + bool prev_initial_state = initial_state_; + if (conservative_initial_phase_) { + initial_state_ = + strong_not_saturated_render_blocks_ < 5 * kNumBlocksPerSecond; + } else { + initial_state_ = strong_not_saturated_render_blocks_ < + initial_state_seconds_ * kNumBlocksPerSecond; + } + + // Flag whether the transition from the initial state has started. + transition_triggered_ = !initial_state_ && prev_initial_state; +} + +AecState::FilterDelay::FilterDelay(const EchoCanceller3Config& config, + size_t num_capture_channels) + : delay_headroom_blocks_(config.delay.delay_headroom_samples / kBlockSize), + filter_delays_blocks_(num_capture_channels, delay_headroom_blocks_), + min_filter_delay_(delay_headroom_blocks_) {} + +void AecState::FilterDelay::Update( + rtc::ArrayView analyzer_filter_delay_estimates_blocks, + const absl::optional& external_delay, + size_t blocks_with_proper_filter_adaptation) { + // Update the delay based on the external delay. + if (external_delay && + (!external_delay_ || external_delay_->delay != external_delay->delay)) { + external_delay_ = external_delay; + external_delay_reported_ = true; + } + + // Override the estimated delay if it is not certain that the filter has had + // time to converge. + const bool delay_estimator_may_not_have_converged = + blocks_with_proper_filter_adaptation < 2 * kNumBlocksPerSecond; + if (delay_estimator_may_not_have_converged && external_delay_) { + const int delay_guess = delay_headroom_blocks_; + std::fill(filter_delays_blocks_.begin(), filter_delays_blocks_.end(), + delay_guess); + } else { + RTC_DCHECK_EQ(filter_delays_blocks_.size(), + analyzer_filter_delay_estimates_blocks.size()); + std::copy(analyzer_filter_delay_estimates_blocks.begin(), + analyzer_filter_delay_estimates_blocks.end(), + filter_delays_blocks_.begin()); + } + + min_filter_delay_ = *std::min_element(filter_delays_blocks_.begin(), + filter_delays_blocks_.end()); +} + +AecState::FilteringQualityAnalyzer::FilteringQualityAnalyzer( + const EchoCanceller3Config& config, + size_t num_capture_channels) + : use_linear_filter_(config.filter.use_linear_filter), + usable_linear_filter_estimates_(num_capture_channels, false) {} + +void AecState::FilteringQualityAnalyzer::Reset() { + std::fill(usable_linear_filter_estimates_.begin(), + usable_linear_filter_estimates_.end(), false); + overall_usable_linear_estimates_ = false; + filter_update_blocks_since_reset_ = 0; +} + +void AecState::FilteringQualityAnalyzer::Update( + bool active_render, + bool transparent_mode, + bool saturated_capture, + const absl::optional& external_delay, + bool any_filter_converged) { + // Update blocks counter. + const bool filter_update = active_render && !saturated_capture; + filter_update_blocks_since_reset_ += filter_update ? 1 : 0; + filter_update_blocks_since_start_ += filter_update ? 1 : 0; + + // Store convergence flag when observed. + convergence_seen_ = convergence_seen_ || any_filter_converged; + + // Verify requirements for achieving a decent filter. The requirements for + // filter adaptation at call startup are more restrictive than after an + // in-call reset. + const bool sufficient_data_to_converge_at_startup = + filter_update_blocks_since_start_ > kNumBlocksPerSecond * 0.4f; + const bool sufficient_data_to_converge_at_reset = + sufficient_data_to_converge_at_startup && + filter_update_blocks_since_reset_ > kNumBlocksPerSecond * 0.2f; + + // The linear filter can only be used if it has had time to converge. + overall_usable_linear_estimates_ = sufficient_data_to_converge_at_startup && + sufficient_data_to_converge_at_reset; + + // The linear filter can only be used if an external delay or convergence have + // been identified + overall_usable_linear_estimates_ = + overall_usable_linear_estimates_ && (external_delay || convergence_seen_); + + // If transparent mode is on, deactivate usign the linear filter. + overall_usable_linear_estimates_ = + overall_usable_linear_estimates_ && !transparent_mode; + + if (use_linear_filter_) { + std::fill(usable_linear_filter_estimates_.begin(), + usable_linear_filter_estimates_.end(), + overall_usable_linear_estimates_); + } +} + +void AecState::SaturationDetector::Update( + rtc::ArrayView> x, + bool saturated_capture, + bool usable_linear_estimate, + rtc::ArrayView subtractor_output, + float echo_path_gain) { + saturated_echo_ = false; + if (!saturated_capture) { + return; + } + + if (usable_linear_estimate) { + constexpr float kSaturationThreshold = 20000.f; + for (size_t ch = 0; ch < subtractor_output.size(); ++ch) { + saturated_echo_ = + saturated_echo_ || + (subtractor_output[ch].s_refined_max_abs > kSaturationThreshold || + subtractor_output[ch].s_coarse_max_abs > kSaturationThreshold); + } + } else { + float max_sample = 0.f; + for (auto& channel : x) { + for (float sample : channel) { + max_sample = std::max(max_sample, fabsf(sample)); + } + } + + const float kMargin = 10.f; + float peak_echo_amplitude = max_sample * echo_path_gain * kMargin; + saturated_echo_ = saturated_echo_ || peak_echo_amplitude > 32000; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/aec_state.h b/webrtc/modules/audio_processing/aec3/aec_state.h new file mode 100644 index 0000000..5b40e95 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/aec_state.h @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_AEC_STATE_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_AEC_STATE_H_ + +#include + +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/echo_audibility.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/erl_estimator.h" +#include "modules/audio_processing/aec3/erle_estimator.h" +#include "modules/audio_processing/aec3/filter_analyzer.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/reverb_model_estimator.h" +#include "modules/audio_processing/aec3/subtractor_output.h" +#include "modules/audio_processing/aec3/subtractor_output_analyzer.h" +#include "modules/audio_processing/aec3/transparent_mode.h" + +namespace webrtc { + +class ApmDataDumper; + +// Handles the state and the conditions for the echo removal functionality. +class AecState { + public: + AecState(const EchoCanceller3Config& config, size_t num_capture_channels); + ~AecState(); + + // Returns whether the echo subtractor can be used to determine the residual + // echo. + bool UsableLinearEstimate() const { + return filter_quality_state_.LinearFilterUsable() && + config_.filter.use_linear_filter; + } + + // Returns whether the echo subtractor output should be used as output. + bool UseLinearFilterOutput() const { + return filter_quality_state_.LinearFilterUsable() && + config_.filter.use_linear_filter; + } + + // Returns whether the render signal is currently active. + bool ActiveRender() const { return blocks_with_active_render_ > 200; } + + // Returns the appropriate scaling of the residual echo to match the + // audibility. + void GetResidualEchoScaling(rtc::ArrayView residual_scaling) const; + + // Returns whether the stationary properties of the signals are used in the + // aec. + bool UseStationarityProperties() const { + return config_.echo_audibility.use_stationarity_properties; + } + + // Returns the ERLE. + rtc::ArrayView> Erle() const { + return erle_estimator_.Erle(); + } + + // Returns an offset to apply to the estimation of the residual echo + // computation. Returning nullopt means that no offset should be used, while + // any other value will be applied as a multiplier to the estimated residual + // echo. + absl::optional ErleUncertainty() const; + + // Returns the fullband ERLE estimate in log2 units. + float FullBandErleLog2() const { return erle_estimator_.FullbandErleLog2(); } + + // Returns the ERL. + const std::array& Erl() const { + return erl_estimator_.Erl(); + } + + // Returns the time-domain ERL. + float ErlTimeDomain() const { return erl_estimator_.ErlTimeDomain(); } + + // Returns the delay estimate based on the linear filter. + int MinDirectPathFilterDelay() const { + return delay_state_.MinDirectPathFilterDelay(); + } + + // Returns whether the capture signal is saturated. + bool SaturatedCapture() const { return capture_signal_saturation_; } + + // Returns whether the echo signal is saturated. + bool SaturatedEcho() const { return saturation_detector_.SaturatedEcho(); } + + // Updates the capture signal saturation. + void UpdateCaptureSaturation(bool capture_signal_saturation) { + capture_signal_saturation_ = capture_signal_saturation; + } + + // Returns whether the transparent mode is active + bool TransparentModeActive() const { + return transparent_state_ && transparent_state_->Active(); + } + + // Takes appropriate action at an echo path change. + void HandleEchoPathChange(const EchoPathVariability& echo_path_variability); + + // Returns the decay factor for the echo reverberation. + float ReverbDecay() const { return reverb_model_estimator_.ReverbDecay(); } + + // Return the frequency response of the reverberant echo. + rtc::ArrayView GetReverbFrequencyResponse() const { + return reverb_model_estimator_.GetReverbFrequencyResponse(); + } + + // Returns whether the transition for going out of the initial stated has + // been triggered. + bool TransitionTriggered() const { + return initial_state_.TransitionTriggered(); + } + + // Updates the aec state. + // TODO(bugs.webrtc.org/10913): Compute multi-channel ERL. + void Update( + const absl::optional& external_delay, + rtc::ArrayView>> + adaptive_filter_frequency_responses, + rtc::ArrayView> + adaptive_filter_impulse_responses, + const RenderBuffer& render_buffer, + rtc::ArrayView> E2_refined, + rtc::ArrayView> Y2, + rtc::ArrayView subtractor_output); + + // Returns filter length in blocks. + int FilterLengthBlocks() const { + // All filters have the same length, so arbitrarily return channel 0 length. + return filter_analyzer_.FilterLengthBlocks(); + } + + private: + static int instance_count_; + std::unique_ptr data_dumper_; + const EchoCanceller3Config config_; + const size_t num_capture_channels_; + const bool deactivate_initial_state_reset_at_echo_path_change_; + const bool full_reset_at_echo_path_change_; + const bool subtractor_analyzer_reset_at_echo_path_change_; + + // Class for controlling the transition from the intial state, which in turn + // controls when the filter parameters for the initial state should be used. + class InitialState { + public: + explicit InitialState(const EchoCanceller3Config& config); + // Resets the state to again begin in the initial state. + void Reset(); + + // Updates the state based on new data. + void Update(bool active_render, bool saturated_capture); + + // Returns whether the initial state is active or not. + bool InitialStateActive() const { return initial_state_; } + + // Returns that the transition from the initial state has was started. + bool TransitionTriggered() const { return transition_triggered_; } + + private: + const bool conservative_initial_phase_; + const float initial_state_seconds_; + bool transition_triggered_ = false; + bool initial_state_ = true; + size_t strong_not_saturated_render_blocks_ = 0; + } initial_state_; + + // Class for choosing the direct-path delay relative to the beginning of the + // filter, as well as any other data related to the delay used within + // AecState. + class FilterDelay { + public: + FilterDelay(const EchoCanceller3Config& config, + size_t num_capture_channels); + + // Returns whether an external delay has been reported to the AecState (from + // the delay estimator). + bool ExternalDelayReported() const { return external_delay_reported_; } + + // Returns the delay in blocks relative to the beginning of the filter that + // corresponds to the direct path of the echo. + rtc::ArrayView DirectPathFilterDelays() const { + return filter_delays_blocks_; + } + + // Returns the minimum delay among the direct path delays relative to the + // beginning of the filter + int MinDirectPathFilterDelay() const { return min_filter_delay_; } + + // Updates the delay estimates based on new data. + void Update( + rtc::ArrayView analyzer_filter_delay_estimates_blocks, + const absl::optional& external_delay, + size_t blocks_with_proper_filter_adaptation); + + private: + const int delay_headroom_blocks_; + bool external_delay_reported_ = false; + std::vector filter_delays_blocks_; + int min_filter_delay_; + absl::optional external_delay_; + } delay_state_; + + // Classifier for toggling transparent mode when there is no echo. + std::unique_ptr transparent_state_; + + // Class for analyzing how well the linear filter is, and can be expected to, + // perform on the current signals. The purpose of this is for using to + // select the echo suppression functionality as well as the input to the echo + // suppressor. + class FilteringQualityAnalyzer { + public: + FilteringQualityAnalyzer(const EchoCanceller3Config& config, + size_t num_capture_channels); + + // Returns whether the linear filter can be used for the echo + // canceller output. + bool LinearFilterUsable() const { return overall_usable_linear_estimates_; } + + // Returns whether an individual filter output can be used for the echo + // canceller output. + const std::vector& UsableLinearFilterOutputs() const { + return usable_linear_filter_estimates_; + } + + // Resets the state of the analyzer. + void Reset(); + + // Updates the analysis based on new data. + void Update(bool active_render, + bool transparent_mode, + bool saturated_capture, + const absl::optional& external_delay, + bool any_filter_converged); + + private: + const bool use_linear_filter_; + bool overall_usable_linear_estimates_ = false; + size_t filter_update_blocks_since_reset_ = 0; + size_t filter_update_blocks_since_start_ = 0; + bool convergence_seen_ = false; + std::vector usable_linear_filter_estimates_; + } filter_quality_state_; + + // Class for detecting whether the echo is to be considered to be + // saturated. + class SaturationDetector { + public: + // Returns whether the echo is to be considered saturated. + bool SaturatedEcho() const { return saturated_echo_; } + + // Updates the detection decision based on new data. + void Update(rtc::ArrayView> x, + bool saturated_capture, + bool usable_linear_estimate, + rtc::ArrayView subtractor_output, + float echo_path_gain); + + private: + bool saturated_echo_ = false; + } saturation_detector_; + + ErlEstimator erl_estimator_; + ErleEstimator erle_estimator_; + size_t strong_not_saturated_render_blocks_ = 0; + size_t blocks_with_active_render_ = 0; + bool capture_signal_saturation_ = false; + FilterAnalyzer filter_analyzer_; + EchoAudibility echo_audibility_; + ReverbModelEstimator reverb_model_estimator_; + ReverbModel avg_render_reverb_; + SubtractorOutputAnalyzer subtractor_output_analyzer_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_AEC_STATE_H_ diff --git a/webrtc/modules/audio_processing/aec3/alignment_mixer.cc b/webrtc/modules/audio_processing/aec3/alignment_mixer.cc new file mode 100644 index 0000000..87488d2 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/alignment_mixer.cc @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/aec3/alignment_mixer.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +AlignmentMixer::MixingVariant ChooseMixingVariant(bool downmix, + bool adaptive_selection, + int num_channels) { + RTC_DCHECK(!(adaptive_selection && downmix)); + RTC_DCHECK_LT(0, num_channels); + + if (num_channels == 1) { + return AlignmentMixer::MixingVariant::kFixed; + } + if (downmix) { + return AlignmentMixer::MixingVariant::kDownmix; + } + if (adaptive_selection) { + return AlignmentMixer::MixingVariant::kAdaptive; + } + return AlignmentMixer::MixingVariant::kFixed; +} + +} // namespace + +AlignmentMixer::AlignmentMixer( + size_t num_channels, + const EchoCanceller3Config::Delay::AlignmentMixing& config) + : AlignmentMixer(num_channels, + config.downmix, + config.adaptive_selection, + config.activity_power_threshold, + config.prefer_first_two_channels) {} + +AlignmentMixer::AlignmentMixer(size_t num_channels, + bool downmix, + bool adaptive_selection, + float activity_power_threshold, + bool prefer_first_two_channels) + : num_channels_(num_channels), + one_by_num_channels_(1.f / num_channels_), + excitation_energy_threshold_(kBlockSize * activity_power_threshold), + prefer_first_two_channels_(prefer_first_two_channels), + selection_variant_( + ChooseMixingVariant(downmix, adaptive_selection, num_channels_)) { + if (selection_variant_ == MixingVariant::kAdaptive) { + std::fill(strong_block_counters_.begin(), strong_block_counters_.end(), 0); + cumulative_energies_.resize(num_channels_); + std::fill(cumulative_energies_.begin(), cumulative_energies_.end(), 0.f); + } +} + +void AlignmentMixer::ProduceOutput(rtc::ArrayView> x, + rtc::ArrayView y) { + RTC_DCHECK_EQ(x.size(), num_channels_); + if (selection_variant_ == MixingVariant::kDownmix) { + Downmix(x, y); + return; + } + + int ch = selection_variant_ == MixingVariant::kFixed ? 0 : SelectChannel(x); + + RTC_DCHECK_GE(x.size(), ch); + std::copy(x[ch].begin(), x[ch].end(), y.begin()); +} + +void AlignmentMixer::Downmix(rtc::ArrayView> x, + rtc::ArrayView y) const { + RTC_DCHECK_EQ(x.size(), num_channels_); + RTC_DCHECK_GE(num_channels_, 2); + std::copy(x[0].begin(), x[0].end(), y.begin()); + for (size_t ch = 1; ch < num_channels_; ++ch) { + for (size_t i = 0; i < kBlockSize; ++i) { + y[i] += x[ch][i]; + } + } + + for (size_t i = 0; i < kBlockSize; ++i) { + y[i] *= one_by_num_channels_; + } +} + +int AlignmentMixer::SelectChannel(rtc::ArrayView> x) { + RTC_DCHECK_EQ(x.size(), num_channels_); + RTC_DCHECK_GE(num_channels_, 2); + RTC_DCHECK_EQ(cumulative_energies_.size(), num_channels_); + + constexpr size_t kBlocksToChooseLeftOrRight = + static_cast(0.5f * kNumBlocksPerSecond); + const bool good_signal_in_left_or_right = + prefer_first_two_channels_ && + (strong_block_counters_[0] > kBlocksToChooseLeftOrRight || + strong_block_counters_[1] > kBlocksToChooseLeftOrRight); + + const int num_ch_to_analyze = + good_signal_in_left_or_right ? 2 : num_channels_; + + constexpr int kNumBlocksBeforeEnergySmoothing = 60 * kNumBlocksPerSecond; + ++block_counter_; + + for (int ch = 0; ch < num_ch_to_analyze; ++ch) { + RTC_DCHECK_EQ(x[ch].size(), kBlockSize); + float x2_sum = 0.f; + for (size_t i = 0; i < kBlockSize; ++i) { + x2_sum += x[ch][i] * x[ch][i]; + } + + if (ch < 2 && x2_sum > excitation_energy_threshold_) { + ++strong_block_counters_[ch]; + } + + if (block_counter_ <= kNumBlocksBeforeEnergySmoothing) { + cumulative_energies_[ch] += x2_sum; + } else { + constexpr float kSmoothing = 1.f / (10 * kNumBlocksPerSecond); + cumulative_energies_[ch] += + kSmoothing * (x2_sum - cumulative_energies_[ch]); + } + } + + // Normalize the energies to allow the energy computations to from now be + // based on smoothing. + if (block_counter_ == kNumBlocksBeforeEnergySmoothing) { + constexpr float kOneByNumBlocksBeforeEnergySmoothing = + 1.f / kNumBlocksBeforeEnergySmoothing; + for (int ch = 0; ch < num_ch_to_analyze; ++ch) { + cumulative_energies_[ch] *= kOneByNumBlocksBeforeEnergySmoothing; + } + } + + int strongest_ch = 0; + for (int ch = 0; ch < num_ch_to_analyze; ++ch) { + if (cumulative_energies_[ch] > cumulative_energies_[strongest_ch]) { + strongest_ch = ch; + } + } + + if ((good_signal_in_left_or_right && selected_channel_ > 1) || + cumulative_energies_[strongest_ch] > + 2.f * cumulative_energies_[selected_channel_]) { + selected_channel_ = strongest_ch; + } + + return selected_channel_; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/alignment_mixer.h b/webrtc/modules/audio_processing/aec3/alignment_mixer.h new file mode 100644 index 0000000..682aec9 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/alignment_mixer.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ALIGNMENT_MIXER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ALIGNMENT_MIXER_H_ + +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// Performs channel conversion to mono for the purpose of providing a decent +// mono input for the delay estimation. This is achieved by analyzing all +// incoming channels and produce one single channel output. +class AlignmentMixer { + public: + AlignmentMixer(size_t num_channels, + const EchoCanceller3Config::Delay::AlignmentMixing& config); + + AlignmentMixer(size_t num_channels, + bool downmix, + bool adaptive_selection, + float excitation_limit, + bool prefer_first_two_channels); + + void ProduceOutput(rtc::ArrayView> x, + rtc::ArrayView y); + + enum class MixingVariant { kDownmix, kAdaptive, kFixed }; + + private: + const size_t num_channels_; + const float one_by_num_channels_; + const float excitation_energy_threshold_; + const bool prefer_first_two_channels_; + const MixingVariant selection_variant_; + std::array strong_block_counters_; + std::vector cumulative_energies_; + int selected_channel_ = 0; + size_t block_counter_ = 0; + + void Downmix(const rtc::ArrayView> x, + rtc::ArrayView y) const; + int SelectChannel(rtc::ArrayView> x); +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ALIGNMENT_MIXER_H_ diff --git a/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.cc b/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.cc new file mode 100644 index 0000000..45f56a5 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.cc @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/api_call_jitter_metrics.h" + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { +namespace { + +bool TimeToReportMetrics(int frames_since_last_report) { + constexpr int kNumFramesPerSecond = 100; + constexpr int kReportingIntervalFrames = 10 * kNumFramesPerSecond; + return frames_since_last_report == kReportingIntervalFrames; +} + +} // namespace + +ApiCallJitterMetrics::Jitter::Jitter() + : max_(0), min_(std::numeric_limits::max()) {} + +void ApiCallJitterMetrics::Jitter::Update(int num_api_calls_in_a_row) { + min_ = std::min(min_, num_api_calls_in_a_row); + max_ = std::max(max_, num_api_calls_in_a_row); +} + +void ApiCallJitterMetrics::Jitter::Reset() { + min_ = std::numeric_limits::max(); + max_ = 0; +} + +void ApiCallJitterMetrics::Reset() { + render_jitter_.Reset(); + capture_jitter_.Reset(); + num_api_calls_in_a_row_ = 0; + frames_since_last_report_ = 0; + last_call_was_render_ = false; + proper_call_observed_ = false; +} + +void ApiCallJitterMetrics::ReportRenderCall() { + if (!last_call_was_render_) { + // If the previous call was a capture and a proper call has been observed + // (containing both render and capture data), storing the last number of + // capture calls into the metrics. + if (proper_call_observed_) { + capture_jitter_.Update(num_api_calls_in_a_row_); + } + + // Reset the call counter to start counting render calls. + num_api_calls_in_a_row_ = 0; + } + ++num_api_calls_in_a_row_; + last_call_was_render_ = true; +} + +void ApiCallJitterMetrics::ReportCaptureCall() { + if (last_call_was_render_) { + // If the previous call was a render and a proper call has been observed + // (containing both render and capture data), storing the last number of + // render calls into the metrics. + if (proper_call_observed_) { + render_jitter_.Update(num_api_calls_in_a_row_); + } + // Reset the call counter to start counting capture calls. + num_api_calls_in_a_row_ = 0; + + // If this statement is reached, at least one render and one capture call + // have been observed. + proper_call_observed_ = true; + } + ++num_api_calls_in_a_row_; + last_call_was_render_ = false; + + // Only report and update jitter metrics for when a proper call, containing + // both render and capture data, has been observed. + if (proper_call_observed_ && + TimeToReportMetrics(++frames_since_last_report_)) { + // Report jitter, where the base basic unit is frames. + constexpr int kMaxJitterToReport = 50; + + // Report max and min jitter for render and capture, in units of 20 ms. + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.MaxRenderJitter", + std::min(kMaxJitterToReport, render_jitter().max()), 1, + kMaxJitterToReport, kMaxJitterToReport); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.MinRenderJitter", + std::min(kMaxJitterToReport, render_jitter().min()), 1, + kMaxJitterToReport, kMaxJitterToReport); + + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.MaxCaptureJitter", + std::min(kMaxJitterToReport, capture_jitter().max()), 1, + kMaxJitterToReport, kMaxJitterToReport); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.MinCaptureJitter", + std::min(kMaxJitterToReport, capture_jitter().min()), 1, + kMaxJitterToReport, kMaxJitterToReport); + + frames_since_last_report_ = 0; + Reset(); + } +} + +bool ApiCallJitterMetrics::WillReportMetricsAtNextCapture() const { + return TimeToReportMetrics(frames_since_last_report_ + 1); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.h b/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.h new file mode 100644 index 0000000..dd1fa82 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/api_call_jitter_metrics.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_API_CALL_JITTER_METRICS_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_API_CALL_JITTER_METRICS_H_ + +namespace webrtc { + +// Stores data for reporting metrics on the API call jitter. +class ApiCallJitterMetrics { + public: + class Jitter { + public: + Jitter(); + void Update(int num_api_calls_in_a_row); + void Reset(); + + int min() const { return min_; } + int max() const { return max_; } + + private: + int max_; + int min_; + }; + + ApiCallJitterMetrics() { Reset(); } + + // Update metrics for render API call. + void ReportRenderCall(); + + // Update and periodically report metrics for capture API call. + void ReportCaptureCall(); + + // Methods used only for testing. + const Jitter& render_jitter() const { return render_jitter_; } + const Jitter& capture_jitter() const { return capture_jitter_; } + bool WillReportMetricsAtNextCapture() const; + + private: + void Reset(); + + Jitter render_jitter_; + Jitter capture_jitter_; + + int num_api_calls_in_a_row_ = 0; + int frames_since_last_report_ = 0; + bool last_call_was_render_ = false; + bool proper_call_observed_ = false; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_API_CALL_JITTER_METRICS_H_ diff --git a/webrtc/modules/audio_processing/aec3/block_buffer.cc b/webrtc/modules/audio_processing/aec3/block_buffer.cc new file mode 100644 index 0000000..77ce3de --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/block_buffer.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/block_buffer.h" + +#include + +namespace webrtc { + +BlockBuffer::BlockBuffer(size_t size, + size_t num_bands, + size_t num_channels, + size_t frame_length) + : size(static_cast(size)), + buffer(size, + std::vector>>( + num_bands, + std::vector>( + num_channels, + std::vector(frame_length, 0.f)))) { + for (auto& block : buffer) { + for (auto& band : block) { + for (auto& channel : band) { + std::fill(channel.begin(), channel.end(), 0.f); + } + } + } +} + +BlockBuffer::~BlockBuffer() = default; + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/block_buffer.h b/webrtc/modules/audio_processing/aec3/block_buffer.h new file mode 100644 index 0000000..b28d659 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/block_buffer.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_BUFFER_H_ + +#include + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +// Struct for bundling a circular buffer of two dimensional vector objects +// together with the read and write indices. +struct BlockBuffer { + BlockBuffer(size_t size, + size_t num_bands, + size_t num_channels, + size_t frame_length); + ~BlockBuffer(); + + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; + } + + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index > 0 ? index - 1 : size - 1; + } + + int OffsetIndex(int index, int offset) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + RTC_DCHECK_GE(size, offset); + return (size + index + offset) % size; + } + + void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } + void IncWriteIndex() { write = IncIndex(write); } + void DecWriteIndex() { write = DecIndex(write); } + void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); } + void IncReadIndex() { read = IncIndex(read); } + void DecReadIndex() { read = DecIndex(read); } + + const int size; + std::vector>>> buffer; + int write = 0; + int read = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/aec3/block_delay_buffer.cc b/webrtc/modules/audio_processing/aec3/block_delay_buffer.cc new file mode 100644 index 0000000..b9eb3c9 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/block_delay_buffer.cc @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/block_delay_buffer.h" + +#include "api/array_view.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +BlockDelayBuffer::BlockDelayBuffer(size_t num_channels, + size_t num_bands, + size_t frame_length, + size_t delay_samples) + : frame_length_(frame_length), + delay_(delay_samples), + buf_(num_channels, + std::vector>(num_bands, + std::vector(delay_, 0.f))) {} + +BlockDelayBuffer::~BlockDelayBuffer() = default; + +void BlockDelayBuffer::DelaySignal(AudioBuffer* frame) { + RTC_DCHECK_EQ(buf_.size(), frame->num_channels()); + if (delay_ == 0) { + return; + } + + const size_t num_bands = buf_[0].size(); + const size_t num_channels = buf_.size(); + + const size_t i_start = last_insert_; + size_t i = 0; + for (size_t ch = 0; ch < num_channels; ++ch) { + RTC_DCHECK_EQ(buf_[ch].size(), frame->num_bands()); + RTC_DCHECK_EQ(buf_[ch].size(), num_bands); + rtc::ArrayView frame_ch(frame->split_bands(ch), num_bands); + + for (size_t band = 0; band < num_bands; ++band) { + RTC_DCHECK_EQ(delay_, buf_[ch][band].size()); + i = i_start; + + for (size_t k = 0; k < frame_length_; ++k) { + const float tmp = buf_[ch][band][i]; + buf_[ch][band][i] = frame_ch[band][k]; + frame_ch[band][k] = tmp; + + i = i < delay_ - 1 ? i + 1 : 0; + } + } + } + + last_insert_ = i; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/block_delay_buffer.h b/webrtc/modules/audio_processing/aec3/block_delay_buffer.h new file mode 100644 index 0000000..711a790 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/block_delay_buffer.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_DELAY_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_DELAY_BUFFER_H_ + +#include + +#include + +#include "modules/audio_processing/audio_buffer.h" + +namespace webrtc { + +// Class for applying a fixed delay to the samples in a signal partitioned using +// the audiobuffer band-splitting scheme. +class BlockDelayBuffer { + public: + BlockDelayBuffer(size_t num_channels, + size_t num_bands, + size_t frame_length, + size_t delay_samples); + ~BlockDelayBuffer(); + + // Delays the samples by the specified delay. + void DelaySignal(AudioBuffer* frame); + + private: + const size_t frame_length_; + const size_t delay_; + std::vector>> buf_; + size_t last_insert_ = 0; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_DELAY_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/aec3/block_framer.cc b/webrtc/modules/audio_processing/aec3/block_framer.cc new file mode 100644 index 0000000..8241ce6 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/block_framer.cc @@ -0,0 +1,86 @@ +/* + * 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 "modules/audio_processing/aec3/block_framer.h" + +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +BlockFramer::BlockFramer(size_t num_bands, size_t num_channels) + : num_bands_(num_bands), + num_channels_(num_channels), + buffer_(num_bands_, + std::vector>( + num_channels, + std::vector(kBlockSize, 0.f))) { + RTC_DCHECK_LT(0, num_bands); + RTC_DCHECK_LT(0, num_channels); +} + +BlockFramer::~BlockFramer() = default; + +// All the constants are chosen so that the buffer is either empty or has enough +// samples for InsertBlockAndExtractSubFrame to produce a frame. In order to +// achieve this, the InsertBlockAndExtractSubFrame and InsertBlock methods need +// to be called in the correct order. +void BlockFramer::InsertBlock( + const std::vector>>& block) { + RTC_DCHECK_EQ(num_bands_, block.size()); + for (size_t band = 0; band < num_bands_; ++band) { + RTC_DCHECK_EQ(num_channels_, block[band].size()); + for (size_t channel = 0; channel < num_channels_; ++channel) { + RTC_DCHECK_EQ(kBlockSize, block[band][channel].size()); + RTC_DCHECK_EQ(0, buffer_[band][channel].size()); + + buffer_[band][channel].insert(buffer_[band][channel].begin(), + block[band][channel].begin(), + block[band][channel].end()); + } + } +} + +void BlockFramer::InsertBlockAndExtractSubFrame( + const std::vector>>& block, + std::vector>>* sub_frame) { + RTC_DCHECK(sub_frame); + RTC_DCHECK_EQ(num_bands_, block.size()); + RTC_DCHECK_EQ(num_bands_, sub_frame->size()); + for (size_t band = 0; band < num_bands_; ++band) { + RTC_DCHECK_EQ(num_channels_, block[band].size()); + RTC_DCHECK_EQ(num_channels_, (*sub_frame)[0].size()); + for (size_t channel = 0; channel < num_channels_; ++channel) { + RTC_DCHECK_LE(kSubFrameLength, + buffer_[band][channel].size() + kBlockSize); + RTC_DCHECK_EQ(kBlockSize, block[band][channel].size()); + RTC_DCHECK_GE(kBlockSize, buffer_[band][channel].size()); + RTC_DCHECK_EQ(kSubFrameLength, (*sub_frame)[band][channel].size()); + + const int samples_to_frame = + kSubFrameLength - buffer_[band][channel].size(); + std::copy(buffer_[band][channel].begin(), buffer_[band][channel].end(), + (*sub_frame)[band][channel].begin()); + std::copy( + block[band][channel].begin(), + block[band][channel].begin() + samples_to_frame, + (*sub_frame)[band][channel].begin() + buffer_[band][channel].size()); + buffer_[band][channel].clear(); + buffer_[band][channel].insert( + buffer_[band][channel].begin(), + block[band][channel].begin() + samples_to_frame, + block[band][channel].end()); + } + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/block_framer.h b/webrtc/modules/audio_processing/aec3/block_framer.h new file mode 100644 index 0000000..1d37866 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/block_framer.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_FRAMER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_FRAMER_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// Class for producing frames consisting of 2 subframes of 80 samples each +// from 64 sample blocks. The class is designed to work together with the +// FrameBlocker class which performs the reverse conversion. Used together with +// that, this class produces output frames are the same rate as frames are +// received by the FrameBlocker class. Note that the internal buffers will +// overrun if any other rate of packets insertion is used. +class BlockFramer { + public: + BlockFramer(size_t num_bands, size_t num_channels); + ~BlockFramer(); + BlockFramer(const BlockFramer&) = delete; + BlockFramer& operator=(const BlockFramer&) = delete; + + // Adds a 64 sample block into the data that will form the next output frame. + void InsertBlock(const std::vector>>& block); + // Adds a 64 sample block and extracts an 80 sample subframe. + void InsertBlockAndExtractSubFrame( + const std::vector>>& block, + std::vector>>* sub_frame); + + private: + const size_t num_bands_; + const size_t num_channels_; + std::vector>> buffer_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_FRAMER_H_ diff --git a/webrtc/modules/audio_processing/aec3/block_processor.cc b/webrtc/modules/audio_processing/aec3/block_processor.cc new file mode 100644 index 0000000..f2f3261 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/block_processor.cc @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/block_processor.h" + +#include + +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/block_processor_metrics.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/echo_remover.h" +#include "modules/audio_processing/aec3/render_delay_buffer.h" +#include "modules/audio_processing/aec3/render_delay_controller.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace { + +enum class BlockProcessorApiCall { kCapture, kRender }; + +class BlockProcessorImpl final : public BlockProcessor { + public: + BlockProcessorImpl(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr render_buffer, + std::unique_ptr delay_controller, + std::unique_ptr echo_remover); + + BlockProcessorImpl() = delete; + + ~BlockProcessorImpl() override; + + void ProcessCapture( + bool echo_path_gain_change, + bool capture_signal_saturation, + std::vector>>* linear_output, + std::vector>>* capture_block) override; + + void BufferRender( + const std::vector>>& block) override; + + void UpdateEchoLeakageStatus(bool leakage_detected) override; + + void GetMetrics(EchoControl::Metrics* metrics) const override; + + void SetAudioBufferDelay(int delay_ms) override; + + private: + static int instance_count_; + std::unique_ptr data_dumper_; + const EchoCanceller3Config config_; + bool capture_properly_started_ = false; + bool render_properly_started_ = false; + const size_t sample_rate_hz_; + std::unique_ptr render_buffer_; + std::unique_ptr delay_controller_; + std::unique_ptr echo_remover_; + BlockProcessorMetrics metrics_; + RenderDelayBuffer::BufferingEvent render_event_; + size_t capture_call_counter_ = 0; + absl::optional estimated_delay_; +}; + +int BlockProcessorImpl::instance_count_ = 0; + +BlockProcessorImpl::BlockProcessorImpl( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr render_buffer, + std::unique_ptr delay_controller, + std::unique_ptr echo_remover) + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + config_(config), + sample_rate_hz_(sample_rate_hz), + render_buffer_(std::move(render_buffer)), + delay_controller_(std::move(delay_controller)), + echo_remover_(std::move(echo_remover)), + render_event_(RenderDelayBuffer::BufferingEvent::kNone) { + RTC_DCHECK(ValidFullBandRate(sample_rate_hz_)); +} + +BlockProcessorImpl::~BlockProcessorImpl() = default; + +void BlockProcessorImpl::ProcessCapture( + bool echo_path_gain_change, + bool capture_signal_saturation, + std::vector>>* linear_output, + std::vector>>* capture_block) { + RTC_DCHECK(capture_block); + RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), capture_block->size()); + RTC_DCHECK_EQ(kBlockSize, (*capture_block)[0][0].size()); + + capture_call_counter_++; + + data_dumper_->DumpRaw("aec3_processblock_call_order", + static_cast(BlockProcessorApiCall::kCapture)); + data_dumper_->DumpWav("aec3_processblock_capture_input", kBlockSize, + &(*capture_block)[0][0][0], 16000, 1); + + if (render_properly_started_) { + if (!capture_properly_started_) { + capture_properly_started_ = true; + render_buffer_->Reset(); + if (delay_controller_) + delay_controller_->Reset(true); + } + } else { + // If no render data has yet arrived, do not process the capture signal. + render_buffer_->HandleSkippedCaptureProcessing(); + return; + } + + EchoPathVariability echo_path_variability( + echo_path_gain_change, EchoPathVariability::DelayAdjustment::kNone, + false); + + if (render_event_ == RenderDelayBuffer::BufferingEvent::kRenderOverrun && + render_properly_started_) { + echo_path_variability.delay_change = + EchoPathVariability::DelayAdjustment::kBufferFlush; + if (delay_controller_) + delay_controller_->Reset(true); + RTC_LOG(LS_WARNING) << "Reset due to render buffer overrun at block " + << capture_call_counter_; + } + render_event_ = RenderDelayBuffer::BufferingEvent::kNone; + + // Update the render buffers with any newly arrived render blocks and prepare + // the render buffers for reading the render data corresponding to the current + // capture block. + RenderDelayBuffer::BufferingEvent buffer_event = + render_buffer_->PrepareCaptureProcessing(); + // Reset the delay controller at render buffer underrun. + if (buffer_event == RenderDelayBuffer::BufferingEvent::kRenderUnderrun) { + if (delay_controller_) + delay_controller_->Reset(false); + } + + data_dumper_->DumpWav("aec3_processblock_capture_input2", kBlockSize, + &(*capture_block)[0][0][0], 16000, 1); + + bool has_delay_estimator = !config_.delay.use_external_delay_estimator; + if (has_delay_estimator) { + RTC_DCHECK(delay_controller_); + // Compute and apply the render delay required to achieve proper signal + // alignment. + estimated_delay_ = delay_controller_->GetDelay( + render_buffer_->GetDownsampledRenderBuffer(), render_buffer_->Delay(), + (*capture_block)[0]); + + if (estimated_delay_) { + bool delay_change = + render_buffer_->AlignFromDelay(estimated_delay_->delay); + if (delay_change) { + rtc::LoggingSeverity log_level = + config_.delay.log_warning_on_delay_changes ? rtc::LS_WARNING + : rtc::LS_INFO; + RTC_LOG_V(log_level) << "Delay changed to " << estimated_delay_->delay + << " at block " << capture_call_counter_; + echo_path_variability.delay_change = + EchoPathVariability::DelayAdjustment::kNewDetectedDelay; + } + } + + echo_path_variability.clock_drift = delay_controller_->HasClockdrift(); + + } else { + render_buffer_->AlignFromExternalDelay(); + } + + // Remove the echo from the capture signal. + if (has_delay_estimator || render_buffer_->HasReceivedBufferDelay()) { + echo_remover_->ProcessCapture( + echo_path_variability, capture_signal_saturation, estimated_delay_, + render_buffer_->GetRenderBuffer(), linear_output, capture_block); + } + + // Update the metrics. + metrics_.UpdateCapture(false); +} + +void BlockProcessorImpl::BufferRender( + const std::vector>>& block) { + RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), block.size()); + RTC_DCHECK_EQ(kBlockSize, block[0][0].size()); + data_dumper_->DumpRaw("aec3_processblock_call_order", + static_cast(BlockProcessorApiCall::kRender)); + data_dumper_->DumpWav("aec3_processblock_render_input", kBlockSize, + &block[0][0][0], 16000, 1); + data_dumper_->DumpWav("aec3_processblock_render_input2", kBlockSize, + &block[0][0][0], 16000, 1); + + render_event_ = render_buffer_->Insert(block); + + metrics_.UpdateRender(render_event_ != + RenderDelayBuffer::BufferingEvent::kNone); + + render_properly_started_ = true; + if (delay_controller_) + delay_controller_->LogRenderCall(); +} + +void BlockProcessorImpl::UpdateEchoLeakageStatus(bool leakage_detected) { + echo_remover_->UpdateEchoLeakageStatus(leakage_detected); +} + +void BlockProcessorImpl::GetMetrics(EchoControl::Metrics* metrics) const { + echo_remover_->GetMetrics(metrics); + constexpr int block_size_ms = 4; + absl::optional delay = render_buffer_->Delay(); + metrics->delay_ms = delay ? static_cast(*delay) * block_size_ms : 0; +} + +void BlockProcessorImpl::SetAudioBufferDelay(int delay_ms) { + render_buffer_->SetAudioBufferDelay(delay_ms); +} + +} // namespace + +BlockProcessor* BlockProcessor::Create(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels) { + std::unique_ptr render_buffer( + RenderDelayBuffer::Create(config, sample_rate_hz, num_render_channels)); + std::unique_ptr delay_controller; + if (!config.delay.use_external_delay_estimator) { + delay_controller.reset(RenderDelayController::Create(config, sample_rate_hz, + num_capture_channels)); + } + std::unique_ptr echo_remover(EchoRemover::Create( + config, sample_rate_hz, num_render_channels, num_capture_channels)); + return Create(config, sample_rate_hz, num_render_channels, + num_capture_channels, std::move(render_buffer), + std::move(delay_controller), std::move(echo_remover)); +} + +BlockProcessor* BlockProcessor::Create( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr render_buffer) { + std::unique_ptr delay_controller; + if (!config.delay.use_external_delay_estimator) { + delay_controller.reset(RenderDelayController::Create(config, sample_rate_hz, + num_capture_channels)); + } + std::unique_ptr echo_remover(EchoRemover::Create( + config, sample_rate_hz, num_render_channels, num_capture_channels)); + return Create(config, sample_rate_hz, num_render_channels, + num_capture_channels, std::move(render_buffer), + std::move(delay_controller), std::move(echo_remover)); +} + +BlockProcessor* BlockProcessor::Create( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr render_buffer, + std::unique_ptr delay_controller, + std::unique_ptr echo_remover) { + return new BlockProcessorImpl(config, sample_rate_hz, num_render_channels, + num_capture_channels, std::move(render_buffer), + std::move(delay_controller), + std::move(echo_remover)); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/block_processor.h b/webrtc/modules/audio_processing/aec3/block_processor.h new file mode 100644 index 0000000..9bb0cf1 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/block_processor.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_H_ + +#include + +#include +#include + +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "modules/audio_processing/aec3/echo_remover.h" +#include "modules/audio_processing/aec3/render_delay_buffer.h" +#include "modules/audio_processing/aec3/render_delay_controller.h" + +namespace webrtc { + +// Class for performing echo cancellation on 64 sample blocks of audio data. +class BlockProcessor { + public: + static BlockProcessor* Create(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels); + // Only used for testing purposes. + static BlockProcessor* Create( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr render_buffer); + static BlockProcessor* Create( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr render_buffer, + std::unique_ptr delay_controller, + std::unique_ptr echo_remover); + + virtual ~BlockProcessor() = default; + + // Get current metrics. + virtual void GetMetrics(EchoControl::Metrics* metrics) const = 0; + + // Provides an optional external estimate of the audio buffer delay. + virtual void SetAudioBufferDelay(int delay_ms) = 0; + + // Processes a block of capture data. + virtual void ProcessCapture( + bool echo_path_gain_change, + bool capture_signal_saturation, + std::vector>>* linear_output, + std::vector>>* capture_block) = 0; + + // Buffers a block of render data supplied by a FrameBlocker object. + virtual void BufferRender( + const std::vector>>& render_block) = 0; + + // Reports whether echo leakage has been detected in the echo canceller + // output. + virtual void UpdateEchoLeakageStatus(bool leakage_detected) = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/block_processor_metrics.cc b/webrtc/modules/audio_processing/aec3/block_processor_metrics.cc new file mode 100644 index 0000000..deac1fc --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/block_processor_metrics.cc @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/block_processor_metrics.h" + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +namespace { + +enum class RenderUnderrunCategory { + kNone, + kFew, + kSeveral, + kMany, + kConstant, + kNumCategories +}; + +enum class RenderOverrunCategory { + kNone, + kFew, + kSeveral, + kMany, + kConstant, + kNumCategories +}; + +} // namespace + +void BlockProcessorMetrics::UpdateCapture(bool underrun) { + ++capture_block_counter_; + if (underrun) { + ++render_buffer_underruns_; + } + + if (capture_block_counter_ == kMetricsReportingIntervalBlocks) { + metrics_reported_ = true; + + RenderUnderrunCategory underrun_category; + if (render_buffer_underruns_ == 0) { + underrun_category = RenderUnderrunCategory::kNone; + } else if (render_buffer_underruns_ > (capture_block_counter_ >> 1)) { + underrun_category = RenderUnderrunCategory::kConstant; + } else if (render_buffer_underruns_ > 100) { + underrun_category = RenderUnderrunCategory::kMany; + } else if (render_buffer_underruns_ > 10) { + underrun_category = RenderUnderrunCategory::kSeveral; + } else { + underrun_category = RenderUnderrunCategory::kFew; + } + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.EchoCanceller.RenderUnderruns", + static_cast(underrun_category), + static_cast(RenderUnderrunCategory::kNumCategories)); + + RenderOverrunCategory overrun_category; + if (render_buffer_overruns_ == 0) { + overrun_category = RenderOverrunCategory::kNone; + } else if (render_buffer_overruns_ > (buffer_render_calls_ >> 1)) { + overrun_category = RenderOverrunCategory::kConstant; + } else if (render_buffer_overruns_ > 100) { + overrun_category = RenderOverrunCategory::kMany; + } else if (render_buffer_overruns_ > 10) { + overrun_category = RenderOverrunCategory::kSeveral; + } else { + overrun_category = RenderOverrunCategory::kFew; + } + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.EchoCanceller.RenderOverruns", + static_cast(overrun_category), + static_cast(RenderOverrunCategory::kNumCategories)); + + ResetMetrics(); + capture_block_counter_ = 0; + } else { + metrics_reported_ = false; + } +} + +void BlockProcessorMetrics::UpdateRender(bool overrun) { + ++buffer_render_calls_; + if (overrun) { + ++render_buffer_overruns_; + } +} + +void BlockProcessorMetrics::ResetMetrics() { + render_buffer_underruns_ = 0; + render_buffer_overruns_ = 0; + buffer_render_calls_ = 0; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/block_processor_metrics.h b/webrtc/modules/audio_processing/aec3/block_processor_metrics.h new file mode 100644 index 0000000..4ba0536 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/block_processor_metrics.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_METRICS_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_METRICS_H_ + +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +// Handles the reporting of metrics for the block_processor. +class BlockProcessorMetrics { + public: + BlockProcessorMetrics() = default; + + // Updates the metric with new capture data. + void UpdateCapture(bool underrun); + + // Updates the metric with new render data. + void UpdateRender(bool overrun); + + // Returns true if the metrics have just been reported, otherwise false. + bool MetricsReported() { return metrics_reported_; } + + private: + // Resets the metrics. + void ResetMetrics(); + + int capture_block_counter_ = 0; + bool metrics_reported_ = false; + int render_buffer_underruns_ = 0; + int render_buffer_overruns_ = 0; + int buffer_render_calls_ = 0; + + RTC_DISALLOW_COPY_AND_ASSIGN(BlockProcessorMetrics); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_PROCESSOR_METRICS_H_ diff --git a/webrtc/modules/audio_processing/aec3/clockdrift_detector.cc b/webrtc/modules/audio_processing/aec3/clockdrift_detector.cc new file mode 100644 index 0000000..2c49b79 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/clockdrift_detector.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/clockdrift_detector.h" + +namespace webrtc { + +ClockdriftDetector::ClockdriftDetector() + : level_(Level::kNone), stability_counter_(0) { + delay_history_.fill(0); +} + +ClockdriftDetector::~ClockdriftDetector() = default; + +void ClockdriftDetector::Update(int delay_estimate) { + if (delay_estimate == delay_history_[0]) { + // Reset clockdrift level if delay estimate is stable for 7500 blocks (30 + // seconds). + if (++stability_counter_ > 7500) + level_ = Level::kNone; + return; + } + + stability_counter_ = 0; + const int d1 = delay_history_[0] - delay_estimate; + const int d2 = delay_history_[1] - delay_estimate; + const int d3 = delay_history_[2] - delay_estimate; + + // Patterns recognized as positive clockdrift: + // [x-3], x-2, x-1, x. + // [x-3], x-1, x-2, x. + const bool probable_drift_up = + (d1 == -1 && d2 == -2) || (d1 == -2 && d2 == -1); + const bool drift_up = probable_drift_up && d3 == -3; + + // Patterns recognized as negative clockdrift: + // [x+3], x+2, x+1, x. + // [x+3], x+1, x+2, x. + const bool probable_drift_down = (d1 == 1 && d2 == 2) || (d1 == 2 && d2 == 1); + const bool drift_down = probable_drift_down && d3 == 3; + + // Set clockdrift level. + if (drift_up || drift_down) { + level_ = Level::kVerified; + } else if ((probable_drift_up || probable_drift_down) && + level_ == Level::kNone) { + level_ = Level::kProbable; + } + + // Shift delay history one step. + delay_history_[2] = delay_history_[1]; + delay_history_[1] = delay_history_[0]; + delay_history_[0] = delay_estimate; +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/clockdrift_detector.h b/webrtc/modules/audio_processing/aec3/clockdrift_detector.h new file mode 100644 index 0000000..2ba90bb --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/clockdrift_detector.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_CLOCKDRIFT_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_CLOCKDRIFT_DETECTOR_H_ + +#include + +#include + +namespace webrtc { + +class ApmDataDumper; +struct DownsampledRenderBuffer; +struct EchoCanceller3Config; + +// Detects clockdrift by analyzing the estimated delay. +class ClockdriftDetector { + public: + enum class Level { kNone, kProbable, kVerified, kNumCategories }; + ClockdriftDetector(); + ~ClockdriftDetector(); + void Update(int delay_estimate); + Level ClockdriftLevel() const { return level_; } + + private: + std::array delay_history_; + Level level_; + size_t stability_counter_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_CLOCKDRIFT_DETECTOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.cc b/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.cc new file mode 100644 index 0000000..f4fb74d --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/coarse_filter_update_gain.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +CoarseFilterUpdateGain::CoarseFilterUpdateGain( + const EchoCanceller3Config::Filter::CoarseConfiguration& config, + size_t config_change_duration_blocks) + : config_change_duration_blocks_( + static_cast(config_change_duration_blocks)) { + SetConfig(config, true); + RTC_DCHECK_LT(0, config_change_duration_blocks_); + one_by_config_change_duration_blocks_ = 1.f / config_change_duration_blocks_; +} + +void CoarseFilterUpdateGain::HandleEchoPathChange() { + poor_signal_excitation_counter_ = 0; + call_counter_ = 0; +} + +void CoarseFilterUpdateGain::Compute( + const std::array& render_power, + const RenderSignalAnalyzer& render_signal_analyzer, + const FftData& E_coarse, + size_t size_partitions, + bool saturated_capture_signal, + FftData* G) { + RTC_DCHECK(G); + ++call_counter_; + + UpdateCurrentConfig(); + + if (render_signal_analyzer.PoorSignalExcitation()) { + poor_signal_excitation_counter_ = 0; + } + + // Do not update the filter if the render is not sufficiently excited. + if (++poor_signal_excitation_counter_ < size_partitions || + saturated_capture_signal || call_counter_ <= size_partitions) { + G->re.fill(0.f); + G->im.fill(0.f); + return; + } + + // Compute mu. + std::array mu; + const auto& X2 = render_power; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + if (X2[k] > current_config_.noise_gate) { + mu[k] = current_config_.rate / X2[k]; + } else { + mu[k] = 0.f; + } + } + + // Avoid updating the filter close to narrow bands in the render signals. + render_signal_analyzer.MaskRegionsAroundNarrowBands(&mu); + + // G = mu * E * X2. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + G->re[k] = mu[k] * E_coarse.re[k]; + G->im[k] = mu[k] * E_coarse.im[k]; + } +} + +void CoarseFilterUpdateGain::UpdateCurrentConfig() { + RTC_DCHECK_GE(config_change_duration_blocks_, config_change_counter_); + if (config_change_counter_ > 0) { + if (--config_change_counter_ > 0) { + auto average = [](float from, float to, float from_weight) { + return from * from_weight + to * (1.f - from_weight); + }; + + float change_factor = + config_change_counter_ * one_by_config_change_duration_blocks_; + + current_config_.rate = + average(old_target_config_.rate, target_config_.rate, change_factor); + current_config_.noise_gate = + average(old_target_config_.noise_gate, target_config_.noise_gate, + change_factor); + } else { + current_config_ = old_target_config_ = target_config_; + } + } + RTC_DCHECK_LE(0, config_change_counter_); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.h b/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.h new file mode 100644 index 0000000..a1a1399 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/coarse_filter_update_gain.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_COARSE_FILTER_UPDATE_GAIN_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_COARSE_FILTER_UPDATE_GAIN_H_ + +#include + +#include + +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/render_signal_analyzer.h" + +namespace webrtc { + +// Provides functionality for computing the fixed gain for the coarse filter. +class CoarseFilterUpdateGain { + public: + explicit CoarseFilterUpdateGain( + const EchoCanceller3Config::Filter::CoarseConfiguration& config, + size_t config_change_duration_blocks); + + // Takes action in the case of a known echo path change. + void HandleEchoPathChange(); + + // Computes the gain. + void Compute(const std::array& render_power, + const RenderSignalAnalyzer& render_signal_analyzer, + const FftData& E_coarse, + size_t size_partitions, + bool saturated_capture_signal, + FftData* G); + + // Sets a new config. + void SetConfig( + const EchoCanceller3Config::Filter::CoarseConfiguration& config, + bool immediate_effect) { + if (immediate_effect) { + old_target_config_ = current_config_ = target_config_ = config; + config_change_counter_ = 0; + } else { + old_target_config_ = current_config_; + target_config_ = config; + config_change_counter_ = config_change_duration_blocks_; + } + } + + private: + EchoCanceller3Config::Filter::CoarseConfiguration current_config_; + EchoCanceller3Config::Filter::CoarseConfiguration target_config_; + EchoCanceller3Config::Filter::CoarseConfiguration old_target_config_; + const int config_change_duration_blocks_; + float one_by_config_change_duration_blocks_; + // TODO(peah): Check whether this counter should instead be initialized to a + // large value. + size_t poor_signal_excitation_counter_ = 0; + size_t call_counter_ = 0; + int config_change_counter_ = 0; + + void UpdateCurrentConfig(); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_COARSE_FILTER_UPDATE_GAIN_H_ diff --git a/webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc b/webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc new file mode 100644 index 0000000..de5227c --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/comfort_noise_generator.h" + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "modules/audio_processing/aec3/vector_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Computes the noise floor value that matches a WGN input of noise_floor_dbfs. +float GetNoiseFloorFactor(float noise_floor_dbfs) { + // kdBfsNormalization = 20.f*log10(32768.f). + constexpr float kdBfsNormalization = 90.30899869919436f; + return 64.f * powf(10.f, (kdBfsNormalization + noise_floor_dbfs) * 0.1f); +} + +// Table of sqrt(2) * sin(2*pi*i/32). +constexpr float kSqrt2Sin[32] = { + +0.0000000f, +0.2758994f, +0.5411961f, +0.7856950f, +1.0000000f, + +1.1758756f, +1.3065630f, +1.3870398f, +1.4142136f, +1.3870398f, + +1.3065630f, +1.1758756f, +1.0000000f, +0.7856950f, +0.5411961f, + +0.2758994f, +0.0000000f, -0.2758994f, -0.5411961f, -0.7856950f, + -1.0000000f, -1.1758756f, -1.3065630f, -1.3870398f, -1.4142136f, + -1.3870398f, -1.3065630f, -1.1758756f, -1.0000000f, -0.7856950f, + -0.5411961f, -0.2758994f}; + +void GenerateComfortNoise(Aec3Optimization optimization, + const std::array& N2, + uint32_t* seed, + FftData* lower_band_noise, + FftData* upper_band_noise) { + FftData* N_low = lower_band_noise; + FftData* N_high = upper_band_noise; + + // Compute square root spectrum. + std::array N; + std::copy(N2.begin(), N2.end(), N.begin()); + aec3::VectorMath(optimization).Sqrt(N); + + // Compute the noise level for the upper bands. + constexpr float kOneByNumBands = 1.f / (kFftLengthBy2Plus1 / 2 + 1); + constexpr int kFftLengthBy2Plus1By2 = kFftLengthBy2Plus1 / 2; + const float high_band_noise_level = + std::accumulate(N.begin() + kFftLengthBy2Plus1By2, N.end(), 0.f) * + kOneByNumBands; + + // The analysis and synthesis windowing cause loss of power when + // cross-fading the noise where frames are completely uncorrelated + // (generated with random phase), hence the factor sqrt(2). + // This is not the case for the speech signal where the input is overlapping + // (strong correlation). + N_low->re[0] = N_low->re[kFftLengthBy2] = N_high->re[0] = + N_high->re[kFftLengthBy2] = 0.f; + for (size_t k = 1; k < kFftLengthBy2; k++) { + constexpr int kIndexMask = 32 - 1; + // Generate a random 31-bit integer. + seed[0] = (seed[0] * 69069 + 1) & (0x80000000 - 1); + // Convert to a 5-bit index. + int i = seed[0] >> 26; + + // y = sqrt(2) * sin(a) + const float x = kSqrt2Sin[i]; + // x = sqrt(2) * cos(a) = sqrt(2) * sin(a + pi/2) + const float y = kSqrt2Sin[(i + 8) & kIndexMask]; + + // Form low-frequency noise via spectral shaping. + N_low->re[k] = N[k] * x; + N_low->im[k] = N[k] * y; + + // Form the high-frequency noise via simple levelling. + N_high->re[k] = high_band_noise_level * x; + N_high->im[k] = high_band_noise_level * y; + } +} + +} // namespace + +ComfortNoiseGenerator::ComfortNoiseGenerator(const EchoCanceller3Config& config, + Aec3Optimization optimization, + size_t num_capture_channels) + : optimization_(optimization), + seed_(42), + num_capture_channels_(num_capture_channels), + noise_floor_(GetNoiseFloorFactor(config.comfort_noise.noise_floor_dbfs)), + N2_initial_( + std::make_unique>>( + num_capture_channels_)), + Y2_smoothed_(num_capture_channels_), + N2_(num_capture_channels_) { + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + (*N2_initial_)[ch].fill(0.f); + Y2_smoothed_[ch].fill(0.f); + N2_[ch].fill(1.0e6f); + } +} + +ComfortNoiseGenerator::~ComfortNoiseGenerator() = default; + +void ComfortNoiseGenerator::Compute( + bool saturated_capture, + rtc::ArrayView> + capture_spectrum, + rtc::ArrayView lower_band_noise, + rtc::ArrayView upper_band_noise) { + const auto& Y2 = capture_spectrum; + + if (!saturated_capture) { + // Smooth Y2. + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + std::transform(Y2_smoothed_[ch].begin(), Y2_smoothed_[ch].end(), + Y2[ch].begin(), Y2_smoothed_[ch].begin(), + [](float a, float b) { return a + 0.1f * (b - a); }); + } + + if (N2_counter_ > 50) { + // Update N2 from Y2_smoothed. + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + std::transform(N2_[ch].begin(), N2_[ch].end(), Y2_smoothed_[ch].begin(), + N2_[ch].begin(), [](float a, float b) { + return b < a ? (0.9f * b + 0.1f * a) * 1.0002f + : a * 1.0002f; + }); + } + } + + if (N2_initial_) { + if (++N2_counter_ == 1000) { + N2_initial_.reset(); + } else { + // Compute the N2_initial from N2. + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + std::transform(N2_[ch].begin(), N2_[ch].end(), + (*N2_initial_)[ch].begin(), (*N2_initial_)[ch].begin(), + [](float a, float b) { + return a > b ? b + 0.001f * (a - b) : a; + }); + } + } + } + + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + for (auto& n : N2_[ch]) { + n = std::max(n, noise_floor_); + } + if (N2_initial_) { + for (auto& n : (*N2_initial_)[ch]) { + n = std::max(n, noise_floor_); + } + } + } + } + + // Choose N2 estimate to use. + const auto& N2 = N2_initial_ ? (*N2_initial_) : N2_; + + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + GenerateComfortNoise(optimization_, N2[ch], &seed_, &lower_band_noise[ch], + &upper_band_noise[ch]); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/comfort_noise_generator.h b/webrtc/modules/audio_processing/aec3/comfort_noise_generator.h new file mode 100644 index 0000000..16eaf35 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/comfort_noise_generator.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_COMFORT_NOISE_GENERATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_COMFORT_NOISE_GENERATOR_H_ + +#include + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec_state.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "rtc_base/constructor_magic.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace aec3 { +#if defined(WEBRTC_ARCH_X86_FAMILY) + +void EstimateComfortNoise_SSE2(const std::array& N2, + uint32_t* seed, + FftData* lower_band_noise, + FftData* upper_band_noise); +#endif +void EstimateComfortNoise(const std::array& N2, + uint32_t* seed, + FftData* lower_band_noise, + FftData* upper_band_noise); + +} // namespace aec3 + +// Generates the comfort noise. +class ComfortNoiseGenerator { + public: + ComfortNoiseGenerator(const EchoCanceller3Config& config, + Aec3Optimization optimization, + size_t num_capture_channels); + ComfortNoiseGenerator() = delete; + ~ComfortNoiseGenerator(); + ComfortNoiseGenerator(const ComfortNoiseGenerator&) = delete; + + // Computes the comfort noise. + void Compute(bool saturated_capture, + rtc::ArrayView> + capture_spectrum, + rtc::ArrayView lower_band_noise, + rtc::ArrayView upper_band_noise); + + // Returns the estimate of the background noise spectrum. + rtc::ArrayView> NoiseSpectrum() + const { + return N2_; + } + + private: + const Aec3Optimization optimization_; + uint32_t seed_; + const size_t num_capture_channels_; + const float noise_floor_; + std::unique_ptr>> + N2_initial_; + std::vector> Y2_smoothed_; + std::vector> N2_; + int N2_counter_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_COMFORT_NOISE_GENERATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/decimator.cc b/webrtc/modules/audio_processing/aec3/decimator.cc new file mode 100644 index 0000000..bd03237 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/decimator.cc @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/decimator.h" + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// signal.butter(2, 3400/8000.0, 'lowpass', analog=False) +const std::vector GetLowPassFilterDS2() { + return std::vector{ + {{-1.f, 0.f}, {0.13833231f, 0.40743176f}, 0.22711796393486466f}, + {{-1.f, 0.f}, {0.13833231f, 0.40743176f}, 0.22711796393486466f}, + {{-1.f, 0.f}, {0.13833231f, 0.40743176f}, 0.22711796393486466f}}; +} + +// signal.ellip(6, 1, 40, 1800/8000, btype='lowpass', analog=False) +const std::vector GetLowPassFilterDS4() { + return std::vector{ + {{-0.08873842f, 0.99605496f}, {0.75916227f, 0.23841065f}, 0.26250696827f}, + {{0.62273832f, 0.78243018f}, {0.74892112f, 0.5410152f}, 0.26250696827f}, + {{0.71107693f, 0.70311421f}, {0.74895534f, 0.63924616f}, 0.26250696827f}}; +} + +// signal.cheby1(1, 6, [1000/8000, 2000/8000], btype='bandpass', analog=False) +const std::vector GetBandPassFilterDS8() { + return std::vector{ + {{1.f, 0.f}, {0.7601815f, 0.46423542f}, 0.10330478266505948f, true}, + {{1.f, 0.f}, {0.7601815f, 0.46423542f}, 0.10330478266505948f, true}, + {{1.f, 0.f}, {0.7601815f, 0.46423542f}, 0.10330478266505948f, true}, + {{1.f, 0.f}, {0.7601815f, 0.46423542f}, 0.10330478266505948f, true}, + {{1.f, 0.f}, {0.7601815f, 0.46423542f}, 0.10330478266505948f, true}}; +} + +// signal.butter(2, 1000/8000.0, 'highpass', analog=False) +const std::vector GetHighPassFilter() { + return std::vector{ + {{1.f, 0.f}, {0.72712179f, 0.21296904f}, 0.7570763753338849f}}; +} + +const std::vector GetPassThroughFilter() { + return std::vector{}; +} +} // namespace + +Decimator::Decimator(size_t down_sampling_factor) + : down_sampling_factor_(down_sampling_factor), + anti_aliasing_filter_(down_sampling_factor_ == 4 + ? GetLowPassFilterDS4() + : (down_sampling_factor_ == 8 + ? GetBandPassFilterDS8() + : GetLowPassFilterDS2())), + noise_reduction_filter_(down_sampling_factor_ == 8 + ? GetPassThroughFilter() + : GetHighPassFilter()) { + RTC_DCHECK(down_sampling_factor_ == 2 || down_sampling_factor_ == 4 || + down_sampling_factor_ == 8); +} + +void Decimator::Decimate(rtc::ArrayView in, + rtc::ArrayView out) { + RTC_DCHECK_EQ(kBlockSize, in.size()); + RTC_DCHECK_EQ(kBlockSize / down_sampling_factor_, out.size()); + std::array x; + + // Limit the frequency content of the signal to avoid aliasing. + anti_aliasing_filter_.Process(in, x); + + // Reduce the impact of near-end noise. + noise_reduction_filter_.Process(x); + + // Downsample the signal. + for (size_t j = 0, k = 0; j < out.size(); ++j, k += down_sampling_factor_) { + RTC_DCHECK_GT(kBlockSize, k); + out[j] = x[k]; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/decimator.h b/webrtc/modules/audio_processing/aec3/decimator.h new file mode 100644 index 0000000..3ccd292 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/decimator.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/utility/cascaded_biquad_filter.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +// Provides functionality for decimating a signal. +class Decimator { + public: + explicit Decimator(size_t down_sampling_factor); + + // Downsamples the signal. + void Decimate(rtc::ArrayView in, rtc::ArrayView out); + + private: + const size_t down_sampling_factor_; + CascadedBiQuadFilter anti_aliasing_filter_; + CascadedBiQuadFilter noise_reduction_filter_; + + RTC_DISALLOW_COPY_AND_ASSIGN(Decimator); +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/delay_estimate.h b/webrtc/modules/audio_processing/aec3/delay_estimate.h new file mode 100644 index 0000000..ea5dd27 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/delay_estimate.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_DELAY_ESTIMATE_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_DELAY_ESTIMATE_H_ + +namespace webrtc { + +// Stores delay_estimates. +struct DelayEstimate { + enum class Quality { kCoarse, kRefined }; + + DelayEstimate(Quality quality, size_t delay) + : quality(quality), delay(delay) {} + + Quality quality; + size_t delay; + size_t blocks_since_last_change = 0; + size_t blocks_since_last_update = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_DELAY_ESTIMATE_H_ diff --git a/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.cc b/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.cc new file mode 100644 index 0000000..40073cf --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.cc @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/aec3/dominant_nearend_detector.h" + +#include + +namespace webrtc { +DominantNearendDetector::DominantNearendDetector( + const EchoCanceller3Config::Suppressor::DominantNearendDetection& config, + size_t num_capture_channels) + : enr_threshold_(config.enr_threshold), + enr_exit_threshold_(config.enr_exit_threshold), + snr_threshold_(config.snr_threshold), + hold_duration_(config.hold_duration), + trigger_threshold_(config.trigger_threshold), + use_during_initial_phase_(config.use_during_initial_phase), + num_capture_channels_(num_capture_channels), + trigger_counters_(num_capture_channels_), + hold_counters_(num_capture_channels_) {} + +void DominantNearendDetector::Update( + rtc::ArrayView> + nearend_spectrum, + rtc::ArrayView> + residual_echo_spectrum, + rtc::ArrayView> + comfort_noise_spectrum, + bool initial_state) { + nearend_state_ = false; + + auto low_frequency_energy = [](rtc::ArrayView spectrum) { + RTC_DCHECK_LE(16, spectrum.size()); + return std::accumulate(spectrum.begin() + 1, spectrum.begin() + 16, 0.f); + }; + + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + const float ne_sum = low_frequency_energy(nearend_spectrum[ch]); + const float echo_sum = low_frequency_energy(residual_echo_spectrum[ch]); + const float noise_sum = low_frequency_energy(comfort_noise_spectrum[ch]); + + // Detect strong active nearend if the nearend is sufficiently stronger than + // the echo and the nearend noise. + if ((!initial_state || use_during_initial_phase_) && + echo_sum < enr_threshold_ * ne_sum && + ne_sum > snr_threshold_ * noise_sum) { + if (++trigger_counters_[ch] >= trigger_threshold_) { + // After a period of strong active nearend activity, flag nearend mode. + hold_counters_[ch] = hold_duration_; + trigger_counters_[ch] = trigger_threshold_; + } + } else { + // Forget previously detected strong active nearend activity. + trigger_counters_[ch] = std::max(0, trigger_counters_[ch] - 1); + } + + // Exit nearend-state early at strong echo. + if (echo_sum > enr_exit_threshold_ * ne_sum && + echo_sum > snr_threshold_ * noise_sum) { + hold_counters_[ch] = 0; + } + + // Remain in any nearend mode for a certain duration. + hold_counters_[ch] = std::max(0, hold_counters_[ch] - 1); + nearend_state_ = nearend_state_ || hold_counters_[ch] > 0; + } +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.h b/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.h new file mode 100644 index 0000000..046d148 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/dominant_nearend_detector.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_DOMINANT_NEAREND_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_DOMINANT_NEAREND_DETECTOR_H_ + +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/nearend_detector.h" + +namespace webrtc { +// Class for selecting whether the suppressor is in the nearend or echo state. +class DominantNearendDetector : public NearendDetector { + public: + DominantNearendDetector( + const EchoCanceller3Config::Suppressor::DominantNearendDetection& config, + size_t num_capture_channels); + + // Returns whether the current state is the nearend state. + bool IsNearendState() const override { return nearend_state_; } + + // Updates the state selection based on latest spectral estimates. + void Update(rtc::ArrayView> + nearend_spectrum, + rtc::ArrayView> + residual_echo_spectrum, + rtc::ArrayView> + comfort_noise_spectrum, + bool initial_state) override; + + private: + const float enr_threshold_; + const float enr_exit_threshold_; + const float snr_threshold_; + const int hold_duration_; + const int trigger_threshold_; + const bool use_during_initial_phase_; + const size_t num_capture_channels_; + + bool nearend_state_ = false; + std::vector trigger_counters_; + std::vector hold_counters_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_DOMINANT_NEAREND_DETECTOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.cc b/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.cc new file mode 100644 index 0000000..c105911 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.cc @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/downsampled_render_buffer.h" + +#include + +namespace webrtc { + +DownsampledRenderBuffer::DownsampledRenderBuffer(size_t downsampled_buffer_size) + : size(static_cast(downsampled_buffer_size)), + buffer(downsampled_buffer_size, 0.f) { + std::fill(buffer.begin(), buffer.end(), 0.f); +} + +DownsampledRenderBuffer::~DownsampledRenderBuffer() = default; + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.h b/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.h new file mode 100644 index 0000000..fbdc9b4 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/downsampled_render_buffer.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_DOWNSAMPLED_RENDER_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_DOWNSAMPLED_RENDER_BUFFER_H_ + +#include + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +// Holds the circular buffer of the downsampled render data. +struct DownsampledRenderBuffer { + explicit DownsampledRenderBuffer(size_t downsampled_buffer_size); + ~DownsampledRenderBuffer(); + + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; + } + + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index > 0 ? index - 1 : size - 1; + } + + int OffsetIndex(int index, int offset) const { + RTC_DCHECK_GE(buffer.size(), offset); + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return (size + index + offset) % size; + } + + void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } + void IncWriteIndex() { write = IncIndex(write); } + void DecWriteIndex() { write = DecIndex(write); } + void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); } + void IncReadIndex() { read = IncIndex(read); } + void DecReadIndex() { read = DecIndex(read); } + + const int size; + std::vector buffer; + int write = 0; + int read = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_DOWNSAMPLED_RENDER_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/aec3/echo_audibility.cc b/webrtc/modules/audio_processing/aec3/echo_audibility.cc new file mode 100644 index 0000000..6ae414e --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_audibility.cc @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/echo_audibility.h" + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/block_buffer.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "modules/audio_processing/aec3/stationarity_estimator.h" + +namespace webrtc { + +EchoAudibility::EchoAudibility(bool use_render_stationarity_at_init) + : use_render_stationarity_at_init_(use_render_stationarity_at_init) { + Reset(); +} + +EchoAudibility::~EchoAudibility() = default; + +void EchoAudibility::Update(const RenderBuffer& render_buffer, + rtc::ArrayView average_reverb, + int delay_blocks, + bool external_delay_seen) { + UpdateRenderNoiseEstimator(render_buffer.GetSpectrumBuffer(), + render_buffer.GetBlockBuffer(), + external_delay_seen); + + if (external_delay_seen || use_render_stationarity_at_init_) { + UpdateRenderStationarityFlags(render_buffer, average_reverb, delay_blocks); + } +} + +void EchoAudibility::Reset() { + render_stationarity_.Reset(); + non_zero_render_seen_ = false; + render_spectrum_write_prev_ = absl::nullopt; +} + +void EchoAudibility::UpdateRenderStationarityFlags( + const RenderBuffer& render_buffer, + rtc::ArrayView average_reverb, + int min_channel_delay_blocks) { + const SpectrumBuffer& spectrum_buffer = render_buffer.GetSpectrumBuffer(); + int idx_at_delay = spectrum_buffer.OffsetIndex(spectrum_buffer.read, + min_channel_delay_blocks); + + int num_lookahead = render_buffer.Headroom() - min_channel_delay_blocks + 1; + num_lookahead = std::max(0, num_lookahead); + + render_stationarity_.UpdateStationarityFlags(spectrum_buffer, average_reverb, + idx_at_delay, num_lookahead); +} + +void EchoAudibility::UpdateRenderNoiseEstimator( + const SpectrumBuffer& spectrum_buffer, + const BlockBuffer& block_buffer, + bool external_delay_seen) { + if (!render_spectrum_write_prev_) { + render_spectrum_write_prev_ = spectrum_buffer.write; + render_block_write_prev_ = block_buffer.write; + return; + } + int render_spectrum_write_current = spectrum_buffer.write; + if (!non_zero_render_seen_ && !external_delay_seen) { + non_zero_render_seen_ = !IsRenderTooLow(block_buffer); + } + if (non_zero_render_seen_) { + for (int idx = render_spectrum_write_prev_.value(); + idx != render_spectrum_write_current; + idx = spectrum_buffer.DecIndex(idx)) { + render_stationarity_.UpdateNoiseEstimator(spectrum_buffer.buffer[idx]); + } + } + render_spectrum_write_prev_ = render_spectrum_write_current; +} + +bool EchoAudibility::IsRenderTooLow(const BlockBuffer& block_buffer) { + const int num_render_channels = + static_cast(block_buffer.buffer[0][0].size()); + bool too_low = false; + const int render_block_write_current = block_buffer.write; + if (render_block_write_current == render_block_write_prev_) { + too_low = true; + } else { + for (int idx = render_block_write_prev_; idx != render_block_write_current; + idx = block_buffer.IncIndex(idx)) { + float max_abs_over_channels = 0.f; + for (int ch = 0; ch < num_render_channels; ++ch) { + auto block = block_buffer.buffer[idx][0][ch]; + auto r = std::minmax_element(block.cbegin(), block.cend()); + float max_abs_channel = + std::max(std::fabs(*r.first), std::fabs(*r.second)); + max_abs_over_channels = + std::max(max_abs_over_channels, max_abs_channel); + } + if (max_abs_over_channels < 10.f) { + too_low = true; // Discards all blocks if one of them is too low. + break; + } + } + } + render_block_write_prev_ = render_block_write_current; + return too_low; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/echo_audibility.h b/webrtc/modules/audio_processing/aec3/echo_audibility.h new file mode 100644 index 0000000..1ffc017 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_audibility.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_AUDIBILITY_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_AUDIBILITY_H_ + +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "modules/audio_processing/aec3/block_buffer.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "modules/audio_processing/aec3/stationarity_estimator.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +class EchoAudibility { + public: + explicit EchoAudibility(bool use_render_stationarity_at_init); + ~EchoAudibility(); + + EchoAudibility(const EchoAudibility&) = delete; + EchoAudibility& operator=(const EchoAudibility&) = delete; + + // Feed new render data to the echo audibility estimator. + void Update(const RenderBuffer& render_buffer, + rtc::ArrayView average_reverb, + int min_channel_delay_blocks, + bool external_delay_seen); + // Get the residual echo scaling. + void GetResidualEchoScaling(bool filter_has_had_time_to_converge, + rtc::ArrayView residual_scaling) const { + for (size_t band = 0; band < residual_scaling.size(); ++band) { + if (render_stationarity_.IsBandStationary(band) && + (filter_has_had_time_to_converge || + use_render_stationarity_at_init_)) { + residual_scaling[band] = 0.f; + } else { + residual_scaling[band] = 1.0f; + } + } + } + + // Returns true if the current render block is estimated as stationary. + bool IsBlockStationary() const { + return render_stationarity_.IsBlockStationary(); + } + + private: + // Reset the EchoAudibility class. + void Reset(); + + // Updates the render stationarity flags for the current frame. + void UpdateRenderStationarityFlags(const RenderBuffer& render_buffer, + rtc::ArrayView average_reverb, + int delay_blocks); + + // Updates the noise estimator with the new render data since the previous + // call to this method. + void UpdateRenderNoiseEstimator(const SpectrumBuffer& spectrum_buffer, + const BlockBuffer& block_buffer, + bool external_delay_seen); + + // Returns a bool being true if the render signal contains just close to zero + // values. + bool IsRenderTooLow(const BlockBuffer& block_buffer); + + absl::optional render_spectrum_write_prev_; + int render_block_write_prev_; + bool non_zero_render_seen_; + const bool use_render_stationarity_at_init_; + StationarityEstimator render_stationarity_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_AUDIBILITY_H_ diff --git a/webrtc/modules/audio_processing/aec3/echo_canceller3.cc b/webrtc/modules/audio_processing/aec3/echo_canceller3.cc new file mode 100644 index 0000000..d2847df --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_canceller3.cc @@ -0,0 +1,868 @@ +/* + * 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 "modules/audio_processing/aec3/echo_canceller3.h" + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/high_pass_filter.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/experiments/field_trial_parser.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/field_trial.h" + +namespace webrtc { + +namespace { + +enum class EchoCanceller3ApiCall { kCapture, kRender }; + +bool DetectSaturation(rtc::ArrayView y) { + for (auto y_k : y) { + if (y_k >= 32700.0f || y_k <= -32700.0f) { + return true; + } + } + return false; +} + +// Retrieves a value from a field trial if it is available. If no value is +// present, the default value is returned. If the retrieved value is beyond the +// specified limits, the default value is returned instead. +void RetrieveFieldTrialValue(const char* trial_name, + float min, + float max, + float* value_to_update) { + const std::string field_trial_str = field_trial::FindFullName(trial_name); + + FieldTrialParameter field_trial_param(/*key=*/"", *value_to_update); + + ParseFieldTrial({&field_trial_param}, field_trial_str); + float field_trial_value = static_cast(field_trial_param.Get()); + + if (field_trial_value >= min && field_trial_value <= max) { + *value_to_update = field_trial_value; + } +} + +void RetrieveFieldTrialValue(const char* trial_name, + int min, + int max, + int* value_to_update) { + const std::string field_trial_str = field_trial::FindFullName(trial_name); + + FieldTrialParameter field_trial_param(/*key=*/"", *value_to_update); + + ParseFieldTrial({&field_trial_param}, field_trial_str); + float field_trial_value = field_trial_param.Get(); + + if (field_trial_value >= min && field_trial_value <= max) { + *value_to_update = field_trial_value; + } +} + +void FillSubFrameView( + AudioBuffer* frame, + size_t sub_frame_index, + std::vector>>* sub_frame_view) { + RTC_DCHECK_GE(1, sub_frame_index); + RTC_DCHECK_LE(0, sub_frame_index); + RTC_DCHECK_EQ(frame->num_bands(), sub_frame_view->size()); + RTC_DCHECK_EQ(frame->num_channels(), (*sub_frame_view)[0].size()); + for (size_t band = 0; band < sub_frame_view->size(); ++band) { + for (size_t channel = 0; channel < (*sub_frame_view)[0].size(); ++channel) { + (*sub_frame_view)[band][channel] = rtc::ArrayView( + &frame->split_bands(channel)[band][sub_frame_index * kSubFrameLength], + kSubFrameLength); + } + } +} + +void FillSubFrameView( + std::vector>>* frame, + size_t sub_frame_index, + std::vector>>* sub_frame_view) { + RTC_DCHECK_GE(1, sub_frame_index); + RTC_DCHECK_EQ(frame->size(), sub_frame_view->size()); + RTC_DCHECK_EQ((*frame)[0].size(), (*sub_frame_view)[0].size()); + for (size_t band = 0; band < frame->size(); ++band) { + for (size_t channel = 0; channel < (*frame)[band].size(); ++channel) { + (*sub_frame_view)[band][channel] = rtc::ArrayView( + &(*frame)[band][channel][sub_frame_index * kSubFrameLength], + kSubFrameLength); + } + } +} + +void ProcessCaptureFrameContent( + AudioBuffer* linear_output, + AudioBuffer* capture, + bool level_change, + bool saturated_microphone_signal, + size_t sub_frame_index, + FrameBlocker* capture_blocker, + BlockFramer* linear_output_framer, + BlockFramer* output_framer, + BlockProcessor* block_processor, + std::vector>>* linear_output_block, + std::vector>>* + linear_output_sub_frame_view, + std::vector>>* capture_block, + std::vector>>* capture_sub_frame_view) { + FillSubFrameView(capture, sub_frame_index, capture_sub_frame_view); + + if (linear_output) { + RTC_DCHECK(linear_output_framer); + RTC_DCHECK(linear_output_block); + RTC_DCHECK(linear_output_sub_frame_view); + FillSubFrameView(linear_output, sub_frame_index, + linear_output_sub_frame_view); + } + + capture_blocker->InsertSubFrameAndExtractBlock(*capture_sub_frame_view, + capture_block); + block_processor->ProcessCapture(level_change, saturated_microphone_signal, + linear_output_block, capture_block); + output_framer->InsertBlockAndExtractSubFrame(*capture_block, + capture_sub_frame_view); + + if (linear_output) { + RTC_DCHECK(linear_output_framer); + linear_output_framer->InsertBlockAndExtractSubFrame( + *linear_output_block, linear_output_sub_frame_view); + } +} + +void ProcessRemainingCaptureFrameContent( + bool level_change, + bool saturated_microphone_signal, + FrameBlocker* capture_blocker, + BlockFramer* linear_output_framer, + BlockFramer* output_framer, + BlockProcessor* block_processor, + std::vector>>* linear_output_block, + std::vector>>* block) { + if (!capture_blocker->IsBlockAvailable()) { + return; + } + + capture_blocker->ExtractBlock(block); + block_processor->ProcessCapture(level_change, saturated_microphone_signal, + linear_output_block, block); + output_framer->InsertBlock(*block); + + if (linear_output_framer) { + RTC_DCHECK(linear_output_block); + linear_output_framer->InsertBlock(*linear_output_block); + } +} + +void BufferRenderFrameContent( + std::vector>>* render_frame, + size_t sub_frame_index, + FrameBlocker* render_blocker, + BlockProcessor* block_processor, + std::vector>>* block, + std::vector>>* sub_frame_view) { + FillSubFrameView(render_frame, sub_frame_index, sub_frame_view); + render_blocker->InsertSubFrameAndExtractBlock(*sub_frame_view, block); + block_processor->BufferRender(*block); +} + +void BufferRemainingRenderFrameContent( + FrameBlocker* render_blocker, + BlockProcessor* block_processor, + std::vector>>* block) { + if (!render_blocker->IsBlockAvailable()) { + return; + } + render_blocker->ExtractBlock(block); + block_processor->BufferRender(*block); +} + +void CopyBufferIntoFrame(const AudioBuffer& buffer, + size_t num_bands, + size_t num_channels, + std::vector>>* frame) { + RTC_DCHECK_EQ(num_bands, frame->size()); + RTC_DCHECK_EQ(num_channels, (*frame)[0].size()); + RTC_DCHECK_EQ(AudioBuffer::kSplitBandSize, (*frame)[0][0].size()); + for (size_t band = 0; band < num_bands; ++band) { + for (size_t channel = 0; channel < num_channels; ++channel) { + rtc::ArrayView buffer_view( + &buffer.split_bands_const(channel)[band][0], + AudioBuffer::kSplitBandSize); + std::copy(buffer_view.begin(), buffer_view.end(), + (*frame)[band][channel].begin()); + } + } +} + +} // namespace + +// TODO(webrtc:5298): Move this to a separate file. +EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) { + EchoCanceller3Config adjusted_cfg = config; + + if (field_trial::IsEnabled("WebRTC-Aec3AntiHowlingMinimizationKillSwitch")) { + adjusted_cfg.suppressor.high_bands_suppression + .anti_howling_activation_threshold = 25.f; + adjusted_cfg.suppressor.high_bands_suppression.anti_howling_gain = 0.01f; + } + + if (field_trial::IsEnabled("WebRTC-Aec3UseShortConfigChangeDuration")) { + adjusted_cfg.filter.config_change_duration_blocks = 10; + } + + if (field_trial::IsEnabled("WebRTC-Aec3UseZeroInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = 0.f; + } else if (field_trial::IsEnabled( + "WebRTC-Aec3UseDot1SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = .1f; + } else if (field_trial::IsEnabled( + "WebRTC-Aec3UseDot2SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = .2f; + } else if (field_trial::IsEnabled( + "WebRTC-Aec3UseDot3SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = .3f; + } else if (field_trial::IsEnabled( + "WebRTC-Aec3UseDot6SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = .6f; + } else if (field_trial::IsEnabled( + "WebRTC-Aec3UseDot9SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = .9f; + } else if (field_trial::IsEnabled( + "WebRTC-Aec3Use1Dot2SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = 1.2f; + } else if (field_trial::IsEnabled( + "WebRTC-Aec3Use1Dot6SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = 1.6f; + } else if (field_trial::IsEnabled( + "WebRTC-Aec3Use2Dot0SecondsInitialStateDuration")) { + adjusted_cfg.filter.initial_state_seconds = 2.0f; + } + + if (field_trial::IsEnabled("WebRTC-Aec3EchoSaturationDetectionKillSwitch")) { + adjusted_cfg.ep_strength.echo_can_saturate = false; + } + + if (field_trial::IsEnabled("WebRTC-Aec3UseDot2ReverbDefaultLen")) { + adjusted_cfg.ep_strength.default_len = 0.2f; + } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot3ReverbDefaultLen")) { + adjusted_cfg.ep_strength.default_len = 0.3f; + } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot4ReverbDefaultLen")) { + adjusted_cfg.ep_strength.default_len = 0.4f; + } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot5ReverbDefaultLen")) { + adjusted_cfg.ep_strength.default_len = 0.5f; + } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot6ReverbDefaultLen")) { + adjusted_cfg.ep_strength.default_len = 0.6f; + } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot7ReverbDefaultLen")) { + adjusted_cfg.ep_strength.default_len = 0.7f; + } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot8ReverbDefaultLen")) { + adjusted_cfg.ep_strength.default_len = 0.8f; + } + + if (field_trial::IsEnabled("WebRTC-Aec3ShortHeadroomKillSwitch")) { + // Two blocks headroom. + adjusted_cfg.delay.delay_headroom_samples = kBlockSize * 2; + } + + if (field_trial::IsEnabled("WebRTC-Aec3ClampInstQualityToZeroKillSwitch")) { + adjusted_cfg.erle.clamp_quality_estimate_to_zero = false; + } + + if (field_trial::IsEnabled("WebRTC-Aec3ClampInstQualityToOneKillSwitch")) { + adjusted_cfg.erle.clamp_quality_estimate_to_one = false; + } + + if (field_trial::IsEnabled("WebRTC-Aec3OnsetDetectionKillSwitch")) { + adjusted_cfg.erle.onset_detection = false; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceRenderDelayEstimationDownmixing")) { + adjusted_cfg.delay.render_alignment_mixing.downmix = true; + adjusted_cfg.delay.render_alignment_mixing.adaptive_selection = false; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceCaptureDelayEstimationDownmixing")) { + adjusted_cfg.delay.capture_alignment_mixing.downmix = true; + adjusted_cfg.delay.capture_alignment_mixing.adaptive_selection = false; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceCaptureDelayEstimationLeftRightPrioritization")) { + adjusted_cfg.delay.capture_alignment_mixing.prefer_first_two_channels = + true; + } + + if (field_trial::IsEnabled( + "WebRTC-" + "Aec3RenderDelayEstimationLeftRightPrioritizationKillSwitch")) { + adjusted_cfg.delay.capture_alignment_mixing.prefer_first_two_channels = + false; + } + + if (field_trial::IsEnabled("WebRTC-Aec3SensitiveDominantNearendActivation")) { + adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold = 0.5f; + } else if (field_trial::IsEnabled( + "WebRTC-Aec3VerySensitiveDominantNearendActivation")) { + adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold = 0.75f; + } + + if (field_trial::IsEnabled("WebRTC-Aec3TransparentAntiHowlingGain")) { + adjusted_cfg.suppressor.high_bands_suppression.anti_howling_gain = 1.f; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceMoreTransparentNormalSuppressorTuning")) { + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_transparent = 0.4f; + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_suppress = 0.5f; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceMoreTransparentNearendSuppressorTuning")) { + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_transparent = 1.29f; + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_suppress = 1.3f; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceMoreTransparentNormalSuppressorHfTuning")) { + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_transparent = 0.3f; + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_suppress = 0.4f; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceMoreTransparentNearendSuppressorHfTuning")) { + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_transparent = 1.09f; + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_suppress = 1.1f; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceRapidlyAdjustingNormalSuppressorTunings")) { + adjusted_cfg.suppressor.normal_tuning.max_inc_factor = 2.5f; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceRapidlyAdjustingNearendSuppressorTunings")) { + adjusted_cfg.suppressor.nearend_tuning.max_inc_factor = 2.5f; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceSlowlyAdjustingNormalSuppressorTunings")) { + adjusted_cfg.suppressor.normal_tuning.max_dec_factor_lf = .2f; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceSlowlyAdjustingNearendSuppressorTunings")) { + adjusted_cfg.suppressor.nearend_tuning.max_dec_factor_lf = .2f; + } + + if (field_trial::IsEnabled("WebRTC-Aec3EnforceStationarityProperties")) { + adjusted_cfg.echo_audibility.use_stationarity_properties = true; + } + + if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceStationarityPropertiesAtInit")) { + adjusted_cfg.echo_audibility.use_stationarity_properties_at_init = true; + } + + if (field_trial::IsEnabled("WebRTC-Aec3EnforceLowActiveRenderLimit")) { + adjusted_cfg.render_levels.active_render_limit = 50.f; + } else if (field_trial::IsEnabled( + "WebRTC-Aec3EnforceVeryLowActiveRenderLimit")) { + adjusted_cfg.render_levels.active_render_limit = 30.f; + } + + if (field_trial::IsEnabled("WebRTC-Aec3NonlinearModeReverbKillSwitch")) { + adjusted_cfg.echo_model.model_reverb_in_nonlinear_mode = false; + } + + // Field-trial based override for the whole suppressor tuning. + const std::string suppressor_tuning_override_trial_name = + field_trial::FindFullName("WebRTC-Aec3SuppressorTuningOverride"); + + FieldTrialParameter nearend_tuning_mask_lf_enr_transparent( + "nearend_tuning_mask_lf_enr_transparent", + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_transparent); + FieldTrialParameter nearend_tuning_mask_lf_enr_suppress( + "nearend_tuning_mask_lf_enr_suppress", + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_suppress); + FieldTrialParameter nearend_tuning_mask_hf_enr_transparent( + "nearend_tuning_mask_hf_enr_transparent", + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_transparent); + FieldTrialParameter nearend_tuning_mask_hf_enr_suppress( + "nearend_tuning_mask_hf_enr_suppress", + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_suppress); + FieldTrialParameter nearend_tuning_max_inc_factor( + "nearend_tuning_max_inc_factor", + adjusted_cfg.suppressor.nearend_tuning.max_inc_factor); + FieldTrialParameter nearend_tuning_max_dec_factor_lf( + "nearend_tuning_max_dec_factor_lf", + adjusted_cfg.suppressor.nearend_tuning.max_dec_factor_lf); + FieldTrialParameter normal_tuning_mask_lf_enr_transparent( + "normal_tuning_mask_lf_enr_transparent", + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_transparent); + FieldTrialParameter normal_tuning_mask_lf_enr_suppress( + "normal_tuning_mask_lf_enr_suppress", + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_suppress); + FieldTrialParameter normal_tuning_mask_hf_enr_transparent( + "normal_tuning_mask_hf_enr_transparent", + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_transparent); + FieldTrialParameter normal_tuning_mask_hf_enr_suppress( + "normal_tuning_mask_hf_enr_suppress", + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_suppress); + FieldTrialParameter normal_tuning_max_inc_factor( + "normal_tuning_max_inc_factor", + adjusted_cfg.suppressor.normal_tuning.max_inc_factor); + FieldTrialParameter normal_tuning_max_dec_factor_lf( + "normal_tuning_max_dec_factor_lf", + adjusted_cfg.suppressor.normal_tuning.max_dec_factor_lf); + FieldTrialParameter dominant_nearend_detection_enr_threshold( + "dominant_nearend_detection_enr_threshold", + adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold); + FieldTrialParameter dominant_nearend_detection_enr_exit_threshold( + "dominant_nearend_detection_enr_exit_threshold", + adjusted_cfg.suppressor.dominant_nearend_detection.enr_exit_threshold); + FieldTrialParameter dominant_nearend_detection_snr_threshold( + "dominant_nearend_detection_snr_threshold", + adjusted_cfg.suppressor.dominant_nearend_detection.snr_threshold); + FieldTrialParameter dominant_nearend_detection_hold_duration( + "dominant_nearend_detection_hold_duration", + adjusted_cfg.suppressor.dominant_nearend_detection.hold_duration); + FieldTrialParameter dominant_nearend_detection_trigger_threshold( + "dominant_nearend_detection_trigger_threshold", + adjusted_cfg.suppressor.dominant_nearend_detection.trigger_threshold); + FieldTrialParameter ep_strength_default_len( + "ep_strength_default_len", adjusted_cfg.ep_strength.default_len); + + ParseFieldTrial( + {&nearend_tuning_mask_lf_enr_transparent, + &nearend_tuning_mask_lf_enr_suppress, + &nearend_tuning_mask_hf_enr_transparent, + &nearend_tuning_mask_hf_enr_suppress, &nearend_tuning_max_inc_factor, + &nearend_tuning_max_dec_factor_lf, + &normal_tuning_mask_lf_enr_transparent, + &normal_tuning_mask_lf_enr_suppress, + &normal_tuning_mask_hf_enr_transparent, + &normal_tuning_mask_hf_enr_suppress, &normal_tuning_max_inc_factor, + &normal_tuning_max_dec_factor_lf, + &dominant_nearend_detection_enr_threshold, + &dominant_nearend_detection_enr_exit_threshold, + &dominant_nearend_detection_snr_threshold, + &dominant_nearend_detection_hold_duration, + &dominant_nearend_detection_trigger_threshold, &ep_strength_default_len}, + suppressor_tuning_override_trial_name); + + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_transparent = + static_cast(nearend_tuning_mask_lf_enr_transparent.Get()); + adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_suppress = + static_cast(nearend_tuning_mask_lf_enr_suppress.Get()); + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_transparent = + static_cast(nearend_tuning_mask_hf_enr_transparent.Get()); + adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_suppress = + static_cast(nearend_tuning_mask_hf_enr_suppress.Get()); + adjusted_cfg.suppressor.nearend_tuning.max_inc_factor = + static_cast(nearend_tuning_max_inc_factor.Get()); + adjusted_cfg.suppressor.nearend_tuning.max_dec_factor_lf = + static_cast(nearend_tuning_max_dec_factor_lf.Get()); + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_transparent = + static_cast(normal_tuning_mask_lf_enr_transparent.Get()); + adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_suppress = + static_cast(normal_tuning_mask_lf_enr_suppress.Get()); + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_transparent = + static_cast(normal_tuning_mask_hf_enr_transparent.Get()); + adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_suppress = + static_cast(normal_tuning_mask_hf_enr_suppress.Get()); + adjusted_cfg.suppressor.normal_tuning.max_inc_factor = + static_cast(normal_tuning_max_inc_factor.Get()); + adjusted_cfg.suppressor.normal_tuning.max_dec_factor_lf = + static_cast(normal_tuning_max_dec_factor_lf.Get()); + adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold = + static_cast(dominant_nearend_detection_enr_threshold.Get()); + adjusted_cfg.suppressor.dominant_nearend_detection.enr_exit_threshold = + static_cast(dominant_nearend_detection_enr_exit_threshold.Get()); + adjusted_cfg.suppressor.dominant_nearend_detection.snr_threshold = + static_cast(dominant_nearend_detection_snr_threshold.Get()); + adjusted_cfg.suppressor.dominant_nearend_detection.hold_duration = + dominant_nearend_detection_hold_duration.Get(); + adjusted_cfg.suppressor.dominant_nearend_detection.trigger_threshold = + dominant_nearend_detection_trigger_threshold.Get(); + adjusted_cfg.ep_strength.default_len = + static_cast(ep_strength_default_len.Get()); + + // Field trial-based overrides of individual suppressor parameters. + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNearendLfMaskTransparentOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_transparent); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNearendLfMaskSuppressOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_suppress); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNearendHfMaskTransparentOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_transparent); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNearendHfMaskSuppressOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.nearend_tuning.mask_hf.enr_suppress); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNearendMaxIncFactorOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.nearend_tuning.max_inc_factor); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNearendMaxDecFactorLfOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.nearend_tuning.max_dec_factor_lf); + + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNormalLfMaskTransparentOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_transparent); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNormalLfMaskSuppressOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_suppress); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNormalHfMaskTransparentOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_transparent); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNormalHfMaskSuppressOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.normal_tuning.mask_hf.enr_suppress); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNormalMaxIncFactorOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.normal_tuning.max_inc_factor); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorNormalMaxDecFactorLfOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.normal_tuning.max_dec_factor_lf); + + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorDominantNearendEnrThresholdOverride", 0.f, 100.f, + &adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorDominantNearendEnrExitThresholdOverride", 0.f, + 100.f, + &adjusted_cfg.suppressor.dominant_nearend_detection.enr_exit_threshold); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorDominantNearendSnrThresholdOverride", 0.f, 100.f, + &adjusted_cfg.suppressor.dominant_nearend_detection.snr_threshold); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorDominantNearendHoldDurationOverride", 0, 1000, + &adjusted_cfg.suppressor.dominant_nearend_detection.hold_duration); + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorDominantNearendTriggerThresholdOverride", 0, 1000, + &adjusted_cfg.suppressor.dominant_nearend_detection.trigger_threshold); + + RetrieveFieldTrialValue( + "WebRTC-Aec3SuppressorAntiHowlingGainOverride", 0.f, 10.f, + &adjusted_cfg.suppressor.high_bands_suppression.anti_howling_gain); + + RetrieveFieldTrialValue("WebRTC-Aec3SuppressorEpStrengthDefaultLenOverride", + -1.f, 1.f, &adjusted_cfg.ep_strength.default_len); + + return adjusted_cfg; +} + +class EchoCanceller3::RenderWriter { + public: + RenderWriter(ApmDataDumper* data_dumper, + SwapQueue>>, + Aec3RenderQueueItemVerifier>* render_transfer_queue, + size_t num_bands, + size_t num_channels); + + RenderWriter() = delete; + RenderWriter(const RenderWriter&) = delete; + RenderWriter& operator=(const RenderWriter&) = delete; + + ~RenderWriter(); + void Insert(const AudioBuffer& input); + + private: + ApmDataDumper* data_dumper_; + const size_t num_bands_; + const size_t num_channels_; + HighPassFilter high_pass_filter_; + std::vector>> render_queue_input_frame_; + SwapQueue>>, + Aec3RenderQueueItemVerifier>* render_transfer_queue_; +}; + +EchoCanceller3::RenderWriter::RenderWriter( + ApmDataDumper* data_dumper, + SwapQueue>>, + Aec3RenderQueueItemVerifier>* render_transfer_queue, + size_t num_bands, + size_t num_channels) + : data_dumper_(data_dumper), + num_bands_(num_bands), + num_channels_(num_channels), + high_pass_filter_(16000, num_channels), + render_queue_input_frame_( + num_bands_, + std::vector>( + num_channels_, + std::vector(AudioBuffer::kSplitBandSize, 0.f))), + render_transfer_queue_(render_transfer_queue) { + RTC_DCHECK(data_dumper); +} + +EchoCanceller3::RenderWriter::~RenderWriter() = default; + +void EchoCanceller3::RenderWriter::Insert(const AudioBuffer& input) { + RTC_DCHECK_EQ(AudioBuffer::kSplitBandSize, input.num_frames_per_band()); + RTC_DCHECK_EQ(num_bands_, input.num_bands()); + RTC_DCHECK_EQ(num_channels_, input.num_channels()); + + // TODO(bugs.webrtc.org/8759) Temporary work-around. + if (num_bands_ != input.num_bands()) + return; + + data_dumper_->DumpWav("aec3_render_input", AudioBuffer::kSplitBandSize, + &input.split_bands_const(0)[0][0], 16000, 1); + + CopyBufferIntoFrame(input, num_bands_, num_channels_, + &render_queue_input_frame_); + high_pass_filter_.Process(&render_queue_input_frame_[0]); + + static_cast(render_transfer_queue_->Insert(&render_queue_input_frame_)); +} + +int EchoCanceller3::instance_count_ = 0; + +EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels) + : EchoCanceller3(AdjustConfig(config), + sample_rate_hz, + num_render_channels, + num_capture_channels, + std::unique_ptr( + BlockProcessor::Create(AdjustConfig(config), + sample_rate_hz, + num_render_channels, + num_capture_channels))) {} +EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr block_processor) + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + config_(config), + sample_rate_hz_(sample_rate_hz), + num_bands_(NumBandsForRate(sample_rate_hz_)), + num_render_channels_(num_render_channels), + num_capture_channels_(num_capture_channels), + output_framer_(num_bands_, num_capture_channels_), + capture_blocker_(num_bands_, num_capture_channels_), + render_blocker_(num_bands_, num_render_channels_), + render_transfer_queue_( + kRenderTransferQueueSizeFrames, + std::vector>>( + num_bands_, + std::vector>( + num_render_channels_, + std::vector(AudioBuffer::kSplitBandSize, 0.f))), + Aec3RenderQueueItemVerifier(num_bands_, + num_render_channels_, + AudioBuffer::kSplitBandSize)), + block_processor_(std::move(block_processor)), + render_queue_output_frame_( + num_bands_, + std::vector>( + num_render_channels_, + std::vector(AudioBuffer::kSplitBandSize, 0.f))), + render_block_( + num_bands_, + std::vector>(num_render_channels_, + std::vector(kBlockSize, 0.f))), + capture_block_( + num_bands_, + std::vector>(num_capture_channels_, + std::vector(kBlockSize, 0.f))), + render_sub_frame_view_( + num_bands_, + std::vector>(num_render_channels_)), + capture_sub_frame_view_( + num_bands_, + std::vector>(num_capture_channels_)) { + RTC_DCHECK(ValidFullBandRate(sample_rate_hz_)); + + if (config_.delay.fixed_capture_delay_samples > 0) { + block_delay_buffer_.reset(new BlockDelayBuffer( + num_capture_channels_, num_bands_, AudioBuffer::kSplitBandSize, + config_.delay.fixed_capture_delay_samples)); + } + + render_writer_.reset(new RenderWriter(data_dumper_.get(), + &render_transfer_queue_, num_bands_, + num_render_channels_)); + + RTC_DCHECK_EQ(num_bands_, std::max(sample_rate_hz_, 16000) / 16000); + RTC_DCHECK_GE(kMaxNumBands, num_bands_); + + if (config_.filter.export_linear_aec_output) { + linear_output_framer_.reset(new BlockFramer(1, num_capture_channels_)); + linear_output_block_ = + std::make_unique>>>( + 1, std::vector>( + num_capture_channels_, std::vector(kBlockSize, 0.f))); + linear_output_sub_frame_view_ = + std::vector>>( + 1, std::vector>(num_capture_channels_)); + } +} + +EchoCanceller3::~EchoCanceller3() = default; + +void EchoCanceller3::AnalyzeRender(const AudioBuffer& render) { + RTC_DCHECK_RUNS_SERIALIZED(&render_race_checker_); + + RTC_DCHECK_EQ(render.num_channels(), num_render_channels_); + data_dumper_->DumpRaw("aec3_call_order", + static_cast(EchoCanceller3ApiCall::kRender)); + + return render_writer_->Insert(render); +} + +void EchoCanceller3::AnalyzeCapture(const AudioBuffer& capture) { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + data_dumper_->DumpWav("aec3_capture_analyze_input", capture.num_frames(), + capture.channels_const()[0], sample_rate_hz_, 1); + saturated_microphone_signal_ = false; + for (size_t channel = 0; channel < capture.num_channels(); ++channel) { + saturated_microphone_signal_ |= + DetectSaturation(rtc::ArrayView( + capture.channels_const()[channel], capture.num_frames())); + if (saturated_microphone_signal_) { + break; + } + } +} + +void EchoCanceller3::ProcessCapture(AudioBuffer* capture, bool level_change) { + ProcessCapture(capture, nullptr, level_change); +} + +void EchoCanceller3::ProcessCapture(AudioBuffer* capture, + AudioBuffer* linear_output, + bool level_change) { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + RTC_DCHECK(capture); + RTC_DCHECK_EQ(num_bands_, capture->num_bands()); + RTC_DCHECK_EQ(AudioBuffer::kSplitBandSize, capture->num_frames_per_band()); + RTC_DCHECK_EQ(capture->num_channels(), num_capture_channels_); + data_dumper_->DumpRaw("aec3_call_order", + static_cast(EchoCanceller3ApiCall::kCapture)); + + if (linear_output && !linear_output_framer_) { + RTC_LOG(LS_ERROR) << "Trying to retrieve the linear AEC output without " + "properly configuring AEC3."; + RTC_NOTREACHED(); + } + + // Report capture call in the metrics and periodically update API call + // metrics. + api_call_metrics_.ReportCaptureCall(); + + // Optionally delay the capture signal. + if (config_.delay.fixed_capture_delay_samples > 0) { + RTC_DCHECK(block_delay_buffer_); + block_delay_buffer_->DelaySignal(capture); + } + + rtc::ArrayView capture_lower_band = rtc::ArrayView( + &capture->split_bands(0)[0][0], AudioBuffer::kSplitBandSize); + + data_dumper_->DumpWav("aec3_capture_input", capture_lower_band, 16000, 1); + + EmptyRenderQueue(); + + ProcessCaptureFrameContent(linear_output, capture, level_change, + saturated_microphone_signal_, 0, &capture_blocker_, + linear_output_framer_.get(), &output_framer_, + block_processor_.get(), linear_output_block_.get(), + &linear_output_sub_frame_view_, &capture_block_, + &capture_sub_frame_view_); + + ProcessCaptureFrameContent(linear_output, capture, level_change, + saturated_microphone_signal_, 1, &capture_blocker_, + linear_output_framer_.get(), &output_framer_, + block_processor_.get(), linear_output_block_.get(), + &linear_output_sub_frame_view_, &capture_block_, + &capture_sub_frame_view_); + + ProcessRemainingCaptureFrameContent( + level_change, saturated_microphone_signal_, &capture_blocker_, + linear_output_framer_.get(), &output_framer_, block_processor_.get(), + linear_output_block_.get(), &capture_block_); + + data_dumper_->DumpWav("aec3_capture_output", AudioBuffer::kSplitBandSize, + &capture->split_bands(0)[0][0], 16000, 1); +} + +EchoControl::Metrics EchoCanceller3::GetMetrics() const { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + Metrics metrics; + block_processor_->GetMetrics(&metrics); + return metrics; +} + +void EchoCanceller3::SetAudioBufferDelay(int delay_ms) { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + block_processor_->SetAudioBufferDelay(delay_ms); +} + +bool EchoCanceller3::ActiveProcessing() const { + return true; +} + +EchoCanceller3Config EchoCanceller3::CreateDefaultConfig( + size_t num_render_channels, + size_t num_capture_channels) { + EchoCanceller3Config cfg; + if (num_render_channels > 1) { + // Use shorter and more rapidly adapting coarse filter to compensate for + // thge increased number of total filter parameters to adapt. + cfg.filter.coarse.length_blocks = 11; + cfg.filter.coarse.rate = 0.95f; + cfg.filter.coarse_initial.length_blocks = 11; + cfg.filter.coarse_initial.rate = 0.95f; + + // Use more concervative suppressor behavior for non-nearend speech. + cfg.suppressor.normal_tuning.max_dec_factor_lf = 0.35f; + cfg.suppressor.normal_tuning.max_inc_factor = 1.5f; + } + return cfg; +} + +void EchoCanceller3::EmptyRenderQueue() { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + bool frame_to_buffer = + render_transfer_queue_.Remove(&render_queue_output_frame_); + while (frame_to_buffer) { + // Report render call in the metrics. + api_call_metrics_.ReportRenderCall(); + + BufferRenderFrameContent(&render_queue_output_frame_, 0, &render_blocker_, + block_processor_.get(), &render_block_, + &render_sub_frame_view_); + + BufferRenderFrameContent(&render_queue_output_frame_, 1, &render_blocker_, + block_processor_.get(), &render_block_, + &render_sub_frame_view_); + + BufferRemainingRenderFrameContent(&render_blocker_, block_processor_.get(), + &render_block_); + + frame_to_buffer = + render_transfer_queue_.Remove(&render_queue_output_frame_); + } +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/echo_canceller3.h b/webrtc/modules/audio_processing/aec3/echo_canceller3.h new file mode 100644 index 0000000..bacd5df --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_canceller3.h @@ -0,0 +1,196 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_CANCELLER3_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_CANCELLER3_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "modules/audio_processing/aec3/api_call_jitter_metrics.h" +#include "modules/audio_processing/aec3/block_delay_buffer.h" +#include "modules/audio_processing/aec3/block_framer.h" +#include "modules/audio_processing/aec3/block_processor.h" +#include "modules/audio_processing/aec3/frame_blocker.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/race_checker.h" +#include "rtc_base/swap_queue.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +// Method for adjusting config parameter dependencies. +// Only to be used externally to AEC3 for testing purposes. +// TODO(webrtc:5298): Move this to a separate file. +EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config); + +// Functor for verifying the invariance of the frames being put into the render +// queue. +class Aec3RenderQueueItemVerifier { + public: + Aec3RenderQueueItemVerifier(size_t num_bands, + size_t num_channels, + size_t frame_length) + : num_bands_(num_bands), + num_channels_(num_channels), + frame_length_(frame_length) {} + + bool operator()(const std::vector>>& v) const { + if (v.size() != num_bands_) { + return false; + } + for (const auto& band : v) { + if (band.size() != num_channels_) { + return false; + } + for (const auto& channel : band) { + if (channel.size() != frame_length_) { + return false; + } + } + } + return true; + } + + private: + const size_t num_bands_; + const size_t num_channels_; + const size_t frame_length_; +}; + +// Main class for the echo canceller3. +// It does 4 things: +// -Receives 10 ms frames of band-split audio. +// -Provides the lower level echo canceller functionality with +// blocks of 64 samples of audio data. +// -Partially handles the jitter in the render and capture API +// call sequence. +// +// The class is supposed to be used in a non-concurrent manner apart from the +// AnalyzeRender call which can be called concurrently with the other methods. +class EchoCanceller3 : public EchoControl { + public: + // Normal c-tor to use. + EchoCanceller3(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels); + // Testing c-tor that is used only for testing purposes. + EchoCanceller3(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels, + std::unique_ptr block_processor); + ~EchoCanceller3() override; + EchoCanceller3(const EchoCanceller3&) = delete; + EchoCanceller3& operator=(const EchoCanceller3&) = delete; + + // Analyzes and stores an internal copy of the split-band domain render + // signal. + void AnalyzeRender(AudioBuffer* render) override { AnalyzeRender(*render); } + // Analyzes the full-band domain capture signal to detect signal saturation. + void AnalyzeCapture(AudioBuffer* capture) override { + AnalyzeCapture(*capture); + } + // Processes the split-band domain capture signal in order to remove any echo + // present in the signal. + void ProcessCapture(AudioBuffer* capture, bool level_change) override; + // As above, but also returns the linear filter output. + void ProcessCapture(AudioBuffer* capture, + AudioBuffer* linear_output, + bool level_change) override; + // Collect current metrics from the echo canceller. + Metrics GetMetrics() const override; + // Provides an optional external estimate of the audio buffer delay. + void SetAudioBufferDelay(int delay_ms) override; + + bool ActiveProcessing() const override; + + // Signals whether an external detector has detected echo leakage from the + // echo canceller. + // Note that in the case echo leakage has been flagged, it should be unflagged + // once it is no longer occurring. + void UpdateEchoLeakageStatus(bool leakage_detected) { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + block_processor_->UpdateEchoLeakageStatus(leakage_detected); + } + + // Produces a default configuration that is suitable for a certain combination + // of render and capture channels. + static EchoCanceller3Config CreateDefaultConfig(size_t num_render_channels, + size_t num_capture_channels); + + private: + class RenderWriter; + + // Empties the render SwapQueue. + void EmptyRenderQueue(); + + // Analyzes and stores an internal copy of the split-band domain render + // signal. + void AnalyzeRender(const AudioBuffer& render); + // Analyzes the full-band domain capture signal to detect signal saturation. + void AnalyzeCapture(const AudioBuffer& capture); + + rtc::RaceChecker capture_race_checker_; + rtc::RaceChecker render_race_checker_; + + // State that is accessed by the AnalyzeRender call. + std::unique_ptr render_writer_ + RTC_GUARDED_BY(render_race_checker_); + + // State that may be accessed by the capture thread. + static int instance_count_; + std::unique_ptr data_dumper_; + const EchoCanceller3Config config_; + const int sample_rate_hz_; + const int num_bands_; + const size_t num_render_channels_; + const size_t num_capture_channels_; + std::unique_ptr linear_output_framer_ + RTC_GUARDED_BY(capture_race_checker_); + BlockFramer output_framer_ RTC_GUARDED_BY(capture_race_checker_); + FrameBlocker capture_blocker_ RTC_GUARDED_BY(capture_race_checker_); + FrameBlocker render_blocker_ RTC_GUARDED_BY(capture_race_checker_); + SwapQueue>>, + Aec3RenderQueueItemVerifier> + render_transfer_queue_; + std::unique_ptr block_processor_ + RTC_GUARDED_BY(capture_race_checker_); + std::vector>> render_queue_output_frame_ + RTC_GUARDED_BY(capture_race_checker_); + bool saturated_microphone_signal_ RTC_GUARDED_BY(capture_race_checker_) = + false; + std::vector>> render_block_ + RTC_GUARDED_BY(capture_race_checker_); + std::unique_ptr>>> + linear_output_block_ RTC_GUARDED_BY(capture_race_checker_); + std::vector>> capture_block_ + RTC_GUARDED_BY(capture_race_checker_); + std::vector>> render_sub_frame_view_ + RTC_GUARDED_BY(capture_race_checker_); + std::vector>> linear_output_sub_frame_view_ + RTC_GUARDED_BY(capture_race_checker_); + std::vector>> capture_sub_frame_view_ + RTC_GUARDED_BY(capture_race_checker_); + std::unique_ptr block_delay_buffer_ + RTC_GUARDED_BY(capture_race_checker_); + ApiCallJitterMetrics api_call_metrics_ RTC_GUARDED_BY(capture_race_checker_); +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_CANCELLER3_H_ diff --git a/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.cc b/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.cc new file mode 100644 index 0000000..2c987f9 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.cc @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/echo_path_delay_estimator.h" + +#include + +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +EchoPathDelayEstimator::EchoPathDelayEstimator( + ApmDataDumper* data_dumper, + const EchoCanceller3Config& config, + size_t num_capture_channels) + : data_dumper_(data_dumper), + down_sampling_factor_(config.delay.down_sampling_factor), + sub_block_size_(down_sampling_factor_ != 0 + ? kBlockSize / down_sampling_factor_ + : kBlockSize), + capture_mixer_(num_capture_channels, + config.delay.capture_alignment_mixing), + capture_decimator_(down_sampling_factor_), + matched_filter_( + data_dumper_, + DetectOptimization(), + sub_block_size_, + kMatchedFilterWindowSizeSubBlocks, + config.delay.num_filters, + kMatchedFilterAlignmentShiftSizeSubBlocks, + config.delay.down_sampling_factor == 8 + ? config.render_levels.poor_excitation_render_limit_ds8 + : config.render_levels.poor_excitation_render_limit, + config.delay.delay_estimate_smoothing, + config.delay.delay_candidate_detection_threshold), + matched_filter_lag_aggregator_(data_dumper_, + matched_filter_.GetMaxFilterLag(), + config.delay.delay_selection_thresholds) { + RTC_DCHECK(data_dumper); + RTC_DCHECK(down_sampling_factor_ > 0); +} + +EchoPathDelayEstimator::~EchoPathDelayEstimator() = default; + +void EchoPathDelayEstimator::Reset(bool reset_delay_confidence) { + Reset(true, reset_delay_confidence); +} + +absl::optional EchoPathDelayEstimator::EstimateDelay( + const DownsampledRenderBuffer& render_buffer, + const std::vector>& capture) { + RTC_DCHECK_EQ(kBlockSize, capture[0].size()); + + std::array downsampled_capture_data; + rtc::ArrayView downsampled_capture(downsampled_capture_data.data(), + sub_block_size_); + + std::array downmixed_capture; + capture_mixer_.ProduceOutput(capture, downmixed_capture); + capture_decimator_.Decimate(downmixed_capture, downsampled_capture); + data_dumper_->DumpWav("aec3_capture_decimator_output", + downsampled_capture.size(), downsampled_capture.data(), + 16000 / down_sampling_factor_, 1); + matched_filter_.Update(render_buffer, downsampled_capture); + + absl::optional aggregated_matched_filter_lag = + matched_filter_lag_aggregator_.Aggregate( + matched_filter_.GetLagEstimates()); + + // Run clockdrift detection. + if (aggregated_matched_filter_lag && + (*aggregated_matched_filter_lag).quality == + DelayEstimate::Quality::kRefined) + clockdrift_detector_.Update((*aggregated_matched_filter_lag).delay); + + // TODO(peah): Move this logging outside of this class once EchoCanceller3 + // development is done. + data_dumper_->DumpRaw( + "aec3_echo_path_delay_estimator_delay", + aggregated_matched_filter_lag + ? static_cast(aggregated_matched_filter_lag->delay * + down_sampling_factor_) + : -1); + + // Return the detected delay in samples as the aggregated matched filter lag + // compensated by the down sampling factor for the signal being correlated. + if (aggregated_matched_filter_lag) { + aggregated_matched_filter_lag->delay *= down_sampling_factor_; + } + + if (old_aggregated_lag_ && aggregated_matched_filter_lag && + old_aggregated_lag_->delay == aggregated_matched_filter_lag->delay) { + ++consistent_estimate_counter_; + } else { + consistent_estimate_counter_ = 0; + } + old_aggregated_lag_ = aggregated_matched_filter_lag; + constexpr size_t kNumBlocksPerSecondBy2 = kNumBlocksPerSecond / 2; + if (consistent_estimate_counter_ > kNumBlocksPerSecondBy2) { + Reset(false, false); + } + + return aggregated_matched_filter_lag; +} + +void EchoPathDelayEstimator::Reset(bool reset_lag_aggregator, + bool reset_delay_confidence) { + if (reset_lag_aggregator) { + matched_filter_lag_aggregator_.Reset(reset_delay_confidence); + } + matched_filter_.Reset(); + old_aggregated_lag_ = absl::nullopt; + consistent_estimate_counter_ = 0; +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.h b/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.h new file mode 100644 index 0000000..6c8c212 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_path_delay_estimator.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_DELAY_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_DELAY_ESTIMATOR_H_ + +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "modules/audio_processing/aec3/alignment_mixer.h" +#include "modules/audio_processing/aec3/clockdrift_detector.h" +#include "modules/audio_processing/aec3/decimator.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/matched_filter.h" +#include "modules/audio_processing/aec3/matched_filter_lag_aggregator.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +class ApmDataDumper; +struct DownsampledRenderBuffer; +struct EchoCanceller3Config; + +// Estimates the delay of the echo path. +class EchoPathDelayEstimator { + public: + EchoPathDelayEstimator(ApmDataDumper* data_dumper, + const EchoCanceller3Config& config, + size_t num_capture_channels); + ~EchoPathDelayEstimator(); + + // Resets the estimation. If the delay confidence is reset, the reset behavior + // is as if the call is restarted. + void Reset(bool reset_delay_confidence); + + // Produce a delay estimate if such is avaliable. + absl::optional EstimateDelay( + const DownsampledRenderBuffer& render_buffer, + const std::vector>& capture); + + // Log delay estimator properties. + void LogDelayEstimationProperties(int sample_rate_hz, size_t shift) const { + matched_filter_.LogFilterProperties(sample_rate_hz, shift, + down_sampling_factor_); + } + + // Returns the level of detected clockdrift. + ClockdriftDetector::Level Clockdrift() const { + return clockdrift_detector_.ClockdriftLevel(); + } + + private: + ApmDataDumper* const data_dumper_; + const size_t down_sampling_factor_; + const size_t sub_block_size_; + AlignmentMixer capture_mixer_; + Decimator capture_decimator_; + MatchedFilter matched_filter_; + MatchedFilterLagAggregator matched_filter_lag_aggregator_; + absl::optional old_aggregated_lag_; + size_t consistent_estimate_counter_ = 0; + ClockdriftDetector clockdrift_detector_; + + // Internal reset method with more granularity. + void Reset(bool reset_lag_aggregator, bool reset_delay_confidence); + + RTC_DISALLOW_COPY_AND_ASSIGN(EchoPathDelayEstimator); +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_DELAY_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/echo_path_variability.cc b/webrtc/modules/audio_processing/aec3/echo_path_variability.cc new file mode 100644 index 0000000..0ae9cff --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_path_variability.cc @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/echo_path_variability.h" + +namespace webrtc { + +EchoPathVariability::EchoPathVariability(bool gain_change, + DelayAdjustment delay_change, + bool clock_drift) + : gain_change(gain_change), + delay_change(delay_change), + clock_drift(clock_drift) {} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/echo_path_variability.h b/webrtc/modules/audio_processing/aec3/echo_path_variability.h new file mode 100644 index 0000000..78e4f64 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_path_variability.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_VARIABILITY_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_VARIABILITY_H_ + +namespace webrtc { + +struct EchoPathVariability { + enum class DelayAdjustment { + kNone, + kBufferFlush, + kNewDetectedDelay + }; + + EchoPathVariability(bool gain_change, + DelayAdjustment delay_change, + bool clock_drift); + + bool AudioPathChanged() const { + return gain_change || delay_change != DelayAdjustment::kNone; + } + bool gain_change; + DelayAdjustment delay_change; + bool clock_drift; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_PATH_VARIABILITY_H_ diff --git a/webrtc/modules/audio_processing/aec3/echo_remover.cc b/webrtc/modules/audio_processing/aec3/echo_remover.cc new file mode 100644 index 0000000..a3cd22f --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_remover.cc @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/echo_remover.h" + +#include +#include + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec3_fft.h" +#include "modules/audio_processing/aec3/aec_state.h" +#include "modules/audio_processing/aec3/comfort_noise_generator.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/echo_remover_metrics.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/render_signal_analyzer.h" +#include "modules/audio_processing/aec3/residual_echo_estimator.h" +#include "modules/audio_processing/aec3/subtractor.h" +#include "modules/audio_processing/aec3/subtractor_output.h" +#include "modules/audio_processing/aec3/suppression_filter.h" +#include "modules/audio_processing/aec3/suppression_gain.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +namespace { + +// Maximum number of channels for which the capture channel data is stored on +// the stack. If the number of channels are larger than this, they are stored +// using scratch memory that is pre-allocated on the heap. The reason for this +// partitioning is not to waste heap space for handling the more common numbers +// of channels, while at the same time not limiting the support for higher +// numbers of channels by enforcing the capture channel data to be stored on the +// stack using a fixed maximum value. +constexpr size_t kMaxNumChannelsOnStack = 2; + +// Chooses the number of channels to store on the heap when that is required due +// to the number of capture channels being larger than the pre-defined number +// of channels to store on the stack. +size_t NumChannelsOnHeap(size_t num_capture_channels) { + return num_capture_channels > kMaxNumChannelsOnStack ? num_capture_channels + : 0; +} + +void LinearEchoPower(const FftData& E, + const FftData& Y, + std::array* S2) { + for (size_t k = 0; k < E.re.size(); ++k) { + (*S2)[k] = (Y.re[k] - E.re[k]) * (Y.re[k] - E.re[k]) + + (Y.im[k] - E.im[k]) * (Y.im[k] - E.im[k]); + } +} + +// Fades between two input signals using a fix-sized transition. +void SignalTransition(rtc::ArrayView from, + rtc::ArrayView to, + rtc::ArrayView out) { + if (from == to) { + RTC_DCHECK_EQ(to.size(), out.size()); + std::copy(to.begin(), to.end(), out.begin()); + } else { + constexpr size_t kTransitionSize = 30; + constexpr float kOneByTransitionSizePlusOne = 1.f / (kTransitionSize + 1); + + RTC_DCHECK_EQ(from.size(), to.size()); + RTC_DCHECK_EQ(from.size(), out.size()); + RTC_DCHECK_LE(kTransitionSize, out.size()); + + for (size_t k = 0; k < kTransitionSize; ++k) { + float a = (k + 1) * kOneByTransitionSizePlusOne; + out[k] = a * to[k] + (1.f - a) * from[k]; + } + + std::copy(to.begin() + kTransitionSize, to.end(), + out.begin() + kTransitionSize); + } +} + +// Computes a windowed (square root Hanning) padded FFT and updates the related +// memory. +void WindowedPaddedFft(const Aec3Fft& fft, + rtc::ArrayView v, + rtc::ArrayView v_old, + FftData* V) { + fft.PaddedFft(v, v_old, Aec3Fft::Window::kSqrtHanning, V); + std::copy(v.begin(), v.end(), v_old.begin()); +} + +// Class for removing the echo from the capture signal. +class EchoRemoverImpl final : public EchoRemover { + public: + EchoRemoverImpl(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels); + ~EchoRemoverImpl() override; + EchoRemoverImpl(const EchoRemoverImpl&) = delete; + EchoRemoverImpl& operator=(const EchoRemoverImpl&) = delete; + + void GetMetrics(EchoControl::Metrics* metrics) const override; + + // Removes the echo from a block of samples from the capture signal. The + // supplied render signal is assumed to be pre-aligned with the capture + // signal. + void ProcessCapture( + EchoPathVariability echo_path_variability, + bool capture_signal_saturation, + const absl::optional& external_delay, + RenderBuffer* render_buffer, + std::vector>>* linear_output, + std::vector>>* capture) override; + + // Updates the status on whether echo leakage is detected in the output of the + // echo remover. + void UpdateEchoLeakageStatus(bool leakage_detected) override { + echo_leakage_detected_ = leakage_detected; + } + + private: + // Selects which of the coarse and refined linear filter outputs that is most + // appropriate to pass to the suppressor and forms the linear filter output by + // smoothly transition between those. + void FormLinearFilterOutput(const SubtractorOutput& subtractor_output, + rtc::ArrayView output); + + static int instance_count_; + const EchoCanceller3Config config_; + const Aec3Fft fft_; + std::unique_ptr data_dumper_; + const Aec3Optimization optimization_; + const int sample_rate_hz_; + const size_t num_render_channels_; + const size_t num_capture_channels_; + const bool use_coarse_filter_output_; + Subtractor subtractor_; + SuppressionGain suppression_gain_; + ComfortNoiseGenerator cng_; + SuppressionFilter suppression_filter_; + RenderSignalAnalyzer render_signal_analyzer_; + ResidualEchoEstimator residual_echo_estimator_; + bool echo_leakage_detected_ = false; + AecState aec_state_; + EchoRemoverMetrics metrics_; + std::vector> e_old_; + std::vector> y_old_; + size_t block_counter_ = 0; + int gain_change_hangover_ = 0; + bool refined_filter_output_last_selected_ = true; + + std::vector> e_heap_; + std::vector> Y2_heap_; + std::vector> E2_heap_; + std::vector> R2_heap_; + std::vector> S2_linear_heap_; + std::vector Y_heap_; + std::vector E_heap_; + std::vector comfort_noise_heap_; + std::vector high_band_comfort_noise_heap_; + std::vector subtractor_output_heap_; +}; + +int EchoRemoverImpl::instance_count_ = 0; + +EchoRemoverImpl::EchoRemoverImpl(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels) + : config_(config), + fft_(), + data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + optimization_(DetectOptimization()), + sample_rate_hz_(sample_rate_hz), + num_render_channels_(num_render_channels), + num_capture_channels_(num_capture_channels), + use_coarse_filter_output_( + config_.filter.enable_coarse_filter_output_usage), + subtractor_(config, + num_render_channels_, + num_capture_channels_, + data_dumper_.get(), + optimization_), + suppression_gain_(config_, + optimization_, + sample_rate_hz, + num_capture_channels), + cng_(config_, optimization_, num_capture_channels_), + suppression_filter_(optimization_, + sample_rate_hz_, + num_capture_channels_), + render_signal_analyzer_(config_), + residual_echo_estimator_(config_, num_render_channels), + aec_state_(config_, num_capture_channels_), + e_old_(num_capture_channels_, {0.f}), + y_old_(num_capture_channels_, {0.f}), + e_heap_(NumChannelsOnHeap(num_capture_channels_), {0.f}), + Y2_heap_(NumChannelsOnHeap(num_capture_channels_)), + E2_heap_(NumChannelsOnHeap(num_capture_channels_)), + R2_heap_(NumChannelsOnHeap(num_capture_channels_)), + S2_linear_heap_(NumChannelsOnHeap(num_capture_channels_)), + Y_heap_(NumChannelsOnHeap(num_capture_channels_)), + E_heap_(NumChannelsOnHeap(num_capture_channels_)), + comfort_noise_heap_(NumChannelsOnHeap(num_capture_channels_)), + high_band_comfort_noise_heap_(NumChannelsOnHeap(num_capture_channels_)), + subtractor_output_heap_(NumChannelsOnHeap(num_capture_channels_)) { + RTC_DCHECK(ValidFullBandRate(sample_rate_hz)); +} + +EchoRemoverImpl::~EchoRemoverImpl() = default; + +void EchoRemoverImpl::GetMetrics(EchoControl::Metrics* metrics) const { + // Echo return loss (ERL) is inverted to go from gain to attenuation. + metrics->echo_return_loss = -10.0 * std::log10(aec_state_.ErlTimeDomain()); + metrics->echo_return_loss_enhancement = + Log2TodB(aec_state_.FullBandErleLog2()); +} + +void EchoRemoverImpl::ProcessCapture( + EchoPathVariability echo_path_variability, + bool capture_signal_saturation, + const absl::optional& external_delay, + RenderBuffer* render_buffer, + std::vector>>* linear_output, + std::vector>>* capture) { + ++block_counter_; + const std::vector>>& x = + render_buffer->Block(0); + std::vector>>* y = capture; + RTC_DCHECK(render_buffer); + RTC_DCHECK(y); + RTC_DCHECK_EQ(x.size(), NumBandsForRate(sample_rate_hz_)); + RTC_DCHECK_EQ(y->size(), NumBandsForRate(sample_rate_hz_)); + RTC_DCHECK_EQ(x[0].size(), num_render_channels_); + RTC_DCHECK_EQ((*y)[0].size(), num_capture_channels_); + RTC_DCHECK_EQ(x[0][0].size(), kBlockSize); + RTC_DCHECK_EQ((*y)[0][0].size(), kBlockSize); + + // Stack allocated data to use when the number of channels is low. + std::array, kMaxNumChannelsOnStack> e_stack; + std::array, kMaxNumChannelsOnStack> + Y2_stack; + std::array, kMaxNumChannelsOnStack> + E2_stack; + std::array, kMaxNumChannelsOnStack> + R2_stack; + std::array, kMaxNumChannelsOnStack> + S2_linear_stack; + std::array Y_stack; + std::array E_stack; + std::array comfort_noise_stack; + std::array high_band_comfort_noise_stack; + std::array subtractor_output_stack; + + rtc::ArrayView> e(e_stack.data(), + num_capture_channels_); + rtc::ArrayView> Y2( + Y2_stack.data(), num_capture_channels_); + rtc::ArrayView> E2( + E2_stack.data(), num_capture_channels_); + rtc::ArrayView> R2( + R2_stack.data(), num_capture_channels_); + rtc::ArrayView> S2_linear( + S2_linear_stack.data(), num_capture_channels_); + rtc::ArrayView Y(Y_stack.data(), num_capture_channels_); + rtc::ArrayView E(E_stack.data(), num_capture_channels_); + rtc::ArrayView comfort_noise(comfort_noise_stack.data(), + num_capture_channels_); + rtc::ArrayView high_band_comfort_noise( + high_band_comfort_noise_stack.data(), num_capture_channels_); + rtc::ArrayView subtractor_output( + subtractor_output_stack.data(), num_capture_channels_); + if (NumChannelsOnHeap(num_capture_channels_) > 0) { + // If the stack-allocated space is too small, use the heap for storing the + // microphone data. + e = rtc::ArrayView>(e_heap_.data(), + num_capture_channels_); + Y2 = rtc::ArrayView>( + Y2_heap_.data(), num_capture_channels_); + E2 = rtc::ArrayView>( + E2_heap_.data(), num_capture_channels_); + R2 = rtc::ArrayView>( + R2_heap_.data(), num_capture_channels_); + S2_linear = rtc::ArrayView>( + S2_linear_heap_.data(), num_capture_channels_); + Y = rtc::ArrayView(Y_heap_.data(), num_capture_channels_); + E = rtc::ArrayView(E_heap_.data(), num_capture_channels_); + comfort_noise = rtc::ArrayView(comfort_noise_heap_.data(), + num_capture_channels_); + high_band_comfort_noise = rtc::ArrayView( + high_band_comfort_noise_heap_.data(), num_capture_channels_); + subtractor_output = rtc::ArrayView( + subtractor_output_heap_.data(), num_capture_channels_); + } + + data_dumper_->DumpWav("aec3_echo_remover_capture_input", kBlockSize, + &(*y)[0][0][0], 16000, 1); + data_dumper_->DumpWav("aec3_echo_remover_render_input", kBlockSize, + &x[0][0][0], 16000, 1); + data_dumper_->DumpRaw("aec3_echo_remover_capture_input", (*y)[0][0]); + data_dumper_->DumpRaw("aec3_echo_remover_render_input", x[0][0]); + + aec_state_.UpdateCaptureSaturation(capture_signal_saturation); + + if (echo_path_variability.AudioPathChanged()) { + // Ensure that the gain change is only acted on once per frame. + if (echo_path_variability.gain_change) { + if (gain_change_hangover_ == 0) { + constexpr int kMaxBlocksPerFrame = 3; + gain_change_hangover_ = kMaxBlocksPerFrame; + rtc::LoggingSeverity log_level = + config_.delay.log_warning_on_delay_changes ? rtc::LS_WARNING + : rtc::LS_VERBOSE; + RTC_LOG_V(log_level) + << "Gain change detected at block " << block_counter_; + } else { + echo_path_variability.gain_change = false; + } + } + + subtractor_.HandleEchoPathChange(echo_path_variability); + aec_state_.HandleEchoPathChange(echo_path_variability); + + if (echo_path_variability.delay_change != + EchoPathVariability::DelayAdjustment::kNone) { + suppression_gain_.SetInitialState(true); + } + } + if (gain_change_hangover_ > 0) { + --gain_change_hangover_; + } + + // Analyze the render signal. + render_signal_analyzer_.Update(*render_buffer, + aec_state_.MinDirectPathFilterDelay()); + + // State transition. + if (aec_state_.TransitionTriggered()) { + subtractor_.ExitInitialState(); + suppression_gain_.SetInitialState(false); + } + + // Perform linear echo cancellation. + subtractor_.Process(*render_buffer, (*y)[0], render_signal_analyzer_, + aec_state_, subtractor_output); + + // Compute spectra. + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + FormLinearFilterOutput(subtractor_output[ch], e[ch]); + WindowedPaddedFft(fft_, (*y)[0][ch], y_old_[ch], &Y[ch]); + WindowedPaddedFft(fft_, e[ch], e_old_[ch], &E[ch]); + LinearEchoPower(E[ch], Y[ch], &S2_linear[ch]); + Y[ch].Spectrum(optimization_, Y2[ch]); + E[ch].Spectrum(optimization_, E2[ch]); + } + + // Optionally return the linear filter output. + if (linear_output) { + RTC_DCHECK_GE(1, linear_output->size()); + RTC_DCHECK_EQ(num_capture_channels_, linear_output[0].size()); + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + RTC_DCHECK_EQ(kBlockSize, (*linear_output)[0][ch].size()); + std::copy(e[ch].begin(), e[ch].end(), (*linear_output)[0][ch].begin()); + } + } + + // Update the AEC state information. + aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponses(), + subtractor_.FilterImpulseResponses(), *render_buffer, E2, + Y2, subtractor_output); + + // Choose the linear output. + const auto& Y_fft = aec_state_.UseLinearFilterOutput() ? E : Y; + + data_dumper_->DumpWav("aec3_output_linear", kBlockSize, &(*y)[0][0][0], 16000, + 1); + data_dumper_->DumpWav("aec3_output_linear2", kBlockSize, &e[0][0], 16000, 1); + + // Estimate the residual echo power. + residual_echo_estimator_.Estimate(aec_state_, *render_buffer, S2_linear, Y2, + R2); + + // Estimate the comfort noise. + cng_.Compute(aec_state_.SaturatedCapture(), Y2, comfort_noise, + high_band_comfort_noise); + + // Suppressor nearend estimate. + if (aec_state_.UsableLinearEstimate()) { + // E2 is bound by Y2. + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + std::transform(E2[ch].begin(), E2[ch].end(), Y2[ch].begin(), + E2[ch].begin(), + [](float a, float b) { return std::min(a, b); }); + } + } + const auto& nearend_spectrum = aec_state_.UsableLinearEstimate() ? E2 : Y2; + + // Suppressor echo estimate. + const auto& echo_spectrum = + aec_state_.UsableLinearEstimate() ? S2_linear : R2; + + // Compute preferred gains. + float high_bands_gain; + std::array G; + suppression_gain_.GetGain(nearend_spectrum, echo_spectrum, R2, + cng_.NoiseSpectrum(), render_signal_analyzer_, + aec_state_, x, &high_bands_gain, &G); + + suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G, + high_bands_gain, Y_fft, y); + + // Update the metrics. + metrics_.Update(aec_state_, cng_.NoiseSpectrum()[0], G); + + // Debug outputs for the purpose of development and analysis. + data_dumper_->DumpWav("aec3_echo_estimate", kBlockSize, + &subtractor_output[0].s_refined[0], 16000, 1); + data_dumper_->DumpRaw("aec3_output", (*y)[0][0]); + data_dumper_->DumpRaw("aec3_narrow_render", + render_signal_analyzer_.NarrowPeakBand() ? 1 : 0); + data_dumper_->DumpRaw("aec3_N2", cng_.NoiseSpectrum()[0]); + data_dumper_->DumpRaw("aec3_suppressor_gain", G); + data_dumper_->DumpWav("aec3_output", + rtc::ArrayView(&(*y)[0][0][0], kBlockSize), + 16000, 1); + data_dumper_->DumpRaw("aec3_using_subtractor_output[0]", + aec_state_.UseLinearFilterOutput() ? 1 : 0); + data_dumper_->DumpRaw("aec3_E2", E2[0]); + data_dumper_->DumpRaw("aec3_S2_linear", S2_linear[0]); + data_dumper_->DumpRaw("aec3_Y2", Y2[0]); + data_dumper_->DumpRaw( + "aec3_X2", render_buffer->Spectrum( + aec_state_.MinDirectPathFilterDelay())[/*channel=*/0]); + data_dumper_->DumpRaw("aec3_R2", R2[0]); + data_dumper_->DumpRaw("aec3_filter_delay", + aec_state_.MinDirectPathFilterDelay()); + data_dumper_->DumpRaw("aec3_capture_saturation", + aec_state_.SaturatedCapture() ? 1 : 0); +} + +void EchoRemoverImpl::FormLinearFilterOutput( + const SubtractorOutput& subtractor_output, + rtc::ArrayView output) { + RTC_DCHECK_EQ(subtractor_output.e_refined.size(), output.size()); + RTC_DCHECK_EQ(subtractor_output.e_coarse.size(), output.size()); + bool use_refined_output = true; + if (use_coarse_filter_output_) { + // As the output of the refined adaptive filter generally should be better + // than the coarse filter output, add a margin and threshold for when + // choosing the coarse filter output. + if (subtractor_output.e2_coarse < 0.9f * subtractor_output.e2_refined && + subtractor_output.y2 > 30.f * 30.f * kBlockSize && + (subtractor_output.s2_refined > 60.f * 60.f * kBlockSize || + subtractor_output.s2_coarse > 60.f * 60.f * kBlockSize)) { + use_refined_output = false; + } else { + // If the refined filter is diverged, choose the filter output that has + // the lowest power. + if (subtractor_output.e2_coarse < subtractor_output.e2_refined && + subtractor_output.y2 < subtractor_output.e2_refined) { + use_refined_output = false; + } + } + } + + SignalTransition(refined_filter_output_last_selected_ + ? subtractor_output.e_refined + : subtractor_output.e_coarse, + use_refined_output ? subtractor_output.e_refined + : subtractor_output.e_coarse, + output); + refined_filter_output_last_selected_ = use_refined_output; +} + +} // namespace + +EchoRemover* EchoRemover::Create(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels) { + return new EchoRemoverImpl(config, sample_rate_hz, num_render_channels, + num_capture_channels); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/echo_remover.h b/webrtc/modules/audio_processing/aec3/echo_remover.h new file mode 100644 index 0000000..ef41646 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_remover.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_H_ + +#include + +#include "absl/types/optional.h" +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/render_buffer.h" + +namespace webrtc { + +// Class for removing the echo from the capture signal. +class EchoRemover { + public: + static EchoRemover* Create(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels); + virtual ~EchoRemover() = default; + + // Get current metrics. + virtual void GetMetrics(EchoControl::Metrics* metrics) const = 0; + + // Removes the echo from a block of samples from the capture signal. The + // supplied render signal is assumed to be pre-aligned with the capture + // signal. + virtual void ProcessCapture( + EchoPathVariability echo_path_variability, + bool capture_signal_saturation, + const absl::optional& external_delay, + RenderBuffer* render_buffer, + std::vector>>* linear_output, + std::vector>>* capture) = 0; + + // Updates the status on whether echo leakage is detected in the output of the + // echo remover. + virtual void UpdateEchoLeakageStatus(bool leakage_detected) = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_H_ diff --git a/webrtc/modules/audio_processing/aec3/echo_remover_metrics.cc b/webrtc/modules/audio_processing/aec3/echo_remover_metrics.cc new file mode 100644 index 0000000..4502f31 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_remover_metrics.cc @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/echo_remover_metrics.h" + +#include +#include + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +namespace { + +constexpr float kOneByMetricsCollectionBlocks = 1.f / kMetricsCollectionBlocks; + +} // namespace + +EchoRemoverMetrics::DbMetric::DbMetric() : DbMetric(0.f, 0.f, 0.f) {} +EchoRemoverMetrics::DbMetric::DbMetric(float sum_value, + float floor_value, + float ceil_value) + : sum_value(sum_value), floor_value(floor_value), ceil_value(ceil_value) {} + +void EchoRemoverMetrics::DbMetric::Update(float value) { + sum_value += value; + floor_value = std::min(floor_value, value); + ceil_value = std::max(ceil_value, value); +} + +void EchoRemoverMetrics::DbMetric::UpdateInstant(float value) { + sum_value = value; + floor_value = std::min(floor_value, value); + ceil_value = std::max(ceil_value, value); +} + +EchoRemoverMetrics::EchoRemoverMetrics() { + ResetMetrics(); +} + +void EchoRemoverMetrics::ResetMetrics() { + erl_.fill(DbMetric(0.f, 10000.f, 0.000f)); + erl_time_domain_ = DbMetric(0.f, 10000.f, 0.000f); + erle_.fill(DbMetric(0.f, 0.f, 1000.f)); + erle_time_domain_ = DbMetric(0.f, 0.f, 1000.f); + active_render_count_ = 0; + saturated_capture_ = false; +} + +void EchoRemoverMetrics::Update( + const AecState& aec_state, + const std::array& comfort_noise_spectrum, + const std::array& suppressor_gain) { + metrics_reported_ = false; + if (++block_counter_ <= kMetricsCollectionBlocks) { + aec3::UpdateDbMetric(aec_state.Erl(), &erl_); + erl_time_domain_.UpdateInstant(aec_state.ErlTimeDomain()); + aec3::UpdateDbMetric(aec_state.Erle()[0], &erle_); + erle_time_domain_.UpdateInstant(aec_state.FullBandErleLog2()); + active_render_count_ += (aec_state.ActiveRender() ? 1 : 0); + saturated_capture_ = saturated_capture_ || aec_state.SaturatedCapture(); + } else { + // Report the metrics over several frames in order to lower the impact of + // the logarithms involved on the computational complexity. + constexpr int kMetricsCollectionBlocksBy2 = kMetricsCollectionBlocks / 2; + switch (block_counter_) { + case kMetricsCollectionBlocks + 1: + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErleBand0.Average", + aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f, + kOneByMetricsCollectionBlocks, + erle_[0].sum_value), + 0, 19, 20); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErleBand0.Max", + aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f, 1.f, + erle_[0].ceil_value), + 0, 19, 20); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErleBand0.Min", + aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f, 1.f, + erle_[0].floor_value), + 0, 19, 20); + break; + case kMetricsCollectionBlocks + 2: + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErleBand1.Average", + aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f, + kOneByMetricsCollectionBlocks, + erle_[1].sum_value), + 0, 19, 20); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErleBand1.Max", + aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f, 1.f, + erle_[1].ceil_value), + 0, 19, 20); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErleBand1.Min", + aec3::TransformDbMetricForReporting(true, 0.f, 19.f, 0.f, 1.f, + erle_[1].floor_value), + 0, 19, 20); + break; + case kMetricsCollectionBlocks + 3: + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErlBand0.Average", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, + kOneByMetricsCollectionBlocks, + erl_[0].sum_value), + 0, 59, 30); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErlBand0.Max", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_[0].ceil_value), + 0, 59, 30); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErlBand0.Min", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_[0].floor_value), + 0, 59, 30); + break; + case kMetricsCollectionBlocks + 4: + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErlBand1.Average", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, + kOneByMetricsCollectionBlocks, + erl_[1].sum_value), + 0, 59, 30); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErlBand1.Max", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_[1].ceil_value), + 0, 59, 30); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.ErlBand1.Min", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_[1].floor_value), + 0, 59, 30); + break; + case kMetricsCollectionBlocks + 5: + RTC_HISTOGRAM_BOOLEAN( + "WebRTC.Audio.EchoCanceller.UsableLinearEstimate", + static_cast(aec_state.UsableLinearEstimate() ? 1 : 0)); + RTC_HISTOGRAM_BOOLEAN( + "WebRTC.Audio.EchoCanceller.ActiveRender", + static_cast( + active_render_count_ > kMetricsCollectionBlocksBy2 ? 1 : 0)); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.EchoCanceller.FilterDelay", + aec_state.MinDirectPathFilterDelay(), 0, 30, + 31); + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.EchoCanceller.CaptureSaturation", + static_cast(saturated_capture_ ? 1 : 0)); + break; + case kMetricsCollectionBlocks + 6: + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erl.Value", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_time_domain_.sum_value), + 0, 59, 30); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erl.Max", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_time_domain_.ceil_value), + 0, 59, 30); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erl.Min", + aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_time_domain_.floor_value), + 0, 59, 30); + break; + case kMetricsCollectionBlocks + 7: + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erle.Value", + aec3::TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, + erle_time_domain_.sum_value), + 0, 19, 20); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erle.Max", + aec3::TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, + erle_time_domain_.ceil_value), + 0, 19, 20); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.EchoCanceller.Erle.Min", + aec3::TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, + erle_time_domain_.floor_value), + 0, 19, 20); + metrics_reported_ = true; + RTC_DCHECK_EQ(kMetricsReportingIntervalBlocks, block_counter_); + block_counter_ = 0; + ResetMetrics(); + break; + default: + RTC_NOTREACHED(); + break; + } + } +} + +namespace aec3 { + +void UpdateDbMetric(const std::array& value, + std::array* statistic) { + RTC_DCHECK(statistic); + // Truncation is intended in the band width computation. + constexpr int kNumBands = 2; + constexpr int kBandWidth = 65 / kNumBands; + constexpr float kOneByBandWidth = 1.f / kBandWidth; + RTC_DCHECK_EQ(kNumBands, statistic->size()); + RTC_DCHECK_EQ(65, value.size()); + for (size_t k = 0; k < statistic->size(); ++k) { + float average_band = + std::accumulate(value.begin() + kBandWidth * k, + value.begin() + kBandWidth * (k + 1), 0.f) * + kOneByBandWidth; + (*statistic)[k].Update(average_band); + } +} + +int TransformDbMetricForReporting(bool negate, + float min_value, + float max_value, + float offset, + float scaling, + float value) { + float new_value = 10.f * std::log10(value * scaling + 1e-10f) + offset; + if (negate) { + new_value = -new_value; + } + return static_cast(rtc::SafeClamp(new_value, min_value, max_value)); +} + +} // namespace aec3 + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/echo_remover_metrics.h b/webrtc/modules/audio_processing/aec3/echo_remover_metrics.h new file mode 100644 index 0000000..77fd8cd --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/echo_remover_metrics.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_METRICS_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_METRICS_H_ + +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec_state.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +// Handles the reporting of metrics for the echo remover. +class EchoRemoverMetrics { + public: + struct DbMetric { + DbMetric(); + DbMetric(float sum_value, float floor_value, float ceil_value); + void Update(float value); + void UpdateInstant(float value); + float sum_value; + float floor_value; + float ceil_value; + }; + + EchoRemoverMetrics(); + + // Updates the metric with new data. + void Update( + const AecState& aec_state, + const std::array& comfort_noise_spectrum, + const std::array& suppressor_gain); + + // Returns true if the metrics have just been reported, otherwise false. + bool MetricsReported() { return metrics_reported_; } + + private: + // Resets the metrics. + void ResetMetrics(); + + int block_counter_ = 0; + std::array erl_; + DbMetric erl_time_domain_; + std::array erle_; + DbMetric erle_time_domain_; + int active_render_count_ = 0; + bool saturated_capture_ = false; + bool metrics_reported_ = false; + + RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverMetrics); +}; + +namespace aec3 { + +// Updates a banded metric of type DbMetric with the values in the supplied +// array. +void UpdateDbMetric(const std::array& value, + std::array* statistic); + +// Transforms a DbMetric from the linear domain into the logarithmic domain. +int TransformDbMetricForReporting(bool negate, + float min_value, + float max_value, + float offset, + float scaling, + float value); + +} // namespace aec3 + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_METRICS_H_ diff --git a/webrtc/modules/audio_processing/aec3/erl_estimator.cc b/webrtc/modules/audio_processing/aec3/erl_estimator.cc new file mode 100644 index 0000000..01cc33c --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/erl_estimator.cc @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/erl_estimator.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +constexpr float kMinErl = 0.01f; +constexpr float kMaxErl = 1000.f; + +} // namespace + +ErlEstimator::ErlEstimator(size_t startup_phase_length_blocks_) + : startup_phase_length_blocks__(startup_phase_length_blocks_) { + erl_.fill(kMaxErl); + hold_counters_.fill(0); + erl_time_domain_ = kMaxErl; + hold_counter_time_domain_ = 0; +} + +ErlEstimator::~ErlEstimator() = default; + +void ErlEstimator::Reset() { + blocks_since_reset_ = 0; +} + +void ErlEstimator::Update( + const std::vector& converged_filters, + rtc::ArrayView> render_spectra, + rtc::ArrayView> + capture_spectra) { + const size_t num_capture_channels = converged_filters.size(); + RTC_DCHECK_EQ(capture_spectra.size(), num_capture_channels); + + // Corresponds to WGN of power -46 dBFS. + constexpr float kX2Min = 44015068.0f; + + const auto first_converged_iter = + std::find(converged_filters.begin(), converged_filters.end(), true); + const bool any_filter_converged = + first_converged_iter != converged_filters.end(); + + if (++blocks_since_reset_ < startup_phase_length_blocks__ || + !any_filter_converged) { + return; + } + + // Use the maximum spectrum across capture and the maximum across render. + std::array max_capture_spectrum_data; + std::array max_capture_spectrum = + capture_spectra[/*channel=*/0]; + if (num_capture_channels > 1) { + // Initialize using the first channel with a converged filter. + const size_t first_converged = + std::distance(converged_filters.begin(), first_converged_iter); + RTC_DCHECK_GE(first_converged, 0); + RTC_DCHECK_LT(first_converged, num_capture_channels); + max_capture_spectrum_data = capture_spectra[first_converged]; + + for (size_t ch = first_converged + 1; ch < num_capture_channels; ++ch) { + if (!converged_filters[ch]) { + continue; + } + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + max_capture_spectrum_data[k] = + std::max(max_capture_spectrum_data[k], capture_spectra[ch][k]); + } + } + max_capture_spectrum = max_capture_spectrum_data; + } + + const size_t num_render_channels = render_spectra.size(); + std::array max_render_spectrum_data; + rtc::ArrayView max_render_spectrum = + render_spectra[/*channel=*/0]; + if (num_render_channels > 1) { + std::copy(render_spectra[0].begin(), render_spectra[0].end(), + max_render_spectrum_data.begin()); + for (size_t ch = 1; ch < num_render_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + max_render_spectrum_data[k] = + std::max(max_render_spectrum_data[k], render_spectra[ch][k]); + } + } + max_render_spectrum = max_render_spectrum_data; + } + + const auto& X2 = max_render_spectrum; + const auto& Y2 = max_capture_spectrum; + + // Update the estimates in a maximum statistics manner. + for (size_t k = 1; k < kFftLengthBy2; ++k) { + if (X2[k] > kX2Min) { + const float new_erl = Y2[k] / X2[k]; + if (new_erl < erl_[k]) { + hold_counters_[k - 1] = 1000; + erl_[k] += 0.1f * (new_erl - erl_[k]); + erl_[k] = std::max(erl_[k], kMinErl); + } + } + } + + std::for_each(hold_counters_.begin(), hold_counters_.end(), + [](int& a) { --a; }); + std::transform(hold_counters_.begin(), hold_counters_.end(), erl_.begin() + 1, + erl_.begin() + 1, [](int a, float b) { + return a > 0 ? b : std::min(kMaxErl, 2.f * b); + }); + + erl_[0] = erl_[1]; + erl_[kFftLengthBy2] = erl_[kFftLengthBy2 - 1]; + + // Compute ERL over all frequency bins. + const float X2_sum = std::accumulate(X2.begin(), X2.end(), 0.0f); + + if (X2_sum > kX2Min * X2.size()) { + const float Y2_sum = std::accumulate(Y2.begin(), Y2.end(), 0.0f); + const float new_erl = Y2_sum / X2_sum; + if (new_erl < erl_time_domain_) { + hold_counter_time_domain_ = 1000; + erl_time_domain_ += 0.1f * (new_erl - erl_time_domain_); + erl_time_domain_ = std::max(erl_time_domain_, kMinErl); + } + } + + --hold_counter_time_domain_; + erl_time_domain_ = (hold_counter_time_domain_ > 0) + ? erl_time_domain_ + : std::min(kMaxErl, 2.f * erl_time_domain_); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/erl_estimator.h b/webrtc/modules/audio_processing/aec3/erl_estimator.h new file mode 100644 index 0000000..89bf6ac --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/erl_estimator.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ERL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ERL_ESTIMATOR_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +// Estimates the echo return loss based on the signal spectra. +class ErlEstimator { + public: + explicit ErlEstimator(size_t startup_phase_length_blocks_); + ~ErlEstimator(); + + // Resets the ERL estimation. + void Reset(); + + // Updates the ERL estimate. + void Update(const std::vector& converged_filters, + rtc::ArrayView> + render_spectra, + rtc::ArrayView> + capture_spectra); + + // Returns the most recent ERL estimate. + const std::array& Erl() const { return erl_; } + float ErlTimeDomain() const { return erl_time_domain_; } + + private: + const size_t startup_phase_length_blocks__; + std::array erl_; + std::array hold_counters_; + float erl_time_domain_; + int hold_counter_time_domain_; + size_t blocks_since_reset_ = 0; + RTC_DISALLOW_COPY_AND_ASSIGN(ErlEstimator); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ERL_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/erle_estimator.cc b/webrtc/modules/audio_processing/aec3/erle_estimator.cc new file mode 100644 index 0000000..4d84345 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/erle_estimator.cc @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/erle_estimator.h" + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +ErleEstimator::ErleEstimator(size_t startup_phase_length_blocks, + const EchoCanceller3Config& config, + size_t num_capture_channels) + : startup_phase_length_blocks_(startup_phase_length_blocks), + fullband_erle_estimator_(config.erle, num_capture_channels), + subband_erle_estimator_(config, num_capture_channels) { + if (config.erle.num_sections > 1) { + signal_dependent_erle_estimator_ = + std::make_unique(config, + num_capture_channels); + } + Reset(true); +} + +ErleEstimator::~ErleEstimator() = default; + +void ErleEstimator::Reset(bool delay_change) { + fullband_erle_estimator_.Reset(); + subband_erle_estimator_.Reset(); + if (signal_dependent_erle_estimator_) { + signal_dependent_erle_estimator_->Reset(); + } + if (delay_change) { + blocks_since_reset_ = 0; + } +} + +void ErleEstimator::Update( + const RenderBuffer& render_buffer, + rtc::ArrayView>> + filter_frequency_responses, + rtc::ArrayView + avg_render_spectrum_with_reverb, + rtc::ArrayView> capture_spectra, + rtc::ArrayView> + subtractor_spectra, + const std::vector& converged_filters) { + RTC_DCHECK_EQ(subband_erle_estimator_.Erle().size(), capture_spectra.size()); + RTC_DCHECK_EQ(subband_erle_estimator_.Erle().size(), + subtractor_spectra.size()); + const auto& X2_reverb = avg_render_spectrum_with_reverb; + const auto& Y2 = capture_spectra; + const auto& E2 = subtractor_spectra; + + if (++blocks_since_reset_ < startup_phase_length_blocks_) { + return; + } + + subband_erle_estimator_.Update(X2_reverb, Y2, E2, converged_filters); + + if (signal_dependent_erle_estimator_) { + signal_dependent_erle_estimator_->Update( + render_buffer, filter_frequency_responses, X2_reverb, Y2, E2, + subband_erle_estimator_.Erle(), converged_filters); + } + + fullband_erle_estimator_.Update(X2_reverb, Y2, E2, converged_filters); +} + +void ErleEstimator::Dump( + const std::unique_ptr& data_dumper) const { + fullband_erle_estimator_.Dump(data_dumper); + subband_erle_estimator_.Dump(data_dumper); + if (signal_dependent_erle_estimator_) { + signal_dependent_erle_estimator_->Dump(data_dumper); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/erle_estimator.h b/webrtc/modules/audio_processing/aec3/erle_estimator.h new file mode 100644 index 0000000..d741cff --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/erle_estimator.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_ERLE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_ERLE_ESTIMATOR_H_ + +#include + +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/fullband_erle_estimator.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/signal_dependent_erle_estimator.h" +#include "modules/audio_processing/aec3/subband_erle_estimator.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +// Estimates the echo return loss enhancement. One estimate is done per subband +// and another one is done using the aggreation of energy over all the subbands. +class ErleEstimator { + public: + ErleEstimator(size_t startup_phase_length_blocks, + const EchoCanceller3Config& config, + size_t num_capture_channels); + ~ErleEstimator(); + + // Resets the fullband ERLE estimator and the subbands ERLE estimators. + void Reset(bool delay_change); + + // Updates the ERLE estimates. + void Update( + const RenderBuffer& render_buffer, + rtc::ArrayView>> + filter_frequency_responses, + rtc::ArrayView + avg_render_spectrum_with_reverb, + rtc::ArrayView> + capture_spectra, + rtc::ArrayView> + subtractor_spectra, + const std::vector& converged_filters); + + // Returns the most recent subband ERLE estimates. + rtc::ArrayView> Erle() const { + return signal_dependent_erle_estimator_ + ? signal_dependent_erle_estimator_->Erle() + : subband_erle_estimator_.Erle(); + } + + // Returns the subband ERLE that are estimated during onsets (only used for + // testing). + rtc::ArrayView> ErleOnsets() + const { + return subband_erle_estimator_.ErleOnsets(); + } + + // Returns the fullband ERLE estimate. + float FullbandErleLog2() const { + return fullband_erle_estimator_.FullbandErleLog2(); + } + + // Returns an estimation of the current linear filter quality based on the + // current and past fullband ERLE estimates. The returned value is a float + // vector with content between 0 and 1 where 1 indicates that, at this current + // time instant, the linear filter is reaching its maximum subtraction + // performance. + rtc::ArrayView> GetInstLinearQualityEstimates() + const { + return fullband_erle_estimator_.GetInstLinearQualityEstimates(); + } + + void Dump(const std::unique_ptr& data_dumper) const; + + private: + const size_t startup_phase_length_blocks_; + FullBandErleEstimator fullband_erle_estimator_; + SubbandErleEstimator subband_erle_estimator_; + std::unique_ptr + signal_dependent_erle_estimator_; + size_t blocks_since_reset_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_ERLE_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/fft_buffer.cc b/webrtc/modules/audio_processing/aec3/fft_buffer.cc new file mode 100644 index 0000000..1ce2d31 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/fft_buffer.cc @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/fft_buffer.h" + +namespace webrtc { + +FftBuffer::FftBuffer(size_t size, size_t num_channels) + : size(static_cast(size)), + buffer(size, std::vector(num_channels)) { + for (auto& block : buffer) { + for (auto& channel_fft_data : block) { + channel_fft_data.Clear(); + } + } +} + +FftBuffer::~FftBuffer() = default; + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/fft_buffer.h b/webrtc/modules/audio_processing/aec3/fft_buffer.h new file mode 100644 index 0000000..4187315 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/fft_buffer.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_FFT_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_FFT_BUFFER_H_ + +#include + +#include + +#include "modules/audio_processing/aec3/fft_data.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Struct for bundling a circular buffer of FftData objects together with the +// read and write indices. +struct FftBuffer { + FftBuffer(size_t size, size_t num_channels); + ~FftBuffer(); + + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; + } + + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index > 0 ? index - 1 : size - 1; + } + + int OffsetIndex(int index, int offset) const { + RTC_DCHECK_GE(buffer.size(), offset); + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return (size + index + offset) % size; + } + + void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } + void IncWriteIndex() { write = IncIndex(write); } + void DecWriteIndex() { write = DecIndex(write); } + void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); } + void IncReadIndex() { read = IncIndex(read); } + void DecReadIndex() { read = DecIndex(read); } + + const int size; + std::vector> buffer; + int write = 0; + int read = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_FFT_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/aec3/fft_data.h b/webrtc/modules/audio_processing/aec3/fft_data.h new file mode 100644 index 0000000..9c25e78 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/fft_data.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_FFT_DATA_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_FFT_DATA_H_ + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// Struct that holds imaginary data produced from 128 point real-valued FFTs. +struct FftData { + // Copies the data in src. + void Assign(const FftData& src) { + std::copy(src.re.begin(), src.re.end(), re.begin()); + std::copy(src.im.begin(), src.im.end(), im.begin()); + im[0] = im[kFftLengthBy2] = 0; + } + + // Clears all the imaginary. + void Clear() { + re.fill(0.f); + im.fill(0.f); + } + + // Computes the power spectrum of the data. + void SpectrumAVX2(rtc::ArrayView power_spectrum) const; + + // Computes the power spectrum of the data. + void Spectrum(Aec3Optimization optimization, + rtc::ArrayView power_spectrum) const { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, power_spectrum.size()); + switch (optimization) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: { + constexpr int kNumFourBinBands = kFftLengthBy2 / 4; + constexpr int kLimit = kNumFourBinBands * 4; + for (size_t k = 0; k < kLimit; k += 4) { + const __m128 r = _mm_loadu_ps(&re[k]); + const __m128 i = _mm_loadu_ps(&im[k]); + const __m128 ii = _mm_mul_ps(i, i); + const __m128 rr = _mm_mul_ps(r, r); + const __m128 rrii = _mm_add_ps(rr, ii); + _mm_storeu_ps(&power_spectrum[k], rrii); + } + power_spectrum[kFftLengthBy2] = re[kFftLengthBy2] * re[kFftLengthBy2] + + im[kFftLengthBy2] * im[kFftLengthBy2]; + } break; + case Aec3Optimization::kAvx2: + SpectrumAVX2(power_spectrum); + break; +#endif + default: + std::transform(re.begin(), re.end(), im.begin(), power_spectrum.begin(), + [](float a, float b) { return a * a + b * b; }); + } + } + + // Copy the data from an interleaved array. + void CopyFromPackedArray(const std::array& v) { + re[0] = v[0]; + re[kFftLengthBy2] = v[1]; + im[0] = im[kFftLengthBy2] = 0; + for (size_t k = 1, j = 2; k < kFftLengthBy2; ++k) { + re[k] = v[j++]; + im[k] = v[j++]; + } + } + + // Copies the data into an interleaved array. + void CopyToPackedArray(std::array* v) const { + RTC_DCHECK(v); + (*v)[0] = re[0]; + (*v)[1] = re[kFftLengthBy2]; + for (size_t k = 1, j = 2; k < kFftLengthBy2; ++k) { + (*v)[j++] = re[k]; + (*v)[j++] = im[k]; + } + } + + std::array re; + std::array im; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_FFT_DATA_H_ diff --git a/webrtc/modules/audio_processing/aec3/fft_data_avx2.cc b/webrtc/modules/audio_processing/aec3/fft_data_avx2.cc new file mode 100644 index 0000000..1fe4bd6 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/fft_data_avx2.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 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 "modules/audio_processing/aec3/fft_data.h" + +#include + +#include "api/array_view.h" + +namespace webrtc { + +// Computes the power spectrum of the data. +void FftData::SpectrumAVX2(rtc::ArrayView power_spectrum) const { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, power_spectrum.size()); + for (size_t k = 0; k < kFftLengthBy2; k += 8) { + __m256 r = _mm256_loadu_ps(&re[k]); + __m256 i = _mm256_loadu_ps(&im[k]); + __m256 ii = _mm256_mul_ps(i, i); + ii = _mm256_fmadd_ps(r, r, ii); + _mm256_storeu_ps(&power_spectrum[k], ii); + } + power_spectrum[kFftLengthBy2] = re[kFftLengthBy2] * re[kFftLengthBy2] + + im[kFftLengthBy2] * im[kFftLengthBy2]; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/filter_analyzer.cc b/webrtc/modules/audio_processing/aec3/filter_analyzer.cc new file mode 100644 index 0000000..be954d3 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/filter_analyzer.cc @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/filter_analyzer.h" + +#include + +#include +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +size_t FindPeakIndex(rtc::ArrayView filter_time_domain, + size_t peak_index_in, + size_t start_sample, + size_t end_sample) { + size_t peak_index_out = peak_index_in; + float max_h2 = + filter_time_domain[peak_index_out] * filter_time_domain[peak_index_out]; + for (size_t k = start_sample; k <= end_sample; ++k) { + float tmp = filter_time_domain[k] * filter_time_domain[k]; + if (tmp > max_h2) { + peak_index_out = k; + max_h2 = tmp; + } + } + + return peak_index_out; +} + +} // namespace + +int FilterAnalyzer::instance_count_ = 0; + +FilterAnalyzer::FilterAnalyzer(const EchoCanceller3Config& config, + size_t num_capture_channels) + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + bounded_erl_(config.ep_strength.bounded_erl), + default_gain_(config.ep_strength.default_gain), + h_highpass_(num_capture_channels, + std::vector( + GetTimeDomainLength(config.filter.refined.length_blocks), + 0.f)), + filter_analysis_states_(num_capture_channels, + FilterAnalysisState(config)), + filter_delays_blocks_(num_capture_channels, 0) { + Reset(); +} + +FilterAnalyzer::~FilterAnalyzer() = default; + +void FilterAnalyzer::Reset() { + blocks_since_reset_ = 0; + ResetRegion(); + for (auto& state : filter_analysis_states_) { + state.Reset(default_gain_); + } + std::fill(filter_delays_blocks_.begin(), filter_delays_blocks_.end(), 0); +} + +void FilterAnalyzer::Update( + rtc::ArrayView> filters_time_domain, + const RenderBuffer& render_buffer, + bool* any_filter_consistent, + float* max_echo_path_gain) { + RTC_DCHECK(any_filter_consistent); + RTC_DCHECK(max_echo_path_gain); + RTC_DCHECK_EQ(filters_time_domain.size(), filter_analysis_states_.size()); + RTC_DCHECK_EQ(filters_time_domain.size(), h_highpass_.size()); + + ++blocks_since_reset_; + SetRegionToAnalyze(filters_time_domain[0].size()); + AnalyzeRegion(filters_time_domain, render_buffer); + + // Aggregate the results for all capture channels. + auto& st_ch0 = filter_analysis_states_[0]; + *any_filter_consistent = st_ch0.consistent_estimate; + *max_echo_path_gain = st_ch0.gain; + min_filter_delay_blocks_ = filter_delays_blocks_[0]; + for (size_t ch = 1; ch < filters_time_domain.size(); ++ch) { + auto& st_ch = filter_analysis_states_[ch]; + *any_filter_consistent = + *any_filter_consistent || st_ch.consistent_estimate; + *max_echo_path_gain = std::max(*max_echo_path_gain, st_ch.gain); + min_filter_delay_blocks_ = + std::min(min_filter_delay_blocks_, filter_delays_blocks_[ch]); + } +} + +void FilterAnalyzer::AnalyzeRegion( + rtc::ArrayView> filters_time_domain, + const RenderBuffer& render_buffer) { + // Preprocess the filter to avoid issues with low-frequency components in the + // filter. + PreProcessFilters(filters_time_domain); + data_dumper_->DumpRaw("aec3_linear_filter_processed_td", h_highpass_[0]); + + constexpr float kOneByBlockSize = 1.f / kBlockSize; + for (size_t ch = 0; ch < filters_time_domain.size(); ++ch) { + RTC_DCHECK_LT(region_.start_sample_, filters_time_domain[ch].size()); + RTC_DCHECK_LT(region_.end_sample_, filters_time_domain[ch].size()); + + auto& st_ch = filter_analysis_states_[ch]; + RTC_DCHECK_EQ(h_highpass_[ch].size(), filters_time_domain[ch].size()); + RTC_DCHECK_GT(h_highpass_[ch].size(), 0); + st_ch.peak_index = std::min(st_ch.peak_index, h_highpass_[ch].size() - 1); + + st_ch.peak_index = + FindPeakIndex(h_highpass_[ch], st_ch.peak_index, region_.start_sample_, + region_.end_sample_); + filter_delays_blocks_[ch] = st_ch.peak_index >> kBlockSizeLog2; + UpdateFilterGain(h_highpass_[ch], &st_ch); + st_ch.filter_length_blocks = + filters_time_domain[ch].size() * kOneByBlockSize; + + st_ch.consistent_estimate = st_ch.consistent_filter_detector.Detect( + h_highpass_[ch], region_, + render_buffer.Block(-filter_delays_blocks_[ch])[0], st_ch.peak_index, + filter_delays_blocks_[ch]); + } +} + +void FilterAnalyzer::UpdateFilterGain( + rtc::ArrayView filter_time_domain, + FilterAnalysisState* st) { + bool sufficient_time_to_converge = + blocks_since_reset_ > 5 * kNumBlocksPerSecond; + + if (sufficient_time_to_converge && st->consistent_estimate) { + st->gain = fabsf(filter_time_domain[st->peak_index]); + } else { + // TODO(peah): Verify whether this check against a float is ok. + if (st->gain) { + st->gain = std::max(st->gain, fabsf(filter_time_domain[st->peak_index])); + } + } + + if (bounded_erl_ && st->gain) { + st->gain = std::max(st->gain, 0.01f); + } +} + +void FilterAnalyzer::PreProcessFilters( + rtc::ArrayView> filters_time_domain) { + for (size_t ch = 0; ch < filters_time_domain.size(); ++ch) { + RTC_DCHECK_LT(region_.start_sample_, filters_time_domain[ch].size()); + RTC_DCHECK_LT(region_.end_sample_, filters_time_domain[ch].size()); + + RTC_DCHECK_GE(h_highpass_[ch].capacity(), filters_time_domain[ch].size()); + h_highpass_[ch].resize(filters_time_domain[ch].size()); + // Minimum phase high-pass filter with cutoff frequency at about 600 Hz. + constexpr std::array h = { + {0.7929742f, -0.36072128f, -0.47047766f}}; + + std::fill(h_highpass_[ch].begin() + region_.start_sample_, + h_highpass_[ch].begin() + region_.end_sample_ + 1, 0.f); + for (size_t k = std::max(h.size() - 1, region_.start_sample_); + k <= region_.end_sample_; ++k) { + for (size_t j = 0; j < h.size(); ++j) { + h_highpass_[ch][k] += filters_time_domain[ch][k - j] * h[j]; + } + } + } +} + +void FilterAnalyzer::ResetRegion() { + region_.start_sample_ = 0; + region_.end_sample_ = 0; +} + +void FilterAnalyzer::SetRegionToAnalyze(size_t filter_size) { + constexpr size_t kNumberBlocksToUpdate = 1; + auto& r = region_; + r.start_sample_ = r.end_sample_ >= filter_size - 1 ? 0 : r.end_sample_ + 1; + r.end_sample_ = + std::min(r.start_sample_ + kNumberBlocksToUpdate * kBlockSize - 1, + filter_size - 1); + + // Check range. + RTC_DCHECK_LT(r.start_sample_, filter_size); + RTC_DCHECK_LT(r.end_sample_, filter_size); + RTC_DCHECK_LE(r.start_sample_, r.end_sample_); +} + +FilterAnalyzer::ConsistentFilterDetector::ConsistentFilterDetector( + const EchoCanceller3Config& config) + : active_render_threshold_(config.render_levels.active_render_limit * + config.render_levels.active_render_limit * + kFftLengthBy2) { + Reset(); +} + +void FilterAnalyzer::ConsistentFilterDetector::Reset() { + significant_peak_ = false; + filter_floor_accum_ = 0.f; + filter_secondary_peak_ = 0.f; + filter_floor_low_limit_ = 0; + filter_floor_high_limit_ = 0; + consistent_estimate_counter_ = 0; + consistent_delay_reference_ = -10; +} + +bool FilterAnalyzer::ConsistentFilterDetector::Detect( + rtc::ArrayView filter_to_analyze, + const FilterRegion& region, + rtc::ArrayView> x_block, + size_t peak_index, + int delay_blocks) { + if (region.start_sample_ == 0) { + filter_floor_accum_ = 0.f; + filter_secondary_peak_ = 0.f; + filter_floor_low_limit_ = peak_index < 64 ? 0 : peak_index - 64; + filter_floor_high_limit_ = + peak_index > filter_to_analyze.size() - 129 ? 0 : peak_index + 128; + } + + for (size_t k = region.start_sample_; + k < std::min(region.end_sample_ + 1, filter_floor_low_limit_); ++k) { + float abs_h = fabsf(filter_to_analyze[k]); + filter_floor_accum_ += abs_h; + filter_secondary_peak_ = std::max(filter_secondary_peak_, abs_h); + } + + for (size_t k = std::max(filter_floor_high_limit_, region.start_sample_); + k <= region.end_sample_; ++k) { + float abs_h = fabsf(filter_to_analyze[k]); + filter_floor_accum_ += abs_h; + filter_secondary_peak_ = std::max(filter_secondary_peak_, abs_h); + } + + if (region.end_sample_ == filter_to_analyze.size() - 1) { + float filter_floor = filter_floor_accum_ / + (filter_floor_low_limit_ + filter_to_analyze.size() - + filter_floor_high_limit_); + + float abs_peak = fabsf(filter_to_analyze[peak_index]); + significant_peak_ = abs_peak > 10.f * filter_floor && + abs_peak > 2.f * filter_secondary_peak_; + } + + if (significant_peak_) { + bool active_render_block = false; + for (auto& x_channel : x_block) { + const float x_energy = std::inner_product( + x_channel.begin(), x_channel.end(), x_channel.begin(), 0.f); + if (x_energy > active_render_threshold_) { + active_render_block = true; + break; + } + } + + if (consistent_delay_reference_ == delay_blocks) { + if (active_render_block) { + ++consistent_estimate_counter_; + } + } else { + consistent_estimate_counter_ = 0; + consistent_delay_reference_ = delay_blocks; + } + } + return consistent_estimate_counter_ > 1.5f * kNumBlocksPerSecond; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/filter_analyzer.h b/webrtc/modules/audio_processing/aec3/filter_analyzer.h new file mode 100644 index 0000000..b0b7070 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/filter_analyzer.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_FILTER_ANALYZER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_FILTER_ANALYZER_H_ + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +class ApmDataDumper; +class RenderBuffer; + +// Class for analyzing the properties of an adaptive filter. +class FilterAnalyzer { + public: + FilterAnalyzer(const EchoCanceller3Config& config, + size_t num_capture_channels); + ~FilterAnalyzer(); + + FilterAnalyzer(const FilterAnalyzer&) = delete; + FilterAnalyzer& operator=(const FilterAnalyzer&) = delete; + + // Resets the analysis. + void Reset(); + + // Updates the estimates with new input data. + void Update(rtc::ArrayView> filters_time_domain, + const RenderBuffer& render_buffer, + bool* any_filter_consistent, + float* max_echo_path_gain); + + // Returns the delay in blocks for each filter. + rtc::ArrayView FilterDelaysBlocks() const { + return filter_delays_blocks_; + } + + // Returns the minimum delay of all filters in terms of blocks. + int MinFilterDelayBlocks() const { return min_filter_delay_blocks_; } + + // Returns the number of blocks for the current used filter. + int FilterLengthBlocks() const { + return filter_analysis_states_[0].filter_length_blocks; + } + + // Returns the preprocessed filter. + rtc::ArrayView> GetAdjustedFilters() const { + return h_highpass_; + } + + // Public for testing purposes only. + void SetRegionToAnalyze(size_t filter_size); + + private: + struct FilterAnalysisState; + + void AnalyzeRegion( + rtc::ArrayView> filters_time_domain, + const RenderBuffer& render_buffer); + + void UpdateFilterGain(rtc::ArrayView filters_time_domain, + FilterAnalysisState* st); + void PreProcessFilters( + rtc::ArrayView> filters_time_domain); + + void ResetRegion(); + + struct FilterRegion { + size_t start_sample_; + size_t end_sample_; + }; + + // This class checks whether the shape of the impulse response has been + // consistent over time. + class ConsistentFilterDetector { + public: + explicit ConsistentFilterDetector(const EchoCanceller3Config& config); + void Reset(); + bool Detect(rtc::ArrayView filter_to_analyze, + const FilterRegion& region, + rtc::ArrayView> x_block, + size_t peak_index, + int delay_blocks); + + private: + bool significant_peak_; + float filter_floor_accum_; + float filter_secondary_peak_; + size_t filter_floor_low_limit_; + size_t filter_floor_high_limit_; + const float active_render_threshold_; + size_t consistent_estimate_counter_ = 0; + int consistent_delay_reference_ = -10; + }; + + struct FilterAnalysisState { + explicit FilterAnalysisState(const EchoCanceller3Config& config) + : filter_length_blocks(config.filter.refined_initial.length_blocks), + consistent_filter_detector(config) { + Reset(config.ep_strength.default_gain); + } + + void Reset(float default_gain) { + peak_index = 0; + gain = default_gain; + consistent_filter_detector.Reset(); + } + + float gain; + size_t peak_index; + int filter_length_blocks; + bool consistent_estimate = false; + ConsistentFilterDetector consistent_filter_detector; + }; + + static int instance_count_; + std::unique_ptr data_dumper_; + const bool bounded_erl_; + const float default_gain_; + std::vector> h_highpass_; + + size_t blocks_since_reset_ = 0; + FilterRegion region_; + + std::vector filter_analysis_states_; + std::vector filter_delays_blocks_; + + int min_filter_delay_blocks_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_FILTER_ANALYZER_H_ diff --git a/webrtc/modules/audio_processing/aec3/frame_blocker.cc b/webrtc/modules/audio_processing/aec3/frame_blocker.cc new file mode 100644 index 0000000..63aaf09 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/frame_blocker.cc @@ -0,0 +1,88 @@ +/* + * 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 "modules/audio_processing/aec3/frame_blocker.h" + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +FrameBlocker::FrameBlocker(size_t num_bands, size_t num_channels) + : num_bands_(num_bands), + num_channels_(num_channels), + buffer_(num_bands_, std::vector>(num_channels)) { + RTC_DCHECK_LT(0, num_bands); + RTC_DCHECK_LT(0, num_channels); + for (auto& band : buffer_) { + for (auto& channel : band) { + channel.reserve(kBlockSize); + RTC_DCHECK(channel.empty()); + } + } +} + +FrameBlocker::~FrameBlocker() = default; + +void FrameBlocker::InsertSubFrameAndExtractBlock( + const std::vector>>& sub_frame, + std::vector>>* block) { + RTC_DCHECK(block); + RTC_DCHECK_EQ(num_bands_, block->size()); + RTC_DCHECK_EQ(num_bands_, sub_frame.size()); + for (size_t band = 0; band < num_bands_; ++band) { + RTC_DCHECK_EQ(num_channels_, (*block)[band].size()); + RTC_DCHECK_EQ(num_channels_, sub_frame[band].size()); + for (size_t channel = 0; channel < num_channels_; ++channel) { + RTC_DCHECK_GE(kBlockSize - 16, buffer_[band][channel].size()); + RTC_DCHECK_EQ(kBlockSize, (*block)[band][channel].size()); + RTC_DCHECK_EQ(kSubFrameLength, sub_frame[band][channel].size()); + const int samples_to_block = kBlockSize - buffer_[band][channel].size(); + (*block)[band][channel].clear(); + (*block)[band][channel].insert((*block)[band][channel].begin(), + buffer_[band][channel].begin(), + buffer_[band][channel].end()); + (*block)[band][channel].insert( + (*block)[band][channel].begin() + buffer_[band][channel].size(), + sub_frame[band][channel].begin(), + sub_frame[band][channel].begin() + samples_to_block); + buffer_[band][channel].clear(); + buffer_[band][channel].insert( + buffer_[band][channel].begin(), + sub_frame[band][channel].begin() + samples_to_block, + sub_frame[band][channel].end()); + } + } +} + +bool FrameBlocker::IsBlockAvailable() const { + return kBlockSize == buffer_[0][0].size(); +} + +void FrameBlocker::ExtractBlock( + std::vector>>* block) { + RTC_DCHECK(block); + RTC_DCHECK_EQ(num_bands_, block->size()); + RTC_DCHECK(IsBlockAvailable()); + for (size_t band = 0; band < num_bands_; ++band) { + RTC_DCHECK_EQ(num_channels_, (*block)[band].size()); + for (size_t channel = 0; channel < num_channels_; ++channel) { + RTC_DCHECK_EQ(kBlockSize, buffer_[band][channel].size()); + RTC_DCHECK_EQ(kBlockSize, (*block)[band][channel].size()); + (*block)[band][channel].clear(); + (*block)[band][channel].insert((*block)[band][channel].begin(), + buffer_[band][channel].begin(), + buffer_[band][channel].end()); + buffer_[band][channel].clear(); + } + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/frame_blocker.h b/webrtc/modules/audio_processing/aec3/frame_blocker.h new file mode 100644 index 0000000..ebd6f77 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/frame_blocker.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_FRAME_BLOCKER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_FRAME_BLOCKER_H_ + +#include + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// Class for producing 64 sample multiband blocks from frames consisting of 2 +// subframes of 80 samples. +class FrameBlocker { + public: + FrameBlocker(size_t num_bands, size_t num_channels); + ~FrameBlocker(); + FrameBlocker(const FrameBlocker&) = delete; + FrameBlocker& operator=(const FrameBlocker&) = delete; + + // Inserts one 80 sample multiband subframe from the multiband frame and + // extracts one 64 sample multiband block. + void InsertSubFrameAndExtractBlock( + const std::vector>>& sub_frame, + std::vector>>* block); + // Reports whether a multiband block of 64 samples is available for + // extraction. + bool IsBlockAvailable() const; + // Extracts a multiband block of 64 samples. + void ExtractBlock(std::vector>>* block); + + private: + const size_t num_bands_; + const size_t num_channels_; + std::vector>> buffer_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_FRAME_BLOCKER_H_ diff --git a/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.cc b/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.cc new file mode 100644 index 0000000..e421214 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.cc @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/fullband_erle_estimator.h" + +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +namespace { +constexpr float kEpsilon = 1e-3f; +constexpr float kX2BandEnergyThreshold = 44015068.0f; +constexpr int kBlocksToHoldErle = 100; +constexpr int kPointsToAccumulate = 6; +} // namespace + +FullBandErleEstimator::FullBandErleEstimator( + const EchoCanceller3Config::Erle& config, + size_t num_capture_channels) + : min_erle_log2_(FastApproxLog2f(config.min + kEpsilon)), + max_erle_lf_log2(FastApproxLog2f(config.max_l + kEpsilon)), + hold_counters_time_domain_(num_capture_channels, 0), + erle_time_domain_log2_(num_capture_channels, min_erle_log2_), + instantaneous_erle_(num_capture_channels, ErleInstantaneous(config)), + linear_filters_qualities_(num_capture_channels) { + Reset(); +} + +FullBandErleEstimator::~FullBandErleEstimator() = default; + +void FullBandErleEstimator::Reset() { + for (auto& instantaneous_erle_ch : instantaneous_erle_) { + instantaneous_erle_ch.Reset(); + } + + UpdateQualityEstimates(); + std::fill(erle_time_domain_log2_.begin(), erle_time_domain_log2_.end(), + min_erle_log2_); + std::fill(hold_counters_time_domain_.begin(), + hold_counters_time_domain_.end(), 0); +} + +void FullBandErleEstimator::Update( + rtc::ArrayView X2, + rtc::ArrayView> Y2, + rtc::ArrayView> E2, + const std::vector& converged_filters) { + for (size_t ch = 0; ch < Y2.size(); ++ch) { + if (converged_filters[ch]) { + // Computes the fullband ERLE. + const float X2_sum = std::accumulate(X2.begin(), X2.end(), 0.0f); + if (X2_sum > kX2BandEnergyThreshold * X2.size()) { + const float Y2_sum = + std::accumulate(Y2[ch].begin(), Y2[ch].end(), 0.0f); + const float E2_sum = + std::accumulate(E2[ch].begin(), E2[ch].end(), 0.0f); + if (instantaneous_erle_[ch].Update(Y2_sum, E2_sum)) { + hold_counters_time_domain_[ch] = kBlocksToHoldErle; + erle_time_domain_log2_[ch] += + 0.1f * ((instantaneous_erle_[ch].GetInstErleLog2().value()) - + erle_time_domain_log2_[ch]); + erle_time_domain_log2_[ch] = rtc::SafeClamp( + erle_time_domain_log2_[ch], min_erle_log2_, max_erle_lf_log2); + } + } + } + --hold_counters_time_domain_[ch]; + if (hold_counters_time_domain_[ch] <= 0) { + erle_time_domain_log2_[ch] = + std::max(min_erle_log2_, erle_time_domain_log2_[ch] - 0.044f); + } + if (hold_counters_time_domain_[ch] == 0) { + instantaneous_erle_[ch].ResetAccumulators(); + } + } + + UpdateQualityEstimates(); +} + +void FullBandErleEstimator::Dump( + const std::unique_ptr& data_dumper) const { + data_dumper->DumpRaw("aec3_fullband_erle_log2", FullbandErleLog2()); + instantaneous_erle_[0].Dump(data_dumper); +} + +void FullBandErleEstimator::UpdateQualityEstimates() { + for (size_t ch = 0; ch < instantaneous_erle_.size(); ++ch) { + linear_filters_qualities_[ch] = + instantaneous_erle_[ch].GetQualityEstimate(); + } +} + +FullBandErleEstimator::ErleInstantaneous::ErleInstantaneous( + const EchoCanceller3Config::Erle& config) + : clamp_inst_quality_to_zero_(config.clamp_quality_estimate_to_zero), + clamp_inst_quality_to_one_(config.clamp_quality_estimate_to_one) { + Reset(); +} + +FullBandErleEstimator::ErleInstantaneous::~ErleInstantaneous() = default; + +bool FullBandErleEstimator::ErleInstantaneous::Update(const float Y2_sum, + const float E2_sum) { + bool update_estimates = false; + E2_acum_ += E2_sum; + Y2_acum_ += Y2_sum; + num_points_++; + if (num_points_ == kPointsToAccumulate) { + if (E2_acum_ > 0.f) { + update_estimates = true; + erle_log2_ = FastApproxLog2f(Y2_acum_ / E2_acum_ + kEpsilon); + } + num_points_ = 0; + E2_acum_ = 0.f; + Y2_acum_ = 0.f; + } + + if (update_estimates) { + UpdateMaxMin(); + UpdateQualityEstimate(); + } + return update_estimates; +} + +void FullBandErleEstimator::ErleInstantaneous::Reset() { + ResetAccumulators(); + max_erle_log2_ = -10.f; // -30 dB. + min_erle_log2_ = 33.f; // 100 dB. + inst_quality_estimate_ = 0.f; +} + +void FullBandErleEstimator::ErleInstantaneous::ResetAccumulators() { + erle_log2_ = absl::nullopt; + inst_quality_estimate_ = 0.f; + num_points_ = 0; + E2_acum_ = 0.f; + Y2_acum_ = 0.f; +} + +void FullBandErleEstimator::ErleInstantaneous::Dump( + const std::unique_ptr& data_dumper) const { + data_dumper->DumpRaw("aec3_fullband_erle_inst_log2", + erle_log2_ ? *erle_log2_ : -10.f); + data_dumper->DumpRaw( + "aec3_erle_instantaneous_quality", + GetQualityEstimate() ? GetQualityEstimate().value() : 0.f); + data_dumper->DumpRaw("aec3_fullband_erle_max_log2", max_erle_log2_); + data_dumper->DumpRaw("aec3_fullband_erle_min_log2", min_erle_log2_); +} + +void FullBandErleEstimator::ErleInstantaneous::UpdateMaxMin() { + RTC_DCHECK(erle_log2_); + if (erle_log2_.value() > max_erle_log2_) { + max_erle_log2_ = erle_log2_.value(); + } else { + max_erle_log2_ -= 0.0004; // Forget factor, approx 1dB every 3 sec. + } + + if (erle_log2_.value() < min_erle_log2_) { + min_erle_log2_ = erle_log2_.value(); + } else { + min_erle_log2_ += 0.0004; // Forget factor, approx 1dB every 3 sec. + } +} + +void FullBandErleEstimator::ErleInstantaneous::UpdateQualityEstimate() { + const float alpha = 0.07f; + float quality_estimate = 0.f; + RTC_DCHECK(erle_log2_); + // TODO(peah): Currently, the estimate can become be less than 0; this should + // be corrected. + if (max_erle_log2_ > min_erle_log2_) { + quality_estimate = (erle_log2_.value() - min_erle_log2_) / + (max_erle_log2_ - min_erle_log2_); + } + if (quality_estimate > inst_quality_estimate_) { + inst_quality_estimate_ = quality_estimate; + } else { + inst_quality_estimate_ += + alpha * (quality_estimate - inst_quality_estimate_); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.h b/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.h new file mode 100644 index 0000000..1580f1a --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/fullband_erle_estimator.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_FULLBAND_ERLE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_FULLBAND_ERLE_ESTIMATOR_H_ + +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +// Estimates the echo return loss enhancement using the energy of all the +// freuquency bands. +class FullBandErleEstimator { + public: + FullBandErleEstimator(const EchoCanceller3Config::Erle& config, + size_t num_capture_channels); + ~FullBandErleEstimator(); + // Resets the ERLE estimator. + void Reset(); + + // Updates the ERLE estimator. + void Update(rtc::ArrayView X2, + rtc::ArrayView> Y2, + rtc::ArrayView> E2, + const std::vector& converged_filters); + + // Returns the fullband ERLE estimates in log2 units. + float FullbandErleLog2() const { + float min_erle = erle_time_domain_log2_[0]; + for (size_t ch = 1; ch < erle_time_domain_log2_.size(); ++ch) { + min_erle = std::min(min_erle, erle_time_domain_log2_[ch]); + } + return min_erle; + } + + // Returns an estimation of the current linear filter quality. It returns a + // float number between 0 and 1 mapping 1 to the highest possible quality. + rtc::ArrayView> GetInstLinearQualityEstimates() + const { + return linear_filters_qualities_; + } + + void Dump(const std::unique_ptr& data_dumper) const; + + private: + void UpdateQualityEstimates(); + + class ErleInstantaneous { + public: + explicit ErleInstantaneous(const EchoCanceller3Config::Erle& config); + ~ErleInstantaneous(); + + // Updates the estimator with a new point, returns true + // if the instantaneous ERLE was updated due to having enough + // points for performing the estimate. + bool Update(const float Y2_sum, const float E2_sum); + // Resets the instantaneous ERLE estimator to its initial state. + void Reset(); + // Resets the members related with an instantaneous estimate. + void ResetAccumulators(); + // Returns the instantaneous ERLE in log2 units. + absl::optional GetInstErleLog2() const { return erle_log2_; } + // Gets an indication between 0 and 1 of the performance of the linear + // filter for the current time instant. + absl::optional GetQualityEstimate() const { + if (erle_log2_) { + float value = inst_quality_estimate_; + if (clamp_inst_quality_to_zero_) { + value = std::max(0.f, value); + } + if (clamp_inst_quality_to_one_) { + value = std::min(1.f, value); + } + return absl::optional(value); + } + return absl::nullopt; + } + void Dump(const std::unique_ptr& data_dumper) const; + + private: + void UpdateMaxMin(); + void UpdateQualityEstimate(); + const bool clamp_inst_quality_to_zero_; + const bool clamp_inst_quality_to_one_; + absl::optional erle_log2_; + float inst_quality_estimate_; + float max_erle_log2_; + float min_erle_log2_; + float Y2_acum_; + float E2_acum_; + int num_points_; + }; + + const float min_erle_log2_; + const float max_erle_lf_log2; + std::vector hold_counters_time_domain_; + std::vector erle_time_domain_log2_; + std::vector instantaneous_erle_; + std::vector> linear_filters_qualities_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_FULLBAND_ERLE_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/matched_filter.cc b/webrtc/modules/audio_processing/aec3/matched_filter.cc new file mode 100644 index 0000000..64b2d4e --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/matched_filter.cc @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/matched_filter.h" + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_HAS_NEON) +#include +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif +#include +#include +#include +#include +#include + +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace aec3 { + +#if defined(WEBRTC_HAS_NEON) + +void MatchedFilterCore_NEON(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView h, + bool* filters_updated, + float* error_sum) { + const int h_size = static_cast(h.size()); + const int x_size = static_cast(x.size()); + RTC_DCHECK_EQ(0, h_size % 4); + + // Process for all samples in the sub-block. + for (size_t i = 0; i < y.size(); ++i) { + // Apply the matched filter as filter * x, and compute x * x. + + RTC_DCHECK_GT(x_size, x_start_index); + const float* x_p = &x[x_start_index]; + const float* h_p = &h[0]; + + // Initialize values for the accumulation. + float32x4_t s_128 = vdupq_n_f32(0); + float32x4_t x2_sum_128 = vdupq_n_f32(0); + float x2_sum = 0.f; + float s = 0; + + // Compute loop chunk sizes until, and after, the wraparound of the circular + // buffer for x. + const int chunk1 = + std::min(h_size, static_cast(x_size - x_start_index)); + + // Perform the loop in two chunks. + const int chunk2 = h_size - chunk1; + for (int limit : {chunk1, chunk2}) { + // Perform 128 bit vector operations. + const int limit_by_4 = limit >> 2; + for (int k = limit_by_4; k > 0; --k, h_p += 4, x_p += 4) { + // Load the data into 128 bit vectors. + const float32x4_t x_k = vld1q_f32(x_p); + const float32x4_t h_k = vld1q_f32(h_p); + // Compute and accumulate x * x and h * x. + x2_sum_128 = vmlaq_f32(x2_sum_128, x_k, x_k); + s_128 = vmlaq_f32(s_128, h_k, x_k); + } + + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_4 * 4; k > 0; --k, ++h_p, ++x_p) { + const float x_k = *x_p; + x2_sum += x_k * x_k; + s += *h_p * x_k; + } + + x_p = &x[0]; + } + + // Combine the accumulated vector and scalar values. + float* v = reinterpret_cast(&x2_sum_128); + x2_sum += v[0] + v[1] + v[2] + v[3]; + v = reinterpret_cast(&s_128); + s += v[0] + v[1] + v[2] + v[3]; + + // Compute the matched filter error. + float e = y[i] - s; + const bool saturation = y[i] >= 32000.f || y[i] <= -32000.f; + (*error_sum) += e * e; + + // Update the matched filter estimate in an NLMS manner. + if (x2_sum > x2_sum_threshold && !saturation) { + RTC_DCHECK_LT(0.f, x2_sum); + const float alpha = smoothing * e / x2_sum; + const float32x4_t alpha_128 = vmovq_n_f32(alpha); + + // filter = filter + smoothing * (y - filter * x) * x / x * x. + float* h_p = &h[0]; + x_p = &x[x_start_index]; + + // Perform the loop in two chunks. + for (int limit : {chunk1, chunk2}) { + // Perform 128 bit vector operations. + const int limit_by_4 = limit >> 2; + for (int k = limit_by_4; k > 0; --k, h_p += 4, x_p += 4) { + // Load the data into 128 bit vectors. + float32x4_t h_k = vld1q_f32(h_p); + const float32x4_t x_k = vld1q_f32(x_p); + // Compute h = h + alpha * x. + h_k = vmlaq_f32(h_k, alpha_128, x_k); + + // Store the result. + vst1q_f32(h_p, h_k); + } + + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_4 * 4; k > 0; --k, ++h_p, ++x_p) { + *h_p += alpha * *x_p; + } + + x_p = &x[0]; + } + + *filters_updated = true; + } + + x_start_index = x_start_index > 0 ? x_start_index - 1 : x_size - 1; + } +} + +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) + +void MatchedFilterCore_SSE2(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView h, + bool* filters_updated, + float* error_sum) { + const int h_size = static_cast(h.size()); + const int x_size = static_cast(x.size()); + RTC_DCHECK_EQ(0, h_size % 4); + + // Process for all samples in the sub-block. + for (size_t i = 0; i < y.size(); ++i) { + // Apply the matched filter as filter * x, and compute x * x. + + RTC_DCHECK_GT(x_size, x_start_index); + const float* x_p = &x[x_start_index]; + const float* h_p = &h[0]; + + // Initialize values for the accumulation. + __m128 s_128 = _mm_set1_ps(0); + __m128 x2_sum_128 = _mm_set1_ps(0); + float x2_sum = 0.f; + float s = 0; + + // Compute loop chunk sizes until, and after, the wraparound of the circular + // buffer for x. + const int chunk1 = + std::min(h_size, static_cast(x_size - x_start_index)); + + // Perform the loop in two chunks. + const int chunk2 = h_size - chunk1; + for (int limit : {chunk1, chunk2}) { + // Perform 128 bit vector operations. + const int limit_by_4 = limit >> 2; + for (int k = limit_by_4; k > 0; --k, h_p += 4, x_p += 4) { + // Load the data into 128 bit vectors. + const __m128 x_k = _mm_loadu_ps(x_p); + const __m128 h_k = _mm_loadu_ps(h_p); + const __m128 xx = _mm_mul_ps(x_k, x_k); + // Compute and accumulate x * x and h * x. + x2_sum_128 = _mm_add_ps(x2_sum_128, xx); + const __m128 hx = _mm_mul_ps(h_k, x_k); + s_128 = _mm_add_ps(s_128, hx); + } + + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_4 * 4; k > 0; --k, ++h_p, ++x_p) { + const float x_k = *x_p; + x2_sum += x_k * x_k; + s += *h_p * x_k; + } + + x_p = &x[0]; + } + + // Combine the accumulated vector and scalar values. + float* v = reinterpret_cast(&x2_sum_128); + x2_sum += v[0] + v[1] + v[2] + v[3]; + v = reinterpret_cast(&s_128); + s += v[0] + v[1] + v[2] + v[3]; + + // Compute the matched filter error. + float e = y[i] - s; + const bool saturation = y[i] >= 32000.f || y[i] <= -32000.f; + (*error_sum) += e * e; + + // Update the matched filter estimate in an NLMS manner. + if (x2_sum > x2_sum_threshold && !saturation) { + RTC_DCHECK_LT(0.f, x2_sum); + const float alpha = smoothing * e / x2_sum; + const __m128 alpha_128 = _mm_set1_ps(alpha); + + // filter = filter + smoothing * (y - filter * x) * x / x * x. + float* h_p = &h[0]; + x_p = &x[x_start_index]; + + // Perform the loop in two chunks. + for (int limit : {chunk1, chunk2}) { + // Perform 128 bit vector operations. + const int limit_by_4 = limit >> 2; + for (int k = limit_by_4; k > 0; --k, h_p += 4, x_p += 4) { + // Load the data into 128 bit vectors. + __m128 h_k = _mm_loadu_ps(h_p); + const __m128 x_k = _mm_loadu_ps(x_p); + + // Compute h = h + alpha * x. + const __m128 alpha_x = _mm_mul_ps(alpha_128, x_k); + h_k = _mm_add_ps(h_k, alpha_x); + + // Store the result. + _mm_storeu_ps(h_p, h_k); + } + + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_4 * 4; k > 0; --k, ++h_p, ++x_p) { + *h_p += alpha * *x_p; + } + + x_p = &x[0]; + } + + *filters_updated = true; + } + + x_start_index = x_start_index > 0 ? x_start_index - 1 : x_size - 1; + } +} +#endif + +void MatchedFilterCore(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView h, + bool* filters_updated, + float* error_sum) { + // Process for all samples in the sub-block. + for (size_t i = 0; i < y.size(); ++i) { + // Apply the matched filter as filter * x, and compute x * x. + float x2_sum = 0.f; + float s = 0; + size_t x_index = x_start_index; + for (size_t k = 0; k < h.size(); ++k) { + x2_sum += x[x_index] * x[x_index]; + s += h[k] * x[x_index]; + x_index = x_index < (x.size() - 1) ? x_index + 1 : 0; + } + + // Compute the matched filter error. + float e = y[i] - s; + const bool saturation = y[i] >= 32000.f || y[i] <= -32000.f; + (*error_sum) += e * e; + + // Update the matched filter estimate in an NLMS manner. + if (x2_sum > x2_sum_threshold && !saturation) { + RTC_DCHECK_LT(0.f, x2_sum); + const float alpha = smoothing * e / x2_sum; + + // filter = filter + smoothing * (y - filter * x) * x / x * x. + size_t x_index = x_start_index; + for (size_t k = 0; k < h.size(); ++k) { + h[k] += alpha * x[x_index]; + x_index = x_index < (x.size() - 1) ? x_index + 1 : 0; + } + *filters_updated = true; + } + + x_start_index = x_start_index > 0 ? x_start_index - 1 : x.size() - 1; + } +} + +} // namespace aec3 + +MatchedFilter::MatchedFilter(ApmDataDumper* data_dumper, + Aec3Optimization optimization, + size_t sub_block_size, + size_t window_size_sub_blocks, + int num_matched_filters, + size_t alignment_shift_sub_blocks, + float excitation_limit, + float smoothing, + float matching_filter_threshold) + : data_dumper_(data_dumper), + optimization_(optimization), + sub_block_size_(sub_block_size), + filter_intra_lag_shift_(alignment_shift_sub_blocks * sub_block_size_), + filters_( + num_matched_filters, + std::vector(window_size_sub_blocks * sub_block_size_, 0.f)), + lag_estimates_(num_matched_filters), + filters_offsets_(num_matched_filters, 0), + excitation_limit_(excitation_limit), + smoothing_(smoothing), + matching_filter_threshold_(matching_filter_threshold) { + RTC_DCHECK(data_dumper); + RTC_DCHECK_LT(0, window_size_sub_blocks); + RTC_DCHECK((kBlockSize % sub_block_size) == 0); + RTC_DCHECK((sub_block_size % 4) == 0); +} + +MatchedFilter::~MatchedFilter() = default; + +void MatchedFilter::Reset() { + for (auto& f : filters_) { + std::fill(f.begin(), f.end(), 0.f); + } + + for (auto& l : lag_estimates_) { + l = MatchedFilter::LagEstimate(); + } +} + +void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer, + rtc::ArrayView capture) { + RTC_DCHECK_EQ(sub_block_size_, capture.size()); + auto& y = capture; + + const float x2_sum_threshold = + filters_[0].size() * excitation_limit_ * excitation_limit_; + + // Apply all matched filters. + size_t alignment_shift = 0; + for (size_t n = 0; n < filters_.size(); ++n) { + float error_sum = 0.f; + bool filters_updated = false; + + size_t x_start_index = + (render_buffer.read + alignment_shift + sub_block_size_ - 1) % + render_buffer.buffer.size(); + + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: + aec3::MatchedFilterCore_SSE2(x_start_index, x2_sum_threshold, + smoothing_, render_buffer.buffer, y, + filters_[n], &filters_updated, &error_sum); + break; + case Aec3Optimization::kAvx2: + aec3::MatchedFilterCore_AVX2(x_start_index, x2_sum_threshold, + smoothing_, render_buffer.buffer, y, + filters_[n], &filters_updated, &error_sum); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: + aec3::MatchedFilterCore_NEON(x_start_index, x2_sum_threshold, + smoothing_, render_buffer.buffer, y, + filters_[n], &filters_updated, &error_sum); + break; +#endif + default: + aec3::MatchedFilterCore(x_start_index, x2_sum_threshold, smoothing_, + render_buffer.buffer, y, filters_[n], + &filters_updated, &error_sum); + } + + // Compute anchor for the matched filter error. + const float error_sum_anchor = + std::inner_product(y.begin(), y.end(), y.begin(), 0.f); + + // Estimate the lag in the matched filter as the distance to the portion in + // the filter that contributes the most to the matched filter output. This + // is detected as the peak of the matched filter. + const size_t lag_estimate = std::distance( + filters_[n].begin(), + std::max_element( + filters_[n].begin(), filters_[n].end(), + [](float a, float b) -> bool { return a * a < b * b; })); + + // Update the lag estimates for the matched filter. + lag_estimates_[n] = LagEstimate( + error_sum_anchor - error_sum, + (lag_estimate > 2 && lag_estimate < (filters_[n].size() - 10) && + error_sum < matching_filter_threshold_ * error_sum_anchor), + lag_estimate + alignment_shift, filters_updated); + + RTC_DCHECK_GE(10, filters_.size()); + switch (n) { + case 0: + data_dumper_->DumpRaw("aec3_correlator_0_h", filters_[0]); + break; + case 1: + data_dumper_->DumpRaw("aec3_correlator_1_h", filters_[1]); + break; + case 2: + data_dumper_->DumpRaw("aec3_correlator_2_h", filters_[2]); + break; + case 3: + data_dumper_->DumpRaw("aec3_correlator_3_h", filters_[3]); + break; + case 4: + data_dumper_->DumpRaw("aec3_correlator_4_h", filters_[4]); + break; + case 5: + data_dumper_->DumpRaw("aec3_correlator_5_h", filters_[5]); + break; + case 6: + data_dumper_->DumpRaw("aec3_correlator_6_h", filters_[6]); + break; + case 7: + data_dumper_->DumpRaw("aec3_correlator_7_h", filters_[7]); + break; + case 8: + data_dumper_->DumpRaw("aec3_correlator_8_h", filters_[8]); + break; + case 9: + data_dumper_->DumpRaw("aec3_correlator_9_h", filters_[9]); + break; + default: + RTC_NOTREACHED(); + } + + alignment_shift += filter_intra_lag_shift_; + } +} + +void MatchedFilter::LogFilterProperties(int sample_rate_hz, + size_t shift, + size_t downsampling_factor) const { + size_t alignment_shift = 0; + constexpr int kFsBy1000 = 16; + for (size_t k = 0; k < filters_.size(); ++k) { + int start = static_cast(alignment_shift * downsampling_factor); + int end = static_cast((alignment_shift + filters_[k].size()) * + downsampling_factor); + RTC_LOG(LS_VERBOSE) << "Filter " << k << ": start: " + << (start - static_cast(shift)) / kFsBy1000 + << " ms, end: " + << (end - static_cast(shift)) / kFsBy1000 + << " ms."; + alignment_shift += filter_intra_lag_shift_; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/matched_filter.h b/webrtc/modules/audio_processing/aec3/matched_filter.h new file mode 100644 index 0000000..fa44eb2 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/matched_filter.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_H_ + +#include + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { + +class ApmDataDumper; +struct DownsampledRenderBuffer; + +namespace aec3 { + +#if defined(WEBRTC_HAS_NEON) + +// Filter core for the matched filter that is optimized for NEON. +void MatchedFilterCore_NEON(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView h, + bool* filters_updated, + float* error_sum); + +#endif + +#if defined(WEBRTC_ARCH_X86_FAMILY) + +// Filter core for the matched filter that is optimized for SSE2. +void MatchedFilterCore_SSE2(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView h, + bool* filters_updated, + float* error_sum); + +// Filter core for the matched filter that is optimized for AVX2. +void MatchedFilterCore_AVX2(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView h, + bool* filters_updated, + float* error_sum); + +#endif + +// Filter core for the matched filter. +void MatchedFilterCore(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView h, + bool* filters_updated, + float* error_sum); + +} // namespace aec3 + +// Produces recursively updated cross-correlation estimates for several signal +// shifts where the intra-shift spacing is uniform. +class MatchedFilter { + public: + // Stores properties for the lag estimate corresponding to a particular signal + // shift. + struct LagEstimate { + LagEstimate() = default; + LagEstimate(float accuracy, bool reliable, size_t lag, bool updated) + : accuracy(accuracy), reliable(reliable), lag(lag), updated(updated) {} + + float accuracy = 0.f; + bool reliable = false; + size_t lag = 0; + bool updated = false; + }; + + MatchedFilter(ApmDataDumper* data_dumper, + Aec3Optimization optimization, + size_t sub_block_size, + size_t window_size_sub_blocks, + int num_matched_filters, + size_t alignment_shift_sub_blocks, + float excitation_limit, + float smoothing, + float matching_filter_threshold); + + MatchedFilter() = delete; + MatchedFilter(const MatchedFilter&) = delete; + MatchedFilter& operator=(const MatchedFilter&) = delete; + + ~MatchedFilter(); + + // Updates the correlation with the values in the capture buffer. + void Update(const DownsampledRenderBuffer& render_buffer, + rtc::ArrayView capture); + + // Resets the matched filter. + void Reset(); + + // Returns the current lag estimates. + rtc::ArrayView GetLagEstimates() const { + return lag_estimates_; + } + + // Returns the maximum filter lag. + size_t GetMaxFilterLag() const { + return filters_.size() * filter_intra_lag_shift_ + filters_[0].size(); + } + + // Log matched filter properties. + void LogFilterProperties(int sample_rate_hz, + size_t shift, + size_t downsampling_factor) const; + + private: + ApmDataDumper* const data_dumper_; + const Aec3Optimization optimization_; + const size_t sub_block_size_; + const size_t filter_intra_lag_shift_; + std::vector> filters_; + std::vector lag_estimates_; + std::vector filters_offsets_; + const float excitation_limit_; + const float smoothing_; + const float matching_filter_threshold_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_H_ diff --git a/webrtc/modules/audio_processing/aec3/matched_filter_avx2.cc b/webrtc/modules/audio_processing/aec3/matched_filter_avx2.cc new file mode 100644 index 0000000..ed32102 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/matched_filter_avx2.cc @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2020 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 "modules/audio_processing/aec3/matched_filter.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace aec3 { + +void MatchedFilterCore_AVX2(size_t x_start_index, + float x2_sum_threshold, + float smoothing, + rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView h, + bool* filters_updated, + float* error_sum) { + const int h_size = static_cast(h.size()); + const int x_size = static_cast(x.size()); + RTC_DCHECK_EQ(0, h_size % 8); + + // Process for all samples in the sub-block. + for (size_t i = 0; i < y.size(); ++i) { + // Apply the matched filter as filter * x, and compute x * x. + + RTC_DCHECK_GT(x_size, x_start_index); + const float* x_p = &x[x_start_index]; + const float* h_p = &h[0]; + + // Initialize values for the accumulation. + __m256 s_256 = _mm256_set1_ps(0); + __m256 x2_sum_256 = _mm256_set1_ps(0); + float x2_sum = 0.f; + float s = 0; + + // Compute loop chunk sizes until, and after, the wraparound of the circular + // buffer for x. + const int chunk1 = + std::min(h_size, static_cast(x_size - x_start_index)); + + // Perform the loop in two chunks. + const int chunk2 = h_size - chunk1; + for (int limit : {chunk1, chunk2}) { + // Perform 256 bit vector operations. + const int limit_by_8 = limit >> 3; + for (int k = limit_by_8; k > 0; --k, h_p += 8, x_p += 8) { + // Load the data into 256 bit vectors. + __m256 x_k = _mm256_loadu_ps(x_p); + __m256 h_k = _mm256_loadu_ps(h_p); + // Compute and accumulate x * x and h * x. + x2_sum_256 = _mm256_fmadd_ps(x_k, x_k, x2_sum_256); + s_256 = _mm256_fmadd_ps(h_k, x_k, s_256); + } + + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_8 * 8; k > 0; --k, ++h_p, ++x_p) { + const float x_k = *x_p; + x2_sum += x_k * x_k; + s += *h_p * x_k; + } + + x_p = &x[0]; + } + + // Sum components together. + __m128 x2_sum_128 = _mm_add_ps(_mm256_extractf128_ps(x2_sum_256, 0), + _mm256_extractf128_ps(x2_sum_256, 1)); + __m128 s_128 = _mm_add_ps(_mm256_extractf128_ps(s_256, 0), + _mm256_extractf128_ps(s_256, 1)); + // Combine the accumulated vector and scalar values. + float* v = reinterpret_cast(&x2_sum_128); + x2_sum += v[0] + v[1] + v[2] + v[3]; + v = reinterpret_cast(&s_128); + s += v[0] + v[1] + v[2] + v[3]; + + // Compute the matched filter error. + float e = y[i] - s; + const bool saturation = y[i] >= 32000.f || y[i] <= -32000.f; + (*error_sum) += e * e; + + // Update the matched filter estimate in an NLMS manner. + if (x2_sum > x2_sum_threshold && !saturation) { + RTC_DCHECK_LT(0.f, x2_sum); + const float alpha = smoothing * e / x2_sum; + const __m256 alpha_256 = _mm256_set1_ps(alpha); + + // filter = filter + smoothing * (y - filter * x) * x / x * x. + float* h_p = &h[0]; + x_p = &x[x_start_index]; + + // Perform the loop in two chunks. + for (int limit : {chunk1, chunk2}) { + // Perform 256 bit vector operations. + const int limit_by_8 = limit >> 3; + for (int k = limit_by_8; k > 0; --k, h_p += 8, x_p += 8) { + // Load the data into 256 bit vectors. + __m256 h_k = _mm256_loadu_ps(h_p); + __m256 x_k = _mm256_loadu_ps(x_p); + // Compute h = h + alpha * x. + h_k = _mm256_fmadd_ps(x_k, alpha_256, h_k); + + // Store the result. + _mm256_storeu_ps(h_p, h_k); + } + + // Perform non-vector operations for any remaining items. + for (int k = limit - limit_by_8 * 8; k > 0; --k, ++h_p, ++x_p) { + *h_p += alpha * *x_p; + } + + x_p = &x[0]; + } + + *filters_updated = true; + } + + x_start_index = x_start_index > 0 ? x_start_index - 1 : x_size - 1; + } +} + +} // namespace aec3 +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc b/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc new file mode 100644 index 0000000..603a864 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/matched_filter_lag_aggregator.h" + +#include +#include + +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +MatchedFilterLagAggregator::MatchedFilterLagAggregator( + ApmDataDumper* data_dumper, + size_t max_filter_lag, + const EchoCanceller3Config::Delay::DelaySelectionThresholds& thresholds) + : data_dumper_(data_dumper), + histogram_(max_filter_lag + 1, 0), + thresholds_(thresholds) { + RTC_DCHECK(data_dumper); + RTC_DCHECK_LE(thresholds_.initial, thresholds_.converged); + histogram_data_.fill(0); +} + +MatchedFilterLagAggregator::~MatchedFilterLagAggregator() = default; + +void MatchedFilterLagAggregator::Reset(bool hard_reset) { + std::fill(histogram_.begin(), histogram_.end(), 0); + histogram_data_.fill(0); + histogram_data_index_ = 0; + if (hard_reset) { + significant_candidate_found_ = false; + } +} + +absl::optional MatchedFilterLagAggregator::Aggregate( + rtc::ArrayView lag_estimates) { + // Choose the strongest lag estimate as the best one. + float best_accuracy = 0.f; + int best_lag_estimate_index = -1; + for (size_t k = 0; k < lag_estimates.size(); ++k) { + if (lag_estimates[k].updated && lag_estimates[k].reliable) { + if (lag_estimates[k].accuracy > best_accuracy) { + best_accuracy = lag_estimates[k].accuracy; + best_lag_estimate_index = static_cast(k); + } + } + } + + // TODO(peah): Remove this logging once all development is done. + data_dumper_->DumpRaw("aec3_echo_path_delay_estimator_best_index", + best_lag_estimate_index); + data_dumper_->DumpRaw("aec3_echo_path_delay_estimator_histogram", histogram_); + + if (best_lag_estimate_index != -1) { + RTC_DCHECK_GT(histogram_.size(), histogram_data_[histogram_data_index_]); + RTC_DCHECK_LE(0, histogram_data_[histogram_data_index_]); + --histogram_[histogram_data_[histogram_data_index_]]; + + histogram_data_[histogram_data_index_] = + lag_estimates[best_lag_estimate_index].lag; + + RTC_DCHECK_GT(histogram_.size(), histogram_data_[histogram_data_index_]); + RTC_DCHECK_LE(0, histogram_data_[histogram_data_index_]); + ++histogram_[histogram_data_[histogram_data_index_]]; + + histogram_data_index_ = + (histogram_data_index_ + 1) % histogram_data_.size(); + + const int candidate = + std::distance(histogram_.begin(), + std::max_element(histogram_.begin(), histogram_.end())); + + significant_candidate_found_ = + significant_candidate_found_ || + histogram_[candidate] > thresholds_.converged; + if (histogram_[candidate] > thresholds_.converged || + (histogram_[candidate] > thresholds_.initial && + !significant_candidate_found_)) { + DelayEstimate::Quality quality = significant_candidate_found_ + ? DelayEstimate::Quality::kRefined + : DelayEstimate::Quality::kCoarse; + return DelayEstimate(quality, candidate); + } + } + + return absl::nullopt; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.h b/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.h new file mode 100644 index 0000000..d48011e --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/matched_filter_lag_aggregator.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_LAG_AGGREGATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_LAG_AGGREGATOR_H_ + +#include + +#include "absl/types/optional.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/matched_filter.h" + +namespace webrtc { + +class ApmDataDumper; + +// Aggregates lag estimates produced by the MatchedFilter class into a single +// reliable combined lag estimate. +class MatchedFilterLagAggregator { + public: + MatchedFilterLagAggregator( + ApmDataDumper* data_dumper, + size_t max_filter_lag, + const EchoCanceller3Config::Delay::DelaySelectionThresholds& thresholds); + + MatchedFilterLagAggregator() = delete; + MatchedFilterLagAggregator(const MatchedFilterLagAggregator&) = delete; + MatchedFilterLagAggregator& operator=(const MatchedFilterLagAggregator&) = + delete; + + ~MatchedFilterLagAggregator(); + + // Resets the aggregator. + void Reset(bool hard_reset); + + // Aggregates the provided lag estimates. + absl::optional Aggregate( + rtc::ArrayView lag_estimates); + + private: + ApmDataDumper* const data_dumper_; + std::vector histogram_; + std::array histogram_data_; + int histogram_data_index_ = 0; + bool significant_candidate_found_ = false; + const EchoCanceller3Config::Delay::DelaySelectionThresholds thresholds_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_MATCHED_FILTER_LAG_AGGREGATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/moving_average.cc b/webrtc/modules/audio_processing/aec3/moving_average.cc new file mode 100644 index 0000000..7a81ee8 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/moving_average.cc @@ -0,0 +1,60 @@ + +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/moving_average.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace aec3 { + +MovingAverage::MovingAverage(size_t num_elem, size_t mem_len) + : num_elem_(num_elem), + mem_len_(mem_len - 1), + scaling_(1.0f / static_cast(mem_len)), + memory_(num_elem * mem_len_, 0.f), + mem_index_(0) { + RTC_DCHECK(num_elem_ > 0); + RTC_DCHECK(mem_len > 0); +} + +MovingAverage::~MovingAverage() = default; + +void MovingAverage::Average(rtc::ArrayView input, + rtc::ArrayView output) { + RTC_DCHECK(input.size() == num_elem_); + RTC_DCHECK(output.size() == num_elem_); + + // Sum all contributions. + std::copy(input.begin(), input.end(), output.begin()); + for (auto i = memory_.begin(); i < memory_.end(); i += num_elem_) { + std::transform(i, i + num_elem_, output.begin(), output.begin(), + std::plus()); + } + + // Divide by mem_len_. + for (float& o : output) { + o *= scaling_; + } + + // Update memory. + if (mem_len_ > 0) { + std::copy(input.begin(), input.end(), + memory_.begin() + mem_index_ * num_elem_); + mem_index_ = (mem_index_ + 1) % mem_len_; + } +} + +} // namespace aec3 +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/moving_average.h b/webrtc/modules/audio_processing/aec3/moving_average.h new file mode 100644 index 0000000..913d785 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/moving_average.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_H_ + +#include + +#include + +#include "api/array_view.h" + +namespace webrtc { +namespace aec3 { + +class MovingAverage { + public: + // Creates an instance of MovingAverage that accepts inputs of length num_elem + // and averages over mem_len inputs. + MovingAverage(size_t num_elem, size_t mem_len); + ~MovingAverage(); + + // Computes the average of input and mem_len-1 previous inputs and stores the + // result in output. + void Average(rtc::ArrayView input, rtc::ArrayView output); + + private: + const size_t num_elem_; + const size_t mem_len_; + const float scaling_; + std::vector memory_; + size_t mem_index_; +}; + +} // namespace aec3 +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_H_ diff --git a/webrtc/modules/audio_processing/aec3/nearend_detector.h b/webrtc/modules/audio_processing/aec3/nearend_detector.h new file mode 100644 index 0000000..0d8a06b --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/nearend_detector.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_NEAREND_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_NEAREND_DETECTOR_H_ + +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { +// Class for selecting whether the suppressor is in the nearend or echo state. +class NearendDetector { + public: + virtual ~NearendDetector() {} + + // Returns whether the current state is the nearend state. + virtual bool IsNearendState() const = 0; + + // Updates the state selection based on latest spectral estimates. + virtual void Update( + rtc::ArrayView> + nearend_spectrum, + rtc::ArrayView> + residual_echo_spectrum, + rtc::ArrayView> + comfort_noise_spectrum, + bool initial_state) = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_NEAREND_DETECTOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.cc b/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.cc new file mode 100644 index 0000000..138329a --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.cc @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/refined_filter_update_gain.h" + +#include +#include + +#include "modules/audio_processing/aec3/adaptive_fir_filter.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/render_signal_analyzer.h" +#include "modules/audio_processing/aec3/subtractor_output.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr float kHErrorInitial = 10000.f; +constexpr int kPoorExcitationCounterInitial = 1000; + +} // namespace + +int RefinedFilterUpdateGain::instance_count_ = 0; + +RefinedFilterUpdateGain::RefinedFilterUpdateGain( + const EchoCanceller3Config::Filter::RefinedConfiguration& config, + size_t config_change_duration_blocks) + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + config_change_duration_blocks_( + static_cast(config_change_duration_blocks)), + poor_excitation_counter_(kPoorExcitationCounterInitial) { + SetConfig(config, true); + H_error_.fill(kHErrorInitial); + RTC_DCHECK_LT(0, config_change_duration_blocks_); + one_by_config_change_duration_blocks_ = 1.f / config_change_duration_blocks_; +} + +RefinedFilterUpdateGain::~RefinedFilterUpdateGain() {} + +void RefinedFilterUpdateGain::HandleEchoPathChange( + const EchoPathVariability& echo_path_variability) { + if (echo_path_variability.gain_change) { + // TODO(bugs.webrtc.org/9526) Handle gain changes. + } + + if (echo_path_variability.delay_change != + EchoPathVariability::DelayAdjustment::kNone) { + H_error_.fill(kHErrorInitial); + } + + if (!echo_path_variability.gain_change) { + poor_excitation_counter_ = kPoorExcitationCounterInitial; + call_counter_ = 0; + } +} + +void RefinedFilterUpdateGain::Compute( + const std::array& render_power, + const RenderSignalAnalyzer& render_signal_analyzer, + const SubtractorOutput& subtractor_output, + rtc::ArrayView erl, + size_t size_partitions, + bool saturated_capture_signal, + FftData* gain_fft) { + RTC_DCHECK(gain_fft); + // Introducing shorter notation to improve readability. + const FftData& E_refined = subtractor_output.E_refined; + const auto& E2_refined = subtractor_output.E2_refined; + const auto& E2_coarse = subtractor_output.E2_coarse; + FftData* G = gain_fft; + const auto& X2 = render_power; + + ++call_counter_; + + UpdateCurrentConfig(); + + if (render_signal_analyzer.PoorSignalExcitation()) { + poor_excitation_counter_ = 0; + } + + // Do not update the filter if the render is not sufficiently excited. + if (++poor_excitation_counter_ < size_partitions || + saturated_capture_signal || call_counter_ <= size_partitions) { + G->re.fill(0.f); + G->im.fill(0.f); + } else { + // Corresponds to WGN of power -39 dBFS. + std::array mu; + // mu = H_error / (0.5* H_error* X2 + n * E2). + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + if (X2[k] >= current_config_.noise_gate) { + mu[k] = H_error_[k] / + (0.5f * H_error_[k] * X2[k] + size_partitions * E2_refined[k]); + } else { + mu[k] = 0.f; + } + } + + // Avoid updating the filter close to narrow bands in the render signals. + render_signal_analyzer.MaskRegionsAroundNarrowBands(&mu); + + // H_error = H_error - 0.5 * mu * X2 * H_error. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + H_error_[k] -= 0.5f * mu[k] * X2[k] * H_error_[k]; + } + + // G = mu * E. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + G->re[k] = mu[k] * E_refined.re[k]; + G->im[k] = mu[k] * E_refined.im[k]; + } + } + + // H_error = H_error + factor * erl. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + if (E2_coarse[k] >= E2_refined[k]) { + H_error_[k] += current_config_.leakage_converged * erl[k]; + } else { + H_error_[k] += current_config_.leakage_diverged * erl[k]; + } + + H_error_[k] = std::max(H_error_[k], current_config_.error_floor); + H_error_[k] = std::min(H_error_[k], current_config_.error_ceil); + } + + data_dumper_->DumpRaw("aec3_refined_gain_H_error", H_error_); +} + +void RefinedFilterUpdateGain::UpdateCurrentConfig() { + RTC_DCHECK_GE(config_change_duration_blocks_, config_change_counter_); + if (config_change_counter_ > 0) { + if (--config_change_counter_ > 0) { + auto average = [](float from, float to, float from_weight) { + return from * from_weight + to * (1.f - from_weight); + }; + + float change_factor = + config_change_counter_ * one_by_config_change_duration_blocks_; + + current_config_.leakage_converged = + average(old_target_config_.leakage_converged, + target_config_.leakage_converged, change_factor); + current_config_.leakage_diverged = + average(old_target_config_.leakage_diverged, + target_config_.leakage_diverged, change_factor); + current_config_.error_floor = + average(old_target_config_.error_floor, target_config_.error_floor, + change_factor); + current_config_.error_ceil = + average(old_target_config_.error_ceil, target_config_.error_ceil, + change_factor); + current_config_.noise_gate = + average(old_target_config_.noise_gate, target_config_.noise_gate, + change_factor); + } else { + current_config_ = old_target_config_ = target_config_; + } + } + RTC_DCHECK_LE(0, config_change_counter_); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.h b/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.h new file mode 100644 index 0000000..5730979 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/refined_filter_update_gain.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_REFINED_FILTER_UPDATE_GAIN_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_REFINED_FILTER_UPDATE_GAIN_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +class AdaptiveFirFilter; +class ApmDataDumper; +struct EchoPathVariability; +struct FftData; +class RenderSignalAnalyzer; +struct SubtractorOutput; + +// Provides functionality for computing the adaptive gain for the refined +// filter. +class RefinedFilterUpdateGain { + public: + RefinedFilterUpdateGain( + const EchoCanceller3Config::Filter::RefinedConfiguration& config, + size_t config_change_duration_blocks); + ~RefinedFilterUpdateGain(); + + RefinedFilterUpdateGain(const RefinedFilterUpdateGain&) = delete; + RefinedFilterUpdateGain& operator=(const RefinedFilterUpdateGain&) = delete; + + // Takes action in the case of a known echo path change. + void HandleEchoPathChange(const EchoPathVariability& echo_path_variability); + + // Computes the gain. + void Compute(const std::array& render_power, + const RenderSignalAnalyzer& render_signal_analyzer, + const SubtractorOutput& subtractor_output, + rtc::ArrayView erl, + size_t size_partitions, + bool saturated_capture_signal, + FftData* gain_fft); + + // Sets a new config. + void SetConfig( + const EchoCanceller3Config::Filter::RefinedConfiguration& config, + bool immediate_effect) { + if (immediate_effect) { + old_target_config_ = current_config_ = target_config_ = config; + config_change_counter_ = 0; + } else { + old_target_config_ = current_config_; + target_config_ = config; + config_change_counter_ = config_change_duration_blocks_; + } + } + + private: + static int instance_count_; + std::unique_ptr data_dumper_; + const int config_change_duration_blocks_; + float one_by_config_change_duration_blocks_; + EchoCanceller3Config::Filter::RefinedConfiguration current_config_; + EchoCanceller3Config::Filter::RefinedConfiguration target_config_; + EchoCanceller3Config::Filter::RefinedConfiguration old_target_config_; + std::array H_error_; + size_t poor_excitation_counter_; + size_t call_counter_ = 0; + int config_change_counter_ = 0; + + // Updates the current config towards the target config. + void UpdateCurrentConfig(); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_REFINED_FILTER_UPDATE_GAIN_H_ diff --git a/webrtc/modules/audio_processing/aec3/render_buffer.cc b/webrtc/modules/audio_processing/aec3/render_buffer.cc new file mode 100644 index 0000000..60ea69c --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/render_buffer.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/render_buffer.h" + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +RenderBuffer::RenderBuffer(BlockBuffer* block_buffer, + SpectrumBuffer* spectrum_buffer, + FftBuffer* fft_buffer) + : block_buffer_(block_buffer), + spectrum_buffer_(spectrum_buffer), + fft_buffer_(fft_buffer) { + RTC_DCHECK(block_buffer_); + RTC_DCHECK(spectrum_buffer_); + RTC_DCHECK(fft_buffer_); + RTC_DCHECK_EQ(block_buffer_->buffer.size(), fft_buffer_->buffer.size()); + RTC_DCHECK_EQ(spectrum_buffer_->buffer.size(), fft_buffer_->buffer.size()); + RTC_DCHECK_EQ(spectrum_buffer_->read, fft_buffer_->read); + RTC_DCHECK_EQ(spectrum_buffer_->write, fft_buffer_->write); +} + +RenderBuffer::~RenderBuffer() = default; + +void RenderBuffer::SpectralSum( + size_t num_spectra, + std::array* X2) const { + X2->fill(0.f); + int position = spectrum_buffer_->read; + for (size_t j = 0; j < num_spectra; ++j) { + for (const auto& channel_spectrum : spectrum_buffer_->buffer[position]) { + std::transform(X2->begin(), X2->end(), channel_spectrum.begin(), + X2->begin(), std::plus()); + } + position = spectrum_buffer_->IncIndex(position); + } +} + +void RenderBuffer::SpectralSums( + size_t num_spectra_shorter, + size_t num_spectra_longer, + std::array* X2_shorter, + std::array* X2_longer) const { + RTC_DCHECK_LE(num_spectra_shorter, num_spectra_longer); + X2_shorter->fill(0.f); + int position = spectrum_buffer_->read; + size_t j = 0; + for (; j < num_spectra_shorter; ++j) { + for (const auto& channel_spectrum : spectrum_buffer_->buffer[position]) { + std::transform(X2_shorter->begin(), X2_shorter->end(), + channel_spectrum.begin(), X2_shorter->begin(), + std::plus()); + } + position = spectrum_buffer_->IncIndex(position); + } + std::copy(X2_shorter->begin(), X2_shorter->end(), X2_longer->begin()); + for (; j < num_spectra_longer; ++j) { + for (const auto& channel_spectrum : spectrum_buffer_->buffer[position]) { + std::transform(X2_longer->begin(), X2_longer->end(), + channel_spectrum.begin(), X2_longer->begin(), + std::plus()); + } + position = spectrum_buffer_->IncIndex(position); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/render_buffer.h b/webrtc/modules/audio_processing/aec3/render_buffer.h new file mode 100644 index 0000000..b8be6f5 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/render_buffer.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RENDER_BUFFER_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/block_buffer.h" +#include "modules/audio_processing/aec3/fft_buffer.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Provides a buffer of the render data for the echo remover. +class RenderBuffer { + public: + RenderBuffer(BlockBuffer* block_buffer, + SpectrumBuffer* spectrum_buffer, + FftBuffer* fft_buffer); + + RenderBuffer() = delete; + RenderBuffer(const RenderBuffer&) = delete; + RenderBuffer& operator=(const RenderBuffer&) = delete; + + ~RenderBuffer(); + + // Get a block. + const std::vector>>& Block( + int buffer_offset_blocks) const { + int position = + block_buffer_->OffsetIndex(block_buffer_->read, buffer_offset_blocks); + return block_buffer_->buffer[position]; + } + + // Get the spectrum from one of the FFTs in the buffer. + rtc::ArrayView> Spectrum( + int buffer_offset_ffts) const { + int position = spectrum_buffer_->OffsetIndex(spectrum_buffer_->read, + buffer_offset_ffts); + return spectrum_buffer_->buffer[position]; + } + + // Returns the circular fft buffer. + rtc::ArrayView> GetFftBuffer() const { + return fft_buffer_->buffer; + } + + // Returns the current position in the circular buffer. + size_t Position() const { + RTC_DCHECK_EQ(spectrum_buffer_->read, fft_buffer_->read); + RTC_DCHECK_EQ(spectrum_buffer_->write, fft_buffer_->write); + return fft_buffer_->read; + } + + // Returns the sum of the spectrums for a certain number of FFTs. + void SpectralSum(size_t num_spectra, + std::array* X2) const; + + // Returns the sums of the spectrums for two numbers of FFTs. + void SpectralSums(size_t num_spectra_shorter, + size_t num_spectra_longer, + std::array* X2_shorter, + std::array* X2_longer) const; + + // Gets the recent activity seen in the render signal. + bool GetRenderActivity() const { return render_activity_; } + + // Specifies the recent activity seen in the render signal. + void SetRenderActivity(bool activity) { render_activity_ = activity; } + + // Returns the headroom between the write and the read positions in the + // buffer. + int Headroom() const { + // The write and read indices are decreased over time. + int headroom = + fft_buffer_->write < fft_buffer_->read + ? fft_buffer_->read - fft_buffer_->write + : fft_buffer_->size - fft_buffer_->write + fft_buffer_->read; + + RTC_DCHECK_LE(0, headroom); + RTC_DCHECK_GE(fft_buffer_->size, headroom); + + return headroom; + } + + // Returns a reference to the spectrum buffer. + const SpectrumBuffer& GetSpectrumBuffer() const { return *spectrum_buffer_; } + + // Returns a reference to the block buffer. + const BlockBuffer& GetBlockBuffer() const { return *block_buffer_; } + + private: + const BlockBuffer* const block_buffer_; + const SpectrumBuffer* const spectrum_buffer_; + const FftBuffer* const fft_buffer_; + bool render_activity_ = false; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RENDER_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/aec3/render_delay_buffer.cc b/webrtc/modules/audio_processing/aec3/render_delay_buffer.cc new file mode 100644 index 0000000..7bebc6f --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/render_delay_buffer.cc @@ -0,0 +1,523 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/render_delay_buffer.h" + +#include + +#include +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec3_fft.h" +#include "modules/audio_processing/aec3/alignment_mixer.h" +#include "modules/audio_processing/aec3/block_buffer.h" +#include "modules/audio_processing/aec3/decimator.h" +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/aec3/fft_buffer.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/field_trial.h" + +namespace webrtc { +namespace { + +bool UpdateCaptureCallCounterOnSkippedBlocks() { + return !field_trial::IsEnabled( + "WebRTC-Aec3RenderBufferCallCounterUpdateKillSwitch"); +} + +class RenderDelayBufferImpl final : public RenderDelayBuffer { + public: + RenderDelayBufferImpl(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels); + RenderDelayBufferImpl() = delete; + ~RenderDelayBufferImpl() override; + + void Reset() override; + BufferingEvent Insert( + const std::vector>>& block) override; + BufferingEvent PrepareCaptureProcessing() override; + void HandleSkippedCaptureProcessing() override; + bool AlignFromDelay(size_t delay) override; + void AlignFromExternalDelay() override; + size_t Delay() const override { return ComputeDelay(); } + size_t MaxDelay() const override { + return blocks_.buffer.size() - 1 - buffer_headroom_; + } + RenderBuffer* GetRenderBuffer() override { return &echo_remover_buffer_; } + + const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const override { + return low_rate_; + } + + int BufferLatency() const; + void SetAudioBufferDelay(int delay_ms) override; + bool HasReceivedBufferDelay() override; + + private: + static int instance_count_; + std::unique_ptr data_dumper_; + const Aec3Optimization optimization_; + const EchoCanceller3Config config_; + const bool update_capture_call_counter_on_skipped_blocks_; + const float render_linear_amplitude_gain_; + const rtc::LoggingSeverity delay_log_level_; + size_t down_sampling_factor_; + const int sub_block_size_; + BlockBuffer blocks_; + SpectrumBuffer spectra_; + FftBuffer ffts_; + absl::optional delay_; + RenderBuffer echo_remover_buffer_; + DownsampledRenderBuffer low_rate_; + AlignmentMixer render_mixer_; + Decimator render_decimator_; + const Aec3Fft fft_; + std::vector render_ds_; + const int buffer_headroom_; + bool last_call_was_render_ = false; + int num_api_calls_in_a_row_ = 0; + int max_observed_jitter_ = 1; + int64_t capture_call_counter_ = 0; + int64_t render_call_counter_ = 0; + bool render_activity_ = false; + size_t render_activity_counter_ = 0; + absl::optional external_audio_buffer_delay_; + bool external_audio_buffer_delay_verified_after_reset_ = false; + size_t min_latency_blocks_ = 0; + size_t excess_render_detection_counter_ = 0; + + int MapDelayToTotalDelay(size_t delay) const; + int ComputeDelay() const; + void ApplyTotalDelay(int delay); + void InsertBlock(const std::vector>>& block, + int previous_write); + bool DetectActiveRender(rtc::ArrayView x) const; + bool DetectExcessRenderBlocks(); + void IncrementWriteIndices(); + void IncrementLowRateReadIndices(); + void IncrementReadIndices(); + bool RenderOverrun(); + bool RenderUnderrun(); +}; + +int RenderDelayBufferImpl::instance_count_ = 0; + +RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels) + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + optimization_(DetectOptimization()), + config_(config), + update_capture_call_counter_on_skipped_blocks_( + UpdateCaptureCallCounterOnSkippedBlocks()), + render_linear_amplitude_gain_( + std::pow(10.0f, config_.render_levels.render_power_gain_db / 20.f)), + delay_log_level_(config_.delay.log_warning_on_delay_changes + ? rtc::LS_WARNING + : rtc::LS_VERBOSE), + down_sampling_factor_(config.delay.down_sampling_factor), + sub_block_size_(static_cast(down_sampling_factor_ > 0 + ? kBlockSize / down_sampling_factor_ + : kBlockSize)), + blocks_(GetRenderDelayBufferSize(down_sampling_factor_, + config.delay.num_filters, + config.filter.refined.length_blocks), + NumBandsForRate(sample_rate_hz), + num_render_channels, + kBlockSize), + spectra_(blocks_.buffer.size(), num_render_channels), + ffts_(blocks_.buffer.size(), num_render_channels), + delay_(config_.delay.default_delay), + echo_remover_buffer_(&blocks_, &spectra_, &ffts_), + low_rate_(GetDownSampledBufferSize(down_sampling_factor_, + config.delay.num_filters)), + render_mixer_(num_render_channels, config.delay.render_alignment_mixing), + render_decimator_(down_sampling_factor_), + fft_(), + render_ds_(sub_block_size_, 0.f), + buffer_headroom_(config.filter.refined.length_blocks) { + RTC_DCHECK_EQ(blocks_.buffer.size(), ffts_.buffer.size()); + RTC_DCHECK_EQ(spectra_.buffer.size(), ffts_.buffer.size()); + for (size_t i = 0; i < blocks_.buffer.size(); ++i) { + RTC_DCHECK_EQ(blocks_.buffer[i][0].size(), ffts_.buffer[i].size()); + RTC_DCHECK_EQ(spectra_.buffer[i].size(), ffts_.buffer[i].size()); + } + + Reset(); +} + +RenderDelayBufferImpl::~RenderDelayBufferImpl() = default; + +// Resets the buffer delays and clears the reported delays. +void RenderDelayBufferImpl::Reset() { + last_call_was_render_ = false; + num_api_calls_in_a_row_ = 1; + min_latency_blocks_ = 0; + excess_render_detection_counter_ = 0; + + // Initialize the read index to one sub-block before the write index. + low_rate_.read = low_rate_.OffsetIndex(low_rate_.write, sub_block_size_); + + // Check for any external audio buffer delay and whether it is feasible. + if (external_audio_buffer_delay_) { + const int headroom = 2; + size_t audio_buffer_delay_to_set; + // Minimum delay is 1 (like the low-rate render buffer). + if (*external_audio_buffer_delay_ <= headroom) { + audio_buffer_delay_to_set = 1; + } else { + audio_buffer_delay_to_set = *external_audio_buffer_delay_ - headroom; + } + + audio_buffer_delay_to_set = std::min(audio_buffer_delay_to_set, MaxDelay()); + + // When an external delay estimate is available, use that delay as the + // initial render buffer delay. + ApplyTotalDelay(audio_buffer_delay_to_set); + delay_ = ComputeDelay(); + + external_audio_buffer_delay_verified_after_reset_ = false; + } else { + // If an external delay estimate is not available, use that delay as the + // initial delay. Set the render buffer delays to the default delay. + ApplyTotalDelay(config_.delay.default_delay); + + // Unset the delays which are set by AlignFromDelay. + delay_ = absl::nullopt; + } +} + +// Inserts a new block into the render buffers. +RenderDelayBuffer::BufferingEvent RenderDelayBufferImpl::Insert( + const std::vector>>& block) { + ++render_call_counter_; + if (delay_) { + if (!last_call_was_render_) { + last_call_was_render_ = true; + num_api_calls_in_a_row_ = 1; + } else { + if (++num_api_calls_in_a_row_ > max_observed_jitter_) { + max_observed_jitter_ = num_api_calls_in_a_row_; + RTC_LOG_V(delay_log_level_) + << "New max number api jitter observed at render block " + << render_call_counter_ << ": " << num_api_calls_in_a_row_ + << " blocks"; + } + } + } + + // Increase the write indices to where the new blocks should be written. + const int previous_write = blocks_.write; + IncrementWriteIndices(); + + // Allow overrun and do a reset when render overrun occurrs due to more render + // data being inserted than capture data is received. + BufferingEvent event = + RenderOverrun() ? BufferingEvent::kRenderOverrun : BufferingEvent::kNone; + + // Detect and update render activity. + if (!render_activity_) { + render_activity_counter_ += DetectActiveRender(block[0][0]) ? 1 : 0; + render_activity_ = render_activity_counter_ >= 20; + } + + // Insert the new render block into the specified position. + InsertBlock(block, previous_write); + + if (event != BufferingEvent::kNone) { + Reset(); + } + + return event; +} + +void RenderDelayBufferImpl::HandleSkippedCaptureProcessing() { + if (update_capture_call_counter_on_skipped_blocks_) { + ++capture_call_counter_; + } +} + +// Prepares the render buffers for processing another capture block. +RenderDelayBuffer::BufferingEvent +RenderDelayBufferImpl::PrepareCaptureProcessing() { + RenderDelayBuffer::BufferingEvent event = BufferingEvent::kNone; + ++capture_call_counter_; + + if (delay_) { + if (last_call_was_render_) { + last_call_was_render_ = false; + num_api_calls_in_a_row_ = 1; + } else { + if (++num_api_calls_in_a_row_ > max_observed_jitter_) { + max_observed_jitter_ = num_api_calls_in_a_row_; + RTC_LOG_V(delay_log_level_) + << "New max number api jitter observed at capture block " + << capture_call_counter_ << ": " << num_api_calls_in_a_row_ + << " blocks"; + } + } + } + + if (DetectExcessRenderBlocks()) { + // Too many render blocks compared to capture blocks. Risk of delay ending + // up before the filter used by the delay estimator. + RTC_LOG_V(delay_log_level_) + << "Excess render blocks detected at block " << capture_call_counter_; + Reset(); + event = BufferingEvent::kRenderOverrun; + } else if (RenderUnderrun()) { + // Don't increment the read indices of the low rate buffer if there is a + // render underrun. + RTC_LOG_V(delay_log_level_) + << "Render buffer underrun detected at block " << capture_call_counter_; + IncrementReadIndices(); + // Incrementing the buffer index without increasing the low rate buffer + // index means that the delay is reduced by one. + if (delay_ && *delay_ > 0) + delay_ = *delay_ - 1; + event = BufferingEvent::kRenderUnderrun; + } else { + // Increment the read indices in the render buffers to point to the most + // recent block to use in the capture processing. + IncrementLowRateReadIndices(); + IncrementReadIndices(); + } + + echo_remover_buffer_.SetRenderActivity(render_activity_); + if (render_activity_) { + render_activity_counter_ = 0; + render_activity_ = false; + } + + return event; +} + +// Sets the delay and returns a bool indicating whether the delay was changed. +bool RenderDelayBufferImpl::AlignFromDelay(size_t delay) { + RTC_DCHECK(!config_.delay.use_external_delay_estimator); + if (!external_audio_buffer_delay_verified_after_reset_ && + external_audio_buffer_delay_ && delay_) { + int difference = static_cast(delay) - static_cast(*delay_); + RTC_LOG_V(delay_log_level_) + << "Mismatch between first estimated delay after reset " + "and externally reported audio buffer delay: " + << difference << " blocks"; + external_audio_buffer_delay_verified_after_reset_ = true; + } + if (delay_ && *delay_ == delay) { + return false; + } + delay_ = delay; + + // Compute the total delay and limit the delay to the allowed range. + int total_delay = MapDelayToTotalDelay(*delay_); + total_delay = + std::min(MaxDelay(), static_cast(std::max(total_delay, 0))); + + // Apply the delay to the buffers. + ApplyTotalDelay(total_delay); + return true; +} + +void RenderDelayBufferImpl::SetAudioBufferDelay(int delay_ms) { + if (!external_audio_buffer_delay_) { + RTC_LOG_V(delay_log_level_) + << "Receiving a first externally reported audio buffer delay of " + << delay_ms << " ms."; + } + + // Convert delay from milliseconds to blocks (rounded down). + external_audio_buffer_delay_ = delay_ms / 4; +} + +bool RenderDelayBufferImpl::HasReceivedBufferDelay() { + return external_audio_buffer_delay_.has_value(); +} + +// Maps the externally computed delay to the delay used internally. +int RenderDelayBufferImpl::MapDelayToTotalDelay( + size_t external_delay_blocks) const { + const int latency_blocks = BufferLatency(); + return latency_blocks + static_cast(external_delay_blocks); +} + +// Returns the delay (not including call jitter). +int RenderDelayBufferImpl::ComputeDelay() const { + const int latency_blocks = BufferLatency(); + int internal_delay = spectra_.read >= spectra_.write + ? spectra_.read - spectra_.write + : spectra_.size + spectra_.read - spectra_.write; + + return internal_delay - latency_blocks; +} + +// Set the read indices according to the delay. +void RenderDelayBufferImpl::ApplyTotalDelay(int delay) { + RTC_LOG_V(delay_log_level_) + << "Applying total delay of " << delay << " blocks."; + blocks_.read = blocks_.OffsetIndex(blocks_.write, -delay); + spectra_.read = spectra_.OffsetIndex(spectra_.write, delay); + ffts_.read = ffts_.OffsetIndex(ffts_.write, delay); +} + +void RenderDelayBufferImpl::AlignFromExternalDelay() { + RTC_DCHECK(config_.delay.use_external_delay_estimator); + if (external_audio_buffer_delay_) { + const int64_t delay = render_call_counter_ - capture_call_counter_ + + *external_audio_buffer_delay_; + const int64_t delay_with_headroom = + delay - config_.delay.delay_headroom_samples / kBlockSize; + ApplyTotalDelay(delay_with_headroom); + } +} + +// Inserts a block into the render buffers. +void RenderDelayBufferImpl::InsertBlock( + const std::vector>>& block, + int previous_write) { + auto& b = blocks_; + auto& lr = low_rate_; + auto& ds = render_ds_; + auto& f = ffts_; + auto& s = spectra_; + const size_t num_bands = b.buffer[b.write].size(); + const size_t num_render_channels = b.buffer[b.write][0].size(); + RTC_DCHECK_EQ(block.size(), b.buffer[b.write].size()); + for (size_t band = 0; band < num_bands; ++band) { + RTC_DCHECK_EQ(block[band].size(), num_render_channels); + RTC_DCHECK_EQ(b.buffer[b.write][band].size(), num_render_channels); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + RTC_DCHECK_EQ(block[band][ch].size(), b.buffer[b.write][band][ch].size()); + std::copy(block[band][ch].begin(), block[band][ch].end(), + b.buffer[b.write][band][ch].begin()); + } + } + + if (render_linear_amplitude_gain_ != 1.f) { + for (size_t band = 0; band < num_bands; ++band) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + for (size_t k = 0; k < 64; ++k) { + b.buffer[b.write][band][ch][k] *= render_linear_amplitude_gain_; + } + } + } + } + + std::array downmixed_render; + render_mixer_.ProduceOutput(b.buffer[b.write][0], downmixed_render); + render_decimator_.Decimate(downmixed_render, ds); + data_dumper_->DumpWav("aec3_render_decimator_output", ds.size(), ds.data(), + 16000 / down_sampling_factor_, 1); + std::copy(ds.rbegin(), ds.rend(), lr.buffer.begin() + lr.write); + for (size_t channel = 0; channel < b.buffer[b.write][0].size(); ++channel) { + fft_.PaddedFft(b.buffer[b.write][0][channel], + b.buffer[previous_write][0][channel], + &f.buffer[f.write][channel]); + f.buffer[f.write][channel].Spectrum(optimization_, + s.buffer[s.write][channel]); + } +} + +bool RenderDelayBufferImpl::DetectActiveRender( + rtc::ArrayView x) const { + const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f); + return x_energy > (config_.render_levels.active_render_limit * + config_.render_levels.active_render_limit) * + kFftLengthBy2; +} + +bool RenderDelayBufferImpl::DetectExcessRenderBlocks() { + bool excess_render_detected = false; + const size_t latency_blocks = static_cast(BufferLatency()); + // The recently seen minimum latency in blocks. Should be close to 0. + min_latency_blocks_ = std::min(min_latency_blocks_, latency_blocks); + // After processing a configurable number of blocks the minimum latency is + // checked. + if (++excess_render_detection_counter_ >= + config_.buffering.excess_render_detection_interval_blocks) { + // If the minimum latency is not lower than the threshold there have been + // more render than capture frames. + excess_render_detected = min_latency_blocks_ > + config_.buffering.max_allowed_excess_render_blocks; + // Reset the counter and let the minimum latency be the current latency. + min_latency_blocks_ = latency_blocks; + excess_render_detection_counter_ = 0; + } + + data_dumper_->DumpRaw("aec3_latency_blocks", latency_blocks); + data_dumper_->DumpRaw("aec3_min_latency_blocks", min_latency_blocks_); + data_dumper_->DumpRaw("aec3_excess_render_detected", excess_render_detected); + return excess_render_detected; +} + +// Computes the latency in the buffer (the number of unread sub-blocks). +int RenderDelayBufferImpl::BufferLatency() const { + const DownsampledRenderBuffer& l = low_rate_; + int latency_samples = (l.buffer.size() + l.read - l.write) % l.buffer.size(); + int latency_blocks = latency_samples / sub_block_size_; + return latency_blocks; +} + +// Increments the write indices for the render buffers. +void RenderDelayBufferImpl::IncrementWriteIndices() { + low_rate_.UpdateWriteIndex(-sub_block_size_); + blocks_.IncWriteIndex(); + spectra_.DecWriteIndex(); + ffts_.DecWriteIndex(); +} + +// Increments the read indices of the low rate render buffers. +void RenderDelayBufferImpl::IncrementLowRateReadIndices() { + low_rate_.UpdateReadIndex(-sub_block_size_); +} + +// Increments the read indices for the render buffers. +void RenderDelayBufferImpl::IncrementReadIndices() { + if (blocks_.read != blocks_.write) { + blocks_.IncReadIndex(); + spectra_.DecReadIndex(); + ffts_.DecReadIndex(); + } +} + +// Checks for a render buffer overrun. +bool RenderDelayBufferImpl::RenderOverrun() { + return low_rate_.read == low_rate_.write || blocks_.read == blocks_.write; +} + +// Checks for a render buffer underrun. +bool RenderDelayBufferImpl::RenderUnderrun() { + return low_rate_.read == low_rate_.write; +} + +} // namespace + +RenderDelayBuffer* RenderDelayBuffer::Create(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels) { + return new RenderDelayBufferImpl(config, sample_rate_hz, num_render_channels); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/render_delay_buffer.h b/webrtc/modules/audio_processing/aec3/render_delay_buffer.h new file mode 100644 index 0000000..79ffc4d --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/render_delay_buffer.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_BUFFER_H_ + +#include + +#include + +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/aec3/render_buffer.h" + +namespace webrtc { + +// Class for buffering the incoming render blocks such that these may be +// extracted with a specified delay. +class RenderDelayBuffer { + public: + enum class BufferingEvent { + kNone, + kRenderUnderrun, + kRenderOverrun, + kApiCallSkew + }; + + static RenderDelayBuffer* Create(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_render_channels); + virtual ~RenderDelayBuffer() = default; + + // Resets the buffer alignment. + virtual void Reset() = 0; + + // Inserts a block into the buffer. + virtual BufferingEvent Insert( + const std::vector>>& block) = 0; + + // Updates the buffers one step based on the specified buffer delay. Returns + // an enum indicating whether there was a special event that occurred. + virtual BufferingEvent PrepareCaptureProcessing() = 0; + + // Called on capture blocks where PrepareCaptureProcessing is not called. + virtual void HandleSkippedCaptureProcessing() = 0; + + // Sets the buffer delay and returns a bool indicating whether the delay + // changed. + virtual bool AlignFromDelay(size_t delay) = 0; + + // Sets the buffer delay from the most recently reported external delay. + virtual void AlignFromExternalDelay() = 0; + + // Gets the buffer delay. + virtual size_t Delay() const = 0; + + // Gets the buffer delay. + virtual size_t MaxDelay() const = 0; + + // Returns the render buffer for the echo remover. + virtual RenderBuffer* GetRenderBuffer() = 0; + + // Returns the downsampled render buffer. + virtual const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const = 0; + + // Returns the maximum non calusal offset that can occur in the delay buffer. + static int DelayEstimatorOffset(const EchoCanceller3Config& config); + + // Provides an optional external estimate of the audio buffer delay. + virtual void SetAudioBufferDelay(int delay_ms) = 0; + + // Returns whether an external delay estimate has been reported via + // SetAudioBufferDelay. + virtual bool HasReceivedBufferDelay() = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/aec3/render_delay_controller.cc b/webrtc/modules/audio_processing/aec3/render_delay_controller.cc new file mode 100644 index 0000000..3677085 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/render_delay_controller.cc @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/render_delay_controller.h" + +#include + +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/aec3/echo_path_delay_estimator.h" +#include "modules/audio_processing/aec3/render_delay_controller_metrics.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +class RenderDelayControllerImpl final : public RenderDelayController { + public: + RenderDelayControllerImpl(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_capture_channels); + + RenderDelayControllerImpl() = delete; + RenderDelayControllerImpl(const RenderDelayControllerImpl&) = delete; + RenderDelayControllerImpl& operator=(const RenderDelayControllerImpl&) = + delete; + + ~RenderDelayControllerImpl() override; + void Reset(bool reset_delay_confidence) override; + void LogRenderCall() override; + absl::optional GetDelay( + const DownsampledRenderBuffer& render_buffer, + size_t render_delay_buffer_delay, + const std::vector>& capture) override; + bool HasClockdrift() const override; + + private: + static int instance_count_; + std::unique_ptr data_dumper_; + const int hysteresis_limit_blocks_; + const int delay_headroom_samples_; + absl::optional delay_; + EchoPathDelayEstimator delay_estimator_; + RenderDelayControllerMetrics metrics_; + absl::optional delay_samples_; + size_t capture_call_counter_ = 0; + int delay_change_counter_ = 0; + DelayEstimate::Quality last_delay_estimate_quality_; +}; + +DelayEstimate ComputeBufferDelay( + const absl::optional& current_delay, + int hysteresis_limit_blocks, + int delay_headroom_samples, + DelayEstimate estimated_delay) { + // Subtract delay headroom. + const int delay_with_headroom_samples = std::max( + static_cast(estimated_delay.delay) - delay_headroom_samples, 0); + + // Compute the buffer delay increase required to achieve the desired latency. + size_t new_delay_blocks = delay_with_headroom_samples >> kBlockSizeLog2; + + // Add hysteresis. + if (current_delay) { + size_t current_delay_blocks = current_delay->delay; + if (new_delay_blocks > current_delay_blocks && + new_delay_blocks <= current_delay_blocks + hysteresis_limit_blocks) { + new_delay_blocks = current_delay_blocks; + } + } + + DelayEstimate new_delay = estimated_delay; + new_delay.delay = new_delay_blocks; + return new_delay; +} + +int RenderDelayControllerImpl::instance_count_ = 0; + +RenderDelayControllerImpl::RenderDelayControllerImpl( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_capture_channels) + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + hysteresis_limit_blocks_( + static_cast(config.delay.hysteresis_limit_blocks)), + delay_headroom_samples_(config.delay.delay_headroom_samples), + delay_estimator_(data_dumper_.get(), config, num_capture_channels), + last_delay_estimate_quality_(DelayEstimate::Quality::kCoarse) { + RTC_DCHECK(ValidFullBandRate(sample_rate_hz)); + delay_estimator_.LogDelayEstimationProperties(sample_rate_hz, 0); +} + +RenderDelayControllerImpl::~RenderDelayControllerImpl() = default; + +void RenderDelayControllerImpl::Reset(bool reset_delay_confidence) { + delay_ = absl::nullopt; + delay_samples_ = absl::nullopt; + delay_estimator_.Reset(reset_delay_confidence); + delay_change_counter_ = 0; + if (reset_delay_confidence) { + last_delay_estimate_quality_ = DelayEstimate::Quality::kCoarse; + } +} + +void RenderDelayControllerImpl::LogRenderCall() {} + +absl::optional RenderDelayControllerImpl::GetDelay( + const DownsampledRenderBuffer& render_buffer, + size_t render_delay_buffer_delay, + const std::vector>& capture) { + RTC_DCHECK_EQ(kBlockSize, capture[0].size()); + ++capture_call_counter_; + + auto delay_samples = delay_estimator_.EstimateDelay(render_buffer, capture); + + if (delay_samples) { + if (!delay_samples_ || delay_samples->delay != delay_samples_->delay) { + delay_change_counter_ = 0; + } + if (delay_samples_) { + delay_samples_->blocks_since_last_change = + delay_samples_->delay == delay_samples->delay + ? delay_samples_->blocks_since_last_change + 1 + : 0; + delay_samples_->blocks_since_last_update = 0; + delay_samples_->delay = delay_samples->delay; + delay_samples_->quality = delay_samples->quality; + } else { + delay_samples_ = delay_samples; + } + } else { + if (delay_samples_) { + ++delay_samples_->blocks_since_last_change; + ++delay_samples_->blocks_since_last_update; + } + } + + if (delay_change_counter_ < 2 * kNumBlocksPerSecond) { + ++delay_change_counter_; + } + + if (delay_samples_) { + // Compute the render delay buffer delay. + const bool use_hysteresis = + last_delay_estimate_quality_ == DelayEstimate::Quality::kRefined && + delay_samples_->quality == DelayEstimate::Quality::kRefined; + delay_ = ComputeBufferDelay(delay_, + use_hysteresis ? hysteresis_limit_blocks_ : 0, + delay_headroom_samples_, *delay_samples_); + last_delay_estimate_quality_ = delay_samples_->quality; + } + + metrics_.Update(delay_samples_ ? absl::optional(delay_samples_->delay) + : absl::nullopt, + delay_ ? delay_->delay : 0, 0, delay_estimator_.Clockdrift()); + + data_dumper_->DumpRaw("aec3_render_delay_controller_delay", + delay_samples ? delay_samples->delay : 0); + data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay", + delay_ ? delay_->delay : 0); + + return delay_; +} + +bool RenderDelayControllerImpl::HasClockdrift() const { + return delay_estimator_.Clockdrift() != ClockdriftDetector::Level::kNone; +} + +} // namespace + +RenderDelayController* RenderDelayController::Create( + const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_capture_channels) { + return new RenderDelayControllerImpl(config, sample_rate_hz, + num_capture_channels); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/render_delay_controller.h b/webrtc/modules/audio_processing/aec3/render_delay_controller.h new file mode 100644 index 0000000..c45ab1f --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/render_delay_controller.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_H_ + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/delay_estimate.h" +#include "modules/audio_processing/aec3/downsampled_render_buffer.h" +#include "modules/audio_processing/aec3/render_delay_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +// Class for aligning the render and capture signal using a RenderDelayBuffer. +class RenderDelayController { + public: + static RenderDelayController* Create(const EchoCanceller3Config& config, + int sample_rate_hz, + size_t num_capture_channels); + virtual ~RenderDelayController() = default; + + // Resets the delay controller. If the delay confidence is reset, the reset + // behavior is as if the call is restarted. + virtual void Reset(bool reset_delay_confidence) = 0; + + // Logs a render call. + virtual void LogRenderCall() = 0; + + // Aligns the render buffer content with the capture signal. + virtual absl::optional GetDelay( + const DownsampledRenderBuffer& render_buffer, + size_t render_delay_buffer_delay, + const std::vector>& capture) = 0; + + // Returns true if clockdrift has been detected. + virtual bool HasClockdrift() const = 0; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_H_ diff --git a/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.cc b/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.cc new file mode 100644 index 0000000..582e033 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.cc @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/render_delay_controller_metrics.h" + +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +namespace { + +enum class DelayReliabilityCategory { + kNone, + kPoor, + kMedium, + kGood, + kExcellent, + kNumCategories +}; +enum class DelayChangesCategory { + kNone, + kFew, + kSeveral, + kMany, + kConstant, + kNumCategories +}; + +constexpr int kMaxSkewShiftCount = 20; + +} // namespace + +RenderDelayControllerMetrics::RenderDelayControllerMetrics() = default; + +void RenderDelayControllerMetrics::Update( + absl::optional delay_samples, + size_t buffer_delay_blocks, + absl::optional skew_shift_blocks, + ClockdriftDetector::Level clockdrift) { + ++call_counter_; + + if (!initial_update) { + size_t delay_blocks; + if (delay_samples) { + ++reliable_delay_estimate_counter_; + delay_blocks = (*delay_samples) / kBlockSize + 2; + } else { + delay_blocks = 0; + } + + if (delay_blocks != delay_blocks_) { + ++delay_change_counter_; + delay_blocks_ = delay_blocks; + } + + if (skew_shift_blocks) { + skew_shift_count_ = std::min(kMaxSkewShiftCount, skew_shift_count_); + } + } else if (++initial_call_counter_ == 5 * kNumBlocksPerSecond) { + initial_update = false; + } + + if (call_counter_ == kMetricsReportingIntervalBlocks) { + int value_to_report = static_cast(delay_blocks_); + value_to_report = std::min(124, value_to_report >> 1); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.EchoCanceller.EchoPathDelay", + value_to_report, 0, 124, 125); + + value_to_report = static_cast(buffer_delay_blocks + 2); + value_to_report = std::min(124, value_to_report >> 1); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.EchoCanceller.BufferDelay", + value_to_report, 0, 124, 125); + + DelayReliabilityCategory delay_reliability; + if (reliable_delay_estimate_counter_ == 0) { + delay_reliability = DelayReliabilityCategory::kNone; + } else if (reliable_delay_estimate_counter_ > (call_counter_ >> 1)) { + delay_reliability = DelayReliabilityCategory::kExcellent; + } else if (reliable_delay_estimate_counter_ > 100) { + delay_reliability = DelayReliabilityCategory::kGood; + } else if (reliable_delay_estimate_counter_ > 10) { + delay_reliability = DelayReliabilityCategory::kMedium; + } else { + delay_reliability = DelayReliabilityCategory::kPoor; + } + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.EchoCanceller.ReliableDelayEstimates", + static_cast(delay_reliability), + static_cast(DelayReliabilityCategory::kNumCategories)); + + DelayChangesCategory delay_changes; + if (delay_change_counter_ == 0) { + delay_changes = DelayChangesCategory::kNone; + } else if (delay_change_counter_ > 10) { + delay_changes = DelayChangesCategory::kConstant; + } else if (delay_change_counter_ > 5) { + delay_changes = DelayChangesCategory::kMany; + } else if (delay_change_counter_ > 2) { + delay_changes = DelayChangesCategory::kSeveral; + } else { + delay_changes = DelayChangesCategory::kFew; + } + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.EchoCanceller.DelayChanges", + static_cast(delay_changes), + static_cast(DelayChangesCategory::kNumCategories)); + + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.EchoCanceller.Clockdrift", static_cast(clockdrift), + static_cast(ClockdriftDetector::Level::kNumCategories)); + + metrics_reported_ = true; + call_counter_ = 0; + ResetMetrics(); + } else { + metrics_reported_ = false; + } + + if (!initial_update && ++skew_report_timer_ == 60 * kNumBlocksPerSecond) { + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.EchoCanceller.MaxSkewShiftCount", + skew_shift_count_, 0, kMaxSkewShiftCount, + kMaxSkewShiftCount + 1); + + skew_shift_count_ = 0; + skew_report_timer_ = 0; + } +} + +void RenderDelayControllerMetrics::ResetMetrics() { + delay_change_counter_ = 0; + reliable_delay_estimate_counter_ = 0; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.h b/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.h new file mode 100644 index 0000000..8c527a1 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/render_delay_controller_metrics.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_METRICS_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_METRICS_H_ + +#include + +#include "absl/types/optional.h" +#include "modules/audio_processing/aec3/clockdrift_detector.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +// Handles the reporting of metrics for the render delay controller. +class RenderDelayControllerMetrics { + public: + RenderDelayControllerMetrics(); + + // Updates the metric with new data. + void Update(absl::optional delay_samples, + size_t buffer_delay_blocks, + absl::optional skew_shift_blocks, + ClockdriftDetector::Level clockdrift); + + // Returns true if the metrics have just been reported, otherwise false. + bool MetricsReported() { return metrics_reported_; } + + private: + // Resets the metrics. + void ResetMetrics(); + + size_t delay_blocks_ = 0; + int reliable_delay_estimate_counter_ = 0; + int delay_change_counter_ = 0; + int call_counter_ = 0; + int skew_report_timer_ = 0; + int initial_call_counter_ = 0; + bool metrics_reported_ = false; + bool initial_update = true; + int skew_shift_count_ = 0; + + RTC_DISALLOW_COPY_AND_ASSIGN(RenderDelayControllerMetrics); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RENDER_DELAY_CONTROLLER_METRICS_H_ diff --git a/webrtc/modules/audio_processing/aec3/render_signal_analyzer.cc b/webrtc/modules/audio_processing/aec3/render_signal_analyzer.cc new file mode 100644 index 0000000..f570aac --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/render_signal_analyzer.cc @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/render_signal_analyzer.h" + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { +constexpr size_t kCounterThreshold = 5; + +// Identifies local bands with narrow characteristics. +void IdentifySmallNarrowBandRegions( + const RenderBuffer& render_buffer, + const absl::optional& delay_partitions, + std::array* narrow_band_counters) { + RTC_DCHECK(narrow_band_counters); + + if (!delay_partitions) { + narrow_band_counters->fill(0); + return; + } + + std::array channel_counters; + channel_counters.fill(0); + rtc::ArrayView> X2 = + render_buffer.Spectrum(*delay_partitions); + for (size_t ch = 0; ch < X2.size(); ++ch) { + for (size_t k = 1; k < kFftLengthBy2; ++k) { + if (X2[ch][k] > 3 * std::max(X2[ch][k - 1], X2[ch][k + 1])) { + ++channel_counters[k - 1]; + } + } + } + for (size_t k = 1; k < kFftLengthBy2; ++k) { + (*narrow_band_counters)[k - 1] = + channel_counters[k - 1] > 0 ? (*narrow_band_counters)[k - 1] + 1 : 0; + } +} + +// Identifies whether the signal has a single strong narrow-band component. +void IdentifyStrongNarrowBandComponent(const RenderBuffer& render_buffer, + int strong_peak_freeze_duration, + absl::optional* narrow_peak_band, + size_t* narrow_peak_counter) { + RTC_DCHECK(narrow_peak_band); + RTC_DCHECK(narrow_peak_counter); + if (*narrow_peak_band && + ++(*narrow_peak_counter) > + static_cast(strong_peak_freeze_duration)) { + *narrow_peak_band = absl::nullopt; + } + + const std::vector>>& x_latest = + render_buffer.Block(0); + float max_peak_level = 0.f; + for (size_t channel = 0; channel < x_latest[0].size(); ++channel) { + rtc::ArrayView X2_latest = + render_buffer.Spectrum(0)[channel]; + + // Identify the spectral peak. + const int peak_bin = + static_cast(std::max_element(X2_latest.begin(), X2_latest.end()) - + X2_latest.begin()); + + // Compute the level around the peak. + float non_peak_power = 0.f; + for (int k = std::max(0, peak_bin - 14); k < peak_bin - 4; ++k) { + non_peak_power = std::max(X2_latest[k], non_peak_power); + } + for (int k = peak_bin + 5; + k < std::min(peak_bin + 15, static_cast(kFftLengthBy2Plus1)); + ++k) { + non_peak_power = std::max(X2_latest[k], non_peak_power); + } + + // Assess the render signal strength. + auto result0 = std::minmax_element(x_latest[0][channel].begin(), + x_latest[0][channel].end()); + float max_abs = std::max(fabs(*result0.first), fabs(*result0.second)); + + if (x_latest.size() > 1) { + const auto result1 = std::minmax_element(x_latest[1][channel].begin(), + x_latest[1][channel].end()); + max_abs = + std::max(max_abs, static_cast(std::max( + fabs(*result1.first), fabs(*result1.second)))); + } + + // Detect whether the spectral peak has as strong narrowband nature. + const float peak_level = X2_latest[peak_bin]; + if (peak_bin > 0 && max_abs > 100 && peak_level > 100 * non_peak_power) { + // Store the strongest peak across channels. + if (peak_level > max_peak_level) { + max_peak_level = peak_level; + *narrow_peak_band = peak_bin; + *narrow_peak_counter = 0; + } + } + } +} + +} // namespace + +RenderSignalAnalyzer::RenderSignalAnalyzer(const EchoCanceller3Config& config) + : strong_peak_freeze_duration_(config.filter.refined.length_blocks) { + narrow_band_counters_.fill(0); +} +RenderSignalAnalyzer::~RenderSignalAnalyzer() = default; + +void RenderSignalAnalyzer::Update( + const RenderBuffer& render_buffer, + const absl::optional& delay_partitions) { + // Identify bands of narrow nature. + IdentifySmallNarrowBandRegions(render_buffer, delay_partitions, + &narrow_band_counters_); + + // Identify the presence of a strong narrow band. + IdentifyStrongNarrowBandComponent(render_buffer, strong_peak_freeze_duration_, + &narrow_peak_band_, &narrow_peak_counter_); +} + +void RenderSignalAnalyzer::MaskRegionsAroundNarrowBands( + std::array* v) const { + RTC_DCHECK(v); + + // Set v to zero around narrow band signal regions. + if (narrow_band_counters_[0] > kCounterThreshold) { + (*v)[1] = (*v)[0] = 0.f; + } + for (size_t k = 2; k < kFftLengthBy2 - 1; ++k) { + if (narrow_band_counters_[k - 1] > kCounterThreshold) { + (*v)[k - 2] = (*v)[k - 1] = (*v)[k] = (*v)[k + 1] = (*v)[k + 2] = 0.f; + } + } + if (narrow_band_counters_[kFftLengthBy2 - 2] > kCounterThreshold) { + (*v)[kFftLengthBy2] = (*v)[kFftLengthBy2 - 1] = 0.f; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/render_signal_analyzer.h b/webrtc/modules/audio_processing/aec3/render_signal_analyzer.h new file mode 100644 index 0000000..c7a3d8b --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/render_signal_analyzer.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_SIGNAL_ANALYZER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RENDER_SIGNAL_ANALYZER_H_ + +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +// Provides functionality for analyzing the properties of the render signal. +class RenderSignalAnalyzer { + public: + explicit RenderSignalAnalyzer(const EchoCanceller3Config& config); + ~RenderSignalAnalyzer(); + + // Updates the render signal analysis with the most recent render signal. + void Update(const RenderBuffer& render_buffer, + const absl::optional& delay_partitions); + + // Returns true if the render signal is poorly exciting. + bool PoorSignalExcitation() const { + RTC_DCHECK_LT(2, narrow_band_counters_.size()); + return std::any_of(narrow_band_counters_.begin(), + narrow_band_counters_.end(), + [](size_t a) { return a > 10; }); + } + + // Zeros the array around regions with narrow bands signal characteristics. + void MaskRegionsAroundNarrowBands( + std::array* v) const; + + absl::optional NarrowPeakBand() const { return narrow_peak_band_; } + + private: + const int strong_peak_freeze_duration_; + std::array narrow_band_counters_; + absl::optional narrow_peak_band_; + size_t narrow_peak_counter_; + + RTC_DISALLOW_COPY_AND_ASSIGN(RenderSignalAnalyzer); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RENDER_SIGNAL_ANALYZER_H_ diff --git a/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc new file mode 100644 index 0000000..46db233 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/residual_echo_estimator.h" + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/reverb_model.h" +#include "rtc_base/checks.h" +#include "system_wrappers/include/field_trial.h" + +namespace webrtc { +namespace { + +constexpr float kDefaultTransparentModeGain = 0.f; + +float GetTransparentModeGain() { + if (field_trial::IsEnabled( + "WebRTC-Aec3NoSuppressionInTransparentModeKillSwitch")) { + return 0.01f; + } else { + return kDefaultTransparentModeGain; + } +} + +float GetEarlyReflectionsDefaultModeGain( + const EchoCanceller3Config::EpStrength& config) { + if (field_trial::IsEnabled("WebRTC-Aec3UseLowEarlyReflectionsDefaultGain")) { + return 0.1f; + } + return config.default_gain; +} + +float GetLateReflectionsDefaultModeGain( + const EchoCanceller3Config::EpStrength& config) { + if (field_trial::IsEnabled("WebRTC-Aec3UseLowLateReflectionsDefaultGain")) { + return 0.1f; + } + return config.default_gain; +} + +// Computes the indexes that will be used for computing spectral power over +// the blocks surrounding the delay. +void GetRenderIndexesToAnalyze( + const SpectrumBuffer& spectrum_buffer, + const EchoCanceller3Config::EchoModel& echo_model, + int filter_delay_blocks, + int* idx_start, + int* idx_stop) { + RTC_DCHECK(idx_start); + RTC_DCHECK(idx_stop); + size_t window_start; + size_t window_end; + window_start = + std::max(0, filter_delay_blocks - + static_cast(echo_model.render_pre_window_size)); + window_end = filter_delay_blocks + + static_cast(echo_model.render_post_window_size); + *idx_start = spectrum_buffer.OffsetIndex(spectrum_buffer.read, window_start); + *idx_stop = spectrum_buffer.OffsetIndex(spectrum_buffer.read, window_end + 1); +} + +// Estimates the residual echo power based on the echo return loss enhancement +// (ERLE) and the linear power estimate. +void LinearEstimate( + rtc::ArrayView> S2_linear, + rtc::ArrayView> erle, + rtc::ArrayView> R2) { + RTC_DCHECK_EQ(S2_linear.size(), erle.size()); + RTC_DCHECK_EQ(S2_linear.size(), R2.size()); + + const size_t num_capture_channels = R2.size(); + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + RTC_DCHECK_LT(0.f, erle[ch][k]); + R2[ch][k] = S2_linear[ch][k] / erle[ch][k]; + } + } +} + +// Estimates the residual echo power based on an uncertainty estimate of the +// echo return loss enhancement (ERLE) and the linear power estimate. +void LinearEstimate( + rtc::ArrayView> S2_linear, + float erle_uncertainty, + rtc::ArrayView> R2) { + RTC_DCHECK_EQ(S2_linear.size(), R2.size()); + + const size_t num_capture_channels = R2.size(); + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + R2[ch][k] = S2_linear[ch][k] * erle_uncertainty; + } + } +} + +// Estimates the residual echo power based on the estimate of the echo path +// gain. +void NonLinearEstimate( + float echo_path_gain, + const std::array& X2, + rtc::ArrayView> R2) { + const size_t num_capture_channels = R2.size(); + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + R2[ch][k] = X2[k] * echo_path_gain; + } + } +} + +// Applies a soft noise gate to the echo generating power. +void ApplyNoiseGate(const EchoCanceller3Config::EchoModel& config, + rtc::ArrayView X2) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + if (config.noise_gate_power > X2[k]) { + X2[k] = std::max(0.f, X2[k] - config.noise_gate_slope * + (config.noise_gate_power - X2[k])); + } + } +} + +// Estimates the echo generating signal power as gated maximal power over a +// time window. +void EchoGeneratingPower(size_t num_render_channels, + const SpectrumBuffer& spectrum_buffer, + const EchoCanceller3Config::EchoModel& echo_model, + int filter_delay_blocks, + rtc::ArrayView X2) { + int idx_stop; + int idx_start; + GetRenderIndexesToAnalyze(spectrum_buffer, echo_model, filter_delay_blocks, + &idx_start, &idx_stop); + + std::fill(X2.begin(), X2.end(), 0.f); + if (num_render_channels == 1) { + for (int k = idx_start; k != idx_stop; k = spectrum_buffer.IncIndex(k)) { + for (size_t j = 0; j < kFftLengthBy2Plus1; ++j) { + X2[j] = std::max(X2[j], spectrum_buffer.buffer[k][/*channel=*/0][j]); + } + } + } else { + for (int k = idx_start; k != idx_stop; k = spectrum_buffer.IncIndex(k)) { + std::array render_power; + render_power.fill(0.f); + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const auto& channel_power = spectrum_buffer.buffer[k][ch]; + for (size_t j = 0; j < kFftLengthBy2Plus1; ++j) { + render_power[j] += channel_power[j]; + } + } + for (size_t j = 0; j < kFftLengthBy2Plus1; ++j) { + X2[j] = std::max(X2[j], render_power[j]); + } + } + } +} + +} // namespace + +ResidualEchoEstimator::ResidualEchoEstimator(const EchoCanceller3Config& config, + size_t num_render_channels) + : config_(config), + num_render_channels_(num_render_channels), + early_reflections_transparent_mode_gain_(GetTransparentModeGain()), + late_reflections_transparent_mode_gain_(GetTransparentModeGain()), + early_reflections_general_gain_( + GetEarlyReflectionsDefaultModeGain(config_.ep_strength)), + late_reflections_general_gain_( + GetLateReflectionsDefaultModeGain(config_.ep_strength)) { + Reset(); +} + +ResidualEchoEstimator::~ResidualEchoEstimator() = default; + +void ResidualEchoEstimator::Estimate( + const AecState& aec_state, + const RenderBuffer& render_buffer, + rtc::ArrayView> S2_linear, + rtc::ArrayView> Y2, + rtc::ArrayView> R2) { + RTC_DCHECK_EQ(R2.size(), Y2.size()); + RTC_DCHECK_EQ(R2.size(), S2_linear.size()); + + const size_t num_capture_channels = R2.size(); + + // Estimate the power of the stationary noise in the render signal. + UpdateRenderNoisePower(render_buffer); + + // Estimate the residual echo power. + if (aec_state.UsableLinearEstimate()) { + // When there is saturated echo, assume the same spectral content as is + // present in the microphone signal. + if (aec_state.SaturatedEcho()) { + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + std::copy(Y2[ch].begin(), Y2[ch].end(), R2[ch].begin()); + } + } else { + absl::optional erle_uncertainty = aec_state.ErleUncertainty(); + if (erle_uncertainty) { + LinearEstimate(S2_linear, *erle_uncertainty, R2); + } else { + LinearEstimate(S2_linear, aec_state.Erle(), R2); + } + } + + AddReverb(ReverbType::kLinear, aec_state, render_buffer, R2); + } else { + const float echo_path_gain = + GetEchoPathGain(aec_state, /*gain_for_early_reflections=*/true); + + // When there is saturated echo, assume the same spectral content as is + // present in the microphone signal. + if (aec_state.SaturatedEcho()) { + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + std::copy(Y2[ch].begin(), Y2[ch].end(), R2[ch].begin()); + } + } else { + // Estimate the echo generating signal power. + std::array X2; + EchoGeneratingPower(num_render_channels_, + render_buffer.GetSpectrumBuffer(), config_.echo_model, + aec_state.MinDirectPathFilterDelay(), X2); + if (!aec_state.UseStationarityProperties()) { + ApplyNoiseGate(config_.echo_model, X2); + } + + // Subtract the stationary noise power to avoid stationary noise causing + // excessive echo suppression. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + X2[k] -= config_.echo_model.stationary_gate_slope * X2_noise_floor_[k]; + X2[k] = std::max(0.f, X2[k]); + } + + NonLinearEstimate(echo_path_gain, X2, R2); + } + + if (config_.echo_model.model_reverb_in_nonlinear_mode && + !aec_state.TransparentModeActive()) { + AddReverb(ReverbType::kNonLinear, aec_state, render_buffer, R2); + } + } + + if (aec_state.UseStationarityProperties()) { + // Scale the echo according to echo audibility. + std::array residual_scaling; + aec_state.GetResidualEchoScaling(residual_scaling); + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + R2[ch][k] *= residual_scaling[k]; + } + } + } +} + +void ResidualEchoEstimator::Reset() { + echo_reverb_.Reset(); + X2_noise_floor_counter_.fill(config_.echo_model.noise_floor_hold); + X2_noise_floor_.fill(config_.echo_model.min_noise_floor_power); +} + +void ResidualEchoEstimator::UpdateRenderNoisePower( + const RenderBuffer& render_buffer) { + std::array render_power_data; + rtc::ArrayView> X2 = + render_buffer.Spectrum(0); + rtc::ArrayView render_power = + X2[/*channel=*/0]; + if (num_render_channels_ > 1) { + render_power_data.fill(0.f); + for (size_t ch = 0; ch < num_render_channels_; ++ch) { + const auto& channel_power = X2[ch]; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + render_power_data[k] += channel_power[k]; + } + } + render_power = render_power_data; + } + + // Estimate the stationary noise power in a minimum statistics manner. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + // Decrease rapidly. + if (render_power[k] < X2_noise_floor_[k]) { + X2_noise_floor_[k] = render_power[k]; + X2_noise_floor_counter_[k] = 0; + } else { + // Increase in a delayed, leaky manner. + if (X2_noise_floor_counter_[k] >= + static_cast(config_.echo_model.noise_floor_hold)) { + X2_noise_floor_[k] = std::max(X2_noise_floor_[k] * 1.1f, + config_.echo_model.min_noise_floor_power); + } else { + ++X2_noise_floor_counter_[k]; + } + } + } +} + +// Adds the estimated power of the reverb to the residual echo power. +void ResidualEchoEstimator::AddReverb( + ReverbType reverb_type, + const AecState& aec_state, + const RenderBuffer& render_buffer, + rtc::ArrayView> R2) { + const size_t num_capture_channels = R2.size(); + + // Choose reverb partition based on what type of echo power model is used. + const size_t first_reverb_partition = + reverb_type == ReverbType::kLinear + ? aec_state.FilterLengthBlocks() + 1 + : aec_state.MinDirectPathFilterDelay() + 1; + + // Compute render power for the reverb. + std::array render_power_data; + rtc::ArrayView> X2 = + render_buffer.Spectrum(first_reverb_partition); + rtc::ArrayView render_power = + X2[/*channel=*/0]; + if (num_render_channels_ > 1) { + render_power_data.fill(0.f); + for (size_t ch = 0; ch < num_render_channels_; ++ch) { + const auto& channel_power = X2[ch]; + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + render_power_data[k] += channel_power[k]; + } + } + render_power = render_power_data; + } + + // Update the reverb estimate. + if (reverb_type == ReverbType::kLinear) { + echo_reverb_.UpdateReverb(render_power, + aec_state.GetReverbFrequencyResponse(), + aec_state.ReverbDecay()); + } else { + const float echo_path_gain = + GetEchoPathGain(aec_state, /*gain_for_early_reflections=*/false); + echo_reverb_.UpdateReverbNoFreqShaping(render_power, echo_path_gain, + aec_state.ReverbDecay()); + } + + // Add the reverb power. + rtc::ArrayView reverb_power = + echo_reverb_.reverb(); + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + R2[ch][k] += reverb_power[k]; + } + } +} + +// Chooses the echo path gain to use. +float ResidualEchoEstimator::GetEchoPathGain( + const AecState& aec_state, + bool gain_for_early_reflections) const { + float gain_amplitude; + if (aec_state.TransparentModeActive()) { + gain_amplitude = gain_for_early_reflections + ? early_reflections_transparent_mode_gain_ + : late_reflections_transparent_mode_gain_; + } else { + gain_amplitude = gain_for_early_reflections + ? early_reflections_general_gain_ + : late_reflections_general_gain_; + } + return gain_amplitude * gain_amplitude; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h new file mode 100644 index 0000000..8fe7a84 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_RESIDUAL_ECHO_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_RESIDUAL_ECHO_ESTIMATOR_H_ + +#include +#include + +#include "absl/types/optional.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec_state.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/reverb_model.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +class ResidualEchoEstimator { + public: + ResidualEchoEstimator(const EchoCanceller3Config& config, + size_t num_render_channels); + ~ResidualEchoEstimator(); + + ResidualEchoEstimator(const ResidualEchoEstimator&) = delete; + ResidualEchoEstimator& operator=(const ResidualEchoEstimator&) = delete; + + void Estimate( + const AecState& aec_state, + const RenderBuffer& render_buffer, + rtc::ArrayView> S2_linear, + rtc::ArrayView> Y2, + rtc::ArrayView> R2); + + private: + enum class ReverbType { kLinear, kNonLinear }; + + // Resets the state. + void Reset(); + + // Updates estimate for the power of the stationary noise component in the + // render signal. + void UpdateRenderNoisePower(const RenderBuffer& render_buffer); + + // Adds the estimated unmodelled echo power to the residual echo power + // estimate. + void AddReverb(ReverbType reverb_type, + const AecState& aec_state, + const RenderBuffer& render_buffer, + rtc::ArrayView> R2); + + // Gets the echo path gain to apply. + float GetEchoPathGain(const AecState& aec_state, + bool gain_for_early_reflections) const; + + const EchoCanceller3Config config_; + const size_t num_render_channels_; + const float early_reflections_transparent_mode_gain_; + const float late_reflections_transparent_mode_gain_; + const float early_reflections_general_gain_; + const float late_reflections_general_gain_; + std::array X2_noise_floor_; + std::array X2_noise_floor_counter_; + ReverbModel echo_reverb_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_RESIDUAL_ECHO_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.cc b/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.cc new file mode 100644 index 0000000..f160b83 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.cc @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/reverb_decay_estimator.h" + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +constexpr int kEarlyReverbMinSizeBlocks = 3; +constexpr int kBlocksPerSection = 6; +// Linear regression approach assumes symmetric index around 0. +constexpr float kEarlyReverbFirstPointAtLinearRegressors = + -0.5f * kBlocksPerSection * kFftLengthBy2 + 0.5f; + +// Averages the values in a block of size kFftLengthBy2; +float BlockAverage(rtc::ArrayView v, size_t block_index) { + constexpr float kOneByFftLengthBy2 = 1.f / kFftLengthBy2; + const int i = block_index * kFftLengthBy2; + RTC_DCHECK_GE(v.size(), i + kFftLengthBy2); + const float sum = + std::accumulate(v.begin() + i, v.begin() + i + kFftLengthBy2, 0.f); + return sum * kOneByFftLengthBy2; +} + +// Analyzes the gain in a block. +void AnalyzeBlockGain(const std::array& h2, + float floor_gain, + float* previous_gain, + bool* block_adapting, + bool* decaying_gain) { + float gain = std::max(BlockAverage(h2, 0), 1e-32f); + *block_adapting = + *previous_gain > 1.1f * gain || *previous_gain < 0.9f * gain; + *decaying_gain = gain > floor_gain; + *previous_gain = gain; +} + +// Arithmetic sum of $2 \sum_{i=0.5}^{(N-1)/2}i^2$ calculated directly. +constexpr float SymmetricArithmetricSum(int N) { + return N * (N * N - 1.0f) * (1.f / 12.f); +} + +// Returns the peak energy of an impulse response. +float BlockEnergyPeak(rtc::ArrayView h, int peak_block) { + RTC_DCHECK_LE((peak_block + 1) * kFftLengthBy2, h.size()); + RTC_DCHECK_GE(peak_block, 0); + float peak_value = + *std::max_element(h.begin() + peak_block * kFftLengthBy2, + h.begin() + (peak_block + 1) * kFftLengthBy2, + [](float a, float b) { return a * a < b * b; }); + return peak_value * peak_value; +} + +// Returns the average energy of an impulse response block. +float BlockEnergyAverage(rtc::ArrayView h, int block_index) { + RTC_DCHECK_LE((block_index + 1) * kFftLengthBy2, h.size()); + RTC_DCHECK_GE(block_index, 0); + constexpr float kOneByFftLengthBy2 = 1.f / kFftLengthBy2; + const auto sum_of_squares = [](float a, float b) { return a + b * b; }; + return std::accumulate(h.begin() + block_index * kFftLengthBy2, + h.begin() + (block_index + 1) * kFftLengthBy2, 0.f, + sum_of_squares) * + kOneByFftLengthBy2; +} + +} // namespace + +ReverbDecayEstimator::ReverbDecayEstimator(const EchoCanceller3Config& config) + : filter_length_blocks_(config.filter.refined.length_blocks), + filter_length_coefficients_(GetTimeDomainLength(filter_length_blocks_)), + use_adaptive_echo_decay_(config.ep_strength.default_len < 0.f), + early_reverb_estimator_(config.filter.refined.length_blocks - + kEarlyReverbMinSizeBlocks), + late_reverb_start_(kEarlyReverbMinSizeBlocks), + late_reverb_end_(kEarlyReverbMinSizeBlocks), + previous_gains_(config.filter.refined.length_blocks, 0.f), + decay_(std::fabs(config.ep_strength.default_len)) { + RTC_DCHECK_GT(config.filter.refined.length_blocks, + static_cast(kEarlyReverbMinSizeBlocks)); +} + +ReverbDecayEstimator::~ReverbDecayEstimator() = default; + +void ReverbDecayEstimator::Update(rtc::ArrayView filter, + const absl::optional& filter_quality, + int filter_delay_blocks, + bool usable_linear_filter, + bool stationary_signal) { + const int filter_size = static_cast(filter.size()); + + if (stationary_signal) { + return; + } + + bool estimation_feasible = + filter_delay_blocks <= + filter_length_blocks_ - kEarlyReverbMinSizeBlocks - 1; + estimation_feasible = + estimation_feasible && filter_size == filter_length_coefficients_; + estimation_feasible = estimation_feasible && filter_delay_blocks > 0; + estimation_feasible = estimation_feasible && usable_linear_filter; + + if (!estimation_feasible) { + ResetDecayEstimation(); + return; + } + + if (!use_adaptive_echo_decay_) { + return; + } + + const float new_smoothing = filter_quality ? *filter_quality * 0.2f : 0.f; + smoothing_constant_ = std::max(new_smoothing, smoothing_constant_); + if (smoothing_constant_ == 0.f) { + return; + } + + if (block_to_analyze_ < filter_length_blocks_) { + // Analyze the filter and accumulate data for reverb estimation. + AnalyzeFilter(filter); + ++block_to_analyze_; + } else { + // When the filter is fully analyzed, estimate the reverb decay and reset + // the block_to_analyze_ counter. + EstimateDecay(filter, filter_delay_blocks); + } +} + +void ReverbDecayEstimator::ResetDecayEstimation() { + early_reverb_estimator_.Reset(); + late_reverb_decay_estimator_.Reset(0); + block_to_analyze_ = 0; + estimation_region_candidate_size_ = 0; + estimation_region_identified_ = false; + smoothing_constant_ = 0.f; + late_reverb_start_ = 0; + late_reverb_end_ = 0; +} + +void ReverbDecayEstimator::EstimateDecay(rtc::ArrayView filter, + int peak_block) { + auto& h = filter; + RTC_DCHECK_EQ(0, h.size() % kFftLengthBy2); + + // Reset the block analysis counter. + block_to_analyze_ = + std::min(peak_block + kEarlyReverbMinSizeBlocks, filter_length_blocks_); + + // To estimate the reverb decay, the energy of the first filter section must + // be substantially larger than the last. Also, the first filter section + // energy must not deviate too much from the max peak. + const float first_reverb_gain = BlockEnergyAverage(h, block_to_analyze_); + const size_t h_size_blocks = h.size() >> kFftLengthBy2Log2; + tail_gain_ = BlockEnergyAverage(h, h_size_blocks - 1); + float peak_energy = BlockEnergyPeak(h, peak_block); + const bool sufficient_reverb_decay = first_reverb_gain > 4.f * tail_gain_; + const bool valid_filter = + first_reverb_gain > 2.f * tail_gain_ && peak_energy < 100.f; + + // Estimate the size of the regions with early and late reflections. + const int size_early_reverb = early_reverb_estimator_.Estimate(); + const int size_late_reverb = + std::max(estimation_region_candidate_size_ - size_early_reverb, 0); + + // Only update the reverb decay estimate if the size of the identified late + // reverb is sufficiently large. + if (size_late_reverb >= 5) { + if (valid_filter && late_reverb_decay_estimator_.EstimateAvailable()) { + float decay = std::pow( + 2.0f, late_reverb_decay_estimator_.Estimate() * kFftLengthBy2); + constexpr float kMaxDecay = 0.95f; // ~1 sec min RT60. + constexpr float kMinDecay = 0.02f; // ~15 ms max RT60. + decay = std::max(.97f * decay_, decay); + decay = std::min(decay, kMaxDecay); + decay = std::max(decay, kMinDecay); + decay_ += smoothing_constant_ * (decay - decay_); + } + + // Update length of decay. Must have enough data (number of sections) in + // order to estimate decay rate. + late_reverb_decay_estimator_.Reset(size_late_reverb * kFftLengthBy2); + late_reverb_start_ = + peak_block + kEarlyReverbMinSizeBlocks + size_early_reverb; + late_reverb_end_ = + block_to_analyze_ + estimation_region_candidate_size_ - 1; + } else { + late_reverb_decay_estimator_.Reset(0); + late_reverb_start_ = 0; + late_reverb_end_ = 0; + } + + // Reset variables for the identification of the region for reverb decay + // estimation. + estimation_region_identified_ = !(valid_filter && sufficient_reverb_decay); + estimation_region_candidate_size_ = 0; + + // Stop estimation of the decay until another good filter is received. + smoothing_constant_ = 0.f; + + // Reset early reflections detector. + early_reverb_estimator_.Reset(); +} + +void ReverbDecayEstimator::AnalyzeFilter(rtc::ArrayView filter) { + auto h = rtc::ArrayView( + filter.begin() + block_to_analyze_ * kFftLengthBy2, kFftLengthBy2); + + // Compute squared filter coeffiecients for the block to analyze_; + std::array h2; + std::transform(h.begin(), h.end(), h2.begin(), [](float a) { return a * a; }); + + // Map out the region for estimating the reverb decay. + bool adapting; + bool above_noise_floor; + AnalyzeBlockGain(h2, tail_gain_, &previous_gains_[block_to_analyze_], + &adapting, &above_noise_floor); + + // Count consecutive number of "good" filter sections, where "good" means: + // 1) energy is above noise floor. + // 2) energy of current section has not changed too much from last check. + estimation_region_identified_ = + estimation_region_identified_ || adapting || !above_noise_floor; + if (!estimation_region_identified_) { + ++estimation_region_candidate_size_; + } + + // Accumulate data for reverb decay estimation and for the estimation of early + // reflections. + if (block_to_analyze_ <= late_reverb_end_) { + if (block_to_analyze_ >= late_reverb_start_) { + for (float h2_k : h2) { + float h2_log2 = FastApproxLog2f(h2_k + 1e-10); + late_reverb_decay_estimator_.Accumulate(h2_log2); + early_reverb_estimator_.Accumulate(h2_log2, smoothing_constant_); + } + } else { + for (float h2_k : h2) { + float h2_log2 = FastApproxLog2f(h2_k + 1e-10); + early_reverb_estimator_.Accumulate(h2_log2, smoothing_constant_); + } + } + } +} + +void ReverbDecayEstimator::Dump(ApmDataDumper* data_dumper) const { + data_dumper->DumpRaw("aec3_reverb_decay", decay_); + data_dumper->DumpRaw("aec3_reverb_tail_energy", tail_gain_); + data_dumper->DumpRaw("aec3_reverb_alpha", smoothing_constant_); + data_dumper->DumpRaw("aec3_num_reverb_decay_blocks", + late_reverb_end_ - late_reverb_start_); + data_dumper->DumpRaw("aec3_late_reverb_start", late_reverb_start_); + data_dumper->DumpRaw("aec3_late_reverb_end", late_reverb_end_); + early_reverb_estimator_.Dump(data_dumper); +} + +void ReverbDecayEstimator::LateReverbLinearRegressor::Reset( + int num_data_points) { + RTC_DCHECK_LE(0, num_data_points); + RTC_DCHECK_EQ(0, num_data_points % 2); + const int N = num_data_points; + nz_ = 0.f; + // Arithmetic sum of $2 \sum_{i=0.5}^{(N-1)/2}i^2$ calculated directly. + nn_ = SymmetricArithmetricSum(N); + // The linear regression approach assumes symmetric index around 0. + count_ = N > 0 ? -N * 0.5f + 0.5f : 0.f; + N_ = N; + n_ = 0; +} + +void ReverbDecayEstimator::LateReverbLinearRegressor::Accumulate(float z) { + nz_ += count_ * z; + ++count_; + ++n_; +} + +float ReverbDecayEstimator::LateReverbLinearRegressor::Estimate() { + RTC_DCHECK(EstimateAvailable()); + if (nn_ == 0.f) { + RTC_NOTREACHED(); + return 0.f; + } + return nz_ / nn_; +} + +ReverbDecayEstimator::EarlyReverbLengthEstimator::EarlyReverbLengthEstimator( + int max_blocks) + : numerators_smooth_(max_blocks - kBlocksPerSection, 0.f), + numerators_(numerators_smooth_.size(), 0.f), + coefficients_counter_(0) { + RTC_DCHECK_LE(0, max_blocks); +} + +ReverbDecayEstimator::EarlyReverbLengthEstimator:: + ~EarlyReverbLengthEstimator() = default; + +void ReverbDecayEstimator::EarlyReverbLengthEstimator::Reset() { + coefficients_counter_ = 0; + std::fill(numerators_.begin(), numerators_.end(), 0.f); + block_counter_ = 0; +} + +void ReverbDecayEstimator::EarlyReverbLengthEstimator::Accumulate( + float value, + float smoothing) { + // Each section is composed by kBlocksPerSection blocks and each section + // overlaps with the next one in (kBlocksPerSection - 1) blocks. For example, + // the first section covers the blocks [0:5], the second covers the blocks + // [1:6] and so on. As a result, for each value, kBlocksPerSection sections + // need to be updated. + int first_section_index = std::max(block_counter_ - kBlocksPerSection + 1, 0); + int last_section_index = + std::min(block_counter_, static_cast(numerators_.size() - 1)); + float x_value = static_cast(coefficients_counter_) + + kEarlyReverbFirstPointAtLinearRegressors; + const float value_to_inc = kFftLengthBy2 * value; + float value_to_add = + x_value * value + (block_counter_ - last_section_index) * value_to_inc; + for (int section = last_section_index; section >= first_section_index; + --section, value_to_add += value_to_inc) { + numerators_[section] += value_to_add; + } + + // Check if this update was the last coefficient of the current block. In that + // case, check if we are at the end of one of the sections and update the + // numerator of the linear regressor that is computed in such section. + if (++coefficients_counter_ == kFftLengthBy2) { + if (block_counter_ >= (kBlocksPerSection - 1)) { + size_t section = block_counter_ - (kBlocksPerSection - 1); + RTC_DCHECK_GT(numerators_.size(), section); + RTC_DCHECK_GT(numerators_smooth_.size(), section); + numerators_smooth_[section] += + smoothing * (numerators_[section] - numerators_smooth_[section]); + n_sections_ = section + 1; + } + ++block_counter_; + coefficients_counter_ = 0; + } +} + +// Estimates the size in blocks of the early reverb. The estimation is done by +// comparing the tilt that is estimated in each section. As an optimization +// detail and due to the fact that all the linear regressors that are computed +// shared the same denominator, the comparison of the tilts is done by a +// comparison of the numerator of the linear regressors. +int ReverbDecayEstimator::EarlyReverbLengthEstimator::Estimate() { + constexpr float N = kBlocksPerSection * kFftLengthBy2; + constexpr float nn = SymmetricArithmetricSum(N); + // numerator_11 refers to the quantity that the linear regressor needs in the + // numerator for getting a decay equal to 1.1 (which is not a decay). + // log2(1.1) * nn / kFftLengthBy2. + constexpr float numerator_11 = 0.13750352374993502f * nn / kFftLengthBy2; + // log2(0.8) * nn / kFftLengthBy2. + constexpr float numerator_08 = -0.32192809488736229f * nn / kFftLengthBy2; + constexpr int kNumSectionsToAnalyze = 9; + + if (n_sections_ < kNumSectionsToAnalyze) { + return 0; + } + + // Estimation of the blocks that correspond to early reverberations. The + // estimation is done by analyzing the impulse response. The portions of the + // impulse response whose energy is not decreasing over its coefficients are + // considered to be part of the early reverberations. Furthermore, the blocks + // where the energy is decreasing faster than what it does at the end of the + // impulse response are also considered to be part of the early + // reverberations. The estimation is limited to the first + // kNumSectionsToAnalyze sections. + + RTC_DCHECK_LE(n_sections_, numerators_smooth_.size()); + const float min_numerator_tail = + *std::min_element(numerators_smooth_.begin() + kNumSectionsToAnalyze, + numerators_smooth_.begin() + n_sections_); + int early_reverb_size_minus_1 = 0; + for (int k = 0; k < kNumSectionsToAnalyze; ++k) { + if ((numerators_smooth_[k] > numerator_11) || + (numerators_smooth_[k] < numerator_08 && + numerators_smooth_[k] < 0.9f * min_numerator_tail)) { + early_reverb_size_minus_1 = k; + } + } + + return early_reverb_size_minus_1 == 0 ? 0 : early_reverb_size_minus_1 + 1; +} + +void ReverbDecayEstimator::EarlyReverbLengthEstimator::Dump( + ApmDataDumper* data_dumper) const { + data_dumper->DumpRaw("aec3_er_acum_numerator", numerators_smooth_); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.h b/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.h new file mode 100644 index 0000000..3bb9b2b --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/reverb_decay_estimator.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_REVERB_DECAY_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_REVERB_DECAY_ESTIMATOR_H_ + +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" // kMaxAdaptiveFilter... + +namespace webrtc { + +class ApmDataDumper; +struct EchoCanceller3Config; + +// Class for estimating the decay of the late reverb. +class ReverbDecayEstimator { + public: + explicit ReverbDecayEstimator(const EchoCanceller3Config& config); + ~ReverbDecayEstimator(); + // Updates the decay estimate. + void Update(rtc::ArrayView filter, + const absl::optional& filter_quality, + int filter_delay_blocks, + bool usable_linear_filter, + bool stationary_signal); + // Returns the decay for the exponential model. + float Decay() const { return decay_; } + // Dumps debug data. + void Dump(ApmDataDumper* data_dumper) const; + + private: + void EstimateDecay(rtc::ArrayView filter, int peak_block); + void AnalyzeFilter(rtc::ArrayView filter); + + void ResetDecayEstimation(); + + // Class for estimating the decay of the late reverb from the linear filter. + class LateReverbLinearRegressor { + public: + // Resets the estimator to receive a specified number of data points. + void Reset(int num_data_points); + // Accumulates estimation data. + void Accumulate(float z); + // Estimates the decay. + float Estimate(); + // Returns whether an estimate is available. + bool EstimateAvailable() const { return n_ == N_ && N_ != 0; } + + public: + float nz_ = 0.f; + float nn_ = 0.f; + float count_ = 0.f; + int N_ = 0; + int n_ = 0; + }; + + // Class for identifying the length of the early reverb from the linear + // filter. For identifying the early reverberations, the impulse response is + // divided in sections and the tilt of each section is computed by a linear + // regressor. + class EarlyReverbLengthEstimator { + public: + explicit EarlyReverbLengthEstimator(int max_blocks); + ~EarlyReverbLengthEstimator(); + + // Resets the estimator. + void Reset(); + // Accumulates estimation data. + void Accumulate(float value, float smoothing); + // Estimates the size in blocks of the early reverb. + int Estimate(); + // Dumps debug data. + void Dump(ApmDataDumper* data_dumper) const; + + private: + std::vector numerators_smooth_; + std::vector numerators_; + int coefficients_counter_; + int block_counter_ = 0; + int n_sections_ = 0; + }; + + const int filter_length_blocks_; + const int filter_length_coefficients_; + const bool use_adaptive_echo_decay_; + LateReverbLinearRegressor late_reverb_decay_estimator_; + EarlyReverbLengthEstimator early_reverb_estimator_; + int late_reverb_start_; + int late_reverb_end_; + int block_to_analyze_ = 0; + int estimation_region_candidate_size_ = 0; + bool estimation_region_identified_ = false; + std::vector previous_gains_; + float decay_; + float tail_gain_ = 0.f; + float smoothing_constant_ = 0.f; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_REVERB_DECAY_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/reverb_frequency_response.cc b/webrtc/modules/audio_processing/aec3/reverb_frequency_response.cc new file mode 100644 index 0000000..f4bd91f --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/reverb_frequency_response.cc @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/reverb_frequency_response.h" + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Computes the ratio of the energies between the direct path and the tail. The +// energy is computed in the power spectrum domain discarding the DC +// contributions. +float AverageDecayWithinFilter( + rtc::ArrayView freq_resp_direct_path, + rtc::ArrayView freq_resp_tail) { + // Skipping the DC for the ratio computation + constexpr size_t kSkipBins = 1; + RTC_CHECK_EQ(freq_resp_direct_path.size(), freq_resp_tail.size()); + + float direct_path_energy = + std::accumulate(freq_resp_direct_path.begin() + kSkipBins, + freq_resp_direct_path.end(), 0.f); + + if (direct_path_energy == 0.f) { + return 0.f; + } + + float tail_energy = std::accumulate(freq_resp_tail.begin() + kSkipBins, + freq_resp_tail.end(), 0.f); + return tail_energy / direct_path_energy; +} + +} // namespace + +ReverbFrequencyResponse::ReverbFrequencyResponse() { + tail_response_.fill(0.f); +} +ReverbFrequencyResponse::~ReverbFrequencyResponse() = default; + +void ReverbFrequencyResponse::Update( + const std::vector>& + frequency_response, + int filter_delay_blocks, + const absl::optional& linear_filter_quality, + bool stationary_block) { + if (stationary_block || !linear_filter_quality) { + return; + } + + Update(frequency_response, filter_delay_blocks, *linear_filter_quality); +} + +void ReverbFrequencyResponse::Update( + const std::vector>& + frequency_response, + int filter_delay_blocks, + float linear_filter_quality) { + rtc::ArrayView freq_resp_tail( + frequency_response[frequency_response.size() - 1]); + + rtc::ArrayView freq_resp_direct_path( + frequency_response[filter_delay_blocks]); + + float average_decay = + AverageDecayWithinFilter(freq_resp_direct_path, freq_resp_tail); + + const float smoothing = 0.2f * linear_filter_quality; + average_decay_ += smoothing * (average_decay - average_decay_); + + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + tail_response_[k] = freq_resp_direct_path[k] * average_decay_; + } + + for (size_t k = 1; k < kFftLengthBy2; ++k) { + const float avg_neighbour = + 0.5f * (tail_response_[k - 1] + tail_response_[k + 1]); + tail_response_[k] = std::max(tail_response_[k], avg_neighbour); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/reverb_frequency_response.h b/webrtc/modules/audio_processing/aec3/reverb_frequency_response.h new file mode 100644 index 0000000..b164186 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/reverb_frequency_response.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_REVERB_FREQUENCY_RESPONSE_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_REVERB_FREQUENCY_RESPONSE_H_ + +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// Class for updating the frequency response for the reverb. +class ReverbFrequencyResponse { + public: + ReverbFrequencyResponse(); + ~ReverbFrequencyResponse(); + + // Updates the frequency response estimate of the reverb. + void Update(const std::vector>& + frequency_response, + int filter_delay_blocks, + const absl::optional& linear_filter_quality, + bool stationary_block); + + // Returns the estimated frequency response for the reverb. + rtc::ArrayView FrequencyResponse() const { + return tail_response_; + } + + private: + void Update(const std::vector>& + frequency_response, + int filter_delay_blocks, + float linear_filter_quality); + + float average_decay_ = 0.f; + std::array tail_response_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_REVERB_FREQUENCY_RESPONSE_H_ diff --git a/webrtc/modules/audio_processing/aec3/reverb_model.cc b/webrtc/modules/audio_processing/aec3/reverb_model.cc new file mode 100644 index 0000000..e4f3507 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/reverb_model.cc @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/reverb_model.h" + +#include + +#include +#include + +#include "api/array_view.h" + +namespace webrtc { + +ReverbModel::ReverbModel() { + Reset(); +} + +ReverbModel::~ReverbModel() = default; + +void ReverbModel::Reset() { + reverb_.fill(0.); +} + +void ReverbModel::UpdateReverbNoFreqShaping( + rtc::ArrayView power_spectrum, + float power_spectrum_scaling, + float reverb_decay) { + if (reverb_decay > 0) { + // Update the estimate of the reverberant power. + for (size_t k = 0; k < power_spectrum.size(); ++k) { + reverb_[k] = (reverb_[k] + power_spectrum[k] * power_spectrum_scaling) * + reverb_decay; + } + } +} + +void ReverbModel::UpdateReverb( + rtc::ArrayView power_spectrum, + rtc::ArrayView power_spectrum_scaling, + float reverb_decay) { + if (reverb_decay > 0) { + // Update the estimate of the reverberant power. + for (size_t k = 0; k < power_spectrum.size(); ++k) { + reverb_[k] = + (reverb_[k] + power_spectrum[k] * power_spectrum_scaling[k]) * + reverb_decay; + } + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/reverb_model.h b/webrtc/modules/audio_processing/aec3/reverb_model.h new file mode 100644 index 0000000..5ba5485 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/reverb_model.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// The ReverbModel class describes an exponential reverberant model +// that can be applied over power spectrums. +class ReverbModel { + public: + ReverbModel(); + ~ReverbModel(); + + // Resets the state. + void Reset(); + + // Returns the reverb. + rtc::ArrayView reverb() const { + return reverb_; + } + + // The methods UpdateReverbNoFreqShaping and UpdateReverb update the + // estimate of the reverberation contribution to an input/output power + // spectrum. Before applying the exponential reverberant model, the input + // power spectrum is pre-scaled. Use the method UpdateReverb when a different + // scaling should be applied per frequency and UpdateReverb_no_freq_shape if + // the same scaling should be used for all the frequencies. + void UpdateReverbNoFreqShaping(rtc::ArrayView power_spectrum, + float power_spectrum_scaling, + float reverb_decay); + + // Update the reverb based on new data. + void UpdateReverb(rtc::ArrayView power_spectrum, + rtc::ArrayView power_spectrum_scaling, + float reverb_decay); + + private: + + std::array reverb_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_H_ diff --git a/webrtc/modules/audio_processing/aec3/reverb_model_estimator.cc b/webrtc/modules/audio_processing/aec3/reverb_model_estimator.cc new file mode 100644 index 0000000..7174311 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/reverb_model_estimator.cc @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/reverb_model_estimator.h" + +namespace webrtc { + +ReverbModelEstimator::ReverbModelEstimator(const EchoCanceller3Config& config, + size_t num_capture_channels) + : reverb_decay_estimators_(num_capture_channels), + reverb_frequency_responses_(num_capture_channels) { + for (size_t ch = 0; ch < reverb_decay_estimators_.size(); ++ch) { + reverb_decay_estimators_[ch] = + std::make_unique(config); + } +} + +ReverbModelEstimator::~ReverbModelEstimator() = default; + +void ReverbModelEstimator::Update( + rtc::ArrayView> impulse_responses, + rtc::ArrayView>> + frequency_responses, + rtc::ArrayView> linear_filter_qualities, + rtc::ArrayView filter_delays_blocks, + const std::vector& usable_linear_estimates, + bool stationary_block) { + const size_t num_capture_channels = reverb_decay_estimators_.size(); + RTC_DCHECK_EQ(num_capture_channels, impulse_responses.size()); + RTC_DCHECK_EQ(num_capture_channels, frequency_responses.size()); + RTC_DCHECK_EQ(num_capture_channels, usable_linear_estimates.size()); + + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + // Estimate the frequency response for the reverb. + reverb_frequency_responses_[ch].Update( + frequency_responses[ch], filter_delays_blocks[ch], + linear_filter_qualities[ch], stationary_block); + + // Estimate the reverb decay, + reverb_decay_estimators_[ch]->Update( + impulse_responses[ch], linear_filter_qualities[ch], + filter_delays_blocks[ch], usable_linear_estimates[ch], + stationary_block); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/reverb_model_estimator.h b/webrtc/modules/audio_processing/aec3/reverb_model_estimator.h new file mode 100644 index 0000000..3b9971a --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/reverb_model_estimator.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_ESTIMATOR_H_ + +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" // kFftLengthBy2Plus1 +#include "modules/audio_processing/aec3/reverb_decay_estimator.h" +#include "modules/audio_processing/aec3/reverb_frequency_response.h" + +namespace webrtc { + +class ApmDataDumper; + +// Class for estimating the model parameters for the reverberant echo. +class ReverbModelEstimator { + public: + ReverbModelEstimator(const EchoCanceller3Config& config, + size_t num_capture_channels); + ~ReverbModelEstimator(); + + // Updates the estimates based on new data. + void Update( + rtc::ArrayView> impulse_responses, + rtc::ArrayView>> + frequency_responses, + rtc::ArrayView> linear_filter_qualities, + rtc::ArrayView filter_delays_blocks, + const std::vector& usable_linear_estimates, + bool stationary_block); + + // Returns the exponential decay of the reverberant echo. + // TODO(peah): Correct to properly support multiple channels. + float ReverbDecay() const { return reverb_decay_estimators_[0]->Decay(); } + + // Return the frequency response of the reverberant echo. + // TODO(peah): Correct to properly support multiple channels. + rtc::ArrayView GetReverbFrequencyResponse() const { + return reverb_frequency_responses_[0].FrequencyResponse(); + } + + // Dumps debug data. + void Dump(ApmDataDumper* data_dumper) const { + reverb_decay_estimators_[0]->Dump(data_dumper); + } + + private: + std::vector> reverb_decay_estimators_; + std::vector reverb_frequency_responses_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc b/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc new file mode 100644 index 0000000..5a3ba6c --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/signal_dependent_erle_estimator.h" + +#include +#include +#include + +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +namespace { + +constexpr std::array + kBandBoundaries = {1, 8, 16, 24, 32, 48, kFftLengthBy2Plus1}; + +std::array FormSubbandMap() { + std::array map_band_to_subband; + size_t subband = 1; + for (size_t k = 0; k < map_band_to_subband.size(); ++k) { + RTC_DCHECK_LT(subband, kBandBoundaries.size()); + if (k >= kBandBoundaries[subband]) { + subband++; + RTC_DCHECK_LT(k, kBandBoundaries[subband]); + } + map_band_to_subband[k] = subband - 1; + } + return map_band_to_subband; +} + +// Defines the size in blocks of the sections that are used for dividing the +// linear filter. The sections are split in a non-linear manner so that lower +// sections that typically represent the direct path have a larger resolution +// than the higher sections which typically represent more reverberant acoustic +// paths. +std::vector DefineFilterSectionSizes(size_t delay_headroom_blocks, + size_t num_blocks, + size_t num_sections) { + size_t filter_length_blocks = num_blocks - delay_headroom_blocks; + std::vector section_sizes(num_sections); + size_t remaining_blocks = filter_length_blocks; + size_t remaining_sections = num_sections; + size_t estimator_size = 2; + size_t idx = 0; + while (remaining_sections > 1 && + remaining_blocks > estimator_size * remaining_sections) { + RTC_DCHECK_LT(idx, section_sizes.size()); + section_sizes[idx] = estimator_size; + remaining_blocks -= estimator_size; + remaining_sections--; + estimator_size *= 2; + idx++; + } + + size_t last_groups_size = remaining_blocks / remaining_sections; + for (; idx < num_sections; idx++) { + section_sizes[idx] = last_groups_size; + } + section_sizes[num_sections - 1] += + remaining_blocks - last_groups_size * remaining_sections; + return section_sizes; +} + +// Forms the limits in blocks for each filter section. Those sections +// are used for analyzing the echo estimates and investigating which +// linear filter sections contribute most to the echo estimate energy. +std::vector SetSectionsBoundaries(size_t delay_headroom_blocks, + size_t num_blocks, + size_t num_sections) { + std::vector estimator_boundaries_blocks(num_sections + 1); + if (estimator_boundaries_blocks.size() == 2) { + estimator_boundaries_blocks[0] = 0; + estimator_boundaries_blocks[1] = num_blocks; + return estimator_boundaries_blocks; + } + RTC_DCHECK_GT(estimator_boundaries_blocks.size(), 2); + const std::vector section_sizes = + DefineFilterSectionSizes(delay_headroom_blocks, num_blocks, + estimator_boundaries_blocks.size() - 1); + + size_t idx = 0; + size_t current_size_block = 0; + RTC_DCHECK_EQ(section_sizes.size() + 1, estimator_boundaries_blocks.size()); + estimator_boundaries_blocks[0] = delay_headroom_blocks; + for (size_t k = delay_headroom_blocks; k < num_blocks; ++k) { + current_size_block++; + if (current_size_block >= section_sizes[idx]) { + idx = idx + 1; + if (idx == section_sizes.size()) { + break; + } + estimator_boundaries_blocks[idx] = k + 1; + current_size_block = 0; + } + } + estimator_boundaries_blocks[section_sizes.size()] = num_blocks; + return estimator_boundaries_blocks; +} + +std::array +SetMaxErleSubbands(float max_erle_l, float max_erle_h, size_t limit_subband_l) { + std::array max_erle; + std::fill(max_erle.begin(), max_erle.begin() + limit_subband_l, max_erle_l); + std::fill(max_erle.begin() + limit_subband_l, max_erle.end(), max_erle_h); + return max_erle; +} + +} // namespace + +SignalDependentErleEstimator::SignalDependentErleEstimator( + const EchoCanceller3Config& config, + size_t num_capture_channels) + : min_erle_(config.erle.min), + num_sections_(config.erle.num_sections), + num_blocks_(config.filter.refined.length_blocks), + delay_headroom_blocks_(config.delay.delay_headroom_samples / kBlockSize), + band_to_subband_(FormSubbandMap()), + max_erle_(SetMaxErleSubbands(config.erle.max_l, + config.erle.max_h, + band_to_subband_[kFftLengthBy2 / 2])), + section_boundaries_blocks_(SetSectionsBoundaries(delay_headroom_blocks_, + num_blocks_, + num_sections_)), + erle_(num_capture_channels), + S2_section_accum_( + num_capture_channels, + std::vector>(num_sections_)), + erle_estimators_( + num_capture_channels, + std::vector>(num_sections_)), + erle_ref_(num_capture_channels), + correction_factors_( + num_capture_channels, + std::vector>(num_sections_)), + num_updates_(num_capture_channels), + n_active_sections_(num_capture_channels) { + RTC_DCHECK_LE(num_sections_, num_blocks_); + RTC_DCHECK_GE(num_sections_, 1); + Reset(); +} + +SignalDependentErleEstimator::~SignalDependentErleEstimator() = default; + +void SignalDependentErleEstimator::Reset() { + for (size_t ch = 0; ch < erle_.size(); ++ch) { + erle_[ch].fill(min_erle_); + for (auto& erle_estimator : erle_estimators_[ch]) { + erle_estimator.fill(min_erle_); + } + erle_ref_[ch].fill(min_erle_); + for (auto& factor : correction_factors_[ch]) { + factor.fill(1.0f); + } + num_updates_[ch].fill(0); + n_active_sections_[ch].fill(0); + } +} + +// Updates the Erle estimate by analyzing the current input signals. It takes +// the render buffer and the filter frequency response in order to do an +// estimation of the number of sections of the linear filter that are needed +// for getting the majority of the energy in the echo estimate. Based on that +// number of sections, it updates the erle estimation by introducing a +// correction factor to the erle that is given as an input to this method. +void SignalDependentErleEstimator::Update( + const RenderBuffer& render_buffer, + rtc::ArrayView>> + filter_frequency_responses, + rtc::ArrayView X2, + rtc::ArrayView> Y2, + rtc::ArrayView> E2, + rtc::ArrayView> average_erle, + const std::vector& converged_filters) { + RTC_DCHECK_GT(num_sections_, 1); + + // Gets the number of filter sections that are needed for achieving 90 % + // of the power spectrum energy of the echo estimate. + ComputeNumberOfActiveFilterSections(render_buffer, + filter_frequency_responses); + + // Updates the correction factors that is used for correcting the erle and + // adapt it to the particular characteristics of the input signal. + UpdateCorrectionFactors(X2, Y2, E2, converged_filters); + + // Applies the correction factor to the input erle for getting a more refined + // erle estimation for the current input signal. + for (size_t ch = 0; ch < erle_.size(); ++ch) { + for (size_t k = 0; k < kFftLengthBy2; ++k) { + RTC_DCHECK_GT(correction_factors_[ch].size(), n_active_sections_[ch][k]); + float correction_factor = + correction_factors_[ch][n_active_sections_[ch][k]] + [band_to_subband_[k]]; + erle_[ch][k] = rtc::SafeClamp(average_erle[ch][k] * correction_factor, + min_erle_, max_erle_[band_to_subband_[k]]); + } + } +} + +void SignalDependentErleEstimator::Dump( + const std::unique_ptr& data_dumper) const { + for (auto& erle : erle_estimators_[0]) { + data_dumper->DumpRaw("aec3_all_erle", erle); + } + data_dumper->DumpRaw("aec3_ref_erle", erle_ref_[0]); + for (auto& factor : correction_factors_[0]) { + data_dumper->DumpRaw("aec3_erle_correction_factor", factor); + } +} + +// Estimates for each band the smallest number of sections in the filter that +// together constitute 90% of the estimated echo energy. +void SignalDependentErleEstimator::ComputeNumberOfActiveFilterSections( + const RenderBuffer& render_buffer, + rtc::ArrayView>> + filter_frequency_responses) { + RTC_DCHECK_GT(num_sections_, 1); + // Computes an approximation of the power spectrum if the filter would have + // been limited to a certain number of filter sections. + ComputeEchoEstimatePerFilterSection(render_buffer, + filter_frequency_responses); + // For each band, computes the number of filter sections that are needed for + // achieving the 90 % energy in the echo estimate. + ComputeActiveFilterSections(); +} + +void SignalDependentErleEstimator::UpdateCorrectionFactors( + rtc::ArrayView X2, + rtc::ArrayView> Y2, + rtc::ArrayView> E2, + const std::vector& converged_filters) { + for (size_t ch = 0; ch < converged_filters.size(); ++ch) { + if (converged_filters[ch]) { + constexpr float kX2BandEnergyThreshold = 44015068.0f; + constexpr float kSmthConstantDecreases = 0.1f; + constexpr float kSmthConstantIncreases = kSmthConstantDecreases / 2.f; + auto subband_powers = [](rtc::ArrayView power_spectrum, + rtc::ArrayView power_spectrum_subbands) { + for (size_t subband = 0; subband < kSubbands; ++subband) { + RTC_DCHECK_LE(kBandBoundaries[subband + 1], power_spectrum.size()); + power_spectrum_subbands[subband] = std::accumulate( + power_spectrum.begin() + kBandBoundaries[subband], + power_spectrum.begin() + kBandBoundaries[subband + 1], 0.f); + } + }; + + std::array X2_subbands, E2_subbands, Y2_subbands; + subband_powers(X2, X2_subbands); + subband_powers(E2[ch], E2_subbands); + subband_powers(Y2[ch], Y2_subbands); + std::array idx_subbands; + for (size_t subband = 0; subband < kSubbands; ++subband) { + // When aggregating the number of active sections in the filter for + // different bands we choose to take the minimum of all of them. As an + // example, if for one of the bands it is the direct path its refined + // contributor to the final echo estimate, we consider the direct path + // is as well the refined contributor for the subband that contains that + // particular band. That aggregate number of sections will be later used + // as the identifier of the erle estimator that needs to be updated. + RTC_DCHECK_LE(kBandBoundaries[subband + 1], + n_active_sections_[ch].size()); + idx_subbands[subband] = *std::min_element( + n_active_sections_[ch].begin() + kBandBoundaries[subband], + n_active_sections_[ch].begin() + kBandBoundaries[subband + 1]); + } + + std::array new_erle; + std::array is_erle_updated; + is_erle_updated.fill(false); + new_erle.fill(0.f); + for (size_t subband = 0; subband < kSubbands; ++subband) { + if (X2_subbands[subband] > kX2BandEnergyThreshold && + E2_subbands[subband] > 0) { + new_erle[subband] = Y2_subbands[subband] / E2_subbands[subband]; + RTC_DCHECK_GT(new_erle[subband], 0); + is_erle_updated[subband] = true; + ++num_updates_[ch][subband]; + } + } + + for (size_t subband = 0; subband < kSubbands; ++subband) { + const size_t idx = idx_subbands[subband]; + RTC_DCHECK_LT(idx, erle_estimators_[ch].size()); + float alpha = new_erle[subband] > erle_estimators_[ch][idx][subband] + ? kSmthConstantIncreases + : kSmthConstantDecreases; + alpha = static_cast(is_erle_updated[subband]) * alpha; + erle_estimators_[ch][idx][subband] += + alpha * (new_erle[subband] - erle_estimators_[ch][idx][subband]); + erle_estimators_[ch][idx][subband] = rtc::SafeClamp( + erle_estimators_[ch][idx][subband], min_erle_, max_erle_[subband]); + } + + for (size_t subband = 0; subband < kSubbands; ++subband) { + float alpha = new_erle[subband] > erle_ref_[ch][subband] + ? kSmthConstantIncreases + : kSmthConstantDecreases; + alpha = static_cast(is_erle_updated[subband]) * alpha; + erle_ref_[ch][subband] += + alpha * (new_erle[subband] - erle_ref_[ch][subband]); + erle_ref_[ch][subband] = rtc::SafeClamp(erle_ref_[ch][subband], + min_erle_, max_erle_[subband]); + } + + for (size_t subband = 0; subband < kSubbands; ++subband) { + constexpr int kNumUpdateThr = 50; + if (is_erle_updated[subband] && + num_updates_[ch][subband] > kNumUpdateThr) { + const size_t idx = idx_subbands[subband]; + RTC_DCHECK_GT(erle_ref_[ch][subband], 0.f); + // Computes the ratio between the erle that is updated using all the + // points and the erle that is updated only on signals that share the + // same number of active filter sections. + float new_correction_factor = + erle_estimators_[ch][idx][subband] / erle_ref_[ch][subband]; + + correction_factors_[ch][idx][subband] += + 0.1f * + (new_correction_factor - correction_factors_[ch][idx][subband]); + } + } + } + } +} + +void SignalDependentErleEstimator::ComputeEchoEstimatePerFilterSection( + const RenderBuffer& render_buffer, + rtc::ArrayView>> + filter_frequency_responses) { + const SpectrumBuffer& spectrum_render_buffer = + render_buffer.GetSpectrumBuffer(); + const size_t num_render_channels = spectrum_render_buffer.buffer[0].size(); + const size_t num_capture_channels = S2_section_accum_.size(); + const float one_by_num_render_channels = 1.f / num_render_channels; + + RTC_DCHECK_EQ(S2_section_accum_.size(), filter_frequency_responses.size()); + + for (size_t capture_ch = 0; capture_ch < num_capture_channels; ++capture_ch) { + RTC_DCHECK_EQ(S2_section_accum_[capture_ch].size() + 1, + section_boundaries_blocks_.size()); + size_t idx_render = render_buffer.Position(); + idx_render = spectrum_render_buffer.OffsetIndex( + idx_render, section_boundaries_blocks_[0]); + + for (size_t section = 0; section < num_sections_; ++section) { + std::array X2_section; + std::array H2_section; + X2_section.fill(0.f); + H2_section.fill(0.f); + const size_t block_limit = + std::min(section_boundaries_blocks_[section + 1], + filter_frequency_responses[capture_ch].size()); + for (size_t block = section_boundaries_blocks_[section]; + block < block_limit; ++block) { + for (size_t render_ch = 0; + render_ch < spectrum_render_buffer.buffer[idx_render].size(); + ++render_ch) { + for (size_t k = 0; k < X2_section.size(); ++k) { + X2_section[k] += + spectrum_render_buffer.buffer[idx_render][render_ch][k] * + one_by_num_render_channels; + } + } + std::transform(H2_section.begin(), H2_section.end(), + filter_frequency_responses[capture_ch][block].begin(), + H2_section.begin(), std::plus()); + idx_render = spectrum_render_buffer.IncIndex(idx_render); + } + + std::transform(X2_section.begin(), X2_section.end(), H2_section.begin(), + S2_section_accum_[capture_ch][section].begin(), + std::multiplies()); + } + + for (size_t section = 1; section < num_sections_; ++section) { + std::transform(S2_section_accum_[capture_ch][section - 1].begin(), + S2_section_accum_[capture_ch][section - 1].end(), + S2_section_accum_[capture_ch][section].begin(), + S2_section_accum_[capture_ch][section].begin(), + std::plus()); + } + } +} + +void SignalDependentErleEstimator::ComputeActiveFilterSections() { + for (size_t ch = 0; ch < n_active_sections_.size(); ++ch) { + std::fill(n_active_sections_[ch].begin(), n_active_sections_[ch].end(), 0); + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + size_t section = num_sections_; + float target = 0.9f * S2_section_accum_[ch][num_sections_ - 1][k]; + while (section > 0 && S2_section_accum_[ch][section - 1][k] >= target) { + n_active_sections_[ch][k] = --section; + } + } + } +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.h b/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.h new file mode 100644 index 0000000..498e922 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/signal_dependent_erle_estimator.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SIGNAL_DEPENDENT_ERLE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SIGNAL_DEPENDENT_ERLE_ESTIMATOR_H_ + +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +// This class estimates the dependency of the Erle to the input signal. By +// looking at the input signal, an estimation on whether the current echo +// estimate is due to the direct path or to a more reverberant one is performed. +// Once that estimation is done, it is possible to refine the average Erle that +// this class receive as an input. +class SignalDependentErleEstimator { + public: + SignalDependentErleEstimator(const EchoCanceller3Config& config, + size_t num_capture_channels); + + ~SignalDependentErleEstimator(); + + void Reset(); + + // Returns the Erle per frequency subband. + rtc::ArrayView> Erle() const { + return erle_; + } + + // Updates the Erle estimate. The Erle that is passed as an input is required + // to be an estimation of the average Erle achieved by the linear filter. + void Update( + const RenderBuffer& render_buffer, + rtc::ArrayView>> + filter_frequency_response, + rtc::ArrayView X2, + rtc::ArrayView> Y2, + rtc::ArrayView> E2, + rtc::ArrayView> average_erle, + const std::vector& converged_filters); + + void Dump(const std::unique_ptr& data_dumper) const; + + static constexpr size_t kSubbands = 6; + + private: + void ComputeNumberOfActiveFilterSections( + const RenderBuffer& render_buffer, + rtc::ArrayView>> + filter_frequency_responses); + + void UpdateCorrectionFactors( + rtc::ArrayView X2, + rtc::ArrayView> Y2, + rtc::ArrayView> E2, + const std::vector& converged_filters); + + void ComputeEchoEstimatePerFilterSection( + const RenderBuffer& render_buffer, + rtc::ArrayView>> + filter_frequency_responses); + + void ComputeActiveFilterSections(); + + const float min_erle_; + const size_t num_sections_; + const size_t num_blocks_; + const size_t delay_headroom_blocks_; + const std::array band_to_subband_; + const std::array max_erle_; + const std::vector section_boundaries_blocks_; + std::vector> erle_; + std::vector>> + S2_section_accum_; + std::vector>> erle_estimators_; + std::vector> erle_ref_; + std::vector>> correction_factors_; + std::vector> num_updates_; + std::vector> n_active_sections_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SIGNAL_DEPENDENT_ERLE_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/spectrum_buffer.cc b/webrtc/modules/audio_processing/aec3/spectrum_buffer.cc new file mode 100644 index 0000000..fe32ece --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/spectrum_buffer.cc @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/spectrum_buffer.h" + +#include + +namespace webrtc { + +SpectrumBuffer::SpectrumBuffer(size_t size, size_t num_channels) + : size(static_cast(size)), + buffer(size, + std::vector>(num_channels)) { + for (auto& channel : buffer) { + for (auto& c : channel) { + std::fill(c.begin(), c.end(), 0.f); + } + } +} + +SpectrumBuffer::~SpectrumBuffer() = default; + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/spectrum_buffer.h b/webrtc/modules/audio_processing/aec3/spectrum_buffer.h new file mode 100644 index 0000000..51e1317 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/spectrum_buffer.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SPECTRUM_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SPECTRUM_BUFFER_H_ + +#include + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Struct for bundling a circular buffer of one dimensional vector objects +// together with the read and write indices. +struct SpectrumBuffer { + SpectrumBuffer(size_t size, size_t num_channels); + ~SpectrumBuffer(); + + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; + } + + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index > 0 ? index - 1 : size - 1; + } + + int OffsetIndex(int index, int offset) const { + RTC_DCHECK_GE(size, offset); + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + RTC_DCHECK_GE(size + index + offset, 0); + return (size + index + offset) % size; + } + + void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } + void IncWriteIndex() { write = IncIndex(write); } + void DecWriteIndex() { write = DecIndex(write); } + void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); } + void IncReadIndex() { read = IncIndex(read); } + void DecReadIndex() { read = DecIndex(read); } + + const int size; + std::vector>> buffer; + int write = 0; + int read = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SPECTRUM_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/aec3/stationarity_estimator.cc b/webrtc/modules/audio_processing/aec3/stationarity_estimator.cc new file mode 100644 index 0000000..01628f3 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/stationarity_estimator.cc @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/stationarity_estimator.h" + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/spectrum_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" + +namespace webrtc { + +namespace { +constexpr float kMinNoisePower = 10.f; +constexpr int kHangoverBlocks = kNumBlocksPerSecond / 20; +constexpr int kNBlocksAverageInitPhase = 20; +constexpr int kNBlocksInitialPhase = kNumBlocksPerSecond * 2.; +} // namespace + +StationarityEstimator::StationarityEstimator() + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))) { + Reset(); +} + +StationarityEstimator::~StationarityEstimator() = default; + +void StationarityEstimator::Reset() { + noise_.Reset(); + hangovers_.fill(0); + stationarity_flags_.fill(false); +} + +// Update just the noise estimator. Usefull until the delay is known +void StationarityEstimator::UpdateNoiseEstimator( + rtc::ArrayView> spectrum) { + noise_.Update(spectrum); + data_dumper_->DumpRaw("aec3_stationarity_noise_spectrum", noise_.Spectrum()); + data_dumper_->DumpRaw("aec3_stationarity_is_block_stationary", + IsBlockStationary()); +} + +void StationarityEstimator::UpdateStationarityFlags( + const SpectrumBuffer& spectrum_buffer, + rtc::ArrayView render_reverb_contribution_spectrum, + int idx_current, + int num_lookahead) { + std::array indexes; + int num_lookahead_bounded = std::min(num_lookahead, kWindowLength - 1); + int idx = idx_current; + + if (num_lookahead_bounded < kWindowLength - 1) { + int num_lookback = (kWindowLength - 1) - num_lookahead_bounded; + idx = spectrum_buffer.OffsetIndex(idx_current, num_lookback); + } + // For estimating the stationarity properties of the current frame, the + // power for each band is accumulated for several consecutive spectra in the + // method EstimateBandStationarity. + // In order to avoid getting the indexes of the spectra for every band with + // its associated overhead, those indexes are stored in an array and then use + // when the estimation is done. + indexes[0] = idx; + for (size_t k = 1; k < indexes.size(); ++k) { + indexes[k] = spectrum_buffer.DecIndex(indexes[k - 1]); + } + RTC_DCHECK_EQ( + spectrum_buffer.DecIndex(indexes[kWindowLength - 1]), + spectrum_buffer.OffsetIndex(idx_current, -(num_lookahead_bounded + 1))); + + for (size_t k = 0; k < stationarity_flags_.size(); ++k) { + stationarity_flags_[k] = EstimateBandStationarity( + spectrum_buffer, render_reverb_contribution_spectrum, indexes, k); + } + UpdateHangover(); + SmoothStationaryPerFreq(); +} + +bool StationarityEstimator::IsBlockStationary() const { + float acum_stationarity = 0.f; + RTC_DCHECK_EQ(stationarity_flags_.size(), kFftLengthBy2Plus1); + for (size_t band = 0; band < stationarity_flags_.size(); ++band) { + bool st = IsBandStationary(band); + acum_stationarity += static_cast(st); + } + return ((acum_stationarity * (1.f / kFftLengthBy2Plus1)) > 0.75f); +} + +bool StationarityEstimator::EstimateBandStationarity( + const SpectrumBuffer& spectrum_buffer, + rtc::ArrayView average_reverb, + const std::array& indexes, + size_t band) const { + constexpr float kThrStationarity = 10.f; + float acum_power = 0.f; + const int num_render_channels = + static_cast(spectrum_buffer.buffer[0].size()); + const float one_by_num_channels = 1.f / num_render_channels; + for (auto idx : indexes) { + for (int ch = 0; ch < num_render_channels; ++ch) { + acum_power += spectrum_buffer.buffer[idx][ch][band] * one_by_num_channels; + } + } + acum_power += average_reverb[band]; + float noise = kWindowLength * GetStationarityPowerBand(band); + RTC_CHECK_LT(0.f, noise); + bool stationary = acum_power < kThrStationarity * noise; + data_dumper_->DumpRaw("aec3_stationarity_long_ratio", acum_power / noise); + return stationary; +} + +bool StationarityEstimator::AreAllBandsStationary() { + for (auto b : stationarity_flags_) { + if (!b) + return false; + } + return true; +} + +void StationarityEstimator::UpdateHangover() { + bool reduce_hangover = AreAllBandsStationary(); + for (size_t k = 0; k < stationarity_flags_.size(); ++k) { + if (!stationarity_flags_[k]) { + hangovers_[k] = kHangoverBlocks; + } else if (reduce_hangover) { + hangovers_[k] = std::max(hangovers_[k] - 1, 0); + } + } +} + +void StationarityEstimator::SmoothStationaryPerFreq() { + std::array all_ahead_stationary_smooth; + for (size_t k = 1; k < kFftLengthBy2Plus1 - 1; ++k) { + all_ahead_stationary_smooth[k] = stationarity_flags_[k - 1] && + stationarity_flags_[k] && + stationarity_flags_[k + 1]; + } + + all_ahead_stationary_smooth[0] = all_ahead_stationary_smooth[1]; + all_ahead_stationary_smooth[kFftLengthBy2Plus1 - 1] = + all_ahead_stationary_smooth[kFftLengthBy2Plus1 - 2]; + + stationarity_flags_ = all_ahead_stationary_smooth; +} + +int StationarityEstimator::instance_count_ = 0; + +StationarityEstimator::NoiseSpectrum::NoiseSpectrum() { + Reset(); +} + +StationarityEstimator::NoiseSpectrum::~NoiseSpectrum() = default; + +void StationarityEstimator::NoiseSpectrum::Reset() { + block_counter_ = 0; + noise_spectrum_.fill(kMinNoisePower); +} + +void StationarityEstimator::NoiseSpectrum::Update( + rtc::ArrayView> spectrum) { + RTC_DCHECK_LE(1, spectrum[0].size()); + const int num_render_channels = static_cast(spectrum.size()); + + std::array avg_spectrum_data; + rtc::ArrayView avg_spectrum; + if (num_render_channels == 1) { + avg_spectrum = spectrum[0]; + } else { + // For multiple channels, average the channel spectra before passing to the + // noise spectrum estimator. + avg_spectrum = avg_spectrum_data; + std::copy(spectrum[0].begin(), spectrum[0].end(), + avg_spectrum_data.begin()); + for (int ch = 1; ch < num_render_channels; ++ch) { + for (size_t k = 1; k < kFftLengthBy2Plus1; ++k) { + avg_spectrum_data[k] += spectrum[ch][k]; + } + } + + const float one_by_num_channels = 1.f / num_render_channels; + for (size_t k = 1; k < kFftLengthBy2Plus1; ++k) { + avg_spectrum_data[k] *= one_by_num_channels; + } + } + + ++block_counter_; + float alpha = GetAlpha(); + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + if (block_counter_ <= kNBlocksAverageInitPhase) { + noise_spectrum_[k] += (1.f / kNBlocksAverageInitPhase) * avg_spectrum[k]; + } else { + noise_spectrum_[k] = + UpdateBandBySmoothing(avg_spectrum[k], noise_spectrum_[k], alpha); + } + } +} + +float StationarityEstimator::NoiseSpectrum::GetAlpha() const { + constexpr float kAlpha = 0.004f; + constexpr float kAlphaInit = 0.04f; + constexpr float kTiltAlpha = (kAlphaInit - kAlpha) / kNBlocksInitialPhase; + + if (block_counter_ > (kNBlocksInitialPhase + kNBlocksAverageInitPhase)) { + return kAlpha; + } else { + return kAlphaInit - + kTiltAlpha * (block_counter_ - kNBlocksAverageInitPhase); + } +} + +float StationarityEstimator::NoiseSpectrum::UpdateBandBySmoothing( + float power_band, + float power_band_noise, + float alpha) const { + float power_band_noise_updated = power_band_noise; + if (power_band_noise < power_band) { + RTC_DCHECK_GT(power_band, 0.f); + float alpha_inc = alpha * (power_band_noise / power_band); + if (block_counter_ > kNBlocksInitialPhase) { + if (10.f * power_band_noise < power_band) { + alpha_inc *= 0.1f; + } + } + power_band_noise_updated += alpha_inc * (power_band - power_band_noise); + } else { + power_band_noise_updated += alpha * (power_band - power_band_noise); + power_band_noise_updated = + std::max(power_band_noise_updated, kMinNoisePower); + } + return power_band_noise_updated; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/stationarity_estimator.h b/webrtc/modules/audio_processing/aec3/stationarity_estimator.h new file mode 100644 index 0000000..6f7ad40 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/stationarity_estimator.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_STATIONARITY_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_STATIONARITY_ESTIMATOR_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" // kFftLengthBy2Plus1... +#include "modules/audio_processing/aec3/reverb_model.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +class ApmDataDumper; +struct SpectrumBuffer; + +class StationarityEstimator { + public: + StationarityEstimator(); + ~StationarityEstimator(); + + // Reset the stationarity estimator. + void Reset(); + + // Update just the noise estimator. Usefull until the delay is known + void UpdateNoiseEstimator( + rtc::ArrayView> spectrum); + + // Update the flag indicating whether this current frame is stationary. For + // getting a more robust estimation, it looks at future and/or past frames. + void UpdateStationarityFlags( + const SpectrumBuffer& spectrum_buffer, + rtc::ArrayView render_reverb_contribution_spectrum, + int idx_current, + int num_lookahead); + + // Returns true if the current band is stationary. + bool IsBandStationary(size_t band) const { + return stationarity_flags_[band] && (hangovers_[band] == 0); + } + + // Returns true if the current block is estimated as stationary. + bool IsBlockStationary() const; + + private: + static constexpr int kWindowLength = 13; + // Returns the power of the stationary noise spectrum at a band. + float GetStationarityPowerBand(size_t k) const { return noise_.Power(k); } + + // Get an estimation of the stationarity for the current band by looking + // at the past/present/future available data. + bool EstimateBandStationarity(const SpectrumBuffer& spectrum_buffer, + rtc::ArrayView average_reverb, + const std::array& indexes, + size_t band) const; + + // True if all bands at the current point are stationary. + bool AreAllBandsStationary(); + + // Update the hangover depending on the stationary status of the current + // frame. + void UpdateHangover(); + + // Smooth the stationarity detection by looking at neighbouring frequency + // bands. + void SmoothStationaryPerFreq(); + + class NoiseSpectrum { + public: + NoiseSpectrum(); + ~NoiseSpectrum(); + + // Reset the noise power spectrum estimate state. + void Reset(); + + // Update the noise power spectrum with a new frame. + void Update( + rtc::ArrayView> spectrum); + + // Get the noise estimation power spectrum. + rtc::ArrayView Spectrum() const { return noise_spectrum_; } + + // Get the noise power spectrum at a certain band. + float Power(size_t band) const { + RTC_DCHECK_LT(band, noise_spectrum_.size()); + return noise_spectrum_[band]; + } + + private: + // Get the update coefficient to be used for the current frame. + float GetAlpha() const; + + // Update the noise power spectrum at a certain band with a new frame. + float UpdateBandBySmoothing(float power_band, + float power_band_noise, + float alpha) const; + std::array noise_spectrum_; + size_t block_counter_; + }; + + static int instance_count_; + std::unique_ptr data_dumper_; + NoiseSpectrum noise_; + std::array hangovers_; + std::array stationarity_flags_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_STATIONARITY_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/subband_erle_estimator.cc b/webrtc/modules/audio_processing/aec3/subband_erle_estimator.cc new file mode 100644 index 0000000..6c00091 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/subband_erle_estimator.cc @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/subband_erle_estimator.h" + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "system_wrappers/include/field_trial.h" + +namespace webrtc { + +namespace { + +constexpr float kX2BandEnergyThreshold = 44015068.0f; +constexpr int kBlocksToHoldErle = 100; +constexpr int kBlocksForOnsetDetection = kBlocksToHoldErle + 150; +constexpr int kPointsToAccumulate = 6; + +std::array SetMaxErleBands(float max_erle_l, + float max_erle_h) { + std::array max_erle; + std::fill(max_erle.begin(), max_erle.begin() + kFftLengthBy2 / 2, max_erle_l); + std::fill(max_erle.begin() + kFftLengthBy2 / 2, max_erle.end(), max_erle_h); + return max_erle; +} + +bool EnableMinErleDuringOnsets() { + return !field_trial::IsEnabled("WebRTC-Aec3MinErleDuringOnsetsKillSwitch"); +} + +} // namespace + +SubbandErleEstimator::SubbandErleEstimator(const EchoCanceller3Config& config, + size_t num_capture_channels) + : use_onset_detection_(config.erle.onset_detection), + min_erle_(config.erle.min), + max_erle_(SetMaxErleBands(config.erle.max_l, config.erle.max_h)), + use_min_erle_during_onsets_(EnableMinErleDuringOnsets()), + accum_spectra_(num_capture_channels), + erle_(num_capture_channels), + erle_onsets_(num_capture_channels), + coming_onset_(num_capture_channels), + hold_counters_(num_capture_channels) { + Reset(); +} + +SubbandErleEstimator::~SubbandErleEstimator() = default; + +void SubbandErleEstimator::Reset() { + for (auto& erle : erle_) { + erle.fill(min_erle_); + } + for (size_t ch = 0; ch < erle_onsets_.size(); ++ch) { + erle_onsets_[ch].fill(min_erle_); + coming_onset_[ch].fill(true); + hold_counters_[ch].fill(0); + } + ResetAccumulatedSpectra(); +} + +void SubbandErleEstimator::Update( + rtc::ArrayView X2, + rtc::ArrayView> Y2, + rtc::ArrayView> E2, + const std::vector& converged_filters) { + UpdateAccumulatedSpectra(X2, Y2, E2, converged_filters); + UpdateBands(converged_filters); + + if (use_onset_detection_) { + DecreaseErlePerBandForLowRenderSignals(); + } + + for (auto& erle : erle_) { + erle[0] = erle[1]; + erle[kFftLengthBy2] = erle[kFftLengthBy2 - 1]; + } +} + +void SubbandErleEstimator::Dump( + const std::unique_ptr& data_dumper) const { + data_dumper->DumpRaw("aec3_erle_onset", ErleOnsets()[0]); +} + +void SubbandErleEstimator::UpdateBands( + const std::vector& converged_filters) { + const int num_capture_channels = static_cast(accum_spectra_.Y2.size()); + for (int ch = 0; ch < num_capture_channels; ++ch) { + // Note that the use of the converged_filter flag already imposed + // a minimum of the erle that can be estimated as that flag would + // be false if the filter is performing poorly. + if (!converged_filters[ch]) { + continue; + } + + std::array new_erle; + std::array is_erle_updated; + is_erle_updated.fill(false); + + for (size_t k = 1; k < kFftLengthBy2; ++k) { + if (accum_spectra_.num_points[ch] == kPointsToAccumulate && + accum_spectra_.E2[ch][k] > 0.f) { + new_erle[k] = accum_spectra_.Y2[ch][k] / accum_spectra_.E2[ch][k]; + is_erle_updated[k] = true; + } + } + + if (use_onset_detection_) { + for (size_t k = 1; k < kFftLengthBy2; ++k) { + if (is_erle_updated[k] && !accum_spectra_.low_render_energy[ch][k]) { + if (coming_onset_[ch][k]) { + coming_onset_[ch][k] = false; + if (!use_min_erle_during_onsets_) { + float alpha = new_erle[k] < erle_onsets_[ch][k] ? 0.3f : 0.15f; + erle_onsets_[ch][k] = rtc::SafeClamp( + erle_onsets_[ch][k] + + alpha * (new_erle[k] - erle_onsets_[ch][k]), + min_erle_, max_erle_[k]); + } + } + hold_counters_[ch][k] = kBlocksForOnsetDetection; + } + } + } + + for (size_t k = 1; k < kFftLengthBy2; ++k) { + if (is_erle_updated[k]) { + float alpha = 0.05f; + if (new_erle[k] < erle_[ch][k]) { + alpha = accum_spectra_.low_render_energy[ch][k] ? 0.f : 0.1f; + } + erle_[ch][k] = + rtc::SafeClamp(erle_[ch][k] + alpha * (new_erle[k] - erle_[ch][k]), + min_erle_, max_erle_[k]); + } + } + } +} + +void SubbandErleEstimator::DecreaseErlePerBandForLowRenderSignals() { + const int num_capture_channels = static_cast(accum_spectra_.Y2.size()); + for (int ch = 0; ch < num_capture_channels; ++ch) { + for (size_t k = 1; k < kFftLengthBy2; ++k) { + --hold_counters_[ch][k]; + if (hold_counters_[ch][k] <= + (kBlocksForOnsetDetection - kBlocksToHoldErle)) { + if (erle_[ch][k] > erle_onsets_[ch][k]) { + erle_[ch][k] = std::max(erle_onsets_[ch][k], 0.97f * erle_[ch][k]); + RTC_DCHECK_LE(min_erle_, erle_[ch][k]); + } + if (hold_counters_[ch][k] <= 0) { + coming_onset_[ch][k] = true; + hold_counters_[ch][k] = 0; + } + } + } + } +} + +void SubbandErleEstimator::ResetAccumulatedSpectra() { + for (size_t ch = 0; ch < erle_onsets_.size(); ++ch) { + accum_spectra_.Y2[ch].fill(0.f); + accum_spectra_.E2[ch].fill(0.f); + accum_spectra_.num_points[ch] = 0; + accum_spectra_.low_render_energy[ch].fill(false); + } +} + +void SubbandErleEstimator::UpdateAccumulatedSpectra( + rtc::ArrayView X2, + rtc::ArrayView> Y2, + rtc::ArrayView> E2, + const std::vector& converged_filters) { + auto& st = accum_spectra_; + RTC_DCHECK_EQ(st.E2.size(), E2.size()); + RTC_DCHECK_EQ(st.E2.size(), E2.size()); + const int num_capture_channels = static_cast(Y2.size()); + for (int ch = 0; ch < num_capture_channels; ++ch) { + // Note that the use of the converged_filter flag already imposed + // a minimum of the erle that can be estimated as that flag would + // be false if the filter is performing poorly. + if (!converged_filters[ch]) { + continue; + } + + if (st.num_points[ch] == kPointsToAccumulate) { + st.num_points[ch] = 0; + st.Y2[ch].fill(0.f); + st.E2[ch].fill(0.f); + st.low_render_energy[ch].fill(false); + } + + std::transform(Y2[ch].begin(), Y2[ch].end(), st.Y2[ch].begin(), + st.Y2[ch].begin(), std::plus()); + std::transform(E2[ch].begin(), E2[ch].end(), st.E2[ch].begin(), + st.E2[ch].begin(), std::plus()); + + for (size_t k = 0; k < X2.size(); ++k) { + st.low_render_energy[ch][k] = + st.low_render_energy[ch][k] || X2[k] < kX2BandEnergyThreshold; + } + + ++st.num_points[ch]; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/subband_erle_estimator.h b/webrtc/modules/audio_processing/aec3/subband_erle_estimator.h new file mode 100644 index 0000000..90363e0 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/subband_erle_estimator.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_ERLE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_ERLE_ESTIMATOR_H_ + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" + +namespace webrtc { + +// Estimates the echo return loss enhancement for each frequency subband. +class SubbandErleEstimator { + public: + SubbandErleEstimator(const EchoCanceller3Config& config, + size_t num_capture_channels); + ~SubbandErleEstimator(); + + // Resets the ERLE estimator. + void Reset(); + + // Updates the ERLE estimate. + void Update(rtc::ArrayView X2, + rtc::ArrayView> Y2, + rtc::ArrayView> E2, + const std::vector& converged_filters); + + // Returns the ERLE estimate. + rtc::ArrayView> Erle() const { + return erle_; + } + + // Returns the ERLE estimate at onsets (only used for testing). + rtc::ArrayView> ErleOnsets() + const { + return erle_onsets_; + } + + void Dump(const std::unique_ptr& data_dumper) const; + + private: + struct AccumulatedSpectra { + explicit AccumulatedSpectra(size_t num_capture_channels) + : Y2(num_capture_channels), + E2(num_capture_channels), + low_render_energy(num_capture_channels), + num_points(num_capture_channels) {} + std::vector> Y2; + std::vector> E2; + std::vector> low_render_energy; + std::vector num_points; + }; + + void UpdateAccumulatedSpectra( + rtc::ArrayView X2, + rtc::ArrayView> Y2, + rtc::ArrayView> E2, + const std::vector& converged_filters); + + void ResetAccumulatedSpectra(); + + void UpdateBands(const std::vector& converged_filters); + void DecreaseErlePerBandForLowRenderSignals(); + + const bool use_onset_detection_; + const float min_erle_; + const std::array max_erle_; + const bool use_min_erle_during_onsets_; + AccumulatedSpectra accum_spectra_; + std::vector> erle_; + std::vector> erle_onsets_; + std::vector> coming_onset_; + std::vector> hold_counters_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_ERLE_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/subband_nearend_detector.cc b/webrtc/modules/audio_processing/aec3/subband_nearend_detector.cc new file mode 100644 index 0000000..2aa400c --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/subband_nearend_detector.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/aec3/subband_nearend_detector.h" + +#include + +namespace webrtc { +SubbandNearendDetector::SubbandNearendDetector( + const EchoCanceller3Config::Suppressor::SubbandNearendDetection& config, + size_t num_capture_channels) + : config_(config), + num_capture_channels_(num_capture_channels), + nearend_smoothers_(num_capture_channels_, + aec3::MovingAverage(kFftLengthBy2Plus1, + config_.nearend_average_blocks)), + one_over_subband_length1_( + 1.f / (config_.subband1.high - config_.subband1.low + 1)), + one_over_subband_length2_( + 1.f / (config_.subband2.high - config_.subband2.low + 1)) {} + +void SubbandNearendDetector::Update( + rtc::ArrayView> + nearend_spectrum, + rtc::ArrayView> + residual_echo_spectrum, + rtc::ArrayView> + comfort_noise_spectrum, + bool initial_state) { + nearend_state_ = false; + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + const std::array& noise = + comfort_noise_spectrum[ch]; + std::array nearend; + nearend_smoothers_[ch].Average(nearend_spectrum[ch], nearend); + + // Noise power of the first region. + float noise_power = + std::accumulate(noise.begin() + config_.subband1.low, + noise.begin() + config_.subband1.high + 1, 0.f) * + one_over_subband_length1_; + + // Nearend power of the first region. + float nearend_power_subband1 = + std::accumulate(nearend.begin() + config_.subband1.low, + nearend.begin() + config_.subband1.high + 1, 0.f) * + one_over_subband_length1_; + + // Nearend power of the second region. + float nearend_power_subband2 = + std::accumulate(nearend.begin() + config_.subband2.low, + nearend.begin() + config_.subband2.high + 1, 0.f) * + one_over_subband_length2_; + + // One channel is sufficient to trigger nearend state. + nearend_state_ = + nearend_state_ || + (nearend_power_subband1 < + config_.nearend_threshold * nearend_power_subband2 && + (nearend_power_subband1 > config_.snr_threshold * noise_power)); + } +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/subband_nearend_detector.h b/webrtc/modules/audio_processing/aec3/subband_nearend_detector.h new file mode 100644 index 0000000..8357edb --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/subband_nearend_detector.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_NEAREND_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_NEAREND_DETECTOR_H_ + +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/nearend_detector.h" + +namespace webrtc { +// Class for selecting whether the suppressor is in the nearend or echo state. +class SubbandNearendDetector : public NearendDetector { + public: + SubbandNearendDetector( + const EchoCanceller3Config::Suppressor::SubbandNearendDetection& config, + size_t num_capture_channels); + + // Returns whether the current state is the nearend state. + bool IsNearendState() const override { return nearend_state_; } + + // Updates the state selection based on latest spectral estimates. + void Update(rtc::ArrayView> + nearend_spectrum, + rtc::ArrayView> + residual_echo_spectrum, + rtc::ArrayView> + comfort_noise_spectrum, + bool initial_state) override; + + private: + const EchoCanceller3Config::Suppressor::SubbandNearendDetection config_; + const size_t num_capture_channels_; + std::vector nearend_smoothers_; + const float one_over_subband_length1_; + const float one_over_subband_length2_; + bool nearend_state_ = false; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUBBAND_NEAREND_DETECTOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/subtractor.cc b/webrtc/modules/audio_processing/aec3/subtractor.cc new file mode 100644 index 0000000..d152299 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/subtractor.cc @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/subtractor.h" + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/adaptive_fir_filter_erl.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +namespace { + +void PredictionError(const Aec3Fft& fft, + const FftData& S, + rtc::ArrayView y, + std::array* e, + std::array* s) { + std::array tmp; + fft.Ifft(S, &tmp); + constexpr float kScale = 1.0f / kFftLengthBy2; + std::transform(y.begin(), y.end(), tmp.begin() + kFftLengthBy2, e->begin(), + [&](float a, float b) { return a - b * kScale; }); + + if (s) { + for (size_t k = 0; k < s->size(); ++k) { + (*s)[k] = kScale * tmp[k + kFftLengthBy2]; + } + } +} + +void ScaleFilterOutput(rtc::ArrayView y, + float factor, + rtc::ArrayView e, + rtc::ArrayView s) { + RTC_DCHECK_EQ(y.size(), e.size()); + RTC_DCHECK_EQ(y.size(), s.size()); + for (size_t k = 0; k < y.size(); ++k) { + s[k] *= factor; + e[k] = y[k] - s[k]; + } +} + +} // namespace + +Subtractor::Subtractor(const EchoCanceller3Config& config, + size_t num_render_channels, + size_t num_capture_channels, + ApmDataDumper* data_dumper, + Aec3Optimization optimization) + : fft_(), + data_dumper_(data_dumper), + optimization_(optimization), + config_(config), + num_capture_channels_(num_capture_channels), + refined_filters_(num_capture_channels_), + coarse_filter_(num_capture_channels_), + refined_gains_(num_capture_channels_), + coarse_gains_(num_capture_channels_), + filter_misadjustment_estimators_(num_capture_channels_), + poor_coarse_filter_counters_(num_capture_channels_, 0), + refined_frequency_responses_( + num_capture_channels_, + std::vector>( + std::max(config_.filter.refined_initial.length_blocks, + config_.filter.refined.length_blocks), + std::array())), + refined_impulse_responses_( + num_capture_channels_, + std::vector(GetTimeDomainLength(std::max( + config_.filter.refined_initial.length_blocks, + config_.filter.refined.length_blocks)), + 0.f)) { + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + refined_filters_[ch] = std::make_unique( + config_.filter.refined.length_blocks, + config_.filter.refined_initial.length_blocks, + config.filter.config_change_duration_blocks, num_render_channels, + optimization, data_dumper_); + + coarse_filter_[ch] = std::make_unique( + config_.filter.coarse.length_blocks, + config_.filter.coarse_initial.length_blocks, + config.filter.config_change_duration_blocks, num_render_channels, + optimization, data_dumper_); + refined_gains_[ch] = std::make_unique( + config_.filter.refined_initial, + config_.filter.config_change_duration_blocks); + coarse_gains_[ch] = std::make_unique( + config_.filter.coarse_initial, + config.filter.config_change_duration_blocks); + } + + RTC_DCHECK(data_dumper_); + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + for (auto& H2_k : refined_frequency_responses_[ch]) { + H2_k.fill(0.f); + } + } +} + +Subtractor::~Subtractor() = default; + +void Subtractor::HandleEchoPathChange( + const EchoPathVariability& echo_path_variability) { + const auto full_reset = [&]() { + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + refined_filters_[ch]->HandleEchoPathChange(); + coarse_filter_[ch]->HandleEchoPathChange(); + refined_gains_[ch]->HandleEchoPathChange(echo_path_variability); + coarse_gains_[ch]->HandleEchoPathChange(); + refined_gains_[ch]->SetConfig(config_.filter.refined_initial, true); + coarse_gains_[ch]->SetConfig(config_.filter.coarse_initial, true); + refined_filters_[ch]->SetSizePartitions( + config_.filter.refined_initial.length_blocks, true); + coarse_filter_[ch]->SetSizePartitions( + config_.filter.coarse_initial.length_blocks, true); + } + }; + + if (echo_path_variability.delay_change != + EchoPathVariability::DelayAdjustment::kNone) { + full_reset(); + } + + if (echo_path_variability.gain_change) { + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + refined_gains_[ch]->HandleEchoPathChange(echo_path_variability); + } + } +} + +void Subtractor::ExitInitialState() { + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + refined_gains_[ch]->SetConfig(config_.filter.refined, false); + coarse_gains_[ch]->SetConfig(config_.filter.coarse, false); + refined_filters_[ch]->SetSizePartitions( + config_.filter.refined.length_blocks, false); + coarse_filter_[ch]->SetSizePartitions(config_.filter.coarse.length_blocks, + false); + } +} + +void Subtractor::Process(const RenderBuffer& render_buffer, + const std::vector>& capture, + const RenderSignalAnalyzer& render_signal_analyzer, + const AecState& aec_state, + rtc::ArrayView outputs) { + RTC_DCHECK_EQ(num_capture_channels_, capture.size()); + + // Compute the render powers. + const bool same_filter_sizes = refined_filters_[0]->SizePartitions() == + coarse_filter_[0]->SizePartitions(); + std::array X2_refined; + std::array X2_coarse_data; + auto& X2_coarse = same_filter_sizes ? X2_refined : X2_coarse_data; + if (same_filter_sizes) { + render_buffer.SpectralSum(refined_filters_[0]->SizePartitions(), + &X2_refined); + } else if (refined_filters_[0]->SizePartitions() > + coarse_filter_[0]->SizePartitions()) { + render_buffer.SpectralSums(coarse_filter_[0]->SizePartitions(), + refined_filters_[0]->SizePartitions(), + &X2_coarse, &X2_refined); + } else { + render_buffer.SpectralSums(refined_filters_[0]->SizePartitions(), + coarse_filter_[0]->SizePartitions(), &X2_refined, + &X2_coarse); + } + + // Process all capture channels + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + RTC_DCHECK_EQ(kBlockSize, capture[ch].size()); + SubtractorOutput& output = outputs[ch]; + rtc::ArrayView y = capture[ch]; + FftData& E_refined = output.E_refined; + FftData E_coarse; + std::array& e_refined = output.e_refined; + std::array& e_coarse = output.e_coarse; + + FftData S; + FftData& G = S; + + // Form the outputs of the refined and coarse filters. + refined_filters_[ch]->Filter(render_buffer, &S); + PredictionError(fft_, S, y, &e_refined, &output.s_refined); + + coarse_filter_[ch]->Filter(render_buffer, &S); + PredictionError(fft_, S, y, &e_coarse, &output.s_coarse); + + // Compute the signal powers in the subtractor output. + output.ComputeMetrics(y); + + // Adjust the filter if needed. + bool refined_filters_adjusted = false; + filter_misadjustment_estimators_[ch].Update(output); + if (filter_misadjustment_estimators_[ch].IsAdjustmentNeeded()) { + float scale = filter_misadjustment_estimators_[ch].GetMisadjustment(); + refined_filters_[ch]->ScaleFilter(scale); + for (auto& h_k : refined_impulse_responses_[ch]) { + h_k *= scale; + } + ScaleFilterOutput(y, scale, e_refined, output.s_refined); + filter_misadjustment_estimators_[ch].Reset(); + refined_filters_adjusted = true; + } + + // Compute the FFts of the refined and coarse filter outputs. + fft_.ZeroPaddedFft(e_refined, Aec3Fft::Window::kHanning, &E_refined); + fft_.ZeroPaddedFft(e_coarse, Aec3Fft::Window::kHanning, &E_coarse); + + // Compute spectra for future use. + E_coarse.Spectrum(optimization_, output.E2_coarse); + E_refined.Spectrum(optimization_, output.E2_refined); + + // Update the refined filter. + if (!refined_filters_adjusted) { + std::array erl; + ComputeErl(optimization_, refined_frequency_responses_[ch], erl); + refined_gains_[ch]->Compute(X2_refined, render_signal_analyzer, output, + erl, refined_filters_[ch]->SizePartitions(), + aec_state.SaturatedCapture(), &G); + } else { + G.re.fill(0.f); + G.im.fill(0.f); + } + refined_filters_[ch]->Adapt(render_buffer, G, + &refined_impulse_responses_[ch]); + refined_filters_[ch]->ComputeFrequencyResponse( + &refined_frequency_responses_[ch]); + + if (ch == 0) { + data_dumper_->DumpRaw("aec3_subtractor_G_refined", G.re); + data_dumper_->DumpRaw("aec3_subtractor_G_refined", G.im); + } + + // Update the coarse filter. + poor_coarse_filter_counters_[ch] = + output.e2_refined < output.e2_coarse + ? poor_coarse_filter_counters_[ch] + 1 + : 0; + if (poor_coarse_filter_counters_[ch] < 5) { + coarse_gains_[ch]->Compute(X2_coarse, render_signal_analyzer, E_coarse, + coarse_filter_[ch]->SizePartitions(), + aec_state.SaturatedCapture(), &G); + } else { + poor_coarse_filter_counters_[ch] = 0; + coarse_filter_[ch]->SetFilter(refined_filters_[ch]->SizePartitions(), + refined_filters_[ch]->GetFilter()); + coarse_gains_[ch]->Compute(X2_coarse, render_signal_analyzer, E_refined, + coarse_filter_[ch]->SizePartitions(), + aec_state.SaturatedCapture(), &G); + } + + coarse_filter_[ch]->Adapt(render_buffer, G); + if (ch == 0) { + data_dumper_->DumpRaw("aec3_subtractor_G_coarse", G.re); + data_dumper_->DumpRaw("aec3_subtractor_G_coarse", G.im); + filter_misadjustment_estimators_[ch].Dump(data_dumper_); + DumpFilters(); + } + + std::for_each(e_refined.begin(), e_refined.end(), + [](float& a) { a = rtc::SafeClamp(a, -32768.f, 32767.f); }); + + if (ch == 0) { + data_dumper_->DumpWav("aec3_refined_filters_output", kBlockSize, + &e_refined[0], 16000, 1); + data_dumper_->DumpWav("aec3_coarse_filter_output", kBlockSize, + &e_coarse[0], 16000, 1); + } + } +} + +void Subtractor::FilterMisadjustmentEstimator::Update( + const SubtractorOutput& output) { + e2_acum_ += output.e2_refined; + y2_acum_ += output.y2; + if (++n_blocks_acum_ == n_blocks_) { + if (y2_acum_ > n_blocks_ * 200.f * 200.f * kBlockSize) { + float update = (e2_acum_ / y2_acum_); + if (e2_acum_ > n_blocks_ * 7500.f * 7500.f * kBlockSize) { + // Duration equal to blockSizeMs * n_blocks_ * 4. + overhang_ = 4; + } else { + overhang_ = std::max(overhang_ - 1, 0); + } + + if ((update < inv_misadjustment_) || (overhang_ > 0)) { + inv_misadjustment_ += 0.1f * (update - inv_misadjustment_); + } + } + e2_acum_ = 0.f; + y2_acum_ = 0.f; + n_blocks_acum_ = 0; + } +} + +void Subtractor::FilterMisadjustmentEstimator::Reset() { + e2_acum_ = 0.f; + y2_acum_ = 0.f; + n_blocks_acum_ = 0; + inv_misadjustment_ = 0.f; + overhang_ = 0.f; +} + +void Subtractor::FilterMisadjustmentEstimator::Dump( + ApmDataDumper* data_dumper) const { + data_dumper->DumpRaw("aec3_inv_misadjustment_factor", inv_misadjustment_); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/subtractor.h b/webrtc/modules/audio_processing/aec3/subtractor.h new file mode 100644 index 0000000..42ca372 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/subtractor.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_H_ + +#include +#include + +#include +#include + +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/adaptive_fir_filter.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec3_fft.h" +#include "modules/audio_processing/aec3/aec_state.h" +#include "modules/audio_processing/aec3/coarse_filter_update_gain.h" +#include "modules/audio_processing/aec3/echo_path_variability.h" +#include "modules/audio_processing/aec3/refined_filter_update_gain.h" +#include "modules/audio_processing/aec3/render_buffer.h" +#include "modules/audio_processing/aec3/render_signal_analyzer.h" +#include "modules/audio_processing/aec3/subtractor_output.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// Proves linear echo cancellation functionality +class Subtractor { + public: + Subtractor(const EchoCanceller3Config& config, + size_t num_render_channels, + size_t num_capture_channels, + ApmDataDumper* data_dumper, + Aec3Optimization optimization); + ~Subtractor(); + Subtractor(const Subtractor&) = delete; + Subtractor& operator=(const Subtractor&) = delete; + + // Performs the echo subtraction. + void Process(const RenderBuffer& render_buffer, + const std::vector>& capture, + const RenderSignalAnalyzer& render_signal_analyzer, + const AecState& aec_state, + rtc::ArrayView outputs); + + void HandleEchoPathChange(const EchoPathVariability& echo_path_variability); + + // Exits the initial state. + void ExitInitialState(); + + // Returns the block-wise frequency responses for the refined adaptive + // filters. + const std::vector>>& + FilterFrequencyResponses() const { + return refined_frequency_responses_; + } + + // Returns the estimates of the impulse responses for the refined adaptive + // filters. + const std::vector>& FilterImpulseResponses() const { + return refined_impulse_responses_; + } + + void DumpFilters() { + data_dumper_->DumpRaw( + "aec3_subtractor_h_refined", + rtc::ArrayView( + refined_impulse_responses_[0].data(), + GetTimeDomainLength( + refined_filters_[0]->max_filter_size_partitions()))); + + refined_filters_[0]->DumpFilter("aec3_subtractor_H_refined"); + coarse_filter_[0]->DumpFilter("aec3_subtractor_H_coarse"); + } + + private: + class FilterMisadjustmentEstimator { + public: + FilterMisadjustmentEstimator() = default; + ~FilterMisadjustmentEstimator() = default; + // Update the misadjustment estimator. + void Update(const SubtractorOutput& output); + // GetMisadjustment() Returns a recommended scale for the filter so the + // prediction error energy gets closer to the energy that is seen at the + // microphone input. + float GetMisadjustment() const { + RTC_DCHECK_GT(inv_misadjustment_, 0.0f); + // It is not aiming to adjust all the estimated mismatch. Instead, + // it adjusts half of that estimated mismatch. + return 2.f / sqrtf(inv_misadjustment_); + } + // Returns true if the prediciton error energy is significantly larger + // than the microphone signal energy and, therefore, an adjustment is + // recommended. + bool IsAdjustmentNeeded() const { return inv_misadjustment_ > 10.f; } + void Reset(); + void Dump(ApmDataDumper* data_dumper) const; + + private: + const int n_blocks_ = 4; + int n_blocks_acum_ = 0; + float e2_acum_ = 0.f; + float y2_acum_ = 0.f; + float inv_misadjustment_ = 0.f; + int overhang_ = 0.f; + }; + + const Aec3Fft fft_; + ApmDataDumper* data_dumper_; + const Aec3Optimization optimization_; + const EchoCanceller3Config config_; + const size_t num_capture_channels_; + + std::vector> refined_filters_; + std::vector> coarse_filter_; + std::vector> refined_gains_; + std::vector> coarse_gains_; + std::vector filter_misadjustment_estimators_; + std::vector poor_coarse_filter_counters_; + std::vector>> + refined_frequency_responses_; + std::vector> refined_impulse_responses_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_H_ diff --git a/webrtc/modules/audio_processing/aec3/subtractor_output.cc b/webrtc/modules/audio_processing/aec3/subtractor_output.cc new file mode 100644 index 0000000..ed80101 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/subtractor_output.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/subtractor_output.h" + +#include + +namespace webrtc { + +SubtractorOutput::SubtractorOutput() = default; +SubtractorOutput::~SubtractorOutput() = default; + +void SubtractorOutput::Reset() { + s_refined.fill(0.f); + s_coarse.fill(0.f); + e_refined.fill(0.f); + e_coarse.fill(0.f); + E_refined.re.fill(0.f); + E_refined.im.fill(0.f); + E2_refined.fill(0.f); + E2_coarse.fill(0.f); + e2_refined = 0.f; + e2_coarse = 0.f; + s2_refined = 0.f; + s2_coarse = 0.f; + y2 = 0.f; +} + +void SubtractorOutput::ComputeMetrics(rtc::ArrayView y) { + const auto sum_of_squares = [](float a, float b) { return a + b * b; }; + y2 = std::accumulate(y.begin(), y.end(), 0.f, sum_of_squares); + e2_refined = + std::accumulate(e_refined.begin(), e_refined.end(), 0.f, sum_of_squares); + e2_coarse = + std::accumulate(e_coarse.begin(), e_coarse.end(), 0.f, sum_of_squares); + s2_refined = + std::accumulate(s_refined.begin(), s_refined.end(), 0.f, sum_of_squares); + s2_coarse = + std::accumulate(s_coarse.begin(), s_coarse.end(), 0.f, sum_of_squares); + + s_refined_max_abs = *std::max_element(s_refined.begin(), s_refined.end()); + s_refined_max_abs = + std::max(s_refined_max_abs, + -(*std::min_element(s_refined.begin(), s_refined.end()))); + + s_coarse_max_abs = *std::max_element(s_coarse.begin(), s_coarse.end()); + s_coarse_max_abs = std::max( + s_coarse_max_abs, -(*std::min_element(s_coarse.begin(), s_coarse.end()))); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/subtractor_output.h b/webrtc/modules/audio_processing/aec3/subtractor_output.h new file mode 100644 index 0000000..d2d1208 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/subtractor_output.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/fft_data.h" + +namespace webrtc { + +// Stores the values being returned from the echo subtractor for a single +// capture channel. +struct SubtractorOutput { + SubtractorOutput(); + ~SubtractorOutput(); + + std::array s_refined; + std::array s_coarse; + std::array e_refined; + std::array e_coarse; + FftData E_refined; + std::array E2_refined; + std::array E2_coarse; + float s2_refined = 0.f; + float s2_coarse = 0.f; + float e2_refined = 0.f; + float e2_coarse = 0.f; + float y2 = 0.f; + float s_refined_max_abs = 0.f; + float s_coarse_max_abs = 0.f; + + // Reset the struct content. + void Reset(); + + // Updates the powers of the signals. + void ComputeMetrics(rtc::ArrayView y); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_H_ diff --git a/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.cc b/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.cc new file mode 100644 index 0000000..8b22185 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/aec3/subtractor_output_analyzer.h" + +#include + +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +SubtractorOutputAnalyzer::SubtractorOutputAnalyzer(size_t num_capture_channels) + : filters_converged_(num_capture_channels, false) {} + +void SubtractorOutputAnalyzer::Update( + rtc::ArrayView subtractor_output, + bool* any_filter_converged, + bool* all_filters_diverged) { + RTC_DCHECK(any_filter_converged); + RTC_DCHECK(all_filters_diverged); + RTC_DCHECK_EQ(subtractor_output.size(), filters_converged_.size()); + + *any_filter_converged = false; + *all_filters_diverged = true; + + for (size_t ch = 0; ch < subtractor_output.size(); ++ch) { + const float y2 = subtractor_output[ch].y2; + const float e2_refined = subtractor_output[ch].e2_refined; + const float e2_coarse = subtractor_output[ch].e2_coarse; + + constexpr float kConvergenceThreshold = 50 * 50 * kBlockSize; + bool refined_filter_converged = + e2_refined < 0.5f * y2 && y2 > kConvergenceThreshold; + bool coarse_filter_converged = + e2_coarse < 0.05f * y2 && y2 > kConvergenceThreshold; + float min_e2 = std::min(e2_refined, e2_coarse); + bool filter_diverged = min_e2 > 1.5f * y2 && y2 > 30.f * 30.f * kBlockSize; + filters_converged_[ch] = + refined_filter_converged || coarse_filter_converged; + + *any_filter_converged = *any_filter_converged || filters_converged_[ch]; + *all_filters_diverged = *all_filters_diverged && filter_diverged; + } +} + +void SubtractorOutputAnalyzer::HandleEchoPathChange() { + std::fill(filters_converged_.begin(), filters_converged_.end(), false); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.h b/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.h new file mode 100644 index 0000000..5328ae7 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/subtractor_output_analyzer.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_ANALYZER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_ANALYZER_H_ + +#include + +#include "modules/audio_processing/aec3/subtractor_output.h" + +namespace webrtc { + +// Class for analyzing the properties subtractor output. +class SubtractorOutputAnalyzer { + public: + explicit SubtractorOutputAnalyzer(size_t num_capture_channels); + ~SubtractorOutputAnalyzer() = default; + + // Analyses the subtractor output. + void Update(rtc::ArrayView subtractor_output, + bool* any_filter_converged, + bool* all_filters_diverged); + + const std::vector& ConvergedFilters() const { + return filters_converged_; + } + + // Handle echo path change. + void HandleEchoPathChange(); + + private: + std::vector filters_converged_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_ANALYZER_H_ diff --git a/webrtc/modules/audio_processing/aec3/suppression_filter.cc b/webrtc/modules/audio_processing/aec3/suppression_filter.cc new file mode 100644 index 0000000..8a813d9 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/suppression_filter.cc @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/suppression_filter.h" + +#include +#include +#include +#include +#include + +#include "modules/audio_processing/aec3/vector_math.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +// Hanning window from Matlab command win = sqrt(hanning(128)). +const float kSqrtHanning[kFftLength] = { + 0.00000000000000f, 0.02454122852291f, 0.04906767432742f, 0.07356456359967f, + 0.09801714032956f, 0.12241067519922f, 0.14673047445536f, 0.17096188876030f, + 0.19509032201613f, 0.21910124015687f, 0.24298017990326f, 0.26671275747490f, + 0.29028467725446f, 0.31368174039889f, 0.33688985339222f, 0.35989503653499f, + 0.38268343236509f, 0.40524131400499f, 0.42755509343028f, 0.44961132965461f, + 0.47139673682600f, 0.49289819222978f, 0.51410274419322f, 0.53499761988710f, + 0.55557023301960f, 0.57580819141785f, 0.59569930449243f, 0.61523159058063f, + 0.63439328416365f, 0.65317284295378f, 0.67155895484702f, 0.68954054473707f, + 0.70710678118655f, 0.72424708295147f, 0.74095112535496f, 0.75720884650648f, + 0.77301045336274f, 0.78834642762661f, 0.80320753148064f, 0.81758481315158f, + 0.83146961230255f, 0.84485356524971f, 0.85772861000027f, 0.87008699110871f, + 0.88192126434835f, 0.89322430119552f, 0.90398929312344f, 0.91420975570353f, + 0.92387953251129f, 0.93299279883474f, 0.94154406518302f, 0.94952818059304f, + 0.95694033573221f, 0.96377606579544f, 0.97003125319454f, 0.97570213003853f, + 0.98078528040323f, 0.98527764238894f, 0.98917650996478f, 0.99247953459871f, + 0.99518472667220f, 0.99729045667869f, 0.99879545620517f, 0.99969881869620f, + 1.00000000000000f, 0.99969881869620f, 0.99879545620517f, 0.99729045667869f, + 0.99518472667220f, 0.99247953459871f, 0.98917650996478f, 0.98527764238894f, + 0.98078528040323f, 0.97570213003853f, 0.97003125319454f, 0.96377606579544f, + 0.95694033573221f, 0.94952818059304f, 0.94154406518302f, 0.93299279883474f, + 0.92387953251129f, 0.91420975570353f, 0.90398929312344f, 0.89322430119552f, + 0.88192126434835f, 0.87008699110871f, 0.85772861000027f, 0.84485356524971f, + 0.83146961230255f, 0.81758481315158f, 0.80320753148064f, 0.78834642762661f, + 0.77301045336274f, 0.75720884650648f, 0.74095112535496f, 0.72424708295147f, + 0.70710678118655f, 0.68954054473707f, 0.67155895484702f, 0.65317284295378f, + 0.63439328416365f, 0.61523159058063f, 0.59569930449243f, 0.57580819141785f, + 0.55557023301960f, 0.53499761988710f, 0.51410274419322f, 0.49289819222978f, + 0.47139673682600f, 0.44961132965461f, 0.42755509343028f, 0.40524131400499f, + 0.38268343236509f, 0.35989503653499f, 0.33688985339222f, 0.31368174039889f, + 0.29028467725446f, 0.26671275747490f, 0.24298017990326f, 0.21910124015687f, + 0.19509032201613f, 0.17096188876030f, 0.14673047445536f, 0.12241067519922f, + 0.09801714032956f, 0.07356456359967f, 0.04906767432742f, 0.02454122852291f}; + +} // namespace + +SuppressionFilter::SuppressionFilter(Aec3Optimization optimization, + int sample_rate_hz, + size_t num_capture_channels) + : optimization_(optimization), + sample_rate_hz_(sample_rate_hz), + num_capture_channels_(num_capture_channels), + fft_(), + e_output_old_(NumBandsForRate(sample_rate_hz_), + std::vector>( + num_capture_channels_)) { + RTC_DCHECK(ValidFullBandRate(sample_rate_hz_)); + for (size_t b = 0; b < e_output_old_.size(); ++b) { + for (size_t ch = 0; ch < e_output_old_[b].size(); ++ch) { + e_output_old_[b][ch].fill(0.f); + } + } +} + +SuppressionFilter::~SuppressionFilter() = default; + +void SuppressionFilter::ApplyGain( + rtc::ArrayView comfort_noise, + rtc::ArrayView comfort_noise_high_band, + const std::array& suppression_gain, + float high_bands_gain, + rtc::ArrayView E_lowest_band, + std::vector>>* e) { + RTC_DCHECK(e); + RTC_DCHECK_EQ(e->size(), NumBandsForRate(sample_rate_hz_)); + + // Comfort noise gain is sqrt(1-g^2), where g is the suppression gain. + std::array noise_gain; + for (size_t i = 0; i < kFftLengthBy2Plus1; ++i) { + noise_gain[i] = 1.f - suppression_gain[i] * suppression_gain[i]; + } + aec3::VectorMath(optimization_).Sqrt(noise_gain); + + const float high_bands_noise_scaling = + 0.4f * std::sqrt(1.f - high_bands_gain * high_bands_gain); + + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + FftData E; + + // Analysis filterbank. + E.Assign(E_lowest_band[ch]); + + for (size_t i = 0; i < kFftLengthBy2Plus1; ++i) { + // Apply suppression gains. + E.re[i] *= suppression_gain[i]; + E.im[i] *= suppression_gain[i]; + + // Scale and add the comfort noise. + E.re[i] += noise_gain[i] * comfort_noise[ch].re[i]; + E.im[i] += noise_gain[i] * comfort_noise[ch].im[i]; + } + + // Synthesis filterbank. + std::array e_extended; + constexpr float kIfftNormalization = 2.f / kFftLength; + fft_.Ifft(E, &e_extended); + + auto& e0 = (*e)[0][ch]; + auto& e0_old = e_output_old_[0][ch]; + + // Window and add the first half of e_extended with the second half of + // e_extended from the previous block. + for (size_t i = 0; i < kFftLengthBy2; ++i) { + e0[i] = e0_old[i] * kSqrtHanning[kFftLengthBy2 + i]; + e0[i] += e_extended[i] * kSqrtHanning[i]; + e0[i] *= kIfftNormalization; + } + + // The second half of e_extended is stored for the succeeding frame. + std::copy(e_extended.begin() + kFftLengthBy2, + e_extended.begin() + kFftLength, std::begin(e0_old)); + + // Apply suppression gain to upper bands. + for (size_t b = 1; b < e->size(); ++b) { + auto& e_band = (*e)[b][ch]; + for (size_t i = 0; i < kFftLengthBy2; ++i) { + e_band[i] *= high_bands_gain; + } + } + + // Add comfort noise to band 1. + if (e->size() > 1) { + E.Assign(comfort_noise_high_band[ch]); + std::array time_domain_high_band_noise; + fft_.Ifft(E, &time_domain_high_band_noise); + + auto& e1 = (*e)[1][ch]; + const float gain = high_bands_noise_scaling * kIfftNormalization; + for (size_t i = 0; i < kFftLengthBy2; ++i) { + e1[i] += time_domain_high_band_noise[i] * gain; + } + } + + // Delay upper bands to match the delay of the filter bank. + for (size_t b = 1; b < e->size(); ++b) { + auto& e_band = (*e)[b][ch]; + auto& e_band_old = e_output_old_[b][ch]; + for (size_t i = 0; i < kFftLengthBy2; ++i) { + std::swap(e_band[i], e_band_old[i]); + } + } + + // Clamp output of all bands. + for (size_t b = 0; b < e->size(); ++b) { + auto& e_band = (*e)[b][ch]; + for (size_t i = 0; i < kFftLengthBy2; ++i) { + e_band[i] = rtc::SafeClamp(e_band[i], -32768.f, 32767.f); + } + } + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/suppression_filter.h b/webrtc/modules/audio_processing/aec3/suppression_filter.h new file mode 100644 index 0000000..dcf2292 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/suppression_filter.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_FILTER_H_ + +#include +#include + +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec3_fft.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +class SuppressionFilter { + public: + SuppressionFilter(Aec3Optimization optimization, + int sample_rate_hz, + size_t num_capture_channels_); + ~SuppressionFilter(); + void ApplyGain(rtc::ArrayView comfort_noise, + rtc::ArrayView comfort_noise_high_bands, + const std::array& suppression_gain, + float high_bands_gain, + rtc::ArrayView E_lowest_band, + std::vector>>* e); + + private: + const Aec3Optimization optimization_; + const int sample_rate_hz_; + const size_t num_capture_channels_; + const Aec3Fft fft_; + std::vector>> e_output_old_; + RTC_DISALLOW_COPY_AND_ASSIGN(SuppressionFilter); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_FILTER_H_ diff --git a/webrtc/modules/audio_processing/aec3/suppression_gain.cc b/webrtc/modules/audio_processing/aec3/suppression_gain.cc new file mode 100644 index 0000000..c1f12b7 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/suppression_gain.cc @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec3/suppression_gain.h" + +#include +#include + +#include +#include + +#include "modules/audio_processing/aec3/dominant_nearend_detector.h" +#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/subband_nearend_detector.h" +#include "modules/audio_processing/aec3/vector_math.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +void PostprocessGains(std::array* gain) { + // TODO(gustaf): Investigate if this can be relaxed to achieve higher + // transparency above 2 kHz. + + // Limit the low frequency gains to avoid the impact of the high-pass filter + // on the lower-frequency gain influencing the overall achieved gain. + (*gain)[0] = (*gain)[1] = std::min((*gain)[1], (*gain)[2]); + + // Limit the high frequency gains to avoid the impact of the anti-aliasing + // filter on the upper-frequency gains influencing the overall achieved + // gain. TODO(peah): Update this when new anti-aliasing filters are + // implemented. + constexpr size_t kAntiAliasingImpactLimit = (64 * 2000) / 8000; + const float min_upper_gain = (*gain)[kAntiAliasingImpactLimit]; + std::for_each( + gain->begin() + kAntiAliasingImpactLimit, gain->end() - 1, + [min_upper_gain](float& a) { a = std::min(a, min_upper_gain); }); + (*gain)[kFftLengthBy2] = (*gain)[kFftLengthBy2Minus1]; + + // Limits the gain in the frequencies for which the adaptive filter has not + // converged. + // TODO(peah): Make adaptive to take the actual filter error into account. + constexpr size_t kUpperAccurateBandPlus1 = 29; + + constexpr float oneByBandsInSum = + 1 / static_cast(kUpperAccurateBandPlus1 - 20); + const float hf_gain_bound = + std::accumulate(gain->begin() + 20, + gain->begin() + kUpperAccurateBandPlus1, 0.f) * + oneByBandsInSum; + + std::for_each(gain->begin() + kUpperAccurateBandPlus1, gain->end(), + [hf_gain_bound](float& a) { a = std::min(a, hf_gain_bound); }); +} + +// Scales the echo according to assessed audibility at the other end. +void WeightEchoForAudibility(const EchoCanceller3Config& config, + rtc::ArrayView echo, + rtc::ArrayView weighted_echo) { + RTC_DCHECK_EQ(kFftLengthBy2Plus1, echo.size()); + RTC_DCHECK_EQ(kFftLengthBy2Plus1, weighted_echo.size()); + + auto weigh = [](float threshold, float normalizer, size_t begin, size_t end, + rtc::ArrayView echo, + rtc::ArrayView weighted_echo) { + for (size_t k = begin; k < end; ++k) { + if (echo[k] < threshold) { + float tmp = (threshold - echo[k]) * normalizer; + weighted_echo[k] = echo[k] * std::max(0.f, 1.f - tmp * tmp); + } else { + weighted_echo[k] = echo[k]; + } + } + }; + + float threshold = config.echo_audibility.floor_power * + config.echo_audibility.audibility_threshold_lf; + float normalizer = 1.f / (threshold - config.echo_audibility.floor_power); + weigh(threshold, normalizer, 0, 3, echo, weighted_echo); + + threshold = config.echo_audibility.floor_power * + config.echo_audibility.audibility_threshold_mf; + normalizer = 1.f / (threshold - config.echo_audibility.floor_power); + weigh(threshold, normalizer, 3, 7, echo, weighted_echo); + + threshold = config.echo_audibility.floor_power * + config.echo_audibility.audibility_threshold_hf; + normalizer = 1.f / (threshold - config.echo_audibility.floor_power); + weigh(threshold, normalizer, 7, kFftLengthBy2Plus1, echo, weighted_echo); +} + +} // namespace + +int SuppressionGain::instance_count_ = 0; + +float SuppressionGain::UpperBandsGain( + rtc::ArrayView> echo_spectrum, + rtc::ArrayView> + comfort_noise_spectrum, + const absl::optional& narrow_peak_band, + bool saturated_echo, + const std::vector>>& render, + const std::array& low_band_gain) const { + RTC_DCHECK_LT(0, render.size()); + if (render.size() == 1) { + return 1.f; + } + const size_t num_render_channels = render[0].size(); + + if (narrow_peak_band && + (*narrow_peak_band > static_cast(kFftLengthBy2Plus1 - 10))) { + return 0.001f; + } + + constexpr size_t kLowBandGainLimit = kFftLengthBy2 / 2; + const float gain_below_8_khz = *std::min_element( + low_band_gain.begin() + kLowBandGainLimit, low_band_gain.end()); + + // Always attenuate the upper bands when there is saturated echo. + if (saturated_echo) { + return std::min(0.001f, gain_below_8_khz); + } + + // Compute the upper and lower band energies. + const auto sum_of_squares = [](float a, float b) { return a + b * b; }; + float low_band_energy = 0.f; + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const float channel_energy = std::accumulate( + render[0][0].begin(), render[0][0].end(), 0.f, sum_of_squares); + low_band_energy = std::max(low_band_energy, channel_energy); + } + float high_band_energy = 0.f; + for (size_t k = 1; k < render.size(); ++k) { + for (size_t ch = 0; ch < num_render_channels; ++ch) { + const float energy = std::accumulate( + render[k][ch].begin(), render[k][ch].end(), 0.f, sum_of_squares); + high_band_energy = std::max(high_band_energy, energy); + } + } + + // If there is more power in the lower frequencies than the upper frequencies, + // or if the power in upper frequencies is low, do not bound the gain in the + // upper bands. + float anti_howling_gain; + const float activation_threshold = + kBlockSize * config_.suppressor.high_bands_suppression + .anti_howling_activation_threshold; + if (high_band_energy < std::max(low_band_energy, activation_threshold)) { + anti_howling_gain = 1.f; + } else { + // In all other cases, bound the gain for upper frequencies. + RTC_DCHECK_LE(low_band_energy, high_band_energy); + RTC_DCHECK_NE(0.f, high_band_energy); + anti_howling_gain = + config_.suppressor.high_bands_suppression.anti_howling_gain * + sqrtf(low_band_energy / high_band_energy); + } + + float gain_bound = 1.f; + if (!dominant_nearend_detector_->IsNearendState()) { + // Bound the upper gain during significant echo activity. + const auto& cfg = config_.suppressor.high_bands_suppression; + auto low_frequency_energy = [](rtc::ArrayView spectrum) { + RTC_DCHECK_LE(16, spectrum.size()); + return std::accumulate(spectrum.begin() + 1, spectrum.begin() + 16, 0.f); + }; + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + const float echo_sum = low_frequency_energy(echo_spectrum[ch]); + const float noise_sum = low_frequency_energy(comfort_noise_spectrum[ch]); + if (echo_sum > cfg.enr_threshold * noise_sum) { + gain_bound = cfg.max_gain_during_echo; + break; + } + } + } + + // Choose the gain as the minimum of the lower and upper gains. + return std::min(std::min(gain_below_8_khz, anti_howling_gain), gain_bound); +} + +// Computes the gain to reduce the echo to a non audible level. +void SuppressionGain::GainToNoAudibleEcho( + const std::array& nearend, + const std::array& echo, + const std::array& masker, + std::array* gain) const { + const auto& p = dominant_nearend_detector_->IsNearendState() ? nearend_params_ + : normal_params_; + for (size_t k = 0; k < gain->size(); ++k) { + float enr = echo[k] / (nearend[k] + 1.f); // Echo-to-nearend ratio. + float emr = echo[k] / (masker[k] + 1.f); // Echo-to-masker (noise) ratio. + float g = 1.0f; + if (enr > p.enr_transparent_[k] && emr > p.emr_transparent_[k]) { + g = (p.enr_suppress_[k] - enr) / + (p.enr_suppress_[k] - p.enr_transparent_[k]); + g = std::max(g, p.emr_transparent_[k] / emr); + } + (*gain)[k] = g; + } +} + +// Compute the minimum gain as the attenuating gain to put the signal just +// above the zero sample values. +void SuppressionGain::GetMinGain( + rtc::ArrayView weighted_residual_echo, + rtc::ArrayView last_nearend, + rtc::ArrayView last_echo, + bool low_noise_render, + bool saturated_echo, + rtc::ArrayView min_gain) const { + if (!saturated_echo) { + const float min_echo_power = + low_noise_render ? config_.echo_audibility.low_render_limit + : config_.echo_audibility.normal_render_limit; + + for (size_t k = 0; k < min_gain.size(); ++k) { + min_gain[k] = weighted_residual_echo[k] > 0.f + ? min_echo_power / weighted_residual_echo[k] + : 1.f; + min_gain[k] = std::min(min_gain[k], 1.f); + } + + const bool is_nearend_state = dominant_nearend_detector_->IsNearendState(); + for (size_t k = 0; k < 6; ++k) { + const auto& dec = is_nearend_state ? nearend_params_.max_dec_factor_lf + : normal_params_.max_dec_factor_lf; + + // Make sure the gains of the low frequencies do not decrease too + // quickly after strong nearend. + if (last_nearend[k] > last_echo[k]) { + min_gain[k] = std::max(min_gain[k], last_gain_[k] * dec); + min_gain[k] = std::min(min_gain[k], 1.f); + } + } + } else { + std::fill(min_gain.begin(), min_gain.end(), 0.f); + } +} + +// Compute the maximum gain by limiting the gain increase from the previous +// gain. +void SuppressionGain::GetMaxGain(rtc::ArrayView max_gain) const { + const auto& inc = dominant_nearend_detector_->IsNearendState() + ? nearend_params_.max_inc_factor + : normal_params_.max_inc_factor; + const auto& floor = config_.suppressor.floor_first_increase; + for (size_t k = 0; k < max_gain.size(); ++k) { + max_gain[k] = std::min(std::max(last_gain_[k] * inc, floor), 1.f); + } +} + +void SuppressionGain::LowerBandGain( + bool low_noise_render, + const AecState& aec_state, + rtc::ArrayView> + suppressor_input, + rtc::ArrayView> residual_echo, + rtc::ArrayView> comfort_noise, + std::array* gain) { + gain->fill(1.f); + const bool saturated_echo = aec_state.SaturatedEcho(); + std::array max_gain; + GetMaxGain(max_gain); + + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + std::array G; + std::array nearend; + nearend_smoothers_[ch].Average(suppressor_input[ch], nearend); + + // Weight echo power in terms of audibility. + std::array weighted_residual_echo; + WeightEchoForAudibility(config_, residual_echo[ch], weighted_residual_echo); + + std::array min_gain; + GetMinGain(weighted_residual_echo, last_nearend_[ch], last_echo_[ch], + low_noise_render, saturated_echo, min_gain); + + GainToNoAudibleEcho(nearend, weighted_residual_echo, comfort_noise[0], &G); + + // Clamp gains. + for (size_t k = 0; k < gain->size(); ++k) { + G[k] = std::max(std::min(G[k], max_gain[k]), min_gain[k]); + (*gain)[k] = std::min((*gain)[k], G[k]); + } + + // Store data required for the gain computation of the next block. + std::copy(nearend.begin(), nearend.end(), last_nearend_[ch].begin()); + std::copy(weighted_residual_echo.begin(), weighted_residual_echo.end(), + last_echo_[ch].begin()); + } + + // Limit high-frequency gains. + PostprocessGains(gain); + + // Store computed gains. + std::copy(gain->begin(), gain->end(), last_gain_.begin()); + + // Transform gains to amplitude domain. + aec3::VectorMath(optimization_).Sqrt(*gain); +} + +SuppressionGain::SuppressionGain(const EchoCanceller3Config& config, + Aec3Optimization optimization, + int sample_rate_hz, + size_t num_capture_channels) + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + optimization_(optimization), + config_(config), + num_capture_channels_(num_capture_channels), + state_change_duration_blocks_( + static_cast(config_.filter.config_change_duration_blocks)), + last_nearend_(num_capture_channels_, {0}), + last_echo_(num_capture_channels_, {0}), + nearend_smoothers_( + num_capture_channels_, + aec3::MovingAverage(kFftLengthBy2Plus1, + config.suppressor.nearend_average_blocks)), + nearend_params_(config_.suppressor.nearend_tuning), + normal_params_(config_.suppressor.normal_tuning) { + RTC_DCHECK_LT(0, state_change_duration_blocks_); + last_gain_.fill(1.f); + if (config_.suppressor.use_subband_nearend_detection) { + dominant_nearend_detector_ = std::make_unique( + config_.suppressor.subband_nearend_detection, num_capture_channels_); + } else { + dominant_nearend_detector_ = std::make_unique( + config_.suppressor.dominant_nearend_detection, num_capture_channels_); + } + RTC_DCHECK(dominant_nearend_detector_); +} + +SuppressionGain::~SuppressionGain() = default; + +void SuppressionGain::GetGain( + rtc::ArrayView> + nearend_spectrum, + rtc::ArrayView> echo_spectrum, + rtc::ArrayView> + residual_echo_spectrum, + rtc::ArrayView> + comfort_noise_spectrum, + const RenderSignalAnalyzer& render_signal_analyzer, + const AecState& aec_state, + const std::vector>>& render, + float* high_bands_gain, + std::array* low_band_gain) { + RTC_DCHECK(high_bands_gain); + RTC_DCHECK(low_band_gain); + + // Update the nearend state selection. + dominant_nearend_detector_->Update(nearend_spectrum, residual_echo_spectrum, + comfort_noise_spectrum, initial_state_); + + // Compute gain for the lower band. + bool low_noise_render = low_render_detector_.Detect(render); + LowerBandGain(low_noise_render, aec_state, nearend_spectrum, + residual_echo_spectrum, comfort_noise_spectrum, low_band_gain); + + // Compute the gain for the upper bands. + const absl::optional narrow_peak_band = + render_signal_analyzer.NarrowPeakBand(); + + *high_bands_gain = + UpperBandsGain(echo_spectrum, comfort_noise_spectrum, narrow_peak_band, + aec_state.SaturatedEcho(), render, *low_band_gain); +} + +void SuppressionGain::SetInitialState(bool state) { + initial_state_ = state; + if (state) { + initial_state_change_counter_ = state_change_duration_blocks_; + } else { + initial_state_change_counter_ = 0; + } +} + +// Detects when the render signal can be considered to have low power and +// consist of stationary noise. +bool SuppressionGain::LowNoiseRenderDetector::Detect( + const std::vector>>& render) { + float x2_sum = 0.f; + float x2_max = 0.f; + for (const auto& x_ch : render[0]) { + for (const auto& x_k : x_ch) { + const float x2 = x_k * x_k; + x2_sum += x2; + x2_max = std::max(x2_max, x2); + } + } + const size_t num_render_channels = render[0].size(); + x2_sum = x2_sum / num_render_channels; + ; + + constexpr float kThreshold = 50.f * 50.f * 64.f; + const bool low_noise_render = + average_power_ < kThreshold && x2_max < 3 * average_power_; + average_power_ = average_power_ * 0.9f + x2_sum * 0.1f; + return low_noise_render; +} + +SuppressionGain::GainParameters::GainParameters( + const EchoCanceller3Config::Suppressor::Tuning& tuning) + : max_inc_factor(tuning.max_inc_factor), + max_dec_factor_lf(tuning.max_dec_factor_lf) { + // Compute per-band masking thresholds. + constexpr size_t kLastLfBand = 5; + constexpr size_t kFirstHfBand = 8; + RTC_DCHECK_LT(kLastLfBand, kFirstHfBand); + auto& lf = tuning.mask_lf; + auto& hf = tuning.mask_hf; + RTC_DCHECK_LT(lf.enr_transparent, lf.enr_suppress); + RTC_DCHECK_LT(hf.enr_transparent, hf.enr_suppress); + for (size_t k = 0; k < kFftLengthBy2Plus1; k++) { + float a; + if (k <= kLastLfBand) { + a = 0.f; + } else if (k < kFirstHfBand) { + a = (k - kLastLfBand) / static_cast(kFirstHfBand - kLastLfBand); + } else { + a = 1.f; + } + enr_transparent_[k] = (1 - a) * lf.enr_transparent + a * hf.enr_transparent; + enr_suppress_[k] = (1 - a) * lf.enr_suppress + a * hf.enr_suppress; + emr_transparent_[k] = (1 - a) * lf.emr_transparent + a * hf.emr_transparent; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/suppression_gain.h b/webrtc/modules/audio_processing/aec3/suppression_gain.h new file mode 100644 index 0000000..f46db0b --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/suppression_gain.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_GAIN_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_GAIN_H_ + +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/aec_state.h" +#include "modules/audio_processing/aec3/fft_data.h" +#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/nearend_detector.h" +#include "modules/audio_processing/aec3/render_signal_analyzer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +class SuppressionGain { + public: + SuppressionGain(const EchoCanceller3Config& config, + Aec3Optimization optimization, + int sample_rate_hz, + size_t num_capture_channels); + ~SuppressionGain(); + void GetGain( + rtc::ArrayView> + nearend_spectrum, + rtc::ArrayView> echo_spectrum, + rtc::ArrayView> + residual_echo_spectrum, + rtc::ArrayView> + comfort_noise_spectrum, + const RenderSignalAnalyzer& render_signal_analyzer, + const AecState& aec_state, + const std::vector>>& render, + float* high_bands_gain, + std::array* low_band_gain); + + // Toggles the usage of the initial state. + void SetInitialState(bool state); + + private: + // Computes the gain to apply for the bands beyond the first band. + float UpperBandsGain( + rtc::ArrayView> echo_spectrum, + rtc::ArrayView> + comfort_noise_spectrum, + const absl::optional& narrow_peak_band, + bool saturated_echo, + const std::vector>>& render, + const std::array& low_band_gain) const; + + void GainToNoAudibleEcho(const std::array& nearend, + const std::array& echo, + const std::array& masker, + std::array* gain) const; + + void LowerBandGain( + bool stationary_with_low_power, + const AecState& aec_state, + rtc::ArrayView> + suppressor_input, + rtc::ArrayView> residual_echo, + rtc::ArrayView> comfort_noise, + std::array* gain); + + void GetMinGain(rtc::ArrayView weighted_residual_echo, + rtc::ArrayView last_nearend, + rtc::ArrayView last_echo, + bool low_noise_render, + bool saturated_echo, + rtc::ArrayView min_gain) const; + + void GetMaxGain(rtc::ArrayView max_gain) const; + + class LowNoiseRenderDetector { + public: + bool Detect(const std::vector>>& render); + + private: + float average_power_ = 32768.f * 32768.f; + }; + + struct GainParameters { + explicit GainParameters( + const EchoCanceller3Config::Suppressor::Tuning& tuning); + const float max_inc_factor; + const float max_dec_factor_lf; + std::array enr_transparent_; + std::array enr_suppress_; + std::array emr_transparent_; + }; + + static int instance_count_; + std::unique_ptr data_dumper_; + const Aec3Optimization optimization_; + const EchoCanceller3Config config_; + const size_t num_capture_channels_; + const int state_change_duration_blocks_; + std::array last_gain_; + std::vector> last_nearend_; + std::vector> last_echo_; + LowNoiseRenderDetector low_render_detector_; + bool initial_state_ = true; + int initial_state_change_counter_ = 0; + std::vector nearend_smoothers_; + const GainParameters nearend_params_; + const GainParameters normal_params_; + std::unique_ptr dominant_nearend_detector_; + + RTC_DISALLOW_COPY_AND_ASSIGN(SuppressionGain); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_GAIN_H_ diff --git a/webrtc/modules/audio_processing/aec3/transparent_mode.cc b/webrtc/modules/audio_processing/aec3/transparent_mode.cc new file mode 100644 index 0000000..1820e16 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/transparent_mode.cc @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2020 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 "modules/audio_processing/aec3/transparent_mode.h" + +#include "rtc_base/checks.h" +#include "system_wrappers/include/field_trial.h" + +namespace webrtc { +namespace { + +constexpr size_t kBlocksSinceConvergencedFilterInit = 10000; +constexpr size_t kBlocksSinceConsistentEstimateInit = 10000; + +bool DeactivateTransparentMode() { + return field_trial::IsEnabled("WebRTC-Aec3TransparentModeKillSwitch"); +} + +bool DeactivateTransparentModeHmm() { + return field_trial::IsEnabled("WebRTC-Aec3TransparentModeHmmKillSwitch"); +} + +} // namespace + +// Classifier that toggles transparent mode which reduces echo suppression when +// headsets are used. +class TransparentModeImpl : public TransparentMode { + public: + bool Active() const override { return transparency_activated_; } + + void Reset() override { + // Determines if transparent mode is used. + transparency_activated_ = false; + + // The estimated probability of being transparent mode. + prob_transparent_state_ = 0.f; + } + + void Update(int filter_delay_blocks, + bool any_filter_consistent, + bool any_filter_converged, + bool all_filters_diverged, + bool active_render, + bool saturated_capture) override { + // The classifier is implemented as a Hidden Markov Model (HMM) with two + // hidden states: "normal" and "transparent". The estimated probabilities of + // the two states are updated by observing filter convergence during active + // render. The filters are less likely to be reported as converged when + // there is no echo present in the microphone signal. + + // The constants have been obtained by observing active_render and + // any_filter_converged under varying call scenarios. They have further been + // hand tuned to prefer normal state during uncertain regions (to avoid echo + // leaks). + + // The model is only updated during active render. + if (!active_render) + return; + + // Probability of switching from one state to the other. + constexpr float kSwitch = 0.000001f; + + // Probability of observing converged filters in states "normal" and + // "transparent" during active render. + constexpr float kConvergedNormal = 0.03f; + constexpr float kConvergedTransparent = 0.005f; + + // Probability of transitioning to transparent state from normal state and + // transparent state respectively. + constexpr float kA[2] = {kSwitch, 1.f - kSwitch}; + + // Probability of the two observations (converged filter or not converged + // filter) in normal state and transparent state respectively. + constexpr float kB[2][2] = { + {1.f - kConvergedNormal, kConvergedNormal}, + {1.f - kConvergedTransparent, kConvergedTransparent}}; + + // Probability of the two states before the update. + const float prob_transparent = prob_transparent_state_; + const float prob_normal = 1.f - prob_transparent; + + // Probability of transitioning to transparent state. + const float prob_transition_transparent = + prob_normal * kA[0] + prob_transparent * kA[1]; + const float prob_transition_normal = 1.f - prob_transition_transparent; + + // Observed output. + const int out = any_filter_converged; + + // Joint probabilites of the observed output and respective states. + const float prob_joint_normal = prob_transition_normal * kB[0][out]; + const float prob_joint_transparent = + prob_transition_transparent * kB[1][out]; + + // Conditional probability of transparent state and the observed output. + RTC_DCHECK_GT(prob_joint_normal + prob_joint_transparent, 0.f); + prob_transparent_state_ = + prob_joint_transparent / (prob_joint_normal + prob_joint_transparent); + + // Transparent mode is only activated when its state probability is high. + // Dead zone between activation/deactivation thresholds to avoid switching + // back and forth. + if (prob_transparent_state_ > 0.95f) { + transparency_activated_ = true; + } else if (prob_transparent_state_ < 0.5f) { + transparency_activated_ = false; + } + } + + private: + bool transparency_activated_ = false; + float prob_transparent_state_ = 0.f; +}; + +// Legacy classifier for toggling transparent mode. +class LegacyTransparentModeImpl : public TransparentMode { + public: + explicit LegacyTransparentModeImpl(const EchoCanceller3Config& config) + : linear_and_stable_echo_path_( + config.echo_removal_control.linear_and_stable_echo_path), + active_blocks_since_sane_filter_(kBlocksSinceConsistentEstimateInit), + non_converged_sequence_size_(kBlocksSinceConvergencedFilterInit) {} + + bool Active() const override { return transparency_activated_; } + + void Reset() override { + non_converged_sequence_size_ = kBlocksSinceConvergencedFilterInit; + diverged_sequence_size_ = 0; + strong_not_saturated_render_blocks_ = 0; + if (linear_and_stable_echo_path_) { + recent_convergence_during_activity_ = false; + } + } + + void Update(int filter_delay_blocks, + bool any_filter_consistent, + bool any_filter_converged, + bool all_filters_diverged, + bool active_render, + bool saturated_capture) override { + ++capture_block_counter_; + strong_not_saturated_render_blocks_ += + active_render && !saturated_capture ? 1 : 0; + + if (any_filter_consistent && filter_delay_blocks < 5) { + sane_filter_observed_ = true; + active_blocks_since_sane_filter_ = 0; + } else if (active_render) { + ++active_blocks_since_sane_filter_; + } + + bool sane_filter_recently_seen; + if (!sane_filter_observed_) { + sane_filter_recently_seen = + capture_block_counter_ <= 5 * kNumBlocksPerSecond; + } else { + sane_filter_recently_seen = + active_blocks_since_sane_filter_ <= 30 * kNumBlocksPerSecond; + } + + if (any_filter_converged) { + recent_convergence_during_activity_ = true; + active_non_converged_sequence_size_ = 0; + non_converged_sequence_size_ = 0; + ++num_converged_blocks_; + } else { + if (++non_converged_sequence_size_ > 20 * kNumBlocksPerSecond) { + num_converged_blocks_ = 0; + } + + if (active_render && + ++active_non_converged_sequence_size_ > 60 * kNumBlocksPerSecond) { + recent_convergence_during_activity_ = false; + } + } + + if (!all_filters_diverged) { + diverged_sequence_size_ = 0; + } else if (++diverged_sequence_size_ >= 60) { + // TODO(peah): Change these lines to ensure proper triggering of usable + // filter. + non_converged_sequence_size_ = kBlocksSinceConvergencedFilterInit; + } + + if (active_non_converged_sequence_size_ > 60 * kNumBlocksPerSecond) { + finite_erl_recently_detected_ = false; + } + if (num_converged_blocks_ > 50) { + finite_erl_recently_detected_ = true; + } + + if (finite_erl_recently_detected_) { + transparency_activated_ = false; + } else if (sane_filter_recently_seen && + recent_convergence_during_activity_) { + transparency_activated_ = false; + } else { + const bool filter_should_have_converged = + strong_not_saturated_render_blocks_ > 6 * kNumBlocksPerSecond; + transparency_activated_ = filter_should_have_converged; + } + } + + private: + const bool linear_and_stable_echo_path_; + size_t capture_block_counter_ = 0; + bool transparency_activated_ = false; + size_t active_blocks_since_sane_filter_; + bool sane_filter_observed_ = false; + bool finite_erl_recently_detected_ = false; + size_t non_converged_sequence_size_; + size_t diverged_sequence_size_ = 0; + size_t active_non_converged_sequence_size_ = 0; + size_t num_converged_blocks_ = 0; + bool recent_convergence_during_activity_ = false; + size_t strong_not_saturated_render_blocks_ = 0; +}; + +std::unique_ptr TransparentMode::Create( + const EchoCanceller3Config& config) { + if (config.ep_strength.bounded_erl || DeactivateTransparentMode()) { + return nullptr; + } + if (DeactivateTransparentModeHmm()) { + return std::make_unique(config); + } + return std::make_unique(); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/transparent_mode.h b/webrtc/modules/audio_processing/aec3/transparent_mode.h new file mode 100644 index 0000000..b1be69b --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/transparent_mode.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_TRANSPARENT_MODE_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_TRANSPARENT_MODE_H_ + +#include + +#include "api/audio/echo_canceller3_config.h" +#include "modules/audio_processing/aec3/aec3_common.h" + +namespace webrtc { + +// Class for detecting and toggling the transparent mode which causes the +// suppressor to apply less suppression. +class TransparentMode { + public: + static std::unique_ptr Create( + const EchoCanceller3Config& config); + + virtual ~TransparentMode() {} + + // Returns whether the transparent mode should be active. + virtual bool Active() const = 0; + + // Resets the state of the detector. + virtual void Reset() = 0; + + // Updates the detection decision based on new data. + virtual void Update(int filter_delay_blocks, + bool any_filter_consistent, + bool any_filter_converged, + bool all_filters_diverged, + bool active_render, + bool saturated_capture) = 0; +}; + +} // namespace webrtc +#endif // MODULES_AUDIO_PROCESSING_AEC3_TRANSPARENT_MODE_H_ diff --git a/webrtc/modules/audio_processing/aec3/vector_math.h b/webrtc/modules/audio_processing/aec3/vector_math.h new file mode 100644 index 0000000..e4d1381 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/vector_math.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_VECTOR_MATH_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_VECTOR_MATH_H_ + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_HAS_NEON) +#include +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/aec3/aec3_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace aec3 { + +// Provides optimizations for mathematical operations based on vectors. +class VectorMath { + public: + explicit VectorMath(Aec3Optimization optimization) + : optimization_(optimization) {} + + // Elementwise square root. + void SqrtAVX2(rtc::ArrayView x); + void Sqrt(rtc::ArrayView x) { + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + __m128 g = _mm_loadu_ps(&x[j]); + g = _mm_sqrt_ps(g); + _mm_storeu_ps(&x[j], g); + } + + for (; j < x_size; ++j) { + x[j] = sqrtf(x[j]); + } + } break; + case Aec3Optimization::kAvx2: + SqrtAVX2(x); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + float32x4_t g = vld1q_f32(&x[j]); +#if !defined(WEBRTC_ARCH_ARM64) + float32x4_t y = vrsqrteq_f32(g); + + // Code to handle sqrt(0). + // If the input to sqrtf() is zero, a zero will be returned. + // If the input to vrsqrteq_f32() is zero, positive infinity is + // returned. + const uint32x4_t vec_p_inf = vdupq_n_u32(0x7F800000); + // check for divide by zero + const uint32x4_t div_by_zero = + vceqq_u32(vec_p_inf, vreinterpretq_u32_f32(y)); + // zero out the positive infinity results + y = vreinterpretq_f32_u32( + vandq_u32(vmvnq_u32(div_by_zero), vreinterpretq_u32_f32(y))); + // from arm documentation + // The Newton-Raphson iteration: + // y[n+1] = y[n] * (3 - d * (y[n] * y[n])) / 2) + // converges to (1/√d) if y0 is the result of VRSQRTE applied to d. + // + // Note: The precision did not improve after 2 iterations. + for (int i = 0; i < 2; i++) { + y = vmulq_f32(vrsqrtsq_f32(vmulq_f32(y, y), g), y); + } + // sqrt(g) = g * 1/sqrt(g) + g = vmulq_f32(g, y); +#else + g = vsqrtq_f32(g); +#endif + vst1q_f32(&x[j], g); + } + + for (; j < x_size; ++j) { + x[j] = sqrtf(x[j]); + } + } +#endif + break; + default: + std::for_each(x.begin(), x.end(), [](float& a) { a = sqrtf(a); }); + } + } + + // Elementwise vector multiplication z = x * y. + void MultiplyAVX2(rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView z); + void Multiply(rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView z) { + RTC_DCHECK_EQ(z.size(), x.size()); + RTC_DCHECK_EQ(z.size(), y.size()); + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + const __m128 x_j = _mm_loadu_ps(&x[j]); + const __m128 y_j = _mm_loadu_ps(&y[j]); + const __m128 z_j = _mm_mul_ps(x_j, y_j); + _mm_storeu_ps(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] = x[j] * y[j]; + } + } break; + case Aec3Optimization::kAvx2: + MultiplyAVX2(x, y, z); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + const float32x4_t x_j = vld1q_f32(&x[j]); + const float32x4_t y_j = vld1q_f32(&y[j]); + const float32x4_t z_j = vmulq_f32(x_j, y_j); + vst1q_f32(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] = x[j] * y[j]; + } + } break; +#endif + default: + std::transform(x.begin(), x.end(), y.begin(), z.begin(), + std::multiplies()); + } + } + + // Elementwise vector accumulation z += x. + void AccumulateAVX2(rtc::ArrayView x, rtc::ArrayView z); + void Accumulate(rtc::ArrayView x, rtc::ArrayView z) { + RTC_DCHECK_EQ(z.size(), x.size()); + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Aec3Optimization::kSse2: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + const __m128 x_j = _mm_loadu_ps(&x[j]); + __m128 z_j = _mm_loadu_ps(&z[j]); + z_j = _mm_add_ps(x_j, z_j); + _mm_storeu_ps(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] += x[j]; + } + } break; + case Aec3Optimization::kAvx2: + AccumulateAVX2(x, z); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Aec3Optimization::kNeon: { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 2; + + int j = 0; + for (; j < vector_limit * 4; j += 4) { + const float32x4_t x_j = vld1q_f32(&x[j]); + float32x4_t z_j = vld1q_f32(&z[j]); + z_j = vaddq_f32(z_j, x_j); + vst1q_f32(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] += x[j]; + } + } break; +#endif + default: + std::transform(x.begin(), x.end(), z.begin(), z.begin(), + std::plus()); + } + } + + private: + Aec3Optimization optimization_; +}; + +} // namespace aec3 + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_VECTOR_MATH_H_ diff --git a/webrtc/modules/audio_processing/aec3/vector_math_avx2.cc b/webrtc/modules/audio_processing/aec3/vector_math_avx2.cc new file mode 100644 index 0000000..0b5f3c1 --- /dev/null +++ b/webrtc/modules/audio_processing/aec3/vector_math_avx2.cc @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 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 "modules/audio_processing/aec3/vector_math.h" + +#include +#include + +#include "api/array_view.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace aec3 { + +// Elementwise square root. +void VectorMath::SqrtAVX2(rtc::ArrayView x) { + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 3; + + int j = 0; + for (; j < vector_limit * 8; j += 8) { + __m256 g = _mm256_loadu_ps(&x[j]); + g = _mm256_sqrt_ps(g); + _mm256_storeu_ps(&x[j], g); + } + + for (; j < x_size; ++j) { + x[j] = sqrtf(x[j]); + } +} + +// Elementwise vector multiplication z = x * y. +void VectorMath::MultiplyAVX2(rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView z) { + RTC_DCHECK_EQ(z.size(), x.size()); + RTC_DCHECK_EQ(z.size(), y.size()); + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 3; + + int j = 0; + for (; j < vector_limit * 8; j += 8) { + const __m256 x_j = _mm256_loadu_ps(&x[j]); + const __m256 y_j = _mm256_loadu_ps(&y[j]); + const __m256 z_j = _mm256_mul_ps(x_j, y_j); + _mm256_storeu_ps(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] = x[j] * y[j]; + } +} + +// Elementwise vector accumulation z += x. +void VectorMath::AccumulateAVX2(rtc::ArrayView x, + rtc::ArrayView z) { + RTC_DCHECK_EQ(z.size(), x.size()); + const int x_size = static_cast(x.size()); + const int vector_limit = x_size >> 3; + + int j = 0; + for (; j < vector_limit * 8; j += 8) { + const __m256 x_j = _mm256_loadu_ps(&x[j]); + __m256 z_j = _mm256_loadu_ps(&z[j]); + z_j = _mm256_add_ps(x_j, z_j); + _mm256_storeu_ps(&z[j], z_j); + } + + for (; j < x_size; ++j) { + z[j] += x[j]; + } +} + +} // namespace aec3 +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec_dump/BUILD.gn b/webrtc/modules/audio_processing/aec_dump/BUILD.gn new file mode 100644 index 0000000..9887f7d --- /dev/null +++ b/webrtc/modules/audio_processing/aec_dump/BUILD.gn @@ -0,0 +1,110 @@ +# Copyright (c) 2017 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. + +import("../../../webrtc.gni") # This contains def of 'rtc_enable_protobuf' + +rtc_source_set("aec_dump") { + visibility = [ "*" ] + sources = [ "aec_dump_factory.h" ] + + deps = [ + "..:aec_dump_interface", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base/system:file_wrapper", + "../../../rtc_base/system:rtc_export", + ] +} + +if (rtc_include_tests) { + rtc_library("mock_aec_dump") { + testonly = true + sources = [ + "mock_aec_dump.cc", + "mock_aec_dump.h", + ] + + deps = [ + "..:aec_dump_interface", + "..:audioproc_test_utils", + "../", + "../../../test:test_support", + ] + } + + rtc_library("mock_aec_dump_unittests") { + testonly = true + configs += [ "..:apm_debug_dump" ] + sources = [ "aec_dump_integration_test.cc" ] + + deps = [ + ":mock_aec_dump", + "..:api", + "..:audioproc_test_utils", + "../", + "../../../rtc_base:rtc_base_approved", + "//testing/gtest", + ] + } +} + +if (rtc_enable_protobuf) { + rtc_library("aec_dump_impl") { + sources = [ + "aec_dump_impl.cc", + "aec_dump_impl.h", + "capture_stream_info.cc", + "capture_stream_info.h", + "write_to_file_task.cc", + "write_to_file_task.h", + ] + + deps = [ + ":aec_dump", + "..:aec_dump_interface", + "../../../api/audio:audio_frame_api", + "../../../api/task_queue", + "../../../rtc_base:checks", + "../../../rtc_base:ignore_wundef", + "../../../rtc_base:protobuf_utils", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:rtc_task_queue", + "../../../rtc_base/system:file_wrapper", + "../../../system_wrappers", + ] + + deps += [ "../:audioproc_debug_proto" ] + } + + if (rtc_include_tests) { + rtc_library("aec_dump_unittests") { + testonly = true + defines = [] + deps = [ + ":aec_dump", + ":aec_dump_impl", + "..:audioproc_debug_proto", + "../", + "../../../rtc_base:task_queue_for_test", + "../../../test:fileutils", + "../../../test:test_support", + "//testing/gtest", + ] + sources = [ "aec_dump_unittest.cc" ] + } + } +} + +rtc_library("null_aec_dump_factory") { + assert_no_deps = [ ":aec_dump_impl" ] + sources = [ "null_aec_dump_factory.cc" ] + + deps = [ + ":aec_dump", + "..:aec_dump_interface", + ] +} diff --git a/webrtc/modules/audio_processing/aec_dump/aec_dump_factory.h b/webrtc/modules/audio_processing/aec_dump/aec_dump_factory.h new file mode 100644 index 0000000..429a8a5 --- /dev/null +++ b/webrtc/modules/audio_processing/aec_dump/aec_dump_factory.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC_DUMP_AEC_DUMP_FACTORY_H_ +#define MODULES_AUDIO_PROCESSING_AEC_DUMP_AEC_DUMP_FACTORY_H_ + +#include +#include + +#include "modules/audio_processing/include/aec_dump.h" +#include "rtc_base/system/file_wrapper.h" +#include "rtc_base/system/rtc_export.h" + +namespace rtc { +class TaskQueue; +} // namespace rtc + +namespace webrtc { + +class RTC_EXPORT AecDumpFactory { + public: + // The |worker_queue| may not be null and must outlive the created + // AecDump instance. |max_log_size_bytes == -1| means the log size + // will be unlimited. |handle| may not be null. The AecDump takes + // responsibility for |handle| and closes it in the destructor. A + // non-null return value indicates that the file has been + // sucessfully opened. + static std::unique_ptr Create(webrtc::FileWrapper file, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue); + static std::unique_ptr Create(std::string file_name, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue); + static std::unique_ptr Create(FILE* handle, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC_DUMP_AEC_DUMP_FACTORY_H_ diff --git a/webrtc/modules/audio_processing/aec_dump/null_aec_dump_factory.cc b/webrtc/modules/audio_processing/aec_dump/null_aec_dump_factory.cc new file mode 100644 index 0000000..126adeb --- /dev/null +++ b/webrtc/modules/audio_processing/aec_dump/null_aec_dump_factory.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/aec_dump/aec_dump_factory.h" +#include "modules/audio_processing/include/aec_dump.h" + +namespace webrtc { + +std::unique_ptr AecDumpFactory::Create(webrtc::FileWrapper file, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue) { + return nullptr; +} + +std::unique_ptr AecDumpFactory::Create(std::string file_name, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue) { + return nullptr; +} + +std::unique_ptr AecDumpFactory::Create(FILE* handle, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue) { + return nullptr; +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aecm/BUILD.gn b/webrtc/modules/audio_processing/aecm/BUILD.gn new file mode 100644 index 0000000..61e9aff --- /dev/null +++ b/webrtc/modules/audio_processing/aecm/BUILD.gn @@ -0,0 +1,44 @@ +# Copyright (c) 2018 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. + +import("../../../webrtc.gni") + +rtc_library("aecm_core") { + sources = [ + "aecm_core.cc", + "aecm_core.h", + "aecm_defines.h", + "echo_control_mobile.cc", + "echo_control_mobile.h", + ] + deps = [ + "../../../common_audio:common_audio_c", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:sanitizer", + "../../../system_wrappers", + "../utility:legacy_delay_estimator", + ] + cflags = [] + + if (rtc_build_with_neon) { + sources += [ "aecm_core_neon.cc" ] + + if (current_cpu != "arm64") { + # Enable compilation for the NEON instruction set. + suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] + cflags += [ "-mfpu=neon" ] + } + } + + if (current_cpu == "mipsel") { + sources += [ "aecm_core_mips.cc" ] + } else { + sources += [ "aecm_core_c.cc" ] + } +} diff --git a/webrtc/modules/audio_processing/aecm/aecm_core.c b/webrtc/modules/audio_processing/aecm/aecm_core.c deleted file mode 100644 index f0d85d5..0000000 --- a/webrtc/modules/audio_processing/aecm/aecm_core.c +++ /dev/null @@ -1,1233 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_processing/aecm/aecm_core.h" - -#include -#include -#include - -#include "webrtc/common_audio/ring_buffer.h" -#include "webrtc/common_audio/signal_processing/include/real_fft.h" -#include "webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h" -#include "webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h" -#include "webrtc/system_wrappers/include/compile_assert_c.h" -#include "webrtc/system_wrappers/include/cpu_features_wrapper.h" -#include "webrtc/typedefs.h" - -#ifdef AEC_DEBUG -FILE *dfile; -FILE *testfile; -#endif - -const int16_t WebRtcAecm_kCosTable[] = { - 8192, 8190, 8187, 8180, 8172, 8160, 8147, 8130, 8112, - 8091, 8067, 8041, 8012, 7982, 7948, 7912, 7874, 7834, - 7791, 7745, 7697, 7647, 7595, 7540, 7483, 7424, 7362, - 7299, 7233, 7164, 7094, 7021, 6947, 6870, 6791, 6710, - 6627, 6542, 6455, 6366, 6275, 6182, 6087, 5991, 5892, - 5792, 5690, 5586, 5481, 5374, 5265, 5155, 5043, 4930, - 4815, 4698, 4580, 4461, 4341, 4219, 4096, 3971, 3845, - 3719, 3591, 3462, 3331, 3200, 3068, 2935, 2801, 2667, - 2531, 2395, 2258, 2120, 1981, 1842, 1703, 1563, 1422, - 1281, 1140, 998, 856, 713, 571, 428, 285, 142, - 0, -142, -285, -428, -571, -713, -856, -998, -1140, - -1281, -1422, -1563, -1703, -1842, -1981, -2120, -2258, -2395, - -2531, -2667, -2801, -2935, -3068, -3200, -3331, -3462, -3591, - -3719, -3845, -3971, -4095, -4219, -4341, -4461, -4580, -4698, - -4815, -4930, -5043, -5155, -5265, -5374, -5481, -5586, -5690, - -5792, -5892, -5991, -6087, -6182, -6275, -6366, -6455, -6542, - -6627, -6710, -6791, -6870, -6947, -7021, -7094, -7164, -7233, - -7299, -7362, -7424, -7483, -7540, -7595, -7647, -7697, -7745, - -7791, -7834, -7874, -7912, -7948, -7982, -8012, -8041, -8067, - -8091, -8112, -8130, -8147, -8160, -8172, -8180, -8187, -8190, - -8191, -8190, -8187, -8180, -8172, -8160, -8147, -8130, -8112, - -8091, -8067, -8041, -8012, -7982, -7948, -7912, -7874, -7834, - -7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424, -7362, - -7299, -7233, -7164, -7094, -7021, -6947, -6870, -6791, -6710, - -6627, -6542, -6455, -6366, -6275, -6182, -6087, -5991, -5892, - -5792, -5690, -5586, -5481, -5374, -5265, -5155, -5043, -4930, - -4815, -4698, -4580, -4461, -4341, -4219, -4096, -3971, -3845, - -3719, -3591, -3462, -3331, -3200, -3068, -2935, -2801, -2667, - -2531, -2395, -2258, -2120, -1981, -1842, -1703, -1563, -1422, - -1281, -1140, -998, -856, -713, -571, -428, -285, -142, - 0, 142, 285, 428, 571, 713, 856, 998, 1140, - 1281, 1422, 1563, 1703, 1842, 1981, 2120, 2258, 2395, - 2531, 2667, 2801, 2935, 3068, 3200, 3331, 3462, 3591, - 3719, 3845, 3971, 4095, 4219, 4341, 4461, 4580, 4698, - 4815, 4930, 5043, 5155, 5265, 5374, 5481, 5586, 5690, - 5792, 5892, 5991, 6087, 6182, 6275, 6366, 6455, 6542, - 6627, 6710, 6791, 6870, 6947, 7021, 7094, 7164, 7233, - 7299, 7362, 7424, 7483, 7540, 7595, 7647, 7697, 7745, - 7791, 7834, 7874, 7912, 7948, 7982, 8012, 8041, 8067, - 8091, 8112, 8130, 8147, 8160, 8172, 8180, 8187, 8190 -}; - -const int16_t WebRtcAecm_kSinTable[] = { - 0, 142, 285, 428, 571, 713, 856, 998, - 1140, 1281, 1422, 1563, 1703, 1842, 1981, 2120, - 2258, 2395, 2531, 2667, 2801, 2935, 3068, 3200, - 3331, 3462, 3591, 3719, 3845, 3971, 4095, 4219, - 4341, 4461, 4580, 4698, 4815, 4930, 5043, 5155, - 5265, 5374, 5481, 5586, 5690, 5792, 5892, 5991, - 6087, 6182, 6275, 6366, 6455, 6542, 6627, 6710, - 6791, 6870, 6947, 7021, 7094, 7164, 7233, 7299, - 7362, 7424, 7483, 7540, 7595, 7647, 7697, 7745, - 7791, 7834, 7874, 7912, 7948, 7982, 8012, 8041, - 8067, 8091, 8112, 8130, 8147, 8160, 8172, 8180, - 8187, 8190, 8191, 8190, 8187, 8180, 8172, 8160, - 8147, 8130, 8112, 8091, 8067, 8041, 8012, 7982, - 7948, 7912, 7874, 7834, 7791, 7745, 7697, 7647, - 7595, 7540, 7483, 7424, 7362, 7299, 7233, 7164, - 7094, 7021, 6947, 6870, 6791, 6710, 6627, 6542, - 6455, 6366, 6275, 6182, 6087, 5991, 5892, 5792, - 5690, 5586, 5481, 5374, 5265, 5155, 5043, 4930, - 4815, 4698, 4580, 4461, 4341, 4219, 4096, 3971, - 3845, 3719, 3591, 3462, 3331, 3200, 3068, 2935, - 2801, 2667, 2531, 2395, 2258, 2120, 1981, 1842, - 1703, 1563, 1422, 1281, 1140, 998, 856, 713, - 571, 428, 285, 142, 0, -142, -285, -428, - -571, -713, -856, -998, -1140, -1281, -1422, -1563, - -1703, -1842, -1981, -2120, -2258, -2395, -2531, -2667, - -2801, -2935, -3068, -3200, -3331, -3462, -3591, -3719, - -3845, -3971, -4095, -4219, -4341, -4461, -4580, -4698, - -4815, -4930, -5043, -5155, -5265, -5374, -5481, -5586, - -5690, -5792, -5892, -5991, -6087, -6182, -6275, -6366, - -6455, -6542, -6627, -6710, -6791, -6870, -6947, -7021, - -7094, -7164, -7233, -7299, -7362, -7424, -7483, -7540, - -7595, -7647, -7697, -7745, -7791, -7834, -7874, -7912, - -7948, -7982, -8012, -8041, -8067, -8091, -8112, -8130, - -8147, -8160, -8172, -8180, -8187, -8190, -8191, -8190, - -8187, -8180, -8172, -8160, -8147, -8130, -8112, -8091, - -8067, -8041, -8012, -7982, -7948, -7912, -7874, -7834, - -7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424, - -7362, -7299, -7233, -7164, -7094, -7021, -6947, -6870, - -6791, -6710, -6627, -6542, -6455, -6366, -6275, -6182, - -6087, -5991, -5892, -5792, -5690, -5586, -5481, -5374, - -5265, -5155, -5043, -4930, -4815, -4698, -4580, -4461, - -4341, -4219, -4096, -3971, -3845, -3719, -3591, -3462, - -3331, -3200, -3068, -2935, -2801, -2667, -2531, -2395, - -2258, -2120, -1981, -1842, -1703, -1563, -1422, -1281, - -1140, -998, -856, -713, -571, -428, -285, -142 -}; - -// Initialization table for echo channel in 8 kHz -static const int16_t kChannelStored8kHz[PART_LEN1] = { - 2040, 1815, 1590, 1498, 1405, 1395, 1385, 1418, - 1451, 1506, 1562, 1644, 1726, 1804, 1882, 1918, - 1953, 1982, 2010, 2025, 2040, 2034, 2027, 2021, - 2014, 1997, 1980, 1925, 1869, 1800, 1732, 1683, - 1635, 1604, 1572, 1545, 1517, 1481, 1444, 1405, - 1367, 1331, 1294, 1270, 1245, 1239, 1233, 1247, - 1260, 1282, 1303, 1338, 1373, 1407, 1441, 1470, - 1499, 1524, 1549, 1565, 1582, 1601, 1621, 1649, - 1676 -}; - -// Initialization table for echo channel in 16 kHz -static const int16_t kChannelStored16kHz[PART_LEN1] = { - 2040, 1590, 1405, 1385, 1451, 1562, 1726, 1882, - 1953, 2010, 2040, 2027, 2014, 1980, 1869, 1732, - 1635, 1572, 1517, 1444, 1367, 1294, 1245, 1233, - 1260, 1303, 1373, 1441, 1499, 1549, 1582, 1621, - 1676, 1741, 1802, 1861, 1921, 1983, 2040, 2102, - 2170, 2265, 2375, 2515, 2651, 2781, 2922, 3075, - 3253, 3471, 3738, 3976, 4151, 4258, 4308, 4288, - 4270, 4253, 4237, 4179, 4086, 3947, 3757, 3484, - 3153 -}; - -// Moves the pointer to the next entry and inserts |far_spectrum| and -// corresponding Q-domain in its buffer. -// -// Inputs: -// - self : Pointer to the delay estimation instance -// - far_spectrum : Pointer to the far end spectrum -// - far_q : Q-domain of far end spectrum -// -void WebRtcAecm_UpdateFarHistory(AecmCore* self, - uint16_t* far_spectrum, - int far_q) { - // Get new buffer position - self->far_history_pos++; - if (self->far_history_pos >= MAX_DELAY) { - self->far_history_pos = 0; - } - // Update Q-domain buffer - self->far_q_domains[self->far_history_pos] = far_q; - // Update far end spectrum buffer - memcpy(&(self->far_history[self->far_history_pos * PART_LEN1]), - far_spectrum, - sizeof(uint16_t) * PART_LEN1); -} - -// Returns a pointer to the far end spectrum aligned to current near end -// spectrum. The function WebRtc_DelayEstimatorProcessFix(...) should have been -// called before AlignedFarend(...). Otherwise, you get the pointer to the -// previous frame. The memory is only valid until the next call of -// WebRtc_DelayEstimatorProcessFix(...). -// -// Inputs: -// - self : Pointer to the AECM instance. -// - delay : Current delay estimate. -// -// Output: -// - far_q : The Q-domain of the aligned far end spectrum -// -// Return value: -// - far_spectrum : Pointer to the aligned far end spectrum -// NULL - Error -// -const uint16_t* WebRtcAecm_AlignedFarend(AecmCore* self, - int* far_q, - int delay) { - int buffer_position = 0; - assert(self != NULL); - buffer_position = self->far_history_pos - delay; - - // Check buffer position - if (buffer_position < 0) { - buffer_position += MAX_DELAY; - } - // Get Q-domain - *far_q = self->far_q_domains[buffer_position]; - // Return far end spectrum - return &(self->far_history[buffer_position * PART_LEN1]); -} - -// Declare function pointers. -CalcLinearEnergies WebRtcAecm_CalcLinearEnergies; -StoreAdaptiveChannel WebRtcAecm_StoreAdaptiveChannel; -ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel; - -AecmCore* WebRtcAecm_CreateCore() { - AecmCore* aecm = malloc(sizeof(AecmCore)); - - aecm->farFrameBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, - sizeof(int16_t)); - if (!aecm->farFrameBuf) - { - WebRtcAecm_FreeCore(aecm); - return NULL; - } - - aecm->nearNoisyFrameBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, - sizeof(int16_t)); - if (!aecm->nearNoisyFrameBuf) - { - WebRtcAecm_FreeCore(aecm); - return NULL; - } - - aecm->nearCleanFrameBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, - sizeof(int16_t)); - if (!aecm->nearCleanFrameBuf) - { - WebRtcAecm_FreeCore(aecm); - return NULL; - } - - aecm->outFrameBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, - sizeof(int16_t)); - if (!aecm->outFrameBuf) - { - WebRtcAecm_FreeCore(aecm); - return NULL; - } - - aecm->delay_estimator_farend = WebRtc_CreateDelayEstimatorFarend(PART_LEN1, - MAX_DELAY); - if (aecm->delay_estimator_farend == NULL) { - WebRtcAecm_FreeCore(aecm); - return NULL; - } - aecm->delay_estimator = - WebRtc_CreateDelayEstimator(aecm->delay_estimator_farend, 0); - if (aecm->delay_estimator == NULL) { - WebRtcAecm_FreeCore(aecm); - return NULL; - } - // TODO(bjornv): Explicitly disable robust delay validation until no - // performance regression has been established. Then remove the line. - WebRtc_enable_robust_validation(aecm->delay_estimator, 0); - - aecm->real_fft = WebRtcSpl_CreateRealFFT(PART_LEN_SHIFT); - if (aecm->real_fft == NULL) { - WebRtcAecm_FreeCore(aecm); - return NULL; - } - - // Init some aecm pointers. 16 and 32 byte alignment is only necessary - // for Neon code currently. - aecm->xBuf = (int16_t*) (((uintptr_t)aecm->xBuf_buf + 31) & ~ 31); - aecm->dBufClean = (int16_t*) (((uintptr_t)aecm->dBufClean_buf + 31) & ~ 31); - aecm->dBufNoisy = (int16_t*) (((uintptr_t)aecm->dBufNoisy_buf + 31) & ~ 31); - aecm->outBuf = (int16_t*) (((uintptr_t)aecm->outBuf_buf + 15) & ~ 15); - aecm->channelStored = (int16_t*) (((uintptr_t) - aecm->channelStored_buf + 15) & ~ 15); - aecm->channelAdapt16 = (int16_t*) (((uintptr_t) - aecm->channelAdapt16_buf + 15) & ~ 15); - aecm->channelAdapt32 = (int32_t*) (((uintptr_t) - aecm->channelAdapt32_buf + 31) & ~ 31); - - return aecm; -} - -void WebRtcAecm_InitEchoPathCore(AecmCore* aecm, const int16_t* echo_path) { - int i = 0; - - // Reset the stored channel - memcpy(aecm->channelStored, echo_path, sizeof(int16_t) * PART_LEN1); - // Reset the adapted channels - memcpy(aecm->channelAdapt16, echo_path, sizeof(int16_t) * PART_LEN1); - for (i = 0; i < PART_LEN1; i++) - { - aecm->channelAdapt32[i] = (int32_t)aecm->channelAdapt16[i] << 16; - } - - // Reset channel storing variables - aecm->mseAdaptOld = 1000; - aecm->mseStoredOld = 1000; - aecm->mseThreshold = WEBRTC_SPL_WORD32_MAX; - aecm->mseChannelCount = 0; -} - -static void CalcLinearEnergiesC(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est, - uint32_t* far_energy, - uint32_t* echo_energy_adapt, - uint32_t* echo_energy_stored) { - int i; - - // Get energy for the delayed far end signal and estimated - // echo using both stored and adapted channels. - for (i = 0; i < PART_LEN1; i++) - { - echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - far_spectrum[i]); - (*far_energy) += (uint32_t)(far_spectrum[i]); - *echo_energy_adapt += aecm->channelAdapt16[i] * far_spectrum[i]; - (*echo_energy_stored) += (uint32_t)echo_est[i]; - } -} - -static void StoreAdaptiveChannelC(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est) { - int i; - - // During startup we store the channel every block. - memcpy(aecm->channelStored, aecm->channelAdapt16, sizeof(int16_t) * PART_LEN1); - // Recalculate echo estimate - for (i = 0; i < PART_LEN; i += 4) - { - echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - far_spectrum[i]); - echo_est[i + 1] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 1], - far_spectrum[i + 1]); - echo_est[i + 2] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 2], - far_spectrum[i + 2]); - echo_est[i + 3] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 3], - far_spectrum[i + 3]); - } - echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - far_spectrum[i]); -} - -static void ResetAdaptiveChannelC(AecmCore* aecm) { - int i; - - // The stored channel has a significantly lower MSE than the adaptive one for - // two consecutive calculations. Reset the adaptive channel. - memcpy(aecm->channelAdapt16, aecm->channelStored, - sizeof(int16_t) * PART_LEN1); - // Restore the W32 channel - for (i = 0; i < PART_LEN; i += 4) - { - aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; - aecm->channelAdapt32[i + 1] = (int32_t)aecm->channelStored[i + 1] << 16; - aecm->channelAdapt32[i + 2] = (int32_t)aecm->channelStored[i + 2] << 16; - aecm->channelAdapt32[i + 3] = (int32_t)aecm->channelStored[i + 3] << 16; - } - aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; -} - -// Initialize function pointers for ARM Neon platform. -#if (defined WEBRTC_DETECT_NEON || defined WEBRTC_HAS_NEON) -static void WebRtcAecm_InitNeon(void) -{ - WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannelNeon; - WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannelNeon; - WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergiesNeon; -} -#endif - -// Initialize function pointers for MIPS platform. -#if defined(MIPS32_LE) -static void WebRtcAecm_InitMips(void) -{ -#if defined(MIPS_DSP_R1_LE) - WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannel_mips; - WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannel_mips; -#endif - WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergies_mips; -} -#endif - -// WebRtcAecm_InitCore(...) -// -// This function initializes the AECM instant created with WebRtcAecm_CreateCore(...) -// Input: -// - aecm : Pointer to the Echo Suppression instance -// - samplingFreq : Sampling Frequency -// -// Output: -// - aecm : Initialized instance -// -// Return value : 0 - Ok -// -1 - Error -// -int WebRtcAecm_InitCore(AecmCore* const aecm, int samplingFreq) { - int i = 0; - int32_t tmp32 = PART_LEN1 * PART_LEN1; - int16_t tmp16 = PART_LEN1; - - if (samplingFreq != 8000 && samplingFreq != 16000) - { - samplingFreq = 8000; - return -1; - } - // sanity check of sampling frequency - aecm->mult = (int16_t)samplingFreq / 8000; - - aecm->farBufWritePos = 0; - aecm->farBufReadPos = 0; - aecm->knownDelay = 0; - aecm->lastKnownDelay = 0; - - WebRtc_InitBuffer(aecm->farFrameBuf); - WebRtc_InitBuffer(aecm->nearNoisyFrameBuf); - WebRtc_InitBuffer(aecm->nearCleanFrameBuf); - WebRtc_InitBuffer(aecm->outFrameBuf); - - memset(aecm->xBuf_buf, 0, sizeof(aecm->xBuf_buf)); - memset(aecm->dBufClean_buf, 0, sizeof(aecm->dBufClean_buf)); - memset(aecm->dBufNoisy_buf, 0, sizeof(aecm->dBufNoisy_buf)); - memset(aecm->outBuf_buf, 0, sizeof(aecm->outBuf_buf)); - - aecm->seed = 666; - aecm->totCount = 0; - - if (WebRtc_InitDelayEstimatorFarend(aecm->delay_estimator_farend) != 0) { - return -1; - } - if (WebRtc_InitDelayEstimator(aecm->delay_estimator) != 0) { - return -1; - } - // Set far end histories to zero - memset(aecm->far_history, 0, sizeof(uint16_t) * PART_LEN1 * MAX_DELAY); - memset(aecm->far_q_domains, 0, sizeof(int) * MAX_DELAY); - aecm->far_history_pos = MAX_DELAY; - - aecm->nlpFlag = 1; - aecm->fixedDelay = -1; - - aecm->dfaCleanQDomain = 0; - aecm->dfaCleanQDomainOld = 0; - aecm->dfaNoisyQDomain = 0; - aecm->dfaNoisyQDomainOld = 0; - - memset(aecm->nearLogEnergy, 0, sizeof(aecm->nearLogEnergy)); - aecm->farLogEnergy = 0; - memset(aecm->echoAdaptLogEnergy, 0, sizeof(aecm->echoAdaptLogEnergy)); - memset(aecm->echoStoredLogEnergy, 0, sizeof(aecm->echoStoredLogEnergy)); - - // Initialize the echo channels with a stored shape. - if (samplingFreq == 8000) - { - WebRtcAecm_InitEchoPathCore(aecm, kChannelStored8kHz); - } - else - { - WebRtcAecm_InitEchoPathCore(aecm, kChannelStored16kHz); - } - - memset(aecm->echoFilt, 0, sizeof(aecm->echoFilt)); - memset(aecm->nearFilt, 0, sizeof(aecm->nearFilt)); - aecm->noiseEstCtr = 0; - - aecm->cngMode = AecmTrue; - - memset(aecm->noiseEstTooLowCtr, 0, sizeof(aecm->noiseEstTooLowCtr)); - memset(aecm->noiseEstTooHighCtr, 0, sizeof(aecm->noiseEstTooHighCtr)); - // Shape the initial noise level to an approximate pink noise. - for (i = 0; i < (PART_LEN1 >> 1) - 1; i++) - { - aecm->noiseEst[i] = (tmp32 << 8); - tmp16--; - tmp32 -= (int32_t)((tmp16 << 1) + 1); - } - for (; i < PART_LEN1; i++) - { - aecm->noiseEst[i] = (tmp32 << 8); - } - - aecm->farEnergyMin = WEBRTC_SPL_WORD16_MAX; - aecm->farEnergyMax = WEBRTC_SPL_WORD16_MIN; - aecm->farEnergyMaxMin = 0; - aecm->farEnergyVAD = FAR_ENERGY_MIN; // This prevents false speech detection at the - // beginning. - aecm->farEnergyMSE = 0; - aecm->currentVADValue = 0; - aecm->vadUpdateCount = 0; - aecm->firstVAD = 1; - - aecm->startupState = 0; - aecm->supGain = SUPGAIN_DEFAULT; - aecm->supGainOld = SUPGAIN_DEFAULT; - - aecm->supGainErrParamA = SUPGAIN_ERROR_PARAM_A; - aecm->supGainErrParamD = SUPGAIN_ERROR_PARAM_D; - aecm->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B; - aecm->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D; - - // Assert a preprocessor definition at compile-time. It's an assumption - // used in assembly code, so check the assembly files before any change. - COMPILE_ASSERT(PART_LEN % 16 == 0); - - // Initialize function pointers. - WebRtcAecm_CalcLinearEnergies = CalcLinearEnergiesC; - WebRtcAecm_StoreAdaptiveChannel = StoreAdaptiveChannelC; - WebRtcAecm_ResetAdaptiveChannel = ResetAdaptiveChannelC; - -#ifdef WEBRTC_DETECT_NEON - uint64_t features = WebRtc_GetCPUFeaturesARM(); - if ((features & kCPUFeatureNEON) != 0) - { - WebRtcAecm_InitNeon(); - } -#elif defined(WEBRTC_HAS_NEON) - WebRtcAecm_InitNeon(); -#endif - -#if defined(MIPS32_LE) - WebRtcAecm_InitMips(); -#endif - return 0; -} - -// TODO(bjornv): This function is currently not used. Add support for these -// parameters from a higher level -int WebRtcAecm_Control(AecmCore* aecm, int delay, int nlpFlag) { - aecm->nlpFlag = nlpFlag; - aecm->fixedDelay = delay; - - return 0; -} - -void WebRtcAecm_FreeCore(AecmCore* aecm) { - if (aecm == NULL) { - return; - } - - WebRtc_FreeBuffer(aecm->farFrameBuf); - WebRtc_FreeBuffer(aecm->nearNoisyFrameBuf); - WebRtc_FreeBuffer(aecm->nearCleanFrameBuf); - WebRtc_FreeBuffer(aecm->outFrameBuf); - - WebRtc_FreeDelayEstimator(aecm->delay_estimator); - WebRtc_FreeDelayEstimatorFarend(aecm->delay_estimator_farend); - WebRtcSpl_FreeRealFFT(aecm->real_fft); - - free(aecm); -} - -int WebRtcAecm_ProcessFrame(AecmCore* aecm, - const int16_t* farend, - const int16_t* nearendNoisy, - const int16_t* nearendClean, - int16_t* out) { - int16_t outBlock_buf[PART_LEN + 8]; // Align buffer to 8-byte boundary. - int16_t* outBlock = (int16_t*) (((uintptr_t) outBlock_buf + 15) & ~ 15); - - int16_t farFrame[FRAME_LEN]; - const int16_t* out_ptr = NULL; - int size = 0; - - // Buffer the current frame. - // Fetch an older one corresponding to the delay. - WebRtcAecm_BufferFarFrame(aecm, farend, FRAME_LEN); - WebRtcAecm_FetchFarFrame(aecm, farFrame, FRAME_LEN, aecm->knownDelay); - - // Buffer the synchronized far and near frames, - // to pass the smaller blocks individually. - WebRtc_WriteBuffer(aecm->farFrameBuf, farFrame, FRAME_LEN); - WebRtc_WriteBuffer(aecm->nearNoisyFrameBuf, nearendNoisy, FRAME_LEN); - if (nearendClean != NULL) - { - WebRtc_WriteBuffer(aecm->nearCleanFrameBuf, nearendClean, FRAME_LEN); - } - - // Process as many blocks as possible. - while (WebRtc_available_read(aecm->farFrameBuf) >= PART_LEN) - { - int16_t far_block[PART_LEN]; - const int16_t* far_block_ptr = NULL; - int16_t near_noisy_block[PART_LEN]; - const int16_t* near_noisy_block_ptr = NULL; - - WebRtc_ReadBuffer(aecm->farFrameBuf, (void**) &far_block_ptr, far_block, - PART_LEN); - WebRtc_ReadBuffer(aecm->nearNoisyFrameBuf, - (void**) &near_noisy_block_ptr, - near_noisy_block, - PART_LEN); - if (nearendClean != NULL) - { - int16_t near_clean_block[PART_LEN]; - const int16_t* near_clean_block_ptr = NULL; - - WebRtc_ReadBuffer(aecm->nearCleanFrameBuf, - (void**) &near_clean_block_ptr, - near_clean_block, - PART_LEN); - if (WebRtcAecm_ProcessBlock(aecm, - far_block_ptr, - near_noisy_block_ptr, - near_clean_block_ptr, - outBlock) == -1) - { - return -1; - } - } else - { - if (WebRtcAecm_ProcessBlock(aecm, - far_block_ptr, - near_noisy_block_ptr, - NULL, - outBlock) == -1) - { - return -1; - } - } - - WebRtc_WriteBuffer(aecm->outFrameBuf, outBlock, PART_LEN); - } - - // Stuff the out buffer if we have less than a frame to output. - // This should only happen for the first frame. - size = (int) WebRtc_available_read(aecm->outFrameBuf); - if (size < FRAME_LEN) - { - WebRtc_MoveReadPtr(aecm->outFrameBuf, size - FRAME_LEN); - } - - // Obtain an output frame. - WebRtc_ReadBuffer(aecm->outFrameBuf, (void**) &out_ptr, out, FRAME_LEN); - if (out_ptr != out) { - // ReadBuffer() hasn't copied to |out| in this case. - memcpy(out, out_ptr, FRAME_LEN * sizeof(int16_t)); - } - - return 0; -} - -// WebRtcAecm_AsymFilt(...) -// -// Performs asymmetric filtering. -// -// Inputs: -// - filtOld : Previous filtered value. -// - inVal : New input value. -// - stepSizePos : Step size when we have a positive contribution. -// - stepSizeNeg : Step size when we have a negative contribution. -// -// Output: -// -// Return: - Filtered value. -// -int16_t WebRtcAecm_AsymFilt(const int16_t filtOld, const int16_t inVal, - const int16_t stepSizePos, - const int16_t stepSizeNeg) -{ - int16_t retVal; - - if ((filtOld == WEBRTC_SPL_WORD16_MAX) | (filtOld == WEBRTC_SPL_WORD16_MIN)) - { - return inVal; - } - retVal = filtOld; - if (filtOld > inVal) - { - retVal -= (filtOld - inVal) >> stepSizeNeg; - } else - { - retVal += (inVal - filtOld) >> stepSizePos; - } - - return retVal; -} - -// ExtractFractionPart(a, zeros) -// -// returns the fraction part of |a|, with |zeros| number of leading zeros, as an -// int16_t scaled to Q8. There is no sanity check of |a| in the sense that the -// number of zeros match. -static int16_t ExtractFractionPart(uint32_t a, int zeros) { - return (int16_t)(((a << zeros) & 0x7FFFFFFF) >> 23); -} - -// Calculates and returns the log of |energy| in Q8. The input |energy| is -// supposed to be in Q(|q_domain|). -static int16_t LogOfEnergyInQ8(uint32_t energy, int q_domain) { - static const int16_t kLogLowValue = PART_LEN_SHIFT << 7; - int16_t log_energy_q8 = kLogLowValue; - if (energy > 0) { - int zeros = WebRtcSpl_NormU32(energy); - int16_t frac = ExtractFractionPart(energy, zeros); - // log2 of |energy| in Q8. - log_energy_q8 += ((31 - zeros) << 8) + frac - (q_domain << 8); - } - return log_energy_q8; -} - -// WebRtcAecm_CalcEnergies(...) -// -// This function calculates the log of energies for nearend, farend and estimated -// echoes. There is also an update of energy decision levels, i.e. internal VAD. -// -// -// @param aecm [i/o] Handle of the AECM instance. -// @param far_spectrum [in] Pointer to farend spectrum. -// @param far_q [in] Q-domain of farend spectrum. -// @param nearEner [in] Near end energy for current block in -// Q(aecm->dfaQDomain). -// @param echoEst [out] Estimated echo in Q(xfa_q+RESOLUTION_CHANNEL16). -// -void WebRtcAecm_CalcEnergies(AecmCore* aecm, - const uint16_t* far_spectrum, - const int16_t far_q, - const uint32_t nearEner, - int32_t* echoEst) { - // Local variables - uint32_t tmpAdapt = 0; - uint32_t tmpStored = 0; - uint32_t tmpFar = 0; - - int i; - - int16_t tmp16; - int16_t increase_max_shifts = 4; - int16_t decrease_max_shifts = 11; - int16_t increase_min_shifts = 11; - int16_t decrease_min_shifts = 3; - - // Get log of near end energy and store in buffer - - // Shift buffer - memmove(aecm->nearLogEnergy + 1, aecm->nearLogEnergy, - sizeof(int16_t) * (MAX_BUF_LEN - 1)); - - // Logarithm of integrated magnitude spectrum (nearEner) - aecm->nearLogEnergy[0] = LogOfEnergyInQ8(nearEner, aecm->dfaNoisyQDomain); - - WebRtcAecm_CalcLinearEnergies(aecm, far_spectrum, echoEst, &tmpFar, &tmpAdapt, &tmpStored); - - // Shift buffers - memmove(aecm->echoAdaptLogEnergy + 1, aecm->echoAdaptLogEnergy, - sizeof(int16_t) * (MAX_BUF_LEN - 1)); - memmove(aecm->echoStoredLogEnergy + 1, aecm->echoStoredLogEnergy, - sizeof(int16_t) * (MAX_BUF_LEN - 1)); - - // Logarithm of delayed far end energy - aecm->farLogEnergy = LogOfEnergyInQ8(tmpFar, far_q); - - // Logarithm of estimated echo energy through adapted channel - aecm->echoAdaptLogEnergy[0] = LogOfEnergyInQ8(tmpAdapt, - RESOLUTION_CHANNEL16 + far_q); - - // Logarithm of estimated echo energy through stored channel - aecm->echoStoredLogEnergy[0] = - LogOfEnergyInQ8(tmpStored, RESOLUTION_CHANNEL16 + far_q); - - // Update farend energy levels (min, max, vad, mse) - if (aecm->farLogEnergy > FAR_ENERGY_MIN) - { - if (aecm->startupState == 0) - { - increase_max_shifts = 2; - decrease_min_shifts = 2; - increase_min_shifts = 8; - } - - aecm->farEnergyMin = WebRtcAecm_AsymFilt(aecm->farEnergyMin, aecm->farLogEnergy, - increase_min_shifts, decrease_min_shifts); - aecm->farEnergyMax = WebRtcAecm_AsymFilt(aecm->farEnergyMax, aecm->farLogEnergy, - increase_max_shifts, decrease_max_shifts); - aecm->farEnergyMaxMin = (aecm->farEnergyMax - aecm->farEnergyMin); - - // Dynamic VAD region size - tmp16 = 2560 - aecm->farEnergyMin; - if (tmp16 > 0) - { - tmp16 = (int16_t)((tmp16 * FAR_ENERGY_VAD_REGION) >> 9); - } else - { - tmp16 = 0; - } - tmp16 += FAR_ENERGY_VAD_REGION; - - if ((aecm->startupState == 0) | (aecm->vadUpdateCount > 1024)) - { - // In startup phase or VAD update halted - aecm->farEnergyVAD = aecm->farEnergyMin + tmp16; - } else - { - if (aecm->farEnergyVAD > aecm->farLogEnergy) - { - aecm->farEnergyVAD += - (aecm->farLogEnergy + tmp16 - aecm->farEnergyVAD) >> 6; - aecm->vadUpdateCount = 0; - } else - { - aecm->vadUpdateCount++; - } - } - // Put MSE threshold higher than VAD - aecm->farEnergyMSE = aecm->farEnergyVAD + (1 << 8); - } - - // Update VAD variables - if (aecm->farLogEnergy > aecm->farEnergyVAD) - { - if ((aecm->startupState == 0) | (aecm->farEnergyMaxMin > FAR_ENERGY_DIFF)) - { - // We are in startup or have significant dynamics in input speech level - aecm->currentVADValue = 1; - } - } else - { - aecm->currentVADValue = 0; - } - if ((aecm->currentVADValue) && (aecm->firstVAD)) - { - aecm->firstVAD = 0; - if (aecm->echoAdaptLogEnergy[0] > aecm->nearLogEnergy[0]) - { - // The estimated echo has higher energy than the near end signal. - // This means that the initialization was too aggressive. Scale - // down by a factor 8 - for (i = 0; i < PART_LEN1; i++) - { - aecm->channelAdapt16[i] >>= 3; - } - // Compensate the adapted echo energy level accordingly. - aecm->echoAdaptLogEnergy[0] -= (3 << 8); - aecm->firstVAD = 1; - } - } -} - -// WebRtcAecm_CalcStepSize(...) -// -// This function calculates the step size used in channel estimation -// -// -// @param aecm [in] Handle of the AECM instance. -// @param mu [out] (Return value) Stepsize in log2(), i.e. number of shifts. -// -// -int16_t WebRtcAecm_CalcStepSize(AecmCore* const aecm) { - int32_t tmp32; - int16_t tmp16; - int16_t mu = MU_MAX; - - // Here we calculate the step size mu used in the - // following NLMS based Channel estimation algorithm - if (!aecm->currentVADValue) - { - // Far end energy level too low, no channel update - mu = 0; - } else if (aecm->startupState > 0) - { - if (aecm->farEnergyMin >= aecm->farEnergyMax) - { - mu = MU_MIN; - } else - { - tmp16 = (aecm->farLogEnergy - aecm->farEnergyMin); - tmp32 = tmp16 * MU_DIFF; - tmp32 = WebRtcSpl_DivW32W16(tmp32, aecm->farEnergyMaxMin); - mu = MU_MIN - 1 - (int16_t)(tmp32); - // The -1 is an alternative to rounding. This way we get a larger - // stepsize, so we in some sense compensate for truncation in NLMS - } - if (mu < MU_MAX) - { - mu = MU_MAX; // Equivalent with maximum step size of 2^-MU_MAX - } - } - - return mu; -} - -// WebRtcAecm_UpdateChannel(...) -// -// This function performs channel estimation. NLMS and decision on channel storage. -// -// -// @param aecm [i/o] Handle of the AECM instance. -// @param far_spectrum [in] Absolute value of the farend signal in Q(far_q) -// @param far_q [in] Q-domain of the farend signal -// @param dfa [in] Absolute value of the nearend signal (Q[aecm->dfaQDomain]) -// @param mu [in] NLMS step size. -// @param echoEst [i/o] Estimated echo in Q(far_q+RESOLUTION_CHANNEL16). -// -void WebRtcAecm_UpdateChannel(AecmCore* aecm, - const uint16_t* far_spectrum, - const int16_t far_q, - const uint16_t* const dfa, - const int16_t mu, - int32_t* echoEst) { - uint32_t tmpU32no1, tmpU32no2; - int32_t tmp32no1, tmp32no2; - int32_t mseStored; - int32_t mseAdapt; - - int i; - - int16_t zerosFar, zerosNum, zerosCh, zerosDfa; - int16_t shiftChFar, shiftNum, shift2ResChan; - int16_t tmp16no1; - int16_t xfaQ, dfaQ; - - // This is the channel estimation algorithm. It is base on NLMS but has a variable step - // length, which was calculated above. - if (mu) - { - for (i = 0; i < PART_LEN1; i++) - { - // Determine norm of channel and farend to make sure we don't get overflow in - // multiplication - zerosCh = WebRtcSpl_NormU32(aecm->channelAdapt32[i]); - zerosFar = WebRtcSpl_NormU32((uint32_t)far_spectrum[i]); - if (zerosCh + zerosFar > 31) - { - // Multiplication is safe - tmpU32no1 = WEBRTC_SPL_UMUL_32_16(aecm->channelAdapt32[i], - far_spectrum[i]); - shiftChFar = 0; - } else - { - // We need to shift down before multiplication - shiftChFar = 32 - zerosCh - zerosFar; - tmpU32no1 = (aecm->channelAdapt32[i] >> shiftChFar) * - far_spectrum[i]; - } - // Determine Q-domain of numerator - zerosNum = WebRtcSpl_NormU32(tmpU32no1); - if (dfa[i]) - { - zerosDfa = WebRtcSpl_NormU32((uint32_t)dfa[i]); - } else - { - zerosDfa = 32; - } - tmp16no1 = zerosDfa - 2 + aecm->dfaNoisyQDomain - - RESOLUTION_CHANNEL32 - far_q + shiftChFar; - if (zerosNum > tmp16no1 + 1) - { - xfaQ = tmp16no1; - dfaQ = zerosDfa - 2; - } else - { - xfaQ = zerosNum - 2; - dfaQ = RESOLUTION_CHANNEL32 + far_q - aecm->dfaNoisyQDomain - - shiftChFar + xfaQ; - } - // Add in the same Q-domain - tmpU32no1 = WEBRTC_SPL_SHIFT_W32(tmpU32no1, xfaQ); - tmpU32no2 = WEBRTC_SPL_SHIFT_W32((uint32_t)dfa[i], dfaQ); - tmp32no1 = (int32_t)tmpU32no2 - (int32_t)tmpU32no1; - zerosNum = WebRtcSpl_NormW32(tmp32no1); - if ((tmp32no1) && (far_spectrum[i] > (CHANNEL_VAD << far_q))) - { - // - // Update is needed - // - // This is what we would like to compute - // - // tmp32no1 = dfa[i] - (aecm->channelAdapt[i] * far_spectrum[i]) - // tmp32norm = (i + 1) - // aecm->channelAdapt[i] += (2^mu) * tmp32no1 - // / (tmp32norm * far_spectrum[i]) - // - - // Make sure we don't get overflow in multiplication. - if (zerosNum + zerosFar > 31) - { - if (tmp32no1 > 0) - { - tmp32no2 = (int32_t)WEBRTC_SPL_UMUL_32_16(tmp32no1, - far_spectrum[i]); - } else - { - tmp32no2 = -(int32_t)WEBRTC_SPL_UMUL_32_16(-tmp32no1, - far_spectrum[i]); - } - shiftNum = 0; - } else - { - shiftNum = 32 - (zerosNum + zerosFar); - if (tmp32no1 > 0) - { - tmp32no2 = (tmp32no1 >> shiftNum) * far_spectrum[i]; - } else - { - tmp32no2 = -((-tmp32no1 >> shiftNum) * far_spectrum[i]); - } - } - // Normalize with respect to frequency bin - tmp32no2 = WebRtcSpl_DivW32W16(tmp32no2, i + 1); - // Make sure we are in the right Q-domain - shift2ResChan = shiftNum + shiftChFar - xfaQ - mu - ((30 - zerosFar) << 1); - if (WebRtcSpl_NormW32(tmp32no2) < shift2ResChan) - { - tmp32no2 = WEBRTC_SPL_WORD32_MAX; - } else - { - tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, shift2ResChan); - } - aecm->channelAdapt32[i] = - WebRtcSpl_AddSatW32(aecm->channelAdapt32[i], tmp32no2); - if (aecm->channelAdapt32[i] < 0) - { - // We can never have negative channel gain - aecm->channelAdapt32[i] = 0; - } - aecm->channelAdapt16[i] = - (int16_t)(aecm->channelAdapt32[i] >> 16); - } - } - } - // END: Adaptive channel update - - // Determine if we should store or restore the channel - if ((aecm->startupState == 0) & (aecm->currentVADValue)) - { - // During startup we store the channel every block, - // and we recalculate echo estimate - WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst); - } else - { - if (aecm->farLogEnergy < aecm->farEnergyMSE) - { - aecm->mseChannelCount = 0; - } else - { - aecm->mseChannelCount++; - } - // Enough data for validation. Store channel if we can. - if (aecm->mseChannelCount >= (MIN_MSE_COUNT + 10)) - { - // We have enough data. - // Calculate MSE of "Adapt" and "Stored" versions. - // It is actually not MSE, but average absolute error. - mseStored = 0; - mseAdapt = 0; - for (i = 0; i < MIN_MSE_COUNT; i++) - { - tmp32no1 = ((int32_t)aecm->echoStoredLogEnergy[i] - - (int32_t)aecm->nearLogEnergy[i]); - tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1); - mseStored += tmp32no2; - - tmp32no1 = ((int32_t)aecm->echoAdaptLogEnergy[i] - - (int32_t)aecm->nearLogEnergy[i]); - tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1); - mseAdapt += tmp32no2; - } - if (((mseStored << MSE_RESOLUTION) < (MIN_MSE_DIFF * mseAdapt)) - & ((aecm->mseStoredOld << MSE_RESOLUTION) < (MIN_MSE_DIFF - * aecm->mseAdaptOld))) - { - // The stored channel has a significantly lower MSE than the adaptive one for - // two consecutive calculations. Reset the adaptive channel. - WebRtcAecm_ResetAdaptiveChannel(aecm); - } else if (((MIN_MSE_DIFF * mseStored) > (mseAdapt << MSE_RESOLUTION)) & (mseAdapt - < aecm->mseThreshold) & (aecm->mseAdaptOld < aecm->mseThreshold)) - { - // The adaptive channel has a significantly lower MSE than the stored one. - // The MSE for the adaptive channel has also been low for two consecutive - // calculations. Store the adaptive channel. - WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst); - - // Update threshold - if (aecm->mseThreshold == WEBRTC_SPL_WORD32_MAX) - { - aecm->mseThreshold = (mseAdapt + aecm->mseAdaptOld); - } else - { - int scaled_threshold = aecm->mseThreshold * 5 / 8; - aecm->mseThreshold += - ((mseAdapt - scaled_threshold) * 205) >> 8; - } - - } - - // Reset counter - aecm->mseChannelCount = 0; - - // Store the MSE values. - aecm->mseStoredOld = mseStored; - aecm->mseAdaptOld = mseAdapt; - } - } - // END: Determine if we should store or reset channel estimate. -} - -// CalcSuppressionGain(...) -// -// This function calculates the suppression gain that is used in the Wiener filter. -// -// -// @param aecm [i/n] Handle of the AECM instance. -// @param supGain [out] (Return value) Suppression gain with which to scale the noise -// level (Q14). -// -// -int16_t WebRtcAecm_CalcSuppressionGain(AecmCore* const aecm) { - int32_t tmp32no1; - - int16_t supGain = SUPGAIN_DEFAULT; - int16_t tmp16no1; - int16_t dE = 0; - - // Determine suppression gain used in the Wiener filter. The gain is based on a mix of far - // end energy and echo estimation error. - // Adjust for the far end signal level. A low signal level indicates no far end signal, - // hence we set the suppression gain to 0 - if (!aecm->currentVADValue) - { - supGain = 0; - } else - { - // Adjust for possible double talk. If we have large variations in estimation error we - // likely have double talk (or poor channel). - tmp16no1 = (aecm->nearLogEnergy[0] - aecm->echoStoredLogEnergy[0] - ENERGY_DEV_OFFSET); - dE = WEBRTC_SPL_ABS_W16(tmp16no1); - - if (dE < ENERGY_DEV_TOL) - { - // Likely no double talk. The better estimation, the more we can suppress signal. - // Update counters - if (dE < SUPGAIN_EPC_DT) - { - tmp32no1 = aecm->supGainErrParamDiffAB * dE; - tmp32no1 += (SUPGAIN_EPC_DT >> 1); - tmp16no1 = (int16_t)WebRtcSpl_DivW32W16(tmp32no1, SUPGAIN_EPC_DT); - supGain = aecm->supGainErrParamA - tmp16no1; - } else - { - tmp32no1 = aecm->supGainErrParamDiffBD * (ENERGY_DEV_TOL - dE); - tmp32no1 += ((ENERGY_DEV_TOL - SUPGAIN_EPC_DT) >> 1); - tmp16no1 = (int16_t)WebRtcSpl_DivW32W16(tmp32no1, (ENERGY_DEV_TOL - - SUPGAIN_EPC_DT)); - supGain = aecm->supGainErrParamD + tmp16no1; - } - } else - { - // Likely in double talk. Use default value - supGain = aecm->supGainErrParamD; - } - } - - if (supGain > aecm->supGainOld) - { - tmp16no1 = supGain; - } else - { - tmp16no1 = aecm->supGainOld; - } - aecm->supGainOld = supGain; - if (tmp16no1 < aecm->supGain) - { - aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4); - } else - { - aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4); - } - - // END: Update suppression gain - - return aecm->supGain; -} - -void WebRtcAecm_BufferFarFrame(AecmCore* const aecm, - const int16_t* const farend, - const int farLen) { - int writeLen = farLen, writePos = 0; - - // Check if the write position must be wrapped - while (aecm->farBufWritePos + writeLen > FAR_BUF_LEN) - { - // Write to remaining buffer space before wrapping - writeLen = FAR_BUF_LEN - aecm->farBufWritePos; - memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos, - sizeof(int16_t) * writeLen); - aecm->farBufWritePos = 0; - writePos = writeLen; - writeLen = farLen - writeLen; - } - - memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos, - sizeof(int16_t) * writeLen); - aecm->farBufWritePos += writeLen; -} - -void WebRtcAecm_FetchFarFrame(AecmCore* const aecm, - int16_t* const farend, - const int farLen, - const int knownDelay) { - int readLen = farLen; - int readPos = 0; - int delayChange = knownDelay - aecm->lastKnownDelay; - - aecm->farBufReadPos -= delayChange; - - // Check if delay forces a read position wrap - while (aecm->farBufReadPos < 0) - { - aecm->farBufReadPos += FAR_BUF_LEN; - } - while (aecm->farBufReadPos > FAR_BUF_LEN - 1) - { - aecm->farBufReadPos -= FAR_BUF_LEN; - } - - aecm->lastKnownDelay = knownDelay; - - // Check if read position must be wrapped - while (aecm->farBufReadPos + readLen > FAR_BUF_LEN) - { - - // Read from remaining buffer space before wrapping - readLen = FAR_BUF_LEN - aecm->farBufReadPos; - memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos, - sizeof(int16_t) * readLen); - aecm->farBufReadPos = 0; - readPos = readLen; - readLen = farLen - readLen; - } - memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos, - sizeof(int16_t) * readLen); - aecm->farBufReadPos += readLen; -} diff --git a/webrtc/modules/audio_processing/aecm/aecm_core.cc b/webrtc/modules/audio_processing/aecm/aecm_core.cc new file mode 100644 index 0000000..78c0133 --- /dev/null +++ b/webrtc/modules/audio_processing/aecm/aecm_core.cc @@ -0,0 +1,1125 @@ +/* + * Copyright (c) 2012 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 "modules/audio_processing/aecm/aecm_core.h" + +#include +#include +#include + +extern "C" { +#include "common_audio/ring_buffer.h" +#include "common_audio/signal_processing/include/real_fft.h" +} +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "modules/audio_processing/aecm/echo_control_mobile.h" +#include "modules/audio_processing/utility/delay_estimator_wrapper.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { + +namespace { + +#ifdef AEC_DEBUG +FILE* dfile; +FILE* testfile; +#endif + +// Initialization table for echo channel in 8 kHz +static const int16_t kChannelStored8kHz[PART_LEN1] = { + 2040, 1815, 1590, 1498, 1405, 1395, 1385, 1418, 1451, 1506, 1562, + 1644, 1726, 1804, 1882, 1918, 1953, 1982, 2010, 2025, 2040, 2034, + 2027, 2021, 2014, 1997, 1980, 1925, 1869, 1800, 1732, 1683, 1635, + 1604, 1572, 1545, 1517, 1481, 1444, 1405, 1367, 1331, 1294, 1270, + 1245, 1239, 1233, 1247, 1260, 1282, 1303, 1338, 1373, 1407, 1441, + 1470, 1499, 1524, 1549, 1565, 1582, 1601, 1621, 1649, 1676}; + +// Initialization table for echo channel in 16 kHz +static const int16_t kChannelStored16kHz[PART_LEN1] = { + 2040, 1590, 1405, 1385, 1451, 1562, 1726, 1882, 1953, 2010, 2040, + 2027, 2014, 1980, 1869, 1732, 1635, 1572, 1517, 1444, 1367, 1294, + 1245, 1233, 1260, 1303, 1373, 1441, 1499, 1549, 1582, 1621, 1676, + 1741, 1802, 1861, 1921, 1983, 2040, 2102, 2170, 2265, 2375, 2515, + 2651, 2781, 2922, 3075, 3253, 3471, 3738, 3976, 4151, 4258, 4308, + 4288, 4270, 4253, 4237, 4179, 4086, 3947, 3757, 3484, 3153}; + +} // namespace + +const int16_t WebRtcAecm_kCosTable[] = { + 8192, 8190, 8187, 8180, 8172, 8160, 8147, 8130, 8112, 8091, 8067, + 8041, 8012, 7982, 7948, 7912, 7874, 7834, 7791, 7745, 7697, 7647, + 7595, 7540, 7483, 7424, 7362, 7299, 7233, 7164, 7094, 7021, 6947, + 6870, 6791, 6710, 6627, 6542, 6455, 6366, 6275, 6182, 6087, 5991, + 5892, 5792, 5690, 5586, 5481, 5374, 5265, 5155, 5043, 4930, 4815, + 4698, 4580, 4461, 4341, 4219, 4096, 3971, 3845, 3719, 3591, 3462, + 3331, 3200, 3068, 2935, 2801, 2667, 2531, 2395, 2258, 2120, 1981, + 1842, 1703, 1563, 1422, 1281, 1140, 998, 856, 713, 571, 428, + 285, 142, 0, -142, -285, -428, -571, -713, -856, -998, -1140, + -1281, -1422, -1563, -1703, -1842, -1981, -2120, -2258, -2395, -2531, -2667, + -2801, -2935, -3068, -3200, -3331, -3462, -3591, -3719, -3845, -3971, -4095, + -4219, -4341, -4461, -4580, -4698, -4815, -4930, -5043, -5155, -5265, -5374, + -5481, -5586, -5690, -5792, -5892, -5991, -6087, -6182, -6275, -6366, -6455, + -6542, -6627, -6710, -6791, -6870, -6947, -7021, -7094, -7164, -7233, -7299, + -7362, -7424, -7483, -7540, -7595, -7647, -7697, -7745, -7791, -7834, -7874, + -7912, -7948, -7982, -8012, -8041, -8067, -8091, -8112, -8130, -8147, -8160, + -8172, -8180, -8187, -8190, -8191, -8190, -8187, -8180, -8172, -8160, -8147, + -8130, -8112, -8091, -8067, -8041, -8012, -7982, -7948, -7912, -7874, -7834, + -7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424, -7362, -7299, -7233, + -7164, -7094, -7021, -6947, -6870, -6791, -6710, -6627, -6542, -6455, -6366, + -6275, -6182, -6087, -5991, -5892, -5792, -5690, -5586, -5481, -5374, -5265, + -5155, -5043, -4930, -4815, -4698, -4580, -4461, -4341, -4219, -4096, -3971, + -3845, -3719, -3591, -3462, -3331, -3200, -3068, -2935, -2801, -2667, -2531, + -2395, -2258, -2120, -1981, -1842, -1703, -1563, -1422, -1281, -1140, -998, + -856, -713, -571, -428, -285, -142, 0, 142, 285, 428, 571, + 713, 856, 998, 1140, 1281, 1422, 1563, 1703, 1842, 1981, 2120, + 2258, 2395, 2531, 2667, 2801, 2935, 3068, 3200, 3331, 3462, 3591, + 3719, 3845, 3971, 4095, 4219, 4341, 4461, 4580, 4698, 4815, 4930, + 5043, 5155, 5265, 5374, 5481, 5586, 5690, 5792, 5892, 5991, 6087, + 6182, 6275, 6366, 6455, 6542, 6627, 6710, 6791, 6870, 6947, 7021, + 7094, 7164, 7233, 7299, 7362, 7424, 7483, 7540, 7595, 7647, 7697, + 7745, 7791, 7834, 7874, 7912, 7948, 7982, 8012, 8041, 8067, 8091, + 8112, 8130, 8147, 8160, 8172, 8180, 8187, 8190}; + +const int16_t WebRtcAecm_kSinTable[] = { + 0, 142, 285, 428, 571, 713, 856, 998, 1140, 1281, 1422, + 1563, 1703, 1842, 1981, 2120, 2258, 2395, 2531, 2667, 2801, 2935, + 3068, 3200, 3331, 3462, 3591, 3719, 3845, 3971, 4095, 4219, 4341, + 4461, 4580, 4698, 4815, 4930, 5043, 5155, 5265, 5374, 5481, 5586, + 5690, 5792, 5892, 5991, 6087, 6182, 6275, 6366, 6455, 6542, 6627, + 6710, 6791, 6870, 6947, 7021, 7094, 7164, 7233, 7299, 7362, 7424, + 7483, 7540, 7595, 7647, 7697, 7745, 7791, 7834, 7874, 7912, 7948, + 7982, 8012, 8041, 8067, 8091, 8112, 8130, 8147, 8160, 8172, 8180, + 8187, 8190, 8191, 8190, 8187, 8180, 8172, 8160, 8147, 8130, 8112, + 8091, 8067, 8041, 8012, 7982, 7948, 7912, 7874, 7834, 7791, 7745, + 7697, 7647, 7595, 7540, 7483, 7424, 7362, 7299, 7233, 7164, 7094, + 7021, 6947, 6870, 6791, 6710, 6627, 6542, 6455, 6366, 6275, 6182, + 6087, 5991, 5892, 5792, 5690, 5586, 5481, 5374, 5265, 5155, 5043, + 4930, 4815, 4698, 4580, 4461, 4341, 4219, 4096, 3971, 3845, 3719, + 3591, 3462, 3331, 3200, 3068, 2935, 2801, 2667, 2531, 2395, 2258, + 2120, 1981, 1842, 1703, 1563, 1422, 1281, 1140, 998, 856, 713, + 571, 428, 285, 142, 0, -142, -285, -428, -571, -713, -856, + -998, -1140, -1281, -1422, -1563, -1703, -1842, -1981, -2120, -2258, -2395, + -2531, -2667, -2801, -2935, -3068, -3200, -3331, -3462, -3591, -3719, -3845, + -3971, -4095, -4219, -4341, -4461, -4580, -4698, -4815, -4930, -5043, -5155, + -5265, -5374, -5481, -5586, -5690, -5792, -5892, -5991, -6087, -6182, -6275, + -6366, -6455, -6542, -6627, -6710, -6791, -6870, -6947, -7021, -7094, -7164, + -7233, -7299, -7362, -7424, -7483, -7540, -7595, -7647, -7697, -7745, -7791, + -7834, -7874, -7912, -7948, -7982, -8012, -8041, -8067, -8091, -8112, -8130, + -8147, -8160, -8172, -8180, -8187, -8190, -8191, -8190, -8187, -8180, -8172, + -8160, -8147, -8130, -8112, -8091, -8067, -8041, -8012, -7982, -7948, -7912, + -7874, -7834, -7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424, -7362, + -7299, -7233, -7164, -7094, -7021, -6947, -6870, -6791, -6710, -6627, -6542, + -6455, -6366, -6275, -6182, -6087, -5991, -5892, -5792, -5690, -5586, -5481, + -5374, -5265, -5155, -5043, -4930, -4815, -4698, -4580, -4461, -4341, -4219, + -4096, -3971, -3845, -3719, -3591, -3462, -3331, -3200, -3068, -2935, -2801, + -2667, -2531, -2395, -2258, -2120, -1981, -1842, -1703, -1563, -1422, -1281, + -1140, -998, -856, -713, -571, -428, -285, -142}; + + +// Moves the pointer to the next entry and inserts |far_spectrum| and +// corresponding Q-domain in its buffer. +// +// Inputs: +// - self : Pointer to the delay estimation instance +// - far_spectrum : Pointer to the far end spectrum +// - far_q : Q-domain of far end spectrum +// +void WebRtcAecm_UpdateFarHistory(AecmCore* self, + uint16_t* far_spectrum, + int far_q) { + // Get new buffer position + self->far_history_pos++; + if (self->far_history_pos >= MAX_DELAY) { + self->far_history_pos = 0; + } + // Update Q-domain buffer + self->far_q_domains[self->far_history_pos] = far_q; + // Update far end spectrum buffer + memcpy(&(self->far_history[self->far_history_pos * PART_LEN1]), far_spectrum, + sizeof(uint16_t) * PART_LEN1); +} + +// Returns a pointer to the far end spectrum aligned to current near end +// spectrum. The function WebRtc_DelayEstimatorProcessFix(...) should have been +// called before AlignedFarend(...). Otherwise, you get the pointer to the +// previous frame. The memory is only valid until the next call of +// WebRtc_DelayEstimatorProcessFix(...). +// +// Inputs: +// - self : Pointer to the AECM instance. +// - delay : Current delay estimate. +// +// Output: +// - far_q : The Q-domain of the aligned far end spectrum +// +// Return value: +// - far_spectrum : Pointer to the aligned far end spectrum +// NULL - Error +// +const uint16_t* WebRtcAecm_AlignedFarend(AecmCore* self, + int* far_q, + int delay) { + int buffer_position = 0; + RTC_DCHECK(self); + buffer_position = self->far_history_pos - delay; + + // Check buffer position + if (buffer_position < 0) { + buffer_position += MAX_DELAY; + } + // Get Q-domain + *far_q = self->far_q_domains[buffer_position]; + // Return far end spectrum + return &(self->far_history[buffer_position * PART_LEN1]); +} + +// Declare function pointers. +CalcLinearEnergies WebRtcAecm_CalcLinearEnergies; +StoreAdaptiveChannel WebRtcAecm_StoreAdaptiveChannel; +ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel; + +AecmCore* WebRtcAecm_CreateCore() { + // Allocate zero-filled memory. + AecmCore* aecm = static_cast(calloc(1, sizeof(AecmCore))); + + aecm->farFrameBuf = + WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); + if (!aecm->farFrameBuf) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + + aecm->nearNoisyFrameBuf = + WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); + if (!aecm->nearNoisyFrameBuf) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + + aecm->nearCleanFrameBuf = + WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); + if (!aecm->nearCleanFrameBuf) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + + aecm->outFrameBuf = + WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); + if (!aecm->outFrameBuf) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + + aecm->delay_estimator_farend = + WebRtc_CreateDelayEstimatorFarend(PART_LEN1, MAX_DELAY); + if (aecm->delay_estimator_farend == NULL) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + aecm->delay_estimator = + WebRtc_CreateDelayEstimator(aecm->delay_estimator_farend, 0); + if (aecm->delay_estimator == NULL) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + // TODO(bjornv): Explicitly disable robust delay validation until no + // performance regression has been established. Then remove the line. + WebRtc_enable_robust_validation(aecm->delay_estimator, 0); + + aecm->real_fft = WebRtcSpl_CreateRealFFT(PART_LEN_SHIFT); + if (aecm->real_fft == NULL) { + WebRtcAecm_FreeCore(aecm); + return NULL; + } + + // Init some aecm pointers. 16 and 32 byte alignment is only necessary + // for Neon code currently. + aecm->xBuf = (int16_t*)(((uintptr_t)aecm->xBuf_buf + 31) & ~31); + aecm->dBufClean = (int16_t*)(((uintptr_t)aecm->dBufClean_buf + 31) & ~31); + aecm->dBufNoisy = (int16_t*)(((uintptr_t)aecm->dBufNoisy_buf + 31) & ~31); + aecm->outBuf = (int16_t*)(((uintptr_t)aecm->outBuf_buf + 15) & ~15); + aecm->channelStored = + (int16_t*)(((uintptr_t)aecm->channelStored_buf + 15) & ~15); + aecm->channelAdapt16 = + (int16_t*)(((uintptr_t)aecm->channelAdapt16_buf + 15) & ~15); + aecm->channelAdapt32 = + (int32_t*)(((uintptr_t)aecm->channelAdapt32_buf + 31) & ~31); + + return aecm; +} + +void WebRtcAecm_InitEchoPathCore(AecmCore* aecm, const int16_t* echo_path) { + int i = 0; + + // Reset the stored channel + memcpy(aecm->channelStored, echo_path, sizeof(int16_t) * PART_LEN1); + // Reset the adapted channels + memcpy(aecm->channelAdapt16, echo_path, sizeof(int16_t) * PART_LEN1); + for (i = 0; i < PART_LEN1; i++) { + aecm->channelAdapt32[i] = (int32_t)aecm->channelAdapt16[i] << 16; + } + + // Reset channel storing variables + aecm->mseAdaptOld = 1000; + aecm->mseStoredOld = 1000; + aecm->mseThreshold = WEBRTC_SPL_WORD32_MAX; + aecm->mseChannelCount = 0; +} + +static void CalcLinearEnergiesC(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est, + uint32_t* far_energy, + uint32_t* echo_energy_adapt, + uint32_t* echo_energy_stored) { + int i; + + // Get energy for the delayed far end signal and estimated + // echo using both stored and adapted channels. + for (i = 0; i < PART_LEN1; i++) { + echo_est[i] = + WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); + (*far_energy) += (uint32_t)(far_spectrum[i]); + *echo_energy_adapt += aecm->channelAdapt16[i] * far_spectrum[i]; + (*echo_energy_stored) += (uint32_t)echo_est[i]; + } +} + +static void StoreAdaptiveChannelC(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est) { + int i; + + // During startup we store the channel every block. + memcpy(aecm->channelStored, aecm->channelAdapt16, + sizeof(int16_t) * PART_LEN1); + // Recalculate echo estimate + for (i = 0; i < PART_LEN; i += 4) { + echo_est[i] = + WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); + echo_est[i + 1] = + WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 1], far_spectrum[i + 1]); + echo_est[i + 2] = + WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 2], far_spectrum[i + 2]); + echo_est[i + 3] = + WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 3], far_spectrum[i + 3]); + } + echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); +} + +static void ResetAdaptiveChannelC(AecmCore* aecm) { + int i; + + // The stored channel has a significantly lower MSE than the adaptive one for + // two consecutive calculations. Reset the adaptive channel. + memcpy(aecm->channelAdapt16, aecm->channelStored, + sizeof(int16_t) * PART_LEN1); + // Restore the W32 channel + for (i = 0; i < PART_LEN; i += 4) { + aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; + aecm->channelAdapt32[i + 1] = (int32_t)aecm->channelStored[i + 1] << 16; + aecm->channelAdapt32[i + 2] = (int32_t)aecm->channelStored[i + 2] << 16; + aecm->channelAdapt32[i + 3] = (int32_t)aecm->channelStored[i + 3] << 16; + } + aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; +} + +// Initialize function pointers for ARM Neon platform. +#if defined(WEBRTC_HAS_NEON) +static void WebRtcAecm_InitNeon(void) { + WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannelNeon; + WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannelNeon; + WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergiesNeon; +} +#endif + +// Initialize function pointers for MIPS platform. +#if defined(MIPS32_LE) +static void WebRtcAecm_InitMips(void) { +#if defined(MIPS_DSP_R1_LE) + WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannel_mips; + WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannel_mips; +#endif + WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergies_mips; +} +#endif + +// WebRtcAecm_InitCore(...) +// +// This function initializes the AECM instant created with +// WebRtcAecm_CreateCore(...) Input: +// - aecm : Pointer to the Echo Suppression instance +// - samplingFreq : Sampling Frequency +// +// Output: +// - aecm : Initialized instance +// +// Return value : 0 - Ok +// -1 - Error +// +int WebRtcAecm_InitCore(AecmCore* const aecm, int samplingFreq) { + int i = 0; + int32_t tmp32 = PART_LEN1 * PART_LEN1; + int16_t tmp16 = PART_LEN1; + + if (samplingFreq != 8000 && samplingFreq != 16000) { + samplingFreq = 8000; + return -1; + } + // sanity check of sampling frequency + aecm->mult = (int16_t)samplingFreq / 8000; + + aecm->farBufWritePos = 0; + aecm->farBufReadPos = 0; + aecm->knownDelay = 0; + aecm->lastKnownDelay = 0; + + WebRtc_InitBuffer(aecm->farFrameBuf); + WebRtc_InitBuffer(aecm->nearNoisyFrameBuf); + WebRtc_InitBuffer(aecm->nearCleanFrameBuf); + WebRtc_InitBuffer(aecm->outFrameBuf); + + memset(aecm->xBuf_buf, 0, sizeof(aecm->xBuf_buf)); + memset(aecm->dBufClean_buf, 0, sizeof(aecm->dBufClean_buf)); + memset(aecm->dBufNoisy_buf, 0, sizeof(aecm->dBufNoisy_buf)); + memset(aecm->outBuf_buf, 0, sizeof(aecm->outBuf_buf)); + + aecm->seed = 666; + aecm->totCount = 0; + + if (WebRtc_InitDelayEstimatorFarend(aecm->delay_estimator_farend) != 0) { + return -1; + } + if (WebRtc_InitDelayEstimator(aecm->delay_estimator) != 0) { + return -1; + } + // Set far end histories to zero + memset(aecm->far_history, 0, sizeof(uint16_t) * PART_LEN1 * MAX_DELAY); + memset(aecm->far_q_domains, 0, sizeof(int) * MAX_DELAY); + aecm->far_history_pos = MAX_DELAY; + + aecm->nlpFlag = 1; + aecm->fixedDelay = -1; + + aecm->dfaCleanQDomain = 0; + aecm->dfaCleanQDomainOld = 0; + aecm->dfaNoisyQDomain = 0; + aecm->dfaNoisyQDomainOld = 0; + + memset(aecm->nearLogEnergy, 0, sizeof(aecm->nearLogEnergy)); + aecm->farLogEnergy = 0; + memset(aecm->echoAdaptLogEnergy, 0, sizeof(aecm->echoAdaptLogEnergy)); + memset(aecm->echoStoredLogEnergy, 0, sizeof(aecm->echoStoredLogEnergy)); + + // Initialize the echo channels with a stored shape. + if (samplingFreq == 8000) { + WebRtcAecm_InitEchoPathCore(aecm, kChannelStored8kHz); + } else { + WebRtcAecm_InitEchoPathCore(aecm, kChannelStored16kHz); + } + + memset(aecm->echoFilt, 0, sizeof(aecm->echoFilt)); + memset(aecm->nearFilt, 0, sizeof(aecm->nearFilt)); + aecm->noiseEstCtr = 0; + + aecm->cngMode = AecmTrue; + + memset(aecm->noiseEstTooLowCtr, 0, sizeof(aecm->noiseEstTooLowCtr)); + memset(aecm->noiseEstTooHighCtr, 0, sizeof(aecm->noiseEstTooHighCtr)); + // Shape the initial noise level to an approximate pink noise. + for (i = 0; i < (PART_LEN1 >> 1) - 1; i++) { + aecm->noiseEst[i] = (tmp32 << 8); + tmp16--; + tmp32 -= (int32_t)((tmp16 << 1) + 1); + } + for (; i < PART_LEN1; i++) { + aecm->noiseEst[i] = (tmp32 << 8); + } + + aecm->farEnergyMin = WEBRTC_SPL_WORD16_MAX; + aecm->farEnergyMax = WEBRTC_SPL_WORD16_MIN; + aecm->farEnergyMaxMin = 0; + aecm->farEnergyVAD = FAR_ENERGY_MIN; // This prevents false speech detection + // at the beginning. + aecm->farEnergyMSE = 0; + aecm->currentVADValue = 0; + aecm->vadUpdateCount = 0; + aecm->firstVAD = 1; + + aecm->startupState = 0; + aecm->supGain = SUPGAIN_DEFAULT; + aecm->supGainOld = SUPGAIN_DEFAULT; + + aecm->supGainErrParamA = SUPGAIN_ERROR_PARAM_A; + aecm->supGainErrParamD = SUPGAIN_ERROR_PARAM_D; + aecm->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B; + aecm->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D; + + // Assert a preprocessor definition at compile-time. It's an assumption + // used in assembly code, so check the assembly files before any change. + static_assert(PART_LEN % 16 == 0, "PART_LEN is not a multiple of 16"); + + // Initialize function pointers. + WebRtcAecm_CalcLinearEnergies = CalcLinearEnergiesC; + WebRtcAecm_StoreAdaptiveChannel = StoreAdaptiveChannelC; + WebRtcAecm_ResetAdaptiveChannel = ResetAdaptiveChannelC; + +#if defined(WEBRTC_HAS_NEON) + WebRtcAecm_InitNeon(); +#endif + +#if defined(MIPS32_LE) + WebRtcAecm_InitMips(); +#endif + return 0; +} + +// TODO(bjornv): This function is currently not used. Add support for these +// parameters from a higher level +int WebRtcAecm_Control(AecmCore* aecm, int delay, int nlpFlag) { + aecm->nlpFlag = nlpFlag; + aecm->fixedDelay = delay; + + return 0; +} + +void WebRtcAecm_FreeCore(AecmCore* aecm) { + if (aecm == NULL) { + return; + } + + WebRtc_FreeBuffer(aecm->farFrameBuf); + WebRtc_FreeBuffer(aecm->nearNoisyFrameBuf); + WebRtc_FreeBuffer(aecm->nearCleanFrameBuf); + WebRtc_FreeBuffer(aecm->outFrameBuf); + + WebRtc_FreeDelayEstimator(aecm->delay_estimator); + WebRtc_FreeDelayEstimatorFarend(aecm->delay_estimator_farend); + WebRtcSpl_FreeRealFFT(aecm->real_fft); + + free(aecm); +} + +int WebRtcAecm_ProcessFrame(AecmCore* aecm, + const int16_t* farend, + const int16_t* nearendNoisy, + const int16_t* nearendClean, + int16_t* out) { + int16_t outBlock_buf[PART_LEN + 8]; // Align buffer to 8-byte boundary. + int16_t* outBlock = (int16_t*)(((uintptr_t)outBlock_buf + 15) & ~15); + + int16_t farFrame[FRAME_LEN]; + const int16_t* out_ptr = NULL; + int size = 0; + + // Buffer the current frame. + // Fetch an older one corresponding to the delay. + WebRtcAecm_BufferFarFrame(aecm, farend, FRAME_LEN); + WebRtcAecm_FetchFarFrame(aecm, farFrame, FRAME_LEN, aecm->knownDelay); + + // Buffer the synchronized far and near frames, + // to pass the smaller blocks individually. + WebRtc_WriteBuffer(aecm->farFrameBuf, farFrame, FRAME_LEN); + WebRtc_WriteBuffer(aecm->nearNoisyFrameBuf, nearendNoisy, FRAME_LEN); + if (nearendClean != NULL) { + WebRtc_WriteBuffer(aecm->nearCleanFrameBuf, nearendClean, FRAME_LEN); + } + + // Process as many blocks as possible. + while (WebRtc_available_read(aecm->farFrameBuf) >= PART_LEN) { + int16_t far_block[PART_LEN]; + const int16_t* far_block_ptr = NULL; + int16_t near_noisy_block[PART_LEN]; + const int16_t* near_noisy_block_ptr = NULL; + + WebRtc_ReadBuffer(aecm->farFrameBuf, (void**)&far_block_ptr, far_block, + PART_LEN); + WebRtc_ReadBuffer(aecm->nearNoisyFrameBuf, (void**)&near_noisy_block_ptr, + near_noisy_block, PART_LEN); + if (nearendClean != NULL) { + int16_t near_clean_block[PART_LEN]; + const int16_t* near_clean_block_ptr = NULL; + + WebRtc_ReadBuffer(aecm->nearCleanFrameBuf, (void**)&near_clean_block_ptr, + near_clean_block, PART_LEN); + if (WebRtcAecm_ProcessBlock(aecm, far_block_ptr, near_noisy_block_ptr, + near_clean_block_ptr, outBlock) == -1) { + return -1; + } + } else { + if (WebRtcAecm_ProcessBlock(aecm, far_block_ptr, near_noisy_block_ptr, + NULL, outBlock) == -1) { + return -1; + } + } + + WebRtc_WriteBuffer(aecm->outFrameBuf, outBlock, PART_LEN); + } + + // Stuff the out buffer if we have less than a frame to output. + // This should only happen for the first frame. + size = (int)WebRtc_available_read(aecm->outFrameBuf); + if (size < FRAME_LEN) { + WebRtc_MoveReadPtr(aecm->outFrameBuf, size - FRAME_LEN); + } + + // Obtain an output frame. + WebRtc_ReadBuffer(aecm->outFrameBuf, (void**)&out_ptr, out, FRAME_LEN); + if (out_ptr != out) { + // ReadBuffer() hasn't copied to |out| in this case. + memcpy(out, out_ptr, FRAME_LEN * sizeof(int16_t)); + } + + return 0; +} + +// WebRtcAecm_AsymFilt(...) +// +// Performs asymmetric filtering. +// +// Inputs: +// - filtOld : Previous filtered value. +// - inVal : New input value. +// - stepSizePos : Step size when we have a positive contribution. +// - stepSizeNeg : Step size when we have a negative contribution. +// +// Output: +// +// Return: - Filtered value. +// +int16_t WebRtcAecm_AsymFilt(const int16_t filtOld, + const int16_t inVal, + const int16_t stepSizePos, + const int16_t stepSizeNeg) { + int16_t retVal; + + if ((filtOld == WEBRTC_SPL_WORD16_MAX) | (filtOld == WEBRTC_SPL_WORD16_MIN)) { + return inVal; + } + retVal = filtOld; + if (filtOld > inVal) { + retVal -= (filtOld - inVal) >> stepSizeNeg; + } else { + retVal += (inVal - filtOld) >> stepSizePos; + } + + return retVal; +} + +// ExtractFractionPart(a, zeros) +// +// returns the fraction part of |a|, with |zeros| number of leading zeros, as an +// int16_t scaled to Q8. There is no sanity check of |a| in the sense that the +// number of zeros match. +static int16_t ExtractFractionPart(uint32_t a, int zeros) { + return (int16_t)(((a << zeros) & 0x7FFFFFFF) >> 23); +} + +// Calculates and returns the log of |energy| in Q8. The input |energy| is +// supposed to be in Q(|q_domain|). +static int16_t LogOfEnergyInQ8(uint32_t energy, int q_domain) { + static const int16_t kLogLowValue = PART_LEN_SHIFT << 7; + int16_t log_energy_q8 = kLogLowValue; + if (energy > 0) { + int zeros = WebRtcSpl_NormU32(energy); + int16_t frac = ExtractFractionPart(energy, zeros); + // log2 of |energy| in Q8. + log_energy_q8 += ((31 - zeros) << 8) + frac - (q_domain << 8); + } + return log_energy_q8; +} + +// WebRtcAecm_CalcEnergies(...) +// +// This function calculates the log of energies for nearend, farend and +// estimated echoes. There is also an update of energy decision levels, i.e. +// internal VAD. +// +// +// @param aecm [i/o] Handle of the AECM instance. +// @param far_spectrum [in] Pointer to farend spectrum. +// @param far_q [in] Q-domain of farend spectrum. +// @param nearEner [in] Near end energy for current block in +// Q(aecm->dfaQDomain). +// @param echoEst [out] Estimated echo in Q(xfa_q+RESOLUTION_CHANNEL16). +// +void WebRtcAecm_CalcEnergies(AecmCore* aecm, + const uint16_t* far_spectrum, + const int16_t far_q, + const uint32_t nearEner, + int32_t* echoEst) { + // Local variables + uint32_t tmpAdapt = 0; + uint32_t tmpStored = 0; + uint32_t tmpFar = 0; + + int i; + + int16_t tmp16; + int16_t increase_max_shifts = 4; + int16_t decrease_max_shifts = 11; + int16_t increase_min_shifts = 11; + int16_t decrease_min_shifts = 3; + + // Get log of near end energy and store in buffer + + // Shift buffer + memmove(aecm->nearLogEnergy + 1, aecm->nearLogEnergy, + sizeof(int16_t) * (MAX_BUF_LEN - 1)); + + // Logarithm of integrated magnitude spectrum (nearEner) + aecm->nearLogEnergy[0] = LogOfEnergyInQ8(nearEner, aecm->dfaNoisyQDomain); + + WebRtcAecm_CalcLinearEnergies(aecm, far_spectrum, echoEst, &tmpFar, &tmpAdapt, + &tmpStored); + + // Shift buffers + memmove(aecm->echoAdaptLogEnergy + 1, aecm->echoAdaptLogEnergy, + sizeof(int16_t) * (MAX_BUF_LEN - 1)); + memmove(aecm->echoStoredLogEnergy + 1, aecm->echoStoredLogEnergy, + sizeof(int16_t) * (MAX_BUF_LEN - 1)); + + // Logarithm of delayed far end energy + aecm->farLogEnergy = LogOfEnergyInQ8(tmpFar, far_q); + + // Logarithm of estimated echo energy through adapted channel + aecm->echoAdaptLogEnergy[0] = + LogOfEnergyInQ8(tmpAdapt, RESOLUTION_CHANNEL16 + far_q); + + // Logarithm of estimated echo energy through stored channel + aecm->echoStoredLogEnergy[0] = + LogOfEnergyInQ8(tmpStored, RESOLUTION_CHANNEL16 + far_q); + + // Update farend energy levels (min, max, vad, mse) + if (aecm->farLogEnergy > FAR_ENERGY_MIN) { + if (aecm->startupState == 0) { + increase_max_shifts = 2; + decrease_min_shifts = 2; + increase_min_shifts = 8; + } + + aecm->farEnergyMin = + WebRtcAecm_AsymFilt(aecm->farEnergyMin, aecm->farLogEnergy, + increase_min_shifts, decrease_min_shifts); + aecm->farEnergyMax = + WebRtcAecm_AsymFilt(aecm->farEnergyMax, aecm->farLogEnergy, + increase_max_shifts, decrease_max_shifts); + aecm->farEnergyMaxMin = (aecm->farEnergyMax - aecm->farEnergyMin); + + // Dynamic VAD region size + tmp16 = 2560 - aecm->farEnergyMin; + if (tmp16 > 0) { + tmp16 = (int16_t)((tmp16 * FAR_ENERGY_VAD_REGION) >> 9); + } else { + tmp16 = 0; + } + tmp16 += FAR_ENERGY_VAD_REGION; + + if ((aecm->startupState == 0) | (aecm->vadUpdateCount > 1024)) { + // In startup phase or VAD update halted + aecm->farEnergyVAD = aecm->farEnergyMin + tmp16; + } else { + if (aecm->farEnergyVAD > aecm->farLogEnergy) { + aecm->farEnergyVAD += + (aecm->farLogEnergy + tmp16 - aecm->farEnergyVAD) >> 6; + aecm->vadUpdateCount = 0; + } else { + aecm->vadUpdateCount++; + } + } + // Put MSE threshold higher than VAD + aecm->farEnergyMSE = aecm->farEnergyVAD + (1 << 8); + } + + // Update VAD variables + if (aecm->farLogEnergy > aecm->farEnergyVAD) { + if ((aecm->startupState == 0) | (aecm->farEnergyMaxMin > FAR_ENERGY_DIFF)) { + // We are in startup or have significant dynamics in input speech level + aecm->currentVADValue = 1; + } + } else { + aecm->currentVADValue = 0; + } + if ((aecm->currentVADValue) && (aecm->firstVAD)) { + aecm->firstVAD = 0; + if (aecm->echoAdaptLogEnergy[0] > aecm->nearLogEnergy[0]) { + // The estimated echo has higher energy than the near end signal. + // This means that the initialization was too aggressive. Scale + // down by a factor 8 + for (i = 0; i < PART_LEN1; i++) { + aecm->channelAdapt16[i] >>= 3; + } + // Compensate the adapted echo energy level accordingly. + aecm->echoAdaptLogEnergy[0] -= (3 << 8); + aecm->firstVAD = 1; + } + } +} + +// WebRtcAecm_CalcStepSize(...) +// +// This function calculates the step size used in channel estimation +// +// +// @param aecm [in] Handle of the AECM instance. +// @param mu [out] (Return value) Stepsize in log2(), i.e. number of +// shifts. +// +// +int16_t WebRtcAecm_CalcStepSize(AecmCore* const aecm) { + int32_t tmp32; + int16_t tmp16; + int16_t mu = MU_MAX; + + // Here we calculate the step size mu used in the + // following NLMS based Channel estimation algorithm + if (!aecm->currentVADValue) { + // Far end energy level too low, no channel update + mu = 0; + } else if (aecm->startupState > 0) { + if (aecm->farEnergyMin >= aecm->farEnergyMax) { + mu = MU_MIN; + } else { + tmp16 = (aecm->farLogEnergy - aecm->farEnergyMin); + tmp32 = tmp16 * MU_DIFF; + tmp32 = WebRtcSpl_DivW32W16(tmp32, aecm->farEnergyMaxMin); + mu = MU_MIN - 1 - (int16_t)(tmp32); + // The -1 is an alternative to rounding. This way we get a larger + // stepsize, so we in some sense compensate for truncation in NLMS + } + if (mu < MU_MAX) { + mu = MU_MAX; // Equivalent with maximum step size of 2^-MU_MAX + } + } + + return mu; +} + +// WebRtcAecm_UpdateChannel(...) +// +// This function performs channel estimation. NLMS and decision on channel +// storage. +// +// +// @param aecm [i/o] Handle of the AECM instance. +// @param far_spectrum [in] Absolute value of the farend signal in Q(far_q) +// @param far_q [in] Q-domain of the farend signal +// @param dfa [in] Absolute value of the nearend signal +// (Q[aecm->dfaQDomain]) +// @param mu [in] NLMS step size. +// @param echoEst [i/o] Estimated echo in Q(far_q+RESOLUTION_CHANNEL16). +// +void WebRtcAecm_UpdateChannel(AecmCore* aecm, + const uint16_t* far_spectrum, + const int16_t far_q, + const uint16_t* const dfa, + const int16_t mu, + int32_t* echoEst) { + uint32_t tmpU32no1, tmpU32no2; + int32_t tmp32no1, tmp32no2; + int32_t mseStored; + int32_t mseAdapt; + + int i; + + int16_t zerosFar, zerosNum, zerosCh, zerosDfa; + int16_t shiftChFar, shiftNum, shift2ResChan; + int16_t tmp16no1; + int16_t xfaQ, dfaQ; + + // This is the channel estimation algorithm. It is base on NLMS but has a + // variable step length, which was calculated above. + if (mu) { + for (i = 0; i < PART_LEN1; i++) { + // Determine norm of channel and farend to make sure we don't get overflow + // in multiplication + zerosCh = WebRtcSpl_NormU32(aecm->channelAdapt32[i]); + zerosFar = WebRtcSpl_NormU32((uint32_t)far_spectrum[i]); + if (zerosCh + zerosFar > 31) { + // Multiplication is safe + tmpU32no1 = + WEBRTC_SPL_UMUL_32_16(aecm->channelAdapt32[i], far_spectrum[i]); + shiftChFar = 0; + } else { + // We need to shift down before multiplication + shiftChFar = 32 - zerosCh - zerosFar; + // If zerosCh == zerosFar == 0, shiftChFar is 32. A + // right shift of 32 is undefined. To avoid that, we + // do this check. + tmpU32no1 = + rtc::dchecked_cast( + shiftChFar >= 32 ? 0 : aecm->channelAdapt32[i] >> shiftChFar) * + far_spectrum[i]; + } + // Determine Q-domain of numerator + zerosNum = WebRtcSpl_NormU32(tmpU32no1); + if (dfa[i]) { + zerosDfa = WebRtcSpl_NormU32((uint32_t)dfa[i]); + } else { + zerosDfa = 32; + } + tmp16no1 = zerosDfa - 2 + aecm->dfaNoisyQDomain - RESOLUTION_CHANNEL32 - + far_q + shiftChFar; + if (zerosNum > tmp16no1 + 1) { + xfaQ = tmp16no1; + dfaQ = zerosDfa - 2; + } else { + xfaQ = zerosNum - 2; + dfaQ = RESOLUTION_CHANNEL32 + far_q - aecm->dfaNoisyQDomain - + shiftChFar + xfaQ; + } + // Add in the same Q-domain + tmpU32no1 = WEBRTC_SPL_SHIFT_W32(tmpU32no1, xfaQ); + tmpU32no2 = WEBRTC_SPL_SHIFT_W32((uint32_t)dfa[i], dfaQ); + tmp32no1 = (int32_t)tmpU32no2 - (int32_t)tmpU32no1; + zerosNum = WebRtcSpl_NormW32(tmp32no1); + if ((tmp32no1) && (far_spectrum[i] > (CHANNEL_VAD << far_q))) { + // + // Update is needed + // + // This is what we would like to compute + // + // tmp32no1 = dfa[i] - (aecm->channelAdapt[i] * far_spectrum[i]) + // tmp32norm = (i + 1) + // aecm->channelAdapt[i] += (2^mu) * tmp32no1 + // / (tmp32norm * far_spectrum[i]) + // + + // Make sure we don't get overflow in multiplication. + if (zerosNum + zerosFar > 31) { + if (tmp32no1 > 0) { + tmp32no2 = + (int32_t)WEBRTC_SPL_UMUL_32_16(tmp32no1, far_spectrum[i]); + } else { + tmp32no2 = + -(int32_t)WEBRTC_SPL_UMUL_32_16(-tmp32no1, far_spectrum[i]); + } + shiftNum = 0; + } else { + shiftNum = 32 - (zerosNum + zerosFar); + if (tmp32no1 > 0) { + tmp32no2 = (tmp32no1 >> shiftNum) * far_spectrum[i]; + } else { + tmp32no2 = -((-tmp32no1 >> shiftNum) * far_spectrum[i]); + } + } + // Normalize with respect to frequency bin + tmp32no2 = WebRtcSpl_DivW32W16(tmp32no2, i + 1); + // Make sure we are in the right Q-domain + shift2ResChan = + shiftNum + shiftChFar - xfaQ - mu - ((30 - zerosFar) << 1); + if (WebRtcSpl_NormW32(tmp32no2) < shift2ResChan) { + tmp32no2 = WEBRTC_SPL_WORD32_MAX; + } else { + tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, shift2ResChan); + } + aecm->channelAdapt32[i] = + WebRtcSpl_AddSatW32(aecm->channelAdapt32[i], tmp32no2); + if (aecm->channelAdapt32[i] < 0) { + // We can never have negative channel gain + aecm->channelAdapt32[i] = 0; + } + aecm->channelAdapt16[i] = (int16_t)(aecm->channelAdapt32[i] >> 16); + } + } + } + // END: Adaptive channel update + + // Determine if we should store or restore the channel + if ((aecm->startupState == 0) & (aecm->currentVADValue)) { + // During startup we store the channel every block, + // and we recalculate echo estimate + WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst); + } else { + if (aecm->farLogEnergy < aecm->farEnergyMSE) { + aecm->mseChannelCount = 0; + } else { + aecm->mseChannelCount++; + } + // Enough data for validation. Store channel if we can. + if (aecm->mseChannelCount >= (MIN_MSE_COUNT + 10)) { + // We have enough data. + // Calculate MSE of "Adapt" and "Stored" versions. + // It is actually not MSE, but average absolute error. + mseStored = 0; + mseAdapt = 0; + for (i = 0; i < MIN_MSE_COUNT; i++) { + tmp32no1 = ((int32_t)aecm->echoStoredLogEnergy[i] - + (int32_t)aecm->nearLogEnergy[i]); + tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1); + mseStored += tmp32no2; + + tmp32no1 = ((int32_t)aecm->echoAdaptLogEnergy[i] - + (int32_t)aecm->nearLogEnergy[i]); + tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1); + mseAdapt += tmp32no2; + } + if (((mseStored << MSE_RESOLUTION) < (MIN_MSE_DIFF * mseAdapt)) & + ((aecm->mseStoredOld << MSE_RESOLUTION) < + (MIN_MSE_DIFF * aecm->mseAdaptOld))) { + // The stored channel has a significantly lower MSE than the adaptive + // one for two consecutive calculations. Reset the adaptive channel. + WebRtcAecm_ResetAdaptiveChannel(aecm); + } else if (((MIN_MSE_DIFF * mseStored) > (mseAdapt << MSE_RESOLUTION)) & + (mseAdapt < aecm->mseThreshold) & + (aecm->mseAdaptOld < aecm->mseThreshold)) { + // The adaptive channel has a significantly lower MSE than the stored + // one. The MSE for the adaptive channel has also been low for two + // consecutive calculations. Store the adaptive channel. + WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst); + + // Update threshold + if (aecm->mseThreshold == WEBRTC_SPL_WORD32_MAX) { + aecm->mseThreshold = (mseAdapt + aecm->mseAdaptOld); + } else { + int scaled_threshold = aecm->mseThreshold * 5 / 8; + aecm->mseThreshold += ((mseAdapt - scaled_threshold) * 205) >> 8; + } + } + + // Reset counter + aecm->mseChannelCount = 0; + + // Store the MSE values. + aecm->mseStoredOld = mseStored; + aecm->mseAdaptOld = mseAdapt; + } + } + // END: Determine if we should store or reset channel estimate. +} + +// CalcSuppressionGain(...) +// +// This function calculates the suppression gain that is used in the Wiener +// filter. +// +// +// @param aecm [i/n] Handle of the AECM instance. +// @param supGain [out] (Return value) Suppression gain with which to scale +// the noise +// level (Q14). +// +// +int16_t WebRtcAecm_CalcSuppressionGain(AecmCore* const aecm) { + int32_t tmp32no1; + + int16_t supGain = SUPGAIN_DEFAULT; + int16_t tmp16no1; + int16_t dE = 0; + + // Determine suppression gain used in the Wiener filter. The gain is based on + // a mix of far end energy and echo estimation error. Adjust for the far end + // signal level. A low signal level indicates no far end signal, hence we set + // the suppression gain to 0 + if (!aecm->currentVADValue) { + supGain = 0; + } else { + // Adjust for possible double talk. If we have large variations in + // estimation error we likely have double talk (or poor channel). + tmp16no1 = (aecm->nearLogEnergy[0] - aecm->echoStoredLogEnergy[0] - + ENERGY_DEV_OFFSET); + dE = WEBRTC_SPL_ABS_W16(tmp16no1); + + if (dE < ENERGY_DEV_TOL) { + // Likely no double talk. The better estimation, the more we can suppress + // signal. Update counters + if (dE < SUPGAIN_EPC_DT) { + tmp32no1 = aecm->supGainErrParamDiffAB * dE; + tmp32no1 += (SUPGAIN_EPC_DT >> 1); + tmp16no1 = (int16_t)WebRtcSpl_DivW32W16(tmp32no1, SUPGAIN_EPC_DT); + supGain = aecm->supGainErrParamA - tmp16no1; + } else { + tmp32no1 = aecm->supGainErrParamDiffBD * (ENERGY_DEV_TOL - dE); + tmp32no1 += ((ENERGY_DEV_TOL - SUPGAIN_EPC_DT) >> 1); + tmp16no1 = (int16_t)WebRtcSpl_DivW32W16( + tmp32no1, (ENERGY_DEV_TOL - SUPGAIN_EPC_DT)); + supGain = aecm->supGainErrParamD + tmp16no1; + } + } else { + // Likely in double talk. Use default value + supGain = aecm->supGainErrParamD; + } + } + + if (supGain > aecm->supGainOld) { + tmp16no1 = supGain; + } else { + tmp16no1 = aecm->supGainOld; + } + aecm->supGainOld = supGain; + if (tmp16no1 < aecm->supGain) { + aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4); + } else { + aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4); + } + + // END: Update suppression gain + + return aecm->supGain; +} + +void WebRtcAecm_BufferFarFrame(AecmCore* const aecm, + const int16_t* const farend, + const int farLen) { + int writeLen = farLen, writePos = 0; + + // Check if the write position must be wrapped + while (aecm->farBufWritePos + writeLen > FAR_BUF_LEN) { + // Write to remaining buffer space before wrapping + writeLen = FAR_BUF_LEN - aecm->farBufWritePos; + memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos, + sizeof(int16_t) * writeLen); + aecm->farBufWritePos = 0; + writePos = writeLen; + writeLen = farLen - writeLen; + } + + memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos, + sizeof(int16_t) * writeLen); + aecm->farBufWritePos += writeLen; +} + +void WebRtcAecm_FetchFarFrame(AecmCore* const aecm, + int16_t* const farend, + const int farLen, + const int knownDelay) { + int readLen = farLen; + int readPos = 0; + int delayChange = knownDelay - aecm->lastKnownDelay; + + aecm->farBufReadPos -= delayChange; + + // Check if delay forces a read position wrap + while (aecm->farBufReadPos < 0) { + aecm->farBufReadPos += FAR_BUF_LEN; + } + while (aecm->farBufReadPos > FAR_BUF_LEN - 1) { + aecm->farBufReadPos -= FAR_BUF_LEN; + } + + aecm->lastKnownDelay = knownDelay; + + // Check if read position must be wrapped + while (aecm->farBufReadPos + readLen > FAR_BUF_LEN) { + // Read from remaining buffer space before wrapping + readLen = FAR_BUF_LEN - aecm->farBufReadPos; + memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos, + sizeof(int16_t) * readLen); + aecm->farBufReadPos = 0; + readPos = readLen; + readLen = farLen - readLen; + } + memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos, + sizeof(int16_t) * readLen); + aecm->farBufReadPos += readLen; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aecm/aecm_core.h b/webrtc/modules/audio_processing/aecm/aecm_core.h index b52bb62..aaa74e1 100644 --- a/webrtc/modules/audio_processing/aecm/aecm_core.h +++ b/webrtc/modules/audio_processing/aecm/aecm_core.h @@ -10,13 +10,18 @@ // Performs echo control (suppression) with fft routines in fixed-point. -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AECM_AECM_CORE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AECM_AECM_CORE_H_ +#ifndef MODULES_AUDIO_PROCESSING_AECM_AECM_CORE_H_ +#define MODULES_AUDIO_PROCESSING_AECM_AECM_CORE_H_ -#include "webrtc/common_audio/ring_buffer.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/aecm/aecm_defines.h" -#include "webrtc/typedefs.h" +extern "C" { +#include "common_audio/ring_buffer.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +} +#include "modules/audio_processing/aecm/aecm_defines.h" + +struct RealFFT; + +namespace webrtc { #ifdef _MSC_VER // visual c++ #define ALIGN8_BEG __declspec(align(8)) @@ -27,109 +32,109 @@ #endif typedef struct { - int16_t real; - int16_t imag; + int16_t real; + int16_t imag; } ComplexInt16; typedef struct { - int farBufWritePos; - int farBufReadPos; - int knownDelay; - int lastKnownDelay; - int firstVAD; // Parameter to control poorly initialized channels + int farBufWritePos; + int farBufReadPos; + int knownDelay; + int lastKnownDelay; + int firstVAD; // Parameter to control poorly initialized channels - RingBuffer* farFrameBuf; - RingBuffer* nearNoisyFrameBuf; - RingBuffer* nearCleanFrameBuf; - RingBuffer* outFrameBuf; + RingBuffer* farFrameBuf; + RingBuffer* nearNoisyFrameBuf; + RingBuffer* nearCleanFrameBuf; + RingBuffer* outFrameBuf; - int16_t farBuf[FAR_BUF_LEN]; + int16_t farBuf[FAR_BUF_LEN]; - int16_t mult; - uint32_t seed; + int16_t mult; + uint32_t seed; - // Delay estimation variables - void* delay_estimator_farend; - void* delay_estimator; - uint16_t currentDelay; - // Far end history variables - // TODO(bjornv): Replace |far_history| with ring_buffer. - uint16_t far_history[PART_LEN1 * MAX_DELAY]; - int far_history_pos; - int far_q_domains[MAX_DELAY]; + // Delay estimation variables + void* delay_estimator_farend; + void* delay_estimator; + uint16_t currentDelay; + // Far end history variables + // TODO(bjornv): Replace |far_history| with ring_buffer. + uint16_t far_history[PART_LEN1 * MAX_DELAY]; + int far_history_pos; + int far_q_domains[MAX_DELAY]; - int16_t nlpFlag; - int16_t fixedDelay; + int16_t nlpFlag; + int16_t fixedDelay; - uint32_t totCount; + uint32_t totCount; - int16_t dfaCleanQDomain; - int16_t dfaCleanQDomainOld; - int16_t dfaNoisyQDomain; - int16_t dfaNoisyQDomainOld; + int16_t dfaCleanQDomain; + int16_t dfaCleanQDomainOld; + int16_t dfaNoisyQDomain; + int16_t dfaNoisyQDomainOld; - int16_t nearLogEnergy[MAX_BUF_LEN]; - int16_t farLogEnergy; - int16_t echoAdaptLogEnergy[MAX_BUF_LEN]; - int16_t echoStoredLogEnergy[MAX_BUF_LEN]; + int16_t nearLogEnergy[MAX_BUF_LEN]; + int16_t farLogEnergy; + int16_t echoAdaptLogEnergy[MAX_BUF_LEN]; + int16_t echoStoredLogEnergy[MAX_BUF_LEN]; - // The extra 16 or 32 bytes in the following buffers are for alignment based - // Neon code. - // It's designed this way since the current GCC compiler can't align a - // buffer in 16 or 32 byte boundaries properly. - int16_t channelStored_buf[PART_LEN1 + 8]; - int16_t channelAdapt16_buf[PART_LEN1 + 8]; - int32_t channelAdapt32_buf[PART_LEN1 + 8]; - int16_t xBuf_buf[PART_LEN2 + 16]; // farend - int16_t dBufClean_buf[PART_LEN2 + 16]; // nearend - int16_t dBufNoisy_buf[PART_LEN2 + 16]; // nearend - int16_t outBuf_buf[PART_LEN + 8]; + // The extra 16 or 32 bytes in the following buffers are for alignment based + // Neon code. + // It's designed this way since the current GCC compiler can't align a + // buffer in 16 or 32 byte boundaries properly. + int16_t channelStored_buf[PART_LEN1 + 8]; + int16_t channelAdapt16_buf[PART_LEN1 + 8]; + int32_t channelAdapt32_buf[PART_LEN1 + 8]; + int16_t xBuf_buf[PART_LEN2 + 16]; // farend + int16_t dBufClean_buf[PART_LEN2 + 16]; // nearend + int16_t dBufNoisy_buf[PART_LEN2 + 16]; // nearend + int16_t outBuf_buf[PART_LEN + 8]; - // Pointers to the above buffers - int16_t *channelStored; - int16_t *channelAdapt16; - int32_t *channelAdapt32; - int16_t *xBuf; - int16_t *dBufClean; - int16_t *dBufNoisy; - int16_t *outBuf; + // Pointers to the above buffers + int16_t* channelStored; + int16_t* channelAdapt16; + int32_t* channelAdapt32; + int16_t* xBuf; + int16_t* dBufClean; + int16_t* dBufNoisy; + int16_t* outBuf; - int32_t echoFilt[PART_LEN1]; - int16_t nearFilt[PART_LEN1]; - int32_t noiseEst[PART_LEN1]; - int noiseEstTooLowCtr[PART_LEN1]; - int noiseEstTooHighCtr[PART_LEN1]; - int16_t noiseEstCtr; - int16_t cngMode; + int32_t echoFilt[PART_LEN1]; + int16_t nearFilt[PART_LEN1]; + int32_t noiseEst[PART_LEN1]; + int noiseEstTooLowCtr[PART_LEN1]; + int noiseEstTooHighCtr[PART_LEN1]; + int16_t noiseEstCtr; + int16_t cngMode; - int32_t mseAdaptOld; - int32_t mseStoredOld; - int32_t mseThreshold; + int32_t mseAdaptOld; + int32_t mseStoredOld; + int32_t mseThreshold; - int16_t farEnergyMin; - int16_t farEnergyMax; - int16_t farEnergyMaxMin; - int16_t farEnergyVAD; - int16_t farEnergyMSE; - int currentVADValue; - int16_t vadUpdateCount; + int16_t farEnergyMin; + int16_t farEnergyMax; + int16_t farEnergyMaxMin; + int16_t farEnergyVAD; + int16_t farEnergyMSE; + int currentVADValue; + int16_t vadUpdateCount; - int16_t startupState; - int16_t mseChannelCount; - int16_t supGain; - int16_t supGainOld; + int16_t startupState; + int16_t mseChannelCount; + int16_t supGain; + int16_t supGainOld; - int16_t supGainErrParamA; - int16_t supGainErrParamD; - int16_t supGainErrParamDiffAB; - int16_t supGainErrParamDiffBD; + int16_t supGainErrParamA; + int16_t supGainErrParamD; + int16_t supGainErrParamDiffAB; + int16_t supGainErrParamDiffBD; - struct RealFFT* real_fft; + struct RealFFT* real_fft; #ifdef AEC_DEBUG - FILE *farFile; - FILE *nearFile; - FILE *outFile; + FILE* farFile; + FILE* nearFile; + FILE* outFile; #endif } AecmCore; @@ -400,7 +405,7 @@ extern ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel; // For the above function pointers, functions for generic platforms are declared // and defined as static in file aecm_core.c, while those for ARM Neon platforms // are declared below and defined in file aecm_core_neon.c. -#if defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON) +#if defined(WEBRTC_HAS_NEON) void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore* aecm, const uint16_t* far_spectrum, int32_t* echo_est, @@ -431,4 +436,6 @@ void WebRtcAecm_ResetAdaptiveChannel_mips(AecmCore* aecm); #endif #endif +} // namespace webrtc + #endif diff --git a/webrtc/modules/audio_processing/aecm/aecm_core_c.c b/webrtc/modules/audio_processing/aecm/aecm_core_c.cc similarity index 63% rename from webrtc/modules/audio_processing/aecm/aecm_core_c.c rename to webrtc/modules/audio_processing/aecm/aecm_core_c.cc index df95e8b..7b6ca59 100644 --- a/webrtc/modules/audio_processing/aecm/aecm_core_c.c +++ b/webrtc/modules/audio_processing/aecm/aecm_core_c.cc @@ -8,49 +8,50 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/aecm/aecm_core.h" - -#include #include #include -#include "webrtc/common_audio/ring_buffer.h" -#include "webrtc/common_audio/signal_processing/include/real_fft.h" -#include "webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h" -#include "webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h" -#include "webrtc/system_wrappers/include/compile_assert_c.h" -#include "webrtc/system_wrappers/include/cpu_features_wrapper.h" -#include "webrtc/typedefs.h" +#include "modules/audio_processing/aecm/aecm_core.h" + +extern "C" { +#include "common_audio/ring_buffer.h" +#include "common_audio/signal_processing/include/real_fft.h" +} +#include "modules/audio_processing/aecm/echo_control_mobile.h" +#include "modules/audio_processing/utility/delay_estimator_wrapper.h" +extern "C" { +#include "system_wrappers/include/cpu_features_wrapper.h" +} + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/sanitizer.h" + +namespace webrtc { + +namespace { // Square root of Hanning window in Q14. -#if defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON) -// Table is defined in an ARM assembly file. -extern const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END; -#else static const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END = { - 0, 399, 798, 1196, 1594, 1990, 2386, 2780, 3172, - 3562, 3951, 4337, 4720, 5101, 5478, 5853, 6224, - 6591, 6954, 7313, 7668, 8019, 8364, 8705, 9040, - 9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514, - 11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553, - 13773, 13985, 14189, 14384, 14571, 14749, 14918, 15079, - 15231, 15373, 15506, 15631, 15746, 15851, 15947, 16034, - 16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384 -}; -#endif + 0, 399, 798, 1196, 1594, 1990, 2386, 2780, 3172, 3562, 3951, + 4337, 4720, 5101, 5478, 5853, 6224, 6591, 6954, 7313, 7668, 8019, + 8364, 8705, 9040, 9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514, + 11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553, 13773, 13985, 14189, + 14384, 14571, 14749, 14918, 15079, 15231, 15373, 15506, 15631, 15746, 15851, + 15947, 16034, 16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384}; #ifdef AECM_WITH_ABS_APPROX -//Q15 alpha = 0.99439986968132 const Factor for magnitude approximation +// Q15 alpha = 0.99439986968132 const Factor for magnitude approximation static const uint16_t kAlpha1 = 32584; -//Q15 beta = 0.12967166976970 const Factor for magnitude approximation +// Q15 beta = 0.12967166976970 const Factor for magnitude approximation static const uint16_t kBeta1 = 4249; -//Q15 alpha = 0.94234827210087 const Factor for magnitude approximation +// Q15 alpha = 0.94234827210087 const Factor for magnitude approximation static const uint16_t kAlpha2 = 30879; -//Q15 beta = 0.33787806009150 const Factor for magnitude approximation +// Q15 beta = 0.33787806009150 const Factor for magnitude approximation static const uint16_t kBeta2 = 11072; -//Q15 alpha = 0.82247698684306 const Factor for magnitude approximation +// Q15 alpha = 0.82247698684306 const Factor for magnitude approximation static const uint16_t kAlpha3 = 26951; -//Q15 beta = 0.57762063060713 const Factor for magnitude approximation +// Q15 beta = 0.57762063060713 const Factor for magnitude approximation static const uint16_t kBeta3 = 18927; #endif @@ -60,7 +61,115 @@ static const int16_t kNoiseEstIncCount = 5; static void ComfortNoise(AecmCore* aecm, const uint16_t* dfa, ComplexInt16* out, - const int16_t* lambda); + const int16_t* lambda) { + int16_t i; + int16_t tmp16; + int32_t tmp32; + + int16_t randW16[PART_LEN]; + int16_t uReal[PART_LEN1]; + int16_t uImag[PART_LEN1]; + int32_t outLShift32; + int16_t noiseRShift16[PART_LEN1]; + + int16_t shiftFromNearToNoise = kNoiseEstQDomain - aecm->dfaCleanQDomain; + int16_t minTrackShift; + + RTC_DCHECK_GE(shiftFromNearToNoise, 0); + RTC_DCHECK_LT(shiftFromNearToNoise, 16); + + if (aecm->noiseEstCtr < 100) { + // Track the minimum more quickly initially. + aecm->noiseEstCtr++; + minTrackShift = 6; + } else { + minTrackShift = 9; + } + + // Estimate noise power. + for (i = 0; i < PART_LEN1; i++) { + // Shift to the noise domain. + tmp32 = (int32_t)dfa[i]; + outLShift32 = tmp32 << shiftFromNearToNoise; + + if (outLShift32 < aecm->noiseEst[i]) { + // Reset "too low" counter + aecm->noiseEstTooLowCtr[i] = 0; + // Track the minimum. + if (aecm->noiseEst[i] < (1 << minTrackShift)) { + // For small values, decrease noiseEst[i] every + // |kNoiseEstIncCount| block. The regular approach below can not + // go further down due to truncation. + aecm->noiseEstTooHighCtr[i]++; + if (aecm->noiseEstTooHighCtr[i] >= kNoiseEstIncCount) { + aecm->noiseEst[i]--; + aecm->noiseEstTooHighCtr[i] = 0; // Reset the counter + } + } else { + aecm->noiseEst[i] -= + ((aecm->noiseEst[i] - outLShift32) >> minTrackShift); + } + } else { + // Reset "too high" counter + aecm->noiseEstTooHighCtr[i] = 0; + // Ramp slowly upwards until we hit the minimum again. + if ((aecm->noiseEst[i] >> 19) > 0) { + // Avoid overflow. + // Multiplication with 2049 will cause wrap around. Scale + // down first and then multiply + aecm->noiseEst[i] >>= 11; + aecm->noiseEst[i] *= 2049; + } else if ((aecm->noiseEst[i] >> 11) > 0) { + // Large enough for relative increase + aecm->noiseEst[i] *= 2049; + aecm->noiseEst[i] >>= 11; + } else { + // Make incremental increases based on size every + // |kNoiseEstIncCount| block + aecm->noiseEstTooLowCtr[i]++; + if (aecm->noiseEstTooLowCtr[i] >= kNoiseEstIncCount) { + aecm->noiseEst[i] += (aecm->noiseEst[i] >> 9) + 1; + aecm->noiseEstTooLowCtr[i] = 0; // Reset counter + } + } + } + } + + for (i = 0; i < PART_LEN1; i++) { + tmp32 = aecm->noiseEst[i] >> shiftFromNearToNoise; + if (tmp32 > 32767) { + tmp32 = 32767; + aecm->noiseEst[i] = tmp32 << shiftFromNearToNoise; + } + noiseRShift16[i] = (int16_t)tmp32; + + tmp16 = ONE_Q14 - lambda[i]; + noiseRShift16[i] = (int16_t)((tmp16 * noiseRShift16[i]) >> 14); + } + + // Generate a uniform random array on [0 2^15-1]. + WebRtcSpl_RandUArray(randW16, PART_LEN, &aecm->seed); + + // Generate noise according to estimated energy. + uReal[0] = 0; // Reject LF noise. + uImag[0] = 0; + for (i = 1; i < PART_LEN1; i++) { + // Get a random index for the cos and sin tables over [0 359]. + tmp16 = (int16_t)((359 * randW16[i - 1]) >> 15); + + // Tables are in Q13. + uReal[i] = + (int16_t)((noiseRShift16[i] * WebRtcAecm_kCosTable[tmp16]) >> 13); + uImag[i] = + (int16_t)((-noiseRShift16[i] * WebRtcAecm_kSinTable[tmp16]) >> 13); + } + uImag[PART_LEN] = 0; + + for (i = 0; i < PART_LEN1; i++) { + out[i].real = WebRtcSpl_AddSatW16(out[i].real, uReal[i]); + out[i].imag = WebRtcSpl_AddSatW16(out[i].imag, uImag[i]); + } +} static void WindowAndFFT(AecmCore* aecm, int16_t* fft, @@ -73,11 +182,11 @@ static void WindowAndFFT(AecmCore* aecm, for (i = 0; i < PART_LEN; i++) { // Window time domain signal and insert into real part of // transformation array |fft| - int16_t scaled_time_signal = time_signal[i] << time_signal_scaling; + int16_t scaled_time_signal = time_signal[i] * (1 << time_signal_scaling); fft[i] = (int16_t)((scaled_time_signal * WebRtcAecm_kSqrtHanning[i]) >> 14); - scaled_time_signal = time_signal[i + PART_LEN] << time_signal_scaling; - fft[PART_LEN + i] = (int16_t)(( - scaled_time_signal * WebRtcAecm_kSqrtHanning[PART_LEN - i]) >> 14); + scaled_time_signal = time_signal[i + PART_LEN] * (1 << time_signal_scaling); + fft[PART_LEN + i] = (int16_t)( + (scaled_time_signal * WebRtcAecm_kSqrtHanning[PART_LEN - i]) >> 14); } // Do forward FFT, then take only the first PART_LEN complex samples, @@ -114,32 +223,27 @@ static void InverseFFTAndWindow(AecmCore* aecm, outCFFT = WebRtcSpl_RealInverseFFT(aecm->real_fft, fft, ifft_out); for (i = 0; i < PART_LEN; i++) { ifft_out[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - ifft_out[i], WebRtcAecm_kSqrtHanning[i], 14); + ifft_out[i], WebRtcAecm_kSqrtHanning[i], 14); tmp32no1 = WEBRTC_SPL_SHIFT_W32((int32_t)ifft_out[i], - outCFFT - aecm->dfaCleanQDomain); + outCFFT - aecm->dfaCleanQDomain); output[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, tmp32no1 + aecm->outBuf[i], WEBRTC_SPL_WORD16_MIN); - tmp32no1 = (ifft_out[PART_LEN + i] * - WebRtcAecm_kSqrtHanning[PART_LEN - i]) >> 14; - tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, - outCFFT - aecm->dfaCleanQDomain); - aecm->outBuf[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, - tmp32no1, - WEBRTC_SPL_WORD16_MIN); + tmp32no1 = + (ifft_out[PART_LEN + i] * WebRtcAecm_kSqrtHanning[PART_LEN - i]) >> 14; + tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, outCFFT - aecm->dfaCleanQDomain); + aecm->outBuf[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, tmp32no1, + WEBRTC_SPL_WORD16_MIN); } // Copy the current block to the old position // (aecm->outBuf is shifted elsewhere) memcpy(aecm->xBuf, aecm->xBuf + PART_LEN, sizeof(int16_t) * PART_LEN); - memcpy(aecm->dBufNoisy, - aecm->dBufNoisy + PART_LEN, + memcpy(aecm->dBufNoisy, aecm->dBufNoisy + PART_LEN, sizeof(int16_t) * PART_LEN); - if (nearendClean != NULL) - { - memcpy(aecm->dBufClean, - aecm->dBufClean + PART_LEN, + if (nearendClean != NULL) { + memcpy(aecm->dBufClean, aecm->dBufClean + PART_LEN, sizeof(int16_t) * PART_LEN); } } @@ -170,7 +274,7 @@ static int TimeToFrequencyDomain(AecmCore* aecm, // In fft_buf, +16 for 32-byte alignment. int16_t fft_buf[PART_LEN4 + 16]; - int16_t *fft = (int16_t *) (((uintptr_t) fft_buf + 31) & ~31); + int16_t* fft = (int16_t*)(((uintptr_t)fft_buf + 31) & ~31); int16_t tmp16no1; #ifndef WEBRTC_ARCH_ARM_V7 @@ -195,23 +299,17 @@ static int TimeToFrequencyDomain(AecmCore* aecm, freq_signal[0].imag = 0; freq_signal[PART_LEN].imag = 0; freq_signal_abs[0] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[0].real); - freq_signal_abs[PART_LEN] = (uint16_t)WEBRTC_SPL_ABS_W16( - freq_signal[PART_LEN].real); - (*freq_signal_sum_abs) = (uint32_t)(freq_signal_abs[0]) + - (uint32_t)(freq_signal_abs[PART_LEN]); + freq_signal_abs[PART_LEN] = + (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[PART_LEN].real); + (*freq_signal_sum_abs) = + (uint32_t)(freq_signal_abs[0]) + (uint32_t)(freq_signal_abs[PART_LEN]); - for (i = 1; i < PART_LEN; i++) - { - if (freq_signal[i].real == 0) - { + for (i = 1; i < PART_LEN; i++) { + if (freq_signal[i].real == 0) { freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].imag); - } - else if (freq_signal[i].imag == 0) - { + } else if (freq_signal[i].imag == 0) { freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].real); - } - else - { + } else { // Approximation for magnitude of complex fft output // magn = sqrt(real^2 + imag^2) // magn ~= alpha * max(|imag|,|real|) + beta * min(|imag|,|real|) @@ -222,27 +320,22 @@ static int TimeToFrequencyDomain(AecmCore* aecm, tmp16no1 = WEBRTC_SPL_ABS_W16(freq_signal[i].real); tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag); - if(tmp16no1 > tmp16no2) - { + if (tmp16no1 > tmp16no2) { max_value = tmp16no1; min_value = tmp16no2; - } else - { + } else { max_value = tmp16no2; min_value = tmp16no1; } // Magnitude in Q(-6) - if ((max_value >> 2) > min_value) - { + if ((max_value >> 2) > min_value) { alpha = kAlpha1; beta = kBeta1; - } else if ((max_value >> 1) > min_value) - { + } else if ((max_value >> 1) > min_value) { alpha = kAlpha2; beta = kBeta2; - } else - { + } else { alpha = kAlpha3; beta = kBeta3; } @@ -252,24 +345,21 @@ static int TimeToFrequencyDomain(AecmCore* aecm, #else #ifdef WEBRTC_ARCH_ARM_V7 __asm __volatile( - "smulbb %[tmp32no1], %[real], %[real]\n\t" - "smlabb %[tmp32no2], %[imag], %[imag], %[tmp32no1]\n\t" - :[tmp32no1]"+&r"(tmp32no1), - [tmp32no2]"=r"(tmp32no2) - :[real]"r"(freq_signal[i].real), - [imag]"r"(freq_signal[i].imag) - ); + "smulbb %[tmp32no1], %[real], %[real]\n\t" + "smlabb %[tmp32no2], %[imag], %[imag], %[tmp32no1]\n\t" + : [tmp32no1] "+&r"(tmp32no1), [tmp32no2] "=r"(tmp32no2) + : [real] "r"(freq_signal[i].real), [imag] "r"(freq_signal[i].imag)); #else tmp16no1 = WEBRTC_SPL_ABS_W16(freq_signal[i].real); tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag); tmp32no1 = tmp16no1 * tmp16no1; tmp32no2 = tmp16no2 * tmp16no2; tmp32no2 = WebRtcSpl_AddSatW32(tmp32no1, tmp32no2); -#endif // WEBRTC_ARCH_ARM_V7 +#endif // WEBRTC_ARCH_ARM_V7 tmp32no1 = WebRtcSpl_SqrtFloor(tmp32no2); freq_signal_abs[i] = (uint16_t)tmp32no1; -#endif // AECM_WITH_ABS_APPROX +#endif // AECM_WITH_ABS_APPROX } (*freq_signal_sum_abs) += (uint32_t)freq_signal_abs[i]; } @@ -277,7 +367,10 @@ static int TimeToFrequencyDomain(AecmCore* aecm, return time_signal_scaling; } -int WebRtcAecm_ProcessBlock(AecmCore* aecm, +} // namespace + +int RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/8200 + WebRtcAecm_ProcessBlock(AecmCore* aecm, const int16_t* farend, const int16_t* nearendNoisy, const int16_t* nearendClean, @@ -300,13 +393,13 @@ int WebRtcAecm_ProcessBlock(AecmCore* aecm, // 32 byte aligned buffers (with +8 or +16). // TODO(kma): define fft with ComplexInt16. - int16_t fft_buf[PART_LEN4 + 2 + 16]; // +2 to make a loop safe. + int16_t fft_buf[PART_LEN4 + 2 + 16]; // +2 to make a loop safe. int32_t echoEst32_buf[PART_LEN1 + 8]; int32_t dfw_buf[PART_LEN2 + 8]; int32_t efw_buf[PART_LEN2 + 8]; - int16_t* fft = (int16_t*) (((uintptr_t) fft_buf + 31) & ~ 31); - int32_t* echoEst32 = (int32_t*) (((uintptr_t) echoEst32_buf + 31) & ~ 31); + int16_t* fft = (int16_t*)(((uintptr_t)fft_buf + 31) & ~31); + int32_t* echoEst32 = (int32_t*)(((uintptr_t)echoEst32_buf + 31) & ~31); ComplexInt16* dfw = (ComplexInt16*)(((uintptr_t)dfw_buf + 31) & ~31); ComplexInt16* efw = (ComplexInt16*)(((uintptr_t)efw_buf + 31) & ~31); @@ -332,53 +425,37 @@ int WebRtcAecm_ProcessBlock(AecmCore* aecm, // (1) another CONV_LEN blocks // (2) the rest - if (aecm->startupState < 2) - { - aecm->startupState = (aecm->totCount >= CONV_LEN) + - (aecm->totCount >= CONV_LEN2); + if (aecm->startupState < 2) { + aecm->startupState = + (aecm->totCount >= CONV_LEN) + (aecm->totCount >= CONV_LEN2); } // END: Determine startup state // Buffer near and far end signals memcpy(aecm->xBuf + PART_LEN, farend, sizeof(int16_t) * PART_LEN); memcpy(aecm->dBufNoisy + PART_LEN, nearendNoisy, sizeof(int16_t) * PART_LEN); - if (nearendClean != NULL) - { - memcpy(aecm->dBufClean + PART_LEN, - nearendClean, + if (nearendClean != NULL) { + memcpy(aecm->dBufClean + PART_LEN, nearendClean, sizeof(int16_t) * PART_LEN); } // Transform far end signal from time domain to frequency domain. - far_q = TimeToFrequencyDomain(aecm, - aecm->xBuf, - dfw, - xfa, - &xfaSum); + far_q = TimeToFrequencyDomain(aecm, aecm->xBuf, dfw, xfa, &xfaSum); // Transform noisy near end signal from time domain to frequency domain. - zerosDBufNoisy = TimeToFrequencyDomain(aecm, - aecm->dBufNoisy, - dfw, - dfaNoisy, - &dfaNoisySum); + zerosDBufNoisy = + TimeToFrequencyDomain(aecm, aecm->dBufNoisy, dfw, dfaNoisy, &dfaNoisySum); aecm->dfaNoisyQDomainOld = aecm->dfaNoisyQDomain; aecm->dfaNoisyQDomain = (int16_t)zerosDBufNoisy; - - if (nearendClean == NULL) - { + if (nearendClean == NULL) { ptrDfaClean = dfaNoisy; aecm->dfaCleanQDomainOld = aecm->dfaNoisyQDomainOld; aecm->dfaCleanQDomain = aecm->dfaNoisyQDomain; dfaCleanSum = dfaNoisySum; - } else - { + } else { // Transform clean near end signal from time domain to frequency domain. - zerosDBufClean = TimeToFrequencyDomain(aecm, - aecm->dBufClean, - dfw, - dfaClean, + zerosDBufClean = TimeToFrequencyDomain(aecm, aecm->dBufClean, dfw, dfaClean, &dfaCleanSum); aecm->dfaCleanQDomainOld = aecm->dfaCleanQDomain; aecm->dfaCleanQDomain = (int16_t)zerosDBufClean; @@ -387,46 +464,34 @@ int WebRtcAecm_ProcessBlock(AecmCore* aecm, // Get the delay // Save far-end history and estimate delay WebRtcAecm_UpdateFarHistory(aecm, xfa, far_q); - if (WebRtc_AddFarSpectrumFix(aecm->delay_estimator_farend, - xfa, - PART_LEN1, + if (WebRtc_AddFarSpectrumFix(aecm->delay_estimator_farend, xfa, PART_LEN1, far_q) == -1) { return -1; } - delay = WebRtc_DelayEstimatorProcessFix(aecm->delay_estimator, - dfaNoisy, - PART_LEN1, - zerosDBufNoisy); - if (delay == -1) - { + delay = WebRtc_DelayEstimatorProcessFix(aecm->delay_estimator, dfaNoisy, + PART_LEN1, zerosDBufNoisy); + if (delay == -1) { return -1; - } - else if (delay == -2) - { + } else if (delay == -2) { // If the delay is unknown, we assume zero. // NOTE: this will have to be adjusted if we ever add lookahead. delay = 0; } - if (aecm->fixedDelay >= 0) - { + if (aecm->fixedDelay >= 0) { // Use fixed delay delay = aecm->fixedDelay; } // Get aligned far end spectrum far_spectrum_ptr = WebRtcAecm_AlignedFarend(aecm, &far_q, delay); - zerosXBuf = (int16_t) far_q; - if (far_spectrum_ptr == NULL) - { + zerosXBuf = (int16_t)far_q; + if (far_spectrum_ptr == NULL) { return -1; } // Calculate log(energy) and update energy threshold levels - WebRtcAecm_CalcEnergies(aecm, - far_spectrum_ptr, - zerosXBuf, - dfaNoisySum, + WebRtcAecm_CalcEnergies(aecm, far_spectrum_ptr, zerosXBuf, dfaNoisySum, echoEst32); // Calculate stepsize @@ -438,63 +503,54 @@ int WebRtcAecm_ProcessBlock(AecmCore* aecm, // This is the channel estimation algorithm. // It is base on NLMS but has a variable step length, // which was calculated above. - WebRtcAecm_UpdateChannel(aecm, - far_spectrum_ptr, - zerosXBuf, - dfaNoisy, - mu, + WebRtcAecm_UpdateChannel(aecm, far_spectrum_ptr, zerosXBuf, dfaNoisy, mu, echoEst32); supGain = WebRtcAecm_CalcSuppressionGain(aecm); - // Calculate Wiener filter hnl[] - for (i = 0; i < PART_LEN1; i++) - { + for (i = 0; i < PART_LEN1; i++) { // Far end signal through channel estimate in Q8 // How much can we shift right to preserve resolution tmp32no1 = echoEst32[i] - aecm->echoFilt[i]; - aecm->echoFilt[i] += (tmp32no1 * 50) >> 8; + aecm->echoFilt[i] += + rtc::dchecked_cast((int64_t{tmp32no1} * 50) >> 8); zeros32 = WebRtcSpl_NormW32(aecm->echoFilt[i]) + 1; zeros16 = WebRtcSpl_NormW16(supGain) + 1; - if (zeros32 + zeros16 > 16) - { + if (zeros32 + zeros16 > 16) { // Multiplication is safe // Result in // Q(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN+ // aecm->xfaQDomainBuf[diff]) - echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], - (uint16_t)supGain); + echoEst32Gained = + WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], (uint16_t)supGain); resolutionDiff = 14 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); - } else - { + } else { tmp16no1 = 17 - zeros32 - zeros16; - resolutionDiff = 14 + tmp16no1 - RESOLUTION_CHANNEL16 - - RESOLUTION_SUPGAIN; + resolutionDiff = + 14 + tmp16no1 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); - if (zeros32 > tmp16no1) - { + if (zeros32 > tmp16no1) { echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], supGain >> tmp16no1); - } else - { + } else { // Result in Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN-16) echoEst32Gained = (aecm->echoFilt[i] >> tmp16no1) * supGain; } } zeros16 = WebRtcSpl_NormW16(aecm->nearFilt[i]); - assert(zeros16 >= 0); // |zeros16| is a norm, hence non-negative. + RTC_DCHECK_GE(zeros16, 0); // |zeros16| is a norm, hence non-negative. dfa_clean_q_domain_diff = aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld; if (zeros16 < dfa_clean_q_domain_diff && aecm->nearFilt[i]) { - tmp16no1 = aecm->nearFilt[i] << zeros16; + tmp16no1 = aecm->nearFilt[i] * (1 << zeros16); qDomainDiff = zeros16 - dfa_clean_q_domain_diff; tmp16no2 = ptrDfaClean[i] >> -qDomainDiff; } else { tmp16no1 = dfa_clean_q_domain_diff < 0 - ? aecm->nearFilt[i] >> -dfa_clean_q_domain_diff - : aecm->nearFilt[i] << dfa_clean_q_domain_diff; + ? aecm->nearFilt[i] >> -dfa_clean_q_domain_diff + : aecm->nearFilt[i] * (1 << dfa_clean_q_domain_diff); qDomainDiff = 0; tmp16no2 = ptrDfaClean[i]; } @@ -505,130 +561,105 @@ int WebRtcAecm_ProcessBlock(AecmCore* aecm, if ((tmp16no2) & (-qDomainDiff > zeros16)) { aecm->nearFilt[i] = WEBRTC_SPL_WORD16_MAX; } else { - aecm->nearFilt[i] = qDomainDiff < 0 ? tmp16no2 << -qDomainDiff + aecm->nearFilt[i] = qDomainDiff < 0 ? tmp16no2 * (1 << -qDomainDiff) : tmp16no2 >> qDomainDiff; } // Wiener filter coefficients, resulting hnl in Q14 - if (echoEst32Gained == 0) - { + if (echoEst32Gained == 0) { hnl[i] = ONE_Q14; - } else if (aecm->nearFilt[i] == 0) - { + } else if (aecm->nearFilt[i] == 0) { hnl[i] = 0; - } else - { + } else { // Multiply the suppression gain // Rounding echoEst32Gained += (uint32_t)(aecm->nearFilt[i] >> 1); - tmpU32 = WebRtcSpl_DivU32U16(echoEst32Gained, - (uint16_t)aecm->nearFilt[i]); + tmpU32 = + WebRtcSpl_DivU32U16(echoEst32Gained, (uint16_t)aecm->nearFilt[i]); // Current resolution is // Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN- max(0,17-zeros16- zeros32)) // Make sure we are in Q14 tmp32no1 = (int32_t)WEBRTC_SPL_SHIFT_W32(tmpU32, resolutionDiff); - if (tmp32no1 > ONE_Q14) - { + if (tmp32no1 > ONE_Q14) { hnl[i] = 0; - } else if (tmp32no1 < 0) - { + } else if (tmp32no1 < 0) { hnl[i] = ONE_Q14; - } else - { + } else { // 1-echoEst/dfa hnl[i] = ONE_Q14 - (int16_t)tmp32no1; - if (hnl[i] < 0) - { + if (hnl[i] < 0) { hnl[i] = 0; } } } - if (hnl[i]) - { + if (hnl[i]) { numPosCoef++; } } // Only in wideband. Prevent the gain in upper band from being larger than // in lower band. - if (aecm->mult == 2) - { + if (aecm->mult == 2) { // TODO(bjornv): Investigate if the scaling of hnl[i] below can cause // speech distortion in double-talk. - for (i = 0; i < PART_LEN1; i++) - { + for (i = 0; i < PART_LEN1; i++) { hnl[i] = (int16_t)((hnl[i] * hnl[i]) >> 14); } - for (i = kMinPrefBand; i <= kMaxPrefBand; i++) - { + for (i = kMinPrefBand; i <= kMaxPrefBand; i++) { avgHnl32 += (int32_t)hnl[i]; } - assert(kMaxPrefBand - kMinPrefBand + 1 > 0); + RTC_DCHECK_GT(kMaxPrefBand - kMinPrefBand + 1, 0); avgHnl32 /= (kMaxPrefBand - kMinPrefBand + 1); - for (i = kMaxPrefBand; i < PART_LEN1; i++) - { - if (hnl[i] > (int16_t)avgHnl32) - { + for (i = kMaxPrefBand; i < PART_LEN1; i++) { + if (hnl[i] > (int16_t)avgHnl32) { hnl[i] = (int16_t)avgHnl32; } } } // Calculate NLP gain, result is in Q14 - if (aecm->nlpFlag) - { - for (i = 0; i < PART_LEN1; i++) - { + if (aecm->nlpFlag) { + for (i = 0; i < PART_LEN1; i++) { // Truncate values close to zero and one. - if (hnl[i] > NLP_COMP_HIGH) - { + if (hnl[i] > NLP_COMP_HIGH) { hnl[i] = ONE_Q14; - } else if (hnl[i] < NLP_COMP_LOW) - { + } else if (hnl[i] < NLP_COMP_LOW) { hnl[i] = 0; } // Remove outliers - if (numPosCoef < 3) - { + if (numPosCoef < 3) { nlpGain = 0; - } else - { + } else { nlpGain = ONE_Q14; } // NLP - if ((hnl[i] == ONE_Q14) && (nlpGain == ONE_Q14)) - { + if ((hnl[i] == ONE_Q14) && (nlpGain == ONE_Q14)) { hnl[i] = ONE_Q14; - } else - { + } else { hnl[i] = (int16_t)((hnl[i] * nlpGain) >> 14); } // multiply with Wiener coefficients - efw[i].real = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real, - hnl[i], 14)); - efw[i].imag = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag, - hnl[i], 14)); + efw[i].real = (int16_t)( + WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real, hnl[i], 14)); + efw[i].imag = (int16_t)( + WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag, hnl[i], 14)); } - } - else - { + } else { // multiply with Wiener coefficients - for (i = 0; i < PART_LEN1; i++) - { - efw[i].real = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real, - hnl[i], 14)); - efw[i].imag = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag, - hnl[i], 14)); + for (i = 0; i < PART_LEN1; i++) { + efw[i].real = (int16_t)( + WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real, hnl[i], 14)); + efw[i].imag = (int16_t)( + WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag, hnl[i], 14)); } } - if (aecm->cngMode == AecmTrue) - { + if (aecm->cngMode == AecmTrue) { ComfortNoise(aecm, ptrDfaClean, efw, hnl); } @@ -637,135 +668,4 @@ int WebRtcAecm_ProcessBlock(AecmCore* aecm, return 0; } -static void ComfortNoise(AecmCore* aecm, - const uint16_t* dfa, - ComplexInt16* out, - const int16_t* lambda) { - int16_t i; - int16_t tmp16; - int32_t tmp32; - - int16_t randW16[PART_LEN]; - int16_t uReal[PART_LEN1]; - int16_t uImag[PART_LEN1]; - int32_t outLShift32; - int16_t noiseRShift16[PART_LEN1]; - - int16_t shiftFromNearToNoise = kNoiseEstQDomain - aecm->dfaCleanQDomain; - int16_t minTrackShift; - - assert(shiftFromNearToNoise >= 0); - assert(shiftFromNearToNoise < 16); - - if (aecm->noiseEstCtr < 100) - { - // Track the minimum more quickly initially. - aecm->noiseEstCtr++; - minTrackShift = 6; - } else - { - minTrackShift = 9; - } - - // Estimate noise power. - for (i = 0; i < PART_LEN1; i++) - { - // Shift to the noise domain. - tmp32 = (int32_t)dfa[i]; - outLShift32 = tmp32 << shiftFromNearToNoise; - - if (outLShift32 < aecm->noiseEst[i]) - { - // Reset "too low" counter - aecm->noiseEstTooLowCtr[i] = 0; - // Track the minimum. - if (aecm->noiseEst[i] < (1 << minTrackShift)) - { - // For small values, decrease noiseEst[i] every - // |kNoiseEstIncCount| block. The regular approach below can not - // go further down due to truncation. - aecm->noiseEstTooHighCtr[i]++; - if (aecm->noiseEstTooHighCtr[i] >= kNoiseEstIncCount) - { - aecm->noiseEst[i]--; - aecm->noiseEstTooHighCtr[i] = 0; // Reset the counter - } - } - else - { - aecm->noiseEst[i] -= ((aecm->noiseEst[i] - outLShift32) - >> minTrackShift); - } - } else - { - // Reset "too high" counter - aecm->noiseEstTooHighCtr[i] = 0; - // Ramp slowly upwards until we hit the minimum again. - if ((aecm->noiseEst[i] >> 19) > 0) - { - // Avoid overflow. - // Multiplication with 2049 will cause wrap around. Scale - // down first and then multiply - aecm->noiseEst[i] >>= 11; - aecm->noiseEst[i] *= 2049; - } - else if ((aecm->noiseEst[i] >> 11) > 0) - { - // Large enough for relative increase - aecm->noiseEst[i] *= 2049; - aecm->noiseEst[i] >>= 11; - } - else - { - // Make incremental increases based on size every - // |kNoiseEstIncCount| block - aecm->noiseEstTooLowCtr[i]++; - if (aecm->noiseEstTooLowCtr[i] >= kNoiseEstIncCount) - { - aecm->noiseEst[i] += (aecm->noiseEst[i] >> 9) + 1; - aecm->noiseEstTooLowCtr[i] = 0; // Reset counter - } - } - } - } - - for (i = 0; i < PART_LEN1; i++) - { - tmp32 = aecm->noiseEst[i] >> shiftFromNearToNoise; - if (tmp32 > 32767) - { - tmp32 = 32767; - aecm->noiseEst[i] = tmp32 << shiftFromNearToNoise; - } - noiseRShift16[i] = (int16_t)tmp32; - - tmp16 = ONE_Q14 - lambda[i]; - noiseRShift16[i] = (int16_t)((tmp16 * noiseRShift16[i]) >> 14); - } - - // Generate a uniform random array on [0 2^15-1]. - WebRtcSpl_RandUArray(randW16, PART_LEN, &aecm->seed); - - // Generate noise according to estimated energy. - uReal[0] = 0; // Reject LF noise. - uImag[0] = 0; - for (i = 1; i < PART_LEN1; i++) - { - // Get a random index for the cos and sin tables over [0 359]. - tmp16 = (int16_t)((359 * randW16[i - 1]) >> 15); - - // Tables are in Q13. - uReal[i] = (int16_t)((noiseRShift16[i] * WebRtcAecm_kCosTable[tmp16]) >> - 13); - uImag[i] = (int16_t)((-noiseRShift16[i] * WebRtcAecm_kSinTable[tmp16]) >> - 13); - } - uImag[PART_LEN] = 0; - - for (i = 0; i < PART_LEN1; i++) - { - out[i].real = WebRtcSpl_AddSatW16(out[i].real, uReal[i]); - out[i].imag = WebRtcSpl_AddSatW16(out[i].imag, uImag[i]); - } -} - +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aecm/aecm_core_mips.c b/webrtc/modules/audio_processing/aecm/aecm_core_mips.c deleted file mode 100644 index 3c2343a..0000000 --- a/webrtc/modules/audio_processing/aecm/aecm_core_mips.c +++ /dev/null @@ -1,1566 +0,0 @@ -/* - * Copyright (c) 2013 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 "webrtc/modules/audio_processing/aecm/aecm_core.h" - -#include - -#include "webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h" -#include "webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h" - -static const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END = { - 0, 399, 798, 1196, 1594, 1990, 2386, 2780, 3172, - 3562, 3951, 4337, 4720, 5101, 5478, 5853, 6224, - 6591, 6954, 7313, 7668, 8019, 8364, 8705, 9040, - 9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514, - 11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553, - 13773, 13985, 14189, 14384, 14571, 14749, 14918, 15079, - 15231, 15373, 15506, 15631, 15746, 15851, 15947, 16034, - 16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384 -}; - -static const int16_t kNoiseEstQDomain = 15; -static const int16_t kNoiseEstIncCount = 5; - -static int16_t coefTable[] = { - 0, 4, 256, 260, 128, 132, 384, 388, - 64, 68, 320, 324, 192, 196, 448, 452, - 32, 36, 288, 292, 160, 164, 416, 420, - 96, 100, 352, 356, 224, 228, 480, 484, - 16, 20, 272, 276, 144, 148, 400, 404, - 80, 84, 336, 340, 208, 212, 464, 468, - 48, 52, 304, 308, 176, 180, 432, 436, - 112, 116, 368, 372, 240, 244, 496, 500, - 8, 12, 264, 268, 136, 140, 392, 396, - 72, 76, 328, 332, 200, 204, 456, 460, - 40, 44, 296, 300, 168, 172, 424, 428, - 104, 108, 360, 364, 232, 236, 488, 492, - 24, 28, 280, 284, 152, 156, 408, 412, - 88, 92, 344, 348, 216, 220, 472, 476, - 56, 60, 312, 316, 184, 188, 440, 444, - 120, 124, 376, 380, 248, 252, 504, 508 -}; - -static int16_t coefTable_ifft[] = { - 0, 512, 256, 508, 128, 252, 384, 380, - 64, 124, 320, 444, 192, 188, 448, 316, - 32, 60, 288, 476, 160, 220, 416, 348, - 96, 92, 352, 412, 224, 156, 480, 284, - 16, 28, 272, 492, 144, 236, 400, 364, - 80, 108, 336, 428, 208, 172, 464, 300, - 48, 44, 304, 460, 176, 204, 432, 332, - 112, 76, 368, 396, 240, 140, 496, 268, - 8, 12, 264, 500, 136, 244, 392, 372, - 72, 116, 328, 436, 200, 180, 456, 308, - 40, 52, 296, 468, 168, 212, 424, 340, - 104, 84, 360, 404, 232, 148, 488, 276, - 24, 20, 280, 484, 152, 228, 408, 356, - 88, 100, 344, 420, 216, 164, 472, 292, - 56, 36, 312, 452, 184, 196, 440, 324, - 120, 68, 376, 388, 248, 132, 504, 260 -}; - -static void ComfortNoise(AecmCore* aecm, - const uint16_t* dfa, - ComplexInt16* out, - const int16_t* lambda); - -static void WindowAndFFT(AecmCore* aecm, - int16_t* fft, - const int16_t* time_signal, - ComplexInt16* freq_signal, - int time_signal_scaling) { - int i, j; - int32_t tmp1, tmp2, tmp3, tmp4; - int16_t* pfrfi; - ComplexInt16* pfreq_signal; - int16_t f_coef, s_coef; - int32_t load_ptr, store_ptr1, store_ptr2, shift, shift1; - int32_t hann, hann1, coefs; - - memset(fft, 0, sizeof(int16_t) * PART_LEN4); - - // FFT of signal - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[shift], %[time_signal_scaling], -14 \n\t" - "addiu %[i], $zero, 64 \n\t" - "addiu %[load_ptr], %[time_signal], 0 \n\t" - "addiu %[hann], %[hanning], 0 \n\t" - "addiu %[hann1], %[hanning], 128 \n\t" - "addiu %[coefs], %[coefTable], 0 \n\t" - "bltz %[shift], 2f \n\t" - " negu %[shift1], %[shift] \n\t" - "1: \n\t" - "lh %[tmp1], 0(%[load_ptr]) \n\t" - "lh %[tmp2], 0(%[hann]) \n\t" - "lh %[tmp3], 128(%[load_ptr]) \n\t" - "lh %[tmp4], 0(%[hann1]) \n\t" - "addiu %[i], %[i], -1 \n\t" - "mul %[tmp1], %[tmp1], %[tmp2] \n\t" - "mul %[tmp3], %[tmp3], %[tmp4] \n\t" - "lh %[f_coef], 0(%[coefs]) \n\t" - "lh %[s_coef], 2(%[coefs]) \n\t" - "addiu %[load_ptr], %[load_ptr], 2 \n\t" - "addiu %[hann], %[hann], 2 \n\t" - "addiu %[hann1], %[hann1], -2 \n\t" - "addu %[store_ptr1], %[fft], %[f_coef] \n\t" - "addu %[store_ptr2], %[fft], %[s_coef] \n\t" - "sllv %[tmp1], %[tmp1], %[shift] \n\t" - "sllv %[tmp3], %[tmp3], %[shift] \n\t" - "sh %[tmp1], 0(%[store_ptr1]) \n\t" - "sh %[tmp3], 0(%[store_ptr2]) \n\t" - "bgtz %[i], 1b \n\t" - " addiu %[coefs], %[coefs], 4 \n\t" - "b 3f \n\t" - " nop \n\t" - "2: \n\t" - "lh %[tmp1], 0(%[load_ptr]) \n\t" - "lh %[tmp2], 0(%[hann]) \n\t" - "lh %[tmp3], 128(%[load_ptr]) \n\t" - "lh %[tmp4], 0(%[hann1]) \n\t" - "addiu %[i], %[i], -1 \n\t" - "mul %[tmp1], %[tmp1], %[tmp2] \n\t" - "mul %[tmp3], %[tmp3], %[tmp4] \n\t" - "lh %[f_coef], 0(%[coefs]) \n\t" - "lh %[s_coef], 2(%[coefs]) \n\t" - "addiu %[load_ptr], %[load_ptr], 2 \n\t" - "addiu %[hann], %[hann], 2 \n\t" - "addiu %[hann1], %[hann1], -2 \n\t" - "addu %[store_ptr1], %[fft], %[f_coef] \n\t" - "addu %[store_ptr2], %[fft], %[s_coef] \n\t" - "srav %[tmp1], %[tmp1], %[shift1] \n\t" - "srav %[tmp3], %[tmp3], %[shift1] \n\t" - "sh %[tmp1], 0(%[store_ptr1]) \n\t" - "sh %[tmp3], 0(%[store_ptr2]) \n\t" - "bgtz %[i], 2b \n\t" - " addiu %[coefs], %[coefs], 4 \n\t" - "3: \n\t" - ".set pop \n\t" - : [load_ptr] "=&r" (load_ptr), [shift] "=&r" (shift), [hann] "=&r" (hann), - [hann1] "=&r" (hann1), [shift1] "=&r" (shift1), [coefs] "=&r" (coefs), - [tmp1] "=&r" (tmp1), [tmp2] "=&r" (tmp2), [tmp3] "=&r" (tmp3), - [tmp4] "=&r" (tmp4), [i] "=&r" (i), [f_coef] "=&r" (f_coef), - [s_coef] "=&r" (s_coef), [store_ptr1] "=&r" (store_ptr1), - [store_ptr2] "=&r" (store_ptr2) - : [time_signal] "r" (time_signal), [coefTable] "r" (coefTable), - [time_signal_scaling] "r" (time_signal_scaling), - [hanning] "r" (WebRtcAecm_kSqrtHanning), [fft] "r" (fft) - : "memory", "hi", "lo" - ); - - WebRtcSpl_ComplexFFT(fft, PART_LEN_SHIFT, 1); - pfrfi = fft; - pfreq_signal = freq_signal; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[j], $zero, 128 \n\t" - "1: \n\t" - "lh %[tmp1], 0(%[pfrfi]) \n\t" - "lh %[tmp2], 2(%[pfrfi]) \n\t" - "lh %[tmp3], 4(%[pfrfi]) \n\t" - "lh %[tmp4], 6(%[pfrfi]) \n\t" - "subu %[tmp2], $zero, %[tmp2] \n\t" - "sh %[tmp1], 0(%[pfreq_signal]) \n\t" - "sh %[tmp2], 2(%[pfreq_signal]) \n\t" - "subu %[tmp4], $zero, %[tmp4] \n\t" - "sh %[tmp3], 4(%[pfreq_signal]) \n\t" - "sh %[tmp4], 6(%[pfreq_signal]) \n\t" - "lh %[tmp1], 8(%[pfrfi]) \n\t" - "lh %[tmp2], 10(%[pfrfi]) \n\t" - "lh %[tmp3], 12(%[pfrfi]) \n\t" - "lh %[tmp4], 14(%[pfrfi]) \n\t" - "addiu %[j], %[j], -8 \n\t" - "subu %[tmp2], $zero, %[tmp2] \n\t" - "sh %[tmp1], 8(%[pfreq_signal]) \n\t" - "sh %[tmp2], 10(%[pfreq_signal]) \n\t" - "subu %[tmp4], $zero, %[tmp4] \n\t" - "sh %[tmp3], 12(%[pfreq_signal]) \n\t" - "sh %[tmp4], 14(%[pfreq_signal]) \n\t" - "addiu %[pfreq_signal], %[pfreq_signal], 16 \n\t" - "bgtz %[j], 1b \n\t" - " addiu %[pfrfi], %[pfrfi], 16 \n\t" - ".set pop \n\t" - : [tmp1] "=&r" (tmp1), [tmp2] "=&r" (tmp2), [tmp3] "=&r" (tmp3), - [j] "=&r" (j), [pfrfi] "+r" (pfrfi), [pfreq_signal] "+r" (pfreq_signal), - [tmp4] "=&r" (tmp4) - : - : "memory" - ); -} - -static void InverseFFTAndWindow(AecmCore* aecm, - int16_t* fft, - ComplexInt16* efw, - int16_t* output, - const int16_t* nearendClean) { - int i, outCFFT; - int32_t tmp1, tmp2, tmp3, tmp4, tmp_re, tmp_im; - int16_t* pcoefTable_ifft = coefTable_ifft; - int16_t* pfft = fft; - int16_t* ppfft = fft; - ComplexInt16* pefw = efw; - int32_t out_aecm; - int16_t* paecm_buf = aecm->outBuf; - const int16_t* p_kSqrtHanning = WebRtcAecm_kSqrtHanning; - const int16_t* pp_kSqrtHanning = &WebRtcAecm_kSqrtHanning[PART_LEN]; - int16_t* output1 = output; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[i], $zero, 64 \n\t" - "1: \n\t" - "lh %[tmp1], 0(%[pcoefTable_ifft]) \n\t" - "lh %[tmp2], 2(%[pcoefTable_ifft]) \n\t" - "lh %[tmp_re], 0(%[pefw]) \n\t" - "lh %[tmp_im], 2(%[pefw]) \n\t" - "addu %[pfft], %[fft], %[tmp2] \n\t" - "sh %[tmp_re], 0(%[pfft]) \n\t" - "sh %[tmp_im], 2(%[pfft]) \n\t" - "addu %[pfft], %[fft], %[tmp1] \n\t" - "sh %[tmp_re], 0(%[pfft]) \n\t" - "subu %[tmp_im], $zero, %[tmp_im] \n\t" - "sh %[tmp_im], 2(%[pfft]) \n\t" - "lh %[tmp1], 4(%[pcoefTable_ifft]) \n\t" - "lh %[tmp2], 6(%[pcoefTable_ifft]) \n\t" - "lh %[tmp_re], 4(%[pefw]) \n\t" - "lh %[tmp_im], 6(%[pefw]) \n\t" - "addu %[pfft], %[fft], %[tmp2] \n\t" - "sh %[tmp_re], 0(%[pfft]) \n\t" - "sh %[tmp_im], 2(%[pfft]) \n\t" - "addu %[pfft], %[fft], %[tmp1] \n\t" - "sh %[tmp_re], 0(%[pfft]) \n\t" - "subu %[tmp_im], $zero, %[tmp_im] \n\t" - "sh %[tmp_im], 2(%[pfft]) \n\t" - "lh %[tmp1], 8(%[pcoefTable_ifft]) \n\t" - "lh %[tmp2], 10(%[pcoefTable_ifft]) \n\t" - "lh %[tmp_re], 8(%[pefw]) \n\t" - "lh %[tmp_im], 10(%[pefw]) \n\t" - "addu %[pfft], %[fft], %[tmp2] \n\t" - "sh %[tmp_re], 0(%[pfft]) \n\t" - "sh %[tmp_im], 2(%[pfft]) \n\t" - "addu %[pfft], %[fft], %[tmp1] \n\t" - "sh %[tmp_re], 0(%[pfft]) \n\t" - "subu %[tmp_im], $zero, %[tmp_im] \n\t" - "sh %[tmp_im], 2(%[pfft]) \n\t" - "lh %[tmp1], 12(%[pcoefTable_ifft]) \n\t" - "lh %[tmp2], 14(%[pcoefTable_ifft]) \n\t" - "lh %[tmp_re], 12(%[pefw]) \n\t" - "lh %[tmp_im], 14(%[pefw]) \n\t" - "addu %[pfft], %[fft], %[tmp2] \n\t" - "sh %[tmp_re], 0(%[pfft]) \n\t" - "sh %[tmp_im], 2(%[pfft]) \n\t" - "addu %[pfft], %[fft], %[tmp1] \n\t" - "sh %[tmp_re], 0(%[pfft]) \n\t" - "subu %[tmp_im], $zero, %[tmp_im] \n\t" - "sh %[tmp_im], 2(%[pfft]) \n\t" - "addiu %[pcoefTable_ifft], %[pcoefTable_ifft], 16 \n\t" - "addiu %[i], %[i], -4 \n\t" - "bgtz %[i], 1b \n\t" - " addiu %[pefw], %[pefw], 16 \n\t" - ".set pop \n\t" - : [tmp1] "=&r" (tmp1), [tmp2] "=&r" (tmp2), [pfft] "+r" (pfft), - [i] "=&r" (i), [tmp_re] "=&r" (tmp_re), [tmp_im] "=&r" (tmp_im), - [pefw] "+r" (pefw), [pcoefTable_ifft] "+r" (pcoefTable_ifft), - [fft] "+r" (fft) - : - : "memory" - ); - - fft[2] = efw[PART_LEN].real; - fft[3] = -efw[PART_LEN].imag; - - outCFFT = WebRtcSpl_ComplexIFFT(fft, PART_LEN_SHIFT, 1); - pfft = fft; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[i], $zero, 128 \n\t" - "1: \n\t" - "lh %[tmp1], 0(%[ppfft]) \n\t" - "lh %[tmp2], 4(%[ppfft]) \n\t" - "lh %[tmp3], 8(%[ppfft]) \n\t" - "lh %[tmp4], 12(%[ppfft]) \n\t" - "addiu %[i], %[i], -4 \n\t" - "sh %[tmp1], 0(%[pfft]) \n\t" - "sh %[tmp2], 2(%[pfft]) \n\t" - "sh %[tmp3], 4(%[pfft]) \n\t" - "sh %[tmp4], 6(%[pfft]) \n\t" - "addiu %[ppfft], %[ppfft], 16 \n\t" - "bgtz %[i], 1b \n\t" - " addiu %[pfft], %[pfft], 8 \n\t" - ".set pop \n\t" - : [tmp1] "=&r" (tmp1), [tmp2] "=&r" (tmp2), [pfft] "+r" (pfft), - [i] "=&r" (i), [tmp3] "=&r" (tmp3), [tmp4] "=&r" (tmp4), - [ppfft] "+r" (ppfft) - : - : "memory" - ); - - pfft = fft; - out_aecm = (int32_t)(outCFFT - aecm->dfaCleanQDomain); - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[i], $zero, 64 \n\t" - "11: \n\t" - "lh %[tmp1], 0(%[pfft]) \n\t" - "lh %[tmp2], 0(%[p_kSqrtHanning]) \n\t" - "addiu %[i], %[i], -2 \n\t" - "mul %[tmp1], %[tmp1], %[tmp2] \n\t" - "lh %[tmp3], 2(%[pfft]) \n\t" - "lh %[tmp4], 2(%[p_kSqrtHanning]) \n\t" - "mul %[tmp3], %[tmp3], %[tmp4] \n\t" - "addiu %[tmp1], %[tmp1], 8192 \n\t" - "sra %[tmp1], %[tmp1], 14 \n\t" - "addiu %[tmp3], %[tmp3], 8192 \n\t" - "sra %[tmp3], %[tmp3], 14 \n\t" - "bgez %[out_aecm], 1f \n\t" - " negu %[tmp2], %[out_aecm] \n\t" - "srav %[tmp1], %[tmp1], %[tmp2] \n\t" - "b 2f \n\t" - " srav %[tmp3], %[tmp3], %[tmp2] \n\t" - "1: \n\t" - "sllv %[tmp1], %[tmp1], %[out_aecm] \n\t" - "sllv %[tmp3], %[tmp3], %[out_aecm] \n\t" - "2: \n\t" - "lh %[tmp4], 0(%[paecm_buf]) \n\t" - "lh %[tmp2], 2(%[paecm_buf]) \n\t" - "addu %[tmp3], %[tmp3], %[tmp2] \n\t" - "addu %[tmp1], %[tmp1], %[tmp4] \n\t" -#if defined(MIPS_DSP_R1_LE) - "shll_s.w %[tmp1], %[tmp1], 16 \n\t" - "sra %[tmp1], %[tmp1], 16 \n\t" - "shll_s.w %[tmp3], %[tmp3], 16 \n\t" - "sra %[tmp3], %[tmp3], 16 \n\t" -#else // #if defined(MIPS_DSP_R1_LE) - "sra %[tmp4], %[tmp1], 31 \n\t" - "sra %[tmp2], %[tmp1], 15 \n\t" - "beq %[tmp4], %[tmp2], 3f \n\t" - " ori %[tmp2], $zero, 0x7fff \n\t" - "xor %[tmp1], %[tmp2], %[tmp4] \n\t" - "3: \n\t" - "sra %[tmp2], %[tmp3], 31 \n\t" - "sra %[tmp4], %[tmp3], 15 \n\t" - "beq %[tmp2], %[tmp4], 4f \n\t" - " ori %[tmp4], $zero, 0x7fff \n\t" - "xor %[tmp3], %[tmp4], %[tmp2] \n\t" - "4: \n\t" -#endif // #if defined(MIPS_DSP_R1_LE) - "sh %[tmp1], 0(%[pfft]) \n\t" - "sh %[tmp1], 0(%[output1]) \n\t" - "sh %[tmp3], 2(%[pfft]) \n\t" - "sh %[tmp3], 2(%[output1]) \n\t" - "lh %[tmp1], 128(%[pfft]) \n\t" - "lh %[tmp2], 0(%[pp_kSqrtHanning]) \n\t" - "mul %[tmp1], %[tmp1], %[tmp2] \n\t" - "lh %[tmp3], 130(%[pfft]) \n\t" - "lh %[tmp4], -2(%[pp_kSqrtHanning]) \n\t" - "mul %[tmp3], %[tmp3], %[tmp4] \n\t" - "sra %[tmp1], %[tmp1], 14 \n\t" - "sra %[tmp3], %[tmp3], 14 \n\t" - "bgez %[out_aecm], 5f \n\t" - " negu %[tmp2], %[out_aecm] \n\t" - "srav %[tmp3], %[tmp3], %[tmp2] \n\t" - "b 6f \n\t" - " srav %[tmp1], %[tmp1], %[tmp2] \n\t" - "5: \n\t" - "sllv %[tmp1], %[tmp1], %[out_aecm] \n\t" - "sllv %[tmp3], %[tmp3], %[out_aecm] \n\t" - "6: \n\t" -#if defined(MIPS_DSP_R1_LE) - "shll_s.w %[tmp1], %[tmp1], 16 \n\t" - "sra %[tmp1], %[tmp1], 16 \n\t" - "shll_s.w %[tmp3], %[tmp3], 16 \n\t" - "sra %[tmp3], %[tmp3], 16 \n\t" -#else // #if defined(MIPS_DSP_R1_LE) - "sra %[tmp4], %[tmp1], 31 \n\t" - "sra %[tmp2], %[tmp1], 15 \n\t" - "beq %[tmp4], %[tmp2], 7f \n\t" - " ori %[tmp2], $zero, 0x7fff \n\t" - "xor %[tmp1], %[tmp2], %[tmp4] \n\t" - "7: \n\t" - "sra %[tmp2], %[tmp3], 31 \n\t" - "sra %[tmp4], %[tmp3], 15 \n\t" - "beq %[tmp2], %[tmp4], 8f \n\t" - " ori %[tmp4], $zero, 0x7fff \n\t" - "xor %[tmp3], %[tmp4], %[tmp2] \n\t" - "8: \n\t" -#endif // #if defined(MIPS_DSP_R1_LE) - "sh %[tmp1], 0(%[paecm_buf]) \n\t" - "sh %[tmp3], 2(%[paecm_buf]) \n\t" - "addiu %[output1], %[output1], 4 \n\t" - "addiu %[paecm_buf], %[paecm_buf], 4 \n\t" - "addiu %[pfft], %[pfft], 4 \n\t" - "addiu %[p_kSqrtHanning], %[p_kSqrtHanning], 4 \n\t" - "bgtz %[i], 11b \n\t" - " addiu %[pp_kSqrtHanning], %[pp_kSqrtHanning], -4 \n\t" - ".set pop \n\t" - : [tmp1] "=&r" (tmp1), [tmp2] "=&r" (tmp2), [pfft] "+r" (pfft), - [output1] "+r" (output1), [tmp3] "=&r" (tmp3), [tmp4] "=&r" (tmp4), - [paecm_buf] "+r" (paecm_buf), [i] "=&r" (i), - [pp_kSqrtHanning] "+r" (pp_kSqrtHanning), - [p_kSqrtHanning] "+r" (p_kSqrtHanning) - : [out_aecm] "r" (out_aecm), - [WebRtcAecm_kSqrtHanning] "r" (WebRtcAecm_kSqrtHanning) - : "hi", "lo","memory" - ); - - // Copy the current block to the old position - // (aecm->outBuf is shifted elsewhere) - memcpy(aecm->xBuf, aecm->xBuf + PART_LEN, sizeof(int16_t) * PART_LEN); - memcpy(aecm->dBufNoisy, - aecm->dBufNoisy + PART_LEN, - sizeof(int16_t) * PART_LEN); - if (nearendClean != NULL) { - memcpy(aecm->dBufClean, - aecm->dBufClean + PART_LEN, - sizeof(int16_t) * PART_LEN); - } -} - -void WebRtcAecm_CalcLinearEnergies_mips(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est, - uint32_t* far_energy, - uint32_t* echo_energy_adapt, - uint32_t* echo_energy_stored) { - int i; - uint32_t par1 = (*far_energy); - uint32_t par2 = (*echo_energy_adapt); - uint32_t par3 = (*echo_energy_stored); - int16_t* ch_stored_p = &(aecm->channelStored[0]); - int16_t* ch_adapt_p = &(aecm->channelAdapt16[0]); - uint16_t* spectrum_p = (uint16_t*)(&(far_spectrum[0])); - int32_t* echo_p = &(echo_est[0]); - int32_t temp0, stored0, echo0, adept0, spectrum0; - int32_t stored1, adept1, spectrum1, echo1, temp1; - - // Get energy for the delayed far end signal and estimated - // echo using both stored and adapted channels. - for (i = 0; i < PART_LEN; i+= 4) { - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "lh %[stored0], 0(%[ch_stored_p]) \n\t" - "lhu %[adept0], 0(%[ch_adapt_p]) \n\t" - "lhu %[spectrum0], 0(%[spectrum_p]) \n\t" - "lh %[stored1], 2(%[ch_stored_p]) \n\t" - "lhu %[adept1], 2(%[ch_adapt_p]) \n\t" - "lhu %[spectrum1], 2(%[spectrum_p]) \n\t" - "mul %[echo0], %[stored0], %[spectrum0] \n\t" - "mul %[temp0], %[adept0], %[spectrum0] \n\t" - "mul %[echo1], %[stored1], %[spectrum1] \n\t" - "mul %[temp1], %[adept1], %[spectrum1] \n\t" - "addu %[par1], %[par1], %[spectrum0] \n\t" - "addu %[par1], %[par1], %[spectrum1] \n\t" - "addiu %[echo_p], %[echo_p], 16 \n\t" - "addu %[par3], %[par3], %[echo0] \n\t" - "addu %[par2], %[par2], %[temp0] \n\t" - "addu %[par3], %[par3], %[echo1] \n\t" - "addu %[par2], %[par2], %[temp1] \n\t" - "usw %[echo0], -16(%[echo_p]) \n\t" - "usw %[echo1], -12(%[echo_p]) \n\t" - "lh %[stored0], 4(%[ch_stored_p]) \n\t" - "lhu %[adept0], 4(%[ch_adapt_p]) \n\t" - "lhu %[spectrum0], 4(%[spectrum_p]) \n\t" - "lh %[stored1], 6(%[ch_stored_p]) \n\t" - "lhu %[adept1], 6(%[ch_adapt_p]) \n\t" - "lhu %[spectrum1], 6(%[spectrum_p]) \n\t" - "mul %[echo0], %[stored0], %[spectrum0] \n\t" - "mul %[temp0], %[adept0], %[spectrum0] \n\t" - "mul %[echo1], %[stored1], %[spectrum1] \n\t" - "mul %[temp1], %[adept1], %[spectrum1] \n\t" - "addu %[par1], %[par1], %[spectrum0] \n\t" - "addu %[par1], %[par1], %[spectrum1] \n\t" - "addiu %[ch_stored_p], %[ch_stored_p], 8 \n\t" - "addiu %[ch_adapt_p], %[ch_adapt_p], 8 \n\t" - "addiu %[spectrum_p], %[spectrum_p], 8 \n\t" - "addu %[par3], %[par3], %[echo0] \n\t" - "addu %[par2], %[par2], %[temp0] \n\t" - "addu %[par3], %[par3], %[echo1] \n\t" - "addu %[par2], %[par2], %[temp1] \n\t" - "usw %[echo0], -8(%[echo_p]) \n\t" - "usw %[echo1], -4(%[echo_p]) \n\t" - ".set pop \n\t" - : [temp0] "=&r" (temp0), [stored0] "=&r" (stored0), - [adept0] "=&r" (adept0), [spectrum0] "=&r" (spectrum0), - [echo0] "=&r" (echo0), [echo_p] "+r" (echo_p), [par3] "+r" (par3), - [par1] "+r" (par1), [par2] "+r" (par2), [stored1] "=&r" (stored1), - [adept1] "=&r" (adept1), [echo1] "=&r" (echo1), - [spectrum1] "=&r" (spectrum1), [temp1] "=&r" (temp1), - [ch_stored_p] "+r" (ch_stored_p), [ch_adapt_p] "+r" (ch_adapt_p), - [spectrum_p] "+r" (spectrum_p) - : - : "hi", "lo", "memory" - ); - } - - echo_est[PART_LEN] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[PART_LEN], - far_spectrum[PART_LEN]); - par1 += (uint32_t)(far_spectrum[PART_LEN]); - par2 += aecm->channelAdapt16[PART_LEN] * far_spectrum[PART_LEN]; - par3 += (uint32_t)echo_est[PART_LEN]; - - (*far_energy) = par1; - (*echo_energy_adapt) = par2; - (*echo_energy_stored) = par3; -} - -#if defined(MIPS_DSP_R1_LE) -void WebRtcAecm_StoreAdaptiveChannel_mips(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est) { - int i; - int16_t* temp1; - uint16_t* temp8; - int32_t temp0, temp2, temp3, temp4, temp5, temp6; - int32_t* temp7 = &(echo_est[0]); - temp1 = &(aecm->channelStored[0]); - temp8 = (uint16_t*)(&far_spectrum[0]); - - // During startup we store the channel every block. - memcpy(aecm->channelStored, aecm->channelAdapt16, - sizeof(int16_t) * PART_LEN1); - // Recalculate echo estimate - for (i = 0; i < PART_LEN; i += 4) { - __asm __volatile ( - "ulw %[temp0], 0(%[temp8]) \n\t" - "ulw %[temp2], 0(%[temp1]) \n\t" - "ulw %[temp4], 4(%[temp8]) \n\t" - "ulw %[temp5], 4(%[temp1]) \n\t" - "muleq_s.w.phl %[temp3], %[temp2], %[temp0] \n\t" - "muleq_s.w.phr %[temp0], %[temp2], %[temp0] \n\t" - "muleq_s.w.phl %[temp6], %[temp5], %[temp4] \n\t" - "muleq_s.w.phr %[temp4], %[temp5], %[temp4] \n\t" - "addiu %[temp7], %[temp7], 16 \n\t" - "addiu %[temp1], %[temp1], 8 \n\t" - "addiu %[temp8], %[temp8], 8 \n\t" - "sra %[temp3], %[temp3], 1 \n\t" - "sra %[temp0], %[temp0], 1 \n\t" - "sra %[temp6], %[temp6], 1 \n\t" - "sra %[temp4], %[temp4], 1 \n\t" - "usw %[temp3], -12(%[temp7]) \n\t" - "usw %[temp0], -16(%[temp7]) \n\t" - "usw %[temp6], -4(%[temp7]) \n\t" - "usw %[temp4], -8(%[temp7]) \n\t" - : [temp0] "=&r" (temp0), [temp2] "=&r" (temp2), [temp3] "=&r" (temp3), - [temp4] "=&r" (temp4), [temp5] "=&r" (temp5), [temp6] "=&r" (temp6), - [temp1] "+r" (temp1), [temp8] "+r" (temp8), [temp7] "+r" (temp7) - : - : "hi", "lo", "memory" - ); - } - echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - far_spectrum[i]); -} - -void WebRtcAecm_ResetAdaptiveChannel_mips(AecmCore* aecm) { - int i; - int32_t* temp3; - int16_t* temp0; - int32_t temp1, temp2, temp4, temp5; - - temp0 = &(aecm->channelStored[0]); - temp3 = &(aecm->channelAdapt32[0]); - - // The stored channel has a significantly lower MSE than the adaptive one for - // two consecutive calculations. Reset the adaptive channel. - memcpy(aecm->channelAdapt16, - aecm->channelStored, - sizeof(int16_t) * PART_LEN1); - - // Restore the W32 channel - for (i = 0; i < PART_LEN; i += 4) { - __asm __volatile ( - "ulw %[temp1], 0(%[temp0]) \n\t" - "ulw %[temp4], 4(%[temp0]) \n\t" - "preceq.w.phl %[temp2], %[temp1] \n\t" - "preceq.w.phr %[temp1], %[temp1] \n\t" - "preceq.w.phl %[temp5], %[temp4] \n\t" - "preceq.w.phr %[temp4], %[temp4] \n\t" - "addiu %[temp0], %[temp0], 8 \n\t" - "usw %[temp2], 4(%[temp3]) \n\t" - "usw %[temp1], 0(%[temp3]) \n\t" - "usw %[temp5], 12(%[temp3]) \n\t" - "usw %[temp4], 8(%[temp3]) \n\t" - "addiu %[temp3], %[temp3], 16 \n\t" - : [temp1] "=&r" (temp1), [temp2] "=&r" (temp2), - [temp4] "=&r" (temp4), [temp5] "=&r" (temp5), - [temp3] "+r" (temp3), [temp0] "+r" (temp0) - : - : "memory" - ); - } - - aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; -} -#endif // #if defined(MIPS_DSP_R1_LE) - -// Transforms a time domain signal into the frequency domain, outputting the -// complex valued signal, absolute value and sum of absolute values. -// -// time_signal [in] Pointer to time domain signal -// freq_signal_real [out] Pointer to real part of frequency domain array -// freq_signal_imag [out] Pointer to imaginary part of frequency domain -// array -// freq_signal_abs [out] Pointer to absolute value of frequency domain -// array -// freq_signal_sum_abs [out] Pointer to the sum of all absolute values in -// the frequency domain array -// return value The Q-domain of current frequency values -// -static int TimeToFrequencyDomain(AecmCore* aecm, - const int16_t* time_signal, - ComplexInt16* freq_signal, - uint16_t* freq_signal_abs, - uint32_t* freq_signal_sum_abs) { - int i = 0; - int time_signal_scaling = 0; - - // In fft_buf, +16 for 32-byte alignment. - int16_t fft_buf[PART_LEN4 + 16]; - int16_t *fft = (int16_t *) (((uintptr_t) fft_buf + 31) & ~31); - - int16_t tmp16no1; -#if !defined(MIPS_DSP_R2_LE) - int32_t tmp32no1; - int32_t tmp32no2; - int16_t tmp16no2; -#else - int32_t tmp32no10, tmp32no11, tmp32no12, tmp32no13; - int32_t tmp32no20, tmp32no21, tmp32no22, tmp32no23; - int16_t* freqp; - uint16_t* freqabsp; - uint32_t freqt0, freqt1, freqt2, freqt3; - uint32_t freqs; -#endif - -#ifdef AECM_DYNAMIC_Q - tmp16no1 = WebRtcSpl_MaxAbsValueW16(time_signal, PART_LEN2); - time_signal_scaling = WebRtcSpl_NormW16(tmp16no1); -#endif - - WindowAndFFT(aecm, fft, time_signal, freq_signal, time_signal_scaling); - - // Extract imaginary and real part, - // calculate the magnitude for all frequency bins - freq_signal[0].imag = 0; - freq_signal[PART_LEN].imag = 0; - freq_signal[PART_LEN].real = fft[PART_LEN2]; - freq_signal_abs[0] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[0].real); - freq_signal_abs[PART_LEN] = (uint16_t)WEBRTC_SPL_ABS_W16( - freq_signal[PART_LEN].real); - (*freq_signal_sum_abs) = (uint32_t)(freq_signal_abs[0]) + - (uint32_t)(freq_signal_abs[PART_LEN]); - -#if !defined(MIPS_DSP_R2_LE) - for (i = 1; i < PART_LEN; i++) { - if (freq_signal[i].real == 0) - { - freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16( - freq_signal[i].imag); - } - else if (freq_signal[i].imag == 0) - { - freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16( - freq_signal[i].real); - } - else - { - // Approximation for magnitude of complex fft output - // magn = sqrt(real^2 + imag^2) - // magn ~= alpha * max(|imag|,|real|) + beta * min(|imag|,|real|) - // - // The parameters alpha and beta are stored in Q15 - tmp16no1 = WEBRTC_SPL_ABS_W16(freq_signal[i].real); - tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag); - tmp32no1 = tmp16no1 * tmp16no1; - tmp32no2 = tmp16no2 * tmp16no2; - tmp32no2 = WebRtcSpl_AddSatW32(tmp32no1, tmp32no2); - tmp32no1 = WebRtcSpl_SqrtFloor(tmp32no2); - - freq_signal_abs[i] = (uint16_t)tmp32no1; - } - (*freq_signal_sum_abs) += (uint32_t)freq_signal_abs[i]; - } -#else // #if !defined(MIPS_DSP_R2_LE) - freqs = (uint32_t)(freq_signal_abs[0]) + - (uint32_t)(freq_signal_abs[PART_LEN]); - freqp = &(freq_signal[1].real); - - __asm __volatile ( - "lw %[freqt0], 0(%[freqp]) \n\t" - "lw %[freqt1], 4(%[freqp]) \n\t" - "lw %[freqt2], 8(%[freqp]) \n\t" - "mult $ac0, $zero, $zero \n\t" - "mult $ac1, $zero, $zero \n\t" - "mult $ac2, $zero, $zero \n\t" - "dpaq_s.w.ph $ac0, %[freqt0], %[freqt0] \n\t" - "dpaq_s.w.ph $ac1, %[freqt1], %[freqt1] \n\t" - "dpaq_s.w.ph $ac2, %[freqt2], %[freqt2] \n\t" - "addiu %[freqp], %[freqp], 12 \n\t" - "extr.w %[tmp32no20], $ac0, 1 \n\t" - "extr.w %[tmp32no21], $ac1, 1 \n\t" - "extr.w %[tmp32no22], $ac2, 1 \n\t" - : [freqt0] "=&r" (freqt0), [freqt1] "=&r" (freqt1), - [freqt2] "=&r" (freqt2), [freqp] "+r" (freqp), - [tmp32no20] "=r" (tmp32no20), [tmp32no21] "=r" (tmp32no21), - [tmp32no22] "=r" (tmp32no22) - : - : "memory", "hi", "lo", "$ac1hi", "$ac1lo", "$ac2hi", "$ac2lo" - ); - - tmp32no10 = WebRtcSpl_SqrtFloor(tmp32no20); - tmp32no11 = WebRtcSpl_SqrtFloor(tmp32no21); - tmp32no12 = WebRtcSpl_SqrtFloor(tmp32no22); - freq_signal_abs[1] = (uint16_t)tmp32no10; - freq_signal_abs[2] = (uint16_t)tmp32no11; - freq_signal_abs[3] = (uint16_t)tmp32no12; - freqs += (uint32_t)tmp32no10; - freqs += (uint32_t)tmp32no11; - freqs += (uint32_t)tmp32no12; - freqabsp = &(freq_signal_abs[4]); - for (i = 4; i < PART_LEN; i+=4) - { - __asm __volatile ( - "ulw %[freqt0], 0(%[freqp]) \n\t" - "ulw %[freqt1], 4(%[freqp]) \n\t" - "ulw %[freqt2], 8(%[freqp]) \n\t" - "ulw %[freqt3], 12(%[freqp]) \n\t" - "mult $ac0, $zero, $zero \n\t" - "mult $ac1, $zero, $zero \n\t" - "mult $ac2, $zero, $zero \n\t" - "mult $ac3, $zero, $zero \n\t" - "dpaq_s.w.ph $ac0, %[freqt0], %[freqt0] \n\t" - "dpaq_s.w.ph $ac1, %[freqt1], %[freqt1] \n\t" - "dpaq_s.w.ph $ac2, %[freqt2], %[freqt2] \n\t" - "dpaq_s.w.ph $ac3, %[freqt3], %[freqt3] \n\t" - "addiu %[freqp], %[freqp], 16 \n\t" - "addiu %[freqabsp], %[freqabsp], 8 \n\t" - "extr.w %[tmp32no20], $ac0, 1 \n\t" - "extr.w %[tmp32no21], $ac1, 1 \n\t" - "extr.w %[tmp32no22], $ac2, 1 \n\t" - "extr.w %[tmp32no23], $ac3, 1 \n\t" - : [freqt0] "=&r" (freqt0), [freqt1] "=&r" (freqt1), - [freqt2] "=&r" (freqt2), [freqt3] "=&r" (freqt3), - [tmp32no20] "=r" (tmp32no20), [tmp32no21] "=r" (tmp32no21), - [tmp32no22] "=r" (tmp32no22), [tmp32no23] "=r" (tmp32no23), - [freqabsp] "+r" (freqabsp), [freqp] "+r" (freqp) - : - : "memory", "hi", "lo", "$ac1hi", "$ac1lo", - "$ac2hi", "$ac2lo", "$ac3hi", "$ac3lo" - ); - - tmp32no10 = WebRtcSpl_SqrtFloor(tmp32no20); - tmp32no11 = WebRtcSpl_SqrtFloor(tmp32no21); - tmp32no12 = WebRtcSpl_SqrtFloor(tmp32no22); - tmp32no13 = WebRtcSpl_SqrtFloor(tmp32no23); - - __asm __volatile ( - "sh %[tmp32no10], -8(%[freqabsp]) \n\t" - "sh %[tmp32no11], -6(%[freqabsp]) \n\t" - "sh %[tmp32no12], -4(%[freqabsp]) \n\t" - "sh %[tmp32no13], -2(%[freqabsp]) \n\t" - "addu %[freqs], %[freqs], %[tmp32no10] \n\t" - "addu %[freqs], %[freqs], %[tmp32no11] \n\t" - "addu %[freqs], %[freqs], %[tmp32no12] \n\t" - "addu %[freqs], %[freqs], %[tmp32no13] \n\t" - : [freqs] "+r" (freqs) - : [tmp32no10] "r" (tmp32no10), [tmp32no11] "r" (tmp32no11), - [tmp32no12] "r" (tmp32no12), [tmp32no13] "r" (tmp32no13), - [freqabsp] "r" (freqabsp) - : "memory" - ); - } - - (*freq_signal_sum_abs) = freqs; -#endif - - return time_signal_scaling; -} - -int WebRtcAecm_ProcessBlock(AecmCore* aecm, - const int16_t* farend, - const int16_t* nearendNoisy, - const int16_t* nearendClean, - int16_t* output) { - int i; - uint32_t xfaSum; - uint32_t dfaNoisySum; - uint32_t dfaCleanSum; - uint32_t echoEst32Gained; - uint32_t tmpU32; - int32_t tmp32no1; - - uint16_t xfa[PART_LEN1]; - uint16_t dfaNoisy[PART_LEN1]; - uint16_t dfaClean[PART_LEN1]; - uint16_t* ptrDfaClean = dfaClean; - const uint16_t* far_spectrum_ptr = NULL; - - // 32 byte aligned buffers (with +8 or +16). - int16_t fft_buf[PART_LEN4 + 2 + 16]; // +2 to make a loop safe. - int32_t echoEst32_buf[PART_LEN1 + 8]; - int32_t dfw_buf[PART_LEN2 + 8]; - int32_t efw_buf[PART_LEN2 + 8]; - - int16_t* fft = (int16_t*)(((uint32_t)fft_buf + 31) & ~ 31); - int32_t* echoEst32 = (int32_t*)(((uint32_t)echoEst32_buf + 31) & ~ 31); - ComplexInt16* dfw = (ComplexInt16*)(((uint32_t)dfw_buf + 31) & ~31); - ComplexInt16* efw = (ComplexInt16*)(((uint32_t)efw_buf + 31) & ~31); - - int16_t hnl[PART_LEN1]; - int16_t numPosCoef = 0; - int delay; - int16_t tmp16no1; - int16_t tmp16no2; - int16_t mu; - int16_t supGain; - int16_t zeros32, zeros16; - int16_t zerosDBufNoisy, zerosDBufClean, zerosXBuf; - int far_q; - int16_t resolutionDiff, qDomainDiff, dfa_clean_q_domain_diff; - - const int kMinPrefBand = 4; - const int kMaxPrefBand = 24; - int32_t avgHnl32 = 0; - - int32_t temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; - int16_t* ptr; - int16_t* ptr1; - int16_t* er_ptr; - int16_t* dr_ptr; - - ptr = &hnl[0]; - ptr1 = &hnl[0]; - er_ptr = &efw[0].real; - dr_ptr = &dfw[0].real; - - // Determine startup state. There are three states: - // (0) the first CONV_LEN blocks - // (1) another CONV_LEN blocks - // (2) the rest - - if (aecm->startupState < 2) { - aecm->startupState = (aecm->totCount >= CONV_LEN) + - (aecm->totCount >= CONV_LEN2); - } - // END: Determine startup state - - // Buffer near and far end signals - memcpy(aecm->xBuf + PART_LEN, farend, sizeof(int16_t) * PART_LEN); - memcpy(aecm->dBufNoisy + PART_LEN, - nearendNoisy, - sizeof(int16_t) * PART_LEN); - if (nearendClean != NULL) { - memcpy(aecm->dBufClean + PART_LEN, - nearendClean, - sizeof(int16_t) * PART_LEN); - } - - // Transform far end signal from time domain to frequency domain. - far_q = TimeToFrequencyDomain(aecm, - aecm->xBuf, - dfw, - xfa, - &xfaSum); - - // Transform noisy near end signal from time domain to frequency domain. - zerosDBufNoisy = TimeToFrequencyDomain(aecm, - aecm->dBufNoisy, - dfw, - dfaNoisy, - &dfaNoisySum); - aecm->dfaNoisyQDomainOld = aecm->dfaNoisyQDomain; - aecm->dfaNoisyQDomain = (int16_t)zerosDBufNoisy; - - if (nearendClean == NULL) { - ptrDfaClean = dfaNoisy; - aecm->dfaCleanQDomainOld = aecm->dfaNoisyQDomainOld; - aecm->dfaCleanQDomain = aecm->dfaNoisyQDomain; - dfaCleanSum = dfaNoisySum; - } else { - // Transform clean near end signal from time domain to frequency domain. - zerosDBufClean = TimeToFrequencyDomain(aecm, - aecm->dBufClean, - dfw, - dfaClean, - &dfaCleanSum); - aecm->dfaCleanQDomainOld = aecm->dfaCleanQDomain; - aecm->dfaCleanQDomain = (int16_t)zerosDBufClean; - } - - // Get the delay - // Save far-end history and estimate delay - WebRtcAecm_UpdateFarHistory(aecm, xfa, far_q); - - if (WebRtc_AddFarSpectrumFix(aecm->delay_estimator_farend, xfa, PART_LEN1, - far_q) == -1) { - return -1; - } - delay = WebRtc_DelayEstimatorProcessFix(aecm->delay_estimator, - dfaNoisy, - PART_LEN1, - zerosDBufNoisy); - if (delay == -1) { - return -1; - } - else if (delay == -2) { - // If the delay is unknown, we assume zero. - // NOTE: this will have to be adjusted if we ever add lookahead. - delay = 0; - } - - if (aecm->fixedDelay >= 0) { - // Use fixed delay - delay = aecm->fixedDelay; - } - - // Get aligned far end spectrum - far_spectrum_ptr = WebRtcAecm_AlignedFarend(aecm, &far_q, delay); - zerosXBuf = (int16_t) far_q; - - if (far_spectrum_ptr == NULL) { - return -1; - } - - // Calculate log(energy) and update energy threshold levels - WebRtcAecm_CalcEnergies(aecm, - far_spectrum_ptr, - zerosXBuf, - dfaNoisySum, - echoEst32); - // Calculate stepsize - mu = WebRtcAecm_CalcStepSize(aecm); - - // Update counters - aecm->totCount++; - - // This is the channel estimation algorithm. - // It is base on NLMS but has a variable step length, - // which was calculated above. - WebRtcAecm_UpdateChannel(aecm, - far_spectrum_ptr, - zerosXBuf, - dfaNoisy, - mu, - echoEst32); - - supGain = WebRtcAecm_CalcSuppressionGain(aecm); - - // Calculate Wiener filter hnl[] - for (i = 0; i < PART_LEN1; i++) { - // Far end signal through channel estimate in Q8 - // How much can we shift right to preserve resolution - tmp32no1 = echoEst32[i] - aecm->echoFilt[i]; - aecm->echoFilt[i] += (tmp32no1 * 50) >> 8; - - zeros32 = WebRtcSpl_NormW32(aecm->echoFilt[i]) + 1; - zeros16 = WebRtcSpl_NormW16(supGain) + 1; - if (zeros32 + zeros16 > 16) { - // Multiplication is safe - // Result in - // Q(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN+aecm->xfaQDomainBuf[diff]) - echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], - (uint16_t)supGain); - resolutionDiff = 14 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; - resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); - } else { - tmp16no1 = 17 - zeros32 - zeros16; - resolutionDiff = 14 + tmp16no1 - RESOLUTION_CHANNEL16 - - RESOLUTION_SUPGAIN; - resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); - if (zeros32 > tmp16no1) { - echoEst32Gained = WEBRTC_SPL_UMUL_32_16( - (uint32_t)aecm->echoFilt[i], - supGain >> tmp16no1); - } else { - // Result in Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN-16) - echoEst32Gained = (aecm->echoFilt[i] >> tmp16no1) * supGain; - } - } - - zeros16 = WebRtcSpl_NormW16(aecm->nearFilt[i]); - assert(zeros16 >= 0); // |zeros16| is a norm, hence non-negative. - dfa_clean_q_domain_diff = aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld; - if (zeros16 < dfa_clean_q_domain_diff && aecm->nearFilt[i]) { - tmp16no1 = aecm->nearFilt[i] << zeros16; - qDomainDiff = zeros16 - dfa_clean_q_domain_diff; - tmp16no2 = ptrDfaClean[i] >> -qDomainDiff; - } else { - tmp16no1 = dfa_clean_q_domain_diff < 0 - ? aecm->nearFilt[i] >> -dfa_clean_q_domain_diff - : aecm->nearFilt[i] << dfa_clean_q_domain_diff; - qDomainDiff = 0; - tmp16no2 = ptrDfaClean[i]; - } - - tmp32no1 = (int32_t)(tmp16no2 - tmp16no1); - tmp16no2 = (int16_t)(tmp32no1 >> 4); - tmp16no2 += tmp16no1; - zeros16 = WebRtcSpl_NormW16(tmp16no2); - if ((tmp16no2) & (-qDomainDiff > zeros16)) { - aecm->nearFilt[i] = WEBRTC_SPL_WORD16_MAX; - } else { - aecm->nearFilt[i] = qDomainDiff < 0 ? tmp16no2 << -qDomainDiff - : tmp16no2 >> qDomainDiff; - } - - // Wiener filter coefficients, resulting hnl in Q14 - if (echoEst32Gained == 0) { - hnl[i] = ONE_Q14; - numPosCoef++; - } else if (aecm->nearFilt[i] == 0) { - hnl[i] = 0; - } else { - // Multiply the suppression gain - // Rounding - echoEst32Gained += (uint32_t)(aecm->nearFilt[i] >> 1); - tmpU32 = WebRtcSpl_DivU32U16(echoEst32Gained, - (uint16_t)aecm->nearFilt[i]); - - // Current resolution is - // Q-(RESOLUTION_CHANNEL + RESOLUTION_SUPGAIN - // - max(0, 17 - zeros16 - zeros32)) - // Make sure we are in Q14 - tmp32no1 = (int32_t)WEBRTC_SPL_SHIFT_W32(tmpU32, resolutionDiff); - if (tmp32no1 > ONE_Q14) { - hnl[i] = 0; - } else if (tmp32no1 < 0) { - hnl[i] = ONE_Q14; - numPosCoef++; - } else { - // 1-echoEst/dfa - hnl[i] = ONE_Q14 - (int16_t)tmp32no1; - if (hnl[i] <= 0) { - hnl[i] = 0; - } else { - numPosCoef++; - } - } - } - } - - // Only in wideband. Prevent the gain in upper band from being larger than - // in lower band. - if (aecm->mult == 2) { - // TODO(bjornv): Investigate if the scaling of hnl[i] below can cause - // speech distortion in double-talk. - for (i = 0; i < (PART_LEN1 >> 3); i++) { - __asm __volatile ( - "lh %[temp1], 0(%[ptr1]) \n\t" - "lh %[temp2], 2(%[ptr1]) \n\t" - "lh %[temp3], 4(%[ptr1]) \n\t" - "lh %[temp4], 6(%[ptr1]) \n\t" - "lh %[temp5], 8(%[ptr1]) \n\t" - "lh %[temp6], 10(%[ptr1]) \n\t" - "lh %[temp7], 12(%[ptr1]) \n\t" - "lh %[temp8], 14(%[ptr1]) \n\t" - "mul %[temp1], %[temp1], %[temp1] \n\t" - "mul %[temp2], %[temp2], %[temp2] \n\t" - "mul %[temp3], %[temp3], %[temp3] \n\t" - "mul %[temp4], %[temp4], %[temp4] \n\t" - "mul %[temp5], %[temp5], %[temp5] \n\t" - "mul %[temp6], %[temp6], %[temp6] \n\t" - "mul %[temp7], %[temp7], %[temp7] \n\t" - "mul %[temp8], %[temp8], %[temp8] \n\t" - "sra %[temp1], %[temp1], 14 \n\t" - "sra %[temp2], %[temp2], 14 \n\t" - "sra %[temp3], %[temp3], 14 \n\t" - "sra %[temp4], %[temp4], 14 \n\t" - "sra %[temp5], %[temp5], 14 \n\t" - "sra %[temp6], %[temp6], 14 \n\t" - "sra %[temp7], %[temp7], 14 \n\t" - "sra %[temp8], %[temp8], 14 \n\t" - "sh %[temp1], 0(%[ptr1]) \n\t" - "sh %[temp2], 2(%[ptr1]) \n\t" - "sh %[temp3], 4(%[ptr1]) \n\t" - "sh %[temp4], 6(%[ptr1]) \n\t" - "sh %[temp5], 8(%[ptr1]) \n\t" - "sh %[temp6], 10(%[ptr1]) \n\t" - "sh %[temp7], 12(%[ptr1]) \n\t" - "sh %[temp8], 14(%[ptr1]) \n\t" - "addiu %[ptr1], %[ptr1], 16 \n\t" - : [temp1] "=&r" (temp1), [temp2] "=&r" (temp2), [temp3] "=&r" (temp3), - [temp4] "=&r" (temp4), [temp5] "=&r" (temp5), [temp6] "=&r" (temp6), - [temp7] "=&r" (temp7), [temp8] "=&r" (temp8), [ptr1] "+r" (ptr1) - : - : "memory", "hi", "lo" - ); - } - for(i = 0; i < (PART_LEN1 & 7); i++) { - __asm __volatile ( - "lh %[temp1], 0(%[ptr1]) \n\t" - "mul %[temp1], %[temp1], %[temp1] \n\t" - "sra %[temp1], %[temp1], 14 \n\t" - "sh %[temp1], 0(%[ptr1]) \n\t" - "addiu %[ptr1], %[ptr1], 2 \n\t" - : [temp1] "=&r" (temp1), [ptr1] "+r" (ptr1) - : - : "memory", "hi", "lo" - ); - } - - for (i = kMinPrefBand; i <= kMaxPrefBand; i++) { - avgHnl32 += (int32_t)hnl[i]; - } - - assert(kMaxPrefBand - kMinPrefBand + 1 > 0); - avgHnl32 /= (kMaxPrefBand - kMinPrefBand + 1); - - for (i = kMaxPrefBand; i < PART_LEN1; i++) { - if (hnl[i] > (int16_t)avgHnl32) { - hnl[i] = (int16_t)avgHnl32; - } - } - } - - // Calculate NLP gain, result is in Q14 - if (aecm->nlpFlag) { - if (numPosCoef < 3) { - for (i = 0; i < PART_LEN1; i++) { - efw[i].real = 0; - efw[i].imag = 0; - hnl[i] = 0; - } - } else { - for (i = 0; i < PART_LEN1; i++) { -#if defined(MIPS_DSP_R1_LE) - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "lh %[temp1], 0(%[ptr]) \n\t" - "lh %[temp2], 0(%[dr_ptr]) \n\t" - "slti %[temp4], %[temp1], 0x4001 \n\t" - "beqz %[temp4], 3f \n\t" - " lh %[temp3], 2(%[dr_ptr]) \n\t" - "slti %[temp5], %[temp1], 3277 \n\t" - "bnez %[temp5], 2f \n\t" - " addiu %[dr_ptr], %[dr_ptr], 4 \n\t" - "mul %[temp2], %[temp2], %[temp1] \n\t" - "mul %[temp3], %[temp3], %[temp1] \n\t" - "shra_r.w %[temp2], %[temp2], 14 \n\t" - "shra_r.w %[temp3], %[temp3], 14 \n\t" - "b 4f \n\t" - " nop \n\t" - "2: \n\t" - "addu %[temp1], $zero, $zero \n\t" - "addu %[temp2], $zero, $zero \n\t" - "addu %[temp3], $zero, $zero \n\t" - "b 1f \n\t" - " nop \n\t" - "3: \n\t" - "addiu %[temp1], $0, 0x4000 \n\t" - "1: \n\t" - "sh %[temp1], 0(%[ptr]) \n\t" - "4: \n\t" - "sh %[temp2], 0(%[er_ptr]) \n\t" - "sh %[temp3], 2(%[er_ptr]) \n\t" - "addiu %[ptr], %[ptr], 2 \n\t" - "addiu %[er_ptr], %[er_ptr], 4 \n\t" - ".set pop \n\t" - : [temp1] "=&r" (temp1), [temp2] "=&r" (temp2), [temp3] "=&r" (temp3), - [temp4] "=&r" (temp4), [temp5] "=&r" (temp5), [ptr] "+r" (ptr), - [er_ptr] "+r" (er_ptr), [dr_ptr] "+r" (dr_ptr) - : - : "memory", "hi", "lo" - ); -#else - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "lh %[temp1], 0(%[ptr]) \n\t" - "lh %[temp2], 0(%[dr_ptr]) \n\t" - "slti %[temp4], %[temp1], 0x4001 \n\t" - "beqz %[temp4], 3f \n\t" - " lh %[temp3], 2(%[dr_ptr]) \n\t" - "slti %[temp5], %[temp1], 3277 \n\t" - "bnez %[temp5], 2f \n\t" - " addiu %[dr_ptr], %[dr_ptr], 4 \n\t" - "mul %[temp2], %[temp2], %[temp1] \n\t" - "mul %[temp3], %[temp3], %[temp1] \n\t" - "addiu %[temp2], %[temp2], 0x2000 \n\t" - "addiu %[temp3], %[temp3], 0x2000 \n\t" - "sra %[temp2], %[temp2], 14 \n\t" - "sra %[temp3], %[temp3], 14 \n\t" - "b 4f \n\t" - " nop \n\t" - "2: \n\t" - "addu %[temp1], $zero, $zero \n\t" - "addu %[temp2], $zero, $zero \n\t" - "addu %[temp3], $zero, $zero \n\t" - "b 1f \n\t" - " nop \n\t" - "3: \n\t" - "addiu %[temp1], $0, 0x4000 \n\t" - "1: \n\t" - "sh %[temp1], 0(%[ptr]) \n\t" - "4: \n\t" - "sh %[temp2], 0(%[er_ptr]) \n\t" - "sh %[temp3], 2(%[er_ptr]) \n\t" - "addiu %[ptr], %[ptr], 2 \n\t" - "addiu %[er_ptr], %[er_ptr], 4 \n\t" - ".set pop \n\t" - : [temp1] "=&r" (temp1), [temp2] "=&r" (temp2), [temp3] "=&r" (temp3), - [temp4] "=&r" (temp4), [temp5] "=&r" (temp5), [ptr] "+r" (ptr), - [er_ptr] "+r" (er_ptr), [dr_ptr] "+r" (dr_ptr) - : - : "memory", "hi", "lo" - ); -#endif - } - } - } - else { - // multiply with Wiener coefficients - for (i = 0; i < PART_LEN1; i++) { - efw[i].real = (int16_t) - (WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real, - hnl[i], - 14)); - efw[i].imag = (int16_t) - (WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag, - hnl[i], - 14)); - } - } - - if (aecm->cngMode == AecmTrue) { - ComfortNoise(aecm, ptrDfaClean, efw, hnl); - } - - InverseFFTAndWindow(aecm, fft, efw, output, nearendClean); - - return 0; -} - -// Generate comfort noise and add to output signal. -static void ComfortNoise(AecmCore* aecm, - const uint16_t* dfa, - ComplexInt16* out, - const int16_t* lambda) { - int16_t i; - int16_t tmp16, tmp161, tmp162, tmp163, nrsh1, nrsh2; - int32_t tmp32, tmp321, tnoise, tnoise1; - int32_t tmp322, tmp323, *tmp1; - int16_t* dfap; - int16_t* lambdap; - const int32_t c2049 = 2049; - const int32_t c359 = 359; - const int32_t c114 = ONE_Q14; - - int16_t randW16[PART_LEN]; - int16_t uReal[PART_LEN1]; - int16_t uImag[PART_LEN1]; - int32_t outLShift32; - - int16_t shiftFromNearToNoise = kNoiseEstQDomain - aecm->dfaCleanQDomain; - int16_t minTrackShift = 9; - - assert(shiftFromNearToNoise >= 0); - assert(shiftFromNearToNoise < 16); - - if (aecm->noiseEstCtr < 100) { - // Track the minimum more quickly initially. - aecm->noiseEstCtr++; - minTrackShift = 6; - } - - // Generate a uniform random array on [0 2^15-1]. - WebRtcSpl_RandUArray(randW16, PART_LEN, &aecm->seed); - int16_t* randW16p = (int16_t*)randW16; -#if defined (MIPS_DSP_R1_LE) - int16_t* kCosTablep = (int16_t*)WebRtcAecm_kCosTable; - int16_t* kSinTablep = (int16_t*)WebRtcAecm_kSinTable; -#endif // #if defined(MIPS_DSP_R1_LE) - tmp1 = (int32_t*)aecm->noiseEst + 1; - dfap = (int16_t*)dfa + 1; - lambdap = (int16_t*)lambda + 1; - // Estimate noise power. - for (i = 1; i < PART_LEN1; i+=2) { - // Shift to the noise domain. - __asm __volatile ( - "lh %[tmp32], 0(%[dfap]) \n\t" - "lw %[tnoise], 0(%[tmp1]) \n\t" - "sllv %[outLShift32], %[tmp32], %[shiftFromNearToNoise] \n\t" - : [tmp32] "=&r" (tmp32), [outLShift32] "=r" (outLShift32), - [tnoise] "=&r" (tnoise) - : [tmp1] "r" (tmp1), [dfap] "r" (dfap), - [shiftFromNearToNoise] "r" (shiftFromNearToNoise) - : "memory" - ); - - if (outLShift32 < tnoise) { - // Reset "too low" counter - aecm->noiseEstTooLowCtr[i] = 0; - // Track the minimum. - if (tnoise < (1 << minTrackShift)) { - // For small values, decrease noiseEst[i] every - // |kNoiseEstIncCount| block. The regular approach below can not - // go further down due to truncation. - aecm->noiseEstTooHighCtr[i]++; - if (aecm->noiseEstTooHighCtr[i] >= kNoiseEstIncCount) { - tnoise--; - aecm->noiseEstTooHighCtr[i] = 0; // Reset the counter - } - } else { - __asm __volatile ( - "subu %[tmp32], %[tnoise], %[outLShift32] \n\t" - "srav %[tmp32], %[tmp32], %[minTrackShift] \n\t" - "subu %[tnoise], %[tnoise], %[tmp32] \n\t" - : [tmp32] "=&r" (tmp32), [tnoise] "+r" (tnoise) - : [outLShift32] "r" (outLShift32), [minTrackShift] "r" (minTrackShift) - ); - } - } else { - // Reset "too high" counter - aecm->noiseEstTooHighCtr[i] = 0; - // Ramp slowly upwards until we hit the minimum again. - if ((tnoise >> 19) <= 0) { - if ((tnoise >> 11) > 0) { - // Large enough for relative increase - __asm __volatile ( - "mul %[tnoise], %[tnoise], %[c2049] \n\t" - "sra %[tnoise], %[tnoise], 11 \n\t" - : [tnoise] "+r" (tnoise) - : [c2049] "r" (c2049) - : "hi", "lo" - ); - } else { - // Make incremental increases based on size every - // |kNoiseEstIncCount| block - aecm->noiseEstTooLowCtr[i]++; - if (aecm->noiseEstTooLowCtr[i] >= kNoiseEstIncCount) { - __asm __volatile ( - "sra %[tmp32], %[tnoise], 9 \n\t" - "addi %[tnoise], %[tnoise], 1 \n\t" - "addu %[tnoise], %[tnoise], %[tmp32] \n\t" - : [tnoise] "+r" (tnoise), [tmp32] "=&r" (tmp32) - : - ); - aecm->noiseEstTooLowCtr[i] = 0; // Reset counter - } - } - } else { - // Avoid overflow. - // Multiplication with 2049 will cause wrap around. Scale - // down first and then multiply - __asm __volatile ( - "sra %[tnoise], %[tnoise], 11 \n\t" - "mul %[tnoise], %[tnoise], %[c2049] \n\t" - : [tnoise] "+r" (tnoise) - : [c2049] "r" (c2049) - : "hi", "lo" - ); - } - } - - // Shift to the noise domain. - __asm __volatile ( - "lh %[tmp32], 2(%[dfap]) \n\t" - "lw %[tnoise1], 4(%[tmp1]) \n\t" - "addiu %[dfap], %[dfap], 4 \n\t" - "sllv %[outLShift32], %[tmp32], %[shiftFromNearToNoise] \n\t" - : [tmp32] "=&r" (tmp32), [dfap] "+r" (dfap), - [outLShift32] "=r" (outLShift32), [tnoise1] "=&r" (tnoise1) - : [tmp1] "r" (tmp1), [shiftFromNearToNoise] "r" (shiftFromNearToNoise) - : "memory" - ); - - if (outLShift32 < tnoise1) { - // Reset "too low" counter - aecm->noiseEstTooLowCtr[i + 1] = 0; - // Track the minimum. - if (tnoise1 < (1 << minTrackShift)) { - // For small values, decrease noiseEst[i] every - // |kNoiseEstIncCount| block. The regular approach below can not - // go further down due to truncation. - aecm->noiseEstTooHighCtr[i + 1]++; - if (aecm->noiseEstTooHighCtr[i + 1] >= kNoiseEstIncCount) { - tnoise1--; - aecm->noiseEstTooHighCtr[i + 1] = 0; // Reset the counter - } - } else { - __asm __volatile ( - "subu %[tmp32], %[tnoise1], %[outLShift32] \n\t" - "srav %[tmp32], %[tmp32], %[minTrackShift] \n\t" - "subu %[tnoise1], %[tnoise1], %[tmp32] \n\t" - : [tmp32] "=&r" (tmp32), [tnoise1] "+r" (tnoise1) - : [outLShift32] "r" (outLShift32), [minTrackShift] "r" (minTrackShift) - ); - } - } else { - // Reset "too high" counter - aecm->noiseEstTooHighCtr[i + 1] = 0; - // Ramp slowly upwards until we hit the minimum again. - if ((tnoise1 >> 19) <= 0) { - if ((tnoise1 >> 11) > 0) { - // Large enough for relative increase - __asm __volatile ( - "mul %[tnoise1], %[tnoise1], %[c2049] \n\t" - "sra %[tnoise1], %[tnoise1], 11 \n\t" - : [tnoise1] "+r" (tnoise1) - : [c2049] "r" (c2049) - : "hi", "lo" - ); - } else { - // Make incremental increases based on size every - // |kNoiseEstIncCount| block - aecm->noiseEstTooLowCtr[i + 1]++; - if (aecm->noiseEstTooLowCtr[i + 1] >= kNoiseEstIncCount) { - __asm __volatile ( - "sra %[tmp32], %[tnoise1], 9 \n\t" - "addi %[tnoise1], %[tnoise1], 1 \n\t" - "addu %[tnoise1], %[tnoise1], %[tmp32] \n\t" - : [tnoise1] "+r" (tnoise1), [tmp32] "=&r" (tmp32) - : - ); - aecm->noiseEstTooLowCtr[i + 1] = 0; // Reset counter - } - } - } else { - // Avoid overflow. - // Multiplication with 2049 will cause wrap around. Scale - // down first and then multiply - __asm __volatile ( - "sra %[tnoise1], %[tnoise1], 11 \n\t" - "mul %[tnoise1], %[tnoise1], %[c2049] \n\t" - : [tnoise1] "+r" (tnoise1) - : [c2049] "r" (c2049) - : "hi", "lo" - ); - } - } - - __asm __volatile ( - "lh %[tmp16], 0(%[lambdap]) \n\t" - "lh %[tmp161], 2(%[lambdap]) \n\t" - "sw %[tnoise], 0(%[tmp1]) \n\t" - "sw %[tnoise1], 4(%[tmp1]) \n\t" - "subu %[tmp16], %[c114], %[tmp16] \n\t" - "subu %[tmp161], %[c114], %[tmp161] \n\t" - "srav %[tmp32], %[tnoise], %[shiftFromNearToNoise] \n\t" - "srav %[tmp321], %[tnoise1], %[shiftFromNearToNoise] \n\t" - "addiu %[lambdap], %[lambdap], 4 \n\t" - "addiu %[tmp1], %[tmp1], 8 \n\t" - : [tmp16] "=&r" (tmp16), [tmp161] "=&r" (tmp161), [tmp1] "+r" (tmp1), - [tmp32] "=&r" (tmp32), [tmp321] "=&r" (tmp321), [lambdap] "+r" (lambdap) - : [tnoise] "r" (tnoise), [tnoise1] "r" (tnoise1), [c114] "r" (c114), - [shiftFromNearToNoise] "r" (shiftFromNearToNoise) - : "memory" - ); - - if (tmp32 > 32767) { - tmp32 = 32767; - aecm->noiseEst[i] = tmp32 << shiftFromNearToNoise; - } - if (tmp321 > 32767) { - tmp321 = 32767; - aecm->noiseEst[i+1] = tmp321 << shiftFromNearToNoise; - } - - __asm __volatile ( - "mul %[tmp32], %[tmp32], %[tmp16] \n\t" - "mul %[tmp321], %[tmp321], %[tmp161] \n\t" - "sra %[nrsh1], %[tmp32], 14 \n\t" - "sra %[nrsh2], %[tmp321], 14 \n\t" - : [nrsh1] "=&r" (nrsh1), [nrsh2] "=r" (nrsh2) - : [tmp16] "r" (tmp16), [tmp161] "r" (tmp161), [tmp32] "r" (tmp32), - [tmp321] "r" (tmp321) - : "memory", "hi", "lo" - ); - - __asm __volatile ( - "lh %[tmp32], 0(%[randW16p]) \n\t" - "lh %[tmp321], 2(%[randW16p]) \n\t" - "addiu %[randW16p], %[randW16p], 4 \n\t" - "mul %[tmp32], %[tmp32], %[c359] \n\t" - "mul %[tmp321], %[tmp321], %[c359] \n\t" - "sra %[tmp16], %[tmp32], 15 \n\t" - "sra %[tmp161], %[tmp321], 15 \n\t" - : [randW16p] "+r" (randW16p), [tmp32] "=&r" (tmp32), - [tmp16] "=r" (tmp16), [tmp161] "=r" (tmp161), [tmp321] "=&r" (tmp321) - : [c359] "r" (c359) - : "memory", "hi", "lo" - ); - -#if !defined(MIPS_DSP_R1_LE) - tmp32 = WebRtcAecm_kCosTable[tmp16]; - tmp321 = WebRtcAecm_kSinTable[tmp16]; - tmp322 = WebRtcAecm_kCosTable[tmp161]; - tmp323 = WebRtcAecm_kSinTable[tmp161]; -#else - __asm __volatile ( - "sll %[tmp16], %[tmp16], 1 \n\t" - "sll %[tmp161], %[tmp161], 1 \n\t" - "lhx %[tmp32], %[tmp16](%[kCosTablep]) \n\t" - "lhx %[tmp321], %[tmp16](%[kSinTablep]) \n\t" - "lhx %[tmp322], %[tmp161](%[kCosTablep]) \n\t" - "lhx %[tmp323], %[tmp161](%[kSinTablep]) \n\t" - : [tmp32] "=&r" (tmp32), [tmp321] "=&r" (tmp321), - [tmp322] "=&r" (tmp322), [tmp323] "=&r" (tmp323) - : [kCosTablep] "r" (kCosTablep), [tmp16] "r" (tmp16), - [tmp161] "r" (tmp161), [kSinTablep] "r" (kSinTablep) - : "memory" - ); -#endif - __asm __volatile ( - "mul %[tmp32], %[tmp32], %[nrsh1] \n\t" - "negu %[tmp162], %[nrsh1] \n\t" - "mul %[tmp322], %[tmp322], %[nrsh2] \n\t" - "negu %[tmp163], %[nrsh2] \n\t" - "sra %[tmp32], %[tmp32], 13 \n\t" - "mul %[tmp321], %[tmp321], %[tmp162] \n\t" - "sra %[tmp322], %[tmp322], 13 \n\t" - "mul %[tmp323], %[tmp323], %[tmp163] \n\t" - "sra %[tmp321], %[tmp321], 13 \n\t" - "sra %[tmp323], %[tmp323], 13 \n\t" - : [tmp32] "+r" (tmp32), [tmp321] "+r" (tmp321), [tmp162] "=&r" (tmp162), - [tmp322] "+r" (tmp322), [tmp323] "+r" (tmp323), [tmp163] "=&r" (tmp163) - : [nrsh1] "r" (nrsh1), [nrsh2] "r" (nrsh2) - : "hi", "lo" - ); - // Tables are in Q13. - uReal[i] = (int16_t)tmp32; - uImag[i] = (int16_t)tmp321; - uReal[i + 1] = (int16_t)tmp322; - uImag[i + 1] = (int16_t)tmp323; - } - - int32_t tt, sgn; - tt = out[0].real; - sgn = ((int)tt) >> 31; - out[0].real = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); - tt = out[0].imag; - sgn = ((int)tt) >> 31; - out[0].imag = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); - for (i = 1; i < PART_LEN; i++) { - tt = out[i].real + uReal[i]; - sgn = ((int)tt) >> 31; - out[i].real = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); - tt = out[i].imag + uImag[i]; - sgn = ((int)tt) >> 31; - out[i].imag = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); - } - tt = out[PART_LEN].real + uReal[PART_LEN]; - sgn = ((int)tt) >> 31; - out[PART_LEN].real = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); - tt = out[PART_LEN].imag; - sgn = ((int)tt) >> 31; - out[PART_LEN].imag = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); -} - diff --git a/webrtc/modules/audio_processing/aecm/aecm_core_mips.cc b/webrtc/modules/audio_processing/aecm/aecm_core_mips.cc new file mode 100644 index 0000000..f2f43e1 --- /dev/null +++ b/webrtc/modules/audio_processing/aecm/aecm_core_mips.cc @@ -0,0 +1,1656 @@ +/* + * Copyright (c) 2013 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 "modules/audio_processing/aecm/aecm_core.h" +#include "modules/audio_processing/aecm/echo_control_mobile.h" +#include "modules/audio_processing/utility/delay_estimator_wrapper.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { + +namespace { + +static const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END = { + 0, 399, 798, 1196, 1594, 1990, 2386, 2780, 3172, 3562, 3951, + 4337, 4720, 5101, 5478, 5853, 6224, 6591, 6954, 7313, 7668, 8019, + 8364, 8705, 9040, 9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514, + 11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553, 13773, 13985, 14189, + 14384, 14571, 14749, 14918, 15079, 15231, 15373, 15506, 15631, 15746, 15851, + 15947, 16034, 16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384}; + +static const int16_t kNoiseEstQDomain = 15; +static const int16_t kNoiseEstIncCount = 5; + +static int16_t coefTable[] = { + 0, 4, 256, 260, 128, 132, 384, 388, 64, 68, 320, 324, 192, 196, 448, + 452, 32, 36, 288, 292, 160, 164, 416, 420, 96, 100, 352, 356, 224, 228, + 480, 484, 16, 20, 272, 276, 144, 148, 400, 404, 80, 84, 336, 340, 208, + 212, 464, 468, 48, 52, 304, 308, 176, 180, 432, 436, 112, 116, 368, 372, + 240, 244, 496, 500, 8, 12, 264, 268, 136, 140, 392, 396, 72, 76, 328, + 332, 200, 204, 456, 460, 40, 44, 296, 300, 168, 172, 424, 428, 104, 108, + 360, 364, 232, 236, 488, 492, 24, 28, 280, 284, 152, 156, 408, 412, 88, + 92, 344, 348, 216, 220, 472, 476, 56, 60, 312, 316, 184, 188, 440, 444, + 120, 124, 376, 380, 248, 252, 504, 508}; + +static int16_t coefTable_ifft[] = { + 0, 512, 256, 508, 128, 252, 384, 380, 64, 124, 320, 444, 192, 188, 448, + 316, 32, 60, 288, 476, 160, 220, 416, 348, 96, 92, 352, 412, 224, 156, + 480, 284, 16, 28, 272, 492, 144, 236, 400, 364, 80, 108, 336, 428, 208, + 172, 464, 300, 48, 44, 304, 460, 176, 204, 432, 332, 112, 76, 368, 396, + 240, 140, 496, 268, 8, 12, 264, 500, 136, 244, 392, 372, 72, 116, 328, + 436, 200, 180, 456, 308, 40, 52, 296, 468, 168, 212, 424, 340, 104, 84, + 360, 404, 232, 148, 488, 276, 24, 20, 280, 484, 152, 228, 408, 356, 88, + 100, 344, 420, 216, 164, 472, 292, 56, 36, 312, 452, 184, 196, 440, 324, + 120, 68, 376, 388, 248, 132, 504, 260}; + +} // namespace + +static void ComfortNoise(AecmCore* aecm, + const uint16_t* dfa, + ComplexInt16* out, + const int16_t* lambda); + +static void WindowAndFFT(AecmCore* aecm, + int16_t* fft, + const int16_t* time_signal, + ComplexInt16* freq_signal, + int time_signal_scaling) { + int i, j; + int32_t tmp1, tmp2, tmp3, tmp4; + int16_t* pfrfi; + ComplexInt16* pfreq_signal; + int16_t f_coef, s_coef; + int32_t load_ptr, store_ptr1, store_ptr2, shift, shift1; + int32_t hann, hann1, coefs; + + memset(fft, 0, sizeof(int16_t) * PART_LEN4); + + // FFT of signal + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[shift], %[time_signal_scaling], -14 \n\t" + "addiu %[i], $zero, 64 \n\t" + "addiu %[load_ptr], %[time_signal], 0 \n\t" + "addiu %[hann], %[hanning], 0 \n\t" + "addiu %[hann1], %[hanning], 128 \n\t" + "addiu %[coefs], %[coefTable], 0 \n\t" + "bltz %[shift], 2f \n\t" + " negu %[shift1], %[shift] \n\t" + "1: " + "\n\t" + "lh %[tmp1], 0(%[load_ptr]) \n\t" + "lh %[tmp2], 0(%[hann]) \n\t" + "lh %[tmp3], 128(%[load_ptr]) \n\t" + "lh %[tmp4], 0(%[hann1]) \n\t" + "addiu %[i], %[i], -1 \n\t" + "mul %[tmp1], %[tmp1], %[tmp2] \n\t" + "mul %[tmp3], %[tmp3], %[tmp4] \n\t" + "lh %[f_coef], 0(%[coefs]) \n\t" + "lh %[s_coef], 2(%[coefs]) \n\t" + "addiu %[load_ptr], %[load_ptr], 2 \n\t" + "addiu %[hann], %[hann], 2 \n\t" + "addiu %[hann1], %[hann1], -2 \n\t" + "addu %[store_ptr1], %[fft], %[f_coef] \n\t" + "addu %[store_ptr2], %[fft], %[s_coef] \n\t" + "sllv %[tmp1], %[tmp1], %[shift] \n\t" + "sllv %[tmp3], %[tmp3], %[shift] \n\t" + "sh %[tmp1], 0(%[store_ptr1]) \n\t" + "sh %[tmp3], 0(%[store_ptr2]) \n\t" + "bgtz %[i], 1b \n\t" + " addiu %[coefs], %[coefs], 4 \n\t" + "b 3f \n\t" + " nop \n\t" + "2: " + "\n\t" + "lh %[tmp1], 0(%[load_ptr]) \n\t" + "lh %[tmp2], 0(%[hann]) \n\t" + "lh %[tmp3], 128(%[load_ptr]) \n\t" + "lh %[tmp4], 0(%[hann1]) \n\t" + "addiu %[i], %[i], -1 \n\t" + "mul %[tmp1], %[tmp1], %[tmp2] \n\t" + "mul %[tmp3], %[tmp3], %[tmp4] \n\t" + "lh %[f_coef], 0(%[coefs]) \n\t" + "lh %[s_coef], 2(%[coefs]) \n\t" + "addiu %[load_ptr], %[load_ptr], 2 \n\t" + "addiu %[hann], %[hann], 2 \n\t" + "addiu %[hann1], %[hann1], -2 \n\t" + "addu %[store_ptr1], %[fft], %[f_coef] \n\t" + "addu %[store_ptr2], %[fft], %[s_coef] \n\t" + "srav %[tmp1], %[tmp1], %[shift1] \n\t" + "srav %[tmp3], %[tmp3], %[shift1] \n\t" + "sh %[tmp1], 0(%[store_ptr1]) \n\t" + "sh %[tmp3], 0(%[store_ptr2]) \n\t" + "bgtz %[i], 2b \n\t" + " addiu %[coefs], %[coefs], 4 \n\t" + "3: " + "\n\t" + ".set pop \n\t" + : [load_ptr] "=&r"(load_ptr), [shift] "=&r"(shift), [hann] "=&r"(hann), + [hann1] "=&r"(hann1), [shift1] "=&r"(shift1), [coefs] "=&r"(coefs), + [tmp1] "=&r"(tmp1), [tmp2] "=&r"(tmp2), [tmp3] "=&r"(tmp3), + [tmp4] "=&r"(tmp4), [i] "=&r"(i), [f_coef] "=&r"(f_coef), + [s_coef] "=&r"(s_coef), [store_ptr1] "=&r"(store_ptr1), + [store_ptr2] "=&r"(store_ptr2) + : [time_signal] "r"(time_signal), [coefTable] "r"(coefTable), + [time_signal_scaling] "r"(time_signal_scaling), + [hanning] "r"(WebRtcAecm_kSqrtHanning), [fft] "r"(fft) + : "memory", "hi", "lo"); + + WebRtcSpl_ComplexFFT(fft, PART_LEN_SHIFT, 1); + pfrfi = fft; + pfreq_signal = freq_signal; + + __asm __volatile( + ".set push " + "\n\t" + ".set noreorder " + "\n\t" + "addiu %[j], $zero, 128 " + "\n\t" + "1: " + "\n\t" + "lh %[tmp1], 0(%[pfrfi]) " + "\n\t" + "lh %[tmp2], 2(%[pfrfi]) " + "\n\t" + "lh %[tmp3], 4(%[pfrfi]) " + "\n\t" + "lh %[tmp4], 6(%[pfrfi]) " + "\n\t" + "subu %[tmp2], $zero, %[tmp2] " + "\n\t" + "sh %[tmp1], 0(%[pfreq_signal]) " + "\n\t" + "sh %[tmp2], 2(%[pfreq_signal]) " + "\n\t" + "subu %[tmp4], $zero, %[tmp4] " + "\n\t" + "sh %[tmp3], 4(%[pfreq_signal]) " + "\n\t" + "sh %[tmp4], 6(%[pfreq_signal]) " + "\n\t" + "lh %[tmp1], 8(%[pfrfi]) " + "\n\t" + "lh %[tmp2], 10(%[pfrfi]) " + "\n\t" + "lh %[tmp3], 12(%[pfrfi]) " + "\n\t" + "lh %[tmp4], 14(%[pfrfi]) " + "\n\t" + "addiu %[j], %[j], -8 " + "\n\t" + "subu %[tmp2], $zero, %[tmp2] " + "\n\t" + "sh %[tmp1], 8(%[pfreq_signal]) " + "\n\t" + "sh %[tmp2], 10(%[pfreq_signal]) " + "\n\t" + "subu %[tmp4], $zero, %[tmp4] " + "\n\t" + "sh %[tmp3], 12(%[pfreq_signal]) " + "\n\t" + "sh %[tmp4], 14(%[pfreq_signal]) " + "\n\t" + "addiu %[pfreq_signal], %[pfreq_signal], 16 " + "\n\t" + "bgtz %[j], 1b " + "\n\t" + " addiu %[pfrfi], %[pfrfi], 16 " + "\n\t" + ".set pop " + "\n\t" + : [tmp1] "=&r"(tmp1), [tmp2] "=&r"(tmp2), [tmp3] "=&r"(tmp3), + [j] "=&r"(j), [pfrfi] "+r"(pfrfi), [pfreq_signal] "+r"(pfreq_signal), + [tmp4] "=&r"(tmp4) + : + : "memory"); +} + +static void InverseFFTAndWindow(AecmCore* aecm, + int16_t* fft, + ComplexInt16* efw, + int16_t* output, + const int16_t* nearendClean) { + int i, outCFFT; + int32_t tmp1, tmp2, tmp3, tmp4, tmp_re, tmp_im; + int16_t* pcoefTable_ifft = coefTable_ifft; + int16_t* pfft = fft; + int16_t* ppfft = fft; + ComplexInt16* pefw = efw; + int32_t out_aecm; + int16_t* paecm_buf = aecm->outBuf; + const int16_t* p_kSqrtHanning = WebRtcAecm_kSqrtHanning; + const int16_t* pp_kSqrtHanning = &WebRtcAecm_kSqrtHanning[PART_LEN]; + int16_t* output1 = output; + + __asm __volatile( + ".set push " + "\n\t" + ".set noreorder " + "\n\t" + "addiu %[i], $zero, 64 " + "\n\t" + "1: " + "\n\t" + "lh %[tmp1], 0(%[pcoefTable_ifft]) " + "\n\t" + "lh %[tmp2], 2(%[pcoefTable_ifft]) " + "\n\t" + "lh %[tmp_re], 0(%[pefw]) " + "\n\t" + "lh %[tmp_im], 2(%[pefw]) " + "\n\t" + "addu %[pfft], %[fft], %[tmp2] " + "\n\t" + "sh %[tmp_re], 0(%[pfft]) " + "\n\t" + "sh %[tmp_im], 2(%[pfft]) " + "\n\t" + "addu %[pfft], %[fft], %[tmp1] " + "\n\t" + "sh %[tmp_re], 0(%[pfft]) " + "\n\t" + "subu %[tmp_im], $zero, %[tmp_im] " + "\n\t" + "sh %[tmp_im], 2(%[pfft]) " + "\n\t" + "lh %[tmp1], 4(%[pcoefTable_ifft]) " + "\n\t" + "lh %[tmp2], 6(%[pcoefTable_ifft]) " + "\n\t" + "lh %[tmp_re], 4(%[pefw]) " + "\n\t" + "lh %[tmp_im], 6(%[pefw]) " + "\n\t" + "addu %[pfft], %[fft], %[tmp2] " + "\n\t" + "sh %[tmp_re], 0(%[pfft]) " + "\n\t" + "sh %[tmp_im], 2(%[pfft]) " + "\n\t" + "addu %[pfft], %[fft], %[tmp1] " + "\n\t" + "sh %[tmp_re], 0(%[pfft]) " + "\n\t" + "subu %[tmp_im], $zero, %[tmp_im] " + "\n\t" + "sh %[tmp_im], 2(%[pfft]) " + "\n\t" + "lh %[tmp1], 8(%[pcoefTable_ifft]) " + "\n\t" + "lh %[tmp2], 10(%[pcoefTable_ifft]) " + "\n\t" + "lh %[tmp_re], 8(%[pefw]) " + "\n\t" + "lh %[tmp_im], 10(%[pefw]) " + "\n\t" + "addu %[pfft], %[fft], %[tmp2] " + "\n\t" + "sh %[tmp_re], 0(%[pfft]) " + "\n\t" + "sh %[tmp_im], 2(%[pfft]) " + "\n\t" + "addu %[pfft], %[fft], %[tmp1] " + "\n\t" + "sh %[tmp_re], 0(%[pfft]) " + "\n\t" + "subu %[tmp_im], $zero, %[tmp_im] " + "\n\t" + "sh %[tmp_im], 2(%[pfft]) " + "\n\t" + "lh %[tmp1], 12(%[pcoefTable_ifft]) " + "\n\t" + "lh %[tmp2], 14(%[pcoefTable_ifft]) " + "\n\t" + "lh %[tmp_re], 12(%[pefw]) " + "\n\t" + "lh %[tmp_im], 14(%[pefw]) " + "\n\t" + "addu %[pfft], %[fft], %[tmp2] " + "\n\t" + "sh %[tmp_re], 0(%[pfft]) " + "\n\t" + "sh %[tmp_im], 2(%[pfft]) " + "\n\t" + "addu %[pfft], %[fft], %[tmp1] " + "\n\t" + "sh %[tmp_re], 0(%[pfft]) " + "\n\t" + "subu %[tmp_im], $zero, %[tmp_im] " + "\n\t" + "sh %[tmp_im], 2(%[pfft]) " + "\n\t" + "addiu %[pcoefTable_ifft], %[pcoefTable_ifft], 16 " + "\n\t" + "addiu %[i], %[i], -4 " + "\n\t" + "bgtz %[i], 1b " + "\n\t" + " addiu %[pefw], %[pefw], 16 " + "\n\t" + ".set pop " + "\n\t" + : [tmp1] "=&r"(tmp1), [tmp2] "=&r"(tmp2), [pfft] "+r"(pfft), [i] "=&r"(i), + [tmp_re] "=&r"(tmp_re), [tmp_im] "=&r"(tmp_im), [pefw] "+r"(pefw), + [pcoefTable_ifft] "+r"(pcoefTable_ifft), [fft] "+r"(fft) + : + : "memory"); + + fft[2] = efw[PART_LEN].real; + fft[3] = -efw[PART_LEN].imag; + + outCFFT = WebRtcSpl_ComplexIFFT(fft, PART_LEN_SHIFT, 1); + pfft = fft; + + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "addiu %[i], $zero, 128 \n\t" + "1: \n\t" + "lh %[tmp1], 0(%[ppfft]) \n\t" + "lh %[tmp2], 4(%[ppfft]) \n\t" + "lh %[tmp3], 8(%[ppfft]) \n\t" + "lh %[tmp4], 12(%[ppfft]) \n\t" + "addiu %[i], %[i], -4 \n\t" + "sh %[tmp1], 0(%[pfft]) \n\t" + "sh %[tmp2], 2(%[pfft]) \n\t" + "sh %[tmp3], 4(%[pfft]) \n\t" + "sh %[tmp4], 6(%[pfft]) \n\t" + "addiu %[ppfft], %[ppfft], 16 \n\t" + "bgtz %[i], 1b \n\t" + " addiu %[pfft], %[pfft], 8 \n\t" + ".set pop \n\t" + : [tmp1] "=&r"(tmp1), [tmp2] "=&r"(tmp2), [pfft] "+r"(pfft), [i] "=&r"(i), + [tmp3] "=&r"(tmp3), [tmp4] "=&r"(tmp4), [ppfft] "+r"(ppfft) + : + : "memory"); + + pfft = fft; + out_aecm = (int32_t)(outCFFT - aecm->dfaCleanQDomain); + + __asm __volatile( + ".set push " + "\n\t" + ".set noreorder " + "\n\t" + "addiu %[i], $zero, 64 " + "\n\t" + "11: " + "\n\t" + "lh %[tmp1], 0(%[pfft]) " + "\n\t" + "lh %[tmp2], 0(%[p_kSqrtHanning]) " + "\n\t" + "addiu %[i], %[i], -2 " + "\n\t" + "mul %[tmp1], %[tmp1], %[tmp2] " + "\n\t" + "lh %[tmp3], 2(%[pfft]) " + "\n\t" + "lh %[tmp4], 2(%[p_kSqrtHanning]) " + "\n\t" + "mul %[tmp3], %[tmp3], %[tmp4] " + "\n\t" + "addiu %[tmp1], %[tmp1], 8192 " + "\n\t" + "sra %[tmp1], %[tmp1], 14 " + "\n\t" + "addiu %[tmp3], %[tmp3], 8192 " + "\n\t" + "sra %[tmp3], %[tmp3], 14 " + "\n\t" + "bgez %[out_aecm], 1f " + "\n\t" + " negu %[tmp2], %[out_aecm] " + "\n\t" + "srav %[tmp1], %[tmp1], %[tmp2] " + "\n\t" + "b 2f " + "\n\t" + " srav %[tmp3], %[tmp3], %[tmp2] " + "\n\t" + "1: " + "\n\t" + "sllv %[tmp1], %[tmp1], %[out_aecm] " + "\n\t" + "sllv %[tmp3], %[tmp3], %[out_aecm] " + "\n\t" + "2: " + "\n\t" + "lh %[tmp4], 0(%[paecm_buf]) " + "\n\t" + "lh %[tmp2], 2(%[paecm_buf]) " + "\n\t" + "addu %[tmp3], %[tmp3], %[tmp2] " + "\n\t" + "addu %[tmp1], %[tmp1], %[tmp4] " + "\n\t" +#if defined(MIPS_DSP_R1_LE) + "shll_s.w %[tmp1], %[tmp1], 16 " + "\n\t" + "sra %[tmp1], %[tmp1], 16 " + "\n\t" + "shll_s.w %[tmp3], %[tmp3], 16 " + "\n\t" + "sra %[tmp3], %[tmp3], 16 " + "\n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "sra %[tmp4], %[tmp1], 31 " + "\n\t" + "sra %[tmp2], %[tmp1], 15 " + "\n\t" + "beq %[tmp4], %[tmp2], 3f " + "\n\t" + " ori %[tmp2], $zero, 0x7fff " + "\n\t" + "xor %[tmp1], %[tmp2], %[tmp4] " + "\n\t" + "3: " + "\n\t" + "sra %[tmp2], %[tmp3], 31 " + "\n\t" + "sra %[tmp4], %[tmp3], 15 " + "\n\t" + "beq %[tmp2], %[tmp4], 4f " + "\n\t" + " ori %[tmp4], $zero, 0x7fff " + "\n\t" + "xor %[tmp3], %[tmp4], %[tmp2] " + "\n\t" + "4: " + "\n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sh %[tmp1], 0(%[pfft]) " + "\n\t" + "sh %[tmp1], 0(%[output1]) " + "\n\t" + "sh %[tmp3], 2(%[pfft]) " + "\n\t" + "sh %[tmp3], 2(%[output1]) " + "\n\t" + "lh %[tmp1], 128(%[pfft]) " + "\n\t" + "lh %[tmp2], 0(%[pp_kSqrtHanning]) " + "\n\t" + "mul %[tmp1], %[tmp1], %[tmp2] " + "\n\t" + "lh %[tmp3], 130(%[pfft]) " + "\n\t" + "lh %[tmp4], -2(%[pp_kSqrtHanning]) " + "\n\t" + "mul %[tmp3], %[tmp3], %[tmp4] " + "\n\t" + "sra %[tmp1], %[tmp1], 14 " + "\n\t" + "sra %[tmp3], %[tmp3], 14 " + "\n\t" + "bgez %[out_aecm], 5f " + "\n\t" + " negu %[tmp2], %[out_aecm] " + "\n\t" + "srav %[tmp3], %[tmp3], %[tmp2] " + "\n\t" + "b 6f " + "\n\t" + " srav %[tmp1], %[tmp1], %[tmp2] " + "\n\t" + "5: " + "\n\t" + "sllv %[tmp1], %[tmp1], %[out_aecm] " + "\n\t" + "sllv %[tmp3], %[tmp3], %[out_aecm] " + "\n\t" + "6: " + "\n\t" +#if defined(MIPS_DSP_R1_LE) + "shll_s.w %[tmp1], %[tmp1], 16 " + "\n\t" + "sra %[tmp1], %[tmp1], 16 " + "\n\t" + "shll_s.w %[tmp3], %[tmp3], 16 " + "\n\t" + "sra %[tmp3], %[tmp3], 16 " + "\n\t" +#else // #if defined(MIPS_DSP_R1_LE) + "sra %[tmp4], %[tmp1], 31 " + "\n\t" + "sra %[tmp2], %[tmp1], 15 " + "\n\t" + "beq %[tmp4], %[tmp2], 7f " + "\n\t" + " ori %[tmp2], $zero, 0x7fff " + "\n\t" + "xor %[tmp1], %[tmp2], %[tmp4] " + "\n\t" + "7: " + "\n\t" + "sra %[tmp2], %[tmp3], 31 " + "\n\t" + "sra %[tmp4], %[tmp3], 15 " + "\n\t" + "beq %[tmp2], %[tmp4], 8f " + "\n\t" + " ori %[tmp4], $zero, 0x7fff " + "\n\t" + "xor %[tmp3], %[tmp4], %[tmp2] " + "\n\t" + "8: " + "\n\t" +#endif // #if defined(MIPS_DSP_R1_LE) + "sh %[tmp1], 0(%[paecm_buf]) " + "\n\t" + "sh %[tmp3], 2(%[paecm_buf]) " + "\n\t" + "addiu %[output1], %[output1], 4 " + "\n\t" + "addiu %[paecm_buf], %[paecm_buf], 4 " + "\n\t" + "addiu %[pfft], %[pfft], 4 " + "\n\t" + "addiu %[p_kSqrtHanning], %[p_kSqrtHanning], 4 " + "\n\t" + "bgtz %[i], 11b " + "\n\t" + " addiu %[pp_kSqrtHanning], %[pp_kSqrtHanning], -4 " + "\n\t" + ".set pop " + "\n\t" + : [tmp1] "=&r"(tmp1), [tmp2] "=&r"(tmp2), [pfft] "+r"(pfft), + [output1] "+r"(output1), [tmp3] "=&r"(tmp3), [tmp4] "=&r"(tmp4), + [paecm_buf] "+r"(paecm_buf), [i] "=&r"(i), + [pp_kSqrtHanning] "+r"(pp_kSqrtHanning), + [p_kSqrtHanning] "+r"(p_kSqrtHanning) + : [out_aecm] "r"(out_aecm), + [WebRtcAecm_kSqrtHanning] "r"(WebRtcAecm_kSqrtHanning) + : "hi", "lo", "memory"); + + // Copy the current block to the old position + // (aecm->outBuf is shifted elsewhere) + memcpy(aecm->xBuf, aecm->xBuf + PART_LEN, sizeof(int16_t) * PART_LEN); + memcpy(aecm->dBufNoisy, aecm->dBufNoisy + PART_LEN, + sizeof(int16_t) * PART_LEN); + if (nearendClean != NULL) { + memcpy(aecm->dBufClean, aecm->dBufClean + PART_LEN, + sizeof(int16_t) * PART_LEN); + } +} + +void WebRtcAecm_CalcLinearEnergies_mips(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est, + uint32_t* far_energy, + uint32_t* echo_energy_adapt, + uint32_t* echo_energy_stored) { + int i; + uint32_t par1 = (*far_energy); + uint32_t par2 = (*echo_energy_adapt); + uint32_t par3 = (*echo_energy_stored); + int16_t* ch_stored_p = &(aecm->channelStored[0]); + int16_t* ch_adapt_p = &(aecm->channelAdapt16[0]); + uint16_t* spectrum_p = (uint16_t*)(&(far_spectrum[0])); + int32_t* echo_p = &(echo_est[0]); + int32_t temp0, stored0, echo0, adept0, spectrum0; + int32_t stored1, adept1, spectrum1, echo1, temp1; + + // Get energy for the delayed far end signal and estimated + // echo using both stored and adapted channels. + for (i = 0; i < PART_LEN; i += 4) { + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "lh %[stored0], 0(%[ch_stored_p]) \n\t" + "lhu %[adept0], 0(%[ch_adapt_p]) \n\t" + "lhu %[spectrum0], 0(%[spectrum_p]) \n\t" + "lh %[stored1], 2(%[ch_stored_p]) \n\t" + "lhu %[adept1], 2(%[ch_adapt_p]) \n\t" + "lhu %[spectrum1], 2(%[spectrum_p]) \n\t" + "mul %[echo0], %[stored0], %[spectrum0] \n\t" + "mul %[temp0], %[adept0], %[spectrum0] \n\t" + "mul %[echo1], %[stored1], %[spectrum1] \n\t" + "mul %[temp1], %[adept1], %[spectrum1] \n\t" + "addu %[par1], %[par1], %[spectrum0] \n\t" + "addu %[par1], %[par1], %[spectrum1] \n\t" + "addiu %[echo_p], %[echo_p], 16 \n\t" + "addu %[par3], %[par3], %[echo0] \n\t" + "addu %[par2], %[par2], %[temp0] \n\t" + "addu %[par3], %[par3], %[echo1] \n\t" + "addu %[par2], %[par2], %[temp1] \n\t" + "usw %[echo0], -16(%[echo_p]) \n\t" + "usw %[echo1], -12(%[echo_p]) \n\t" + "lh %[stored0], 4(%[ch_stored_p]) \n\t" + "lhu %[adept0], 4(%[ch_adapt_p]) \n\t" + "lhu %[spectrum0], 4(%[spectrum_p]) \n\t" + "lh %[stored1], 6(%[ch_stored_p]) \n\t" + "lhu %[adept1], 6(%[ch_adapt_p]) \n\t" + "lhu %[spectrum1], 6(%[spectrum_p]) \n\t" + "mul %[echo0], %[stored0], %[spectrum0] \n\t" + "mul %[temp0], %[adept0], %[spectrum0] \n\t" + "mul %[echo1], %[stored1], %[spectrum1] \n\t" + "mul %[temp1], %[adept1], %[spectrum1] \n\t" + "addu %[par1], %[par1], %[spectrum0] \n\t" + "addu %[par1], %[par1], %[spectrum1] \n\t" + "addiu %[ch_stored_p], %[ch_stored_p], 8 \n\t" + "addiu %[ch_adapt_p], %[ch_adapt_p], 8 \n\t" + "addiu %[spectrum_p], %[spectrum_p], 8 \n\t" + "addu %[par3], %[par3], %[echo0] \n\t" + "addu %[par2], %[par2], %[temp0] \n\t" + "addu %[par3], %[par3], %[echo1] \n\t" + "addu %[par2], %[par2], %[temp1] \n\t" + "usw %[echo0], -8(%[echo_p]) \n\t" + "usw %[echo1], -4(%[echo_p]) \n\t" + ".set pop \n\t" + : [temp0] "=&r"(temp0), [stored0] "=&r"(stored0), + [adept0] "=&r"(adept0), [spectrum0] "=&r"(spectrum0), + [echo0] "=&r"(echo0), [echo_p] "+r"(echo_p), [par3] "+r"(par3), + [par1] "+r"(par1), [par2] "+r"(par2), [stored1] "=&r"(stored1), + [adept1] "=&r"(adept1), [echo1] "=&r"(echo1), + [spectrum1] "=&r"(spectrum1), [temp1] "=&r"(temp1), + [ch_stored_p] "+r"(ch_stored_p), [ch_adapt_p] "+r"(ch_adapt_p), + [spectrum_p] "+r"(spectrum_p) + : + : "hi", "lo", "memory"); + } + + echo_est[PART_LEN] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[PART_LEN], + far_spectrum[PART_LEN]); + par1 += (uint32_t)(far_spectrum[PART_LEN]); + par2 += aecm->channelAdapt16[PART_LEN] * far_spectrum[PART_LEN]; + par3 += (uint32_t)echo_est[PART_LEN]; + + (*far_energy) = par1; + (*echo_energy_adapt) = par2; + (*echo_energy_stored) = par3; +} + +#if defined(MIPS_DSP_R1_LE) +void WebRtcAecm_StoreAdaptiveChannel_mips(AecmCore* aecm, + const uint16_t* far_spectrum, + int32_t* echo_est) { + int i; + int16_t* temp1; + uint16_t* temp8; + int32_t temp0, temp2, temp3, temp4, temp5, temp6; + int32_t* temp7 = &(echo_est[0]); + temp1 = &(aecm->channelStored[0]); + temp8 = (uint16_t*)(&far_spectrum[0]); + + // During startup we store the channel every block. + memcpy(aecm->channelStored, aecm->channelAdapt16, + sizeof(int16_t) * PART_LEN1); + // Recalculate echo estimate + for (i = 0; i < PART_LEN; i += 4) { + __asm __volatile( + "ulw %[temp0], 0(%[temp8]) \n\t" + "ulw %[temp2], 0(%[temp1]) \n\t" + "ulw %[temp4], 4(%[temp8]) \n\t" + "ulw %[temp5], 4(%[temp1]) \n\t" + "muleq_s.w.phl %[temp3], %[temp2], %[temp0] \n\t" + "muleq_s.w.phr %[temp0], %[temp2], %[temp0] \n\t" + "muleq_s.w.phl %[temp6], %[temp5], %[temp4] \n\t" + "muleq_s.w.phr %[temp4], %[temp5], %[temp4] \n\t" + "addiu %[temp7], %[temp7], 16 \n\t" + "addiu %[temp1], %[temp1], 8 \n\t" + "addiu %[temp8], %[temp8], 8 \n\t" + "sra %[temp3], %[temp3], 1 \n\t" + "sra %[temp0], %[temp0], 1 \n\t" + "sra %[temp6], %[temp6], 1 \n\t" + "sra %[temp4], %[temp4], 1 \n\t" + "usw %[temp3], -12(%[temp7]) \n\t" + "usw %[temp0], -16(%[temp7]) \n\t" + "usw %[temp6], -4(%[temp7]) \n\t" + "usw %[temp4], -8(%[temp7]) \n\t" + : [temp0] "=&r"(temp0), [temp2] "=&r"(temp2), [temp3] "=&r"(temp3), + [temp4] "=&r"(temp4), [temp5] "=&r"(temp5), [temp6] "=&r"(temp6), + [temp1] "+r"(temp1), [temp8] "+r"(temp8), [temp7] "+r"(temp7) + : + : "hi", "lo", "memory"); + } + echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); +} + +void WebRtcAecm_ResetAdaptiveChannel_mips(AecmCore* aecm) { + int i; + int32_t* temp3; + int16_t* temp0; + int32_t temp1, temp2, temp4, temp5; + + temp0 = &(aecm->channelStored[0]); + temp3 = &(aecm->channelAdapt32[0]); + + // The stored channel has a significantly lower MSE than the adaptive one for + // two consecutive calculations. Reset the adaptive channel. + memcpy(aecm->channelAdapt16, aecm->channelStored, + sizeof(int16_t) * PART_LEN1); + + // Restore the W32 channel + for (i = 0; i < PART_LEN; i += 4) { + __asm __volatile( + "ulw %[temp1], 0(%[temp0]) \n\t" + "ulw %[temp4], 4(%[temp0]) \n\t" + "preceq.w.phl %[temp2], %[temp1] \n\t" + "preceq.w.phr %[temp1], %[temp1] \n\t" + "preceq.w.phl %[temp5], %[temp4] \n\t" + "preceq.w.phr %[temp4], %[temp4] \n\t" + "addiu %[temp0], %[temp0], 8 \n\t" + "usw %[temp2], 4(%[temp3]) \n\t" + "usw %[temp1], 0(%[temp3]) \n\t" + "usw %[temp5], 12(%[temp3]) \n\t" + "usw %[temp4], 8(%[temp3]) \n\t" + "addiu %[temp3], %[temp3], 16 \n\t" + : [temp1] "=&r"(temp1), [temp2] "=&r"(temp2), [temp4] "=&r"(temp4), + [temp5] "=&r"(temp5), [temp3] "+r"(temp3), [temp0] "+r"(temp0) + : + : "memory"); + } + + aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; +} +#endif // #if defined(MIPS_DSP_R1_LE) + +// Transforms a time domain signal into the frequency domain, outputting the +// complex valued signal, absolute value and sum of absolute values. +// +// time_signal [in] Pointer to time domain signal +// freq_signal_real [out] Pointer to real part of frequency domain array +// freq_signal_imag [out] Pointer to imaginary part of frequency domain +// array +// freq_signal_abs [out] Pointer to absolute value of frequency domain +// array +// freq_signal_sum_abs [out] Pointer to the sum of all absolute values in +// the frequency domain array +// return value The Q-domain of current frequency values +// +static int TimeToFrequencyDomain(AecmCore* aecm, + const int16_t* time_signal, + ComplexInt16* freq_signal, + uint16_t* freq_signal_abs, + uint32_t* freq_signal_sum_abs) { + int i = 0; + int time_signal_scaling = 0; + + // In fft_buf, +16 for 32-byte alignment. + int16_t fft_buf[PART_LEN4 + 16]; + int16_t* fft = (int16_t*)(((uintptr_t)fft_buf + 31) & ~31); + + int16_t tmp16no1; +#if !defined(MIPS_DSP_R2_LE) + int32_t tmp32no1; + int32_t tmp32no2; + int16_t tmp16no2; +#else + int32_t tmp32no10, tmp32no11, tmp32no12, tmp32no13; + int32_t tmp32no20, tmp32no21, tmp32no22, tmp32no23; + int16_t* freqp; + uint16_t* freqabsp; + uint32_t freqt0, freqt1, freqt2, freqt3; + uint32_t freqs; +#endif + +#ifdef AECM_DYNAMIC_Q + tmp16no1 = WebRtcSpl_MaxAbsValueW16(time_signal, PART_LEN2); + time_signal_scaling = WebRtcSpl_NormW16(tmp16no1); +#endif + + WindowAndFFT(aecm, fft, time_signal, freq_signal, time_signal_scaling); + + // Extract imaginary and real part, + // calculate the magnitude for all frequency bins + freq_signal[0].imag = 0; + freq_signal[PART_LEN].imag = 0; + freq_signal[PART_LEN].real = fft[PART_LEN2]; + freq_signal_abs[0] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[0].real); + freq_signal_abs[PART_LEN] = + (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[PART_LEN].real); + (*freq_signal_sum_abs) = + (uint32_t)(freq_signal_abs[0]) + (uint32_t)(freq_signal_abs[PART_LEN]); + +#if !defined(MIPS_DSP_R2_LE) + for (i = 1; i < PART_LEN; i++) { + if (freq_signal[i].real == 0) { + freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].imag); + } else if (freq_signal[i].imag == 0) { + freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].real); + } else { + // Approximation for magnitude of complex fft output + // magn = sqrt(real^2 + imag^2) + // magn ~= alpha * max(|imag|,|real|) + beta * min(|imag|,|real|) + // + // The parameters alpha and beta are stored in Q15 + tmp16no1 = WEBRTC_SPL_ABS_W16(freq_signal[i].real); + tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag); + tmp32no1 = tmp16no1 * tmp16no1; + tmp32no2 = tmp16no2 * tmp16no2; + tmp32no2 = WebRtcSpl_AddSatW32(tmp32no1, tmp32no2); + tmp32no1 = WebRtcSpl_SqrtFloor(tmp32no2); + + freq_signal_abs[i] = (uint16_t)tmp32no1; + } + (*freq_signal_sum_abs) += (uint32_t)freq_signal_abs[i]; + } +#else // #if !defined(MIPS_DSP_R2_LE) + freqs = + (uint32_t)(freq_signal_abs[0]) + (uint32_t)(freq_signal_abs[PART_LEN]); + freqp = &(freq_signal[1].real); + + __asm __volatile( + "lw %[freqt0], 0(%[freqp]) \n\t" + "lw %[freqt1], 4(%[freqp]) \n\t" + "lw %[freqt2], 8(%[freqp]) \n\t" + "mult $ac0, $zero, $zero \n\t" + "mult $ac1, $zero, $zero \n\t" + "mult $ac2, $zero, $zero \n\t" + "dpaq_s.w.ph $ac0, %[freqt0], %[freqt0] \n\t" + "dpaq_s.w.ph $ac1, %[freqt1], %[freqt1] \n\t" + "dpaq_s.w.ph $ac2, %[freqt2], %[freqt2] \n\t" + "addiu %[freqp], %[freqp], 12 \n\t" + "extr.w %[tmp32no20], $ac0, 1 \n\t" + "extr.w %[tmp32no21], $ac1, 1 \n\t" + "extr.w %[tmp32no22], $ac2, 1 \n\t" + : [freqt0] "=&r"(freqt0), [freqt1] "=&r"(freqt1), [freqt2] "=&r"(freqt2), + [freqp] "+r"(freqp), [tmp32no20] "=r"(tmp32no20), + [tmp32no21] "=r"(tmp32no21), [tmp32no22] "=r"(tmp32no22) + : + : "memory", "hi", "lo", "$ac1hi", "$ac1lo", "$ac2hi", "$ac2lo"); + + tmp32no10 = WebRtcSpl_SqrtFloor(tmp32no20); + tmp32no11 = WebRtcSpl_SqrtFloor(tmp32no21); + tmp32no12 = WebRtcSpl_SqrtFloor(tmp32no22); + freq_signal_abs[1] = (uint16_t)tmp32no10; + freq_signal_abs[2] = (uint16_t)tmp32no11; + freq_signal_abs[3] = (uint16_t)tmp32no12; + freqs += (uint32_t)tmp32no10; + freqs += (uint32_t)tmp32no11; + freqs += (uint32_t)tmp32no12; + freqabsp = &(freq_signal_abs[4]); + for (i = 4; i < PART_LEN; i += 4) { + __asm __volatile( + "ulw %[freqt0], 0(%[freqp]) \n\t" + "ulw %[freqt1], 4(%[freqp]) \n\t" + "ulw %[freqt2], 8(%[freqp]) \n\t" + "ulw %[freqt3], 12(%[freqp]) \n\t" + "mult $ac0, $zero, $zero \n\t" + "mult $ac1, $zero, $zero \n\t" + "mult $ac2, $zero, $zero \n\t" + "mult $ac3, $zero, $zero \n\t" + "dpaq_s.w.ph $ac0, %[freqt0], %[freqt0] \n\t" + "dpaq_s.w.ph $ac1, %[freqt1], %[freqt1] \n\t" + "dpaq_s.w.ph $ac2, %[freqt2], %[freqt2] \n\t" + "dpaq_s.w.ph $ac3, %[freqt3], %[freqt3] \n\t" + "addiu %[freqp], %[freqp], 16 \n\t" + "addiu %[freqabsp], %[freqabsp], 8 \n\t" + "extr.w %[tmp32no20], $ac0, 1 \n\t" + "extr.w %[tmp32no21], $ac1, 1 \n\t" + "extr.w %[tmp32no22], $ac2, 1 \n\t" + "extr.w %[tmp32no23], $ac3, 1 \n\t" + : [freqt0] "=&r"(freqt0), [freqt1] "=&r"(freqt1), + [freqt2] "=&r"(freqt2), [freqt3] "=&r"(freqt3), + [tmp32no20] "=r"(tmp32no20), [tmp32no21] "=r"(tmp32no21), + [tmp32no22] "=r"(tmp32no22), [tmp32no23] "=r"(tmp32no23), + [freqabsp] "+r"(freqabsp), [freqp] "+r"(freqp) + : + : "memory", "hi", "lo", "$ac1hi", "$ac1lo", "$ac2hi", "$ac2lo", + "$ac3hi", "$ac3lo"); + + tmp32no10 = WebRtcSpl_SqrtFloor(tmp32no20); + tmp32no11 = WebRtcSpl_SqrtFloor(tmp32no21); + tmp32no12 = WebRtcSpl_SqrtFloor(tmp32no22); + tmp32no13 = WebRtcSpl_SqrtFloor(tmp32no23); + + __asm __volatile( + "sh %[tmp32no10], -8(%[freqabsp]) \n\t" + "sh %[tmp32no11], -6(%[freqabsp]) \n\t" + "sh %[tmp32no12], -4(%[freqabsp]) \n\t" + "sh %[tmp32no13], -2(%[freqabsp]) \n\t" + "addu %[freqs], %[freqs], %[tmp32no10] \n\t" + "addu %[freqs], %[freqs], %[tmp32no11] \n\t" + "addu %[freqs], %[freqs], %[tmp32no12] \n\t" + "addu %[freqs], %[freqs], %[tmp32no13] \n\t" + : [freqs] "+r"(freqs) + : [tmp32no10] "r"(tmp32no10), [tmp32no11] "r"(tmp32no11), + [tmp32no12] "r"(tmp32no12), [tmp32no13] "r"(tmp32no13), + [freqabsp] "r"(freqabsp) + : "memory"); + } + + (*freq_signal_sum_abs) = freqs; +#endif + + return time_signal_scaling; +} + +int WebRtcAecm_ProcessBlock(AecmCore* aecm, + const int16_t* farend, + const int16_t* nearendNoisy, + const int16_t* nearendClean, + int16_t* output) { + int i; + uint32_t xfaSum; + uint32_t dfaNoisySum; + uint32_t dfaCleanSum; + uint32_t echoEst32Gained; + uint32_t tmpU32; + int32_t tmp32no1; + + uint16_t xfa[PART_LEN1]; + uint16_t dfaNoisy[PART_LEN1]; + uint16_t dfaClean[PART_LEN1]; + uint16_t* ptrDfaClean = dfaClean; + const uint16_t* far_spectrum_ptr = NULL; + + // 32 byte aligned buffers (with +8 or +16). + int16_t fft_buf[PART_LEN4 + 2 + 16]; // +2 to make a loop safe. + int32_t echoEst32_buf[PART_LEN1 + 8]; + int32_t dfw_buf[PART_LEN2 + 8]; + int32_t efw_buf[PART_LEN2 + 8]; + + int16_t* fft = (int16_t*)(((uint32_t)fft_buf + 31) & ~31); + int32_t* echoEst32 = (int32_t*)(((uint32_t)echoEst32_buf + 31) & ~31); + ComplexInt16* dfw = (ComplexInt16*)(((uint32_t)dfw_buf + 31) & ~31); + ComplexInt16* efw = (ComplexInt16*)(((uint32_t)efw_buf + 31) & ~31); + + int16_t hnl[PART_LEN1]; + int16_t numPosCoef = 0; + int delay; + int16_t tmp16no1; + int16_t tmp16no2; + int16_t mu; + int16_t supGain; + int16_t zeros32, zeros16; + int16_t zerosDBufNoisy, zerosDBufClean, zerosXBuf; + int far_q; + int16_t resolutionDiff, qDomainDiff, dfa_clean_q_domain_diff; + + const int kMinPrefBand = 4; + const int kMaxPrefBand = 24; + int32_t avgHnl32 = 0; + + int32_t temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; + int16_t* ptr; + int16_t* ptr1; + int16_t* er_ptr; + int16_t* dr_ptr; + + ptr = &hnl[0]; + ptr1 = &hnl[0]; + er_ptr = &efw[0].real; + dr_ptr = &dfw[0].real; + + // Determine startup state. There are three states: + // (0) the first CONV_LEN blocks + // (1) another CONV_LEN blocks + // (2) the rest + + if (aecm->startupState < 2) { + aecm->startupState = + (aecm->totCount >= CONV_LEN) + (aecm->totCount >= CONV_LEN2); + } + // END: Determine startup state + + // Buffer near and far end signals + memcpy(aecm->xBuf + PART_LEN, farend, sizeof(int16_t) * PART_LEN); + memcpy(aecm->dBufNoisy + PART_LEN, nearendNoisy, sizeof(int16_t) * PART_LEN); + if (nearendClean != NULL) { + memcpy(aecm->dBufClean + PART_LEN, nearendClean, + sizeof(int16_t) * PART_LEN); + } + + // Transform far end signal from time domain to frequency domain. + far_q = TimeToFrequencyDomain(aecm, aecm->xBuf, dfw, xfa, &xfaSum); + + // Transform noisy near end signal from time domain to frequency domain. + zerosDBufNoisy = + TimeToFrequencyDomain(aecm, aecm->dBufNoisy, dfw, dfaNoisy, &dfaNoisySum); + aecm->dfaNoisyQDomainOld = aecm->dfaNoisyQDomain; + aecm->dfaNoisyQDomain = (int16_t)zerosDBufNoisy; + + if (nearendClean == NULL) { + ptrDfaClean = dfaNoisy; + aecm->dfaCleanQDomainOld = aecm->dfaNoisyQDomainOld; + aecm->dfaCleanQDomain = aecm->dfaNoisyQDomain; + dfaCleanSum = dfaNoisySum; + } else { + // Transform clean near end signal from time domain to frequency domain. + zerosDBufClean = TimeToFrequencyDomain(aecm, aecm->dBufClean, dfw, dfaClean, + &dfaCleanSum); + aecm->dfaCleanQDomainOld = aecm->dfaCleanQDomain; + aecm->dfaCleanQDomain = (int16_t)zerosDBufClean; + } + + // Get the delay + // Save far-end history and estimate delay + WebRtcAecm_UpdateFarHistory(aecm, xfa, far_q); + + if (WebRtc_AddFarSpectrumFix(aecm->delay_estimator_farend, xfa, PART_LEN1, + far_q) == -1) { + return -1; + } + delay = WebRtc_DelayEstimatorProcessFix(aecm->delay_estimator, dfaNoisy, + PART_LEN1, zerosDBufNoisy); + if (delay == -1) { + return -1; + } else if (delay == -2) { + // If the delay is unknown, we assume zero. + // NOTE: this will have to be adjusted if we ever add lookahead. + delay = 0; + } + + if (aecm->fixedDelay >= 0) { + // Use fixed delay + delay = aecm->fixedDelay; + } + + // Get aligned far end spectrum + far_spectrum_ptr = WebRtcAecm_AlignedFarend(aecm, &far_q, delay); + zerosXBuf = (int16_t)far_q; + + if (far_spectrum_ptr == NULL) { + return -1; + } + + // Calculate log(energy) and update energy threshold levels + WebRtcAecm_CalcEnergies(aecm, far_spectrum_ptr, zerosXBuf, dfaNoisySum, + echoEst32); + // Calculate stepsize + mu = WebRtcAecm_CalcStepSize(aecm); + + // Update counters + aecm->totCount++; + + // This is the channel estimation algorithm. + // It is base on NLMS but has a variable step length, + // which was calculated above. + WebRtcAecm_UpdateChannel(aecm, far_spectrum_ptr, zerosXBuf, dfaNoisy, mu, + echoEst32); + + supGain = WebRtcAecm_CalcSuppressionGain(aecm); + + // Calculate Wiener filter hnl[] + for (i = 0; i < PART_LEN1; i++) { + // Far end signal through channel estimate in Q8 + // How much can we shift right to preserve resolution + tmp32no1 = echoEst32[i] - aecm->echoFilt[i]; + aecm->echoFilt[i] += + rtc::dchecked_cast((int64_t{tmp32no1} * 50) >> 8); + + zeros32 = WebRtcSpl_NormW32(aecm->echoFilt[i]) + 1; + zeros16 = WebRtcSpl_NormW16(supGain) + 1; + if (zeros32 + zeros16 > 16) { + // Multiplication is safe + // Result in + // Q(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN+aecm->xfaQDomainBuf[diff]) + echoEst32Gained = + WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], (uint16_t)supGain); + resolutionDiff = 14 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; + resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); + } else { + tmp16no1 = 17 - zeros32 - zeros16; + resolutionDiff = + 14 + tmp16no1 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; + resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); + if (zeros32 > tmp16no1) { + echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], + supGain >> tmp16no1); + } else { + // Result in Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN-16) + echoEst32Gained = (aecm->echoFilt[i] >> tmp16no1) * supGain; + } + } + + zeros16 = WebRtcSpl_NormW16(aecm->nearFilt[i]); + RTC_DCHECK_GE(zeros16, 0); // |zeros16| is a norm, hence non-negative. + dfa_clean_q_domain_diff = aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld; + if (zeros16 < dfa_clean_q_domain_diff && aecm->nearFilt[i]) { + tmp16no1 = aecm->nearFilt[i] << zeros16; + qDomainDiff = zeros16 - dfa_clean_q_domain_diff; + tmp16no2 = ptrDfaClean[i] >> -qDomainDiff; + } else { + tmp16no1 = dfa_clean_q_domain_diff < 0 + ? aecm->nearFilt[i] >> -dfa_clean_q_domain_diff + : aecm->nearFilt[i] << dfa_clean_q_domain_diff; + qDomainDiff = 0; + tmp16no2 = ptrDfaClean[i]; + } + + tmp32no1 = (int32_t)(tmp16no2 - tmp16no1); + tmp16no2 = (int16_t)(tmp32no1 >> 4); + tmp16no2 += tmp16no1; + zeros16 = WebRtcSpl_NormW16(tmp16no2); + if ((tmp16no2) & (-qDomainDiff > zeros16)) { + aecm->nearFilt[i] = WEBRTC_SPL_WORD16_MAX; + } else { + aecm->nearFilt[i] = + qDomainDiff < 0 ? tmp16no2 << -qDomainDiff : tmp16no2 >> qDomainDiff; + } + + // Wiener filter coefficients, resulting hnl in Q14 + if (echoEst32Gained == 0) { + hnl[i] = ONE_Q14; + numPosCoef++; + } else if (aecm->nearFilt[i] == 0) { + hnl[i] = 0; + } else { + // Multiply the suppression gain + // Rounding + echoEst32Gained += (uint32_t)(aecm->nearFilt[i] >> 1); + tmpU32 = + WebRtcSpl_DivU32U16(echoEst32Gained, (uint16_t)aecm->nearFilt[i]); + + // Current resolution is + // Q-(RESOLUTION_CHANNEL + RESOLUTION_SUPGAIN + // - max(0, 17 - zeros16 - zeros32)) + // Make sure we are in Q14 + tmp32no1 = (int32_t)WEBRTC_SPL_SHIFT_W32(tmpU32, resolutionDiff); + if (tmp32no1 > ONE_Q14) { + hnl[i] = 0; + } else if (tmp32no1 < 0) { + hnl[i] = ONE_Q14; + numPosCoef++; + } else { + // 1-echoEst/dfa + hnl[i] = ONE_Q14 - (int16_t)tmp32no1; + if (hnl[i] <= 0) { + hnl[i] = 0; + } else { + numPosCoef++; + } + } + } + } + + // Only in wideband. Prevent the gain in upper band from being larger than + // in lower band. + if (aecm->mult == 2) { + // TODO(bjornv): Investigate if the scaling of hnl[i] below can cause + // speech distortion in double-talk. + for (i = 0; i < (PART_LEN1 >> 3); i++) { + __asm __volatile( + "lh %[temp1], 0(%[ptr1]) \n\t" + "lh %[temp2], 2(%[ptr1]) \n\t" + "lh %[temp3], 4(%[ptr1]) \n\t" + "lh %[temp4], 6(%[ptr1]) \n\t" + "lh %[temp5], 8(%[ptr1]) \n\t" + "lh %[temp6], 10(%[ptr1]) \n\t" + "lh %[temp7], 12(%[ptr1]) \n\t" + "lh %[temp8], 14(%[ptr1]) \n\t" + "mul %[temp1], %[temp1], %[temp1] \n\t" + "mul %[temp2], %[temp2], %[temp2] \n\t" + "mul %[temp3], %[temp3], %[temp3] \n\t" + "mul %[temp4], %[temp4], %[temp4] \n\t" + "mul %[temp5], %[temp5], %[temp5] \n\t" + "mul %[temp6], %[temp6], %[temp6] \n\t" + "mul %[temp7], %[temp7], %[temp7] \n\t" + "mul %[temp8], %[temp8], %[temp8] \n\t" + "sra %[temp1], %[temp1], 14 \n\t" + "sra %[temp2], %[temp2], 14 \n\t" + "sra %[temp3], %[temp3], 14 \n\t" + "sra %[temp4], %[temp4], 14 \n\t" + "sra %[temp5], %[temp5], 14 \n\t" + "sra %[temp6], %[temp6], 14 \n\t" + "sra %[temp7], %[temp7], 14 \n\t" + "sra %[temp8], %[temp8], 14 \n\t" + "sh %[temp1], 0(%[ptr1]) \n\t" + "sh %[temp2], 2(%[ptr1]) \n\t" + "sh %[temp3], 4(%[ptr1]) \n\t" + "sh %[temp4], 6(%[ptr1]) \n\t" + "sh %[temp5], 8(%[ptr1]) \n\t" + "sh %[temp6], 10(%[ptr1]) \n\t" + "sh %[temp7], 12(%[ptr1]) \n\t" + "sh %[temp8], 14(%[ptr1]) \n\t" + "addiu %[ptr1], %[ptr1], 16 \n\t" + : [temp1] "=&r"(temp1), [temp2] "=&r"(temp2), [temp3] "=&r"(temp3), + [temp4] "=&r"(temp4), [temp5] "=&r"(temp5), [temp6] "=&r"(temp6), + [temp7] "=&r"(temp7), [temp8] "=&r"(temp8), [ptr1] "+r"(ptr1) + : + : "memory", "hi", "lo"); + } + for (i = 0; i < (PART_LEN1 & 7); i++) { + __asm __volatile( + "lh %[temp1], 0(%[ptr1]) \n\t" + "mul %[temp1], %[temp1], %[temp1] \n\t" + "sra %[temp1], %[temp1], 14 \n\t" + "sh %[temp1], 0(%[ptr1]) \n\t" + "addiu %[ptr1], %[ptr1], 2 \n\t" + : [temp1] "=&r"(temp1), [ptr1] "+r"(ptr1) + : + : "memory", "hi", "lo"); + } + + for (i = kMinPrefBand; i <= kMaxPrefBand; i++) { + avgHnl32 += (int32_t)hnl[i]; + } + + RTC_DCHECK_GT(kMaxPrefBand - kMinPrefBand + 1, 0); + avgHnl32 /= (kMaxPrefBand - kMinPrefBand + 1); + + for (i = kMaxPrefBand; i < PART_LEN1; i++) { + if (hnl[i] > (int16_t)avgHnl32) { + hnl[i] = (int16_t)avgHnl32; + } + } + } + + // Calculate NLP gain, result is in Q14 + if (aecm->nlpFlag) { + if (numPosCoef < 3) { + for (i = 0; i < PART_LEN1; i++) { + efw[i].real = 0; + efw[i].imag = 0; + hnl[i] = 0; + } + } else { + for (i = 0; i < PART_LEN1; i++) { +#if defined(MIPS_DSP_R1_LE) + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "lh %[temp1], 0(%[ptr]) \n\t" + "lh %[temp2], 0(%[dr_ptr]) \n\t" + "slti %[temp4], %[temp1], 0x4001 \n\t" + "beqz %[temp4], 3f \n\t" + " lh %[temp3], 2(%[dr_ptr]) \n\t" + "slti %[temp5], %[temp1], 3277 \n\t" + "bnez %[temp5], 2f \n\t" + " addiu %[dr_ptr], %[dr_ptr], 4 \n\t" + "mul %[temp2], %[temp2], %[temp1] \n\t" + "mul %[temp3], %[temp3], %[temp1] \n\t" + "shra_r.w %[temp2], %[temp2], 14 \n\t" + "shra_r.w %[temp3], %[temp3], 14 \n\t" + "b 4f \n\t" + " nop \n\t" + "2: \n\t" + "addu %[temp1], $zero, $zero \n\t" + "addu %[temp2], $zero, $zero \n\t" + "addu %[temp3], $zero, $zero \n\t" + "b 1f \n\t" + " nop \n\t" + "3: \n\t" + "addiu %[temp1], $0, 0x4000 \n\t" + "1: \n\t" + "sh %[temp1], 0(%[ptr]) \n\t" + "4: \n\t" + "sh %[temp2], 0(%[er_ptr]) \n\t" + "sh %[temp3], 2(%[er_ptr]) \n\t" + "addiu %[ptr], %[ptr], 2 \n\t" + "addiu %[er_ptr], %[er_ptr], 4 \n\t" + ".set pop \n\t" + : [temp1] "=&r"(temp1), [temp2] "=&r"(temp2), [temp3] "=&r"(temp3), + [temp4] "=&r"(temp4), [temp5] "=&r"(temp5), [ptr] "+r"(ptr), + [er_ptr] "+r"(er_ptr), [dr_ptr] "+r"(dr_ptr) + : + : "memory", "hi", "lo"); +#else + __asm __volatile( + ".set push \n\t" + ".set noreorder \n\t" + "lh %[temp1], 0(%[ptr]) \n\t" + "lh %[temp2], 0(%[dr_ptr]) \n\t" + "slti %[temp4], %[temp1], 0x4001 \n\t" + "beqz %[temp4], 3f \n\t" + " lh %[temp3], 2(%[dr_ptr]) \n\t" + "slti %[temp5], %[temp1], 3277 \n\t" + "bnez %[temp5], 2f \n\t" + " addiu %[dr_ptr], %[dr_ptr], 4 \n\t" + "mul %[temp2], %[temp2], %[temp1] \n\t" + "mul %[temp3], %[temp3], %[temp1] \n\t" + "addiu %[temp2], %[temp2], 0x2000 \n\t" + "addiu %[temp3], %[temp3], 0x2000 \n\t" + "sra %[temp2], %[temp2], 14 \n\t" + "sra %[temp3], %[temp3], 14 \n\t" + "b 4f \n\t" + " nop \n\t" + "2: \n\t" + "addu %[temp1], $zero, $zero \n\t" + "addu %[temp2], $zero, $zero \n\t" + "addu %[temp3], $zero, $zero \n\t" + "b 1f \n\t" + " nop \n\t" + "3: \n\t" + "addiu %[temp1], $0, 0x4000 \n\t" + "1: \n\t" + "sh %[temp1], 0(%[ptr]) \n\t" + "4: \n\t" + "sh %[temp2], 0(%[er_ptr]) \n\t" + "sh %[temp3], 2(%[er_ptr]) \n\t" + "addiu %[ptr], %[ptr], 2 \n\t" + "addiu %[er_ptr], %[er_ptr], 4 \n\t" + ".set pop \n\t" + : [temp1] "=&r"(temp1), [temp2] "=&r"(temp2), [temp3] "=&r"(temp3), + [temp4] "=&r"(temp4), [temp5] "=&r"(temp5), [ptr] "+r"(ptr), + [er_ptr] "+r"(er_ptr), [dr_ptr] "+r"(dr_ptr) + : + : "memory", "hi", "lo"); +#endif + } + } + } else { + // multiply with Wiener coefficients + for (i = 0; i < PART_LEN1; i++) { + efw[i].real = (int16_t)( + WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real, hnl[i], 14)); + efw[i].imag = (int16_t)( + WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag, hnl[i], 14)); + } + } + + if (aecm->cngMode == AecmTrue) { + ComfortNoise(aecm, ptrDfaClean, efw, hnl); + } + + InverseFFTAndWindow(aecm, fft, efw, output, nearendClean); + + return 0; +} + +// Generate comfort noise and add to output signal. +static void ComfortNoise(AecmCore* aecm, + const uint16_t* dfa, + ComplexInt16* out, + const int16_t* lambda) { + int16_t i; + int16_t tmp16, tmp161, tmp162, tmp163, nrsh1, nrsh2; + int32_t tmp32, tmp321, tnoise, tnoise1; + int32_t tmp322, tmp323, *tmp1; + int16_t* dfap; + int16_t* lambdap; + const int32_t c2049 = 2049; + const int32_t c359 = 359; + const int32_t c114 = ONE_Q14; + + int16_t randW16[PART_LEN]; + int16_t uReal[PART_LEN1]; + int16_t uImag[PART_LEN1]; + int32_t outLShift32; + + int16_t shiftFromNearToNoise = kNoiseEstQDomain - aecm->dfaCleanQDomain; + int16_t minTrackShift = 9; + + RTC_DCHECK_GE(shiftFromNearToNoise, 0); + RTC_DCHECK_LT(shiftFromNearToNoise, 16); + + if (aecm->noiseEstCtr < 100) { + // Track the minimum more quickly initially. + aecm->noiseEstCtr++; + minTrackShift = 6; + } + + // Generate a uniform random array on [0 2^15-1]. + WebRtcSpl_RandUArray(randW16, PART_LEN, &aecm->seed); + int16_t* randW16p = (int16_t*)randW16; +#if defined(MIPS_DSP_R1_LE) + int16_t* kCosTablep = (int16_t*)WebRtcAecm_kCosTable; + int16_t* kSinTablep = (int16_t*)WebRtcAecm_kSinTable; +#endif // #if defined(MIPS_DSP_R1_LE) + tmp1 = (int32_t*)aecm->noiseEst + 1; + dfap = (int16_t*)dfa + 1; + lambdap = (int16_t*)lambda + 1; + // Estimate noise power. + for (i = 1; i < PART_LEN1; i += 2) { + // Shift to the noise domain. + __asm __volatile( + "lh %[tmp32], 0(%[dfap]) \n\t" + "lw %[tnoise], 0(%[tmp1]) \n\t" + "sllv %[outLShift32], %[tmp32], %[shiftFromNearToNoise] \n\t" + : [tmp32] "=&r"(tmp32), [outLShift32] "=r"(outLShift32), + [tnoise] "=&r"(tnoise) + : [tmp1] "r"(tmp1), [dfap] "r"(dfap), + [shiftFromNearToNoise] "r"(shiftFromNearToNoise) + : "memory"); + + if (outLShift32 < tnoise) { + // Reset "too low" counter + aecm->noiseEstTooLowCtr[i] = 0; + // Track the minimum. + if (tnoise < (1 << minTrackShift)) { + // For small values, decrease noiseEst[i] every + // |kNoiseEstIncCount| block. The regular approach below can not + // go further down due to truncation. + aecm->noiseEstTooHighCtr[i]++; + if (aecm->noiseEstTooHighCtr[i] >= kNoiseEstIncCount) { + tnoise--; + aecm->noiseEstTooHighCtr[i] = 0; // Reset the counter + } + } else { + __asm __volatile( + "subu %[tmp32], %[tnoise], %[outLShift32] \n\t" + "srav %[tmp32], %[tmp32], %[minTrackShift] \n\t" + "subu %[tnoise], %[tnoise], %[tmp32] \n\t" + : [tmp32] "=&r"(tmp32), [tnoise] "+r"(tnoise) + : + [outLShift32] "r"(outLShift32), [minTrackShift] "r"(minTrackShift)); + } + } else { + // Reset "too high" counter + aecm->noiseEstTooHighCtr[i] = 0; + // Ramp slowly upwards until we hit the minimum again. + if ((tnoise >> 19) <= 0) { + if ((tnoise >> 11) > 0) { + // Large enough for relative increase + __asm __volatile( + "mul %[tnoise], %[tnoise], %[c2049] \n\t" + "sra %[tnoise], %[tnoise], 11 \n\t" + : [tnoise] "+r"(tnoise) + : [c2049] "r"(c2049) + : "hi", "lo"); + } else { + // Make incremental increases based on size every + // |kNoiseEstIncCount| block + aecm->noiseEstTooLowCtr[i]++; + if (aecm->noiseEstTooLowCtr[i] >= kNoiseEstIncCount) { + __asm __volatile( + "sra %[tmp32], %[tnoise], 9 \n\t" + "addi %[tnoise], %[tnoise], 1 \n\t" + "addu %[tnoise], %[tnoise], %[tmp32] \n\t" + : [tnoise] "+r"(tnoise), [tmp32] "=&r"(tmp32) + :); + aecm->noiseEstTooLowCtr[i] = 0; // Reset counter + } + } + } else { + // Avoid overflow. + // Multiplication with 2049 will cause wrap around. Scale + // down first and then multiply + __asm __volatile( + "sra %[tnoise], %[tnoise], 11 \n\t" + "mul %[tnoise], %[tnoise], %[c2049] \n\t" + : [tnoise] "+r"(tnoise) + : [c2049] "r"(c2049) + : "hi", "lo"); + } + } + + // Shift to the noise domain. + __asm __volatile( + "lh %[tmp32], 2(%[dfap]) \n\t" + "lw %[tnoise1], 4(%[tmp1]) \n\t" + "addiu %[dfap], %[dfap], 4 \n\t" + "sllv %[outLShift32], %[tmp32], %[shiftFromNearToNoise] \n\t" + : [tmp32] "=&r"(tmp32), [dfap] "+r"(dfap), + [outLShift32] "=r"(outLShift32), [tnoise1] "=&r"(tnoise1) + : [tmp1] "r"(tmp1), [shiftFromNearToNoise] "r"(shiftFromNearToNoise) + : "memory"); + + if (outLShift32 < tnoise1) { + // Reset "too low" counter + aecm->noiseEstTooLowCtr[i + 1] = 0; + // Track the minimum. + if (tnoise1 < (1 << minTrackShift)) { + // For small values, decrease noiseEst[i] every + // |kNoiseEstIncCount| block. The regular approach below can not + // go further down due to truncation. + aecm->noiseEstTooHighCtr[i + 1]++; + if (aecm->noiseEstTooHighCtr[i + 1] >= kNoiseEstIncCount) { + tnoise1--; + aecm->noiseEstTooHighCtr[i + 1] = 0; // Reset the counter + } + } else { + __asm __volatile( + "subu %[tmp32], %[tnoise1], %[outLShift32] \n\t" + "srav %[tmp32], %[tmp32], %[minTrackShift] \n\t" + "subu %[tnoise1], %[tnoise1], %[tmp32] \n\t" + : [tmp32] "=&r"(tmp32), [tnoise1] "+r"(tnoise1) + : + [outLShift32] "r"(outLShift32), [minTrackShift] "r"(minTrackShift)); + } + } else { + // Reset "too high" counter + aecm->noiseEstTooHighCtr[i + 1] = 0; + // Ramp slowly upwards until we hit the minimum again. + if ((tnoise1 >> 19) <= 0) { + if ((tnoise1 >> 11) > 0) { + // Large enough for relative increase + __asm __volatile( + "mul %[tnoise1], %[tnoise1], %[c2049] \n\t" + "sra %[tnoise1], %[tnoise1], 11 \n\t" + : [tnoise1] "+r"(tnoise1) + : [c2049] "r"(c2049) + : "hi", "lo"); + } else { + // Make incremental increases based on size every + // |kNoiseEstIncCount| block + aecm->noiseEstTooLowCtr[i + 1]++; + if (aecm->noiseEstTooLowCtr[i + 1] >= kNoiseEstIncCount) { + __asm __volatile( + "sra %[tmp32], %[tnoise1], 9 \n\t" + "addi %[tnoise1], %[tnoise1], 1 \n\t" + "addu %[tnoise1], %[tnoise1], %[tmp32] \n\t" + : [tnoise1] "+r"(tnoise1), [tmp32] "=&r"(tmp32) + :); + aecm->noiseEstTooLowCtr[i + 1] = 0; // Reset counter + } + } + } else { + // Avoid overflow. + // Multiplication with 2049 will cause wrap around. Scale + // down first and then multiply + __asm __volatile( + "sra %[tnoise1], %[tnoise1], 11 \n\t" + "mul %[tnoise1], %[tnoise1], %[c2049] \n\t" + : [tnoise1] "+r"(tnoise1) + : [c2049] "r"(c2049) + : "hi", "lo"); + } + } + + __asm __volatile( + "lh %[tmp16], 0(%[lambdap]) \n\t" + "lh %[tmp161], 2(%[lambdap]) \n\t" + "sw %[tnoise], 0(%[tmp1]) \n\t" + "sw %[tnoise1], 4(%[tmp1]) \n\t" + "subu %[tmp16], %[c114], %[tmp16] \n\t" + "subu %[tmp161], %[c114], %[tmp161] \n\t" + "srav %[tmp32], %[tnoise], %[shiftFromNearToNoise] \n\t" + "srav %[tmp321], %[tnoise1], %[shiftFromNearToNoise] \n\t" + "addiu %[lambdap], %[lambdap], 4 \n\t" + "addiu %[tmp1], %[tmp1], 8 \n\t" + : [tmp16] "=&r"(tmp16), [tmp161] "=&r"(tmp161), [tmp1] "+r"(tmp1), + [tmp32] "=&r"(tmp32), [tmp321] "=&r"(tmp321), [lambdap] "+r"(lambdap) + : [tnoise] "r"(tnoise), [tnoise1] "r"(tnoise1), [c114] "r"(c114), + [shiftFromNearToNoise] "r"(shiftFromNearToNoise) + : "memory"); + + if (tmp32 > 32767) { + tmp32 = 32767; + aecm->noiseEst[i] = tmp32 << shiftFromNearToNoise; + } + if (tmp321 > 32767) { + tmp321 = 32767; + aecm->noiseEst[i + 1] = tmp321 << shiftFromNearToNoise; + } + + __asm __volatile( + "mul %[tmp32], %[tmp32], %[tmp16] \n\t" + "mul %[tmp321], %[tmp321], %[tmp161] \n\t" + "sra %[nrsh1], %[tmp32], 14 \n\t" + "sra %[nrsh2], %[tmp321], 14 \n\t" + : [nrsh1] "=&r"(nrsh1), [nrsh2] "=r"(nrsh2) + : [tmp16] "r"(tmp16), [tmp161] "r"(tmp161), [tmp32] "r"(tmp32), + [tmp321] "r"(tmp321) + : "memory", "hi", "lo"); + + __asm __volatile( + "lh %[tmp32], 0(%[randW16p]) \n\t" + "lh %[tmp321], 2(%[randW16p]) \n\t" + "addiu %[randW16p], %[randW16p], 4 \n\t" + "mul %[tmp32], %[tmp32], %[c359] \n\t" + "mul %[tmp321], %[tmp321], %[c359] \n\t" + "sra %[tmp16], %[tmp32], 15 \n\t" + "sra %[tmp161], %[tmp321], 15 \n\t" + : [randW16p] "+r"(randW16p), [tmp32] "=&r"(tmp32), [tmp16] "=r"(tmp16), + [tmp161] "=r"(tmp161), [tmp321] "=&r"(tmp321) + : [c359] "r"(c359) + : "memory", "hi", "lo"); + +#if !defined(MIPS_DSP_R1_LE) + tmp32 = WebRtcAecm_kCosTable[tmp16]; + tmp321 = WebRtcAecm_kSinTable[tmp16]; + tmp322 = WebRtcAecm_kCosTable[tmp161]; + tmp323 = WebRtcAecm_kSinTable[tmp161]; +#else + __asm __volatile( + "sll %[tmp16], %[tmp16], 1 \n\t" + "sll %[tmp161], %[tmp161], 1 \n\t" + "lhx %[tmp32], %[tmp16](%[kCosTablep]) \n\t" + "lhx %[tmp321], %[tmp16](%[kSinTablep]) \n\t" + "lhx %[tmp322], %[tmp161](%[kCosTablep]) \n\t" + "lhx %[tmp323], %[tmp161](%[kSinTablep]) \n\t" + : [tmp32] "=&r"(tmp32), [tmp321] "=&r"(tmp321), [tmp322] "=&r"(tmp322), + [tmp323] "=&r"(tmp323) + : [kCosTablep] "r"(kCosTablep), [tmp16] "r"(tmp16), + [tmp161] "r"(tmp161), [kSinTablep] "r"(kSinTablep) + : "memory"); +#endif + __asm __volatile( + "mul %[tmp32], %[tmp32], %[nrsh1] \n\t" + "negu %[tmp162], %[nrsh1] \n\t" + "mul %[tmp322], %[tmp322], %[nrsh2] \n\t" + "negu %[tmp163], %[nrsh2] \n\t" + "sra %[tmp32], %[tmp32], 13 \n\t" + "mul %[tmp321], %[tmp321], %[tmp162] \n\t" + "sra %[tmp322], %[tmp322], 13 \n\t" + "mul %[tmp323], %[tmp323], %[tmp163] \n\t" + "sra %[tmp321], %[tmp321], 13 \n\t" + "sra %[tmp323], %[tmp323], 13 \n\t" + : [tmp32] "+r"(tmp32), [tmp321] "+r"(tmp321), [tmp162] "=&r"(tmp162), + [tmp322] "+r"(tmp322), [tmp323] "+r"(tmp323), [tmp163] "=&r"(tmp163) + : [nrsh1] "r"(nrsh1), [nrsh2] "r"(nrsh2) + : "hi", "lo"); + // Tables are in Q13. + uReal[i] = (int16_t)tmp32; + uImag[i] = (int16_t)tmp321; + uReal[i + 1] = (int16_t)tmp322; + uImag[i + 1] = (int16_t)tmp323; + } + + int32_t tt, sgn; + tt = out[0].real; + sgn = ((int)tt) >> 31; + out[0].real = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); + tt = out[0].imag; + sgn = ((int)tt) >> 31; + out[0].imag = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); + for (i = 1; i < PART_LEN; i++) { + tt = out[i].real + uReal[i]; + sgn = ((int)tt) >> 31; + out[i].real = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); + tt = out[i].imag + uImag[i]; + sgn = ((int)tt) >> 31; + out[i].imag = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); + } + tt = out[PART_LEN].real + uReal[PART_LEN]; + sgn = ((int)tt) >> 31; + out[PART_LEN].real = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); + tt = out[PART_LEN].imag; + sgn = ((int)tt) >> 31; + out[PART_LEN].imag = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aecm/aecm_core_neon.c b/webrtc/modules/audio_processing/aecm/aecm_core_neon.cc similarity index 84% rename from webrtc/modules/audio_processing/aecm/aecm_core_neon.c rename to webrtc/modules/audio_processing/aecm/aecm_core_neon.cc index 1751fcf..584110d 100644 --- a/webrtc/modules/audio_processing/aecm/aecm_core_neon.c +++ b/webrtc/modules/audio_processing/aecm/aecm_core_neon.cc @@ -8,29 +8,19 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/aecm/aecm_core.h" - #include -#include -#include "webrtc/common_audio/signal_processing/include/real_fft.h" +#include "common_audio/signal_processing/include/real_fft.h" +#include "modules/audio_processing/aecm/aecm_core.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { // TODO(kma): Re-write the corresponding assembly file, the offset // generating script and makefile, to replace these C functions. -// Square root of Hanning window in Q14. -const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END = { - 0, - 399, 798, 1196, 1594, 1990, 2386, 2780, 3172, - 3562, 3951, 4337, 4720, 5101, 5478, 5853, 6224, - 6591, 6954, 7313, 7668, 8019, 8364, 8705, 9040, - 9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514, - 11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553, - 13773, 13985, 14189, 14384, 14571, 14749, 14918, 15079, - 15231, 15373, 15506, 15631, 15746, 15851, 15947, 16034, - 16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384 -}; - static inline void AddLanes(uint32_t* ptr, uint32x4_t v) { #if defined(WEBRTC_ARCH_ARM64) *(ptr) = vaddvq_u32(v); @@ -42,6 +32,8 @@ static inline void AddLanes(uint32_t* ptr, uint32x4_t v) { #endif } +} // namespace + void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore* aecm, const uint16_t* far_spectrum, int32_t* echo_est, @@ -90,12 +82,12 @@ void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore* aecm, echo_stored_v = vaddq_u32(echo_est_v_low, echo_stored_v); echo_stored_v = vaddq_u32(echo_est_v_high, echo_stored_v); - echo_adapt_v = vmlal_u16(echo_adapt_v, - vreinterpret_u16_s16(vget_low_s16(adapt_v)), - vget_low_u16(spectrum_v)); - echo_adapt_v = vmlal_u16(echo_adapt_v, - vreinterpret_u16_s16(vget_high_s16(adapt_v)), - vget_high_u16(spectrum_v)); + echo_adapt_v = + vmlal_u16(echo_adapt_v, vreinterpret_u16_s16(vget_low_s16(adapt_v)), + vget_low_u16(spectrum_v)); + echo_adapt_v = + vmlal_u16(echo_adapt_v, vreinterpret_u16_s16(vget_high_s16(adapt_v)), + vget_high_u16(spectrum_v)); start_stored_p += 8; start_adapt_p += 8; @@ -117,9 +109,9 @@ void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore* aecm, void WebRtcAecm_StoreAdaptiveChannelNeon(AecmCore* aecm, const uint16_t* far_spectrum, int32_t* echo_est) { - assert((uintptr_t)echo_est % 32 == 0); - assert((uintptr_t)(aecm->channelStored) % 16 == 0); - assert((uintptr_t)(aecm->channelAdapt16) % 16 == 0); + RTC_DCHECK_EQ(0, (uintptr_t)echo_est % 32); + RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelStored % 16); + RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelAdapt16 % 16); // This is C code of following optimized code. // During startup we store the channel every block. @@ -174,9 +166,9 @@ void WebRtcAecm_StoreAdaptiveChannelNeon(AecmCore* aecm, } void WebRtcAecm_ResetAdaptiveChannelNeon(AecmCore* aecm) { - assert((uintptr_t)(aecm->channelStored) % 16 == 0); - assert((uintptr_t)(aecm->channelAdapt16) % 16 == 0); - assert((uintptr_t)(aecm->channelAdapt32) % 32 == 0); + RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelStored % 16); + RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelAdapt16 % 16); + RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelAdapt32 % 32); // The C code of following optimized code. // for (i = 0; i < PART_LEN1; i++) { @@ -210,3 +202,5 @@ void WebRtcAecm_ResetAdaptiveChannelNeon(AecmCore* aecm) { aecm->channelAdapt16[PART_LEN] = aecm->channelStored[PART_LEN]; aecm->channelAdapt32[PART_LEN] = (int32_t)aecm->channelStored[PART_LEN] << 16; } + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aecm/aecm_defines.h b/webrtc/modules/audio_processing/aecm/aecm_defines.h index 6d63990..5805549 100644 --- a/webrtc/modules/audio_processing/aecm/aecm_defines.h +++ b/webrtc/modules/audio_processing/aecm/aecm_defines.h @@ -8,80 +8,80 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AECM_AECM_DEFINES_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AECM_AECM_DEFINES_H_ +#ifndef MODULES_AUDIO_PROCESSING_AECM_AECM_DEFINES_H_ +#define MODULES_AUDIO_PROCESSING_AECM_AECM_DEFINES_H_ -#define AECM_DYNAMIC_Q /* Turn on/off dynamic Q-domain. */ +#define AECM_DYNAMIC_Q /* Turn on/off dynamic Q-domain. */ /* Algorithm parameters */ -#define FRAME_LEN 80 /* Total frame length, 10 ms. */ +#define FRAME_LEN 80 /* Total frame length, 10 ms. */ -#define PART_LEN 64 /* Length of partition. */ -#define PART_LEN_SHIFT 7 /* Length of (PART_LEN * 2) in base 2. */ +#define PART_LEN 64 /* Length of partition. */ +#define PART_LEN_SHIFT 7 /* Length of (PART_LEN * 2) in base 2. */ -#define PART_LEN1 (PART_LEN + 1) /* Unique fft coefficients. */ -#define PART_LEN2 (PART_LEN << 1) /* Length of partition * 2. */ -#define PART_LEN4 (PART_LEN << 2) /* Length of partition * 4. */ -#define FAR_BUF_LEN PART_LEN4 /* Length of buffers. */ -#define MAX_DELAY 100 +#define PART_LEN1 (PART_LEN + 1) /* Unique fft coefficients. */ +#define PART_LEN2 (PART_LEN << 1) /* Length of partition * 2. */ +#define PART_LEN4 (PART_LEN << 2) /* Length of partition * 4. */ +#define FAR_BUF_LEN PART_LEN4 /* Length of buffers. */ +#define MAX_DELAY 100 /* Counter parameters */ -#define CONV_LEN 512 /* Convergence length used at startup. */ -#define CONV_LEN2 (CONV_LEN << 1) /* Used at startup. */ +#define CONV_LEN 512 /* Convergence length used at startup. */ +#define CONV_LEN2 (CONV_LEN << 1) /* Used at startup. */ /* Energy parameters */ -#define MAX_BUF_LEN 64 /* History length of energy signals. */ -#define FAR_ENERGY_MIN 1025 /* Lowest Far energy level: At least 2 */ - /* in energy. */ -#define FAR_ENERGY_DIFF 929 /* Allowed difference between max */ - /* and min. */ -#define ENERGY_DEV_OFFSET 0 /* The energy error offset in Q8. */ -#define ENERGY_DEV_TOL 400 /* The energy estimation tolerance (Q8). */ -#define FAR_ENERGY_VAD_REGION 230 /* Far VAD tolerance region. */ +#define MAX_BUF_LEN 64 /* History length of energy signals. */ +#define FAR_ENERGY_MIN 1025 /* Lowest Far energy level: At least 2 */ + /* in energy. */ +#define FAR_ENERGY_DIFF 929 /* Allowed difference between max */ + /* and min. */ +#define ENERGY_DEV_OFFSET 0 /* The energy error offset in Q8. */ +#define ENERGY_DEV_TOL 400 /* The energy estimation tolerance (Q8). */ +#define FAR_ENERGY_VAD_REGION 230 /* Far VAD tolerance region. */ /* Stepsize parameters */ -#define MU_MIN 10 /* Min stepsize 2^-MU_MIN (far end energy */ - /* dependent). */ -#define MU_MAX 1 /* Max stepsize 2^-MU_MAX (far end energy */ - /* dependent). */ -#define MU_DIFF 9 /* MU_MIN - MU_MAX */ +#define MU_MIN 10 /* Min stepsize 2^-MU_MIN (far end energy */ + /* dependent). */ +#define MU_MAX 1 /* Max stepsize 2^-MU_MAX (far end energy */ + /* dependent). */ +#define MU_DIFF 9 /* MU_MIN - MU_MAX */ /* Channel parameters */ -#define MIN_MSE_COUNT 20 /* Min number of consecutive blocks with enough */ - /* far end energy to compare channel estimates. */ -#define MIN_MSE_DIFF 29 /* The ratio between adapted and stored channel to */ - /* accept a new storage (0.8 in Q-MSE_RESOLUTION). */ -#define MSE_RESOLUTION 5 /* MSE parameter resolution. */ -#define RESOLUTION_CHANNEL16 12 /* W16 Channel in Q-RESOLUTION_CHANNEL16. */ -#define RESOLUTION_CHANNEL32 28 /* W32 Channel in Q-RESOLUTION_CHANNEL. */ -#define CHANNEL_VAD 16 /* Minimum energy in frequency band */ - /* to update channel. */ +#define MIN_MSE_COUNT 20 /* Min number of consecutive blocks with enough */ + /* far end energy to compare channel estimates. */ +#define MIN_MSE_DIFF 29 /* The ratio between adapted and stored channel to */ + /* accept a new storage (0.8 in Q-MSE_RESOLUTION). */ +#define MSE_RESOLUTION 5 /* MSE parameter resolution. */ +#define RESOLUTION_CHANNEL16 12 /* W16 Channel in Q-RESOLUTION_CHANNEL16. */ +#define RESOLUTION_CHANNEL32 28 /* W32 Channel in Q-RESOLUTION_CHANNEL. */ +#define CHANNEL_VAD 16 /* Minimum energy in frequency band */ + /* to update channel. */ /* Suppression gain parameters: SUPGAIN parameters in Q-(RESOLUTION_SUPGAIN). */ -#define RESOLUTION_SUPGAIN 8 /* Channel in Q-(RESOLUTION_SUPGAIN). */ -#define SUPGAIN_DEFAULT (1 << RESOLUTION_SUPGAIN) /* Default. */ -#define SUPGAIN_ERROR_PARAM_A 3072 /* Estimation error parameter */ - /* (Maximum gain) (8 in Q8). */ -#define SUPGAIN_ERROR_PARAM_B 1536 /* Estimation error parameter */ - /* (Gain before going down). */ -#define SUPGAIN_ERROR_PARAM_D SUPGAIN_DEFAULT /* Estimation error parameter */ - /* (Should be the same as Default) (1 in Q8). */ -#define SUPGAIN_EPC_DT 200 /* SUPGAIN_ERROR_PARAM_C * ENERGY_DEV_TOL */ +#define RESOLUTION_SUPGAIN 8 /* Channel in Q-(RESOLUTION_SUPGAIN). */ +#define SUPGAIN_DEFAULT (1 << RESOLUTION_SUPGAIN) /* Default. */ +#define SUPGAIN_ERROR_PARAM_A 3072 /* Estimation error parameter */ + /* (Maximum gain) (8 in Q8). */ +#define SUPGAIN_ERROR_PARAM_B 1536 /* Estimation error parameter */ + /* (Gain before going down). */ +#define SUPGAIN_ERROR_PARAM_D SUPGAIN_DEFAULT /* Estimation error parameter */ +/* (Should be the same as Default) (1 in Q8). */ +#define SUPGAIN_EPC_DT 200 /* SUPGAIN_ERROR_PARAM_C * ENERGY_DEV_TOL */ /* Defines for "check delay estimation" */ -#define CORR_WIDTH 31 /* Number of samples to correlate over. */ -#define CORR_MAX 16 /* Maximum correlation offset. */ -#define CORR_MAX_BUF 63 -#define CORR_DEV 4 -#define CORR_MAX_LEVEL 20 -#define CORR_MAX_LOW 4 -#define CORR_BUF_LEN (CORR_MAX << 1) + 1 +#define CORR_WIDTH 31 /* Number of samples to correlate over. */ +#define CORR_MAX 16 /* Maximum correlation offset. */ +#define CORR_MAX_BUF 63 +#define CORR_DEV 4 +#define CORR_MAX_LEVEL 20 +#define CORR_MAX_LOW 4 +#define CORR_BUF_LEN (CORR_MAX << 1) + 1 /* Note that CORR_WIDTH + 2*CORR_MAX <= MAX_BUF_LEN. */ -#define ONE_Q14 (1 << 14) +#define ONE_Q14 (1 << 14) /* NLP defines */ -#define NLP_COMP_LOW 3277 /* 0.2 in Q14 */ -#define NLP_COMP_HIGH ONE_Q14 /* 1 in Q14 */ +#define NLP_COMP_LOW 3277 /* 0.2 in Q14 */ +#define NLP_COMP_HIGH ONE_Q14 /* 1 in Q14 */ #endif diff --git a/webrtc/modules/audio_processing/aecm/echo_control_mobile.c b/webrtc/modules/audio_processing/aecm/echo_control_mobile.c deleted file mode 100644 index 83781e9..0000000 --- a/webrtc/modules/audio_processing/aecm/echo_control_mobile.c +++ /dev/null @@ -1,702 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h" - -#ifdef AEC_DEBUG -#include -#endif -#include - -#include "webrtc/common_audio/ring_buffer.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/aecm/aecm_core.h" - -#define BUF_SIZE_FRAMES 50 // buffer size (frames) -// Maximum length of resampled signal. Must be an integer multiple of frames -// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN -// The factor of 2 handles wb, and the + 1 is as a safety margin -#define MAX_RESAMP_LEN (5 * FRAME_LEN) - -static const size_t kBufSizeSamp = BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples) -static const int kSampMsNb = 8; // samples per ms in nb -// Target suppression levels for nlp modes -// log{0.001, 0.00001, 0.00000001} -static const int kInitCheck = 42; - -typedef struct -{ - int sampFreq; - int scSampFreq; - short bufSizeStart; - int knownDelay; - - // Stores the last frame added to the farend buffer - short farendOld[2][FRAME_LEN]; - short initFlag; // indicates if AEC has been initialized - - // Variables used for averaging far end buffer size - short counter; - short sum; - short firstVal; - short checkBufSizeCtr; - - // Variables used for delay shifts - short msInSndCardBuf; - short filtDelay; - int timeForDelayChange; - int ECstartup; - int checkBuffSize; - int delayChange; - short lastDelayDiff; - - int16_t echoMode; - -#ifdef AEC_DEBUG - FILE *bufFile; - FILE *delayFile; - FILE *preCompFile; - FILE *postCompFile; -#endif // AEC_DEBUG - // Structures - RingBuffer *farendBuf; - - int lastError; - - AecmCore* aecmCore; -} AecMobile; - -// Estimates delay to set the position of the farend buffer read pointer -// (controlled by knownDelay) -static int WebRtcAecm_EstBufDelay(AecMobile* aecmInst, short msInSndCardBuf); - -// Stuffs the farend buffer if the estimated delay is too large -static int WebRtcAecm_DelayComp(AecMobile* aecmInst); - -void* WebRtcAecm_Create() { - AecMobile* aecm = malloc(sizeof(AecMobile)); - - WebRtcSpl_Init(); - - aecm->aecmCore = WebRtcAecm_CreateCore(); - if (!aecm->aecmCore) { - WebRtcAecm_Free(aecm); - return NULL; - } - - aecm->farendBuf = WebRtc_CreateBuffer(kBufSizeSamp, - sizeof(int16_t)); - if (!aecm->farendBuf) - { - WebRtcAecm_Free(aecm); - return NULL; - } - - aecm->initFlag = 0; - aecm->lastError = 0; - -#ifdef AEC_DEBUG - aecm->aecmCore->farFile = fopen("aecFar.pcm","wb"); - aecm->aecmCore->nearFile = fopen("aecNear.pcm","wb"); - aecm->aecmCore->outFile = fopen("aecOut.pcm","wb"); - //aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb"); - - aecm->bufFile = fopen("aecBuf.dat", "wb"); - aecm->delayFile = fopen("aecDelay.dat", "wb"); - aecm->preCompFile = fopen("preComp.pcm", "wb"); - aecm->postCompFile = fopen("postComp.pcm", "wb"); -#endif // AEC_DEBUG - return aecm; -} - -void WebRtcAecm_Free(void* aecmInst) { - AecMobile* aecm = aecmInst; - - if (aecm == NULL) { - return; - } - -#ifdef AEC_DEBUG - fclose(aecm->aecmCore->farFile); - fclose(aecm->aecmCore->nearFile); - fclose(aecm->aecmCore->outFile); - //fclose(aecm->aecmCore->outLpFile); - - fclose(aecm->bufFile); - fclose(aecm->delayFile); - fclose(aecm->preCompFile); - fclose(aecm->postCompFile); -#endif // AEC_DEBUG - WebRtcAecm_FreeCore(aecm->aecmCore); - WebRtc_FreeBuffer(aecm->farendBuf); - free(aecm); -} - -int32_t WebRtcAecm_Init(void *aecmInst, int32_t sampFreq) -{ - AecMobile* aecm = aecmInst; - AecmConfig aecConfig; - - if (aecm == NULL) - { - return -1; - } - - if (sampFreq != 8000 && sampFreq != 16000) - { - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - aecm->sampFreq = sampFreq; - - // Initialize AECM core - if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1) - { - aecm->lastError = AECM_UNSPECIFIED_ERROR; - return -1; - } - - // Initialize farend buffer - WebRtc_InitBuffer(aecm->farendBuf); - - aecm->initFlag = kInitCheck; // indicates that initialization has been done - - aecm->delayChange = 1; - - aecm->sum = 0; - aecm->counter = 0; - aecm->checkBuffSize = 1; - aecm->firstVal = 0; - - aecm->ECstartup = 1; - aecm->bufSizeStart = 0; - aecm->checkBufSizeCtr = 0; - aecm->filtDelay = 0; - aecm->timeForDelayChange = 0; - aecm->knownDelay = 0; - aecm->lastDelayDiff = 0; - - memset(&aecm->farendOld[0][0], 0, 160); - - // Default settings. - aecConfig.cngMode = AecmTrue; - aecConfig.echoMode = 3; - - if (WebRtcAecm_set_config(aecm, aecConfig) == -1) - { - aecm->lastError = AECM_UNSPECIFIED_ERROR; - return -1; - } - - return 0; -} - -int32_t WebRtcAecm_BufferFarend(void *aecmInst, const int16_t *farend, - size_t nrOfSamples) -{ - AecMobile* aecm = aecmInst; - int32_t retVal = 0; - - if (aecm == NULL) - { - return -1; - } - - if (farend == NULL) - { - aecm->lastError = AECM_NULL_POINTER_ERROR; - return -1; - } - - if (aecm->initFlag != kInitCheck) - { - aecm->lastError = AECM_UNINITIALIZED_ERROR; - return -1; - } - - if (nrOfSamples != 80 && nrOfSamples != 160) - { - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - - // TODO: Is this really a good idea? - if (!aecm->ECstartup) - { - WebRtcAecm_DelayComp(aecm); - } - - WebRtc_WriteBuffer(aecm->farendBuf, farend, nrOfSamples); - - return retVal; -} - -int32_t WebRtcAecm_Process(void *aecmInst, const int16_t *nearendNoisy, - const int16_t *nearendClean, int16_t *out, - size_t nrOfSamples, int16_t msInSndCardBuf) -{ - AecMobile* aecm = aecmInst; - int32_t retVal = 0; - size_t i; - short nmbrOfFilledBuffers; - size_t nBlocks10ms; - size_t nFrames; -#ifdef AEC_DEBUG - short msInAECBuf; -#endif - - if (aecm == NULL) - { - return -1; - } - - if (nearendNoisy == NULL) - { - aecm->lastError = AECM_NULL_POINTER_ERROR; - return -1; - } - - if (out == NULL) - { - aecm->lastError = AECM_NULL_POINTER_ERROR; - return -1; - } - - if (aecm->initFlag != kInitCheck) - { - aecm->lastError = AECM_UNINITIALIZED_ERROR; - return -1; - } - - if (nrOfSamples != 80 && nrOfSamples != 160) - { - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - - if (msInSndCardBuf < 0) - { - msInSndCardBuf = 0; - aecm->lastError = AECM_BAD_PARAMETER_WARNING; - retVal = -1; - } else if (msInSndCardBuf > 500) - { - msInSndCardBuf = 500; - aecm->lastError = AECM_BAD_PARAMETER_WARNING; - retVal = -1; - } - msInSndCardBuf += 10; - aecm->msInSndCardBuf = msInSndCardBuf; - - nFrames = nrOfSamples / FRAME_LEN; - nBlocks10ms = nFrames / aecm->aecmCore->mult; - - if (aecm->ECstartup) - { - if (nearendClean == NULL) - { - if (out != nearendNoisy) - { - memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples); - } - } else if (out != nearendClean) - { - memcpy(out, nearendClean, sizeof(short) * nrOfSamples); - } - - nmbrOfFilledBuffers = - (short) WebRtc_available_read(aecm->farendBuf) / FRAME_LEN; - // The AECM is in the start up mode - // AECM is disabled until the soundcard buffer and farend buffers are OK - - // Mechanism to ensure that the soundcard buffer is reasonably stable. - if (aecm->checkBuffSize) - { - aecm->checkBufSizeCtr++; - // Before we fill up the far end buffer we require the amount of data on the - // sound card to be stable (+/-8 ms) compared to the first value. This - // comparison is made during the following 4 consecutive frames. If it seems - // to be stable then we start to fill up the far end buffer. - - if (aecm->counter == 0) - { - aecm->firstVal = aecm->msInSndCardBuf; - aecm->sum = 0; - } - - if (abs(aecm->firstVal - aecm->msInSndCardBuf) - < WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb)) - { - aecm->sum += aecm->msInSndCardBuf; - aecm->counter++; - } else - { - aecm->counter = 0; - } - - if (aecm->counter * nBlocks10ms >= 6) - { - // The farend buffer size is determined in blocks of 80 samples - // Use 75% of the average value of the soundcard buffer - aecm->bufSizeStart - = WEBRTC_SPL_MIN((3 * aecm->sum - * aecm->aecmCore->mult) / (aecm->counter * 40), BUF_SIZE_FRAMES); - // buffersize has now been determined - aecm->checkBuffSize = 0; - } - - if (aecm->checkBufSizeCtr * nBlocks10ms > 50) - { - // for really bad sound cards, don't disable echocanceller for more than 0.5 sec - aecm->bufSizeStart = WEBRTC_SPL_MIN((3 * aecm->msInSndCardBuf - * aecm->aecmCore->mult) / 40, BUF_SIZE_FRAMES); - aecm->checkBuffSize = 0; - } - } - - // if checkBuffSize changed in the if-statement above - if (!aecm->checkBuffSize) - { - // soundcard buffer is now reasonably stable - // When the far end buffer is filled with approximately the same amount of - // data as the amount on the sound card we end the start up phase and start - // to cancel echoes. - - if (nmbrOfFilledBuffers == aecm->bufSizeStart) - { - aecm->ECstartup = 0; // Enable the AECM - } else if (nmbrOfFilledBuffers > aecm->bufSizeStart) - { - WebRtc_MoveReadPtr(aecm->farendBuf, - (int) WebRtc_available_read(aecm->farendBuf) - - (int) aecm->bufSizeStart * FRAME_LEN); - aecm->ECstartup = 0; - } - } - - } else - { - // AECM is enabled - - // Note only 1 block supported for nb and 2 blocks for wb - for (i = 0; i < nFrames; i++) - { - int16_t farend[FRAME_LEN]; - const int16_t* farend_ptr = NULL; - - nmbrOfFilledBuffers = - (short) WebRtc_available_read(aecm->farendBuf) / FRAME_LEN; - - // Check that there is data in the far end buffer - if (nmbrOfFilledBuffers > 0) - { - // Get the next 80 samples from the farend buffer - WebRtc_ReadBuffer(aecm->farendBuf, (void**) &farend_ptr, farend, - FRAME_LEN); - - // Always store the last frame for use when we run out of data - memcpy(&(aecm->farendOld[i][0]), farend_ptr, - FRAME_LEN * sizeof(short)); - } else - { - // We have no data so we use the last played frame - memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short)); - farend_ptr = farend; - } - - // Call buffer delay estimator when all data is extracted, - // i,e. i = 0 for NB and i = 1 for WB - if ((i == 0 && aecm->sampFreq == 8000) || (i == 1 && aecm->sampFreq == 16000)) - { - WebRtcAecm_EstBufDelay(aecm, aecm->msInSndCardBuf); - } - - // Call the AECM - /*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i], - &out[FRAME_LEN * i], aecm->knownDelay);*/ - if (WebRtcAecm_ProcessFrame(aecm->aecmCore, - farend_ptr, - &nearendNoisy[FRAME_LEN * i], - (nearendClean - ? &nearendClean[FRAME_LEN * i] - : NULL), - &out[FRAME_LEN * i]) == -1) - return -1; - } - } - -#ifdef AEC_DEBUG - msInAECBuf = (short) WebRtc_available_read(aecm->farendBuf) / - (kSampMsNb * aecm->aecmCore->mult); - fwrite(&msInAECBuf, 2, 1, aecm->bufFile); - fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile); -#endif - - return retVal; -} - -int32_t WebRtcAecm_set_config(void *aecmInst, AecmConfig config) -{ - AecMobile* aecm = aecmInst; - - if (aecm == NULL) - { - return -1; - } - - if (aecm->initFlag != kInitCheck) - { - aecm->lastError = AECM_UNINITIALIZED_ERROR; - return -1; - } - - if (config.cngMode != AecmFalse && config.cngMode != AecmTrue) - { - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - aecm->aecmCore->cngMode = config.cngMode; - - if (config.echoMode < 0 || config.echoMode > 4) - { - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - aecm->echoMode = config.echoMode; - - if (aecm->echoMode == 0) - { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3; - aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 3) - - (SUPGAIN_ERROR_PARAM_B >> 3); - aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 3) - - (SUPGAIN_ERROR_PARAM_D >> 3); - } else if (aecm->echoMode == 1) - { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2; - aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 2) - - (SUPGAIN_ERROR_PARAM_B >> 2); - aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 2) - - (SUPGAIN_ERROR_PARAM_D >> 2); - } else if (aecm->echoMode == 2) - { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1; - aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 1) - - (SUPGAIN_ERROR_PARAM_B >> 1); - aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 1) - - (SUPGAIN_ERROR_PARAM_D >> 1); - } else if (aecm->echoMode == 3) - { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D; - aecm->aecmCore->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B; - aecm->aecmCore->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D; - } else if (aecm->echoMode == 4) - { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1; - aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A << 1) - - (SUPGAIN_ERROR_PARAM_B << 1); - aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B << 1) - - (SUPGAIN_ERROR_PARAM_D << 1); - } - - return 0; -} - -int32_t WebRtcAecm_get_config(void *aecmInst, AecmConfig *config) -{ - AecMobile* aecm = aecmInst; - - if (aecm == NULL) - { - return -1; - } - - if (config == NULL) - { - aecm->lastError = AECM_NULL_POINTER_ERROR; - return -1; - } - - if (aecm->initFlag != kInitCheck) - { - aecm->lastError = AECM_UNINITIALIZED_ERROR; - return -1; - } - - config->cngMode = aecm->aecmCore->cngMode; - config->echoMode = aecm->echoMode; - - return 0; -} - -int32_t WebRtcAecm_InitEchoPath(void* aecmInst, - const void* echo_path, - size_t size_bytes) -{ - AecMobile* aecm = aecmInst; - const int16_t* echo_path_ptr = echo_path; - - if (aecmInst == NULL) { - return -1; - } - if (echo_path == NULL) { - aecm->lastError = AECM_NULL_POINTER_ERROR; - return -1; - } - if (size_bytes != WebRtcAecm_echo_path_size_bytes()) - { - // Input channel size does not match the size of AECM - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - if (aecm->initFlag != kInitCheck) - { - aecm->lastError = AECM_UNINITIALIZED_ERROR; - return -1; - } - - WebRtcAecm_InitEchoPathCore(aecm->aecmCore, echo_path_ptr); - - return 0; -} - -int32_t WebRtcAecm_GetEchoPath(void* aecmInst, - void* echo_path, - size_t size_bytes) -{ - AecMobile* aecm = aecmInst; - int16_t* echo_path_ptr = echo_path; - - if (aecmInst == NULL) { - return -1; - } - if (echo_path == NULL) { - aecm->lastError = AECM_NULL_POINTER_ERROR; - return -1; - } - if (size_bytes != WebRtcAecm_echo_path_size_bytes()) - { - // Input channel size does not match the size of AECM - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - if (aecm->initFlag != kInitCheck) - { - aecm->lastError = AECM_UNINITIALIZED_ERROR; - return -1; - } - - memcpy(echo_path_ptr, aecm->aecmCore->channelStored, size_bytes); - return 0; -} - -size_t WebRtcAecm_echo_path_size_bytes() -{ - return (PART_LEN1 * sizeof(int16_t)); -} - -int32_t WebRtcAecm_get_error_code(void *aecmInst) -{ - AecMobile* aecm = aecmInst; - - if (aecm == NULL) - { - return -1; - } - - return aecm->lastError; -} - -static int WebRtcAecm_EstBufDelay(AecMobile* aecm, short msInSndCardBuf) { - short delayNew, nSampSndCard; - short nSampFar = (short) WebRtc_available_read(aecm->farendBuf); - short diff; - - nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; - - delayNew = nSampSndCard - nSampFar; - - if (delayNew < FRAME_LEN) - { - WebRtc_MoveReadPtr(aecm->farendBuf, FRAME_LEN); - delayNew += FRAME_LEN; - } - - aecm->filtDelay = WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10); - - diff = aecm->filtDelay - aecm->knownDelay; - if (diff > 224) - { - if (aecm->lastDelayDiff < 96) - { - aecm->timeForDelayChange = 0; - } else - { - aecm->timeForDelayChange++; - } - } else if (diff < 96 && aecm->knownDelay > 0) - { - if (aecm->lastDelayDiff > 224) - { - aecm->timeForDelayChange = 0; - } else - { - aecm->timeForDelayChange++; - } - } else - { - aecm->timeForDelayChange = 0; - } - aecm->lastDelayDiff = diff; - - if (aecm->timeForDelayChange > 25) - { - aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0); - } - return 0; -} - -static int WebRtcAecm_DelayComp(AecMobile* aecm) { - int nSampFar = (int) WebRtc_available_read(aecm->farendBuf); - int nSampSndCard, delayNew, nSampAdd; - const int maxStuffSamp = 10 * FRAME_LEN; - - nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; - delayNew = nSampSndCard - nSampFar; - - if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult) - { - // The difference of the buffer sizes is larger than the maximum - // allowed known delay. Compensate by stuffing the buffer. - nSampAdd = (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar), - FRAME_LEN)); - nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp); - - WebRtc_MoveReadPtr(aecm->farendBuf, -nSampAdd); - aecm->delayChange = 1; // the delay needs to be updated - } - - return 0; -} diff --git a/webrtc/modules/audio_processing/aecm/echo_control_mobile.cc b/webrtc/modules/audio_processing/aecm/echo_control_mobile.cc new file mode 100644 index 0000000..14522c0 --- /dev/null +++ b/webrtc/modules/audio_processing/aecm/echo_control_mobile.cc @@ -0,0 +1,599 @@ +/* + * Copyright (c) 2012 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 "modules/audio_processing/aecm/echo_control_mobile.h" + +#ifdef AEC_DEBUG +#include +#endif +#include +#include + +extern "C" { +#include "common_audio/ring_buffer.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "modules/audio_processing/aecm/aecm_defines.h" +} +#include "modules/audio_processing/aecm/aecm_core.h" + +namespace webrtc { + +namespace { + +#define BUF_SIZE_FRAMES 50 // buffer size (frames) +// Maximum length of resampled signal. Must be an integer multiple of frames +// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN +// The factor of 2 handles wb, and the + 1 is as a safety margin +#define MAX_RESAMP_LEN (5 * FRAME_LEN) + +static const size_t kBufSizeSamp = + BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples) +static const int kSampMsNb = 8; // samples per ms in nb +// Target suppression levels for nlp modes +// log{0.001, 0.00001, 0.00000001} +static const int kInitCheck = 42; + +typedef struct { + int sampFreq; + int scSampFreq; + short bufSizeStart; + int knownDelay; + + // Stores the last frame added to the farend buffer + short farendOld[2][FRAME_LEN]; + short initFlag; // indicates if AEC has been initialized + + // Variables used for averaging far end buffer size + short counter; + short sum; + short firstVal; + short checkBufSizeCtr; + + // Variables used for delay shifts + short msInSndCardBuf; + short filtDelay; + int timeForDelayChange; + int ECstartup; + int checkBuffSize; + int delayChange; + short lastDelayDiff; + + int16_t echoMode; + +#ifdef AEC_DEBUG + FILE* bufFile; + FILE* delayFile; + FILE* preCompFile; + FILE* postCompFile; +#endif // AEC_DEBUG + // Structures + RingBuffer* farendBuf; + + AecmCore* aecmCore; +} AecMobile; + +} // namespace + +// Estimates delay to set the position of the farend buffer read pointer +// (controlled by knownDelay) +static int WebRtcAecm_EstBufDelay(AecMobile* aecm, short msInSndCardBuf); + +// Stuffs the farend buffer if the estimated delay is too large +static int WebRtcAecm_DelayComp(AecMobile* aecm); + +void* WebRtcAecm_Create() { + // Allocate zero-filled memory. + AecMobile* aecm = static_cast(calloc(1, sizeof(AecMobile))); + + aecm->aecmCore = WebRtcAecm_CreateCore(); + if (!aecm->aecmCore) { + WebRtcAecm_Free(aecm); + return NULL; + } + + aecm->farendBuf = WebRtc_CreateBuffer(kBufSizeSamp, sizeof(int16_t)); + if (!aecm->farendBuf) { + WebRtcAecm_Free(aecm); + return NULL; + } + +#ifdef AEC_DEBUG + aecm->aecmCore->farFile = fopen("aecFar.pcm", "wb"); + aecm->aecmCore->nearFile = fopen("aecNear.pcm", "wb"); + aecm->aecmCore->outFile = fopen("aecOut.pcm", "wb"); + // aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb"); + + aecm->bufFile = fopen("aecBuf.dat", "wb"); + aecm->delayFile = fopen("aecDelay.dat", "wb"); + aecm->preCompFile = fopen("preComp.pcm", "wb"); + aecm->postCompFile = fopen("postComp.pcm", "wb"); +#endif // AEC_DEBUG + return aecm; +} + +void WebRtcAecm_Free(void* aecmInst) { + AecMobile* aecm = static_cast(aecmInst); + + if (aecm == NULL) { + return; + } + +#ifdef AEC_DEBUG + fclose(aecm->aecmCore->farFile); + fclose(aecm->aecmCore->nearFile); + fclose(aecm->aecmCore->outFile); + // fclose(aecm->aecmCore->outLpFile); + + fclose(aecm->bufFile); + fclose(aecm->delayFile); + fclose(aecm->preCompFile); + fclose(aecm->postCompFile); +#endif // AEC_DEBUG + WebRtcAecm_FreeCore(aecm->aecmCore); + WebRtc_FreeBuffer(aecm->farendBuf); + free(aecm); +} + +int32_t WebRtcAecm_Init(void* aecmInst, int32_t sampFreq) { + AecMobile* aecm = static_cast(aecmInst); + AecmConfig aecConfig; + + if (aecm == NULL) { + return -1; + } + + if (sampFreq != 8000 && sampFreq != 16000) { + return AECM_BAD_PARAMETER_ERROR; + } + aecm->sampFreq = sampFreq; + + // Initialize AECM core + if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1) { + return AECM_UNSPECIFIED_ERROR; + } + + // Initialize farend buffer + WebRtc_InitBuffer(aecm->farendBuf); + + aecm->initFlag = kInitCheck; // indicates that initialization has been done + + aecm->delayChange = 1; + + aecm->sum = 0; + aecm->counter = 0; + aecm->checkBuffSize = 1; + aecm->firstVal = 0; + + aecm->ECstartup = 1; + aecm->bufSizeStart = 0; + aecm->checkBufSizeCtr = 0; + aecm->filtDelay = 0; + aecm->timeForDelayChange = 0; + aecm->knownDelay = 0; + aecm->lastDelayDiff = 0; + + memset(&aecm->farendOld, 0, sizeof(aecm->farendOld)); + + // Default settings. + aecConfig.cngMode = AecmTrue; + aecConfig.echoMode = 3; + + if (WebRtcAecm_set_config(aecm, aecConfig) == -1) { + return AECM_UNSPECIFIED_ERROR; + } + + return 0; +} + +// Returns any error that is caused when buffering the +// farend signal. +int32_t WebRtcAecm_GetBufferFarendError(void* aecmInst, + const int16_t* farend, + size_t nrOfSamples) { + AecMobile* aecm = static_cast(aecmInst); + + if (aecm == NULL) + return -1; + + if (farend == NULL) + return AECM_NULL_POINTER_ERROR; + + if (aecm->initFlag != kInitCheck) + return AECM_UNINITIALIZED_ERROR; + + if (nrOfSamples != 80 && nrOfSamples != 160) + return AECM_BAD_PARAMETER_ERROR; + + return 0; +} + +int32_t WebRtcAecm_BufferFarend(void* aecmInst, + const int16_t* farend, + size_t nrOfSamples) { + AecMobile* aecm = static_cast(aecmInst); + + const int32_t err = + WebRtcAecm_GetBufferFarendError(aecmInst, farend, nrOfSamples); + + if (err != 0) + return err; + + // TODO(unknown): Is this really a good idea? + if (!aecm->ECstartup) { + WebRtcAecm_DelayComp(aecm); + } + + WebRtc_WriteBuffer(aecm->farendBuf, farend, nrOfSamples); + + return 0; +} + +int32_t WebRtcAecm_Process(void* aecmInst, + const int16_t* nearendNoisy, + const int16_t* nearendClean, + int16_t* out, + size_t nrOfSamples, + int16_t msInSndCardBuf) { + AecMobile* aecm = static_cast(aecmInst); + int32_t retVal = 0; + size_t i; + short nmbrOfFilledBuffers; + size_t nBlocks10ms; + size_t nFrames; +#ifdef AEC_DEBUG + short msInAECBuf; +#endif + + if (aecm == NULL) { + return -1; + } + + if (nearendNoisy == NULL) { + return AECM_NULL_POINTER_ERROR; + } + + if (out == NULL) { + return AECM_NULL_POINTER_ERROR; + } + + if (aecm->initFlag != kInitCheck) { + return AECM_UNINITIALIZED_ERROR; + } + + if (nrOfSamples != 80 && nrOfSamples != 160) { + return AECM_BAD_PARAMETER_ERROR; + } + + if (msInSndCardBuf < 0) { + msInSndCardBuf = 0; + retVal = AECM_BAD_PARAMETER_WARNING; + } else if (msInSndCardBuf > 500) { + msInSndCardBuf = 500; + retVal = AECM_BAD_PARAMETER_WARNING; + } + msInSndCardBuf += 10; + aecm->msInSndCardBuf = msInSndCardBuf; + + nFrames = nrOfSamples / FRAME_LEN; + nBlocks10ms = nFrames / aecm->aecmCore->mult; + + if (aecm->ECstartup) { + if (nearendClean == NULL) { + if (out != nearendNoisy) { + memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples); + } + } else if (out != nearendClean) { + memcpy(out, nearendClean, sizeof(short) * nrOfSamples); + } + + nmbrOfFilledBuffers = + (short)WebRtc_available_read(aecm->farendBuf) / FRAME_LEN; + // The AECM is in the start up mode + // AECM is disabled until the soundcard buffer and farend buffers are OK + + // Mechanism to ensure that the soundcard buffer is reasonably stable. + if (aecm->checkBuffSize) { + aecm->checkBufSizeCtr++; + // Before we fill up the far end buffer we require the amount of data on + // the sound card to be stable (+/-8 ms) compared to the first value. This + // comparison is made during the following 4 consecutive frames. If it + // seems to be stable then we start to fill up the far end buffer. + + if (aecm->counter == 0) { + aecm->firstVal = aecm->msInSndCardBuf; + aecm->sum = 0; + } + + if (abs(aecm->firstVal - aecm->msInSndCardBuf) < + WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb)) { + aecm->sum += aecm->msInSndCardBuf; + aecm->counter++; + } else { + aecm->counter = 0; + } + + if (aecm->counter * nBlocks10ms >= 6) { + // The farend buffer size is determined in blocks of 80 samples + // Use 75% of the average value of the soundcard buffer + aecm->bufSizeStart = WEBRTC_SPL_MIN( + (3 * aecm->sum * aecm->aecmCore->mult) / (aecm->counter * 40), + BUF_SIZE_FRAMES); + // buffersize has now been determined + aecm->checkBuffSize = 0; + } + + if (aecm->checkBufSizeCtr * nBlocks10ms > 50) { + // for really bad sound cards, don't disable echocanceller for more than + // 0.5 sec + aecm->bufSizeStart = WEBRTC_SPL_MIN( + (3 * aecm->msInSndCardBuf * aecm->aecmCore->mult) / 40, + BUF_SIZE_FRAMES); + aecm->checkBuffSize = 0; + } + } + + // if checkBuffSize changed in the if-statement above + if (!aecm->checkBuffSize) { + // soundcard buffer is now reasonably stable + // When the far end buffer is filled with approximately the same amount of + // data as the amount on the sound card we end the start up phase and + // start to cancel echoes. + + if (nmbrOfFilledBuffers == aecm->bufSizeStart) { + aecm->ECstartup = 0; // Enable the AECM + } else if (nmbrOfFilledBuffers > aecm->bufSizeStart) { + WebRtc_MoveReadPtr(aecm->farendBuf, + (int)WebRtc_available_read(aecm->farendBuf) - + (int)aecm->bufSizeStart * FRAME_LEN); + aecm->ECstartup = 0; + } + } + + } else { + // AECM is enabled + + // Note only 1 block supported for nb and 2 blocks for wb + for (i = 0; i < nFrames; i++) { + int16_t farend[FRAME_LEN]; + const int16_t* farend_ptr = NULL; + + nmbrOfFilledBuffers = + (short)WebRtc_available_read(aecm->farendBuf) / FRAME_LEN; + + // Check that there is data in the far end buffer + if (nmbrOfFilledBuffers > 0) { + // Get the next 80 samples from the farend buffer + WebRtc_ReadBuffer(aecm->farendBuf, (void**)&farend_ptr, farend, + FRAME_LEN); + + // Always store the last frame for use when we run out of data + memcpy(&(aecm->farendOld[i][0]), farend_ptr, FRAME_LEN * sizeof(short)); + } else { + // We have no data so we use the last played frame + memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short)); + farend_ptr = farend; + } + + // Call buffer delay estimator when all data is extracted, + // i,e. i = 0 for NB and i = 1 for WB + if ((i == 0 && aecm->sampFreq == 8000) || + (i == 1 && aecm->sampFreq == 16000)) { + WebRtcAecm_EstBufDelay(aecm, aecm->msInSndCardBuf); + } + + // Call the AECM + /*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i], + &out[FRAME_LEN * i], aecm->knownDelay);*/ + if (WebRtcAecm_ProcessFrame( + aecm->aecmCore, farend_ptr, &nearendNoisy[FRAME_LEN * i], + (nearendClean ? &nearendClean[FRAME_LEN * i] : NULL), + &out[FRAME_LEN * i]) == -1) + return -1; + } + } + +#ifdef AEC_DEBUG + msInAECBuf = (short)WebRtc_available_read(aecm->farendBuf) / + (kSampMsNb * aecm->aecmCore->mult); + fwrite(&msInAECBuf, 2, 1, aecm->bufFile); + fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile); +#endif + + return retVal; +} + +int32_t WebRtcAecm_set_config(void* aecmInst, AecmConfig config) { + AecMobile* aecm = static_cast(aecmInst); + + if (aecm == NULL) { + return -1; + } + + if (aecm->initFlag != kInitCheck) { + return AECM_UNINITIALIZED_ERROR; + } + + if (config.cngMode != AecmFalse && config.cngMode != AecmTrue) { + return AECM_BAD_PARAMETER_ERROR; + } + aecm->aecmCore->cngMode = config.cngMode; + + if (config.echoMode < 0 || config.echoMode > 4) { + return AECM_BAD_PARAMETER_ERROR; + } + aecm->echoMode = config.echoMode; + + if (aecm->echoMode == 0) { + aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3; + aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3; + aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3; + aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3; + aecm->aecmCore->supGainErrParamDiffAB = + (SUPGAIN_ERROR_PARAM_A >> 3) - (SUPGAIN_ERROR_PARAM_B >> 3); + aecm->aecmCore->supGainErrParamDiffBD = + (SUPGAIN_ERROR_PARAM_B >> 3) - (SUPGAIN_ERROR_PARAM_D >> 3); + } else if (aecm->echoMode == 1) { + aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2; + aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2; + aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2; + aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2; + aecm->aecmCore->supGainErrParamDiffAB = + (SUPGAIN_ERROR_PARAM_A >> 2) - (SUPGAIN_ERROR_PARAM_B >> 2); + aecm->aecmCore->supGainErrParamDiffBD = + (SUPGAIN_ERROR_PARAM_B >> 2) - (SUPGAIN_ERROR_PARAM_D >> 2); + } else if (aecm->echoMode == 2) { + aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1; + aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1; + aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1; + aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1; + aecm->aecmCore->supGainErrParamDiffAB = + (SUPGAIN_ERROR_PARAM_A >> 1) - (SUPGAIN_ERROR_PARAM_B >> 1); + aecm->aecmCore->supGainErrParamDiffBD = + (SUPGAIN_ERROR_PARAM_B >> 1) - (SUPGAIN_ERROR_PARAM_D >> 1); + } else if (aecm->echoMode == 3) { + aecm->aecmCore->supGain = SUPGAIN_DEFAULT; + aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT; + aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A; + aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D; + aecm->aecmCore->supGainErrParamDiffAB = + SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B; + aecm->aecmCore->supGainErrParamDiffBD = + SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D; + } else if (aecm->echoMode == 4) { + aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1; + aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1; + aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1; + aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1; + aecm->aecmCore->supGainErrParamDiffAB = + (SUPGAIN_ERROR_PARAM_A << 1) - (SUPGAIN_ERROR_PARAM_B << 1); + aecm->aecmCore->supGainErrParamDiffBD = + (SUPGAIN_ERROR_PARAM_B << 1) - (SUPGAIN_ERROR_PARAM_D << 1); + } + + return 0; +} + +int32_t WebRtcAecm_InitEchoPath(void* aecmInst, + const void* echo_path, + size_t size_bytes) { + AecMobile* aecm = static_cast(aecmInst); + const int16_t* echo_path_ptr = static_cast(echo_path); + + if (aecmInst == NULL) { + return -1; + } + if (echo_path == NULL) { + return AECM_NULL_POINTER_ERROR; + } + if (size_bytes != WebRtcAecm_echo_path_size_bytes()) { + // Input channel size does not match the size of AECM + return AECM_BAD_PARAMETER_ERROR; + } + if (aecm->initFlag != kInitCheck) { + return AECM_UNINITIALIZED_ERROR; + } + + WebRtcAecm_InitEchoPathCore(aecm->aecmCore, echo_path_ptr); + + return 0; +} + +int32_t WebRtcAecm_GetEchoPath(void* aecmInst, + void* echo_path, + size_t size_bytes) { + AecMobile* aecm = static_cast(aecmInst); + int16_t* echo_path_ptr = static_cast(echo_path); + + if (aecmInst == NULL) { + return -1; + } + if (echo_path == NULL) { + return AECM_NULL_POINTER_ERROR; + } + if (size_bytes != WebRtcAecm_echo_path_size_bytes()) { + // Input channel size does not match the size of AECM + return AECM_BAD_PARAMETER_ERROR; + } + if (aecm->initFlag != kInitCheck) { + return AECM_UNINITIALIZED_ERROR; + } + + memcpy(echo_path_ptr, aecm->aecmCore->channelStored, size_bytes); + return 0; +} + +size_t WebRtcAecm_echo_path_size_bytes() { + return (PART_LEN1 * sizeof(int16_t)); +} + +static int WebRtcAecm_EstBufDelay(AecMobile* aecm, short msInSndCardBuf) { + short delayNew, nSampSndCard; + short nSampFar = (short)WebRtc_available_read(aecm->farendBuf); + short diff; + + nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; + + delayNew = nSampSndCard - nSampFar; + + if (delayNew < FRAME_LEN) { + WebRtc_MoveReadPtr(aecm->farendBuf, FRAME_LEN); + delayNew += FRAME_LEN; + } + + aecm->filtDelay = + WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10); + + diff = aecm->filtDelay - aecm->knownDelay; + if (diff > 224) { + if (aecm->lastDelayDiff < 96) { + aecm->timeForDelayChange = 0; + } else { + aecm->timeForDelayChange++; + } + } else if (diff < 96 && aecm->knownDelay > 0) { + if (aecm->lastDelayDiff > 224) { + aecm->timeForDelayChange = 0; + } else { + aecm->timeForDelayChange++; + } + } else { + aecm->timeForDelayChange = 0; + } + aecm->lastDelayDiff = diff; + + if (aecm->timeForDelayChange > 25) { + aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0); + } + return 0; +} + +static int WebRtcAecm_DelayComp(AecMobile* aecm) { + int nSampFar = (int)WebRtc_available_read(aecm->farendBuf); + int nSampSndCard, delayNew, nSampAdd; + const int maxStuffSamp = 10 * FRAME_LEN; + + nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; + delayNew = nSampSndCard - nSampFar; + + if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult) { + // The difference of the buffer sizes is larger than the maximum + // allowed known delay. Compensate by stuffing the buffer. + nSampAdd = + (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar), FRAME_LEN)); + nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp); + + WebRtc_MoveReadPtr(aecm->farendBuf, -nSampAdd); + aecm->delayChange = 1; // the delay needs to be updated + } + + return 0; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h b/webrtc/modules/audio_processing/aecm/echo_control_mobile.h similarity index 78% rename from webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h rename to webrtc/modules/audio_processing/aecm/echo_control_mobile.h index 7ae15c2..ee78052 100644 --- a/webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h +++ b/webrtc/modules/audio_processing/aecm/echo_control_mobile.h @@ -8,31 +8,29 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AECM_INCLUDE_ECHO_CONTROL_MOBILE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AECM_INCLUDE_ECHO_CONTROL_MOBILE_H_ +#ifndef MODULES_AUDIO_PROCESSING_AECM_ECHO_CONTROL_MOBILE_H_ +#define MODULES_AUDIO_PROCESSING_AECM_ECHO_CONTROL_MOBILE_H_ -#include +#include +#include -#include "webrtc/typedefs.h" +namespace webrtc { -enum { - AecmFalse = 0, - AecmTrue -}; +enum { AecmFalse = 0, AecmTrue }; // Errors -#define AECM_UNSPECIFIED_ERROR 12000 -#define AECM_UNSUPPORTED_FUNCTION_ERROR 12001 -#define AECM_UNINITIALIZED_ERROR 12002 -#define AECM_NULL_POINTER_ERROR 12003 -#define AECM_BAD_PARAMETER_ERROR 12004 +#define AECM_UNSPECIFIED_ERROR 12000 +#define AECM_UNSUPPORTED_FUNCTION_ERROR 12001 +#define AECM_UNINITIALIZED_ERROR 12002 +#define AECM_NULL_POINTER_ERROR 12003 +#define AECM_BAD_PARAMETER_ERROR 12004 // Warnings -#define AECM_BAD_PARAMETER_WARNING 12100 +#define AECM_BAD_PARAMETER_WARNING 12100 typedef struct { - int16_t cngMode; // AECM_FALSE, AECM_TRUE (default) - int16_t echoMode; // 0, 1, 2, 3 (default), 4 + int16_t cngMode; // AECM_FALSE, AECM_TRUE (default) + int16_t echoMode; // 0, 1, 2, 3 (default), 4 } AecmConfig; #ifdef __cplusplus @@ -66,7 +64,7 @@ void WebRtcAecm_Free(void* aecmInst); * Outputs Description * ------------------------------------------------------------------- * int32_t return 0: OK - * -1: error + * 1200-12004,12100: error/warning */ int32_t WebRtcAecm_Init(void* aecmInst, int32_t sampFreq); @@ -83,12 +81,31 @@ int32_t WebRtcAecm_Init(void* aecmInst, int32_t sampFreq); * Outputs Description * ------------------------------------------------------------------- * int32_t return 0: OK - * -1: error + * 1200-12004,12100: error/warning */ int32_t WebRtcAecm_BufferFarend(void* aecmInst, const int16_t* farend, size_t nrOfSamples); +/* + * Reports any errors that would arise when buffering a farend buffer. + * + * Inputs Description + * ------------------------------------------------------------------- + * void* aecmInst Pointer to the AECM instance + * int16_t* farend In buffer containing one frame of + * farend signal + * int16_t nrOfSamples Number of samples in farend buffer + * + * Outputs Description + * ------------------------------------------------------------------- + * int32_t return 0: OK + * 1200-12004,12100: error/warning + */ +int32_t WebRtcAecm_GetBufferFarendError(void* aecmInst, + const int16_t* farend, + size_t nrOfSamples); + /* * Runs the AECM on an 80 or 160 sample blocks of data. * @@ -112,7 +129,7 @@ int32_t WebRtcAecm_BufferFarend(void* aecmInst, * ------------------------------------------------------------------- * int16_t* out Out buffer, one frame of processed nearend * int32_t return 0: OK - * -1: error + * 1200-12004,12100: error/warning */ int32_t WebRtcAecm_Process(void* aecmInst, const int16_t* nearendNoisy, @@ -133,26 +150,10 @@ int32_t WebRtcAecm_Process(void* aecmInst, * Outputs Description * ------------------------------------------------------------------- * int32_t return 0: OK - * -1: error + * 1200-12004,12100: error/warning */ int32_t WebRtcAecm_set_config(void* aecmInst, AecmConfig config); -/* - * This function enables the user to set certain parameters on-the-fly - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecmInst Pointer to the AECM instance - * - * Outputs Description - * ------------------------------------------------------------------- - * AecmConfig* config Pointer to the config instance that - * all properties will be written to - * int32_t return 0: OK - * -1: error - */ -int32_t WebRtcAecm_get_config(void *aecmInst, AecmConfig *config); - /* * This function enables the user to set the echo path on-the-fly. * @@ -165,7 +166,7 @@ int32_t WebRtcAecm_get_config(void *aecmInst, AecmConfig *config); * Outputs Description * ------------------------------------------------------------------- * int32_t return 0: OK - * -1: error + * 1200-12004,12100: error/warning */ int32_t WebRtcAecm_InitEchoPath(void* aecmInst, const void* echo_path, @@ -184,7 +185,7 @@ int32_t WebRtcAecm_InitEchoPath(void* aecmInst, * Outputs Description * ------------------------------------------------------------------- * int32_t return 0: OK - * -1: error + * 1200-12004,12100: error/warning */ int32_t WebRtcAecm_GetEchoPath(void* aecmInst, void* echo_path, @@ -199,20 +200,10 @@ int32_t WebRtcAecm_GetEchoPath(void* aecmInst, */ size_t WebRtcAecm_echo_path_size_bytes(); -/* - * Gets the last error code. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecmInst Pointer to the AECM instance - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 11000-11100: error code - */ -int32_t WebRtcAecm_get_error_code(void *aecmInst); - #ifdef __cplusplus } #endif -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AECM_INCLUDE_ECHO_CONTROL_MOBILE_H_ + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AECM_ECHO_CONTROL_MOBILE_H_ diff --git a/webrtc/modules/audio_processing/agc/BUILD.gn b/webrtc/modules/audio_processing/agc/BUILD.gn new file mode 100644 index 0000000..8235456 --- /dev/null +++ b/webrtc/modules/audio_processing/agc/BUILD.gn @@ -0,0 +1,116 @@ +# Copyright (c) 2018 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. + +import("../../../webrtc.gni") + +rtc_source_set("gain_control_interface") { + sources = [ "gain_control.h" ] +} + +rtc_library("agc") { + sources = [ + "agc_manager_direct.cc", + "agc_manager_direct.h", + ] + configs += [ "..:apm_debug_dump" ] + deps = [ + ":gain_control_interface", + ":gain_map", + ":level_estimation", + "..:apm_logging", + "..:audio_buffer", + "../../../common_audio", + "../../../common_audio:common_audio_c", + "../../../rtc_base:checks", + "../../../rtc_base:gtest_prod", + "../../../rtc_base:logging", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_minmax", + "../../../system_wrappers:field_trial", + "../../../system_wrappers:metrics", + "../agc2:level_estimation_agc", + "../vad", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("level_estimation") { + sources = [ + "agc.cc", + "agc.h", + "loudness_histogram.cc", + "loudness_histogram.h", + "utility.cc", + "utility.h", + ] + deps = [ + "../../../rtc_base:checks", + "../vad", + ] +} + +rtc_library("legacy_agc") { + visibility = [ + ":*", + "..:*", + ] # Only targets in this file and in + # audio_processing can depend on + # this. + + sources = [ + "legacy/analog_agc.cc", + "legacy/analog_agc.h", + "legacy/digital_agc.cc", + "legacy/digital_agc.h", + "legacy/gain_control.h", + ] + + deps = [ + "../../../common_audio", + "../../../common_audio:common_audio_c", + "../../../common_audio/third_party/ooura:fft_size_256", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../../../system_wrappers", + ] + + if (rtc_build_with_neon) { + if (current_cpu != "arm64") { + # Enable compilation for the NEON instruction set. + suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] + cflags = [ "-mfpu=neon" ] + } + } +} + +rtc_source_set("gain_map") { + sources = [ "gain_map_internal.h" ] +} + +if (rtc_include_tests) { + rtc_library("agc_unittests") { + testonly = true + sources = [ + "agc_manager_direct_unittest.cc", + "loudness_histogram_unittest.cc", + "mock_agc.h", + ] + configs += [ "..:apm_debug_dump" ] + + deps = [ + ":agc", + ":gain_control_interface", + ":level_estimation", + "..:mocks", + "../../../test:field_trial", + "../../../test:fileutils", + "../../../test:test_support", + "//testing/gtest", + ] + } +} diff --git a/webrtc/modules/audio_processing/agc/agc.cc b/webrtc/modules/audio_processing/agc/agc.cc index 706b963..a89ae11 100644 --- a/webrtc/modules/audio_processing/agc/agc.cc +++ b/webrtc/modules/audio_processing/agc/agc.cc @@ -8,18 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/agc/agc.h" +#include "modules/audio_processing/agc/agc.h" #include #include - -#include #include -#include "webrtc/base/checks.h" -#include "webrtc/modules/audio_processing/agc/histogram.h" -#include "webrtc/modules/audio_processing/agc/utility.h" -#include "webrtc/modules/interface/module_common_types.h" +#include "modules/audio_processing/agc/loudness_histogram.h" +#include "modules/audio_processing/agc/utility.h" +#include "rtc_base/checks.h" namespace webrtc { namespace { @@ -33,23 +30,12 @@ const double kActivityThreshold = 0.3; Agc::Agc() : target_level_loudness_(Dbfs2Loudness(kDefaultLevelDbfs)), target_level_dbfs_(kDefaultLevelDbfs), - histogram_(Histogram::Create(kNumAnalysisFrames)), - inactive_histogram_(Histogram::Create()) { - } + histogram_(LoudnessHistogram::Create(kNumAnalysisFrames)), + inactive_histogram_(LoudnessHistogram::Create()) {} -Agc::~Agc() {} +Agc::~Agc() = default; -float Agc::AnalyzePreproc(const int16_t* audio, size_t length) { - assert(length > 0); - size_t num_clipped = 0; - for (size_t i = 0; i < length; ++i) { - if (audio[i] == 32767 || audio[i] == -32768) - ++num_clipped; - } - return 1.0f * num_clipped / length; -} - -int Agc::Process(const int16_t* audio, size_t length, int sample_rate_hz) { +void Agc::Process(const int16_t* audio, size_t length, int sample_rate_hz) { vad_.ProcessChunk(audio, length, sample_rate_hz); const std::vector& rms = vad_.chunkwise_rms(); const std::vector& probabilities = @@ -58,12 +44,11 @@ int Agc::Process(const int16_t* audio, size_t length, int sample_rate_hz) { for (size_t i = 0; i < rms.size(); ++i) { histogram_->Update(rms[i], probabilities[i]); } - return 0; } bool Agc::GetRmsErrorDb(int* error) { if (!error) { - assert(false); + RTC_NOTREACHED(); return false; } @@ -98,4 +83,12 @@ int Agc::set_target_level_dbfs(int level) { return 0; } +int Agc::target_level_dbfs() const { + return target_level_dbfs_; +} + +float Agc::voice_probability() const { + return vad_.last_voice_probability(); +} + } // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc/agc.h b/webrtc/modules/audio_processing/agc/agc.h index 08c287f..b9bd5ea 100644 --- a/webrtc/modules/audio_processing/agc/agc.h +++ b/webrtc/modules/audio_processing/agc/agc.h @@ -8,29 +8,25 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_AGC_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_AGC_H_ +#ifndef MODULES_AUDIO_PROCESSING_AGC_AGC_H_ +#define MODULES_AUDIO_PROCESSING_AGC_AGC_H_ -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/modules/audio_processing/vad/voice_activity_detector.h" -#include "webrtc/typedefs.h" +#include + +#include "modules/audio_processing/vad/voice_activity_detector.h" namespace webrtc { -class AudioFrame; -class Histogram; +class LoudnessHistogram; class Agc { public: Agc(); virtual ~Agc(); - // Returns the proportion of samples in the buffer which are at full-scale - // (and presumably clipped). - virtual float AnalyzePreproc(const int16_t* audio, size_t length); // |audio| must be mono; in a multi-channel stream, provide the first (usually // left) channel. - virtual int Process(const int16_t* audio, size_t length, int sample_rate_hz); + virtual void Process(const int16_t* audio, size_t length, int sample_rate_hz); // Retrieves the difference between the target RMS level and the current // signal RMS level in dB. Returns true if an update is available and false @@ -39,20 +35,17 @@ class Agc { virtual void Reset(); virtual int set_target_level_dbfs(int level); - virtual int target_level_dbfs() const { return target_level_dbfs_; } - - virtual float voice_probability() const { - return vad_.last_voice_probability(); - } + virtual int target_level_dbfs() const; + virtual float voice_probability() const; private: double target_level_loudness_; int target_level_dbfs_; - rtc::scoped_ptr histogram_; - rtc::scoped_ptr inactive_histogram_; + std::unique_ptr histogram_; + std::unique_ptr inactive_histogram_; VoiceActivityDetector vad_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_AGC_H_ +#endif // MODULES_AUDIO_PROCESSING_AGC_AGC_H_ diff --git a/webrtc/modules/audio_processing/agc/agc_manager_direct.cc b/webrtc/modules/audio_processing/agc/agc_manager_direct.cc index 867022d..1428d2a 100644 --- a/webrtc/modules/audio_processing/agc/agc_manager_direct.cc +++ b/webrtc/modules/audio_processing/agc/agc_manager_direct.cc @@ -8,26 +8,26 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/agc/agc_manager_direct.h" +#include "modules/audio_processing/agc/agc_manager_direct.h" -#include +#include #include -#ifdef WEBRTC_AGC_DEBUG_DUMP -#include -#endif - -#include "webrtc/modules/audio_processing/agc/gain_map_internal.h" -#include "webrtc/modules/audio_processing/gain_control_impl.h" -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/system_wrappers/include/logging.h" +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/agc/gain_control.h" +#include "modules/audio_processing/agc/gain_map_internal.h" +#include "modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "system_wrappers/include/field_trial.h" +#include "system_wrappers/include/metrics.h" namespace webrtc { namespace { -// Lowest the microphone level can be lowered due to clipping. -const int kClippedLevelMin = 170; // Amount the microphone level is lowered with every clipping event. const int kClippedLevelStep = 15; // Proportion of clipped samples required to declare a clipping event. @@ -56,185 +56,123 @@ const int kMaxResidualGainChange = 15; // restrictions from clipping events. const int kSurplusCompressionGain = 6; -int ClampLevel(int mic_level) { - return std::min(std::max(kMinMicLevel, mic_level), kMaxMicLevel); +// Returns whether a fall-back solution to choose the maximum level should be +// chosen. +bool UseMaxAnalogChannelLevel() { + return field_trial::IsEnabled("WebRTC-UseMaxAnalogAgcChannelLevel"); } -int LevelFromGainError(int gain_error, int level) { - assert(level >= 0 && level <= kMaxMicLevel); +// Returns kMinMicLevel if no field trial exists or if it has been disabled. +// Returns a value between 0 and 255 depending on the field-trial string. +// Example: 'WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-80' => returns 80. +int GetMinMicLevel() { + RTC_LOG(LS_INFO) << "[agc] GetMinMicLevel"; + constexpr char kMinMicLevelFieldTrial[] = + "WebRTC-Audio-AgcMinMicLevelExperiment"; + if (!webrtc::field_trial::IsEnabled(kMinMicLevelFieldTrial)) { + RTC_LOG(LS_INFO) << "[agc] Using default min mic level: " << kMinMicLevel; + return kMinMicLevel; + } + const auto field_trial_string = + webrtc::field_trial::FindFullName(kMinMicLevelFieldTrial); + int min_mic_level = -1; + sscanf(field_trial_string.c_str(), "Enabled-%d", &min_mic_level); + if (min_mic_level >= 0 && min_mic_level <= 255) { + RTC_LOG(LS_INFO) << "[agc] Experimental min mic level: " << min_mic_level; + return min_mic_level; + } else { + RTC_LOG(LS_WARNING) << "[agc] Invalid parameter for " + << kMinMicLevelFieldTrial << ", ignored."; + return kMinMicLevel; + } +} + +int ClampLevel(int mic_level, int min_mic_level) { + return rtc::SafeClamp(mic_level, min_mic_level, kMaxMicLevel); +} + +int LevelFromGainError(int gain_error, int level, int min_mic_level) { + RTC_DCHECK_GE(level, 0); + RTC_DCHECK_LE(level, kMaxMicLevel); if (gain_error == 0) { return level; } - // TODO(ajm): Could be made more efficient with a binary search. + int new_level = level; if (gain_error > 0) { while (kGainMap[new_level] - kGainMap[level] < gain_error && - new_level < kMaxMicLevel) { + new_level < kMaxMicLevel) { ++new_level; } } else { while (kGainMap[new_level] - kGainMap[level] > gain_error && - new_level > kMinMicLevel) { + new_level > min_mic_level) { --new_level; } } return new_level; } +// Returns the proportion of samples in the buffer which are at full-scale +// (and presumably clipped). +float ComputeClippedRatio(const float* const* audio, + size_t num_channels, + size_t samples_per_channel) { + RTC_DCHECK_GT(samples_per_channel, 0); + int num_clipped = 0; + for (size_t ch = 0; ch < num_channels; ++ch) { + int num_clipped_in_ch = 0; + for (size_t i = 0; i < samples_per_channel; ++i) { + RTC_DCHECK(audio[ch]); + if (audio[ch][i] >= 32767.f || audio[ch][i] <= -32768.f) { + ++num_clipped_in_ch; + } + } + num_clipped = std::max(num_clipped, num_clipped_in_ch); + } + return static_cast(num_clipped) / (samples_per_channel); +} + } // namespace -// Facility for dumping debug audio files. All methods are no-ops in the -// default case where WEBRTC_AGC_DEBUG_DUMP is undefined. -class DebugFile { -#ifdef WEBRTC_AGC_DEBUG_DUMP - public: - explicit DebugFile(const char* filename) - : file_(fopen(filename, "wb")) { - assert(file_); - } - ~DebugFile() { - fclose(file_); - } - void Write(const int16_t* data, size_t length_samples) { - fwrite(data, 1, length_samples * sizeof(int16_t), file_); - } - private: - FILE* file_; -#else - public: - explicit DebugFile(const char* filename) { - } - ~DebugFile() { - } - void Write(const int16_t* data, size_t length_samples) { - } -#endif // WEBRTC_AGC_DEBUG_DUMP -}; - -AgcManagerDirect::AgcManagerDirect(GainControl* gctrl, - VolumeCallbacks* volume_callbacks, - int startup_min_level) - : agc_(new Agc()), - gctrl_(gctrl), - volume_callbacks_(volume_callbacks), - frames_since_clipped_(kClippedWaitFrames), - level_(0), +MonoAgc::MonoAgc(ApmDataDumper* data_dumper, + int startup_min_level, + int clipped_level_min, + bool use_agc2_level_estimation, + bool disable_digital_adaptive, + int min_mic_level) + : min_mic_level_(min_mic_level), + disable_digital_adaptive_(disable_digital_adaptive), max_level_(kMaxMicLevel), max_compression_gain_(kMaxCompressionGain), target_compression_(kDefaultCompressionGain), compression_(target_compression_), compression_accumulator_(compression_), - capture_muted_(false), - check_volume_on_next_process_(true), // Check at startup. - startup_(true), - startup_min_level_(ClampLevel(startup_min_level)), - file_preproc_(new DebugFile("agc_preproc.pcm")), - file_postproc_(new DebugFile("agc_postproc.pcm")) { + startup_min_level_(ClampLevel(startup_min_level, min_mic_level_)), + clipped_level_min_(clipped_level_min) { + if (use_agc2_level_estimation) { + agc_ = std::make_unique(data_dumper); + } else { + agc_ = std::make_unique(); + } } -AgcManagerDirect::AgcManagerDirect(Agc* agc, - GainControl* gctrl, - VolumeCallbacks* volume_callbacks, - int startup_min_level) - : agc_(agc), - gctrl_(gctrl), - volume_callbacks_(volume_callbacks), - frames_since_clipped_(kClippedWaitFrames), - level_(0), - max_level_(kMaxMicLevel), - max_compression_gain_(kMaxCompressionGain), - target_compression_(kDefaultCompressionGain), - compression_(target_compression_), - compression_accumulator_(compression_), - capture_muted_(false), - check_volume_on_next_process_(true), // Check at startup. - startup_(true), - startup_min_level_(ClampLevel(startup_min_level)), - file_preproc_(new DebugFile("agc_preproc.pcm")), - file_postproc_(new DebugFile("agc_postproc.pcm")) { -} +MonoAgc::~MonoAgc() = default; -AgcManagerDirect::~AgcManagerDirect() {} - -int AgcManagerDirect::Initialize() { +void MonoAgc::Initialize() { max_level_ = kMaxMicLevel; max_compression_gain_ = kMaxCompressionGain; - target_compression_ = kDefaultCompressionGain; - compression_ = target_compression_; + target_compression_ = disable_digital_adaptive_ ? 0 : kDefaultCompressionGain; + compression_ = disable_digital_adaptive_ ? 0 : target_compression_; compression_accumulator_ = compression_; capture_muted_ = false; check_volume_on_next_process_ = true; - // TODO(bjornv): Investigate if we need to reset |startup_| as well. For - // example, what happens when we change devices. - - if (gctrl_->set_mode(GainControl::kFixedDigital) != 0) { - LOG_FERR1(LS_ERROR, set_mode, GainControl::kFixedDigital); - return -1; - } - if (gctrl_->set_target_level_dbfs(2) != 0) { - LOG_FERR1(LS_ERROR, set_target_level_dbfs, 2); - return -1; - } - if (gctrl_->set_compression_gain_db(kDefaultCompressionGain) != 0) { - LOG_FERR1(LS_ERROR, set_compression_gain_db, kDefaultCompressionGain); - return -1; - } - if (gctrl_->enable_limiter(true) != 0) { - LOG_FERR1(LS_ERROR, enable_limiter, true); - return -1; - } - return 0; } -void AgcManagerDirect::AnalyzePreProcess(int16_t* audio, - int num_channels, - size_t samples_per_channel) { - size_t length = num_channels * samples_per_channel; - if (capture_muted_) { - return; - } - - file_preproc_->Write(audio, length); - - if (frames_since_clipped_ < kClippedWaitFrames) { - ++frames_since_clipped_; - return; - } - - // Check for clipped samples, as the AGC has difficulty detecting pitch - // under clipping distortion. We do this in the preprocessing phase in order - // to catch clipped echo as well. - // - // If we find a sufficiently clipped frame, drop the current microphone level - // and enforce a new maximum level, dropped the same amount from the current - // maximum. This harsh treatment is an effort to avoid repeated clipped echo - // events. As compensation for this restriction, the maximum compression - // gain is increased, through SetMaxLevel(). - float clipped_ratio = agc_->AnalyzePreproc(audio, length); - if (clipped_ratio > kClippedRatioThreshold) { - LOG(LS_INFO) << "[agc] Clipping detected. clipped_ratio=" - << clipped_ratio; - // Always decrease the maximum level, even if the current level is below - // threshold. - SetMaxLevel(std::max(kClippedLevelMin, max_level_ - kClippedLevelStep)); - if (level_ > kClippedLevelMin) { - // Don't try to adjust the level if we're already below the limit. As - // a consequence, if the user has brought the level above the limit, we - // will still not react until the postproc updates the level. - SetLevel(std::max(kClippedLevelMin, level_ - kClippedLevelStep)); - // Reset the AGC since the level has changed. - agc_->Reset(); - } - frames_since_clipped_ = 0; - } -} - -void AgcManagerDirect::Process(const int16_t* audio, - size_t length, - int sample_rate_hz) { - if (capture_muted_) { - return; - } +void MonoAgc::Process(const int16_t* audio, + size_t samples_per_channel, + int sample_rate_hz) { + new_compression_to_set_ = absl::nullopt; if (check_volume_on_next_process_) { check_volume_on_next_process_ = false; @@ -243,35 +181,50 @@ void AgcManagerDirect::Process(const int16_t* audio, CheckVolumeAndReset(); } - if (agc_->Process(audio, length, sample_rate_hz) != 0) { - LOG_FERR0(LS_ERROR, Agc::Process); - assert(false); - } + agc_->Process(audio, samples_per_channel, sample_rate_hz); UpdateGain(); - UpdateCompressor(); - - file_postproc_->Write(audio, length); + if (!disable_digital_adaptive_) { + UpdateCompressor(); + } } -void AgcManagerDirect::SetLevel(int new_level) { - int voe_level = volume_callbacks_->GetMicVolume(); - if (voe_level < 0) { - return; +void MonoAgc::HandleClipping() { + // Always decrease the maximum level, even if the current level is below + // threshold. + SetMaxLevel(std::max(clipped_level_min_, max_level_ - kClippedLevelStep)); + if (log_to_histograms_) { + RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed", + level_ - kClippedLevelStep >= clipped_level_min_); } + if (level_ > clipped_level_min_) { + // Don't try to adjust the level if we're already below the limit. As + // a consequence, if the user has brought the level above the limit, we + // will still not react until the postproc updates the level. + SetLevel(std::max(clipped_level_min_, level_ - kClippedLevelStep)); + // Reset the AGCs for all channels since the level has changed. + agc_->Reset(); + } +} + +void MonoAgc::SetLevel(int new_level) { + int voe_level = stream_analog_level_; if (voe_level == 0) { - LOG(LS_INFO) << "[agc] VolumeCallbacks returned level=0, taking no action."; + RTC_DLOG(LS_INFO) + << "[agc] VolumeCallbacks returned level=0, taking no action."; return; } - if (voe_level > kMaxMicLevel) { - LOG(LS_ERROR) << "VolumeCallbacks returned an invalid level=" << voe_level; + if (voe_level < 0 || voe_level > kMaxMicLevel) { + RTC_LOG(LS_ERROR) << "VolumeCallbacks returned an invalid level=" + << voe_level; return; } if (voe_level > level_ + kLevelQuantizationSlack || voe_level < level_ - kLevelQuantizationSlack) { - LOG(LS_INFO) << "[agc] Mic volume was manually adjusted. Updating " - << "stored level from " << level_ << " to " << voe_level; + RTC_DLOG(LS_INFO) << "[agc] Mic volume was manually adjusted. Updating " + "stored level from " + << level_ << " to " << voe_level; level_ = voe_level; // Always allow the user to increase the volume. if (level_ > max_level_) { @@ -281,6 +234,7 @@ void AgcManagerDirect::SetLevel(int new_level) { // was manually adjusted. The compressor will still provide some of the // desired gain change. agc_->Reset(); + return; } @@ -289,26 +243,27 @@ void AgcManagerDirect::SetLevel(int new_level) { return; } - volume_callbacks_->SetMicVolume(new_level); - LOG(LS_INFO) << "[agc] voe_level=" << voe_level << ", " - << "level_=" << level_ << ", " - << "new_level=" << new_level; + stream_analog_level_ = new_level; + RTC_DLOG(LS_INFO) << "[agc] voe_level=" << voe_level << ", level_=" << level_ + << ", new_level=" << new_level; level_ = new_level; } -void AgcManagerDirect::SetMaxLevel(int level) { - assert(level >= kClippedLevelMin); +void MonoAgc::SetMaxLevel(int level) { + RTC_DCHECK_GE(level, clipped_level_min_); max_level_ = level; // Scale the |kSurplusCompressionGain| linearly across the restricted // level range. - max_compression_gain_ = kMaxCompressionGain + std::floor( - (1.f * kMaxMicLevel - max_level_) / (kMaxMicLevel - kClippedLevelMin) * - kSurplusCompressionGain + 0.5f); - LOG(LS_INFO) << "[agc] max_level_=" << max_level_ - << ", max_compression_gain_=" << max_compression_gain_; + max_compression_gain_ = + kMaxCompressionGain + std::floor((1.f * kMaxMicLevel - max_level_) / + (kMaxMicLevel - clipped_level_min_) * + kSurplusCompressionGain + + 0.5f); + RTC_DLOG(LS_INFO) << "[agc] max_level_=" << max_level_ + << ", max_compression_gain_=" << max_compression_gain_; } -void AgcManagerDirect::SetCaptureMuted(bool muted) { +void MonoAgc::SetCaptureMuted(bool muted) { if (capture_muted_ == muted) { return; } @@ -320,34 +275,29 @@ void AgcManagerDirect::SetCaptureMuted(bool muted) { } } -float AgcManagerDirect::voice_probability() { - return agc_->voice_probability(); -} - -int AgcManagerDirect::CheckVolumeAndReset() { - int level = volume_callbacks_->GetMicVolume(); - if (level < 0) { - return -1; - } +int MonoAgc::CheckVolumeAndReset() { + int level = stream_analog_level_; // Reasons for taking action at startup: // 1) A person starting a call is expected to be heard. // 2) Independent of interpretation of |level| == 0 we should raise it so the // AGC can do its job properly. if (level == 0 && !startup_) { - LOG(LS_INFO) << "[agc] VolumeCallbacks returned level=0, taking no action."; + RTC_DLOG(LS_INFO) + << "[agc] VolumeCallbacks returned level=0, taking no action."; return 0; } - if (level > kMaxMicLevel) { - LOG(LS_ERROR) << "VolumeCallbacks returned an invalid level=" << level; + if (level < 0 || level > kMaxMicLevel) { + RTC_LOG(LS_ERROR) << "[agc] VolumeCallbacks returned an invalid level=" + << level; return -1; } - LOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level; + RTC_DLOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level; - int minLevel = startup_ ? startup_min_level_ : kMinMicLevel; + int minLevel = startup_ ? startup_min_level_ : min_mic_level_; if (level < minLevel) { level = minLevel; - LOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level; - volume_callbacks_->SetMicVolume(level); + RTC_DLOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level; + stream_analog_level_ = level; } agc_->Reset(); level_ = level; @@ -362,7 +312,7 @@ int AgcManagerDirect::CheckVolumeAndReset() { // // If the slider needs to be moved, we check first if the user has adjusted // it, in which case we take no action and cache the updated level. -void AgcManagerDirect::UpdateGain() { +void MonoAgc::UpdateGain() { int rms_error = 0; if (!agc_->GetRmsErrorDb(&rms_error)) { // No error update ready. @@ -374,39 +324,55 @@ void AgcManagerDirect::UpdateGain() { rms_error += kMinCompressionGain; // Handle as much error as possible with the compressor first. - int raw_compression = std::max(std::min(rms_error, max_compression_gain_), - kMinCompressionGain); + int raw_compression = + rtc::SafeClamp(rms_error, kMinCompressionGain, max_compression_gain_); + // Deemphasize the compression gain error. Move halfway between the current // target and the newly received target. This serves to soften perceptible // intra-talkspurt adjustments, at the cost of some adaptation speed. if ((raw_compression == max_compression_gain_ && - target_compression_ == max_compression_gain_ - 1) || + target_compression_ == max_compression_gain_ - 1) || (raw_compression == kMinCompressionGain && - target_compression_ == kMinCompressionGain + 1)) { + target_compression_ == kMinCompressionGain + 1)) { // Special case to allow the target to reach the endpoints of the // compression range. The deemphasis would otherwise halt it at 1 dB shy. target_compression_ = raw_compression; } else { - target_compression_ = (raw_compression - target_compression_) / 2 - + target_compression_; + target_compression_ = + (raw_compression - target_compression_) / 2 + target_compression_; } // Residual error will be handled by adjusting the volume slider. Use the // raw rather than deemphasized compression here as we would otherwise // shrink the amount of slack the compressor provides. - int residual_gain = rms_error - raw_compression; - residual_gain = std::min(std::max(residual_gain, -kMaxResidualGainChange), - kMaxResidualGainChange); - LOG(LS_INFO) << "[agc] rms_error=" << rms_error << ", " - << "target_compression=" << target_compression_ << ", " - << "residual_gain=" << residual_gain; + const int residual_gain = + rtc::SafeClamp(rms_error - raw_compression, -kMaxResidualGainChange, + kMaxResidualGainChange); + RTC_DLOG(LS_INFO) << "[agc] rms_error=" << rms_error + << ", target_compression=" << target_compression_ + << ", residual_gain=" << residual_gain; if (residual_gain == 0) return; - SetLevel(LevelFromGainError(residual_gain, level_)); + int old_level = level_; + SetLevel(LevelFromGainError(residual_gain, level_, min_mic_level_)); + if (old_level != level_) { + // level_ was updated by SetLevel; log the new value. + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.AgcSetLevel", level_, 1, + kMaxMicLevel, 50); + // Reset the AGC since the level has changed. + agc_->Reset(); + } } -void AgcManagerDirect::UpdateCompressor() { +void MonoAgc::UpdateCompressor() { + calls_since_last_gain_log_++; + if (calls_since_last_gain_log_ == 100) { + calls_since_last_gain_log_ = 0; + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainApplied", + compression_, 0, kMaxCompressionGain, + kMaxCompressionGain + 1); + } if (compression_ == target_compression_) { return; } @@ -431,10 +397,209 @@ void AgcManagerDirect::UpdateCompressor() { // Set the new compression gain. if (new_compression != compression_) { + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainUpdated", + new_compression, 0, kMaxCompressionGain, + kMaxCompressionGain + 1); compression_ = new_compression; compression_accumulator_ = new_compression; - if (gctrl_->set_compression_gain_db(compression_) != 0) { - LOG_FERR1(LS_ERROR, set_compression_gain_db, compression_); + new_compression_to_set_ = compression_; + } +} + +int AgcManagerDirect::instance_counter_ = 0; + +AgcManagerDirect::AgcManagerDirect(Agc* agc, + int startup_min_level, + int clipped_level_min, + int sample_rate_hz) + : AgcManagerDirect(/*num_capture_channels*/ 1, + startup_min_level, + clipped_level_min, + /*use_agc2_level_estimation*/ false, + /*disable_digital_adaptive*/ false, + sample_rate_hz) { + RTC_DCHECK(channel_agcs_[0]); + RTC_DCHECK(agc); + channel_agcs_[0]->set_agc(agc); +} + +AgcManagerDirect::AgcManagerDirect(int num_capture_channels, + int startup_min_level, + int clipped_level_min, + bool use_agc2_level_estimation, + bool disable_digital_adaptive, + int sample_rate_hz) + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_counter_))), + use_min_channel_level_(!UseMaxAnalogChannelLevel()), + sample_rate_hz_(sample_rate_hz), + num_capture_channels_(num_capture_channels), + disable_digital_adaptive_(disable_digital_adaptive), + frames_since_clipped_(kClippedWaitFrames), + capture_muted_(false), + channel_agcs_(num_capture_channels), + new_compressions_to_set_(num_capture_channels) { + const int min_mic_level = GetMinMicLevel(); + for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { + ApmDataDumper* data_dumper_ch = ch == 0 ? data_dumper_.get() : nullptr; + + channel_agcs_[ch] = std::make_unique( + data_dumper_ch, startup_min_level, clipped_level_min, + use_agc2_level_estimation, disable_digital_adaptive_, min_mic_level); + } + RTC_DCHECK_LT(0, channel_agcs_.size()); + channel_agcs_[0]->ActivateLogging(); +} + +AgcManagerDirect::~AgcManagerDirect() {} + +void AgcManagerDirect::Initialize() { + RTC_DLOG(LS_INFO) << "AgcManagerDirect::Initialize"; + data_dumper_->InitiateNewSetOfRecordings(); + for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { + channel_agcs_[ch]->Initialize(); + } + capture_muted_ = false; + + AggregateChannelLevels(); +} + +void AgcManagerDirect::SetupDigitalGainControl( + GainControl* gain_control) const { + RTC_DCHECK(gain_control); + if (gain_control->set_mode(GainControl::kFixedDigital) != 0) { + RTC_LOG(LS_ERROR) << "set_mode(GainControl::kFixedDigital) failed."; + } + const int target_level_dbfs = disable_digital_adaptive_ ? 0 : 2; + if (gain_control->set_target_level_dbfs(target_level_dbfs) != 0) { + RTC_LOG(LS_ERROR) << "set_target_level_dbfs() failed."; + } + const int compression_gain_db = + disable_digital_adaptive_ ? 0 : kDefaultCompressionGain; + if (gain_control->set_compression_gain_db(compression_gain_db) != 0) { + RTC_LOG(LS_ERROR) << "set_compression_gain_db() failed."; + } + const bool enable_limiter = !disable_digital_adaptive_; + if (gain_control->enable_limiter(enable_limiter) != 0) { + RTC_LOG(LS_ERROR) << "enable_limiter() failed."; + } +} + +void AgcManagerDirect::AnalyzePreProcess(const AudioBuffer* audio) { + RTC_DCHECK(audio); + AnalyzePreProcess(audio->channels_const(), audio->num_frames()); +} + +void AgcManagerDirect::AnalyzePreProcess(const float* const* audio, + size_t samples_per_channel) { + RTC_DCHECK(audio); + AggregateChannelLevels(); + if (capture_muted_) { + return; + } + + if (frames_since_clipped_ < kClippedWaitFrames) { + ++frames_since_clipped_; + return; + } + + // Check for clipped samples, as the AGC has difficulty detecting pitch + // under clipping distortion. We do this in the preprocessing phase in order + // to catch clipped echo as well. + // + // If we find a sufficiently clipped frame, drop the current microphone level + // and enforce a new maximum level, dropped the same amount from the current + // maximum. This harsh treatment is an effort to avoid repeated clipped echo + // events. As compensation for this restriction, the maximum compression + // gain is increased, through SetMaxLevel(). + float clipped_ratio = + ComputeClippedRatio(audio, num_capture_channels_, samples_per_channel); + + if (clipped_ratio > kClippedRatioThreshold) { + RTC_DLOG(LS_INFO) << "[agc] Clipping detected. clipped_ratio=" + << clipped_ratio; + for (auto& state_ch : channel_agcs_) { + state_ch->HandleClipping(); + } + frames_since_clipped_ = 0; + } + AggregateChannelLevels(); +} + +void AgcManagerDirect::Process(const AudioBuffer* audio) { + AggregateChannelLevels(); + + if (capture_muted_) { + return; + } + + for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { + int16_t* audio_use = nullptr; + std::array audio_data; + int num_frames_per_band; + if (audio) { + FloatS16ToS16(audio->split_bands_const_f(ch)[0], + audio->num_frames_per_band(), audio_data.data()); + audio_use = audio_data.data(); + num_frames_per_band = audio->num_frames_per_band(); + } else { + // Only used for testing. + // TODO(peah): Change unittests to only allow on non-null audio input. + num_frames_per_band = 320; + } + channel_agcs_[ch]->Process(audio_use, num_frames_per_band, sample_rate_hz_); + new_compressions_to_set_[ch] = channel_agcs_[ch]->new_compression(); + } + + AggregateChannelLevels(); +} + +absl::optional AgcManagerDirect::GetDigitalComressionGain() { + return new_compressions_to_set_[channel_controlling_gain_]; +} + +void AgcManagerDirect::SetCaptureMuted(bool muted) { + for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { + channel_agcs_[ch]->SetCaptureMuted(muted); + } + capture_muted_ = muted; +} + +float AgcManagerDirect::voice_probability() const { + float max_prob = 0.f; + for (const auto& state_ch : channel_agcs_) { + max_prob = std::max(max_prob, state_ch->voice_probability()); + } + + return max_prob; +} + +void AgcManagerDirect::set_stream_analog_level(int level) { + for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { + channel_agcs_[ch]->set_stream_analog_level(level); + } + + AggregateChannelLevels(); +} + +void AgcManagerDirect::AggregateChannelLevels() { + stream_analog_level_ = channel_agcs_[0]->stream_analog_level(); + channel_controlling_gain_ = 0; + if (use_min_channel_level_) { + for (size_t ch = 1; ch < channel_agcs_.size(); ++ch) { + int level = channel_agcs_[ch]->stream_analog_level(); + if (level < stream_analog_level_) { + stream_analog_level_ = level; + channel_controlling_gain_ = static_cast(ch); + } + } + } else { + for (size_t ch = 1; ch < channel_agcs_.size(); ++ch) { + int level = channel_agcs_[ch]->stream_analog_level(); + if (level > stream_analog_level_) { + stream_analog_level_ = level; + channel_controlling_gain_ = static_cast(ch); + } } } } diff --git a/webrtc/modules/audio_processing/agc/agc_manager_direct.h b/webrtc/modules/audio_processing/agc/agc_manager_direct.h index 6edb0f7..d3663be 100644 --- a/webrtc/modules/audio_processing/agc/agc_manager_direct.h +++ b/webrtc/modules/audio_processing/agc/agc_manager_direct.h @@ -8,29 +8,22 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_AGC_MANAGER_DIRECT_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_AGC_MANAGER_DIRECT_H_ +#ifndef MODULES_AUDIO_PROCESSING_AGC_AGC_MANAGER_DIRECT_H_ +#define MODULES_AUDIO_PROCESSING_AGC_AGC_MANAGER_DIRECT_H_ -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/modules/audio_processing/agc/agc.h" +#include + +#include "absl/types/optional.h" +#include "modules/audio_processing/agc/agc.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/gtest_prod_util.h" namespace webrtc { -class AudioFrame; -class DebugFile; +class MonoAgc; class GainControl; -// Callbacks that need to be injected into AgcManagerDirect to read and control -// the volume values. This is done to remove the VoiceEngine dependency in -// AgcManagerDirect. -// TODO(aluebs): Remove VolumeCallbacks. -class VolumeCallbacks { - public: - virtual ~VolumeCallbacks() {} - virtual void SetMicVolume(int volume) = 0; - virtual int GetMicVolume() = 0; -}; - // Direct interface to use AGC to set volume and compression values. // AudioProcessing uses this interface directly to integrate the callback-less // AGC. @@ -42,30 +35,105 @@ class AgcManagerDirect final { // responsible for processing the audio using it after the call to Process. // The operating range of startup_min_level is [12, 255] and any input value // outside that range will be clamped. - AgcManagerDirect(GainControl* gctrl, - VolumeCallbacks* volume_callbacks, - int startup_min_level); - // Dependency injection for testing. Don't delete |agc| as the memory is owned - // by the manager. - AgcManagerDirect(Agc* agc, - GainControl* gctrl, - VolumeCallbacks* volume_callbacks, - int startup_min_level); - ~AgcManagerDirect(); + AgcManagerDirect(int num_capture_channels, + int startup_min_level, + int clipped_level_min, + bool use_agc2_level_estimation, + bool disable_digital_adaptive, + int sample_rate_hz); - int Initialize(); - void AnalyzePreProcess(int16_t* audio, - int num_channels, - size_t samples_per_channel); - void Process(const int16_t* audio, size_t length, int sample_rate_hz); + ~AgcManagerDirect(); + AgcManagerDirect(const AgcManagerDirect&) = delete; + AgcManagerDirect& operator=(const AgcManagerDirect&) = delete; + + void Initialize(); + void SetupDigitalGainControl(GainControl* gain_control) const; + + void AnalyzePreProcess(const AudioBuffer* audio); + void Process(const AudioBuffer* audio); // Call when the capture stream has been muted/unmuted. This causes the // manager to disregard all incoming audio; chances are good it's background // noise to which we'd like to avoid adapting. void SetCaptureMuted(bool muted); - bool capture_muted() { return capture_muted_; } + float voice_probability() const; - float voice_probability(); + int stream_analog_level() const { return stream_analog_level_; } + void set_stream_analog_level(int level); + int num_channels() const { return num_capture_channels_; } + int sample_rate_hz() const { return sample_rate_hz_; } + + // If available, returns a new compression gain for the digital gain control. + absl::optional GetDigitalComressionGain(); + + private: + friend class AgcManagerDirectTest; + + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectStandaloneTest, + DisableDigitalDisablesDigital); + FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectStandaloneTest, + AgcMinMicLevelExperiment); + + // Dependency injection for testing. Don't delete |agc| as the memory is owned + // by the manager. + AgcManagerDirect(Agc* agc, + int startup_min_level, + int clipped_level_min, + int sample_rate_hz); + + void AnalyzePreProcess(const float* const* audio, size_t samples_per_channel); + + void AggregateChannelLevels(); + + std::unique_ptr data_dumper_; + static int instance_counter_; + const bool use_min_channel_level_; + const int sample_rate_hz_; + const int num_capture_channels_; + const bool disable_digital_adaptive_; + + int frames_since_clipped_; + int stream_analog_level_ = 0; + bool capture_muted_; + int channel_controlling_gain_ = 0; + + std::vector> channel_agcs_; + std::vector> new_compressions_to_set_; +}; + +class MonoAgc { + public: + MonoAgc(ApmDataDumper* data_dumper, + int startup_min_level, + int clipped_level_min, + bool use_agc2_level_estimation, + bool disable_digital_adaptive, + int min_mic_level); + ~MonoAgc(); + MonoAgc(const MonoAgc&) = delete; + MonoAgc& operator=(const MonoAgc&) = delete; + + void Initialize(); + void SetCaptureMuted(bool muted); + + void HandleClipping(); + + void Process(const int16_t* audio, + size_t samples_per_channel, + int sample_rate_hz); + + void set_stream_analog_level(int level) { stream_analog_level_ = level; } + int stream_analog_level() const { return stream_analog_level_; } + float voice_probability() const { return agc_->voice_probability(); } + void ActivateLogging() { log_to_histograms_ = true; } + absl::optional new_compression() const { + return new_compression_to_set_; + } + + // Only used for testing. + void set_agc(Agc* agc) { agc_.reset(agc); } + int min_mic_level() const { return min_mic_level_; } + int startup_min_level() const { return startup_min_level_; } private: // Sets a new microphone level, after first checking that it hasn't been @@ -81,28 +149,26 @@ class AgcManagerDirect final { void UpdateGain(); void UpdateCompressor(); - rtc::scoped_ptr agc_; - GainControl* gctrl_; - VolumeCallbacks* volume_callbacks_; - - int frames_since_clipped_; - int level_; + const int min_mic_level_; + const bool disable_digital_adaptive_; + std::unique_ptr agc_; + int level_ = 0; int max_level_; int max_compression_gain_; int target_compression_; int compression_; float compression_accumulator_; - bool capture_muted_; - bool check_volume_on_next_process_; - bool startup_; + bool capture_muted_ = false; + bool check_volume_on_next_process_ = true; + bool startup_ = true; int startup_min_level_; - - rtc::scoped_ptr file_preproc_; - rtc::scoped_ptr file_postproc_; - - RTC_DISALLOW_COPY_AND_ASSIGN(AgcManagerDirect); + int calls_since_last_gain_log_ = 0; + int stream_analog_level_ = 0; + absl::optional new_compression_to_set_; + bool log_to_histograms_ = false; + const int clipped_level_min_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_AGC_MANAGER_DIRECT_H_ +#endif // MODULES_AUDIO_PROCESSING_AGC_AGC_MANAGER_DIRECT_H_ diff --git a/webrtc/modules/audio_processing/agc/gain_control.h b/webrtc/modules/audio_processing/agc/gain_control.h new file mode 100644 index 0000000..f8c706b --- /dev/null +++ b/webrtc/modules/audio_processing/agc/gain_control.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC_GAIN_CONTROL_H_ +#define MODULES_AUDIO_PROCESSING_AGC_GAIN_CONTROL_H_ + +namespace webrtc { + +// The automatic gain control (AGC) component brings the signal to an +// appropriate range. This is done by applying a digital gain directly and, in +// the analog mode, prescribing an analog gain to be applied at the audio HAL. +// +// Recommended to be enabled on the client-side. +class GainControl { + public: + // When an analog mode is set, this must be called prior to |ProcessStream()| + // to pass the current analog level from the audio HAL. Must be within the + // range provided to |set_analog_level_limits()|. + virtual int set_stream_analog_level(int level) = 0; + + // When an analog mode is set, this should be called after |ProcessStream()| + // to obtain the recommended new analog level for the audio HAL. It is the + // users responsibility to apply this level. + virtual int stream_analog_level() const = 0; + + enum Mode { + // Adaptive mode intended for use if an analog volume control is available + // on the capture device. It will require the user to provide coupling + // between the OS mixer controls and AGC through the |stream_analog_level()| + // functions. + // + // It consists of an analog gain prescription for the audio device and a + // digital compression stage. + kAdaptiveAnalog, + + // Adaptive mode intended for situations in which an analog volume control + // is unavailable. It operates in a similar fashion to the adaptive analog + // mode, but with scaling instead applied in the digital domain. As with + // the analog mode, it additionally uses a digital compression stage. + kAdaptiveDigital, + + // Fixed mode which enables only the digital compression stage also used by + // the two adaptive modes. + // + // It is distinguished from the adaptive modes by considering only a + // short time-window of the input signal. It applies a fixed gain through + // most of the input level range, and compresses (gradually reduces gain + // with increasing level) the input signal at higher levels. This mode is + // preferred on embedded devices where the capture signal level is + // predictable, so that a known gain can be applied. + kFixedDigital + }; + + virtual int set_mode(Mode mode) = 0; + virtual Mode mode() const = 0; + + // Sets the target peak |level| (or envelope) of the AGC in dBFs (decibels + // from digital full-scale). The convention is to use positive values. For + // instance, passing in a value of 3 corresponds to -3 dBFs, or a target + // level 3 dB below full-scale. Limited to [0, 31]. + // + // TODO(ajm): use a negative value here instead, if/when VoE will similarly + // update its interface. + virtual int set_target_level_dbfs(int level) = 0; + virtual int target_level_dbfs() const = 0; + + // Sets the maximum |gain| the digital compression stage may apply, in dB. A + // higher number corresponds to greater compression, while a value of 0 will + // leave the signal uncompressed. Limited to [0, 90]. + virtual int set_compression_gain_db(int gain) = 0; + virtual int compression_gain_db() const = 0; + + // When enabled, the compression stage will hard limit the signal to the + // target level. Otherwise, the signal will be compressed but not limited + // above the target level. + virtual int enable_limiter(bool enable) = 0; + virtual bool is_limiter_enabled() const = 0; + + // Sets the |minimum| and |maximum| analog levels of the audio capture device. + // Must be set if and only if an analog mode is used. Limited to [0, 65535]. + virtual int set_analog_level_limits(int minimum, int maximum) = 0; + virtual int analog_level_minimum() const = 0; + virtual int analog_level_maximum() const = 0; + + // Returns true if the AGC has detected a saturation event (period where the + // signal reaches digital full-scale) in the current frame and the analog + // level cannot be reduced. + // + // This could be used as an indicator to reduce or disable analog mic gain at + // the audio HAL. + virtual bool stream_is_saturated() const = 0; + + protected: + virtual ~GainControl() {} +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_GAIN_CONTROL_H_ diff --git a/webrtc/modules/audio_processing/agc/gain_map_internal.h b/webrtc/modules/audio_processing/agc/gain_map_internal.h index 53c71c1..547f0f3 100644 --- a/webrtc/modules/audio_processing/agc/gain_map_internal.h +++ b/webrtc/modules/audio_processing/agc/gain_map_internal.h @@ -8,268 +8,33 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_GAIN_MAP_INTERNAL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_GAIN_MAP_INTERNAL_H_ +#ifndef MODULES_AUDIO_PROCESSING_AGC_GAIN_MAP_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_AGC_GAIN_MAP_INTERNAL_H_ + +namespace webrtc { static const int kGainMapSize = 256; // Uses parameters: si = 2, sf = 0.25, D = 8/256 static const int kGainMap[kGainMapSize] = { - -56, - -54, - -52, - -50, - -48, - -47, - -45, - -43, - -42, - -40, - -38, - -37, - -35, - -34, - -33, - -31, - -30, - -29, - -27, - -26, - -25, - -24, - -23, - -22, - -20, - -19, - -18, - -17, - -16, - -15, - -14, - -14, - -13, - -12, - -11, - -10, - -9, - -8, - -8, - -7, - -6, - -5, - -5, - -4, - -3, - -2, - -2, - -1, - 0, - 0, - 1, - 1, - 2, - 3, - 3, - 4, - 4, - 5, - 5, - 6, - 6, - 7, - 7, - 8, - 8, - 9, - 9, - 10, - 10, - 11, - 11, - 12, - 12, - 13, - 13, - 13, - 14, - 14, - 15, - 15, - 15, - 16, - 16, - 17, - 17, - 17, - 18, - 18, - 18, - 19, - 19, - 19, - 20, - 20, - 21, - 21, - 21, - 22, - 22, - 22, - 23, - 23, - 23, - 24, - 24, - 24, - 24, - 25, - 25, - 25, - 26, - 26, - 26, - 27, - 27, - 27, - 28, - 28, - 28, - 28, - 29, - 29, - 29, - 30, - 30, - 30, - 30, - 31, - 31, - 31, - 32, - 32, - 32, - 32, - 33, - 33, - 33, - 33, - 34, - 34, - 34, - 35, - 35, - 35, - 35, - 36, - 36, - 36, - 36, - 37, - 37, - 37, - 38, - 38, - 38, - 38, - 39, - 39, - 39, - 39, - 40, - 40, - 40, - 40, - 41, - 41, - 41, - 41, - 42, - 42, - 42, - 42, - 43, - 43, - 43, - 44, - 44, - 44, - 44, - 45, - 45, - 45, - 45, - 46, - 46, - 46, - 46, - 47, - 47, - 47, - 47, - 48, - 48, - 48, - 48, - 49, - 49, - 49, - 49, - 50, - 50, - 50, - 50, - 51, - 51, - 51, - 51, - 52, - 52, - 52, - 52, - 53, - 53, - 53, - 53, - 54, - 54, - 54, - 54, - 55, - 55, - 55, - 55, - 56, - 56, - 56, - 56, - 57, - 57, - 57, - 57, - 58, - 58, - 58, - 58, - 59, - 59, - 59, - 59, - 60, - 60, - 60, - 60, - 61, - 61, - 61, - 61, - 62, - 62, - 62, - 62, - 63, - 63, - 63, - 63, - 64 -}; + -56, -54, -52, -50, -48, -47, -45, -43, -42, -40, -38, -37, -35, -34, -33, + -31, -30, -29, -27, -26, -25, -24, -23, -22, -20, -19, -18, -17, -16, -15, + -14, -14, -13, -12, -11, -10, -9, -8, -8, -7, -6, -5, -5, -4, -3, + -2, -2, -1, 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, + 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, 17, 18, 18, 18, 19, + 19, 19, 20, 20, 21, 21, 21, 22, 22, 22, 23, 23, 23, 24, 24, + 24, 24, 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 28, + 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 32, 32, 32, 32, 33, + 33, 33, 33, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, + 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, + 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 44, 44, 44, 44, 45, + 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, 48, + 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, + 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, + 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, + 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, + 64}; -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_GAIN_MAP_INTERNAL_H_ +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_GAIN_MAP_INTERNAL_H_ diff --git a/webrtc/modules/audio_processing/agc/legacy/analog_agc.c b/webrtc/modules/audio_processing/agc/legacy/analog_agc.c deleted file mode 100644 index be644d9..0000000 --- a/webrtc/modules/audio_processing/agc/legacy/analog_agc.c +++ /dev/null @@ -1,1519 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -/* analog_agc.c - * - * Using a feedback system, determines an appropriate analog volume level - * given an input signal and current volume level. Targets a conservative - * signal level and is intended for use with a digital AGC to apply - * additional gain. - * - */ - -#include "webrtc/modules/audio_processing/agc/legacy/analog_agc.h" - -#include -#include -#ifdef WEBRTC_AGC_DEBUG_DUMP -#include -#endif - -/* The slope of in Q13*/ -static const int16_t kSlope1[8] = {21793, 12517, 7189, 4129, 2372, 1362, 472, 78}; - -/* The offset in Q14 */ -static const int16_t kOffset1[8] = {25395, 23911, 22206, 20737, 19612, 18805, 17951, - 17367}; - -/* The slope of in Q13*/ -static const int16_t kSlope2[8] = {2063, 1731, 1452, 1218, 1021, 857, 597, 337}; - -/* The offset in Q14 */ -static const int16_t kOffset2[8] = {18432, 18379, 18290, 18177, 18052, 17920, 17670, - 17286}; - -static const int16_t kMuteGuardTimeMs = 8000; -static const int16_t kInitCheck = 42; -static const size_t kNumSubframes = 10; - -/* Default settings if config is not used */ -#define AGC_DEFAULT_TARGET_LEVEL 3 -#define AGC_DEFAULT_COMP_GAIN 9 -/* This is the target level for the analog part in ENV scale. To convert to RMS scale you - * have to add OFFSET_ENV_TO_RMS. - */ -#define ANALOG_TARGET_LEVEL 11 -#define ANALOG_TARGET_LEVEL_2 5 // ANALOG_TARGET_LEVEL / 2 -/* Offset between RMS scale (analog part) and ENV scale (digital part). This value actually - * varies with the FIXED_ANALOG_TARGET_LEVEL, hence we should in the future replace it with - * a table. - */ -#define OFFSET_ENV_TO_RMS 9 -/* The reference input level at which the digital part gives an output of targetLevelDbfs - * (desired level) if we have no compression gain. This level should be set high enough not - * to compress the peaks due to the dynamics. - */ -#define DIGITAL_REF_AT_0_COMP_GAIN 4 -/* Speed of reference level decrease. - */ -#define DIFF_REF_TO_ANALOG 5 - -#ifdef MIC_LEVEL_FEEDBACK -#define NUM_BLOCKS_IN_SAT_BEFORE_CHANGE_TARGET 7 -#endif -/* Size of analog gain table */ -#define GAIN_TBL_LEN 32 -/* Matlab code: - * fprintf(1, '\t%i, %i, %i, %i,\n', round(10.^(linspace(0,10,32)/20) * 2^12)); - */ -/* Q12 */ -static const uint16_t kGainTableAnalog[GAIN_TBL_LEN] = {4096, 4251, 4412, 4579, 4752, - 4932, 5118, 5312, 5513, 5722, 5938, 6163, 6396, 6638, 6889, 7150, 7420, 7701, 7992, - 8295, 8609, 8934, 9273, 9623, 9987, 10365, 10758, 11165, 11587, 12025, 12480, 12953}; - -/* Gain/Suppression tables for virtual Mic (in Q10) */ -static const uint16_t kGainTableVirtualMic[128] = {1052, 1081, 1110, 1141, 1172, 1204, - 1237, 1271, 1305, 1341, 1378, 1416, 1454, 1494, 1535, 1577, 1620, 1664, 1710, 1757, - 1805, 1854, 1905, 1957, 2010, 2065, 2122, 2180, 2239, 2301, 2364, 2428, 2495, 2563, - 2633, 2705, 2779, 2855, 2933, 3013, 3096, 3180, 3267, 3357, 3449, 3543, 3640, 3739, - 3842, 3947, 4055, 4166, 4280, 4397, 4517, 4640, 4767, 4898, 5032, 5169, 5311, 5456, - 5605, 5758, 5916, 6078, 6244, 6415, 6590, 6770, 6956, 7146, 7341, 7542, 7748, 7960, - 8178, 8402, 8631, 8867, 9110, 9359, 9615, 9878, 10148, 10426, 10711, 11004, 11305, - 11614, 11932, 12258, 12593, 12938, 13292, 13655, 14029, 14412, 14807, 15212, 15628, - 16055, 16494, 16945, 17409, 17885, 18374, 18877, 19393, 19923, 20468, 21028, 21603, - 22194, 22801, 23425, 24065, 24724, 25400, 26095, 26808, 27541, 28295, 29069, 29864, - 30681, 31520, 32382}; -static const uint16_t kSuppressionTableVirtualMic[128] = {1024, 1006, 988, 970, 952, - 935, 918, 902, 886, 870, 854, 839, 824, 809, 794, 780, 766, 752, 739, 726, 713, 700, - 687, 675, 663, 651, 639, 628, 616, 605, 594, 584, 573, 563, 553, 543, 533, 524, 514, - 505, 496, 487, 478, 470, 461, 453, 445, 437, 429, 421, 414, 406, 399, 392, 385, 378, - 371, 364, 358, 351, 345, 339, 333, 327, 321, 315, 309, 304, 298, 293, 288, 283, 278, - 273, 268, 263, 258, 254, 249, 244, 240, 236, 232, 227, 223, 219, 215, 211, 208, 204, - 200, 197, 193, 190, 186, 183, 180, 176, 173, 170, 167, 164, 161, 158, 155, 153, 150, - 147, 145, 142, 139, 137, 134, 132, 130, 127, 125, 123, 121, 118, 116, 114, 112, 110, - 108, 106, 104, 102}; - -/* Table for target energy levels. Values in Q(-7) - * Matlab code - * targetLevelTable = fprintf('%d,\t%d,\t%d,\t%d,\n', round((32767*10.^(-(0:63)'/20)).^2*16/2^7) */ - -static const int32_t kTargetLevelTable[64] = {134209536, 106606424, 84680493, 67264106, - 53429779, 42440782, 33711911, 26778323, 21270778, 16895980, 13420954, 10660642, - 8468049, 6726411, 5342978, 4244078, 3371191, 2677832, 2127078, 1689598, 1342095, - 1066064, 846805, 672641, 534298, 424408, 337119, 267783, 212708, 168960, 134210, - 106606, 84680, 67264, 53430, 42441, 33712, 26778, 21271, 16896, 13421, 10661, 8468, - 6726, 5343, 4244, 3371, 2678, 2127, 1690, 1342, 1066, 847, 673, 534, 424, 337, 268, - 213, 169, 134, 107, 85, 67}; - -int WebRtcAgc_AddMic(void *state, int16_t* const* in_mic, size_t num_bands, - size_t samples) -{ - int32_t nrg, max_nrg, sample, tmp32; - int32_t *ptr; - uint16_t targetGainIdx, gain; - size_t i; - int16_t n, L, tmp16, tmp_speech[16]; - LegacyAgc* stt; - stt = (LegacyAgc*)state; - - if (stt->fs == 8000) { - L = 8; - if (samples != 80) { - return -1; - } - } else { - L = 16; - if (samples != 160) { - return -1; - } - } - - /* apply slowly varying digital gain */ - if (stt->micVol > stt->maxAnalog) - { - /* |maxLevel| is strictly >= |micVol|, so this condition should be - * satisfied here, ensuring there is no divide-by-zero. */ - assert(stt->maxLevel > stt->maxAnalog); - - /* Q1 */ - tmp16 = (int16_t)(stt->micVol - stt->maxAnalog); - tmp32 = (GAIN_TBL_LEN - 1) * tmp16; - tmp16 = (int16_t)(stt->maxLevel - stt->maxAnalog); - targetGainIdx = tmp32 / tmp16; - assert(targetGainIdx < GAIN_TBL_LEN); - - /* Increment through the table towards the target gain. - * If micVol drops below maxAnalog, we allow the gain - * to be dropped immediately. */ - if (stt->gainTableIdx < targetGainIdx) - { - stt->gainTableIdx++; - } else if (stt->gainTableIdx > targetGainIdx) - { - stt->gainTableIdx--; - } - - /* Q12 */ - gain = kGainTableAnalog[stt->gainTableIdx]; - - for (i = 0; i < samples; i++) - { - size_t j; - for (j = 0; j < num_bands; ++j) - { - sample = (in_mic[j][i] * gain) >> 12; - if (sample > 32767) - { - in_mic[j][i] = 32767; - } else if (sample < -32768) - { - in_mic[j][i] = -32768; - } else - { - in_mic[j][i] = (int16_t)sample; - } - } - } - } else - { - stt->gainTableIdx = 0; - } - - /* compute envelope */ - if (stt->inQueue > 0) - { - ptr = stt->env[1]; - } else - { - ptr = stt->env[0]; - } - - for (i = 0; i < kNumSubframes; i++) - { - /* iterate over samples */ - max_nrg = 0; - for (n = 0; n < L; n++) - { - nrg = in_mic[0][i * L + n] * in_mic[0][i * L + n]; - if (nrg > max_nrg) - { - max_nrg = nrg; - } - } - ptr[i] = max_nrg; - } - - /* compute energy */ - if (stt->inQueue > 0) - { - ptr = stt->Rxx16w32_array[1]; - } else - { - ptr = stt->Rxx16w32_array[0]; - } - - for (i = 0; i < kNumSubframes / 2; i++) - { - if (stt->fs == 16000) - { - WebRtcSpl_DownsampleBy2(&in_mic[0][i * 32], - 32, - tmp_speech, - stt->filterState); - } else - { - memcpy(tmp_speech, &in_mic[0][i * 16], 16 * sizeof(short)); - } - /* Compute energy in blocks of 16 samples */ - ptr[i] = WebRtcSpl_DotProductWithScale(tmp_speech, tmp_speech, 16, 4); - } - - /* update queue information */ - if (stt->inQueue == 0) - { - stt->inQueue = 1; - } else - { - stt->inQueue = 2; - } - - /* call VAD (use low band only) */ - WebRtcAgc_ProcessVad(&stt->vadMic, in_mic[0], samples); - - return 0; -} - -int WebRtcAgc_AddFarend(void *state, const int16_t *in_far, size_t samples) -{ - LegacyAgc* stt; - stt = (LegacyAgc*)state; - - if (stt == NULL) - { - return -1; - } - - if (stt->fs == 8000) - { - if (samples != 80) - { - return -1; - } - } else if (stt->fs == 16000 || stt->fs == 32000 || stt->fs == 48000) - { - if (samples != 160) - { - return -1; - } - } else - { - return -1; - } - - return WebRtcAgc_AddFarendToDigital(&stt->digitalAgc, in_far, samples); -} - -int WebRtcAgc_VirtualMic(void *agcInst, int16_t* const* in_near, - size_t num_bands, size_t samples, int32_t micLevelIn, - int32_t *micLevelOut) -{ - int32_t tmpFlt, micLevelTmp, gainIdx; - uint16_t gain; - size_t ii, j; - LegacyAgc* stt; - - uint32_t nrg; - size_t sampleCntr; - uint32_t frameNrg = 0; - uint32_t frameNrgLimit = 5500; - int16_t numZeroCrossing = 0; - const int16_t kZeroCrossingLowLim = 15; - const int16_t kZeroCrossingHighLim = 20; - - stt = (LegacyAgc*)agcInst; - - /* - * Before applying gain decide if this is a low-level signal. - * The idea is that digital AGC will not adapt to low-level - * signals. - */ - if (stt->fs != 8000) - { - frameNrgLimit = frameNrgLimit << 1; - } - - frameNrg = (uint32_t)(in_near[0][0] * in_near[0][0]); - for (sampleCntr = 1; sampleCntr < samples; sampleCntr++) - { - - // increment frame energy if it is less than the limit - // the correct value of the energy is not important - if (frameNrg < frameNrgLimit) - { - nrg = (uint32_t)(in_near[0][sampleCntr] * in_near[0][sampleCntr]); - frameNrg += nrg; - } - - // Count the zero crossings - numZeroCrossing += - ((in_near[0][sampleCntr] ^ in_near[0][sampleCntr - 1]) < 0); - } - - if ((frameNrg < 500) || (numZeroCrossing <= 5)) - { - stt->lowLevelSignal = 1; - } else if (numZeroCrossing <= kZeroCrossingLowLim) - { - stt->lowLevelSignal = 0; - } else if (frameNrg <= frameNrgLimit) - { - stt->lowLevelSignal = 1; - } else if (numZeroCrossing >= kZeroCrossingHighLim) - { - stt->lowLevelSignal = 1; - } else - { - stt->lowLevelSignal = 0; - } - - micLevelTmp = micLevelIn << stt->scale; - /* Set desired level */ - gainIdx = stt->micVol; - if (stt->micVol > stt->maxAnalog) - { - gainIdx = stt->maxAnalog; - } - if (micLevelTmp != stt->micRef) - { - /* Something has happened with the physical level, restart. */ - stt->micRef = micLevelTmp; - stt->micVol = 127; - *micLevelOut = 127; - stt->micGainIdx = 127; - gainIdx = 127; - } - /* Pre-process the signal to emulate the microphone level. */ - /* Take one step at a time in the gain table. */ - if (gainIdx > 127) - { - gain = kGainTableVirtualMic[gainIdx - 128]; - } else - { - gain = kSuppressionTableVirtualMic[127 - gainIdx]; - } - for (ii = 0; ii < samples; ii++) - { - tmpFlt = (in_near[0][ii] * gain) >> 10; - if (tmpFlt > 32767) - { - tmpFlt = 32767; - gainIdx--; - if (gainIdx >= 127) - { - gain = kGainTableVirtualMic[gainIdx - 127]; - } else - { - gain = kSuppressionTableVirtualMic[127 - gainIdx]; - } - } - if (tmpFlt < -32768) - { - tmpFlt = -32768; - gainIdx--; - if (gainIdx >= 127) - { - gain = kGainTableVirtualMic[gainIdx - 127]; - } else - { - gain = kSuppressionTableVirtualMic[127 - gainIdx]; - } - } - in_near[0][ii] = (int16_t)tmpFlt; - for (j = 1; j < num_bands; ++j) - { - tmpFlt = (in_near[j][ii] * gain) >> 10; - if (tmpFlt > 32767) - { - tmpFlt = 32767; - } - if (tmpFlt < -32768) - { - tmpFlt = -32768; - } - in_near[j][ii] = (int16_t)tmpFlt; - } - } - /* Set the level we (finally) used */ - stt->micGainIdx = gainIdx; -// *micLevelOut = stt->micGainIdx; - *micLevelOut = stt->micGainIdx >> stt->scale; - /* Add to Mic as if it was the output from a true microphone */ - if (WebRtcAgc_AddMic(agcInst, in_near, num_bands, samples) != 0) - { - return -1; - } - return 0; -} - -void WebRtcAgc_UpdateAgcThresholds(LegacyAgc* stt) { - int16_t tmp16; -#ifdef MIC_LEVEL_FEEDBACK - int zeros; - - if (stt->micLvlSat) - { - /* Lower the analog target level since we have reached its maximum */ - zeros = WebRtcSpl_NormW32(stt->Rxx160_LPw32); - stt->targetIdxOffset = (3 * zeros - stt->targetIdx - 2) / 4; - } -#endif - - /* Set analog target level in envelope dBOv scale */ - tmp16 = (DIFF_REF_TO_ANALOG * stt->compressionGaindB) + ANALOG_TARGET_LEVEL_2; - tmp16 = WebRtcSpl_DivW32W16ResW16((int32_t)tmp16, ANALOG_TARGET_LEVEL); - stt->analogTarget = DIGITAL_REF_AT_0_COMP_GAIN + tmp16; - if (stt->analogTarget < DIGITAL_REF_AT_0_COMP_GAIN) - { - stt->analogTarget = DIGITAL_REF_AT_0_COMP_GAIN; - } - if (stt->agcMode == kAgcModeFixedDigital) - { - /* Adjust for different parameter interpretation in FixedDigital mode */ - stt->analogTarget = stt->compressionGaindB; - } -#ifdef MIC_LEVEL_FEEDBACK - stt->analogTarget += stt->targetIdxOffset; -#endif - /* Since the offset between RMS and ENV is not constant, we should make this into a - * table, but for now, we'll stick with a constant, tuned for the chosen analog - * target level. - */ - stt->targetIdx = ANALOG_TARGET_LEVEL + OFFSET_ENV_TO_RMS; -#ifdef MIC_LEVEL_FEEDBACK - stt->targetIdx += stt->targetIdxOffset; -#endif - /* Analog adaptation limits */ - /* analogTargetLevel = round((32767*10^(-targetIdx/20))^2*16/2^7) */ - stt->analogTargetLevel = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx]; /* ex. -20 dBov */ - stt->startUpperLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx - 1];/* -19 dBov */ - stt->startLowerLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx + 1];/* -21 dBov */ - stt->upperPrimaryLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx - 2];/* -18 dBov */ - stt->lowerPrimaryLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx + 2];/* -22 dBov */ - stt->upperSecondaryLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx - 5];/* -15 dBov */ - stt->lowerSecondaryLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx + 5];/* -25 dBov */ - stt->upperLimit = stt->startUpperLimit; - stt->lowerLimit = stt->startLowerLimit; -} - -void WebRtcAgc_SaturationCtrl(LegacyAgc* stt, - uint8_t* saturated, - int32_t* env) { - int16_t i, tmpW16; - - /* Check if the signal is saturated */ - for (i = 0; i < 10; i++) - { - tmpW16 = (int16_t)(env[i] >> 20); - if (tmpW16 > 875) - { - stt->envSum += tmpW16; - } - } - - if (stt->envSum > 25000) - { - *saturated = 1; - stt->envSum = 0; - } - - /* stt->envSum *= 0.99; */ - stt->envSum = (int16_t)((stt->envSum * 32440) >> 15); -} - -void WebRtcAgc_ZeroCtrl(LegacyAgc* stt, int32_t* inMicLevel, int32_t* env) { - int16_t i; - int32_t tmp32 = 0; - int32_t midVal; - - /* Is the input signal zero? */ - for (i = 0; i < 10; i++) - { - tmp32 += env[i]; - } - - /* Each block is allowed to have a few non-zero - * samples. - */ - if (tmp32 < 500) - { - stt->msZero += 10; - } else - { - stt->msZero = 0; - } - - if (stt->muteGuardMs > 0) - { - stt->muteGuardMs -= 10; - } - - if (stt->msZero > 500) - { - stt->msZero = 0; - - /* Increase microphone level only if it's less than 50% */ - midVal = (stt->maxAnalog + stt->minLevel + 1) / 2; - if (*inMicLevel < midVal) - { - /* *inMicLevel *= 1.1; */ - *inMicLevel = (1126 * *inMicLevel) >> 10; - /* Reduces risk of a muted mic repeatedly triggering excessive levels due - * to zero signal detection. */ - *inMicLevel = WEBRTC_SPL_MIN(*inMicLevel, stt->zeroCtrlMax); - stt->micVol = *inMicLevel; - } - -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "\t\tAGC->zeroCntrl, frame %d: 500 ms under threshold," - " micVol: %d\n", - stt->fcount, - stt->micVol); -#endif - - stt->activeSpeech = 0; - stt->Rxx16_LPw32Max = 0; - - /* The AGC has a tendency (due to problems with the VAD parameters), to - * vastly increase the volume after a muting event. This timer prevents - * upwards adaptation for a short period. */ - stt->muteGuardMs = kMuteGuardTimeMs; - } -} - -void WebRtcAgc_SpeakerInactiveCtrl(LegacyAgc* stt) { - /* Check if the near end speaker is inactive. - * If that is the case the VAD threshold is - * increased since the VAD speech model gets - * more sensitive to any sound after a long - * silence. - */ - - int32_t tmp32; - int16_t vadThresh; - - if (stt->vadMic.stdLongTerm < 2500) - { - stt->vadThreshold = 1500; - } else - { - vadThresh = kNormalVadThreshold; - if (stt->vadMic.stdLongTerm < 4500) - { - /* Scale between min and max threshold */ - vadThresh += (4500 - stt->vadMic.stdLongTerm) / 2; - } - - /* stt->vadThreshold = (31 * stt->vadThreshold + vadThresh) / 32; */ - tmp32 = vadThresh + 31 * stt->vadThreshold; - stt->vadThreshold = (int16_t)(tmp32 >> 5); - } -} - -void WebRtcAgc_ExpCurve(int16_t volume, int16_t *index) -{ - // volume in Q14 - // index in [0-7] - /* 8 different curves */ - if (volume > 5243) - { - if (volume > 7864) - { - if (volume > 12124) - { - *index = 7; - } else - { - *index = 6; - } - } else - { - if (volume > 6554) - { - *index = 5; - } else - { - *index = 4; - } - } - } else - { - if (volume > 2621) - { - if (volume > 3932) - { - *index = 3; - } else - { - *index = 2; - } - } else - { - if (volume > 1311) - { - *index = 1; - } else - { - *index = 0; - } - } - } -} - -int32_t WebRtcAgc_ProcessAnalog(void *state, int32_t inMicLevel, - int32_t *outMicLevel, - int16_t vadLogRatio, - int16_t echo, uint8_t *saturationWarning) -{ - uint32_t tmpU32; - int32_t Rxx16w32, tmp32; - int32_t inMicLevelTmp, lastMicVol; - int16_t i; - uint8_t saturated = 0; - LegacyAgc* stt; - - stt = (LegacyAgc*)state; - inMicLevelTmp = inMicLevel << stt->scale; - - if (inMicLevelTmp > stt->maxAnalog) - { -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: micLvl > maxAnalog\n", - stt->fcount); -#endif - return -1; - } else if (inMicLevelTmp < stt->minLevel) - { -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: micLvl < minLevel\n", - stt->fcount); -#endif - return -1; - } - - if (stt->firstCall == 0) - { - int32_t tmpVol; - stt->firstCall = 1; - tmp32 = ((stt->maxLevel - stt->minLevel) * 51) >> 9; - tmpVol = (stt->minLevel + tmp32); - - /* If the mic level is very low at start, increase it! */ - if ((inMicLevelTmp < tmpVol) && (stt->agcMode == kAgcModeAdaptiveAnalog)) - { - inMicLevelTmp = tmpVol; - } - stt->micVol = inMicLevelTmp; - } - - /* Set the mic level to the previous output value if there is digital input gain */ - if ((inMicLevelTmp == stt->maxAnalog) && (stt->micVol > stt->maxAnalog)) - { - inMicLevelTmp = stt->micVol; - } - - /* If the mic level was manually changed to a very low value raise it! */ - if ((inMicLevelTmp != stt->micVol) && (inMicLevelTmp < stt->minOutput)) - { - tmp32 = ((stt->maxLevel - stt->minLevel) * 51) >> 9; - inMicLevelTmp = (stt->minLevel + tmp32); - stt->micVol = inMicLevelTmp; -#ifdef MIC_LEVEL_FEEDBACK - //stt->numBlocksMicLvlSat = 0; -#endif -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: micLvl < minLevel by manual" - " decrease, raise vol\n", - stt->fcount); -#endif - } - - if (inMicLevelTmp != stt->micVol) - { - if (inMicLevel == stt->lastInMicLevel) { - // We requested a volume adjustment, but it didn't occur. This is - // probably due to a coarse quantization of the volume slider. - // Restore the requested value to prevent getting stuck. - inMicLevelTmp = stt->micVol; - } - else { - // As long as the value changed, update to match. - stt->micVol = inMicLevelTmp; - } - } - - if (inMicLevelTmp > stt->maxLevel) - { - // Always allow the user to raise the volume above the maxLevel. - stt->maxLevel = inMicLevelTmp; - } - - // Store last value here, after we've taken care of manual updates etc. - stt->lastInMicLevel = inMicLevel; - lastMicVol = stt->micVol; - - /* Checks if the signal is saturated. Also a check if individual samples - * are larger than 12000 is done. If they are the counter for increasing - * the volume level is set to -100ms - */ - WebRtcAgc_SaturationCtrl(stt, &saturated, stt->env[0]); - - /* The AGC is always allowed to lower the level if the signal is saturated */ - if (saturated == 1) - { - /* Lower the recording level - * Rxx160_LP is adjusted down because it is so slow it could - * cause the AGC to make wrong decisions. */ - /* stt->Rxx160_LPw32 *= 0.875; */ - stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 8) * 7; - - stt->zeroCtrlMax = stt->micVol; - - /* stt->micVol *= 0.903; */ - tmp32 = inMicLevelTmp - stt->minLevel; - tmpU32 = WEBRTC_SPL_UMUL(29591, (uint32_t)(tmp32)); - stt->micVol = (tmpU32 >> 15) + stt->minLevel; - if (stt->micVol > lastMicVol - 2) - { - stt->micVol = lastMicVol - 2; - } - inMicLevelTmp = stt->micVol; - -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: saturated, micVol = %d\n", - stt->fcount, - stt->micVol); -#endif - - if (stt->micVol < stt->minOutput) - { - *saturationWarning = 1; - } - - /* Reset counter for decrease of volume level to avoid - * decreasing too much. The saturation control can still - * lower the level if needed. */ - stt->msTooHigh = -100; - - /* Enable the control mechanism to ensure that our measure, - * Rxx160_LP, is in the correct range. This must be done since - * the measure is very slow. */ - stt->activeSpeech = 0; - stt->Rxx16_LPw32Max = 0; - - /* Reset to initial values */ - stt->msecSpeechInnerChange = kMsecSpeechInner; - stt->msecSpeechOuterChange = kMsecSpeechOuter; - stt->changeToSlowMode = 0; - - stt->muteGuardMs = 0; - - stt->upperLimit = stt->startUpperLimit; - stt->lowerLimit = stt->startLowerLimit; -#ifdef MIC_LEVEL_FEEDBACK - //stt->numBlocksMicLvlSat = 0; -#endif - } - - /* Check if the input speech is zero. If so the mic volume - * is increased. On some computers the input is zero up as high - * level as 17% */ - WebRtcAgc_ZeroCtrl(stt, &inMicLevelTmp, stt->env[0]); - - /* Check if the near end speaker is inactive. - * If that is the case the VAD threshold is - * increased since the VAD speech model gets - * more sensitive to any sound after a long - * silence. - */ - WebRtcAgc_SpeakerInactiveCtrl(stt); - - for (i = 0; i < 5; i++) - { - /* Computed on blocks of 16 samples */ - - Rxx16w32 = stt->Rxx16w32_array[0][i]; - - /* Rxx160w32 in Q(-7) */ - tmp32 = (Rxx16w32 - stt->Rxx16_vectorw32[stt->Rxx16pos]) >> 3; - stt->Rxx160w32 = stt->Rxx160w32 + tmp32; - stt->Rxx16_vectorw32[stt->Rxx16pos] = Rxx16w32; - - /* Circular buffer */ - stt->Rxx16pos++; - if (stt->Rxx16pos == RXX_BUFFER_LEN) - { - stt->Rxx16pos = 0; - } - - /* Rxx16_LPw32 in Q(-4) */ - tmp32 = (Rxx16w32 - stt->Rxx16_LPw32) >> kAlphaShortTerm; - stt->Rxx16_LPw32 = (stt->Rxx16_LPw32) + tmp32; - - if (vadLogRatio > stt->vadThreshold) - { - /* Speech detected! */ - - /* Check if Rxx160_LP is in the correct range. If - * it is too high/low then we set it to the maximum of - * Rxx16_LPw32 during the first 200ms of speech. - */ - if (stt->activeSpeech < 250) - { - stt->activeSpeech += 2; - - if (stt->Rxx16_LPw32 > stt->Rxx16_LPw32Max) - { - stt->Rxx16_LPw32Max = stt->Rxx16_LPw32; - } - } else if (stt->activeSpeech == 250) - { - stt->activeSpeech += 2; - tmp32 = stt->Rxx16_LPw32Max >> 3; - stt->Rxx160_LPw32 = tmp32 * RXX_BUFFER_LEN; - } - - tmp32 = (stt->Rxx160w32 - stt->Rxx160_LPw32) >> kAlphaLongTerm; - stt->Rxx160_LPw32 = stt->Rxx160_LPw32 + tmp32; - - if (stt->Rxx160_LPw32 > stt->upperSecondaryLimit) - { - stt->msTooHigh += 2; - stt->msTooLow = 0; - stt->changeToSlowMode = 0; - - if (stt->msTooHigh > stt->msecSpeechOuterChange) - { - stt->msTooHigh = 0; - - /* Lower the recording level */ - /* Multiply by 0.828125 which corresponds to decreasing ~0.8dB */ - tmp32 = stt->Rxx160_LPw32 >> 6; - stt->Rxx160_LPw32 = tmp32 * 53; - - /* Reduce the max gain to avoid excessive oscillation - * (but never drop below the maximum analog level). - */ - stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; - stt->maxLevel = WEBRTC_SPL_MAX(stt->maxLevel, stt->maxAnalog); - - stt->zeroCtrlMax = stt->micVol; - - /* 0.95 in Q15 */ - tmp32 = inMicLevelTmp - stt->minLevel; - tmpU32 = WEBRTC_SPL_UMUL(31130, (uint32_t)(tmp32)); - stt->micVol = (tmpU32 >> 15) + stt->minLevel; - if (stt->micVol > lastMicVol - 1) - { - stt->micVol = lastMicVol - 1; - } - inMicLevelTmp = stt->micVol; - - /* Enable the control mechanism to ensure that our measure, - * Rxx160_LP, is in the correct range. - */ - stt->activeSpeech = 0; - stt->Rxx16_LPw32Max = 0; -#ifdef MIC_LEVEL_FEEDBACK - //stt->numBlocksMicLvlSat = 0; -#endif -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: measure >" - " 2ndUpperLim, micVol = %d, maxLevel = %d\n", - stt->fcount, - stt->micVol, - stt->maxLevel); -#endif - } - } else if (stt->Rxx160_LPw32 > stt->upperLimit) - { - stt->msTooHigh += 2; - stt->msTooLow = 0; - stt->changeToSlowMode = 0; - - if (stt->msTooHigh > stt->msecSpeechInnerChange) - { - /* Lower the recording level */ - stt->msTooHigh = 0; - /* Multiply by 0.828125 which corresponds to decreasing ~0.8dB */ - stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 53; - - /* Reduce the max gain to avoid excessive oscillation - * (but never drop below the maximum analog level). - */ - stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; - stt->maxLevel = WEBRTC_SPL_MAX(stt->maxLevel, stt->maxAnalog); - - stt->zeroCtrlMax = stt->micVol; - - /* 0.965 in Q15 */ - tmp32 = inMicLevelTmp - stt->minLevel; - tmpU32 = WEBRTC_SPL_UMUL(31621, (uint32_t)(inMicLevelTmp - stt->minLevel)); - stt->micVol = (tmpU32 >> 15) + stt->minLevel; - if (stt->micVol > lastMicVol - 1) - { - stt->micVol = lastMicVol - 1; - } - inMicLevelTmp = stt->micVol; - -#ifdef MIC_LEVEL_FEEDBACK - //stt->numBlocksMicLvlSat = 0; -#endif -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: measure >" - " UpperLim, micVol = %d, maxLevel = %d\n", - stt->fcount, - stt->micVol, - stt->maxLevel); -#endif - } - } else if (stt->Rxx160_LPw32 < stt->lowerSecondaryLimit) - { - stt->msTooHigh = 0; - stt->changeToSlowMode = 0; - stt->msTooLow += 2; - - if (stt->msTooLow > stt->msecSpeechOuterChange) - { - /* Raise the recording level */ - int16_t index, weightFIX; - int16_t volNormFIX = 16384; // =1 in Q14. - - stt->msTooLow = 0; - - /* Normalize the volume level */ - tmp32 = (inMicLevelTmp - stt->minLevel) << 14; - if (stt->maxInit != stt->minLevel) - { - volNormFIX = tmp32 / (stt->maxInit - stt->minLevel); - } - - /* Find correct curve */ - WebRtcAgc_ExpCurve(volNormFIX, &index); - - /* Compute weighting factor for the volume increase, 32^(-2*X)/2+1.05 */ - weightFIX = kOffset1[index] - - (int16_t)((kSlope1[index] * volNormFIX) >> 13); - - /* stt->Rxx160_LPw32 *= 1.047 [~0.2 dB]; */ - stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 67; - - tmp32 = inMicLevelTmp - stt->minLevel; - tmpU32 = ((uint32_t)weightFIX * (uint32_t)(inMicLevelTmp - stt->minLevel)); - stt->micVol = (tmpU32 >> 14) + stt->minLevel; - if (stt->micVol < lastMicVol + 2) - { - stt->micVol = lastMicVol + 2; - } - - inMicLevelTmp = stt->micVol; - -#ifdef MIC_LEVEL_FEEDBACK - /* Count ms in level saturation */ - //if (stt->micVol > stt->maxAnalog) { - if (stt->micVol > 150) - { - /* mic level is saturated */ - stt->numBlocksMicLvlSat++; - fprintf(stderr, "Sat mic Level: %d\n", stt->numBlocksMicLvlSat); - } -#endif -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: measure <" - " 2ndLowerLim, micVol = %d\n", - stt->fcount, - stt->micVol); -#endif - } - } else if (stt->Rxx160_LPw32 < stt->lowerLimit) - { - stt->msTooHigh = 0; - stt->changeToSlowMode = 0; - stt->msTooLow += 2; - - if (stt->msTooLow > stt->msecSpeechInnerChange) - { - /* Raise the recording level */ - int16_t index, weightFIX; - int16_t volNormFIX = 16384; // =1 in Q14. - - stt->msTooLow = 0; - - /* Normalize the volume level */ - tmp32 = (inMicLevelTmp - stt->minLevel) << 14; - if (stt->maxInit != stt->minLevel) - { - volNormFIX = tmp32 / (stt->maxInit - stt->minLevel); - } - - /* Find correct curve */ - WebRtcAgc_ExpCurve(volNormFIX, &index); - - /* Compute weighting factor for the volume increase, (3.^(-2.*X))/8+1 */ - weightFIX = kOffset2[index] - - (int16_t)((kSlope2[index] * volNormFIX) >> 13); - - /* stt->Rxx160_LPw32 *= 1.047 [~0.2 dB]; */ - stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 67; - - tmp32 = inMicLevelTmp - stt->minLevel; - tmpU32 = ((uint32_t)weightFIX * (uint32_t)(inMicLevelTmp - stt->minLevel)); - stt->micVol = (tmpU32 >> 14) + stt->minLevel; - if (stt->micVol < lastMicVol + 1) - { - stt->micVol = lastMicVol + 1; - } - - inMicLevelTmp = stt->micVol; - -#ifdef MIC_LEVEL_FEEDBACK - /* Count ms in level saturation */ - //if (stt->micVol > stt->maxAnalog) { - if (stt->micVol > 150) - { - /* mic level is saturated */ - stt->numBlocksMicLvlSat++; - fprintf(stderr, "Sat mic Level: %d\n", stt->numBlocksMicLvlSat); - } -#endif -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: measure < LowerLim, micVol = %d\n", - stt->fcount, - stt->micVol); -#endif - - } - } else - { - /* The signal is inside the desired range which is: - * lowerLimit < Rxx160_LP/640 < upperLimit - */ - if (stt->changeToSlowMode > 4000) - { - stt->msecSpeechInnerChange = 1000; - stt->msecSpeechOuterChange = 500; - stt->upperLimit = stt->upperPrimaryLimit; - stt->lowerLimit = stt->lowerPrimaryLimit; - } else - { - stt->changeToSlowMode += 2; // in milliseconds - } - stt->msTooLow = 0; - stt->msTooHigh = 0; - - stt->micVol = inMicLevelTmp; - - } -#ifdef MIC_LEVEL_FEEDBACK - if (stt->numBlocksMicLvlSat > NUM_BLOCKS_IN_SAT_BEFORE_CHANGE_TARGET) - { - stt->micLvlSat = 1; - fprintf(stderr, "target before = %d (%d)\n", stt->analogTargetLevel, stt->targetIdx); - WebRtcAgc_UpdateAgcThresholds(stt); - WebRtcAgc_CalculateGainTable(&(stt->digitalAgc.gainTable[0]), - stt->compressionGaindB, stt->targetLevelDbfs, stt->limiterEnable, - stt->analogTarget); - stt->numBlocksMicLvlSat = 0; - stt->micLvlSat = 0; - fprintf(stderr, "target offset = %d\n", stt->targetIdxOffset); - fprintf(stderr, "target after = %d (%d)\n", stt->analogTargetLevel, stt->targetIdx); - } -#endif - } - } - - /* Ensure gain is not increased in presence of echo or after a mute event - * (but allow the zeroCtrl() increase on the frame of a mute detection). - */ - if (echo == 1 || (stt->muteGuardMs > 0 && stt->muteGuardMs < kMuteGuardTimeMs)) - { - if (stt->micVol > lastMicVol) - { - stt->micVol = lastMicVol; - } - } - - /* limit the gain */ - if (stt->micVol > stt->maxLevel) - { - stt->micVol = stt->maxLevel; - } else if (stt->micVol < stt->minOutput) - { - stt->micVol = stt->minOutput; - } - - *outMicLevel = WEBRTC_SPL_MIN(stt->micVol, stt->maxAnalog) >> stt->scale; - - return 0; -} - -int WebRtcAgc_Process(void *agcInst, const int16_t* const* in_near, - size_t num_bands, size_t samples, - int16_t* const* out, int32_t inMicLevel, - int32_t *outMicLevel, int16_t echo, - uint8_t *saturationWarning) -{ - LegacyAgc* stt; - - stt = (LegacyAgc*)agcInst; - - // - if (stt == NULL) - { - return -1; - } - // - - - if (stt->fs == 8000) - { - if (samples != 80) - { - return -1; - } - } else if (stt->fs == 16000 || stt->fs == 32000 || stt->fs == 48000) - { - if (samples != 160) - { - return -1; - } - } else - { - return -1; - } - - *saturationWarning = 0; - //TODO: PUT IN RANGE CHECKING FOR INPUT LEVELS - *outMicLevel = inMicLevel; - -#ifdef WEBRTC_AGC_DEBUG_DUMP - stt->fcount++; -#endif - - if (WebRtcAgc_ProcessDigital(&stt->digitalAgc, - in_near, - num_bands, - out, - stt->fs, - stt->lowLevelSignal) == -1) - { -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "AGC->Process, frame %d: Error from DigAGC\n\n", - stt->fcount); -#endif - return -1; - } - if (stt->agcMode < kAgcModeFixedDigital && - (stt->lowLevelSignal == 0 || stt->agcMode != kAgcModeAdaptiveDigital)) - { - if (WebRtcAgc_ProcessAnalog(agcInst, - inMicLevel, - outMicLevel, - stt->vadMic.logRatio, - echo, - saturationWarning) == -1) - { - return -1; - } - } -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->agcLog, - "%5d\t%d\t%d\t%d\t%d\n", - stt->fcount, - inMicLevel, - *outMicLevel, - stt->maxLevel, - stt->micVol); -#endif - - /* update queue */ - if (stt->inQueue > 1) - { - memcpy(stt->env[0], stt->env[1], 10 * sizeof(int32_t)); - memcpy(stt->Rxx16w32_array[0], - stt->Rxx16w32_array[1], - 5 * sizeof(int32_t)); - } - - if (stt->inQueue > 0) - { - stt->inQueue--; - } - - return 0; -} - -int WebRtcAgc_set_config(void* agcInst, WebRtcAgcConfig agcConfig) { - LegacyAgc* stt; - stt = (LegacyAgc*)agcInst; - - if (stt == NULL) - { - return -1; - } - - if (stt->initFlag != kInitCheck) - { - stt->lastError = AGC_UNINITIALIZED_ERROR; - return -1; - } - - if (agcConfig.limiterEnable != kAgcFalse && agcConfig.limiterEnable != kAgcTrue) - { - stt->lastError = AGC_BAD_PARAMETER_ERROR; - return -1; - } - stt->limiterEnable = agcConfig.limiterEnable; - stt->compressionGaindB = agcConfig.compressionGaindB; - if ((agcConfig.targetLevelDbfs < 0) || (agcConfig.targetLevelDbfs > 31)) - { - stt->lastError = AGC_BAD_PARAMETER_ERROR; - return -1; - } - stt->targetLevelDbfs = agcConfig.targetLevelDbfs; - - if (stt->agcMode == kAgcModeFixedDigital) - { - /* Adjust for different parameter interpretation in FixedDigital mode */ - stt->compressionGaindB += agcConfig.targetLevelDbfs; - } - - /* Update threshold levels for analog adaptation */ - WebRtcAgc_UpdateAgcThresholds(stt); - - /* Recalculate gain table */ - if (WebRtcAgc_CalculateGainTable(&(stt->digitalAgc.gainTable[0]), stt->compressionGaindB, - stt->targetLevelDbfs, stt->limiterEnable, stt->analogTarget) == -1) - { -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "AGC->set_config, frame %d: Error from calcGainTable\n\n", - stt->fcount); -#endif - return -1; - } - /* Store the config in a WebRtcAgcConfig */ - stt->usedConfig.compressionGaindB = agcConfig.compressionGaindB; - stt->usedConfig.limiterEnable = agcConfig.limiterEnable; - stt->usedConfig.targetLevelDbfs = agcConfig.targetLevelDbfs; - - return 0; -} - -int WebRtcAgc_get_config(void* agcInst, WebRtcAgcConfig* config) { - LegacyAgc* stt; - stt = (LegacyAgc*)agcInst; - - if (stt == NULL) - { - return -1; - } - - if (config == NULL) - { - stt->lastError = AGC_NULL_POINTER_ERROR; - return -1; - } - - if (stt->initFlag != kInitCheck) - { - stt->lastError = AGC_UNINITIALIZED_ERROR; - return -1; - } - - config->limiterEnable = stt->usedConfig.limiterEnable; - config->targetLevelDbfs = stt->usedConfig.targetLevelDbfs; - config->compressionGaindB = stt->usedConfig.compressionGaindB; - - return 0; -} - -void* WebRtcAgc_Create() { - LegacyAgc* stt = malloc(sizeof(LegacyAgc)); - -#ifdef WEBRTC_AGC_DEBUG_DUMP - stt->fpt = fopen("./agc_test_log.txt", "wt"); - stt->agcLog = fopen("./agc_debug_log.txt", "wt"); - stt->digitalAgc.logFile = fopen("./agc_log.txt", "wt"); -#endif - - stt->initFlag = 0; - stt->lastError = 0; - - return stt; -} - -void WebRtcAgc_Free(void *state) { - LegacyAgc* stt; - - stt = (LegacyAgc*)state; -#ifdef WEBRTC_AGC_DEBUG_DUMP - fclose(stt->fpt); - fclose(stt->agcLog); - fclose(stt->digitalAgc.logFile); -#endif - free(stt); -} - -/* minLevel - Minimum volume level - * maxLevel - Maximum volume level - */ -int WebRtcAgc_Init(void *agcInst, int32_t minLevel, int32_t maxLevel, - int16_t agcMode, uint32_t fs) -{ - int32_t max_add, tmp32; - int16_t i; - int tmpNorm; - LegacyAgc* stt; - - /* typecast state pointer */ - stt = (LegacyAgc*)agcInst; - - if (WebRtcAgc_InitDigital(&stt->digitalAgc, agcMode) != 0) - { - stt->lastError = AGC_UNINITIALIZED_ERROR; - return -1; - } - - /* Analog AGC variables */ - stt->envSum = 0; - - /* mode = 0 - Only saturation protection - * 1 - Analog Automatic Gain Control [-targetLevelDbfs (default -3 dBOv)] - * 2 - Digital Automatic Gain Control [-targetLevelDbfs (default -3 dBOv)] - * 3 - Fixed Digital Gain [compressionGaindB (default 8 dB)] - */ -#ifdef WEBRTC_AGC_DEBUG_DUMP - stt->fcount = 0; - fprintf(stt->fpt, "AGC->Init\n"); -#endif - if (agcMode < kAgcModeUnchanged || agcMode > kAgcModeFixedDigital) - { -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, "AGC->Init: error, incorrect mode\n\n"); -#endif - return -1; - } - stt->agcMode = agcMode; - stt->fs = fs; - - /* initialize input VAD */ - WebRtcAgc_InitVad(&stt->vadMic); - - /* If the volume range is smaller than 0-256 then - * the levels are shifted up to Q8-domain */ - tmpNorm = WebRtcSpl_NormU32((uint32_t)maxLevel); - stt->scale = tmpNorm - 23; - if (stt->scale < 0) - { - stt->scale = 0; - } - // TODO(bjornv): Investigate if we really need to scale up a small range now when we have - // a guard against zero-increments. For now, we do not support scale up (scale = 0). - stt->scale = 0; - maxLevel <<= stt->scale; - minLevel <<= stt->scale; - - /* Make minLevel and maxLevel static in AdaptiveDigital */ - if (stt->agcMode == kAgcModeAdaptiveDigital) - { - minLevel = 0; - maxLevel = 255; - stt->scale = 0; - } - /* The maximum supplemental volume range is based on a vague idea - * of how much lower the gain will be than the real analog gain. */ - max_add = (maxLevel - minLevel) / 4; - - /* Minimum/maximum volume level that can be set */ - stt->minLevel = minLevel; - stt->maxAnalog = maxLevel; - stt->maxLevel = maxLevel + max_add; - stt->maxInit = stt->maxLevel; - - stt->zeroCtrlMax = stt->maxAnalog; - stt->lastInMicLevel = 0; - - /* Initialize micVol parameter */ - stt->micVol = stt->maxAnalog; - if (stt->agcMode == kAgcModeAdaptiveDigital) - { - stt->micVol = 127; /* Mid-point of mic level */ - } - stt->micRef = stt->micVol; - stt->micGainIdx = 127; -#ifdef MIC_LEVEL_FEEDBACK - stt->numBlocksMicLvlSat = 0; - stt->micLvlSat = 0; -#endif -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, - "AGC->Init: minLevel = %d, maxAnalog = %d, maxLevel = %d\n", - stt->minLevel, - stt->maxAnalog, - stt->maxLevel); -#endif - - /* Minimum output volume is 4% higher than the available lowest volume level */ - tmp32 = ((stt->maxLevel - stt->minLevel) * 10) >> 8; - stt->minOutput = (stt->minLevel + tmp32); - - stt->msTooLow = 0; - stt->msTooHigh = 0; - stt->changeToSlowMode = 0; - stt->firstCall = 0; - stt->msZero = 0; - stt->muteGuardMs = 0; - stt->gainTableIdx = 0; - - stt->msecSpeechInnerChange = kMsecSpeechInner; - stt->msecSpeechOuterChange = kMsecSpeechOuter; - - stt->activeSpeech = 0; - stt->Rxx16_LPw32Max = 0; - - stt->vadThreshold = kNormalVadThreshold; - stt->inActive = 0; - - for (i = 0; i < RXX_BUFFER_LEN; i++) - { - stt->Rxx16_vectorw32[i] = (int32_t)1000; /* -54dBm0 */ - } - stt->Rxx160w32 = 125 * RXX_BUFFER_LEN; /* (stt->Rxx16_vectorw32[0]>>3) = 125 */ - - stt->Rxx16pos = 0; - stt->Rxx16_LPw32 = (int32_t)16284; /* Q(-4) */ - - for (i = 0; i < 5; i++) - { - stt->Rxx16w32_array[0][i] = 0; - } - for (i = 0; i < 10; i++) - { - stt->env[0][i] = 0; - stt->env[1][i] = 0; - } - stt->inQueue = 0; - -#ifdef MIC_LEVEL_FEEDBACK - stt->targetIdxOffset = 0; -#endif - - WebRtcSpl_MemSetW32(stt->filterState, 0, 8); - - stt->initFlag = kInitCheck; - // Default config settings. - stt->defaultConfig.limiterEnable = kAgcTrue; - stt->defaultConfig.targetLevelDbfs = AGC_DEFAULT_TARGET_LEVEL; - stt->defaultConfig.compressionGaindB = AGC_DEFAULT_COMP_GAIN; - - if (WebRtcAgc_set_config(stt, stt->defaultConfig) == -1) - { - stt->lastError = AGC_UNSPECIFIED_ERROR; - return -1; - } - stt->Rxx160_LPw32 = stt->analogTargetLevel; // Initialize rms value - - stt->lowLevelSignal = 0; - - /* Only positive values are allowed that are not too large */ - if ((minLevel >= maxLevel) || (maxLevel & 0xFC000000)) - { -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, "minLevel, maxLevel value(s) are invalid\n\n"); -#endif - return -1; - } else - { -#ifdef WEBRTC_AGC_DEBUG_DUMP - fprintf(stt->fpt, "\n"); -#endif - return 0; - } -} diff --git a/webrtc/modules/audio_processing/agc/legacy/analog_agc.cc b/webrtc/modules/audio_processing/agc/legacy/analog_agc.cc new file mode 100644 index 0000000..b53e3f9 --- /dev/null +++ b/webrtc/modules/audio_processing/agc/legacy/analog_agc.cc @@ -0,0 +1,1238 @@ +/* + * Copyright (c) 2012 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. + */ + +/* + * + * Using a feedback system, determines an appropriate analog volume level + * given an input signal and current volume level. Targets a conservative + * signal level and is intended for use with a digital AGC to apply + * additional gain. + * + */ + +#include "modules/audio_processing/agc/legacy/analog_agc.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Errors +#define AGC_UNSPECIFIED_ERROR 18000 +#define AGC_UNINITIALIZED_ERROR 18002 +#define AGC_NULL_POINTER_ERROR 18003 +#define AGC_BAD_PARAMETER_ERROR 18004 + +/* The slope of in Q13*/ +static const int16_t kSlope1[8] = {21793, 12517, 7189, 4129, + 2372, 1362, 472, 78}; + +/* The offset in Q14 */ +static const int16_t kOffset1[8] = {25395, 23911, 22206, 20737, + 19612, 18805, 17951, 17367}; + +/* The slope of in Q13*/ +static const int16_t kSlope2[8] = {2063, 1731, 1452, 1218, 1021, 857, 597, 337}; + +/* The offset in Q14 */ +static const int16_t kOffset2[8] = {18432, 18379, 18290, 18177, + 18052, 17920, 17670, 17286}; + +static const int16_t kMuteGuardTimeMs = 8000; +static const int16_t kInitCheck = 42; +static const size_t kNumSubframes = 10; + +/* Default settings if config is not used */ +#define AGC_DEFAULT_TARGET_LEVEL 3 +#define AGC_DEFAULT_COMP_GAIN 9 +/* This is the target level for the analog part in ENV scale. To convert to RMS + * scale you + * have to add OFFSET_ENV_TO_RMS. + */ +#define ANALOG_TARGET_LEVEL 11 +#define ANALOG_TARGET_LEVEL_2 5 // ANALOG_TARGET_LEVEL / 2 +/* Offset between RMS scale (analog part) and ENV scale (digital part). This + * value actually + * varies with the FIXED_ANALOG_TARGET_LEVEL, hence we should in the future + * replace it with + * a table. + */ +#define OFFSET_ENV_TO_RMS 9 +/* The reference input level at which the digital part gives an output of + * targetLevelDbfs + * (desired level) if we have no compression gain. This level should be set high + * enough not + * to compress the peaks due to the dynamics. + */ +#define DIGITAL_REF_AT_0_COMP_GAIN 4 +/* Speed of reference level decrease. + */ +#define DIFF_REF_TO_ANALOG 5 + +/* Size of analog gain table */ +#define GAIN_TBL_LEN 32 +/* Matlab code: + * fprintf(1, '\t%i, %i, %i, %i,\n', round(10.^(linspace(0,10,32)/20) * 2^12)); + */ +/* Q12 */ +static const uint16_t kGainTableAnalog[GAIN_TBL_LEN] = { + 4096, 4251, 4412, 4579, 4752, 4932, 5118, 5312, 5513, 5722, 5938, + 6163, 6396, 6638, 6889, 7150, 7420, 7701, 7992, 8295, 8609, 8934, + 9273, 9623, 9987, 10365, 10758, 11165, 11587, 12025, 12480, 12953}; + +/* Gain/Suppression tables for virtual Mic (in Q10) */ +static const uint16_t kGainTableVirtualMic[128] = { + 1052, 1081, 1110, 1141, 1172, 1204, 1237, 1271, 1305, 1341, 1378, + 1416, 1454, 1494, 1535, 1577, 1620, 1664, 1710, 1757, 1805, 1854, + 1905, 1957, 2010, 2065, 2122, 2180, 2239, 2301, 2364, 2428, 2495, + 2563, 2633, 2705, 2779, 2855, 2933, 3013, 3096, 3180, 3267, 3357, + 3449, 3543, 3640, 3739, 3842, 3947, 4055, 4166, 4280, 4397, 4517, + 4640, 4767, 4898, 5032, 5169, 5311, 5456, 5605, 5758, 5916, 6078, + 6244, 6415, 6590, 6770, 6956, 7146, 7341, 7542, 7748, 7960, 8178, + 8402, 8631, 8867, 9110, 9359, 9615, 9878, 10148, 10426, 10711, 11004, + 11305, 11614, 11932, 12258, 12593, 12938, 13292, 13655, 14029, 14412, 14807, + 15212, 15628, 16055, 16494, 16945, 17409, 17885, 18374, 18877, 19393, 19923, + 20468, 21028, 21603, 22194, 22801, 23425, 24065, 24724, 25400, 26095, 26808, + 27541, 28295, 29069, 29864, 30681, 31520, 32382}; +static const uint16_t kSuppressionTableVirtualMic[128] = { + 1024, 1006, 988, 970, 952, 935, 918, 902, 886, 870, 854, 839, 824, 809, 794, + 780, 766, 752, 739, 726, 713, 700, 687, 675, 663, 651, 639, 628, 616, 605, + 594, 584, 573, 563, 553, 543, 533, 524, 514, 505, 496, 487, 478, 470, 461, + 453, 445, 437, 429, 421, 414, 406, 399, 392, 385, 378, 371, 364, 358, 351, + 345, 339, 333, 327, 321, 315, 309, 304, 298, 293, 288, 283, 278, 273, 268, + 263, 258, 254, 249, 244, 240, 236, 232, 227, 223, 219, 215, 211, 208, 204, + 200, 197, 193, 190, 186, 183, 180, 176, 173, 170, 167, 164, 161, 158, 155, + 153, 150, 147, 145, 142, 139, 137, 134, 132, 130, 127, 125, 123, 121, 118, + 116, 114, 112, 110, 108, 106, 104, 102}; + +/* Table for target energy levels. Values in Q(-7) + * Matlab code + * targetLevelTable = fprintf('%d,\t%d,\t%d,\t%d,\n', + * round((32767*10.^(-(0:63)'/20)).^2*16/2^7) */ + +static const int32_t kTargetLevelTable[64] = { + 134209536, 106606424, 84680493, 67264106, 53429779, 42440782, 33711911, + 26778323, 21270778, 16895980, 13420954, 10660642, 8468049, 6726411, + 5342978, 4244078, 3371191, 2677832, 2127078, 1689598, 1342095, + 1066064, 846805, 672641, 534298, 424408, 337119, 267783, + 212708, 168960, 134210, 106606, 84680, 67264, 53430, + 42441, 33712, 26778, 21271, 16896, 13421, 10661, + 8468, 6726, 5343, 4244, 3371, 2678, 2127, + 1690, 1342, 1066, 847, 673, 534, 424, + 337, 268, 213, 169, 134, 107, 85, + 67}; + +} // namespace + +int WebRtcAgc_AddMic(void* state, + int16_t* const* in_mic, + size_t num_bands, + size_t samples) { + int32_t nrg, max_nrg, sample, tmp32; + int32_t* ptr; + uint16_t targetGainIdx, gain; + size_t i; + int16_t n, L, tmp16, tmp_speech[16]; + LegacyAgc* stt; + stt = reinterpret_cast(state); + + if (stt->fs == 8000) { + L = 8; + if (samples != 80) { + return -1; + } + } else { + L = 16; + if (samples != 160) { + return -1; + } + } + + /* apply slowly varying digital gain */ + if (stt->micVol > stt->maxAnalog) { + /* |maxLevel| is strictly >= |micVol|, so this condition should be + * satisfied here, ensuring there is no divide-by-zero. */ + RTC_DCHECK_GT(stt->maxLevel, stt->maxAnalog); + + /* Q1 */ + tmp16 = (int16_t)(stt->micVol - stt->maxAnalog); + tmp32 = (GAIN_TBL_LEN - 1) * tmp16; + tmp16 = (int16_t)(stt->maxLevel - stt->maxAnalog); + targetGainIdx = tmp32 / tmp16; + RTC_DCHECK_LT(targetGainIdx, GAIN_TBL_LEN); + + /* Increment through the table towards the target gain. + * If micVol drops below maxAnalog, we allow the gain + * to be dropped immediately. */ + if (stt->gainTableIdx < targetGainIdx) { + stt->gainTableIdx++; + } else if (stt->gainTableIdx > targetGainIdx) { + stt->gainTableIdx--; + } + + /* Q12 */ + gain = kGainTableAnalog[stt->gainTableIdx]; + + for (i = 0; i < samples; i++) { + size_t j; + for (j = 0; j < num_bands; ++j) { + sample = (in_mic[j][i] * gain) >> 12; + if (sample > 32767) { + in_mic[j][i] = 32767; + } else if (sample < -32768) { + in_mic[j][i] = -32768; + } else { + in_mic[j][i] = (int16_t)sample; + } + } + } + } else { + stt->gainTableIdx = 0; + } + + /* compute envelope */ + if (stt->inQueue > 0) { + ptr = stt->env[1]; + } else { + ptr = stt->env[0]; + } + + for (i = 0; i < kNumSubframes; i++) { + /* iterate over samples */ + max_nrg = 0; + for (n = 0; n < L; n++) { + nrg = in_mic[0][i * L + n] * in_mic[0][i * L + n]; + if (nrg > max_nrg) { + max_nrg = nrg; + } + } + ptr[i] = max_nrg; + } + + /* compute energy */ + if (stt->inQueue > 0) { + ptr = stt->Rxx16w32_array[1]; + } else { + ptr = stt->Rxx16w32_array[0]; + } + + for (i = 0; i < kNumSubframes / 2; i++) { + if (stt->fs == 16000) { + WebRtcSpl_DownsampleBy2(&in_mic[0][i * 32], 32, tmp_speech, + stt->filterState); + } else { + memcpy(tmp_speech, &in_mic[0][i * 16], 16 * sizeof(int16_t)); + } + /* Compute energy in blocks of 16 samples */ + ptr[i] = WebRtcSpl_DotProductWithScale(tmp_speech, tmp_speech, 16, 4); + } + + /* update queue information */ + if (stt->inQueue == 0) { + stt->inQueue = 1; + } else { + stt->inQueue = 2; + } + + /* call VAD (use low band only) */ + WebRtcAgc_ProcessVad(&stt->vadMic, in_mic[0], samples); + + return 0; +} + +int WebRtcAgc_AddFarend(void* state, const int16_t* in_far, size_t samples) { + LegacyAgc* stt = reinterpret_cast(state); + + int err = WebRtcAgc_GetAddFarendError(state, samples); + + if (err != 0) + return err; + + return WebRtcAgc_AddFarendToDigital(&stt->digitalAgc, in_far, samples); +} + +int WebRtcAgc_GetAddFarendError(void* state, size_t samples) { + LegacyAgc* stt; + stt = reinterpret_cast(state); + + if (stt == NULL) + return -1; + + if (stt->fs == 8000) { + if (samples != 80) + return -1; + } else if (stt->fs == 16000 || stt->fs == 32000 || stt->fs == 48000) { + if (samples != 160) + return -1; + } else { + return -1; + } + + return 0; +} + +int WebRtcAgc_VirtualMic(void* agcInst, + int16_t* const* in_near, + size_t num_bands, + size_t samples, + int32_t micLevelIn, + int32_t* micLevelOut) { + int32_t tmpFlt, micLevelTmp, gainIdx; + uint16_t gain; + size_t ii, j; + LegacyAgc* stt; + + uint32_t nrg; + size_t sampleCntr; + uint32_t frameNrg = 0; + uint32_t frameNrgLimit = 5500; + int16_t numZeroCrossing = 0; + const int16_t kZeroCrossingLowLim = 15; + const int16_t kZeroCrossingHighLim = 20; + + stt = reinterpret_cast(agcInst); + + /* + * Before applying gain decide if this is a low-level signal. + * The idea is that digital AGC will not adapt to low-level + * signals. + */ + if (stt->fs != 8000) { + frameNrgLimit = frameNrgLimit << 1; + } + + frameNrg = (uint32_t)(in_near[0][0] * in_near[0][0]); + for (sampleCntr = 1; sampleCntr < samples; sampleCntr++) { + // increment frame energy if it is less than the limit + // the correct value of the energy is not important + if (frameNrg < frameNrgLimit) { + nrg = (uint32_t)(in_near[0][sampleCntr] * in_near[0][sampleCntr]); + frameNrg += nrg; + } + + // Count the zero crossings + numZeroCrossing += + ((in_near[0][sampleCntr] ^ in_near[0][sampleCntr - 1]) < 0); + } + + if ((frameNrg < 500) || (numZeroCrossing <= 5)) { + stt->lowLevelSignal = 1; + } else if (numZeroCrossing <= kZeroCrossingLowLim) { + stt->lowLevelSignal = 0; + } else if (frameNrg <= frameNrgLimit) { + stt->lowLevelSignal = 1; + } else if (numZeroCrossing >= kZeroCrossingHighLim) { + stt->lowLevelSignal = 1; + } else { + stt->lowLevelSignal = 0; + } + + micLevelTmp = micLevelIn << stt->scale; + /* Set desired level */ + gainIdx = stt->micVol; + if (stt->micVol > stt->maxAnalog) { + gainIdx = stt->maxAnalog; + } + if (micLevelTmp != stt->micRef) { + /* Something has happened with the physical level, restart. */ + stt->micRef = micLevelTmp; + stt->micVol = 127; + *micLevelOut = 127; + stt->micGainIdx = 127; + gainIdx = 127; + } + /* Pre-process the signal to emulate the microphone level. */ + /* Take one step at a time in the gain table. */ + if (gainIdx > 127) { + gain = kGainTableVirtualMic[gainIdx - 128]; + } else { + gain = kSuppressionTableVirtualMic[127 - gainIdx]; + } + for (ii = 0; ii < samples; ii++) { + tmpFlt = (in_near[0][ii] * gain) >> 10; + if (tmpFlt > 32767) { + tmpFlt = 32767; + gainIdx--; + if (gainIdx >= 127) { + gain = kGainTableVirtualMic[gainIdx - 127]; + } else { + gain = kSuppressionTableVirtualMic[127 - gainIdx]; + } + } + if (tmpFlt < -32768) { + tmpFlt = -32768; + gainIdx--; + if (gainIdx >= 127) { + gain = kGainTableVirtualMic[gainIdx - 127]; + } else { + gain = kSuppressionTableVirtualMic[127 - gainIdx]; + } + } + in_near[0][ii] = (int16_t)tmpFlt; + for (j = 1; j < num_bands; ++j) { + tmpFlt = (in_near[j][ii] * gain) >> 10; + if (tmpFlt > 32767) { + tmpFlt = 32767; + } + if (tmpFlt < -32768) { + tmpFlt = -32768; + } + in_near[j][ii] = (int16_t)tmpFlt; + } + } + /* Set the level we (finally) used */ + stt->micGainIdx = gainIdx; + // *micLevelOut = stt->micGainIdx; + *micLevelOut = stt->micGainIdx >> stt->scale; + /* Add to Mic as if it was the output from a true microphone */ + if (WebRtcAgc_AddMic(agcInst, in_near, num_bands, samples) != 0) { + return -1; + } + return 0; +} + +void WebRtcAgc_UpdateAgcThresholds(LegacyAgc* stt) { + int16_t tmp16; + + /* Set analog target level in envelope dBOv scale */ + tmp16 = (DIFF_REF_TO_ANALOG * stt->compressionGaindB) + ANALOG_TARGET_LEVEL_2; + tmp16 = WebRtcSpl_DivW32W16ResW16((int32_t)tmp16, ANALOG_TARGET_LEVEL); + stt->analogTarget = DIGITAL_REF_AT_0_COMP_GAIN + tmp16; + if (stt->analogTarget < DIGITAL_REF_AT_0_COMP_GAIN) { + stt->analogTarget = DIGITAL_REF_AT_0_COMP_GAIN; + } + if (stt->agcMode == kAgcModeFixedDigital) { + /* Adjust for different parameter interpretation in FixedDigital mode */ + stt->analogTarget = stt->compressionGaindB; + } + /* Since the offset between RMS and ENV is not constant, we should make this + * into a + * table, but for now, we'll stick with a constant, tuned for the chosen + * analog + * target level. + */ + stt->targetIdx = ANALOG_TARGET_LEVEL + OFFSET_ENV_TO_RMS; + /* Analog adaptation limits */ + /* analogTargetLevel = round((32767*10^(-targetIdx/20))^2*16/2^7) */ + stt->analogTargetLevel = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx]; /* ex. -20 dBov */ + stt->startUpperLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx - 1]; /* -19 dBov */ + stt->startLowerLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx + 1]; /* -21 dBov */ + stt->upperPrimaryLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx - 2]; /* -18 dBov */ + stt->lowerPrimaryLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx + 2]; /* -22 dBov */ + stt->upperSecondaryLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx - 5]; /* -15 dBov */ + stt->lowerSecondaryLimit = + kRxxBufferLen * kTargetLevelTable[stt->targetIdx + 5]; /* -25 dBov */ + stt->upperLimit = stt->startUpperLimit; + stt->lowerLimit = stt->startLowerLimit; +} + +void WebRtcAgc_SaturationCtrl(LegacyAgc* stt, + uint8_t* saturated, + int32_t* env) { + int16_t i, tmpW16; + + /* Check if the signal is saturated */ + for (i = 0; i < 10; i++) { + tmpW16 = (int16_t)(env[i] >> 20); + if (tmpW16 > 875) { + stt->envSum += tmpW16; + } + } + + if (stt->envSum > 25000) { + *saturated = 1; + stt->envSum = 0; + } + + /* stt->envSum *= 0.99; */ + stt->envSum = (int16_t)((stt->envSum * 32440) >> 15); +} + +void WebRtcAgc_ZeroCtrl(LegacyAgc* stt, int32_t* inMicLevel, int32_t* env) { + int16_t i; + int64_t tmp = 0; + int32_t midVal; + + /* Is the input signal zero? */ + for (i = 0; i < 10; i++) { + tmp += env[i]; + } + + /* Each block is allowed to have a few non-zero + * samples. + */ + if (tmp < 500) { + stt->msZero += 10; + } else { + stt->msZero = 0; + } + + if (stt->muteGuardMs > 0) { + stt->muteGuardMs -= 10; + } + + if (stt->msZero > 500) { + stt->msZero = 0; + + /* Increase microphone level only if it's less than 50% */ + midVal = (stt->maxAnalog + stt->minLevel + 1) / 2; + if (*inMicLevel < midVal) { + /* *inMicLevel *= 1.1; */ + *inMicLevel = (1126 * *inMicLevel) >> 10; + /* Reduces risk of a muted mic repeatedly triggering excessive levels due + * to zero signal detection. */ + *inMicLevel = WEBRTC_SPL_MIN(*inMicLevel, stt->zeroCtrlMax); + stt->micVol = *inMicLevel; + } + + stt->activeSpeech = 0; + stt->Rxx16_LPw32Max = 0; + + /* The AGC has a tendency (due to problems with the VAD parameters), to + * vastly increase the volume after a muting event. This timer prevents + * upwards adaptation for a short period. */ + stt->muteGuardMs = kMuteGuardTimeMs; + } +} + +void WebRtcAgc_SpeakerInactiveCtrl(LegacyAgc* stt) { + /* Check if the near end speaker is inactive. + * If that is the case the VAD threshold is + * increased since the VAD speech model gets + * more sensitive to any sound after a long + * silence. + */ + + int32_t tmp32; + int16_t vadThresh; + + if (stt->vadMic.stdLongTerm < 2500) { + stt->vadThreshold = 1500; + } else { + vadThresh = kNormalVadThreshold; + if (stt->vadMic.stdLongTerm < 4500) { + /* Scale between min and max threshold */ + vadThresh += (4500 - stt->vadMic.stdLongTerm) / 2; + } + + /* stt->vadThreshold = (31 * stt->vadThreshold + vadThresh) / 32; */ + tmp32 = vadThresh + 31 * stt->vadThreshold; + stt->vadThreshold = (int16_t)(tmp32 >> 5); + } +} + +void WebRtcAgc_ExpCurve(int16_t volume, int16_t* index) { + // volume in Q14 + // index in [0-7] + /* 8 different curves */ + if (volume > 5243) { + if (volume > 7864) { + if (volume > 12124) { + *index = 7; + } else { + *index = 6; + } + } else { + if (volume > 6554) { + *index = 5; + } else { + *index = 4; + } + } + } else { + if (volume > 2621) { + if (volume > 3932) { + *index = 3; + } else { + *index = 2; + } + } else { + if (volume > 1311) { + *index = 1; + } else { + *index = 0; + } + } + } +} + +int32_t WebRtcAgc_ProcessAnalog(void* state, + int32_t inMicLevel, + int32_t* outMicLevel, + int16_t vadLogRatio, + int16_t echo, + uint8_t* saturationWarning) { + uint32_t tmpU32; + int32_t Rxx16w32, tmp32; + int32_t inMicLevelTmp, lastMicVol; + int16_t i; + uint8_t saturated = 0; + LegacyAgc* stt; + + stt = reinterpret_cast(state); + inMicLevelTmp = inMicLevel << stt->scale; + + if (inMicLevelTmp > stt->maxAnalog) { + return -1; + } else if (inMicLevelTmp < stt->minLevel) { + return -1; + } + + if (stt->firstCall == 0) { + int32_t tmpVol; + stt->firstCall = 1; + tmp32 = ((stt->maxLevel - stt->minLevel) * 51) >> 9; + tmpVol = (stt->minLevel + tmp32); + + /* If the mic level is very low at start, increase it! */ + if ((inMicLevelTmp < tmpVol) && (stt->agcMode == kAgcModeAdaptiveAnalog)) { + inMicLevelTmp = tmpVol; + } + stt->micVol = inMicLevelTmp; + } + + /* Set the mic level to the previous output value if there is digital input + * gain */ + if ((inMicLevelTmp == stt->maxAnalog) && (stt->micVol > stt->maxAnalog)) { + inMicLevelTmp = stt->micVol; + } + + /* If the mic level was manually changed to a very low value raise it! */ + if ((inMicLevelTmp != stt->micVol) && (inMicLevelTmp < stt->minOutput)) { + tmp32 = ((stt->maxLevel - stt->minLevel) * 51) >> 9; + inMicLevelTmp = (stt->minLevel + tmp32); + stt->micVol = inMicLevelTmp; + } + + if (inMicLevelTmp != stt->micVol) { + if (inMicLevel == stt->lastInMicLevel) { + // We requested a volume adjustment, but it didn't occur. This is + // probably due to a coarse quantization of the volume slider. + // Restore the requested value to prevent getting stuck. + inMicLevelTmp = stt->micVol; + } else { + // As long as the value changed, update to match. + stt->micVol = inMicLevelTmp; + } + } + + if (inMicLevelTmp > stt->maxLevel) { + // Always allow the user to raise the volume above the maxLevel. + stt->maxLevel = inMicLevelTmp; + } + + // Store last value here, after we've taken care of manual updates etc. + stt->lastInMicLevel = inMicLevel; + lastMicVol = stt->micVol; + + /* Checks if the signal is saturated. Also a check if individual samples + * are larger than 12000 is done. If they are the counter for increasing + * the volume level is set to -100ms + */ + WebRtcAgc_SaturationCtrl(stt, &saturated, stt->env[0]); + + /* The AGC is always allowed to lower the level if the signal is saturated */ + if (saturated == 1) { + /* Lower the recording level + * Rxx160_LP is adjusted down because it is so slow it could + * cause the AGC to make wrong decisions. */ + /* stt->Rxx160_LPw32 *= 0.875; */ + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 8) * 7; + + stt->zeroCtrlMax = stt->micVol; + + /* stt->micVol *= 0.903; */ + tmp32 = inMicLevelTmp - stt->minLevel; + tmpU32 = WEBRTC_SPL_UMUL(29591, (uint32_t)(tmp32)); + stt->micVol = (tmpU32 >> 15) + stt->minLevel; + if (stt->micVol > lastMicVol - 2) { + stt->micVol = lastMicVol - 2; + } + inMicLevelTmp = stt->micVol; + + if (stt->micVol < stt->minOutput) { + *saturationWarning = 1; + } + + /* Reset counter for decrease of volume level to avoid + * decreasing too much. The saturation control can still + * lower the level if needed. */ + stt->msTooHigh = -100; + + /* Enable the control mechanism to ensure that our measure, + * Rxx160_LP, is in the correct range. This must be done since + * the measure is very slow. */ + stt->activeSpeech = 0; + stt->Rxx16_LPw32Max = 0; + + /* Reset to initial values */ + stt->msecSpeechInnerChange = kMsecSpeechInner; + stt->msecSpeechOuterChange = kMsecSpeechOuter; + stt->changeToSlowMode = 0; + + stt->muteGuardMs = 0; + + stt->upperLimit = stt->startUpperLimit; + stt->lowerLimit = stt->startLowerLimit; + } + + /* Check if the input speech is zero. If so the mic volume + * is increased. On some computers the input is zero up as high + * level as 17% */ + WebRtcAgc_ZeroCtrl(stt, &inMicLevelTmp, stt->env[0]); + + /* Check if the near end speaker is inactive. + * If that is the case the VAD threshold is + * increased since the VAD speech model gets + * more sensitive to any sound after a long + * silence. + */ + WebRtcAgc_SpeakerInactiveCtrl(stt); + + for (i = 0; i < 5; i++) { + /* Computed on blocks of 16 samples */ + + Rxx16w32 = stt->Rxx16w32_array[0][i]; + + /* Rxx160w32 in Q(-7) */ + tmp32 = (Rxx16w32 - stt->Rxx16_vectorw32[stt->Rxx16pos]) >> 3; + stt->Rxx160w32 = stt->Rxx160w32 + tmp32; + stt->Rxx16_vectorw32[stt->Rxx16pos] = Rxx16w32; + + /* Circular buffer */ + stt->Rxx16pos++; + if (stt->Rxx16pos == kRxxBufferLen) { + stt->Rxx16pos = 0; + } + + /* Rxx16_LPw32 in Q(-4) */ + tmp32 = (Rxx16w32 - stt->Rxx16_LPw32) >> kAlphaShortTerm; + stt->Rxx16_LPw32 = (stt->Rxx16_LPw32) + tmp32; + + if (vadLogRatio > stt->vadThreshold) { + /* Speech detected! */ + + /* Check if Rxx160_LP is in the correct range. If + * it is too high/low then we set it to the maximum of + * Rxx16_LPw32 during the first 200ms of speech. + */ + if (stt->activeSpeech < 250) { + stt->activeSpeech += 2; + + if (stt->Rxx16_LPw32 > stt->Rxx16_LPw32Max) { + stt->Rxx16_LPw32Max = stt->Rxx16_LPw32; + } + } else if (stt->activeSpeech == 250) { + stt->activeSpeech += 2; + tmp32 = stt->Rxx16_LPw32Max >> 3; + stt->Rxx160_LPw32 = tmp32 * kRxxBufferLen; + } + + tmp32 = (stt->Rxx160w32 - stt->Rxx160_LPw32) >> kAlphaLongTerm; + stt->Rxx160_LPw32 = stt->Rxx160_LPw32 + tmp32; + + if (stt->Rxx160_LPw32 > stt->upperSecondaryLimit) { + stt->msTooHigh += 2; + stt->msTooLow = 0; + stt->changeToSlowMode = 0; + + if (stt->msTooHigh > stt->msecSpeechOuterChange) { + stt->msTooHigh = 0; + + /* Lower the recording level */ + /* Multiply by 0.828125 which corresponds to decreasing ~0.8dB */ + tmp32 = stt->Rxx160_LPw32 >> 6; + stt->Rxx160_LPw32 = tmp32 * 53; + + /* Reduce the max gain to avoid excessive oscillation + * (but never drop below the maximum analog level). + */ + stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; + stt->maxLevel = WEBRTC_SPL_MAX(stt->maxLevel, stt->maxAnalog); + + stt->zeroCtrlMax = stt->micVol; + + /* 0.95 in Q15 */ + tmp32 = inMicLevelTmp - stt->minLevel; + tmpU32 = WEBRTC_SPL_UMUL(31130, (uint32_t)(tmp32)); + stt->micVol = (tmpU32 >> 15) + stt->minLevel; + if (stt->micVol > lastMicVol - 1) { + stt->micVol = lastMicVol - 1; + } + inMicLevelTmp = stt->micVol; + + /* Enable the control mechanism to ensure that our measure, + * Rxx160_LP, is in the correct range. + */ + stt->activeSpeech = 0; + stt->Rxx16_LPw32Max = 0; + } + } else if (stt->Rxx160_LPw32 > stt->upperLimit) { + stt->msTooHigh += 2; + stt->msTooLow = 0; + stt->changeToSlowMode = 0; + + if (stt->msTooHigh > stt->msecSpeechInnerChange) { + /* Lower the recording level */ + stt->msTooHigh = 0; + /* Multiply by 0.828125 which corresponds to decreasing ~0.8dB */ + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 53; + + /* Reduce the max gain to avoid excessive oscillation + * (but never drop below the maximum analog level). + */ + stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; + stt->maxLevel = WEBRTC_SPL_MAX(stt->maxLevel, stt->maxAnalog); + + stt->zeroCtrlMax = stt->micVol; + + /* 0.965 in Q15 */ + tmp32 = inMicLevelTmp - stt->minLevel; + tmpU32 = + WEBRTC_SPL_UMUL(31621, (uint32_t)(inMicLevelTmp - stt->minLevel)); + stt->micVol = (tmpU32 >> 15) + stt->minLevel; + if (stt->micVol > lastMicVol - 1) { + stt->micVol = lastMicVol - 1; + } + inMicLevelTmp = stt->micVol; + } + } else if (stt->Rxx160_LPw32 < stt->lowerSecondaryLimit) { + stt->msTooHigh = 0; + stt->changeToSlowMode = 0; + stt->msTooLow += 2; + + if (stt->msTooLow > stt->msecSpeechOuterChange) { + /* Raise the recording level */ + int16_t index, weightFIX; + int16_t volNormFIX = 16384; // =1 in Q14. + + stt->msTooLow = 0; + + /* Normalize the volume level */ + tmp32 = (inMicLevelTmp - stt->minLevel) << 14; + if (stt->maxInit != stt->minLevel) { + volNormFIX = tmp32 / (stt->maxInit - stt->minLevel); + } + + /* Find correct curve */ + WebRtcAgc_ExpCurve(volNormFIX, &index); + + /* Compute weighting factor for the volume increase, 32^(-2*X)/2+1.05 + */ + weightFIX = + kOffset1[index] - (int16_t)((kSlope1[index] * volNormFIX) >> 13); + + /* stt->Rxx160_LPw32 *= 1.047 [~0.2 dB]; */ + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 67; + + tmp32 = inMicLevelTmp - stt->minLevel; + tmpU32 = + ((uint32_t)weightFIX * (uint32_t)(inMicLevelTmp - stt->minLevel)); + stt->micVol = (tmpU32 >> 14) + stt->minLevel; + if (stt->micVol < lastMicVol + 2) { + stt->micVol = lastMicVol + 2; + } + + inMicLevelTmp = stt->micVol; + } + } else if (stt->Rxx160_LPw32 < stt->lowerLimit) { + stt->msTooHigh = 0; + stt->changeToSlowMode = 0; + stt->msTooLow += 2; + + if (stt->msTooLow > stt->msecSpeechInnerChange) { + /* Raise the recording level */ + int16_t index, weightFIX; + int16_t volNormFIX = 16384; // =1 in Q14. + + stt->msTooLow = 0; + + /* Normalize the volume level */ + tmp32 = (inMicLevelTmp - stt->minLevel) << 14; + if (stt->maxInit != stt->minLevel) { + volNormFIX = tmp32 / (stt->maxInit - stt->minLevel); + } + + /* Find correct curve */ + WebRtcAgc_ExpCurve(volNormFIX, &index); + + /* Compute weighting factor for the volume increase, (3.^(-2.*X))/8+1 + */ + weightFIX = + kOffset2[index] - (int16_t)((kSlope2[index] * volNormFIX) >> 13); + + /* stt->Rxx160_LPw32 *= 1.047 [~0.2 dB]; */ + stt->Rxx160_LPw32 = (stt->Rxx160_LPw32 / 64) * 67; + + tmp32 = inMicLevelTmp - stt->minLevel; + tmpU32 = + ((uint32_t)weightFIX * (uint32_t)(inMicLevelTmp - stt->minLevel)); + stt->micVol = (tmpU32 >> 14) + stt->minLevel; + if (stt->micVol < lastMicVol + 1) { + stt->micVol = lastMicVol + 1; + } + + inMicLevelTmp = stt->micVol; + } + } else { + /* The signal is inside the desired range which is: + * lowerLimit < Rxx160_LP/640 < upperLimit + */ + if (stt->changeToSlowMode > 4000) { + stt->msecSpeechInnerChange = 1000; + stt->msecSpeechOuterChange = 500; + stt->upperLimit = stt->upperPrimaryLimit; + stt->lowerLimit = stt->lowerPrimaryLimit; + } else { + stt->changeToSlowMode += 2; // in milliseconds + } + stt->msTooLow = 0; + stt->msTooHigh = 0; + + stt->micVol = inMicLevelTmp; + } + } + } + + /* Ensure gain is not increased in presence of echo or after a mute event + * (but allow the zeroCtrl() increase on the frame of a mute detection). + */ + if (echo == 1 || + (stt->muteGuardMs > 0 && stt->muteGuardMs < kMuteGuardTimeMs)) { + if (stt->micVol > lastMicVol) { + stt->micVol = lastMicVol; + } + } + + /* limit the gain */ + if (stt->micVol > stt->maxLevel) { + stt->micVol = stt->maxLevel; + } else if (stt->micVol < stt->minOutput) { + stt->micVol = stt->minOutput; + } + + *outMicLevel = WEBRTC_SPL_MIN(stt->micVol, stt->maxAnalog) >> stt->scale; + + return 0; +} + +int WebRtcAgc_Analyze(void* agcInst, + const int16_t* const* in_near, + size_t num_bands, + size_t samples, + int32_t inMicLevel, + int32_t* outMicLevel, + int16_t echo, + uint8_t* saturationWarning, + int32_t gains[11]) { + LegacyAgc* stt = reinterpret_cast(agcInst); + + if (stt == NULL) { + return -1; + } + + if (stt->fs == 8000) { + if (samples != 80) { + return -1; + } + } else if (stt->fs == 16000 || stt->fs == 32000 || stt->fs == 48000) { + if (samples != 160) { + return -1; + } + } else { + return -1; + } + + *saturationWarning = 0; + // TODO(minyue): PUT IN RANGE CHECKING FOR INPUT LEVELS + *outMicLevel = inMicLevel; + + int32_t error = + WebRtcAgc_ComputeDigitalGains(&stt->digitalAgc, in_near, num_bands, + stt->fs, stt->lowLevelSignal, gains); + if (error == -1) { + return -1; + } + + if (stt->agcMode < kAgcModeFixedDigital && + (stt->lowLevelSignal == 0 || stt->agcMode != kAgcModeAdaptiveDigital)) { + if (WebRtcAgc_ProcessAnalog(agcInst, inMicLevel, outMicLevel, + stt->vadMic.logRatio, echo, + saturationWarning) == -1) { + return -1; + } + } + + /* update queue */ + if (stt->inQueue > 1) { + memcpy(stt->env[0], stt->env[1], 10 * sizeof(int32_t)); + memcpy(stt->Rxx16w32_array[0], stt->Rxx16w32_array[1], 5 * sizeof(int32_t)); + } + + if (stt->inQueue > 0) { + stt->inQueue--; + } + + return 0; +} + +int WebRtcAgc_Process(const void* agcInst, + const int32_t gains[11], + const int16_t* const* in_near, + size_t num_bands, + int16_t* const* out) { + const LegacyAgc* stt = (const LegacyAgc*)agcInst; + return WebRtcAgc_ApplyDigitalGains(gains, num_bands, stt->fs, in_near, out); +} + +int WebRtcAgc_set_config(void* agcInst, WebRtcAgcConfig agcConfig) { + LegacyAgc* stt; + stt = reinterpret_cast(agcInst); + + if (stt == NULL) { + return -1; + } + + if (stt->initFlag != kInitCheck) { + stt->lastError = AGC_UNINITIALIZED_ERROR; + return -1; + } + + if (agcConfig.limiterEnable != kAgcFalse && + agcConfig.limiterEnable != kAgcTrue) { + stt->lastError = AGC_BAD_PARAMETER_ERROR; + return -1; + } + stt->limiterEnable = agcConfig.limiterEnable; + stt->compressionGaindB = agcConfig.compressionGaindB; + if ((agcConfig.targetLevelDbfs < 0) || (agcConfig.targetLevelDbfs > 31)) { + stt->lastError = AGC_BAD_PARAMETER_ERROR; + return -1; + } + stt->targetLevelDbfs = agcConfig.targetLevelDbfs; + + if (stt->agcMode == kAgcModeFixedDigital) { + /* Adjust for different parameter interpretation in FixedDigital mode */ + stt->compressionGaindB += agcConfig.targetLevelDbfs; + } + + /* Update threshold levels for analog adaptation */ + WebRtcAgc_UpdateAgcThresholds(stt); + + /* Recalculate gain table */ + if (WebRtcAgc_CalculateGainTable( + &(stt->digitalAgc.gainTable[0]), stt->compressionGaindB, + stt->targetLevelDbfs, stt->limiterEnable, stt->analogTarget) == -1) { + return -1; + } + /* Store the config in a WebRtcAgcConfig */ + stt->usedConfig.compressionGaindB = agcConfig.compressionGaindB; + stt->usedConfig.limiterEnable = agcConfig.limiterEnable; + stt->usedConfig.targetLevelDbfs = agcConfig.targetLevelDbfs; + + return 0; +} + +int WebRtcAgc_get_config(void* agcInst, WebRtcAgcConfig* config) { + LegacyAgc* stt; + stt = reinterpret_cast(agcInst); + + if (stt == NULL) { + return -1; + } + + if (config == NULL) { + stt->lastError = AGC_NULL_POINTER_ERROR; + return -1; + } + + if (stt->initFlag != kInitCheck) { + stt->lastError = AGC_UNINITIALIZED_ERROR; + return -1; + } + + config->limiterEnable = stt->usedConfig.limiterEnable; + config->targetLevelDbfs = stt->usedConfig.targetLevelDbfs; + config->compressionGaindB = stt->usedConfig.compressionGaindB; + + return 0; +} + +void* WebRtcAgc_Create() { + LegacyAgc* stt = static_cast(malloc(sizeof(LegacyAgc))); + + stt->initFlag = 0; + stt->lastError = 0; + + return stt; +} + +void WebRtcAgc_Free(void* state) { + LegacyAgc* stt; + + stt = reinterpret_cast(state); + free(stt); +} + +/* minLevel - Minimum volume level + * maxLevel - Maximum volume level + */ +int WebRtcAgc_Init(void* agcInst, + int32_t minLevel, + int32_t maxLevel, + int16_t agcMode, + uint32_t fs) { + int32_t max_add, tmp32; + int16_t i; + int tmpNorm; + LegacyAgc* stt; + + /* typecast state pointer */ + stt = reinterpret_cast(agcInst); + + if (WebRtcAgc_InitDigital(&stt->digitalAgc, agcMode) != 0) { + stt->lastError = AGC_UNINITIALIZED_ERROR; + return -1; + } + + /* Analog AGC variables */ + stt->envSum = 0; + + /* mode = 0 - Only saturation protection + * 1 - Analog Automatic Gain Control [-targetLevelDbfs (default -3 + * dBOv)] + * 2 - Digital Automatic Gain Control [-targetLevelDbfs (default -3 + * dBOv)] + * 3 - Fixed Digital Gain [compressionGaindB (default 8 dB)] + */ + if (agcMode < kAgcModeUnchanged || agcMode > kAgcModeFixedDigital) { + return -1; + } + stt->agcMode = agcMode; + stt->fs = fs; + + /* initialize input VAD */ + WebRtcAgc_InitVad(&stt->vadMic); + + /* If the volume range is smaller than 0-256 then + * the levels are shifted up to Q8-domain */ + tmpNorm = WebRtcSpl_NormU32((uint32_t)maxLevel); + stt->scale = tmpNorm - 23; + if (stt->scale < 0) { + stt->scale = 0; + } + // TODO(bjornv): Investigate if we really need to scale up a small range now + // when we have + // a guard against zero-increments. For now, we do not support scale up (scale + // = 0). + stt->scale = 0; + maxLevel <<= stt->scale; + minLevel <<= stt->scale; + + /* Make minLevel and maxLevel static in AdaptiveDigital */ + if (stt->agcMode == kAgcModeAdaptiveDigital) { + minLevel = 0; + maxLevel = 255; + stt->scale = 0; + } + /* The maximum supplemental volume range is based on a vague idea + * of how much lower the gain will be than the real analog gain. */ + max_add = (maxLevel - minLevel) / 4; + + /* Minimum/maximum volume level that can be set */ + stt->minLevel = minLevel; + stt->maxAnalog = maxLevel; + stt->maxLevel = maxLevel + max_add; + stt->maxInit = stt->maxLevel; + + stt->zeroCtrlMax = stt->maxAnalog; + stt->lastInMicLevel = 0; + + /* Initialize micVol parameter */ + stt->micVol = stt->maxAnalog; + if (stt->agcMode == kAgcModeAdaptiveDigital) { + stt->micVol = 127; /* Mid-point of mic level */ + } + stt->micRef = stt->micVol; + stt->micGainIdx = 127; + + /* Minimum output volume is 4% higher than the available lowest volume level + */ + tmp32 = ((stt->maxLevel - stt->minLevel) * 10) >> 8; + stt->minOutput = (stt->minLevel + tmp32); + + stt->msTooLow = 0; + stt->msTooHigh = 0; + stt->changeToSlowMode = 0; + stt->firstCall = 0; + stt->msZero = 0; + stt->muteGuardMs = 0; + stt->gainTableIdx = 0; + + stt->msecSpeechInnerChange = kMsecSpeechInner; + stt->msecSpeechOuterChange = kMsecSpeechOuter; + + stt->activeSpeech = 0; + stt->Rxx16_LPw32Max = 0; + + stt->vadThreshold = kNormalVadThreshold; + stt->inActive = 0; + + for (i = 0; i < kRxxBufferLen; i++) { + stt->Rxx16_vectorw32[i] = (int32_t)1000; /* -54dBm0 */ + } + stt->Rxx160w32 = 125 * kRxxBufferLen; /* (stt->Rxx16_vectorw32[0]>>3) = 125 */ + + stt->Rxx16pos = 0; + stt->Rxx16_LPw32 = (int32_t)16284; /* Q(-4) */ + + for (i = 0; i < 5; i++) { + stt->Rxx16w32_array[0][i] = 0; + } + for (i = 0; i < 10; i++) { + stt->env[0][i] = 0; + stt->env[1][i] = 0; + } + stt->inQueue = 0; + + WebRtcSpl_MemSetW32(stt->filterState, 0, 8); + + stt->initFlag = kInitCheck; + // Default config settings. + stt->defaultConfig.limiterEnable = kAgcTrue; + stt->defaultConfig.targetLevelDbfs = AGC_DEFAULT_TARGET_LEVEL; + stt->defaultConfig.compressionGaindB = AGC_DEFAULT_COMP_GAIN; + + if (WebRtcAgc_set_config(stt, stt->defaultConfig) == -1) { + stt->lastError = AGC_UNSPECIFIED_ERROR; + return -1; + } + stt->Rxx160_LPw32 = stt->analogTargetLevel; // Initialize rms value + + stt->lowLevelSignal = 0; + + /* Only positive values are allowed that are not too large */ + if ((minLevel >= maxLevel) || (maxLevel & 0xFC000000)) { + return -1; + } else { + return 0; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc/legacy/analog_agc.h b/webrtc/modules/audio_processing/agc/legacy/analog_agc.h index 820221a..22cd924 100644 --- a/webrtc/modules/audio_processing/agc/legacy/analog_agc.h +++ b/webrtc/modules/audio_processing/agc/legacy/analog_agc.h @@ -8,17 +8,14 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_ANALOG_AGC_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_ANALOG_AGC_H_ +#ifndef MODULES_AUDIO_PROCESSING_AGC_LEGACY_ANALOG_AGC_H_ +#define MODULES_AUDIO_PROCESSING_AGC_LEGACY_ANALOG_AGC_H_ -//#define MIC_LEVEL_FEEDBACK -#ifdef WEBRTC_AGC_DEBUG_DUMP -#include -#endif -#include "webrtc/modules/audio_processing/agc/legacy/digital_agc.h" -#include "webrtc/modules/audio_processing/agc/legacy/gain_control.h" -#include "webrtc/typedefs.h" +#include "modules/audio_processing/agc/legacy/digital_agc.h" +#include "modules/audio_processing/agc/legacy/gain_control.h" + +namespace webrtc { /* Analog Automatic Gain Control variables: * Constant declarations (inner limits inside which no changes are done) @@ -32,102 +29,90 @@ * of our measure Rxx160_LP. Remember that the levels are in blocks of 16 in * Q(-7). (Example matlab code: round(db2pow(-21.2)*16/2^7) ) */ -#define RXX_BUFFER_LEN 10 +constexpr int16_t kRxxBufferLen = 10; static const int16_t kMsecSpeechInner = 520; static const int16_t kMsecSpeechOuter = 340; static const int16_t kNormalVadThreshold = 400; -static const int16_t kAlphaShortTerm = 6; // 1 >> 6 = 0.0156 -static const int16_t kAlphaLongTerm = 10; // 1 >> 10 = 0.000977 +static const int16_t kAlphaShortTerm = 6; // 1 >> 6 = 0.0156 +static const int16_t kAlphaLongTerm = 10; // 1 >> 10 = 0.000977 -typedef struct -{ - // Configurable parameters/variables - uint32_t fs; // Sampling frequency - int16_t compressionGaindB; // Fixed gain level in dB - int16_t targetLevelDbfs; // Target level in -dBfs of envelope (default -3) - int16_t agcMode; // Hard coded mode (adaptAna/adaptDig/fixedDig) - uint8_t limiterEnable; // Enabling limiter (on/off (default off)) - WebRtcAgcConfig defaultConfig; - WebRtcAgcConfig usedConfig; +typedef struct { + // Configurable parameters/variables + uint32_t fs; // Sampling frequency + int16_t compressionGaindB; // Fixed gain level in dB + int16_t targetLevelDbfs; // Target level in -dBfs of envelope (default -3) + int16_t agcMode; // Hard coded mode (adaptAna/adaptDig/fixedDig) + uint8_t limiterEnable; // Enabling limiter (on/off (default off)) + WebRtcAgcConfig defaultConfig; + WebRtcAgcConfig usedConfig; - // General variables - int16_t initFlag; - int16_t lastError; + // General variables + int16_t initFlag; + int16_t lastError; - // Target level parameters - // Based on the above: analogTargetLevel = round((32767*10^(-22/20))^2*16/2^7) - int32_t analogTargetLevel; // = RXX_BUFFER_LEN * 846805; -22 dBfs - int32_t startUpperLimit; // = RXX_BUFFER_LEN * 1066064; -21 dBfs - int32_t startLowerLimit; // = RXX_BUFFER_LEN * 672641; -23 dBfs - int32_t upperPrimaryLimit; // = RXX_BUFFER_LEN * 1342095; -20 dBfs - int32_t lowerPrimaryLimit; // = RXX_BUFFER_LEN * 534298; -24 dBfs - int32_t upperSecondaryLimit;// = RXX_BUFFER_LEN * 2677832; -17 dBfs - int32_t lowerSecondaryLimit;// = RXX_BUFFER_LEN * 267783; -27 dBfs - uint16_t targetIdx; // Table index for corresponding target level -#ifdef MIC_LEVEL_FEEDBACK - uint16_t targetIdxOffset; // Table index offset for level compensation -#endif - int16_t analogTarget; // Digital reference level in ENV scale + // Target level parameters + // Based on the above: analogTargetLevel = round((32767*10^(-22/20))^2*16/2^7) + int32_t analogTargetLevel; // = kRxxBufferLen * 846805; -22 dBfs + int32_t startUpperLimit; // = kRxxBufferLen * 1066064; -21 dBfs + int32_t startLowerLimit; // = kRxxBufferLen * 672641; -23 dBfs + int32_t upperPrimaryLimit; // = kRxxBufferLen * 1342095; -20 dBfs + int32_t lowerPrimaryLimit; // = kRxxBufferLen * 534298; -24 dBfs + int32_t upperSecondaryLimit; // = kRxxBufferLen * 2677832; -17 dBfs + int32_t lowerSecondaryLimit; // = kRxxBufferLen * 267783; -27 dBfs + uint16_t targetIdx; // Table index for corresponding target level + int16_t analogTarget; // Digital reference level in ENV scale - // Analog AGC specific variables - int32_t filterState[8]; // For downsampling wb to nb - int32_t upperLimit; // Upper limit for mic energy - int32_t lowerLimit; // Lower limit for mic energy - int32_t Rxx160w32; // Average energy for one frame - int32_t Rxx16_LPw32; // Low pass filtered subframe energies - int32_t Rxx160_LPw32; // Low pass filtered frame energies - int32_t Rxx16_LPw32Max; // Keeps track of largest energy subframe - int32_t Rxx16_vectorw32[RXX_BUFFER_LEN];// Array with subframe energies - int32_t Rxx16w32_array[2][5];// Energy values of microphone signal - int32_t env[2][10]; // Envelope values of subframes + // Analog AGC specific variables + int32_t filterState[8]; // For downsampling wb to nb + int32_t upperLimit; // Upper limit for mic energy + int32_t lowerLimit; // Lower limit for mic energy + int32_t Rxx160w32; // Average energy for one frame + int32_t Rxx16_LPw32; // Low pass filtered subframe energies + int32_t Rxx160_LPw32; // Low pass filtered frame energies + int32_t Rxx16_LPw32Max; // Keeps track of largest energy subframe + int32_t Rxx16_vectorw32[kRxxBufferLen]; // Array with subframe energies + int32_t Rxx16w32_array[2][5]; // Energy values of microphone signal + int32_t env[2][10]; // Envelope values of subframes - int16_t Rxx16pos; // Current position in the Rxx16_vectorw32 - int16_t envSum; // Filtered scaled envelope in subframes - int16_t vadThreshold; // Threshold for VAD decision - int16_t inActive; // Inactive time in milliseconds - int16_t msTooLow; // Milliseconds of speech at a too low level - int16_t msTooHigh; // Milliseconds of speech at a too high level - int16_t changeToSlowMode; // Change to slow mode after some time at target - int16_t firstCall; // First call to the process-function - int16_t msZero; // Milliseconds of zero input - int16_t msecSpeechOuterChange;// Min ms of speech between volume changes - int16_t msecSpeechInnerChange;// Min ms of speech between volume changes - int16_t activeSpeech; // Milliseconds of active speech - int16_t muteGuardMs; // Counter to prevent mute action - int16_t inQueue; // 10 ms batch indicator + int16_t Rxx16pos; // Current position in the Rxx16_vectorw32 + int16_t envSum; // Filtered scaled envelope in subframes + int16_t vadThreshold; // Threshold for VAD decision + int16_t inActive; // Inactive time in milliseconds + int16_t msTooLow; // Milliseconds of speech at a too low level + int16_t msTooHigh; // Milliseconds of speech at a too high level + int16_t changeToSlowMode; // Change to slow mode after some time at target + int16_t firstCall; // First call to the process-function + int16_t msZero; // Milliseconds of zero input + int16_t msecSpeechOuterChange; // Min ms of speech between volume changes + int16_t msecSpeechInnerChange; // Min ms of speech between volume changes + int16_t activeSpeech; // Milliseconds of active speech + int16_t muteGuardMs; // Counter to prevent mute action + int16_t inQueue; // 10 ms batch indicator - // Microphone level variables - int32_t micRef; // Remember ref. mic level for virtual mic - uint16_t gainTableIdx; // Current position in virtual gain table - int32_t micGainIdx; // Gain index of mic level to increase slowly - int32_t micVol; // Remember volume between frames - int32_t maxLevel; // Max possible vol level, incl dig gain - int32_t maxAnalog; // Maximum possible analog volume level - int32_t maxInit; // Initial value of "max" - int32_t minLevel; // Minimum possible volume level - int32_t minOutput; // Minimum output volume level - int32_t zeroCtrlMax; // Remember max gain => don't amp low input - int32_t lastInMicLevel; + // Microphone level variables + int32_t micRef; // Remember ref. mic level for virtual mic + uint16_t gainTableIdx; // Current position in virtual gain table + int32_t micGainIdx; // Gain index of mic level to increase slowly + int32_t micVol; // Remember volume between frames + int32_t maxLevel; // Max possible vol level, incl dig gain + int32_t maxAnalog; // Maximum possible analog volume level + int32_t maxInit; // Initial value of "max" + int32_t minLevel; // Minimum possible volume level + int32_t minOutput; // Minimum output volume level + int32_t zeroCtrlMax; // Remember max gain => don't amp low input + int32_t lastInMicLevel; - int16_t scale; // Scale factor for internal volume levels -#ifdef MIC_LEVEL_FEEDBACK - int16_t numBlocksMicLvlSat; - uint8_t micLvlSat; -#endif - // Structs for VAD and digital_agc - AgcVad vadMic; - DigitalAgc digitalAgc; + int16_t scale; // Scale factor for internal volume levels + // Structs for VAD and digital_agc + AgcVad vadMic; + DigitalAgc digitalAgc; -#ifdef WEBRTC_AGC_DEBUG_DUMP - FILE* fpt; - FILE* agcLog; - int32_t fcount; -#endif - - int16_t lowLevelSignal; + int16_t lowLevelSignal; } LegacyAgc; -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_ANALOG_AGC_H_ +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_LEGACY_ANALOG_AGC_H_ diff --git a/webrtc/modules/audio_processing/agc/legacy/digital_agc.c b/webrtc/modules/audio_processing/agc/legacy/digital_agc.c deleted file mode 100644 index aeafb65..0000000 --- a/webrtc/modules/audio_processing/agc/legacy/digital_agc.c +++ /dev/null @@ -1,772 +0,0 @@ -/* - * 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. - */ - -/* digital_agc.c - * - */ - -#include "webrtc/modules/audio_processing/agc/legacy/digital_agc.h" - -#include -#include -#ifdef WEBRTC_AGC_DEBUG_DUMP -#include -#endif - -#include "webrtc/modules/audio_processing/agc/legacy/gain_control.h" - -// To generate the gaintable, copy&paste the following lines to a Matlab window: -// MaxGain = 6; MinGain = 0; CompRatio = 3; Knee = 1; -// zeros = 0:31; lvl = 2.^(1-zeros); -// A = -10*log10(lvl) * (CompRatio - 1) / CompRatio; -// B = MaxGain - MinGain; -// gains = round(2^16*10.^(0.05 * (MinGain + B * ( log(exp(-Knee*A)+exp(-Knee*B)) - log(1+exp(-Knee*B)) ) / log(1/(1+exp(Knee*B)))))); -// fprintf(1, '\t%i, %i, %i, %i,\n', gains); -// % Matlab code for plotting the gain and input/output level characteristic (copy/paste the following 3 lines): -// in = 10*log10(lvl); out = 20*log10(gains/65536); -// subplot(121); plot(in, out); axis([-30, 0, -5, 20]); grid on; xlabel('Input (dB)'); ylabel('Gain (dB)'); -// subplot(122); plot(in, in+out); axis([-30, 0, -30, 5]); grid on; xlabel('Input (dB)'); ylabel('Output (dB)'); -// zoom on; - -// Generator table for y=log2(1+e^x) in Q8. -enum { kGenFuncTableSize = 128 }; -static const uint16_t kGenFuncTable[kGenFuncTableSize] = { - 256, 485, 786, 1126, 1484, 1849, 2217, 2586, - 2955, 3324, 3693, 4063, 4432, 4801, 5171, 5540, - 5909, 6279, 6648, 7017, 7387, 7756, 8125, 8495, - 8864, 9233, 9603, 9972, 10341, 10711, 11080, 11449, - 11819, 12188, 12557, 12927, 13296, 13665, 14035, 14404, - 14773, 15143, 15512, 15881, 16251, 16620, 16989, 17359, - 17728, 18097, 18466, 18836, 19205, 19574, 19944, 20313, - 20682, 21052, 21421, 21790, 22160, 22529, 22898, 23268, - 23637, 24006, 24376, 24745, 25114, 25484, 25853, 26222, - 26592, 26961, 27330, 27700, 28069, 28438, 28808, 29177, - 29546, 29916, 30285, 30654, 31024, 31393, 31762, 32132, - 32501, 32870, 33240, 33609, 33978, 34348, 34717, 35086, - 35456, 35825, 36194, 36564, 36933, 37302, 37672, 38041, - 38410, 38780, 39149, 39518, 39888, 40257, 40626, 40996, - 41365, 41734, 42104, 42473, 42842, 43212, 43581, 43950, - 44320, 44689, 45058, 45428, 45797, 46166, 46536, 46905 -}; - -static const int16_t kAvgDecayTime = 250; // frames; < 3000 - -int32_t WebRtcAgc_CalculateGainTable(int32_t *gainTable, // Q16 - int16_t digCompGaindB, // Q0 - int16_t targetLevelDbfs,// Q0 - uint8_t limiterEnable, - int16_t analogTarget) // Q0 -{ - // This function generates the compressor gain table used in the fixed digital part. - uint32_t tmpU32no1, tmpU32no2, absInLevel, logApprox; - int32_t inLevel, limiterLvl; - int32_t tmp32, tmp32no1, tmp32no2, numFIX, den, y32; - const uint16_t kLog10 = 54426; // log2(10) in Q14 - const uint16_t kLog10_2 = 49321; // 10*log10(2) in Q14 - const uint16_t kLogE_1 = 23637; // log2(e) in Q14 - uint16_t constMaxGain; - uint16_t tmpU16, intPart, fracPart; - const int16_t kCompRatio = 3; - const int16_t kSoftLimiterLeft = 1; - int16_t limiterOffset = 0; // Limiter offset - int16_t limiterIdx, limiterLvlX; - int16_t constLinApprox, zeroGainLvl, maxGain, diffGain; - int16_t i, tmp16, tmp16no1; - int zeros, zerosScale; - - // Constants -// kLogE_1 = 23637; // log2(e) in Q14 -// kLog10 = 54426; // log2(10) in Q14 -// kLog10_2 = 49321; // 10*log10(2) in Q14 - - // Calculate maximum digital gain and zero gain level - tmp32no1 = (digCompGaindB - analogTarget) * (kCompRatio - 1); - tmp16no1 = analogTarget - targetLevelDbfs; - tmp16no1 += WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio); - maxGain = WEBRTC_SPL_MAX(tmp16no1, (analogTarget - targetLevelDbfs)); - tmp32no1 = maxGain * kCompRatio; - zeroGainLvl = digCompGaindB; - zeroGainLvl -= WebRtcSpl_DivW32W16ResW16(tmp32no1 + ((kCompRatio - 1) >> 1), - kCompRatio - 1); - if ((digCompGaindB <= analogTarget) && (limiterEnable)) - { - zeroGainLvl += (analogTarget - digCompGaindB + kSoftLimiterLeft); - limiterOffset = 0; - } - - // Calculate the difference between maximum gain and gain at 0dB0v: - // diffGain = maxGain + (compRatio-1)*zeroGainLvl/compRatio - // = (compRatio-1)*digCompGaindB/compRatio - tmp32no1 = digCompGaindB * (kCompRatio - 1); - diffGain = WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio); - if (diffGain < 0 || diffGain >= kGenFuncTableSize) - { - assert(0); - return -1; - } - - // Calculate the limiter level and index: - // limiterLvlX = analogTarget - limiterOffset - // limiterLvl = targetLevelDbfs + limiterOffset/compRatio - limiterLvlX = analogTarget - limiterOffset; - limiterIdx = - 2 + WebRtcSpl_DivW32W16ResW16((int32_t)limiterLvlX << 13, kLog10_2 / 2); - tmp16no1 = WebRtcSpl_DivW32W16ResW16(limiterOffset + (kCompRatio >> 1), kCompRatio); - limiterLvl = targetLevelDbfs + tmp16no1; - - // Calculate (through table lookup): - // constMaxGain = log2(1+2^(log2(e)*diffGain)); (in Q8) - constMaxGain = kGenFuncTable[diffGain]; // in Q8 - - // Calculate a parameter used to approximate the fractional part of 2^x with a - // piecewise linear function in Q14: - // constLinApprox = round(3/2*(4*(3-2*sqrt(2))/(log(2)^2)-0.5)*2^14); - constLinApprox = 22817; // in Q14 - - // Calculate a denominator used in the exponential part to convert from dB to linear scale: - // den = 20*constMaxGain (in Q8) - den = WEBRTC_SPL_MUL_16_U16(20, constMaxGain); // in Q8 - - for (i = 0; i < 32; i++) - { - // Calculate scaled input level (compressor): - // inLevel = fix((-constLog10_2*(compRatio-1)*(1-i)+fix(compRatio/2))/compRatio) - tmp16 = (int16_t)((kCompRatio - 1) * (i - 1)); // Q0 - tmp32 = WEBRTC_SPL_MUL_16_U16(tmp16, kLog10_2) + 1; // Q14 - inLevel = WebRtcSpl_DivW32W16(tmp32, kCompRatio); // Q14 - - // Calculate diffGain-inLevel, to map using the genFuncTable - inLevel = ((int32_t)diffGain << 14) - inLevel; // Q14 - - // Make calculations on abs(inLevel) and compensate for the sign afterwards. - absInLevel = (uint32_t)WEBRTC_SPL_ABS_W32(inLevel); // Q14 - - // LUT with interpolation - intPart = (uint16_t)(absInLevel >> 14); - fracPart = (uint16_t)(absInLevel & 0x00003FFF); // extract the fractional part - tmpU16 = kGenFuncTable[intPart + 1] - kGenFuncTable[intPart]; // Q8 - tmpU32no1 = tmpU16 * fracPart; // Q22 - tmpU32no1 += (uint32_t)kGenFuncTable[intPart] << 14; // Q22 - logApprox = tmpU32no1 >> 8; // Q14 - // Compensate for negative exponent using the relation: - // log2(1 + 2^-x) = log2(1 + 2^x) - x - if (inLevel < 0) - { - zeros = WebRtcSpl_NormU32(absInLevel); - zerosScale = 0; - if (zeros < 15) - { - // Not enough space for multiplication - tmpU32no2 = absInLevel >> (15 - zeros); // Q(zeros-1) - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no2, kLogE_1); // Q(zeros+13) - if (zeros < 9) - { - zerosScale = 9 - zeros; - tmpU32no1 >>= zerosScale; // Q(zeros+13) - } else - { - tmpU32no2 >>= zeros - 9; // Q22 - } - } else - { - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(absInLevel, kLogE_1); // Q28 - tmpU32no2 >>= 6; // Q22 - } - logApprox = 0; - if (tmpU32no2 < tmpU32no1) - { - logApprox = (tmpU32no1 - tmpU32no2) >> (8 - zerosScale); //Q14 - } - } - numFIX = (maxGain * constMaxGain) << 6; // Q14 - numFIX -= (int32_t)logApprox * diffGain; // Q14 - - // Calculate ratio - // Shift |numFIX| as much as possible. - // Ensure we avoid wrap-around in |den| as well. - if (numFIX > (den >> 8)) // |den| is Q8. - { - zeros = WebRtcSpl_NormW32(numFIX); - } else - { - zeros = WebRtcSpl_NormW32(den) + 8; - } - numFIX <<= zeros; // Q(14+zeros) - - // Shift den so we end up in Qy1 - tmp32no1 = WEBRTC_SPL_SHIFT_W32(den, zeros - 8); // Q(zeros) - if (numFIX < 0) - { - numFIX -= tmp32no1 / 2; - } else - { - numFIX += tmp32no1 / 2; - } - y32 = numFIX / tmp32no1; // in Q14 - if (limiterEnable && (i < limiterIdx)) - { - tmp32 = WEBRTC_SPL_MUL_16_U16(i - 1, kLog10_2); // Q14 - tmp32 -= limiterLvl << 14; // Q14 - y32 = WebRtcSpl_DivW32W16(tmp32 + 10, 20); - } - if (y32 > 39000) - { - tmp32 = (y32 >> 1) * kLog10 + 4096; // in Q27 - tmp32 >>= 13; // In Q14. - } else - { - tmp32 = y32 * kLog10 + 8192; // in Q28 - tmp32 >>= 14; // In Q14. - } - tmp32 += 16 << 14; // in Q14 (Make sure final output is in Q16) - - // Calculate power - if (tmp32 > 0) - { - intPart = (int16_t)(tmp32 >> 14); - fracPart = (uint16_t)(tmp32 & 0x00003FFF); // in Q14 - if ((fracPart >> 13) != 0) - { - tmp16 = (2 << 14) - constLinApprox; - tmp32no2 = (1 << 14) - fracPart; - tmp32no2 *= tmp16; - tmp32no2 >>= 13; - tmp32no2 = (1 << 14) - tmp32no2; - } else - { - tmp16 = constLinApprox - (1 << 14); - tmp32no2 = (fracPart * tmp16) >> 13; - } - fracPart = (uint16_t)tmp32no2; - gainTable[i] = - (1 << intPart) + WEBRTC_SPL_SHIFT_W32(fracPart, intPart - 14); - } else - { - gainTable[i] = 0; - } - } - - return 0; -} - -int32_t WebRtcAgc_InitDigital(DigitalAgc* stt, int16_t agcMode) { - if (agcMode == kAgcModeFixedDigital) - { - // start at minimum to find correct gain faster - stt->capacitorSlow = 0; - } else - { - // start out with 0 dB gain - stt->capacitorSlow = 134217728; // (int32_t)(0.125f * 32768.0f * 32768.0f); - } - stt->capacitorFast = 0; - stt->gain = 65536; - stt->gatePrevious = 0; - stt->agcMode = agcMode; -#ifdef WEBRTC_AGC_DEBUG_DUMP - stt->frameCounter = 0; -#endif - - // initialize VADs - WebRtcAgc_InitVad(&stt->vadNearend); - WebRtcAgc_InitVad(&stt->vadFarend); - - return 0; -} - -int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc* stt, - const int16_t* in_far, - size_t nrSamples) { - assert(stt != NULL); - // VAD for far end - WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples); - - return 0; -} - -int32_t WebRtcAgc_ProcessDigital(DigitalAgc* stt, - const int16_t* const* in_near, - size_t num_bands, - int16_t* const* out, - uint32_t FS, - int16_t lowlevelSignal) { - // array for gains (one value per ms, incl start & end) - int32_t gains[11]; - - int32_t out_tmp, tmp32; - int32_t env[10]; - int32_t max_nrg; - int32_t cur_level; - int32_t gain32, delta; - int16_t logratio; - int16_t lower_thr, upper_thr; - int16_t zeros = 0, zeros_fast, frac = 0; - int16_t decay; - int16_t gate, gain_adj; - int16_t k; - size_t n, i, L; - int16_t L2; // samples/subframe - - // determine number of samples per ms - if (FS == 8000) - { - L = 8; - L2 = 3; - } else if (FS == 16000 || FS == 32000 || FS == 48000) - { - L = 16; - L2 = 4; - } else - { - return -1; - } - - for (i = 0; i < num_bands; ++i) - { - if (in_near[i] != out[i]) - { - // Only needed if they don't already point to the same place. - memcpy(out[i], in_near[i], 10 * L * sizeof(in_near[i][0])); - } - } - // VAD for near end - logratio = WebRtcAgc_ProcessVad(&stt->vadNearend, out[0], L * 10); - - // Account for far end VAD - if (stt->vadFarend.counter > 10) - { - tmp32 = 3 * logratio; - logratio = (int16_t)((tmp32 - stt->vadFarend.logRatio) >> 2); - } - - // Determine decay factor depending on VAD - // upper_thr = 1.0f; - // lower_thr = 0.25f; - upper_thr = 1024; // Q10 - lower_thr = 0; // Q10 - if (logratio > upper_thr) - { - // decay = -2^17 / DecayTime; -> -65 - decay = -65; - } else if (logratio < lower_thr) - { - decay = 0; - } else - { - // decay = (int16_t)(((lower_thr - logratio) - // * (2^27/(DecayTime*(upper_thr-lower_thr)))) >> 10); - // SUBSTITUTED: 2^27/(DecayTime*(upper_thr-lower_thr)) -> 65 - tmp32 = (lower_thr - logratio) * 65; - decay = (int16_t)(tmp32 >> 10); - } - - // adjust decay factor for long silence (detected as low standard deviation) - // This is only done in the adaptive modes - if (stt->agcMode != kAgcModeFixedDigital) - { - if (stt->vadNearend.stdLongTerm < 4000) - { - decay = 0; - } else if (stt->vadNearend.stdLongTerm < 8096) - { - // decay = (int16_t)(((stt->vadNearend.stdLongTerm - 4000) * decay) >> 12); - tmp32 = (stt->vadNearend.stdLongTerm - 4000) * decay; - decay = (int16_t)(tmp32 >> 12); - } - - if (lowlevelSignal != 0) - { - decay = 0; - } - } -#ifdef WEBRTC_AGC_DEBUG_DUMP - stt->frameCounter++; - fprintf(stt->logFile, - "%5.2f\t%d\t%d\t%d\t", - (float)(stt->frameCounter) / 100, - logratio, - decay, - stt->vadNearend.stdLongTerm); -#endif - // Find max amplitude per sub frame - // iterate over sub frames - for (k = 0; k < 10; k++) - { - // iterate over samples - max_nrg = 0; - for (n = 0; n < L; n++) - { - int32_t nrg = out[0][k * L + n] * out[0][k * L + n]; - if (nrg > max_nrg) - { - max_nrg = nrg; - } - } - env[k] = max_nrg; - } - - // Calculate gain per sub frame - gains[0] = stt->gain; - for (k = 0; k < 10; k++) - { - // Fast envelope follower - // decay time = -131000 / -1000 = 131 (ms) - stt->capacitorFast = AGC_SCALEDIFF32(-1000, stt->capacitorFast, stt->capacitorFast); - if (env[k] > stt->capacitorFast) - { - stt->capacitorFast = env[k]; - } - // Slow envelope follower - if (env[k] > stt->capacitorSlow) - { - // increase capacitorSlow - stt->capacitorSlow - = AGC_SCALEDIFF32(500, (env[k] - stt->capacitorSlow), stt->capacitorSlow); - } else - { - // decrease capacitorSlow - stt->capacitorSlow - = AGC_SCALEDIFF32(decay, stt->capacitorSlow, stt->capacitorSlow); - } - - // use maximum of both capacitors as current level - if (stt->capacitorFast > stt->capacitorSlow) - { - cur_level = stt->capacitorFast; - } else - { - cur_level = stt->capacitorSlow; - } - // Translate signal level into gain, using a piecewise linear approximation - // find number of leading zeros - zeros = WebRtcSpl_NormU32((uint32_t)cur_level); - if (cur_level == 0) - { - zeros = 31; - } - tmp32 = (cur_level << zeros) & 0x7FFFFFFF; - frac = (int16_t)(tmp32 >> 19); // Q12. - tmp32 = (stt->gainTable[zeros-1] - stt->gainTable[zeros]) * frac; - gains[k + 1] = stt->gainTable[zeros] + (tmp32 >> 12); -#ifdef WEBRTC_AGC_DEBUG_DUMP - if (k == 0) { - fprintf(stt->logFile, - "%d\t%d\t%d\t%d\t%d\n", - env[0], - cur_level, - stt->capacitorFast, - stt->capacitorSlow, - zeros); - } -#endif - } - - // Gate processing (lower gain during absence of speech) - zeros = (zeros << 9) - (frac >> 3); - // find number of leading zeros - zeros_fast = WebRtcSpl_NormU32((uint32_t)stt->capacitorFast); - if (stt->capacitorFast == 0) - { - zeros_fast = 31; - } - tmp32 = (stt->capacitorFast << zeros_fast) & 0x7FFFFFFF; - zeros_fast <<= 9; - zeros_fast -= (int16_t)(tmp32 >> 22); - - gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm; - - if (gate < 0) - { - stt->gatePrevious = 0; - } else - { - tmp32 = stt->gatePrevious * 7; - gate = (int16_t)((gate + tmp32) >> 3); - stt->gatePrevious = gate; - } - // gate < 0 -> no gate - // gate > 2500 -> max gate - if (gate > 0) - { - if (gate < 2500) - { - gain_adj = (2500 - gate) >> 5; - } else - { - gain_adj = 0; - } - for (k = 0; k < 10; k++) - { - if ((gains[k + 1] - stt->gainTable[0]) > 8388608) - { - // To prevent wraparound - tmp32 = (gains[k + 1] - stt->gainTable[0]) >> 8; - tmp32 *= 178 + gain_adj; - } else - { - tmp32 = (gains[k+1] - stt->gainTable[0]) * (178 + gain_adj); - tmp32 >>= 8; - } - gains[k + 1] = stt->gainTable[0] + tmp32; - } - } - - // Limit gain to avoid overload distortion - for (k = 0; k < 10; k++) - { - // To prevent wrap around - zeros = 10; - if (gains[k + 1] > 47453132) - { - zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]); - } - gain32 = (gains[k + 1] >> zeros) + 1; - gain32 *= gain32; - // check for overflow - while (AGC_MUL32((env[k] >> 12) + 1, gain32) - > WEBRTC_SPL_SHIFT_W32((int32_t)32767, 2 * (1 - zeros + 10))) - { - // multiply by 253/256 ==> -0.1 dB - if (gains[k + 1] > 8388607) - { - // Prevent wrap around - gains[k + 1] = (gains[k+1] / 256) * 253; - } else - { - gains[k + 1] = (gains[k+1] * 253) / 256; - } - gain32 = (gains[k + 1] >> zeros) + 1; - gain32 *= gain32; - } - } - // gain reductions should be done 1 ms earlier than gain increases - for (k = 1; k < 10; k++) - { - if (gains[k] > gains[k + 1]) - { - gains[k] = gains[k + 1]; - } - } - // save start gain for next frame - stt->gain = gains[10]; - - // Apply gain - // handle first sub frame separately - delta = (gains[1] - gains[0]) << (4 - L2); - gain32 = gains[0] << 4; - // iterate over samples - for (n = 0; n < L; n++) - { - for (i = 0; i < num_bands; ++i) - { - tmp32 = out[i][n] * ((gain32 + 127) >> 7); - out_tmp = tmp32 >> 16; - if (out_tmp > 4095) - { - out[i][n] = (int16_t)32767; - } else if (out_tmp < -4096) - { - out[i][n] = (int16_t)-32768; - } else - { - tmp32 = out[i][n] * (gain32 >> 4); - out[i][n] = (int16_t)(tmp32 >> 16); - } - } - // - - gain32 += delta; - } - // iterate over subframes - for (k = 1; k < 10; k++) - { - delta = (gains[k+1] - gains[k]) << (4 - L2); - gain32 = gains[k] << 4; - // iterate over samples - for (n = 0; n < L; n++) - { - for (i = 0; i < num_bands; ++i) - { - tmp32 = out[i][k * L + n] * (gain32 >> 4); - out[i][k * L + n] = (int16_t)(tmp32 >> 16); - } - gain32 += delta; - } - } - - return 0; -} - -void WebRtcAgc_InitVad(AgcVad* state) { - int16_t k; - - state->HPstate = 0; // state of high pass filter - state->logRatio = 0; // log( P(active) / P(inactive) ) - // average input level (Q10) - state->meanLongTerm = 15 << 10; - - // variance of input level (Q8) - state->varianceLongTerm = 500 << 8; - - state->stdLongTerm = 0; // standard deviation of input level in dB - // short-term average input level (Q10) - state->meanShortTerm = 15 << 10; - - // short-term variance of input level (Q8) - state->varianceShortTerm = 500 << 8; - - state->stdShortTerm = 0; // short-term standard deviation of input level in dB - state->counter = 3; // counts updates - for (k = 0; k < 8; k++) - { - // downsampling filter - state->downState[k] = 0; - } -} - -int16_t WebRtcAgc_ProcessVad(AgcVad* state, // (i) VAD state - const int16_t* in, // (i) Speech signal - size_t nrSamples) // (i) number of samples -{ - int32_t out, nrg, tmp32, tmp32b; - uint16_t tmpU16; - int16_t k, subfr, tmp16; - int16_t buf1[8]; - int16_t buf2[4]; - int16_t HPstate; - int16_t zeros, dB; - - // process in 10 sub frames of 1 ms (to save on memory) - nrg = 0; - HPstate = state->HPstate; - for (subfr = 0; subfr < 10; subfr++) - { - // downsample to 4 kHz - if (nrSamples == 160) - { - for (k = 0; k < 8; k++) - { - tmp32 = (int32_t)in[2 * k] + (int32_t)in[2 * k + 1]; - tmp32 >>= 1; - buf1[k] = (int16_t)tmp32; - } - in += 16; - - WebRtcSpl_DownsampleBy2(buf1, 8, buf2, state->downState); - } else - { - WebRtcSpl_DownsampleBy2(in, 8, buf2, state->downState); - in += 8; - } - - // high pass filter and compute energy - for (k = 0; k < 4; k++) - { - out = buf2[k] + HPstate; - tmp32 = 600 * out; - HPstate = (int16_t)((tmp32 >> 10) - buf2[k]); - nrg += (out * out) >> 6; - } - } - state->HPstate = HPstate; - - // find number of leading zeros - if (!(0xFFFF0000 & nrg)) - { - zeros = 16; - } else - { - zeros = 0; - } - if (!(0xFF000000 & (nrg << zeros))) - { - zeros += 8; - } - if (!(0xF0000000 & (nrg << zeros))) - { - zeros += 4; - } - if (!(0xC0000000 & (nrg << zeros))) - { - zeros += 2; - } - if (!(0x80000000 & (nrg << zeros))) - { - zeros += 1; - } - - // energy level (range {-32..30}) (Q10) - dB = (15 - zeros) << 11; - - // Update statistics - - if (state->counter < kAvgDecayTime) - { - // decay time = AvgDecTime * 10 ms - state->counter++; - } - - // update short-term estimate of mean energy level (Q10) - tmp32 = state->meanShortTerm * 15 + dB; - state->meanShortTerm = (int16_t)(tmp32 >> 4); - - // update short-term estimate of variance in energy level (Q8) - tmp32 = (dB * dB) >> 12; - tmp32 += state->varianceShortTerm * 15; - state->varianceShortTerm = tmp32 / 16; - - // update short-term estimate of standard deviation in energy level (Q10) - tmp32 = state->meanShortTerm * state->meanShortTerm; - tmp32 = (state->varianceShortTerm << 12) - tmp32; - state->stdShortTerm = (int16_t)WebRtcSpl_Sqrt(tmp32); - - // update long-term estimate of mean energy level (Q10) - tmp32 = state->meanLongTerm * state->counter + dB; - state->meanLongTerm = WebRtcSpl_DivW32W16ResW16( - tmp32, WebRtcSpl_AddSatW16(state->counter, 1)); - - // update long-term estimate of variance in energy level (Q8) - tmp32 = (dB * dB) >> 12; - tmp32 += state->varianceLongTerm * state->counter; - state->varianceLongTerm = WebRtcSpl_DivW32W16( - tmp32, WebRtcSpl_AddSatW16(state->counter, 1)); - - // update long-term estimate of standard deviation in energy level (Q10) - tmp32 = state->meanLongTerm * state->meanLongTerm; - tmp32 = (state->varianceLongTerm << 12) - tmp32; - state->stdLongTerm = (int16_t)WebRtcSpl_Sqrt(tmp32); - - // update voice activity measure (Q10) - tmp16 = 3 << 12; - // TODO(bjornv): (dB - state->meanLongTerm) can overflow, e.g., in - // ApmTest.Process unit test. Previously the macro WEBRTC_SPL_MUL_16_16() - // was used, which did an intermediate cast to (int16_t), hence losing - // significant bits. This cause logRatio to max out positive, rather than - // negative. This is a bug, but has very little significance. - tmp32 = tmp16 * (int16_t)(dB - state->meanLongTerm); - tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm); - tmpU16 = (13 << 12); - tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16); - tmp32 += tmp32b >> 10; - - state->logRatio = (int16_t)(tmp32 >> 6); - - // limit - if (state->logRatio > 2048) - { - state->logRatio = 2048; - } - if (state->logRatio < -2048) - { - state->logRatio = -2048; - } - - return state->logRatio; // Q10 -} diff --git a/webrtc/modules/audio_processing/agc/legacy/digital_agc.cc b/webrtc/modules/audio_processing/agc/legacy/digital_agc.cc new file mode 100644 index 0000000..185e849 --- /dev/null +++ b/webrtc/modules/audio_processing/agc/legacy/digital_agc.cc @@ -0,0 +1,714 @@ +/* + * 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 "modules/audio_processing/agc/legacy/digital_agc.h" + +#include + +#include "modules/audio_processing/agc/legacy/gain_control.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// To generate the gaintable, copy&paste the following lines to a Matlab window: +// MaxGain = 6; MinGain = 0; CompRatio = 3; Knee = 1; +// zeros = 0:31; lvl = 2.^(1-zeros); +// A = -10*log10(lvl) * (CompRatio - 1) / CompRatio; +// B = MaxGain - MinGain; +// gains = round(2^16*10.^(0.05 * (MinGain + B * ( +// log(exp(-Knee*A)+exp(-Knee*B)) - log(1+exp(-Knee*B)) ) / +// log(1/(1+exp(Knee*B)))))); +// fprintf(1, '\t%i, %i, %i, %i,\n', gains); +// % Matlab code for plotting the gain and input/output level characteristic +// (copy/paste the following 3 lines): +// in = 10*log10(lvl); out = 20*log10(gains/65536); +// subplot(121); plot(in, out); axis([-30, 0, -5, 20]); grid on; xlabel('Input +// (dB)'); ylabel('Gain (dB)'); +// subplot(122); plot(in, in+out); axis([-30, 0, -30, 5]); grid on; +// xlabel('Input (dB)'); ylabel('Output (dB)'); +// zoom on; + +// Generator table for y=log2(1+e^x) in Q8. +enum { kGenFuncTableSize = 128 }; +static const uint16_t kGenFuncTable[kGenFuncTableSize] = { + 256, 485, 786, 1126, 1484, 1849, 2217, 2586, 2955, 3324, 3693, + 4063, 4432, 4801, 5171, 5540, 5909, 6279, 6648, 7017, 7387, 7756, + 8125, 8495, 8864, 9233, 9603, 9972, 10341, 10711, 11080, 11449, 11819, + 12188, 12557, 12927, 13296, 13665, 14035, 14404, 14773, 15143, 15512, 15881, + 16251, 16620, 16989, 17359, 17728, 18097, 18466, 18836, 19205, 19574, 19944, + 20313, 20682, 21052, 21421, 21790, 22160, 22529, 22898, 23268, 23637, 24006, + 24376, 24745, 25114, 25484, 25853, 26222, 26592, 26961, 27330, 27700, 28069, + 28438, 28808, 29177, 29546, 29916, 30285, 30654, 31024, 31393, 31762, 32132, + 32501, 32870, 33240, 33609, 33978, 34348, 34717, 35086, 35456, 35825, 36194, + 36564, 36933, 37302, 37672, 38041, 38410, 38780, 39149, 39518, 39888, 40257, + 40626, 40996, 41365, 41734, 42104, 42473, 42842, 43212, 43581, 43950, 44320, + 44689, 45058, 45428, 45797, 46166, 46536, 46905}; + +static const int16_t kAvgDecayTime = 250; // frames; < 3000 + +// the 32 most significant bits of A(19) * B(26) >> 13 +#define AGC_MUL32(A, B) (((B) >> 13) * (A) + (((0x00001FFF & (B)) * (A)) >> 13)) +// C + the 32 most significant bits of A * B +#define AGC_SCALEDIFF32(A, B, C) \ + ((C) + ((B) >> 16) * (A) + (((0x0000FFFF & (B)) * (A)) >> 16)) + +} // namespace + +int32_t WebRtcAgc_CalculateGainTable(int32_t* gainTable, // Q16 + int16_t digCompGaindB, // Q0 + int16_t targetLevelDbfs, // Q0 + uint8_t limiterEnable, + int16_t analogTarget) { // Q0 + // This function generates the compressor gain table used in the fixed digital + // part. + uint32_t tmpU32no1, tmpU32no2, absInLevel, logApprox; + int32_t inLevel, limiterLvl; + int32_t tmp32, tmp32no1, tmp32no2, numFIX, den, y32; + const uint16_t kLog10 = 54426; // log2(10) in Q14 + const uint16_t kLog10_2 = 49321; // 10*log10(2) in Q14 + const uint16_t kLogE_1 = 23637; // log2(e) in Q14 + uint16_t constMaxGain; + uint16_t tmpU16, intPart, fracPart; + const int16_t kCompRatio = 3; + const int16_t kSoftLimiterLeft = 1; + int16_t limiterOffset = 0; // Limiter offset + int16_t limiterIdx, limiterLvlX; + int16_t constLinApprox, zeroGainLvl, maxGain, diffGain; + int16_t i, tmp16, tmp16no1; + int zeros, zerosScale; + + // Constants + // kLogE_1 = 23637; // log2(e) in Q14 + // kLog10 = 54426; // log2(10) in Q14 + // kLog10_2 = 49321; // 10*log10(2) in Q14 + + // Calculate maximum digital gain and zero gain level + tmp32no1 = (digCompGaindB - analogTarget) * (kCompRatio - 1); + tmp16no1 = analogTarget - targetLevelDbfs; + tmp16no1 += + WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio); + maxGain = WEBRTC_SPL_MAX(tmp16no1, (analogTarget - targetLevelDbfs)); + tmp32no1 = maxGain * kCompRatio; + zeroGainLvl = digCompGaindB; + zeroGainLvl -= WebRtcSpl_DivW32W16ResW16(tmp32no1 + ((kCompRatio - 1) >> 1), + kCompRatio - 1); + if ((digCompGaindB <= analogTarget) && (limiterEnable)) { + zeroGainLvl += (analogTarget - digCompGaindB + kSoftLimiterLeft); + limiterOffset = 0; + } + + // Calculate the difference between maximum gain and gain at 0dB0v: + // diffGain = maxGain + (compRatio-1)*zeroGainLvl/compRatio + // = (compRatio-1)*digCompGaindB/compRatio + tmp32no1 = digCompGaindB * (kCompRatio - 1); + diffGain = + WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio); + if (diffGain < 0 || diffGain >= kGenFuncTableSize) { + RTC_DCHECK(0); + return -1; + } + + // Calculate the limiter level and index: + // limiterLvlX = analogTarget - limiterOffset + // limiterLvl = targetLevelDbfs + limiterOffset/compRatio + limiterLvlX = analogTarget - limiterOffset; + limiterIdx = 2 + WebRtcSpl_DivW32W16ResW16((int32_t)limiterLvlX * (1 << 13), + kLog10_2 / 2); + tmp16no1 = + WebRtcSpl_DivW32W16ResW16(limiterOffset + (kCompRatio >> 1), kCompRatio); + limiterLvl = targetLevelDbfs + tmp16no1; + + // Calculate (through table lookup): + // constMaxGain = log2(1+2^(log2(e)*diffGain)); (in Q8) + constMaxGain = kGenFuncTable[diffGain]; // in Q8 + + // Calculate a parameter used to approximate the fractional part of 2^x with a + // piecewise linear function in Q14: + // constLinApprox = round(3/2*(4*(3-2*sqrt(2))/(log(2)^2)-0.5)*2^14); + constLinApprox = 22817; // in Q14 + + // Calculate a denominator used in the exponential part to convert from dB to + // linear scale: + // den = 20*constMaxGain (in Q8) + den = WEBRTC_SPL_MUL_16_U16(20, constMaxGain); // in Q8 + + for (i = 0; i < 32; i++) { + // Calculate scaled input level (compressor): + // inLevel = + // fix((-constLog10_2*(compRatio-1)*(1-i)+fix(compRatio/2))/compRatio) + tmp16 = (int16_t)((kCompRatio - 1) * (i - 1)); // Q0 + tmp32 = WEBRTC_SPL_MUL_16_U16(tmp16, kLog10_2) + 1; // Q14 + inLevel = WebRtcSpl_DivW32W16(tmp32, kCompRatio); // Q14 + + // Calculate diffGain-inLevel, to map using the genFuncTable + inLevel = (int32_t)diffGain * (1 << 14) - inLevel; // Q14 + + // Make calculations on abs(inLevel) and compensate for the sign afterwards. + absInLevel = (uint32_t)WEBRTC_SPL_ABS_W32(inLevel); // Q14 + + // LUT with interpolation + intPart = (uint16_t)(absInLevel >> 14); + fracPart = + (uint16_t)(absInLevel & 0x00003FFF); // extract the fractional part + tmpU16 = kGenFuncTable[intPart + 1] - kGenFuncTable[intPart]; // Q8 + tmpU32no1 = tmpU16 * fracPart; // Q22 + tmpU32no1 += (uint32_t)kGenFuncTable[intPart] << 14; // Q22 + logApprox = tmpU32no1 >> 8; // Q14 + // Compensate for negative exponent using the relation: + // log2(1 + 2^-x) = log2(1 + 2^x) - x + if (inLevel < 0) { + zeros = WebRtcSpl_NormU32(absInLevel); + zerosScale = 0; + if (zeros < 15) { + // Not enough space for multiplication + tmpU32no2 = absInLevel >> (15 - zeros); // Q(zeros-1) + tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no2, kLogE_1); // Q(zeros+13) + if (zeros < 9) { + zerosScale = 9 - zeros; + tmpU32no1 >>= zerosScale; // Q(zeros+13) + } else { + tmpU32no2 >>= zeros - 9; // Q22 + } + } else { + tmpU32no2 = WEBRTC_SPL_UMUL_32_16(absInLevel, kLogE_1); // Q28 + tmpU32no2 >>= 6; // Q22 + } + logApprox = 0; + if (tmpU32no2 < tmpU32no1) { + logApprox = (tmpU32no1 - tmpU32no2) >> (8 - zerosScale); // Q14 + } + } + numFIX = (maxGain * constMaxGain) * (1 << 6); // Q14 + numFIX -= (int32_t)logApprox * diffGain; // Q14 + + // Calculate ratio + // Shift |numFIX| as much as possible. + // Ensure we avoid wrap-around in |den| as well. + if (numFIX > (den >> 8) || -numFIX > (den >> 8)) { // |den| is Q8. + zeros = WebRtcSpl_NormW32(numFIX); + } else { + zeros = WebRtcSpl_NormW32(den) + 8; + } + numFIX *= 1 << zeros; // Q(14+zeros) + + // Shift den so we end up in Qy1 + tmp32no1 = WEBRTC_SPL_SHIFT_W32(den, zeros - 9); // Q(zeros - 1) + y32 = numFIX / tmp32no1; // in Q15 + // This is to do rounding in Q14. + y32 = y32 >= 0 ? (y32 + 1) >> 1 : -((-y32 + 1) >> 1); + + if (limiterEnable && (i < limiterIdx)) { + tmp32 = WEBRTC_SPL_MUL_16_U16(i - 1, kLog10_2); // Q14 + tmp32 -= limiterLvl * (1 << 14); // Q14 + y32 = WebRtcSpl_DivW32W16(tmp32 + 10, 20); + } + if (y32 > 39000) { + tmp32 = (y32 >> 1) * kLog10 + 4096; // in Q27 + tmp32 >>= 13; // In Q14. + } else { + tmp32 = y32 * kLog10 + 8192; // in Q28 + tmp32 >>= 14; // In Q14. + } + tmp32 += 16 << 14; // in Q14 (Make sure final output is in Q16) + + // Calculate power + if (tmp32 > 0) { + intPart = (int16_t)(tmp32 >> 14); + fracPart = (uint16_t)(tmp32 & 0x00003FFF); // in Q14 + if ((fracPart >> 13) != 0) { + tmp16 = (2 << 14) - constLinApprox; + tmp32no2 = (1 << 14) - fracPart; + tmp32no2 *= tmp16; + tmp32no2 >>= 13; + tmp32no2 = (1 << 14) - tmp32no2; + } else { + tmp16 = constLinApprox - (1 << 14); + tmp32no2 = (fracPart * tmp16) >> 13; + } + fracPart = (uint16_t)tmp32no2; + gainTable[i] = + (1 << intPart) + WEBRTC_SPL_SHIFT_W32(fracPart, intPart - 14); + } else { + gainTable[i] = 0; + } + } + + return 0; +} + +int32_t WebRtcAgc_InitDigital(DigitalAgc* stt, int16_t agcMode) { + if (agcMode == kAgcModeFixedDigital) { + // start at minimum to find correct gain faster + stt->capacitorSlow = 0; + } else { + // start out with 0 dB gain + stt->capacitorSlow = 134217728; // (int32_t)(0.125f * 32768.0f * 32768.0f); + } + stt->capacitorFast = 0; + stt->gain = 65536; + stt->gatePrevious = 0; + stt->agcMode = agcMode; + + // initialize VADs + WebRtcAgc_InitVad(&stt->vadNearend); + WebRtcAgc_InitVad(&stt->vadFarend); + + return 0; +} + +int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc* stt, + const int16_t* in_far, + size_t nrSamples) { + RTC_DCHECK(stt); + // VAD for far end + WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples); + + return 0; +} + +// Gains is an 11 element long array (one value per ms, incl start & end). +int32_t WebRtcAgc_ComputeDigitalGains(DigitalAgc* stt, + const int16_t* const* in_near, + size_t num_bands, + uint32_t FS, + int16_t lowlevelSignal, + int32_t gains[11]) { + int32_t tmp32; + int32_t env[10]; + int32_t max_nrg; + int32_t cur_level; + int32_t gain32; + int16_t logratio; + int16_t lower_thr, upper_thr; + int16_t zeros = 0, zeros_fast, frac = 0; + int16_t decay; + int16_t gate, gain_adj; + int16_t k; + size_t n, L; + int16_t L2; // samples/subframe + + // determine number of samples per ms + if (FS == 8000) { + L = 8; + L2 = 3; + } else if (FS == 16000 || FS == 32000 || FS == 48000) { + L = 16; + L2 = 4; + } else { + return -1; + } + + // VAD for near end + logratio = WebRtcAgc_ProcessVad(&stt->vadNearend, in_near[0], L * 10); + + // Account for far end VAD + if (stt->vadFarend.counter > 10) { + tmp32 = 3 * logratio; + logratio = (int16_t)((tmp32 - stt->vadFarend.logRatio) >> 2); + } + + // Determine decay factor depending on VAD + // upper_thr = 1.0f; + // lower_thr = 0.25f; + upper_thr = 1024; // Q10 + lower_thr = 0; // Q10 + if (logratio > upper_thr) { + // decay = -2^17 / DecayTime; -> -65 + decay = -65; + } else if (logratio < lower_thr) { + decay = 0; + } else { + // decay = (int16_t)(((lower_thr - logratio) + // * (2^27/(DecayTime*(upper_thr-lower_thr)))) >> 10); + // SUBSTITUTED: 2^27/(DecayTime*(upper_thr-lower_thr)) -> 65 + tmp32 = (lower_thr - logratio) * 65; + decay = (int16_t)(tmp32 >> 10); + } + + // adjust decay factor for long silence (detected as low standard deviation) + // This is only done in the adaptive modes + if (stt->agcMode != kAgcModeFixedDigital) { + if (stt->vadNearend.stdLongTerm < 4000) { + decay = 0; + } else if (stt->vadNearend.stdLongTerm < 8096) { + // decay = (int16_t)(((stt->vadNearend.stdLongTerm - 4000) * decay) >> + // 12); + tmp32 = (stt->vadNearend.stdLongTerm - 4000) * decay; + decay = (int16_t)(tmp32 >> 12); + } + + if (lowlevelSignal != 0) { + decay = 0; + } + } + // Find max amplitude per sub frame + // iterate over sub frames + for (k = 0; k < 10; k++) { + // iterate over samples + max_nrg = 0; + for (n = 0; n < L; n++) { + int32_t nrg = in_near[0][k * L + n] * in_near[0][k * L + n]; + if (nrg > max_nrg) { + max_nrg = nrg; + } + } + env[k] = max_nrg; + } + + // Calculate gain per sub frame + gains[0] = stt->gain; + for (k = 0; k < 10; k++) { + // Fast envelope follower + // decay time = -131000 / -1000 = 131 (ms) + stt->capacitorFast = + AGC_SCALEDIFF32(-1000, stt->capacitorFast, stt->capacitorFast); + if (env[k] > stt->capacitorFast) { + stt->capacitorFast = env[k]; + } + // Slow envelope follower + if (env[k] > stt->capacitorSlow) { + // increase capacitorSlow + stt->capacitorSlow = AGC_SCALEDIFF32(500, (env[k] - stt->capacitorSlow), + stt->capacitorSlow); + } else { + // decrease capacitorSlow + stt->capacitorSlow = + AGC_SCALEDIFF32(decay, stt->capacitorSlow, stt->capacitorSlow); + } + + // use maximum of both capacitors as current level + if (stt->capacitorFast > stt->capacitorSlow) { + cur_level = stt->capacitorFast; + } else { + cur_level = stt->capacitorSlow; + } + // Translate signal level into gain, using a piecewise linear approximation + // find number of leading zeros + zeros = WebRtcSpl_NormU32((uint32_t)cur_level); + if (cur_level == 0) { + zeros = 31; + } + tmp32 = ((uint32_t)cur_level << zeros) & 0x7FFFFFFF; + frac = (int16_t)(tmp32 >> 19); // Q12. + // Interpolate between gainTable[zeros] and gainTable[zeros-1]. + tmp32 = + ((stt->gainTable[zeros - 1] - stt->gainTable[zeros]) * (int64_t)frac) >> + 12; + gains[k + 1] = stt->gainTable[zeros] + tmp32; + } + + // Gate processing (lower gain during absence of speech) + zeros = (zeros << 9) - (frac >> 3); + // find number of leading zeros + zeros_fast = WebRtcSpl_NormU32((uint32_t)stt->capacitorFast); + if (stt->capacitorFast == 0) { + zeros_fast = 31; + } + tmp32 = ((uint32_t)stt->capacitorFast << zeros_fast) & 0x7FFFFFFF; + zeros_fast <<= 9; + zeros_fast -= (int16_t)(tmp32 >> 22); + + gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm; + + if (gate < 0) { + stt->gatePrevious = 0; + } else { + tmp32 = stt->gatePrevious * 7; + gate = (int16_t)((gate + tmp32) >> 3); + stt->gatePrevious = gate; + } + // gate < 0 -> no gate + // gate > 2500 -> max gate + if (gate > 0) { + if (gate < 2500) { + gain_adj = (2500 - gate) >> 5; + } else { + gain_adj = 0; + } + for (k = 0; k < 10; k++) { + if ((gains[k + 1] - stt->gainTable[0]) > 8388608) { + // To prevent wraparound + tmp32 = (gains[k + 1] - stt->gainTable[0]) >> 8; + tmp32 *= 178 + gain_adj; + } else { + tmp32 = (gains[k + 1] - stt->gainTable[0]) * (178 + gain_adj); + tmp32 >>= 8; + } + gains[k + 1] = stt->gainTable[0] + tmp32; + } + } + + // Limit gain to avoid overload distortion + for (k = 0; k < 10; k++) { + // Find a shift of gains[k + 1] such that it can be squared without + // overflow, but at least by 10 bits. + zeros = 10; + if (gains[k + 1] > 47452159) { + zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]); + } + gain32 = (gains[k + 1] >> zeros) + 1; + gain32 *= gain32; + // check for overflow + while (AGC_MUL32((env[k] >> 12) + 1, gain32) > + WEBRTC_SPL_SHIFT_W32((int32_t)32767, 2 * (1 - zeros + 10))) { + // multiply by 253/256 ==> -0.1 dB + if (gains[k + 1] > 8388607) { + // Prevent wrap around + gains[k + 1] = (gains[k + 1] / 256) * 253; + } else { + gains[k + 1] = (gains[k + 1] * 253) / 256; + } + gain32 = (gains[k + 1] >> zeros) + 1; + gain32 *= gain32; + } + } + // gain reductions should be done 1 ms earlier than gain increases + for (k = 1; k < 10; k++) { + if (gains[k] > gains[k + 1]) { + gains[k] = gains[k + 1]; + } + } + // save start gain for next frame + stt->gain = gains[10]; + + return 0; +} + +int32_t WebRtcAgc_ApplyDigitalGains(const int32_t gains[11], + size_t num_bands, + uint32_t FS, + const int16_t* const* in_near, + int16_t* const* out) { + // Apply gain + // handle first sub frame separately + size_t L; + int16_t L2; // samples/subframe + + // determine number of samples per ms + if (FS == 8000) { + L = 8; + L2 = 3; + } else if (FS == 16000 || FS == 32000 || FS == 48000) { + L = 16; + L2 = 4; + } else { + return -1; + } + + for (size_t i = 0; i < num_bands; ++i) { + if (in_near[i] != out[i]) { + // Only needed if they don't already point to the same place. + memcpy(out[i], in_near[i], 10 * L * sizeof(in_near[i][0])); + } + } + + // iterate over samples + int32_t delta = (gains[1] - gains[0]) * (1 << (4 - L2)); + int32_t gain32 = gains[0] * (1 << 4); + for (size_t n = 0; n < L; n++) { + for (size_t i = 0; i < num_bands; ++i) { + int32_t out_tmp = (int64_t)out[i][n] * ((gain32 + 127) >> 7) >> 16; + if (out_tmp > 4095) { + out[i][n] = (int16_t)32767; + } else if (out_tmp < -4096) { + out[i][n] = (int16_t)-32768; + } else { + int32_t tmp32 = ((int64_t)out[i][n] * (gain32 >> 4)) >> 16; + out[i][n] = (int16_t)tmp32; + } + } + + gain32 += delta; + } + // iterate over subframes + for (int k = 1; k < 10; k++) { + delta = (gains[k + 1] - gains[k]) * (1 << (4 - L2)); + gain32 = gains[k] * (1 << 4); + // iterate over samples + for (size_t n = 0; n < L; n++) { + for (size_t i = 0; i < num_bands; ++i) { + int64_t tmp64 = ((int64_t)(out[i][k * L + n])) * (gain32 >> 4); + tmp64 = tmp64 >> 16; + if (tmp64 > 32767) { + out[i][k * L + n] = 32767; + } else if (tmp64 < -32768) { + out[i][k * L + n] = -32768; + } else { + out[i][k * L + n] = (int16_t)(tmp64); + } + } + gain32 += delta; + } + } + return 0; +} + +void WebRtcAgc_InitVad(AgcVad* state) { + int16_t k; + + state->HPstate = 0; // state of high pass filter + state->logRatio = 0; // log( P(active) / P(inactive) ) + // average input level (Q10) + state->meanLongTerm = 15 << 10; + + // variance of input level (Q8) + state->varianceLongTerm = 500 << 8; + + state->stdLongTerm = 0; // standard deviation of input level in dB + // short-term average input level (Q10) + state->meanShortTerm = 15 << 10; + + // short-term variance of input level (Q8) + state->varianceShortTerm = 500 << 8; + + state->stdShortTerm = + 0; // short-term standard deviation of input level in dB + state->counter = 3; // counts updates + for (k = 0; k < 8; k++) { + // downsampling filter + state->downState[k] = 0; + } +} + +int16_t WebRtcAgc_ProcessVad(AgcVad* state, // (i) VAD state + const int16_t* in, // (i) Speech signal + size_t nrSamples) { // (i) number of samples + uint32_t nrg; + int32_t out, tmp32, tmp32b; + uint16_t tmpU16; + int16_t k, subfr, tmp16; + int16_t buf1[8]; + int16_t buf2[4]; + int16_t HPstate; + int16_t zeros, dB; + int64_t tmp64; + + // process in 10 sub frames of 1 ms (to save on memory) + nrg = 0; + HPstate = state->HPstate; + for (subfr = 0; subfr < 10; subfr++) { + // downsample to 4 kHz + if (nrSamples == 160) { + for (k = 0; k < 8; k++) { + tmp32 = (int32_t)in[2 * k] + (int32_t)in[2 * k + 1]; + tmp32 >>= 1; + buf1[k] = (int16_t)tmp32; + } + in += 16; + + WebRtcSpl_DownsampleBy2(buf1, 8, buf2, state->downState); + } else { + WebRtcSpl_DownsampleBy2(in, 8, buf2, state->downState); + in += 8; + } + + // high pass filter and compute energy + for (k = 0; k < 4; k++) { + out = buf2[k] + HPstate; + tmp32 = 600 * out; + HPstate = (int16_t)((tmp32 >> 10) - buf2[k]); + + // Add 'out * out / 2**6' to 'nrg' in a non-overflowing + // way. Guaranteed to work as long as 'out * out / 2**6' fits in + // an int32_t. + nrg += out * (out / (1 << 6)); + nrg += out * (out % (1 << 6)) / (1 << 6); + } + } + state->HPstate = HPstate; + + // find number of leading zeros + if (!(0xFFFF0000 & nrg)) { + zeros = 16; + } else { + zeros = 0; + } + if (!(0xFF000000 & (nrg << zeros))) { + zeros += 8; + } + if (!(0xF0000000 & (nrg << zeros))) { + zeros += 4; + } + if (!(0xC0000000 & (nrg << zeros))) { + zeros += 2; + } + if (!(0x80000000 & (nrg << zeros))) { + zeros += 1; + } + + // energy level (range {-32..30}) (Q10) + dB = (15 - zeros) * (1 << 11); + + // Update statistics + + if (state->counter < kAvgDecayTime) { + // decay time = AvgDecTime * 10 ms + state->counter++; + } + + // update short-term estimate of mean energy level (Q10) + tmp32 = state->meanShortTerm * 15 + dB; + state->meanShortTerm = (int16_t)(tmp32 >> 4); + + // update short-term estimate of variance in energy level (Q8) + tmp32 = (dB * dB) >> 12; + tmp32 += state->varianceShortTerm * 15; + state->varianceShortTerm = tmp32 / 16; + + // update short-term estimate of standard deviation in energy level (Q10) + tmp32 = state->meanShortTerm * state->meanShortTerm; + tmp32 = (state->varianceShortTerm << 12) - tmp32; + state->stdShortTerm = (int16_t)WebRtcSpl_Sqrt(tmp32); + + // update long-term estimate of mean energy level (Q10) + tmp32 = state->meanLongTerm * state->counter + dB; + state->meanLongTerm = + WebRtcSpl_DivW32W16ResW16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1)); + + // update long-term estimate of variance in energy level (Q8) + tmp32 = (dB * dB) >> 12; + tmp32 += state->varianceLongTerm * state->counter; + state->varianceLongTerm = + WebRtcSpl_DivW32W16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1)); + + // update long-term estimate of standard deviation in energy level (Q10) + tmp32 = state->meanLongTerm * state->meanLongTerm; + tmp32 = (state->varianceLongTerm << 12) - tmp32; + state->stdLongTerm = (int16_t)WebRtcSpl_Sqrt(tmp32); + + // update voice activity measure (Q10) + tmp16 = 3 << 12; + // TODO(bjornv): (dB - state->meanLongTerm) can overflow, e.g., in + // ApmTest.Process unit test. Previously the macro WEBRTC_SPL_MUL_16_16() + // was used, which did an intermediate cast to (int16_t), hence losing + // significant bits. This cause logRatio to max out positive, rather than + // negative. This is a bug, but has very little significance. + tmp32 = tmp16 * (int16_t)(dB - state->meanLongTerm); + tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm); + tmpU16 = (13 << 12); + tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16); + tmp64 = tmp32; + tmp64 += tmp32b >> 10; + tmp64 >>= 6; + + // limit + if (tmp64 > 2048) { + tmp64 = 2048; + } else if (tmp64 < -2048) { + tmp64 = -2048; + } + state->logRatio = (int16_t)tmp64; + + return state->logRatio; // Q10 +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc/legacy/digital_agc.h b/webrtc/modules/audio_processing/agc/legacy/digital_agc.h index 819844d..223c74b 100644 --- a/webrtc/modules/audio_processing/agc/legacy/digital_agc.h +++ b/webrtc/modules/audio_processing/agc/legacy/digital_agc.h @@ -8,58 +8,51 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_DIGITAL_AGC_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_DIGITAL_AGC_H_ +#ifndef MODULES_AUDIO_PROCESSING_AGC_LEGACY_DIGITAL_AGC_H_ +#define MODULES_AUDIO_PROCESSING_AGC_LEGACY_DIGITAL_AGC_H_ -#ifdef WEBRTC_AGC_DEBUG_DUMP -#include -#endif -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/typedefs.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" -// the 32 most significant bits of A(19) * B(26) >> 13 -#define AGC_MUL32(A, B) (((B)>>13)*(A) + ( ((0x00001FFF & (B))*(A)) >> 13 )) -// C + the 32 most significant bits of A * B -#define AGC_SCALEDIFF32(A, B, C) ((C) + ((B)>>16)*(A) + ( ((0x0000FFFF & (B))*(A)) >> 16 )) +namespace webrtc { -typedef struct -{ - int32_t downState[8]; - int16_t HPstate; - int16_t counter; - int16_t logRatio; // log( P(active) / P(inactive) ) (Q10) - int16_t meanLongTerm; // Q10 - int32_t varianceLongTerm; // Q8 - int16_t stdLongTerm; // Q10 - int16_t meanShortTerm; // Q10 - int32_t varianceShortTerm; // Q8 - int16_t stdShortTerm; // Q10 -} AgcVad; // total = 54 bytes +typedef struct { + int32_t downState[8]; + int16_t HPstate; + int16_t counter; + int16_t logRatio; // log( P(active) / P(inactive) ) (Q10) + int16_t meanLongTerm; // Q10 + int32_t varianceLongTerm; // Q8 + int16_t stdLongTerm; // Q10 + int16_t meanShortTerm; // Q10 + int32_t varianceShortTerm; // Q8 + int16_t stdShortTerm; // Q10 +} AgcVad; // total = 54 bytes -typedef struct -{ - int32_t capacitorSlow; - int32_t capacitorFast; - int32_t gain; - int32_t gainTable[32]; - int16_t gatePrevious; - int16_t agcMode; - AgcVad vadNearend; - AgcVad vadFarend; -#ifdef WEBRTC_AGC_DEBUG_DUMP - FILE* logFile; - int frameCounter; -#endif +typedef struct { + int32_t capacitorSlow; + int32_t capacitorFast; + int32_t gain; + int32_t gainTable[32]; + int16_t gatePrevious; + int16_t agcMode; + AgcVad vadNearend; + AgcVad vadFarend; } DigitalAgc; int32_t WebRtcAgc_InitDigital(DigitalAgc* digitalAgcInst, int16_t agcMode); -int32_t WebRtcAgc_ProcessDigital(DigitalAgc* digitalAgcInst, - const int16_t* const* inNear, - size_t num_bands, - int16_t* const* out, - uint32_t FS, - int16_t lowLevelSignal); +int32_t WebRtcAgc_ComputeDigitalGains(DigitalAgc* digitalAgcInst, + const int16_t* const* inNear, + size_t num_bands, + uint32_t FS, + int16_t lowLevelSignal, + int32_t gains[11]); + +int32_t WebRtcAgc_ApplyDigitalGains(const int32_t gains[11], + size_t num_bands, + uint32_t FS, + const int16_t* const* in_near, + int16_t* const* out); int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc* digitalAgcInst, const int16_t* inFar, @@ -67,14 +60,16 @@ int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc* digitalAgcInst, void WebRtcAgc_InitVad(AgcVad* vadInst); -int16_t WebRtcAgc_ProcessVad(AgcVad* vadInst, // (i) VAD state - const int16_t* in, // (i) Speech signal +int16_t WebRtcAgc_ProcessVad(AgcVad* vadInst, // (i) VAD state + const int16_t* in, // (i) Speech signal size_t nrSamples); // (i) number of samples -int32_t WebRtcAgc_CalculateGainTable(int32_t *gainTable, // Q16 - int16_t compressionGaindB, // Q0 (in dB) - int16_t targetLevelDbfs,// Q0 (in dB) +int32_t WebRtcAgc_CalculateGainTable(int32_t* gainTable, // Q16 + int16_t compressionGaindB, // Q0 (in dB) + int16_t targetLevelDbfs, // Q0 (in dB) uint8_t limiterEnable, int16_t analogTarget); -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_DIGITAL_AGC_H_ +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_LEGACY_DIGITAL_AGC_H_ diff --git a/webrtc/modules/audio_processing/agc/legacy/gain_control.h b/webrtc/modules/audio_processing/agc/legacy/gain_control.h index 08c1988..abb8e63 100644 --- a/webrtc/modules/audio_processing/agc/legacy/gain_control.h +++ b/webrtc/modules/audio_processing/agc/legacy/gain_control.h @@ -8,46 +8,39 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ +#ifndef MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ +#define MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ -#include "webrtc/typedefs.h" +namespace webrtc { -// Errors -#define AGC_UNSPECIFIED_ERROR 18000 -#define AGC_UNSUPPORTED_FUNCTION_ERROR 18001 -#define AGC_UNINITIALIZED_ERROR 18002 -#define AGC_NULL_POINTER_ERROR 18003 -#define AGC_BAD_PARAMETER_ERROR 18004 - -// Warnings -#define AGC_BAD_PARAMETER_WARNING 18050 - -enum -{ - kAgcModeUnchanged, - kAgcModeAdaptiveAnalog, - kAgcModeAdaptiveDigital, - kAgcModeFixedDigital +enum { + kAgcModeUnchanged, + kAgcModeAdaptiveAnalog, + kAgcModeAdaptiveDigital, + kAgcModeFixedDigital }; -enum -{ - kAgcFalse = 0, - kAgcTrue -}; +enum { kAgcFalse = 0, kAgcTrue }; -typedef struct -{ - int16_t targetLevelDbfs; // default 3 (-3 dBOv) - int16_t compressionGaindB; // default 9 dB - uint8_t limiterEnable; // default kAgcTrue (on) +typedef struct { + int16_t targetLevelDbfs; // default 3 (-3 dBOv) + int16_t compressionGaindB; // default 9 dB + uint8_t limiterEnable; // default kAgcTrue (on) } WebRtcAgcConfig; -#if defined(__cplusplus) -extern "C" -{ -#endif +/* + * This function analyses the number of samples passed to + * farend and produces any error code that could arise. + * + * Input: + * - agcInst : AGC instance. + * - samples : Number of samples in input vector. + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error. + */ +int WebRtcAgc_GetAddFarendError(void* state, size_t samples); /* * This function processes a 10 ms frame of far-end speech to determine @@ -64,9 +57,7 @@ extern "C" * : 0 - Normal operation. * : -1 - Error */ -int WebRtcAgc_AddFarend(void* agcInst, - const int16_t* inFar, - size_t samples); +int WebRtcAgc_AddFarend(void* agcInst, const int16_t* inFar, size_t samples); /* * This function processes a 10 ms frame of microphone speech to determine @@ -124,12 +115,12 @@ int WebRtcAgc_VirtualMic(void* agcInst, int32_t* micLevelOut); /* - * This function processes a 10 ms frame and adjusts (normalizes) the gain both - * analog and digitally. The gain adjustments are done only during active - * periods of speech. The length of the speech vectors must be given in samples - * (80 when FS=8000, and 160 when FS=16000, FS=32000 or FS=48000). The echo - * parameter can be used to ensure the AGC will not adjust upward in the - * presence of echo. + * This function analyses a 10 ms frame and produces the analog and digital + * gains required to normalize the signal. The gain adjustments are done only + * during active periods of speech. The length of the speech vectors must be + * given in samples (80 when FS=8000, and 160 when FS=16000, FS=32000 or + * FS=48000). The echo parameter can be used to ensure the AGC will not adjust + * upward in the presence of echo. * * This function should be called after processing the near-end microphone * signal, in any case after any echo cancellation. @@ -147,25 +138,47 @@ int WebRtcAgc_VirtualMic(void* agcInst, * * Output: * - outMicLevel : Adjusted microphone volume level - * - out : Gain-adjusted near-end speech vector - * : May be the same vector as the input. * - saturationWarning : A returned value of 1 indicates a saturation event * has occurred and the volume cannot be further * reduced. Otherwise will be set to 0. + * - gains : Vector of gains to apply for digital normalization * * Return value: * : 0 - Normal operation. * : -1 - Error */ -int WebRtcAgc_Process(void* agcInst, +int WebRtcAgc_Analyze(void* agcInst, const int16_t* const* inNear, size_t num_bands, size_t samples, - int16_t* const* out, int32_t inMicLevel, int32_t* outMicLevel, int16_t echo, - uint8_t* saturationWarning); + uint8_t* saturationWarning, + int32_t gains[11]); + +/* + * This function processes a 10 ms frame by applying precomputed digital gains. + * + * Input: + * - agcInst : AGC instance + * - gains : Vector of gains to apply for digital normalization + * - in_near : Near-end input speech vector for each band + * - num_bands : Number of bands in input/output vector + * + * Output: + * - out : Gain-adjusted near-end speech vector + * : May be the same vector as the input. + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_Process(const void* agcInst, + const int32_t gains[11], + const int16_t* const* in_near, + size_t num_bands, + int16_t* const* out); /* * This function sets the config parameters (targetLevelDbfs, @@ -203,7 +216,7 @@ int WebRtcAgc_get_config(void* agcInst, WebRtcAgcConfig* config); * This function creates and returns an AGC instance, which will contain the * state information for one (duplex) channel. */ -void* WebRtcAgc_Create(); +void* WebRtcAgc_Create(void); /* * This function frees the AGC instance created at the beginning. @@ -229,14 +242,12 @@ void WebRtcAgc_Free(void* agcInst); * Return value : 0 - Ok * -1 - Error */ -int WebRtcAgc_Init(void *agcInst, +int WebRtcAgc_Init(void* agcInst, int32_t minLevel, int32_t maxLevel, int16_t agcMode, uint32_t fs); -#if defined(__cplusplus) -} -#endif +} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ +#endif // MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ diff --git a/webrtc/modules/audio_processing/agc/histogram.cc b/webrtc/modules/audio_processing/agc/loudness_histogram.cc similarity index 59% rename from webrtc/modules/audio_processing/agc/histogram.cc rename to webrtc/modules/audio_processing/agc/loudness_histogram.cc index 1d3035f..4775ff7 100644 --- a/webrtc/modules/audio_processing/agc/histogram.cc +++ b/webrtc/modules/audio_processing/agc/loudness_histogram.cc @@ -8,57 +8,58 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/agc/histogram.h" +#include "modules/audio_processing/agc/loudness_histogram.h" + +#include #include -#include -#include "webrtc/modules/interface/module_common_types.h" +#include "rtc_base/checks.h" namespace webrtc { static const double kHistBinCenters[] = { - 7.59621091765857e-02, 9.02036021061016e-02, 1.07115112009343e-01, - 1.27197217770508e-01, 1.51044347572047e-01, 1.79362373905283e-01, - 2.12989507320644e-01, 2.52921107370304e-01, 3.00339145144454e-01, - 3.56647189489147e-01, 4.23511952494003e-01, 5.02912623991786e-01, - 5.97199455365749e-01, 7.09163326739184e-01, 8.42118356728544e-01, - 1.00000000000000e+00, 1.18748153630660e+00, 1.41011239906908e+00, - 1.67448243801153e+00, 1.98841697800836e+00, 2.36120844786349e+00, - 2.80389143520905e+00, 3.32956930911896e+00, 3.95380207843188e+00, - 4.69506696634852e+00, 5.57530533426190e+00, 6.62057214370769e+00, - 7.86180718043869e+00, 9.33575086877358e+00, 1.10860317842269e+01, - 1.31644580546776e+01, 1.56325508754123e+01, 1.85633655299256e+01, - 2.20436538184971e+01, 2.61764319021997e+01, 3.10840295702492e+01, - 3.69117111886792e+01, 4.38319755100383e+01, 5.20496616180135e+01, - 6.18080121423973e+01, 7.33958732149108e+01, 8.71562442838066e+01, - 1.03496430860848e+02, 1.22900100720889e+02, 1.45941600416277e+02, - 1.73302955873365e+02, 2.05794060286978e+02, 2.44376646872353e+02, - 2.90192756065437e+02, 3.44598539797631e+02, 4.09204403447902e+02, - 4.85922673669740e+02, 5.77024203055553e+02, 6.85205587130498e+02, - 8.13668983291589e+02, 9.66216894324125e+02, 1.14736472207740e+03, - 1.36247442287647e+03, 1.61791322085579e+03, 1.92124207711260e+03, - 2.28143949334655e+03, 2.70916727454970e+03, 3.21708611729384e+03, - 3.82023036499473e+03, 4.53645302286906e+03, 5.38695420497926e+03, - 6.39690865534207e+03, 7.59621091765857e+03, 9.02036021061016e+03, - 1.07115112009343e+04, 1.27197217770508e+04, 1.51044347572047e+04, - 1.79362373905283e+04, 2.12989507320644e+04, 2.52921107370304e+04, - 3.00339145144454e+04, 3.56647189489147e+04}; + 7.59621091765857e-02, 9.02036021061016e-02, 1.07115112009343e-01, + 1.27197217770508e-01, 1.51044347572047e-01, 1.79362373905283e-01, + 2.12989507320644e-01, 2.52921107370304e-01, 3.00339145144454e-01, + 3.56647189489147e-01, 4.23511952494003e-01, 5.02912623991786e-01, + 5.97199455365749e-01, 7.09163326739184e-01, 8.42118356728544e-01, + 1.00000000000000e+00, 1.18748153630660e+00, 1.41011239906908e+00, + 1.67448243801153e+00, 1.98841697800836e+00, 2.36120844786349e+00, + 2.80389143520905e+00, 3.32956930911896e+00, 3.95380207843188e+00, + 4.69506696634852e+00, 5.57530533426190e+00, 6.62057214370769e+00, + 7.86180718043869e+00, 9.33575086877358e+00, 1.10860317842269e+01, + 1.31644580546776e+01, 1.56325508754123e+01, 1.85633655299256e+01, + 2.20436538184971e+01, 2.61764319021997e+01, 3.10840295702492e+01, + 3.69117111886792e+01, 4.38319755100383e+01, 5.20496616180135e+01, + 6.18080121423973e+01, 7.33958732149108e+01, 8.71562442838066e+01, + 1.03496430860848e+02, 1.22900100720889e+02, 1.45941600416277e+02, + 1.73302955873365e+02, 2.05794060286978e+02, 2.44376646872353e+02, + 2.90192756065437e+02, 3.44598539797631e+02, 4.09204403447902e+02, + 4.85922673669740e+02, 5.77024203055553e+02, 6.85205587130498e+02, + 8.13668983291589e+02, 9.66216894324125e+02, 1.14736472207740e+03, + 1.36247442287647e+03, 1.61791322085579e+03, 1.92124207711260e+03, + 2.28143949334655e+03, 2.70916727454970e+03, 3.21708611729384e+03, + 3.82023036499473e+03, 4.53645302286906e+03, 5.38695420497926e+03, + 6.39690865534207e+03, 7.59621091765857e+03, 9.02036021061016e+03, + 1.07115112009343e+04, 1.27197217770508e+04, 1.51044347572047e+04, + 1.79362373905283e+04, 2.12989507320644e+04, 2.52921107370304e+04, + 3.00339145144454e+04, 3.56647189489147e+04}; static const double kProbQDomain = 1024.0; // Loudness of -15 dB (smallest expected loudness) in log domain, // loudness_db = 13.5 * log10(rms); static const double kLogDomainMinBinCenter = -2.57752062648587; // Loudness step of 1 dB in log domain -static const double kLogDomainStepSizeInverse = 5.81954605750359; +static const double kLogDomainStepSizeInverse = 5.81954605750359; static const int kTransientWidthThreshold = 7; static const double kLowProbabilityThreshold = 0.2; -static const int kLowProbThresholdQ10 = static_cast( - kLowProbabilityThreshold * kProbQDomain); +static const int kLowProbThresholdQ10 = + static_cast(kLowProbabilityThreshold * kProbQDomain); -Histogram::Histogram() +LoudnessHistogram::LoudnessHistogram() : num_updates_(0), audio_content_q10_(0), bin_count_q10_(), @@ -73,7 +74,7 @@ Histogram::Histogram() "histogram bin centers incorrect size"); } -Histogram::Histogram(int window_size) +LoudnessHistogram::LoudnessHistogram(int window_size) : num_updates_(0), audio_content_q10_(0), bin_count_q10_(), @@ -84,9 +85,9 @@ Histogram::Histogram(int window_size) len_circular_buffer_(window_size), len_high_activity_(0) {} -Histogram::~Histogram() {} +LoudnessHistogram::~LoudnessHistogram() {} -void Histogram::Update(double rms, double activity_probaility) { +void LoudnessHistogram::Update(double rms, double activity_probaility) { // If circular histogram is activated then remove the oldest entry. if (len_circular_buffer_ > 0) RemoveOldestEntryAndUpdate(); @@ -94,14 +95,14 @@ void Histogram::Update(double rms, double activity_probaility) { // Find the corresponding bin. int hist_index = GetBinIndex(rms); // To Q10 domain. - int prob_q10 = static_cast(floor(activity_probaility * - kProbQDomain)); + int prob_q10 = + static_cast(floor(activity_probaility * kProbQDomain)); InsertNewestEntryAndUpdate(prob_q10, hist_index); } // Doing nothing if buffer is not full, yet. -void Histogram::RemoveOldestEntryAndUpdate() { - assert(len_circular_buffer_ > 0); +void LoudnessHistogram::RemoveOldestEntryAndUpdate() { + RTC_DCHECK_GT(len_circular_buffer_, 0); // Do nothing if circular buffer is not full. if (!buffer_is_full_) return; @@ -111,12 +112,12 @@ void Histogram::RemoveOldestEntryAndUpdate() { UpdateHist(-oldest_prob, oldest_hist_index); } -void Histogram::RemoveTransient() { +void LoudnessHistogram::RemoveTransient() { // Don't expect to be here if high-activity region is longer than // |kTransientWidthThreshold| or there has not been any transient. - assert(len_high_activity_ <= kTransientWidthThreshold); - int index = (buffer_index_ > 0) ? (buffer_index_ - 1) : - len_circular_buffer_ - 1; + RTC_DCHECK_LE(len_high_activity_, kTransientWidthThreshold); + int index = + (buffer_index_ > 0) ? (buffer_index_ - 1) : len_circular_buffer_ - 1; while (len_high_activity_ > 0) { UpdateHist(-activity_probability_[index], hist_bin_index_[index]); activity_probability_[index] = 0; @@ -125,8 +126,8 @@ void Histogram::RemoveTransient() { } } -void Histogram::InsertNewestEntryAndUpdate(int activity_prob_q10, - int hist_index) { +void LoudnessHistogram::InsertNewestEntryAndUpdate(int activity_prob_q10, + int hist_index) { // Update the circular buffer if it is enabled. if (len_circular_buffer_ > 0) { // Removing transient. @@ -158,26 +159,26 @@ void Histogram::InsertNewestEntryAndUpdate(int activity_prob_q10, UpdateHist(activity_prob_q10, hist_index); } -void Histogram::UpdateHist(int activity_prob_q10, int hist_index) { +void LoudnessHistogram::UpdateHist(int activity_prob_q10, int hist_index) { bin_count_q10_[hist_index] += activity_prob_q10; audio_content_q10_ += activity_prob_q10; } -double Histogram::AudioContent() const { +double LoudnessHistogram::AudioContent() const { return audio_content_q10_ / kProbQDomain; } -Histogram* Histogram::Create() { - return new Histogram; +LoudnessHistogram* LoudnessHistogram::Create() { + return new LoudnessHistogram; } -Histogram* Histogram::Create(int window_size) { +LoudnessHistogram* LoudnessHistogram::Create(int window_size) { if (window_size < 0) return NULL; - return new Histogram(window_size); + return new LoudnessHistogram(window_size); } -void Histogram::Reset() { +void LoudnessHistogram::Reset() { // Reset the histogram, audio-content and number of updates. memset(bin_count_q10_, 0, sizeof(bin_count_q10_)); audio_content_q10_ = 0; @@ -188,7 +189,7 @@ void Histogram::Reset() { len_high_activity_ = 0; } -int Histogram::GetBinIndex(double rms) { +int LoudnessHistogram::GetBinIndex(double rms) { // First exclude overload cases. if (rms <= kHistBinCenters[0]) { return 0; @@ -199,8 +200,8 @@ int Histogram::GetBinIndex(double rms) { // search in linear domain. double rms_log = log(rms); - int index = static_cast(floor((rms_log - kLogDomainMinBinCenter) * - kLogDomainStepSizeInverse)); + int index = static_cast( + floor((rms_log - kLogDomainMinBinCenter) * kLogDomainStepSizeInverse)); // The final decision is in linear domain. double b = 0.5 * (kHistBinCenters[index] + kHistBinCenters[index + 1]); if (rms > b) { @@ -210,7 +211,7 @@ int Histogram::GetBinIndex(double rms) { } } -double Histogram::CurrentRms() const { +double LoudnessHistogram::CurrentRms() const { double p; double mean_val = 0; if (audio_content_q10_ > 0) { diff --git a/webrtc/modules/audio_processing/agc/histogram.h b/webrtc/modules/audio_processing/agc/loudness_histogram.h similarity index 71% rename from webrtc/modules/audio_processing/agc/histogram.h rename to webrtc/modules/audio_processing/agc/loudness_histogram.h index a8706bb..badd443 100644 --- a/webrtc/modules/audio_processing/agc/histogram.h +++ b/webrtc/modules/audio_processing/agc/loudness_histogram.h @@ -8,27 +8,26 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_HISTOGRAM_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_HISTOGRAM_H_ +#ifndef MODULES_AUDIO_PROCESSING_AGC_LOUDNESS_HISTOGRAM_H_ +#define MODULES_AUDIO_PROCESSING_AGC_LOUDNESS_HISTOGRAM_H_ -#include +#include -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/typedefs.h" +#include namespace webrtc { // This class implements the histogram of loudness with circular buffers so that // the histogram tracks the last T seconds of the loudness. -class Histogram { +class LoudnessHistogram { public: - // Create a non-sliding Histogram. - static Histogram* Create(); + // Create a non-sliding LoudnessHistogram. + static LoudnessHistogram* Create(); - // Create a sliding Histogram, i.e. the histogram represents the last + // Create a sliding LoudnessHistogram, i.e. the histogram represents the last // |window_size| samples. - static Histogram* Create(int window_size); - ~Histogram(); + static LoudnessHistogram* Create(int window_size); + ~LoudnessHistogram(); // Insert RMS and the corresponding activity probability. void Update(double rms, double activity_probability); @@ -47,8 +46,8 @@ class Histogram { int num_updates() const { return num_updates_; } private: - Histogram(); - explicit Histogram(int window); + LoudnessHistogram(); + explicit LoudnessHistogram(int window); // Find the histogram bin associated with the given |rms|. int GetBinIndex(double rms); @@ -67,15 +66,15 @@ class Histogram { // |bin_count_q10_|. int64_t audio_content_q10_; - // Histogram of input RMS in Q10 with |kHistSize_| bins. In each 'Update(),' - // we increment the associated histogram-bin with the given probability. The - // increment is implemented in Q10 to avoid rounding errors. + // LoudnessHistogram of input RMS in Q10 with |kHistSize_| bins. In each + // 'Update(),' we increment the associated histogram-bin with the given + // probability. The increment is implemented in Q10 to avoid rounding errors. int64_t bin_count_q10_[kHistSize]; // Circular buffer for probabilities - rtc::scoped_ptr activity_probability_; + std::unique_ptr activity_probability_; // Circular buffer for histogram-indices of probabilities. - rtc::scoped_ptr hist_bin_index_; + std::unique_ptr hist_bin_index_; // Current index of circular buffer, where the newest data will be written to, // therefore, pointing to the oldest data if buffer is full. int buffer_index_; @@ -88,4 +87,4 @@ class Histogram { } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_HISTOGRAM_H_ +#endif // MODULES_AUDIO_PROCESSING_AGC_LOUDNESS_HISTOGRAM_H_ diff --git a/webrtc/modules/audio_processing/agc/mock_agc.h b/webrtc/modules/audio_processing/agc/mock_agc.h new file mode 100644 index 0000000..0ef41c6 --- /dev/null +++ b/webrtc/modules/audio_processing/agc/mock_agc.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC_MOCK_AGC_H_ +#define MODULES_AUDIO_PROCESSING_AGC_MOCK_AGC_H_ + +#include "modules/audio_processing/agc/agc.h" +#include "test/gmock.h" + +namespace webrtc { + +class MockAgc : public Agc { + public: + virtual ~MockAgc() {} + MOCK_METHOD(void, + Process, + (const int16_t* audio, size_t length, int sample_rate_hz), + (override)); + MOCK_METHOD(bool, GetRmsErrorDb, (int* error), (override)); + MOCK_METHOD(void, Reset, (), (override)); + MOCK_METHOD(int, set_target_level_dbfs, (int level), (override)); + MOCK_METHOD(int, target_level_dbfs, (), (const, override)); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_MOCK_AGC_H_ diff --git a/webrtc/modules/audio_processing/agc/utility.cc b/webrtc/modules/audio_processing/agc/utility.cc index 48458ad..2a87e5c 100644 --- a/webrtc/modules/audio_processing/agc/utility.cc +++ b/webrtc/modules/audio_processing/agc/utility.cc @@ -8,10 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/agc/utility.h" +#include "modules/audio_processing/agc/utility.h" #include +namespace webrtc { + static const double kLog10 = 2.30258509299; static const double kLinear2DbScale = 20.0 / kLog10; static const double kLinear2LoudnessScale = 13.4 / kLog10; @@ -33,3 +35,5 @@ double Db2Loudness(double db) { double Dbfs2Loudness(double dbfs) { return Db2Loudness(90 + dbfs); } + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc/utility.h b/webrtc/modules/audio_processing/agc/utility.h index df85c2e..56eec24 100644 --- a/webrtc/modules/audio_processing/agc/utility.h +++ b/webrtc/modules/audio_processing/agc/utility.h @@ -8,8 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_UTILITY_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_UTILITY_H_ +#ifndef MODULES_AUDIO_PROCESSING_AGC_UTILITY_H_ +#define MODULES_AUDIO_PROCESSING_AGC_UTILITY_H_ + +namespace webrtc { // TODO(turajs): Add description of function. double Loudness2Db(double loudness); @@ -20,4 +22,6 @@ double Db2Loudness(double db); double Dbfs2Loudness(double dbfs); -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_UTILITY_H_ +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC_UTILITY_H_ diff --git a/webrtc/modules/audio_processing/agc2/BUILD.gn b/webrtc/modules/audio_processing/agc2/BUILD.gn new file mode 100644 index 0000000..bf09533 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/BUILD.gn @@ -0,0 +1,290 @@ +# Copyright (c) 2017 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. + +import("../../../webrtc.gni") + +group("agc2") { + deps = [ + ":adaptive_digital", + ":fixed_digital", + ] +} + +rtc_library("level_estimation_agc") { + sources = [ + "adaptive_mode_level_estimator_agc.cc", + "adaptive_mode_level_estimator_agc.h", + ] + configs += [ "..:apm_debug_dump" ] + deps = [ + ":adaptive_digital", + ":common", + ":gain_applier", + ":noise_level_estimator", + ":rnn_vad_with_level", + "..:api", + "..:apm_logging", + "..:audio_frame_view", + "../../../api:array_view", + "../../../common_audio", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_minmax", + "../agc:level_estimation", + "../vad", + ] +} + +rtc_library("adaptive_digital") { + sources = [ + "adaptive_agc.cc", + "adaptive_agc.h", + "adaptive_digital_gain_applier.cc", + "adaptive_digital_gain_applier.h", + "adaptive_mode_level_estimator.cc", + "adaptive_mode_level_estimator.h", + "saturation_protector.cc", + "saturation_protector.h", + ] + + configs += [ "..:apm_debug_dump" ] + + deps = [ + ":common", + ":gain_applier", + ":noise_level_estimator", + ":rnn_vad_with_level", + "..:api", + "..:apm_logging", + "..:audio_frame_view", + "../../../api:array_view", + "../../../common_audio", + "../../../rtc_base:checks", + "../../../rtc_base:logging", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_compare", + "../../../rtc_base:safe_minmax", + "../../../system_wrappers:metrics", + ] + + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("biquad_filter") { + visibility = [ "./*" ] + sources = [ + "biquad_filter.cc", + "biquad_filter.h", + ] + deps = [ + "../../../api:array_view", + "../../../rtc_base:rtc_base_approved", + ] +} + +rtc_source_set("common") { + sources = [ "agc2_common.h" ] +} + +rtc_library("fixed_digital") { + sources = [ + "fixed_digital_level_estimator.cc", + "fixed_digital_level_estimator.h", + "interpolated_gain_curve.cc", + "interpolated_gain_curve.h", + "limiter.cc", + "limiter.h", + ] + + configs += [ "..:apm_debug_dump" ] + + deps = [ + ":common", + "..:apm_logging", + "..:audio_frame_view", + "../../../api:array_view", + "../../../common_audio", + "../../../rtc_base:checks", + "../../../rtc_base:gtest_prod", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_minmax", + "../../../system_wrappers:metrics", + ] +} + +rtc_library("gain_applier") { + sources = [ + "gain_applier.cc", + "gain_applier.h", + ] + deps = [ + ":common", + "..:audio_frame_view", + "../../../api:array_view", + "../../../rtc_base:safe_minmax", + ] +} + +rtc_library("noise_level_estimator") { + sources = [ + "down_sampler.cc", + "down_sampler.h", + "noise_level_estimator.cc", + "noise_level_estimator.h", + "noise_spectrum_estimator.cc", + "noise_spectrum_estimator.h", + "signal_classifier.cc", + "signal_classifier.h", + ] + deps = [ + ":biquad_filter", + "..:apm_logging", + "..:audio_frame_view", + "../../../api:array_view", + "../../../common_audio", + "../../../common_audio/third_party/ooura:fft_size_128", + "../../../rtc_base:checks", + "../../../rtc_base:macromagic", + "../../../system_wrappers", + ] + + configs += [ "..:apm_debug_dump" ] +} + +rtc_library("rnn_vad_with_level") { + sources = [ + "vad_with_level.cc", + "vad_with_level.h", + ] + deps = [ + ":common", + "..:audio_frame_view", + "../../../api:array_view", + "../../../common_audio", + "../../../rtc_base:checks", + "rnn_vad", + "rnn_vad:rnn_vad_common", + ] +} + +rtc_library("adaptive_digital_unittests") { + testonly = true + configs += [ "..:apm_debug_dump" ] + + sources = [ + "adaptive_digital_gain_applier_unittest.cc", + "adaptive_mode_level_estimator_unittest.cc", + "gain_applier_unittest.cc", + "saturation_protector_unittest.cc", + ] + deps = [ + ":adaptive_digital", + ":common", + ":gain_applier", + ":test_utils", + "..:apm_logging", + "..:audio_frame_view", + "../../../api:array_view", + "../../../common_audio", + "../../../rtc_base:checks", + "../../../rtc_base:gunit_helpers", + "../../../rtc_base:rtc_base_approved", + "../../../test:test_support", + ] +} + +rtc_library("biquad_filter_unittests") { + testonly = true + sources = [ "biquad_filter_unittest.cc" ] + deps = [ + ":biquad_filter", + "../../../rtc_base:gunit_helpers", + ] +} + +rtc_library("fixed_digital_unittests") { + testonly = true + configs += [ "..:apm_debug_dump" ] + + sources = [ + "agc2_testing_common_unittest.cc", + "compute_interpolated_gain_curve.cc", + "compute_interpolated_gain_curve.h", + "fixed_digital_level_estimator_unittest.cc", + "interpolated_gain_curve_unittest.cc", + "limiter_db_gain_curve.cc", + "limiter_db_gain_curve.h", + "limiter_db_gain_curve_unittest.cc", + "limiter_unittest.cc", + ] + deps = [ + ":common", + ":fixed_digital", + ":test_utils", + "..:apm_logging", + "..:audio_frame_view", + "../../../api:array_view", + "../../../common_audio", + "../../../rtc_base:checks", + "../../../rtc_base:gunit_helpers", + "../../../rtc_base:rtc_base_approved", + "../../../system_wrappers:metrics", + ] +} + +rtc_library("noise_estimator_unittests") { + testonly = true + configs += [ "..:apm_debug_dump" ] + + sources = [ + "noise_level_estimator_unittest.cc", + "signal_classifier_unittest.cc", + ] + deps = [ + ":noise_level_estimator", + ":test_utils", + "..:apm_logging", + "..:audio_frame_view", + "../../../api:array_view", + "../../../rtc_base:checks", + "../../../rtc_base:gunit_helpers", + "../../../rtc_base:rtc_base_approved", + ] +} + +rtc_library("rnn_vad_with_level_unittests") { + testonly = true + sources = [ "vad_with_level_unittest.cc" ] + deps = [ + ":common", + ":rnn_vad_with_level", + "..:audio_frame_view", + "../../../rtc_base:gunit_helpers", + "../../../rtc_base:safe_compare", + "../../../test:test_support", + ] +} + +rtc_library("test_utils") { + testonly = true + visibility = [ + ":*", + "..:audio_processing_unittests", + ] + sources = [ + "agc2_testing_common.cc", + "agc2_testing_common.h", + "vector_float_frame.cc", + "vector_float_frame.h", + ] + deps = [ + "..:audio_frame_view", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + ] +} diff --git a/webrtc/modules/audio_processing/agc2/adaptive_agc.cc b/webrtc/modules/audio_processing/agc2/adaptive_agc.cc new file mode 100644 index 0000000..0372ccf --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/adaptive_agc.cc @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/adaptive_agc.h" + +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/agc2/vad_with_level.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace { + +void DumpDebugData(const AdaptiveDigitalGainApplier::FrameInfo& info, + ApmDataDumper& dumper) { + dumper.DumpRaw("agc2_vad_probability", info.vad_result.speech_probability); + dumper.DumpRaw("agc2_vad_rms_dbfs", info.vad_result.rms_dbfs); + dumper.DumpRaw("agc2_vad_peak_dbfs", info.vad_result.peak_dbfs); + dumper.DumpRaw("agc2_noise_estimate_dbfs", info.input_noise_level_dbfs); + dumper.DumpRaw("agc2_last_limiter_audio_level", info.limiter_envelope_dbfs); +} + +constexpr int kGainApplierAdjacentSpeechFramesThreshold = 1; +constexpr float kMaxGainChangePerSecondDb = 3.f; +constexpr float kMaxOutputNoiseLevelDbfs = -50.f; + +} // namespace + +AdaptiveAgc::AdaptiveAgc(ApmDataDumper* apm_data_dumper) + : speech_level_estimator_(apm_data_dumper), + gain_applier_(apm_data_dumper, + kGainApplierAdjacentSpeechFramesThreshold, + kMaxGainChangePerSecondDb, + kMaxOutputNoiseLevelDbfs), + apm_data_dumper_(apm_data_dumper), + noise_level_estimator_(apm_data_dumper) { + RTC_DCHECK(apm_data_dumper); +} + +AdaptiveAgc::AdaptiveAgc(ApmDataDumper* apm_data_dumper, + const AudioProcessing::Config::GainController2& config) + : speech_level_estimator_( + apm_data_dumper, + config.adaptive_digital.level_estimator, + config.adaptive_digital + .level_estimator_adjacent_speech_frames_threshold, + config.adaptive_digital.initial_saturation_margin_db, + config.adaptive_digital.extra_saturation_margin_db), + vad_(config.adaptive_digital.vad_probability_attack), + gain_applier_( + apm_data_dumper, + config.adaptive_digital.gain_applier_adjacent_speech_frames_threshold, + config.adaptive_digital.max_gain_change_db_per_second, + config.adaptive_digital.max_output_noise_level_dbfs), + apm_data_dumper_(apm_data_dumper), + noise_level_estimator_(apm_data_dumper) { + RTC_DCHECK(apm_data_dumper); + if (!config.adaptive_digital.use_saturation_protector) { + RTC_LOG(LS_WARNING) << "The saturation protector cannot be disabled."; + } +} + +AdaptiveAgc::~AdaptiveAgc() = default; + +void AdaptiveAgc::Process(AudioFrameView frame, float limiter_envelope) { + AdaptiveDigitalGainApplier::FrameInfo info; + info.vad_result = vad_.AnalyzeFrame(frame); + speech_level_estimator_.Update(info.vad_result); + info.input_level_dbfs = speech_level_estimator_.level_dbfs(); + info.input_noise_level_dbfs = noise_level_estimator_.Analyze(frame); + info.limiter_envelope_dbfs = + limiter_envelope > 0 ? FloatS16ToDbfs(limiter_envelope) : -90.f; + info.estimate_is_confident = speech_level_estimator_.IsConfident(); + DumpDebugData(info, *apm_data_dumper_); + gain_applier_.Process(info, frame); +} + +void AdaptiveAgc::Reset() { + speech_level_estimator_.Reset(); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/adaptive_agc.h b/webrtc/modules/audio_processing/agc2/adaptive_agc.h new file mode 100644 index 0000000..f3c7854 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/adaptive_agc.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_AGC_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_AGC_H_ + +#include "modules/audio_processing/agc2/adaptive_digital_gain_applier.h" +#include "modules/audio_processing/agc2/adaptive_mode_level_estimator.h" +#include "modules/audio_processing/agc2/noise_level_estimator.h" +#include "modules/audio_processing/agc2/vad_with_level.h" +#include "modules/audio_processing/include/audio_frame_view.h" +#include "modules/audio_processing/include/audio_processing.h" + +namespace webrtc { +class ApmDataDumper; + +// Adaptive digital gain controller. +// TODO(crbug.com/webrtc/7494): Unify with `AdaptiveDigitalGainApplier`. +class AdaptiveAgc { + public: + explicit AdaptiveAgc(ApmDataDumper* apm_data_dumper); + // TODO(crbug.com/webrtc/7494): Remove ctor above. + AdaptiveAgc(ApmDataDumper* apm_data_dumper, + const AudioProcessing::Config::GainController2& config); + ~AdaptiveAgc(); + + // Analyzes `frame` and applies a digital adaptive gain to it. Takes into + // account the envelope measured by the limiter. + // TODO(crbug.com/webrtc/7494): Make the class depend on the limiter. + void Process(AudioFrameView frame, float limiter_envelope); + void Reset(); + + private: + AdaptiveModeLevelEstimator speech_level_estimator_; + VadLevelAnalyzer vad_; + AdaptiveDigitalGainApplier gain_applier_; + ApmDataDumper* const apm_data_dumper_; + NoiseLevelEstimator noise_level_estimator_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_AGC_H_ diff --git a/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc b/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc new file mode 100644 index 0000000..36ef9be --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/adaptive_digital_gain_applier.h" + +#include + +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { +namespace { + +// This function maps input level to desired applied gain. We want to +// boost the signal so that peaks are at -kHeadroomDbfs. We can't +// apply more than kMaxGainDb gain. +float ComputeGainDb(float input_level_dbfs) { + // If the level is very low, boost it as much as we can. + if (input_level_dbfs < -(kHeadroomDbfs + kMaxGainDb)) { + return kMaxGainDb; + } + + // We expect to end up here most of the time: the level is below + // -headroom, but we can boost it to -headroom. + if (input_level_dbfs < -kHeadroomDbfs) { + return -kHeadroomDbfs - input_level_dbfs; + } + + // Otherwise, the level is too high and we can't boost. The + // LevelEstimator is responsible for not reporting bogus gain + // values. + RTC_DCHECK_LE(input_level_dbfs, 0.f); + return 0.f; +} + +// Returns `target_gain` if the output noise level is below +// `max_output_noise_level_dbfs`; otherwise returns a capped gain so that the +// output noise level equals `max_output_noise_level_dbfs`. +float LimitGainByNoise(float target_gain, + float input_noise_level_dbfs, + float max_output_noise_level_dbfs, + ApmDataDumper& apm_data_dumper) { + const float noise_headroom_db = + max_output_noise_level_dbfs - input_noise_level_dbfs; + apm_data_dumper.DumpRaw("agc2_noise_headroom_db", noise_headroom_db); + return std::min(target_gain, std::max(noise_headroom_db, 0.f)); +} + +float LimitGainByLowConfidence(float target_gain, + float last_gain, + float limiter_audio_level_dbfs, + bool estimate_is_confident) { + if (estimate_is_confident || + limiter_audio_level_dbfs <= kLimiterThresholdForAgcGainDbfs) { + return target_gain; + } + const float limiter_level_before_gain = limiter_audio_level_dbfs - last_gain; + + // Compute a new gain so that limiter_level_before_gain + new_gain <= + // kLimiterThreshold. + const float new_target_gain = std::max( + kLimiterThresholdForAgcGainDbfs - limiter_level_before_gain, 0.f); + return std::min(new_target_gain, target_gain); +} + +// Computes how the gain should change during this frame. +// Return the gain difference in db to 'last_gain_db'. +float ComputeGainChangeThisFrameDb(float target_gain_db, + float last_gain_db, + bool gain_increase_allowed, + float max_gain_change_db) { + float target_gain_difference_db = target_gain_db - last_gain_db; + if (!gain_increase_allowed) { + target_gain_difference_db = std::min(target_gain_difference_db, 0.f); + } + return rtc::SafeClamp(target_gain_difference_db, -max_gain_change_db, + max_gain_change_db); +} + +} // namespace + +AdaptiveDigitalGainApplier::AdaptiveDigitalGainApplier( + ApmDataDumper* apm_data_dumper, + int adjacent_speech_frames_threshold, + float max_gain_change_db_per_second, + float max_output_noise_level_dbfs) + : apm_data_dumper_(apm_data_dumper), + gain_applier_( + /*hard_clip_samples=*/false, + /*initial_gain_factor=*/DbToRatio(kInitialAdaptiveDigitalGainDb)), + adjacent_speech_frames_threshold_(adjacent_speech_frames_threshold), + max_gain_change_db_per_10ms_(max_gain_change_db_per_second * + kFrameDurationMs / 1000.f), + max_output_noise_level_dbfs_(max_output_noise_level_dbfs), + calls_since_last_gain_log_(0), + frames_to_gain_increase_allowed_(adjacent_speech_frames_threshold_), + last_gain_db_(kInitialAdaptiveDigitalGainDb) { + RTC_DCHECK_GT(max_gain_change_db_per_second, 0.f); + RTC_DCHECK_GE(frames_to_gain_increase_allowed_, 1); + RTC_DCHECK_GE(max_output_noise_level_dbfs_, -90.f); + RTC_DCHECK_LE(max_output_noise_level_dbfs_, 0.f); +} + +void AdaptiveDigitalGainApplier::Process(const FrameInfo& info, + AudioFrameView frame) { + RTC_DCHECK_GE(info.input_level_dbfs, -150.f); + RTC_DCHECK_GE(frame.num_channels(), 1); + RTC_DCHECK( + frame.samples_per_channel() == 80 || frame.samples_per_channel() == 160 || + frame.samples_per_channel() == 320 || frame.samples_per_channel() == 480) + << "`frame` does not look like a 10 ms frame for an APM supported sample " + "rate"; + + const float target_gain_db = LimitGainByLowConfidence( + LimitGainByNoise(ComputeGainDb(std::min(info.input_level_dbfs, 0.f)), + info.input_noise_level_dbfs, + max_output_noise_level_dbfs_, *apm_data_dumper_), + last_gain_db_, info.limiter_envelope_dbfs, info.estimate_is_confident); + + // Forbid increasing the gain until enough adjacent speech frames are + // observed. + if (info.vad_result.speech_probability < kVadConfidenceThreshold) { + frames_to_gain_increase_allowed_ = adjacent_speech_frames_threshold_; + } else if (frames_to_gain_increase_allowed_ > 0) { + frames_to_gain_increase_allowed_--; + } + + const float gain_change_this_frame_db = ComputeGainChangeThisFrameDb( + target_gain_db, last_gain_db_, + /*gain_increase_allowed=*/frames_to_gain_increase_allowed_ == 0, + max_gain_change_db_per_10ms_); + + apm_data_dumper_->DumpRaw("agc2_want_to_change_by_db", + target_gain_db - last_gain_db_); + apm_data_dumper_->DumpRaw("agc2_will_change_by_db", + gain_change_this_frame_db); + + // Optimization: avoid calling math functions if gain does not + // change. + if (gain_change_this_frame_db != 0.f) { + gain_applier_.SetGainFactor( + DbToRatio(last_gain_db_ + gain_change_this_frame_db)); + } + gain_applier_.ApplyGain(frame); + + // Remember that the gain has changed for the next iteration. + last_gain_db_ = last_gain_db_ + gain_change_this_frame_db; + apm_data_dumper_->DumpRaw("agc2_applied_gain_db", last_gain_db_); + + // Log every 10 seconds. + calls_since_last_gain_log_++; + if (calls_since_last_gain_log_ == 1000) { + calls_since_last_gain_log_ = 0; + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc2.DigitalGainApplied", + last_gain_db_, 0, kMaxGainDb, kMaxGainDb + 1); + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.Agc2.EstimatedSpeechPlusNoiseLevel", + -info.input_level_dbfs, 0, 100, 101); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc2.EstimatedNoiseLevel", + -info.input_noise_level_dbfs, 0, 100, 101); + RTC_LOG(LS_INFO) << "AGC2 adaptive digital" + << " | speech_plus_noise_dbfs: " << info.input_level_dbfs + << " | noise_dbfs: " << info.input_noise_level_dbfs + << " | gain_db: " << last_gain_db_; + } +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_applier.h b/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_applier.h new file mode 100644 index 0000000..a65379f --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/adaptive_digital_gain_applier.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_APPLIER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_APPLIER_H_ + +#include "modules/audio_processing/agc2/gain_applier.h" +#include "modules/audio_processing/agc2/vad_with_level.h" +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { + +class ApmDataDumper; + +// Part of the adaptive digital controller that applies a digital adaptive gain. +// The gain is updated towards a target. The logic decides when gain updates are +// allowed, it controls the adaptation speed and caps the target based on the +// estimated noise level and the speech level estimate confidence. +class AdaptiveDigitalGainApplier { + public: + // Information about a frame to process. + struct FrameInfo { + float input_level_dbfs; // Estimated speech plus noise level. + float input_noise_level_dbfs; // Estimated noise level. + VadLevelAnalyzer::Result vad_result; + float limiter_envelope_dbfs; // Envelope level from the limiter. + bool estimate_is_confident; + }; + + // Ctor. + // `adjacent_speech_frames_threshold` indicates how many speech frames are + // required before a gain increase is allowed. `max_gain_change_db_per_second` + // limits the adaptation speed (uniformly operated across frames). + // `max_output_noise_level_dbfs` limits the output noise level. + AdaptiveDigitalGainApplier(ApmDataDumper* apm_data_dumper, + int adjacent_speech_frames_threshold, + float max_gain_change_db_per_second, + float max_output_noise_level_dbfs); + AdaptiveDigitalGainApplier(const AdaptiveDigitalGainApplier&) = delete; + AdaptiveDigitalGainApplier& operator=(const AdaptiveDigitalGainApplier&) = + delete; + + // Analyzes `info`, updates the digital gain and applies it to a 10 ms + // `frame`. Supports any sample rate supported by APM. + void Process(const FrameInfo& info, AudioFrameView frame); + + private: + ApmDataDumper* const apm_data_dumper_; + GainApplier gain_applier_; + + const int adjacent_speech_frames_threshold_; + const float max_gain_change_db_per_10ms_; + const float max_output_noise_level_dbfs_; + + int calls_since_last_gain_log_; + int frames_to_gain_increase_allowed_; + float last_gain_db_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_APPLIER_H_ diff --git a/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc b/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc new file mode 100644 index 0000000..f09f63b --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/adaptive_mode_level_estimator.h" + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +using LevelEstimatorType = + AudioProcessing::Config::GainController2::LevelEstimator; + +// Combines a level estimation with the saturation protector margins. +float ComputeLevelEstimateDbfs(float level_estimate_dbfs, + float saturation_margin_db, + float extra_saturation_margin_db) { + return rtc::SafeClamp( + level_estimate_dbfs + saturation_margin_db + extra_saturation_margin_db, + -90.f, 30.f); +} + +// Returns the level of given type from `vad_level`. +float GetLevel(const VadLevelAnalyzer::Result& vad_level, + LevelEstimatorType type) { + switch (type) { + case LevelEstimatorType::kRms: + return vad_level.rms_dbfs; + break; + case LevelEstimatorType::kPeak: + return vad_level.peak_dbfs; + break; + } +} + +} // namespace + +bool AdaptiveModeLevelEstimator::LevelEstimatorState::operator==( + const AdaptiveModeLevelEstimator::LevelEstimatorState& b) const { + return time_to_full_buffer_ms == b.time_to_full_buffer_ms && + level_dbfs.numerator == b.level_dbfs.numerator && + level_dbfs.denominator == b.level_dbfs.denominator && + saturation_protector == b.saturation_protector; +} + +float AdaptiveModeLevelEstimator::LevelEstimatorState::Ratio::GetRatio() const { + RTC_DCHECK_NE(denominator, 0.f); + return numerator / denominator; +} + +AdaptiveModeLevelEstimator::AdaptiveModeLevelEstimator( + ApmDataDumper* apm_data_dumper) + : AdaptiveModeLevelEstimator( + apm_data_dumper, + AudioProcessing::Config::GainController2::LevelEstimator::kRms, + kDefaultLevelEstimatorAdjacentSpeechFramesThreshold, + kDefaultInitialSaturationMarginDb, + kDefaultExtraSaturationMarginDb) {} + +AdaptiveModeLevelEstimator::AdaptiveModeLevelEstimator( + ApmDataDumper* apm_data_dumper, + AudioProcessing::Config::GainController2::LevelEstimator level_estimator, + int adjacent_speech_frames_threshold, + float initial_saturation_margin_db, + float extra_saturation_margin_db) + : apm_data_dumper_(apm_data_dumper), + level_estimator_type_(level_estimator), + adjacent_speech_frames_threshold_(adjacent_speech_frames_threshold), + initial_saturation_margin_db_(initial_saturation_margin_db), + extra_saturation_margin_db_(extra_saturation_margin_db), + level_dbfs_(ComputeLevelEstimateDbfs(kInitialSpeechLevelEstimateDbfs, + initial_saturation_margin_db_, + extra_saturation_margin_db_)) { + RTC_DCHECK(apm_data_dumper_); + RTC_DCHECK_GE(adjacent_speech_frames_threshold_, 1); + Reset(); +} + +void AdaptiveModeLevelEstimator::Update( + const VadLevelAnalyzer::Result& vad_level) { + RTC_DCHECK_GT(vad_level.rms_dbfs, -150.f); + RTC_DCHECK_LT(vad_level.rms_dbfs, 50.f); + RTC_DCHECK_GT(vad_level.peak_dbfs, -150.f); + RTC_DCHECK_LT(vad_level.peak_dbfs, 50.f); + RTC_DCHECK_GE(vad_level.speech_probability, 0.f); + RTC_DCHECK_LE(vad_level.speech_probability, 1.f); + DumpDebugData(); + + if (vad_level.speech_probability < kVadConfidenceThreshold) { + // Not a speech frame. + if (adjacent_speech_frames_threshold_ > 1) { + // When two or more adjacent speech frames are required in order to update + // the state, we need to decide whether to discard or confirm the updates + // based on the speech sequence length. + if (num_adjacent_speech_frames_ >= adjacent_speech_frames_threshold_) { + // First non-speech frame after a long enough sequence of speech frames. + // Update the reliable state. + reliable_state_ = preliminary_state_; + } else if (num_adjacent_speech_frames_ > 0) { + // First non-speech frame after a too short sequence of speech frames. + // Reset to the last reliable state. + preliminary_state_ = reliable_state_; + } + } + num_adjacent_speech_frames_ = 0; + return; + } + + // Speech frame observed. + num_adjacent_speech_frames_++; + + // Update preliminary level estimate. + RTC_DCHECK_GE(preliminary_state_.time_to_full_buffer_ms, 0); + const bool buffer_is_full = preliminary_state_.time_to_full_buffer_ms == 0; + if (!buffer_is_full) { + preliminary_state_.time_to_full_buffer_ms -= kFrameDurationMs; + } + // Weighted average of levels with speech probability as weight. + RTC_DCHECK_GT(vad_level.speech_probability, 0.f); + const float leak_factor = buffer_is_full ? kFullBufferLeakFactor : 1.f; + preliminary_state_.level_dbfs.numerator = + preliminary_state_.level_dbfs.numerator * leak_factor + + GetLevel(vad_level, level_estimator_type_) * vad_level.speech_probability; + preliminary_state_.level_dbfs.denominator = + preliminary_state_.level_dbfs.denominator * leak_factor + + vad_level.speech_probability; + + const float level_dbfs = preliminary_state_.level_dbfs.GetRatio(); + + UpdateSaturationProtectorState(vad_level.peak_dbfs, level_dbfs, + preliminary_state_.saturation_protector); + + if (num_adjacent_speech_frames_ >= adjacent_speech_frames_threshold_) { + // `preliminary_state_` is now reliable. Update the last level estimation. + level_dbfs_ = ComputeLevelEstimateDbfs( + level_dbfs, preliminary_state_.saturation_protector.margin_db, + extra_saturation_margin_db_); + } +} + +bool AdaptiveModeLevelEstimator::IsConfident() const { + if (adjacent_speech_frames_threshold_ == 1) { + // Ignore `reliable_state_` when a single frame is enough to update the + // level estimate (because it is not used). + return preliminary_state_.time_to_full_buffer_ms == 0; + } + // Once confident, it remains confident. + RTC_DCHECK(reliable_state_.time_to_full_buffer_ms != 0 || + preliminary_state_.time_to_full_buffer_ms == 0); + // During the first long enough speech sequence, `reliable_state_` must be + // ignored since `preliminary_state_` is used. + return reliable_state_.time_to_full_buffer_ms == 0 || + (num_adjacent_speech_frames_ >= adjacent_speech_frames_threshold_ && + preliminary_state_.time_to_full_buffer_ms == 0); +} + +void AdaptiveModeLevelEstimator::Reset() { + ResetLevelEstimatorState(preliminary_state_); + ResetLevelEstimatorState(reliable_state_); + level_dbfs_ = ComputeLevelEstimateDbfs(kInitialSpeechLevelEstimateDbfs, + initial_saturation_margin_db_, + extra_saturation_margin_db_); + num_adjacent_speech_frames_ = 0; +} + +void AdaptiveModeLevelEstimator::ResetLevelEstimatorState( + LevelEstimatorState& state) const { + state.time_to_full_buffer_ms = kFullBufferSizeMs; + state.level_dbfs.numerator = 0.f; + state.level_dbfs.denominator = 0.f; + ResetSaturationProtectorState(initial_saturation_margin_db_, + state.saturation_protector); +} + +void AdaptiveModeLevelEstimator::DumpDebugData() const { + apm_data_dumper_->DumpRaw("agc2_adaptive_level_estimate_dbfs", level_dbfs_); + apm_data_dumper_->DumpRaw("agc2_adaptive_num_adjacent_speech_frames_", + num_adjacent_speech_frames_); + apm_data_dumper_->DumpRaw("agc2_adaptive_preliminary_level_estimate_num", + preliminary_state_.level_dbfs.numerator); + apm_data_dumper_->DumpRaw("agc2_adaptive_preliminary_level_estimate_den", + preliminary_state_.level_dbfs.denominator); + apm_data_dumper_->DumpRaw("agc2_adaptive_preliminary_saturation_margin_db", + preliminary_state_.saturation_protector.margin_db); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator.h b/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator.h new file mode 100644 index 0000000..213fc0f --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_H_ + +#include +#include + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/agc2/saturation_protector.h" +#include "modules/audio_processing/agc2/vad_with_level.h" +#include "modules/audio_processing/include/audio_processing.h" + +namespace webrtc { +class ApmDataDumper; + +// Level estimator for the digital adaptive gain controller. +class AdaptiveModeLevelEstimator { + public: + explicit AdaptiveModeLevelEstimator(ApmDataDumper* apm_data_dumper); + AdaptiveModeLevelEstimator(const AdaptiveModeLevelEstimator&) = delete; + AdaptiveModeLevelEstimator& operator=(const AdaptiveModeLevelEstimator&) = + delete; + AdaptiveModeLevelEstimator( + ApmDataDumper* apm_data_dumper, + AudioProcessing::Config::GainController2::LevelEstimator level_estimator, + int adjacent_speech_frames_threshold, + float initial_saturation_margin_db, + float extra_saturation_margin_db); + + // Updates the level estimation. + void Update(const VadLevelAnalyzer::Result& vad_data); + // Returns the estimated speech plus noise level. + float level_dbfs() const { return level_dbfs_; } + // Returns true if the estimator is confident on its current estimate. + bool IsConfident() const; + + void Reset(); + + private: + // Part of the level estimator state used for check-pointing and restore ops. + struct LevelEstimatorState { + bool operator==(const LevelEstimatorState& s) const; + inline bool operator!=(const LevelEstimatorState& s) const { + return !(*this == s); + } + struct Ratio { + float numerator; + float denominator; + float GetRatio() const; + }; + // TODO(crbug.com/webrtc/7494): Remove time_to_full_buffer_ms if redundant. + int time_to_full_buffer_ms; + Ratio level_dbfs; + SaturationProtectorState saturation_protector; + }; + static_assert(std::is_trivially_copyable::value, ""); + + void ResetLevelEstimatorState(LevelEstimatorState& state) const; + + void DumpDebugData() const; + + ApmDataDumper* const apm_data_dumper_; + + const AudioProcessing::Config::GainController2::LevelEstimator + level_estimator_type_; + const int adjacent_speech_frames_threshold_; + const float initial_saturation_margin_db_; + const float extra_saturation_margin_db_; + LevelEstimatorState preliminary_state_; + LevelEstimatorState reliable_state_; + float level_dbfs_; + int num_adjacent_speech_frames_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc b/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc new file mode 100644 index 0000000..5ceeb7d --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h" + +#include +#include + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { + +AdaptiveModeLevelEstimatorAgc::AdaptiveModeLevelEstimatorAgc( + ApmDataDumper* apm_data_dumper) + : level_estimator_(apm_data_dumper) { + set_target_level_dbfs(kDefaultAgc2LevelHeadroomDbfs); +} + +// |audio| must be mono; in a multi-channel stream, provide the first (usually +// left) channel. +void AdaptiveModeLevelEstimatorAgc::Process(const int16_t* audio, + size_t length, + int sample_rate_hz) { + std::vector float_audio_frame(audio, audio + length); + const float* const first_channel = &float_audio_frame[0]; + AudioFrameView frame_view(&first_channel, 1 /* num channels */, + length); + const auto vad_prob = agc2_vad_.AnalyzeFrame(frame_view); + latest_voice_probability_ = vad_prob.speech_probability; + if (latest_voice_probability_ > kVadConfidenceThreshold) { + time_in_ms_since_last_estimate_ += kFrameDurationMs; + } + level_estimator_.Update(vad_prob); +} + +// Retrieves the difference between the target RMS level and the current +// signal RMS level in dB. Returns true if an update is available and false +// otherwise, in which case |error| should be ignored and no action taken. +bool AdaptiveModeLevelEstimatorAgc::GetRmsErrorDb(int* error) { + if (time_in_ms_since_last_estimate_ <= kTimeUntilConfidentMs) { + return false; + } + *error = + std::floor(target_level_dbfs() - level_estimator_.level_dbfs() + 0.5f); + time_in_ms_since_last_estimate_ = 0; + return true; +} + +void AdaptiveModeLevelEstimatorAgc::Reset() { + level_estimator_.Reset(); +} + +float AdaptiveModeLevelEstimatorAgc::voice_probability() const { + return latest_voice_probability_; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h b/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h new file mode 100644 index 0000000..bc6fa84 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_AGC_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_AGC_H_ + +#include +#include + +#include "modules/audio_processing/agc/agc.h" +#include "modules/audio_processing/agc2/adaptive_mode_level_estimator.h" +#include "modules/audio_processing/agc2/saturation_protector.h" +#include "modules/audio_processing/agc2/vad_with_level.h" + +namespace webrtc { +class AdaptiveModeLevelEstimatorAgc : public Agc { + public: + explicit AdaptiveModeLevelEstimatorAgc(ApmDataDumper* apm_data_dumper); + + // |audio| must be mono; in a multi-channel stream, provide the first (usually + // left) channel. + void Process(const int16_t* audio, + size_t length, + int sample_rate_hz) override; + + // Retrieves the difference between the target RMS level and the current + // signal RMS level in dB. Returns true if an update is available and false + // otherwise, in which case |error| should be ignored and no action taken. + bool GetRmsErrorDb(int* error) override; + void Reset() override; + + float voice_probability() const override; + + private: + static constexpr int kTimeUntilConfidentMs = 700; + static constexpr int kDefaultAgc2LevelHeadroomDbfs = -1; + int32_t time_in_ms_since_last_estimate_ = 0; + AdaptiveModeLevelEstimator level_estimator_; + VadLevelAnalyzer agc2_vad_; + float latest_voice_probability_ = 0.f; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_AGC_H_ diff --git a/webrtc/modules/audio_processing/agc2/agc2_common.h b/webrtc/modules/audio_processing/agc2/agc2_common.h new file mode 100644 index 0000000..5d01100 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/agc2_common.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_AGC2_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_AGC2_COMMON_H_ + +#include + +namespace webrtc { + +constexpr float kMinFloatS16Value = -32768.f; +constexpr float kMaxFloatS16Value = 32767.f; +constexpr float kMaxAbsFloatS16Value = 32768.0f; + +constexpr size_t kFrameDurationMs = 10; +constexpr size_t kSubFramesInFrame = 20; +constexpr size_t kMaximalNumberOfSamplesPerChannel = 480; + +constexpr float kAttackFilterConstant = 0.f; + +// Adaptive digital gain applier settings below. +constexpr float kHeadroomDbfs = 1.f; +constexpr float kMaxGainDb = 30.f; +constexpr float kInitialAdaptiveDigitalGainDb = 8.f; +// At what limiter levels should we start decreasing the adaptive digital gain. +constexpr float kLimiterThresholdForAgcGainDbfs = -kHeadroomDbfs; + +// This is the threshold for speech. Speech frames are used for updating the +// speech level, measuring the amount of speech, and decide when to allow target +// gain reduction. +constexpr float kVadConfidenceThreshold = 0.9f; + +// The amount of 'memory' of the Level Estimator. Decides leak factors. +constexpr size_t kFullBufferSizeMs = 1200; +constexpr float kFullBufferLeakFactor = 1.f - 1.f / kFullBufferSizeMs; + +constexpr float kInitialSpeechLevelEstimateDbfs = -30.f; + +// Robust VAD probability and speech decisions. +constexpr float kDefaultSmoothedVadProbabilityAttack = 1.f; +constexpr int kDefaultLevelEstimatorAdjacentSpeechFramesThreshold = 1; + +// Saturation Protector settings. +constexpr float kDefaultInitialSaturationMarginDb = 20.f; +constexpr float kDefaultExtraSaturationMarginDb = 2.f; + +constexpr size_t kPeakEnveloperSuperFrameLengthMs = 400; +static_assert(kFullBufferSizeMs % kPeakEnveloperSuperFrameLengthMs == 0, + "Full buffer size should be a multiple of super frame length for " + "optimal Saturation Protector performance."); + +constexpr size_t kPeakEnveloperBufferSize = + kFullBufferSizeMs / kPeakEnveloperSuperFrameLengthMs + 1; + +// This value is 10 ** (-1/20 * frame_size_ms / satproc_attack_ms), +// where satproc_attack_ms is 5000. +constexpr float kSaturationProtectorAttackConstant = 0.9988493699365052f; + +// This value is 10 ** (-1/20 * frame_size_ms / satproc_decay_ms), +// where satproc_decay_ms is 1000. +constexpr float kSaturationProtectorDecayConstant = 0.9997697679981565f; + +// This is computed from kDecayMs by +// 10 ** (-1/20 * subframe_duration / kDecayMs). +// |subframe_duration| is |kFrameDurationMs / kSubFramesInFrame|. +// kDecayMs is defined in agc2_testing_common.h +constexpr float kDecayFilterConstant = 0.9998848773724686f; + +// Number of interpolation points for each region of the limiter. +// These values have been tuned to limit the interpolated gain curve error given +// the limiter parameters and allowing a maximum error of +/- 32768^-1. +constexpr size_t kInterpolatedGainCurveKneePoints = 22; +constexpr size_t kInterpolatedGainCurveBeyondKneePoints = 10; +constexpr size_t kInterpolatedGainCurveTotalPoints = + kInterpolatedGainCurveKneePoints + kInterpolatedGainCurveBeyondKneePoints; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_AGC2_COMMON_H_ diff --git a/webrtc/modules/audio_processing/agc2/agc2_testing_common.cc b/webrtc/modules/audio_processing/agc2/agc2_testing_common.cc new file mode 100644 index 0000000..6c22492 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/agc2_testing_common.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/agc2_testing_common.h" + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace test { + +std::vector LinSpace(const double l, + const double r, + size_t num_points) { + RTC_CHECK(num_points >= 2); + std::vector points(num_points); + const double step = (r - l) / (num_points - 1.0); + points[0] = l; + for (size_t i = 1; i < num_points - 1; i++) { + points[i] = static_cast(l) + i * step; + } + points[num_points - 1] = r; + return points; +} +} // namespace test +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/agc2_testing_common.h b/webrtc/modules/audio_processing/agc2/agc2_testing_common.h new file mode 100644 index 0000000..7bfadbb --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/agc2_testing_common.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_AGC2_TESTING_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_AGC2_TESTING_COMMON_H_ + +#include + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace test { + +// Level Estimator test parameters. +constexpr float kDecayMs = 500.f; + +// Limiter parameters. +constexpr float kLimiterMaxInputLevelDbFs = 1.f; +constexpr float kLimiterKneeSmoothnessDb = 1.f; +constexpr float kLimiterCompressionRatio = 5.f; +constexpr float kPi = 3.1415926536f; + +std::vector LinSpace(const double l, const double r, size_t num_points); + +class SineGenerator { + public: + SineGenerator(float frequency, int rate) + : frequency_(frequency), rate_(rate) {} + float operator()() { + x_radians_ += frequency_ / rate_ * 2 * kPi; + if (x_radians_ > 2 * kPi) { + x_radians_ -= 2 * kPi; + } + return 1000.f * sinf(x_radians_); + } + + private: + float frequency_; + int rate_; + float x_radians_ = 0.f; +}; + +class PulseGenerator { + public: + PulseGenerator(float frequency, int rate) + : samples_period_( + static_cast(static_cast(rate) / frequency)) { + RTC_DCHECK_GT(rate, frequency); + } + float operator()() { + sample_counter_++; + if (sample_counter_ >= samples_period_) { + sample_counter_ -= samples_period_; + } + return static_cast( + sample_counter_ == 0 ? std::numeric_limits::max() : 10.f); + } + + private: + int samples_period_; + int sample_counter_ = 0; +}; + +} // namespace test +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_AGC2_TESTING_COMMON_H_ diff --git a/webrtc/modules/audio_processing/agc2/biquad_filter.cc b/webrtc/modules/audio_processing/agc2/biquad_filter.cc new file mode 100644 index 0000000..da8557c --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/biquad_filter.cc @@ -0,0 +1,36 @@ +/* + * 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 "modules/audio_processing/agc2/biquad_filter.h" + +#include + +namespace webrtc { + +// Transposed direct form I implementation of a bi-quad filter applied to an +// input signal |x| to produce an output signal |y|. +void BiQuadFilter::Process(rtc::ArrayView x, + rtc::ArrayView y) { + for (size_t k = 0; k < x.size(); ++k) { + // Use temporary variable for x[k] to allow in-place function call + // (that x and y refer to the same array). + const float tmp = x[k]; + y[k] = coefficients_.b[0] * tmp + coefficients_.b[1] * biquad_state_.b[0] + + coefficients_.b[2] * biquad_state_.b[1] - + coefficients_.a[0] * biquad_state_.a[0] - + coefficients_.a[1] * biquad_state_.a[1]; + biquad_state_.b[1] = biquad_state_.b[0]; + biquad_state_.b[0] = tmp; + biquad_state_.a[1] = biquad_state_.a[0]; + biquad_state_.a[0] = y[k]; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/biquad_filter.h b/webrtc/modules/audio_processing/agc2/biquad_filter.h new file mode 100644 index 0000000..7bf3301 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/biquad_filter.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_ + +#include + +#include "api/array_view.h" +#include "rtc_base/arraysize.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +class BiQuadFilter { + public: + // Normalized filter coefficients. + // b_0 + b_1 • z^(-1) + b_2 • z^(-2) + // H(z) = --------------------------------- + // 1 + a_1 • z^(-1) + a_2 • z^(-2) + struct BiQuadCoefficients { + float b[3]; + float a[2]; + }; + + BiQuadFilter() = default; + + void Initialize(const BiQuadCoefficients& coefficients) { + coefficients_ = coefficients; + } + + void Reset() { biquad_state_.Reset(); } + + // Produces a filtered output y of the input x. Both x and y need to + // have the same length. In-place modification is allowed. + void Process(rtc::ArrayView x, rtc::ArrayView y); + + private: + struct BiQuadState { + BiQuadState() { Reset(); } + + void Reset() { + std::fill(b, b + arraysize(b), 0.f); + std::fill(a, a + arraysize(a), 0.f); + } + + float b[2]; + float a[2]; + }; + + BiQuadState biquad_state_; + BiQuadCoefficients coefficients_; + + RTC_DISALLOW_COPY_AND_ASSIGN(BiQuadFilter); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_ diff --git a/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc b/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc new file mode 100644 index 0000000..bc92613 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/compute_interpolated_gain_curve.h" + +#include +#include +#include +#include +#include +#include + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/agc2/agc2_testing_common.h" +#include "modules/audio_processing/agc2/limiter_db_gain_curve.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +std::pair ComputeLinearApproximationParams( + const LimiterDbGainCurve* limiter, + const double x) { + const double m = limiter->GetGainFirstDerivativeLinear(x); + const double q = limiter->GetGainLinear(x) - m * x; + return {m, q}; +} + +double ComputeAreaUnderPiecewiseLinearApproximation( + const LimiterDbGainCurve* limiter, + const double x0, + const double x1) { + RTC_CHECK_LT(x0, x1); + + // Linear approximation in x0 and x1. + double m0, q0, m1, q1; + std::tie(m0, q0) = ComputeLinearApproximationParams(limiter, x0); + std::tie(m1, q1) = ComputeLinearApproximationParams(limiter, x1); + + // Intersection point between two adjacent linear pieces. + RTC_CHECK_NE(m1, m0); + const double x_split = (q0 - q1) / (m1 - m0); + RTC_CHECK_LT(x0, x_split); + RTC_CHECK_LT(x_split, x1); + + auto area_under_linear_piece = [](double x_l, double x_r, double m, + double q) { + return x_r * (m * x_r / 2.0 + q) - x_l * (m * x_l / 2.0 + q); + }; + return area_under_linear_piece(x0, x_split, m0, q0) + + area_under_linear_piece(x_split, x1, m1, q1); +} + +// Computes the approximation error in the limiter region for a given interval. +// The error is computed as the difference between the areas beneath the limiter +// curve to approximate and its linear under-approximation. +double LimiterUnderApproximationNegativeError(const LimiterDbGainCurve* limiter, + const double x0, + const double x1) { + const double area_limiter = limiter->GetGainIntegralLinear(x0, x1); + const double area_interpolated_curve = + ComputeAreaUnderPiecewiseLinearApproximation(limiter, x0, x1); + RTC_CHECK_GE(area_limiter, area_interpolated_curve); + return area_limiter - area_interpolated_curve; +} + +// Automatically finds where to sample the beyond-knee region of a limiter using +// a greedy optimization algorithm that iteratively decreases the approximation +// error. +// The solution is sub-optimal because the algorithm is greedy and the points +// are assigned by halving intervals (starting with the whole beyond-knee region +// as a single interval). However, even if sub-optimal, this algorithm works +// well in practice and it is efficiently implemented using priority queues. +std::vector SampleLimiterRegion(const LimiterDbGainCurve* limiter) { + static_assert(kInterpolatedGainCurveBeyondKneePoints > 2, ""); + + struct Interval { + Interval() = default; // Ctor required by std::priority_queue. + Interval(double l, double r, double e) : x0(l), x1(r), error(e) { + RTC_CHECK(x0 < x1); + } + bool operator<(const Interval& other) const { return error < other.error; } + + double x0; + double x1; + double error; + }; + + std::priority_queue> q; + q.emplace(limiter->limiter_start_linear(), limiter->max_input_level_linear(), + LimiterUnderApproximationNegativeError( + limiter, limiter->limiter_start_linear(), + limiter->max_input_level_linear())); + + // Iteratively find points by halving the interval with greatest error. + while (q.size() < kInterpolatedGainCurveBeyondKneePoints) { + // Get the interval with highest error. + const auto interval = q.top(); + q.pop(); + + // Split |interval| and enqueue. + double x_split = (interval.x0 + interval.x1) / 2.0; + q.emplace(interval.x0, x_split, + LimiterUnderApproximationNegativeError(limiter, interval.x0, + x_split)); // Left. + q.emplace(x_split, interval.x1, + LimiterUnderApproximationNegativeError(limiter, x_split, + interval.x1)); // Right. + } + + // Copy x1 values and sort them. + RTC_CHECK_EQ(q.size(), kInterpolatedGainCurveBeyondKneePoints); + std::vector samples(kInterpolatedGainCurveBeyondKneePoints); + for (size_t i = 0; i < kInterpolatedGainCurveBeyondKneePoints; ++i) { + const auto interval = q.top(); + q.pop(); + samples[i] = interval.x1; + } + RTC_CHECK(q.empty()); + std::sort(samples.begin(), samples.end()); + + return samples; +} + +// Compute the parameters to over-approximate the knee region via linear +// interpolation. Over-approximating is saturation-safe since the knee region is +// convex. +void PrecomputeKneeApproxParams(const LimiterDbGainCurve* limiter, + test::InterpolatedParameters* parameters) { + static_assert(kInterpolatedGainCurveKneePoints > 2, ""); + // Get |kInterpolatedGainCurveKneePoints| - 1 equally spaced points. + const std::vector points = test::LinSpace( + limiter->knee_start_linear(), limiter->limiter_start_linear(), + kInterpolatedGainCurveKneePoints - 1); + + // Set the first two points. The second is computed to help with the beginning + // of the knee region, which has high curvature. + parameters->computed_approximation_params_x[0] = points[0]; + parameters->computed_approximation_params_x[1] = + (points[0] + points[1]) / 2.0; + // Copy the remaining points. + std::copy(std::begin(points) + 1, std::end(points), + std::begin(parameters->computed_approximation_params_x) + 2); + + // Compute (m, q) pairs for each linear piece y = mx + q. + for (size_t i = 0; i < kInterpolatedGainCurveKneePoints - 1; ++i) { + const double x0 = parameters->computed_approximation_params_x[i]; + const double x1 = parameters->computed_approximation_params_x[i + 1]; + const double y0 = limiter->GetGainLinear(x0); + const double y1 = limiter->GetGainLinear(x1); + RTC_CHECK_NE(x1, x0); + parameters->computed_approximation_params_m[i] = (y1 - y0) / (x1 - x0); + parameters->computed_approximation_params_q[i] = + y0 - parameters->computed_approximation_params_m[i] * x0; + } +} + +// Compute the parameters to under-approximate the beyond-knee region via linear +// interpolation and greedy sampling. Under-approximating is saturation-safe +// since the beyond-knee region is concave. +void PrecomputeBeyondKneeApproxParams( + const LimiterDbGainCurve* limiter, + test::InterpolatedParameters* parameters) { + // Find points on which the linear pieces are tangent to the gain curve. + const auto samples = SampleLimiterRegion(limiter); + + // Parametrize each linear piece. + double m, q; + std::tie(m, q) = ComputeLinearApproximationParams( + limiter, + parameters + ->computed_approximation_params_x[kInterpolatedGainCurveKneePoints - + 1]); + parameters + ->computed_approximation_params_m[kInterpolatedGainCurveKneePoints - 1] = + m; + parameters + ->computed_approximation_params_q[kInterpolatedGainCurveKneePoints - 1] = + q; + for (size_t i = 0; i < samples.size(); ++i) { + std::tie(m, q) = ComputeLinearApproximationParams(limiter, samples[i]); + parameters + ->computed_approximation_params_m[i + + kInterpolatedGainCurveKneePoints] = m; + parameters + ->computed_approximation_params_q[i + + kInterpolatedGainCurveKneePoints] = q; + } + + // Find the point of intersection between adjacent linear pieces. They will be + // used as boundaries between adjacent linear pieces. + for (size_t i = kInterpolatedGainCurveKneePoints; + i < kInterpolatedGainCurveKneePoints + + kInterpolatedGainCurveBeyondKneePoints; + ++i) { + RTC_CHECK_NE(parameters->computed_approximation_params_m[i], + parameters->computed_approximation_params_m[i - 1]); + parameters->computed_approximation_params_x[i] = + ( // Formula: (q0 - q1) / (m1 - m0). + parameters->computed_approximation_params_q[i - 1] - + parameters->computed_approximation_params_q[i]) / + (parameters->computed_approximation_params_m[i] - + parameters->computed_approximation_params_m[i - 1]); + } +} + +} // namespace + +namespace test { + +InterpolatedParameters ComputeInterpolatedGainCurveApproximationParams() { + InterpolatedParameters parameters; + LimiterDbGainCurve limiter; + parameters.computed_approximation_params_x.fill(0.0f); + parameters.computed_approximation_params_m.fill(0.0f); + parameters.computed_approximation_params_q.fill(0.0f); + PrecomputeKneeApproxParams(&limiter, ¶meters); + PrecomputeBeyondKneeApproxParams(&limiter, ¶meters); + return parameters; +} +} // namespace test +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.h b/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.h new file mode 100644 index 0000000..5f52441 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/compute_interpolated_gain_curve.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_COMPUTE_INTERPOLATED_GAIN_CURVE_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_COMPUTE_INTERPOLATED_GAIN_CURVE_H_ + +#include + +#include "modules/audio_processing/agc2/agc2_common.h" + +namespace webrtc { + +namespace test { + +// Parameters for interpolated gain curve using under-approximation to +// avoid saturation. +// +// The saturation gain is defined in order to let hard-clipping occur for +// those samples having a level that falls in the saturation region. It is an +// upper bound of the actual gain to apply - i.e., that returned by the +// limiter. + +// Knee and beyond-knee regions approximation parameters. +// The gain curve is approximated as a piece-wise linear function. +// |approx_params_x_| are the boundaries between adjacent linear pieces, +// |approx_params_m_| and |approx_params_q_| are the slope and the y-intercept +// values of each piece. +struct InterpolatedParameters { + std::array + computed_approximation_params_x; + std::array + computed_approximation_params_m; + std::array + computed_approximation_params_q; +}; + +InterpolatedParameters ComputeInterpolatedGainCurveApproximationParams(); +} // namespace test +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_COMPUTE_INTERPOLATED_GAIN_CURVE_H_ diff --git a/webrtc/modules/audio_processing/agc2/down_sampler.cc b/webrtc/modules/audio_processing/agc2/down_sampler.cc new file mode 100644 index 0000000..654ed4b --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/down_sampler.cc @@ -0,0 +1,99 @@ +/* + * 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 "modules/audio_processing/agc2/down_sampler.h" + +#include + +#include + +#include "modules/audio_processing/agc2/biquad_filter.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr int kChunkSizeMs = 10; +constexpr int kSampleRate8kHz = 8000; +constexpr int kSampleRate16kHz = 16000; +constexpr int kSampleRate32kHz = 32000; +constexpr int kSampleRate48kHz = 48000; + +// Bandlimiter coefficients computed based on that only +// the first 40 bins of the spectrum for the downsampled +// signal are used. +// [B,A] = butter(2,(41/64*4000)/8000) +const BiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients_16kHz = { + {0.1455f, 0.2911f, 0.1455f}, + {-0.6698f, 0.2520f}}; + +// [B,A] = butter(2,(41/64*4000)/16000) +const BiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients_32kHz = { + {0.0462f, 0.0924f, 0.0462f}, + {-1.3066f, 0.4915f}}; + +// [B,A] = butter(2,(41/64*4000)/24000) +const BiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients_48kHz = { + {0.0226f, 0.0452f, 0.0226f}, + {-1.5320f, 0.6224f}}; + +} // namespace + +DownSampler::DownSampler(ApmDataDumper* data_dumper) + : data_dumper_(data_dumper) { + Initialize(48000); +} +void DownSampler::Initialize(int sample_rate_hz) { + RTC_DCHECK( + sample_rate_hz == kSampleRate8kHz || sample_rate_hz == kSampleRate16kHz || + sample_rate_hz == kSampleRate32kHz || sample_rate_hz == kSampleRate48kHz); + + sample_rate_hz_ = sample_rate_hz; + down_sampling_factor_ = rtc::CheckedDivExact(sample_rate_hz_, 8000); + + /// Note that the down sampling filter is not used if the sample rate is 8 + /// kHz. + if (sample_rate_hz_ == kSampleRate16kHz) { + low_pass_filter_.Initialize(kLowPassFilterCoefficients_16kHz); + } else if (sample_rate_hz_ == kSampleRate32kHz) { + low_pass_filter_.Initialize(kLowPassFilterCoefficients_32kHz); + } else if (sample_rate_hz_ == kSampleRate48kHz) { + low_pass_filter_.Initialize(kLowPassFilterCoefficients_48kHz); + } +} + +void DownSampler::DownSample(rtc::ArrayView in, + rtc::ArrayView out) { + data_dumper_->DumpWav("lc_down_sampler_input", in, sample_rate_hz_, 1); + RTC_DCHECK_EQ(sample_rate_hz_ * kChunkSizeMs / 1000, in.size()); + RTC_DCHECK_EQ(kSampleRate8kHz * kChunkSizeMs / 1000, out.size()); + const size_t kMaxNumFrames = kSampleRate48kHz * kChunkSizeMs / 1000; + float x[kMaxNumFrames]; + + // Band-limit the signal to 4 kHz. + if (sample_rate_hz_ != kSampleRate8kHz) { + low_pass_filter_.Process(in, rtc::ArrayView(x, in.size())); + + // Downsample the signal. + size_t k = 0; + for (size_t j = 0; j < out.size(); ++j) { + RTC_DCHECK_GT(kMaxNumFrames, k); + out[j] = x[k]; + k += down_sampling_factor_; + } + } else { + std::copy(in.data(), in.data() + in.size(), out.data()); + } + + data_dumper_->DumpWav("lc_down_sampler_output", out, kSampleRate8kHz, 1); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/down_sampler.h b/webrtc/modules/audio_processing/agc2/down_sampler.h new file mode 100644 index 0000000..be7cbb3 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/down_sampler.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_DOWN_SAMPLER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_DOWN_SAMPLER_H_ + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/biquad_filter.h" + +namespace webrtc { + +class ApmDataDumper; + +class DownSampler { + public: + explicit DownSampler(ApmDataDumper* data_dumper); + + DownSampler() = delete; + DownSampler(const DownSampler&) = delete; + DownSampler& operator=(const DownSampler&) = delete; + + void Initialize(int sample_rate_hz); + + void DownSample(rtc::ArrayView in, rtc::ArrayView out); + + private: + ApmDataDumper* data_dumper_; + int sample_rate_hz_; + int down_sampling_factor_; + BiQuadFilter low_pass_filter_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_DOWN_SAMPLER_H_ diff --git a/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.cc b/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.cc new file mode 100644 index 0000000..971f4f6 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.cc @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/fixed_digital_level_estimator.h" + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr float kInitialFilterStateLevel = 0.f; + +} // namespace + +FixedDigitalLevelEstimator::FixedDigitalLevelEstimator( + size_t sample_rate_hz, + ApmDataDumper* apm_data_dumper) + : apm_data_dumper_(apm_data_dumper), + filter_state_level_(kInitialFilterStateLevel) { + SetSampleRate(sample_rate_hz); + CheckParameterCombination(); + RTC_DCHECK(apm_data_dumper_); + apm_data_dumper_->DumpRaw("agc2_level_estimator_samplerate", sample_rate_hz); +} + +void FixedDigitalLevelEstimator::CheckParameterCombination() { + RTC_DCHECK_GT(samples_in_frame_, 0); + RTC_DCHECK_LE(kSubFramesInFrame, samples_in_frame_); + RTC_DCHECK_EQ(samples_in_frame_ % kSubFramesInFrame, 0); + RTC_DCHECK_GT(samples_in_sub_frame_, 1); +} + +std::array FixedDigitalLevelEstimator::ComputeLevel( + const AudioFrameView& float_frame) { + RTC_DCHECK_GT(float_frame.num_channels(), 0); + RTC_DCHECK_EQ(float_frame.samples_per_channel(), samples_in_frame_); + + // Compute max envelope without smoothing. + std::array envelope{}; + for (size_t channel_idx = 0; channel_idx < float_frame.num_channels(); + ++channel_idx) { + const auto channel = float_frame.channel(channel_idx); + for (size_t sub_frame = 0; sub_frame < kSubFramesInFrame; ++sub_frame) { + for (size_t sample_in_sub_frame = 0; + sample_in_sub_frame < samples_in_sub_frame_; ++sample_in_sub_frame) { + envelope[sub_frame] = + std::max(envelope[sub_frame], + std::abs(channel[sub_frame * samples_in_sub_frame_ + + sample_in_sub_frame])); + } + } + } + + // Make sure envelope increases happen one step earlier so that the + // corresponding *gain decrease* doesn't miss a sudden signal + // increase due to interpolation. + for (size_t sub_frame = 0; sub_frame < kSubFramesInFrame - 1; ++sub_frame) { + if (envelope[sub_frame] < envelope[sub_frame + 1]) { + envelope[sub_frame] = envelope[sub_frame + 1]; + } + } + + // Add attack / decay smoothing. + for (size_t sub_frame = 0; sub_frame < kSubFramesInFrame; ++sub_frame) { + const float envelope_value = envelope[sub_frame]; + if (envelope_value > filter_state_level_) { + envelope[sub_frame] = envelope_value * (1 - kAttackFilterConstant) + + filter_state_level_ * kAttackFilterConstant; + } else { + envelope[sub_frame] = envelope_value * (1 - kDecayFilterConstant) + + filter_state_level_ * kDecayFilterConstant; + } + filter_state_level_ = envelope[sub_frame]; + + // Dump data for debug. + RTC_DCHECK(apm_data_dumper_); + const auto channel = float_frame.channel(0); + apm_data_dumper_->DumpRaw("agc2_level_estimator_samples", + samples_in_sub_frame_, + &channel[sub_frame * samples_in_sub_frame_]); + apm_data_dumper_->DumpRaw("agc2_level_estimator_level", + envelope[sub_frame]); + } + + return envelope; +} + +void FixedDigitalLevelEstimator::SetSampleRate(size_t sample_rate_hz) { + samples_in_frame_ = rtc::CheckedDivExact(sample_rate_hz * kFrameDurationMs, + static_cast(1000)); + samples_in_sub_frame_ = + rtc::CheckedDivExact(samples_in_frame_, kSubFramesInFrame); + CheckParameterCombination(); +} + +void FixedDigitalLevelEstimator::Reset() { + filter_state_level_ = kInitialFilterStateLevel; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.h b/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.h new file mode 100644 index 0000000..aa84a2e --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_FIXED_DIGITAL_LEVEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_FIXED_DIGITAL_LEVEL_ESTIMATOR_H_ + +#include +#include + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/include/audio_frame_view.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +class ApmDataDumper; +// Produces a smooth signal level estimate from an input audio +// stream. The estimate smoothing is done through exponential +// filtering. +class FixedDigitalLevelEstimator { + public: + // Sample rates are allowed if the number of samples in a frame + // (sample_rate_hz * kFrameDurationMs / 1000) is divisible by + // kSubFramesInSample. For kFrameDurationMs=10 and + // kSubFramesInSample=20, this means that sample_rate_hz has to be + // divisible by 2000. + FixedDigitalLevelEstimator(size_t sample_rate_hz, + ApmDataDumper* apm_data_dumper); + + // The input is assumed to be in FloatS16 format. Scaled input will + // produce similarly scaled output. A frame of with kFrameDurationMs + // ms of audio produces a level estimates in the same scale. The + // level estimate contains kSubFramesInFrame values. + std::array ComputeLevel( + const AudioFrameView& float_frame); + + // Rate may be changed at any time (but not concurrently) from the + // value passed to the constructor. The class is not thread safe. + void SetSampleRate(size_t sample_rate_hz); + + // Resets the level estimator internal state. + void Reset(); + + float LastAudioLevel() const { return filter_state_level_; } + + private: + void CheckParameterCombination(); + + ApmDataDumper* const apm_data_dumper_ = nullptr; + float filter_state_level_; + size_t samples_in_frame_; + size_t samples_in_sub_frame_; + + RTC_DISALLOW_COPY_AND_ASSIGN(FixedDigitalLevelEstimator); +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_FIXED_DIGITAL_LEVEL_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/agc2/fixed_gain_controller.cc b/webrtc/modules/audio_processing/agc2/fixed_gain_controller.cc new file mode 100644 index 0000000..ef908dc --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/fixed_gain_controller.cc @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/fixed_gain_controller.h" + +#include "api/array_view.h" +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +// Returns true when the gain factor is so close to 1 that it would +// not affect int16 samples. +bool CloseToOne(float gain_factor) { + return 1.f - 1.f / kMaxFloatS16Value <= gain_factor && + gain_factor <= 1.f + 1.f / kMaxFloatS16Value; +} +} // namespace + +FixedGainController::FixedGainController(ApmDataDumper* apm_data_dumper) + : FixedGainController(apm_data_dumper, "Agc2") {} + +FixedGainController::FixedGainController(ApmDataDumper* apm_data_dumper, + std::string histogram_name_prefix) + : apm_data_dumper_(apm_data_dumper), + limiter_(48000, apm_data_dumper_, histogram_name_prefix) { + // Do update histograms.xml when adding name prefixes. + RTC_DCHECK(histogram_name_prefix == "" || histogram_name_prefix == "Test" || + histogram_name_prefix == "AudioMixer" || + histogram_name_prefix == "Agc2"); +} + +void FixedGainController::SetGain(float gain_to_apply_db) { + // Changes in gain_to_apply_ cause discontinuities. We assume + // gain_to_apply_ is set in the beginning of the call. If it is + // frequently changed, we should add interpolation between the + // values. + // The gain + RTC_DCHECK_LE(-50.f, gain_to_apply_db); + RTC_DCHECK_LE(gain_to_apply_db, 50.f); + const float previous_applied_gained = gain_to_apply_; + gain_to_apply_ = DbToRatio(gain_to_apply_db); + RTC_DCHECK_LT(0.f, gain_to_apply_); + RTC_DLOG(LS_INFO) << "Gain to apply: " << gain_to_apply_db << " db."; + // Reset the gain curve applier to quickly react on abrupt level changes + // caused by large changes of the applied gain. + if (previous_applied_gained != gain_to_apply_) { + limiter_.Reset(); + } +} + +void FixedGainController::SetSampleRate(size_t sample_rate_hz) { + limiter_.SetSampleRate(sample_rate_hz); +} + +void FixedGainController::Process(AudioFrameView signal) { + // Apply fixed digital gain. One of the + // planned usages of the FGC is to only use the limiter. In that + // case, the gain would be 1.0. Not doing the multiplications speeds + // it up considerably. Hence the check. + if (!CloseToOne(gain_to_apply_)) { + for (size_t k = 0; k < signal.num_channels(); ++k) { + rtc::ArrayView channel_view = signal.channel(k); + for (auto& sample : channel_view) { + sample *= gain_to_apply_; + } + } + } + + // Use the limiter. + limiter_.Process(signal); + + // Dump data for debug. + const auto channel_view = signal.channel(0); + apm_data_dumper_->DumpRaw("agc2_fixed_digital_gain_curve_applier", + channel_view.size(), channel_view.data()); + // Hard-clipping. + for (size_t k = 0; k < signal.num_channels(); ++k) { + rtc::ArrayView channel_view = signal.channel(k); + for (auto& sample : channel_view) { + sample = rtc::SafeClamp(sample, kMinFloatS16Value, kMaxFloatS16Value); + } + } +} + +float FixedGainController::LastAudioLevel() const { + return limiter_.LastAudioLevel(); +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/gain_applier.cc b/webrtc/modules/audio_processing/agc2/gain_applier.cc new file mode 100644 index 0000000..8c43717 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/gain_applier.cc @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/gain_applier.h" + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +// Returns true when the gain factor is so close to 1 that it would +// not affect int16 samples. +bool GainCloseToOne(float gain_factor) { + return 1.f - 1.f / kMaxFloatS16Value <= gain_factor && + gain_factor <= 1.f + 1.f / kMaxFloatS16Value; +} + +void ClipSignal(AudioFrameView signal) { + for (size_t k = 0; k < signal.num_channels(); ++k) { + rtc::ArrayView channel_view = signal.channel(k); + for (auto& sample : channel_view) { + sample = rtc::SafeClamp(sample, kMinFloatS16Value, kMaxFloatS16Value); + } + } +} + +void ApplyGainWithRamping(float last_gain_linear, + float gain_at_end_of_frame_linear, + float inverse_samples_per_channel, + AudioFrameView float_frame) { + // Do not modify the signal. + if (last_gain_linear == gain_at_end_of_frame_linear && + GainCloseToOne(gain_at_end_of_frame_linear)) { + return; + } + + // Gain is constant and different from 1. + if (last_gain_linear == gain_at_end_of_frame_linear) { + for (size_t k = 0; k < float_frame.num_channels(); ++k) { + rtc::ArrayView channel_view = float_frame.channel(k); + for (auto& sample : channel_view) { + sample *= gain_at_end_of_frame_linear; + } + } + return; + } + + // The gain changes. We have to change slowly to avoid discontinuities. + const float increment = (gain_at_end_of_frame_linear - last_gain_linear) * + inverse_samples_per_channel; + float gain = last_gain_linear; + for (size_t i = 0; i < float_frame.samples_per_channel(); ++i) { + for (size_t ch = 0; ch < float_frame.num_channels(); ++ch) { + float_frame.channel(ch)[i] *= gain; + } + gain += increment; + } +} + +} // namespace + +GainApplier::GainApplier(bool hard_clip_samples, float initial_gain_factor) + : hard_clip_samples_(hard_clip_samples), + last_gain_factor_(initial_gain_factor), + current_gain_factor_(initial_gain_factor) {} + +void GainApplier::ApplyGain(AudioFrameView signal) { + if (static_cast(signal.samples_per_channel()) != samples_per_channel_) { + Initialize(signal.samples_per_channel()); + } + + ApplyGainWithRamping(last_gain_factor_, current_gain_factor_, + inverse_samples_per_channel_, signal); + + last_gain_factor_ = current_gain_factor_; + + if (hard_clip_samples_) { + ClipSignal(signal); + } +} + +void GainApplier::SetGainFactor(float gain_factor) { + RTC_DCHECK_GT(gain_factor, 0.f); + current_gain_factor_ = gain_factor; +} + +void GainApplier::Initialize(size_t samples_per_channel) { + RTC_DCHECK_GT(samples_per_channel, 0); + samples_per_channel_ = static_cast(samples_per_channel); + inverse_samples_per_channel_ = 1.f / samples_per_channel_; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/gain_applier.h b/webrtc/modules/audio_processing/agc2/gain_applier.h new file mode 100644 index 0000000..d9aa19d --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/gain_applier.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_GAIN_APPLIER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_GAIN_APPLIER_H_ + +#include + +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { +class GainApplier { + public: + GainApplier(bool hard_clip_samples, float initial_gain_factor); + + void ApplyGain(AudioFrameView signal); + void SetGainFactor(float gain_factor); + float GetGainFactor() const { return current_gain_factor_; } + + private: + void Initialize(size_t samples_per_channel); + + // Whether to clip samples after gain is applied. If 'true', result + // will fit in FloatS16 range. + const bool hard_clip_samples_; + float last_gain_factor_; + + // If this value is not equal to 'last_gain_factor', gain will be + // ramped from 'last_gain_factor_' to this value during the next + // 'ApplyGain'. + float current_gain_factor_; + int samples_per_channel_ = -1; + float inverse_samples_per_channel_ = -1.f; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_GAIN_APPLIER_H_ diff --git a/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.cc b/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.cc new file mode 100644 index 0000000..502e702 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.cc @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/interpolated_gain_curve.h" + +#include +#include + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +constexpr std::array + InterpolatedGainCurve::approximation_params_x_; + +constexpr std::array + InterpolatedGainCurve::approximation_params_m_; + +constexpr std::array + InterpolatedGainCurve::approximation_params_q_; + +InterpolatedGainCurve::InterpolatedGainCurve(ApmDataDumper* apm_data_dumper, + std::string histogram_name_prefix) + : region_logger_("WebRTC.Audio." + histogram_name_prefix + + ".FixedDigitalGainCurveRegion.Identity", + "WebRTC.Audio." + histogram_name_prefix + + ".FixedDigitalGainCurveRegion.Knee", + "WebRTC.Audio." + histogram_name_prefix + + ".FixedDigitalGainCurveRegion.Limiter", + "WebRTC.Audio." + histogram_name_prefix + + ".FixedDigitalGainCurveRegion.Saturation"), + apm_data_dumper_(apm_data_dumper) {} + +InterpolatedGainCurve::~InterpolatedGainCurve() { + if (stats_.available) { + RTC_DCHECK(apm_data_dumper_); + apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_identity", + stats_.look_ups_identity_region); + apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_knee", + stats_.look_ups_knee_region); + apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_limiter", + stats_.look_ups_limiter_region); + apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_saturation", + stats_.look_ups_saturation_region); + region_logger_.LogRegionStats(stats_); + } +} + +InterpolatedGainCurve::RegionLogger::RegionLogger( + std::string identity_histogram_name, + std::string knee_histogram_name, + std::string limiter_histogram_name, + std::string saturation_histogram_name) + : identity_histogram( + metrics::HistogramFactoryGetCounts(identity_histogram_name, + 1, + 10000, + 50)), + knee_histogram(metrics::HistogramFactoryGetCounts(knee_histogram_name, + 1, + 10000, + 50)), + limiter_histogram( + metrics::HistogramFactoryGetCounts(limiter_histogram_name, + 1, + 10000, + 50)), + saturation_histogram( + metrics::HistogramFactoryGetCounts(saturation_histogram_name, + 1, + 10000, + 50)) {} + +InterpolatedGainCurve::RegionLogger::~RegionLogger() = default; + +void InterpolatedGainCurve::RegionLogger::LogRegionStats( + const InterpolatedGainCurve::Stats& stats) const { + using Region = InterpolatedGainCurve::GainCurveRegion; + const int duration_s = + stats.region_duration_frames / (1000 / kFrameDurationMs); + + switch (stats.region) { + case Region::kIdentity: { + if (identity_histogram) { + metrics::HistogramAdd(identity_histogram, duration_s); + } + break; + } + case Region::kKnee: { + if (knee_histogram) { + metrics::HistogramAdd(knee_histogram, duration_s); + } + break; + } + case Region::kLimiter: { + if (limiter_histogram) { + metrics::HistogramAdd(limiter_histogram, duration_s); + } + break; + } + case Region::kSaturation: { + if (saturation_histogram) { + metrics::HistogramAdd(saturation_histogram, duration_s); + } + break; + } + default: { + RTC_NOTREACHED(); + } + } +} + +void InterpolatedGainCurve::UpdateStats(float input_level) const { + stats_.available = true; + + GainCurveRegion region; + + if (input_level < approximation_params_x_[0]) { + stats_.look_ups_identity_region++; + region = GainCurveRegion::kIdentity; + } else if (input_level < + approximation_params_x_[kInterpolatedGainCurveKneePoints - 1]) { + stats_.look_ups_knee_region++; + region = GainCurveRegion::kKnee; + } else if (input_level < kMaxInputLevelLinear) { + stats_.look_ups_limiter_region++; + region = GainCurveRegion::kLimiter; + } else { + stats_.look_ups_saturation_region++; + region = GainCurveRegion::kSaturation; + } + + if (region == stats_.region) { + ++stats_.region_duration_frames; + } else { + region_logger_.LogRegionStats(stats_); + + stats_.region_duration_frames = 0; + stats_.region = region; + } +} + +// Looks up a gain to apply given a non-negative input level. +// The cost of this operation depends on the region in which |input_level| +// falls. +// For the identity and the saturation regions the cost is O(1). +// For the other regions, namely knee and limiter, the cost is +// O(2 + log2(|LightkInterpolatedGainCurveTotalPoints|), plus O(1) for the +// linear interpolation (one product and one sum). +float InterpolatedGainCurve::LookUpGainToApply(float input_level) const { + UpdateStats(input_level); + + if (input_level <= approximation_params_x_[0]) { + // Identity region. + return 1.0f; + } + + if (input_level >= kMaxInputLevelLinear) { + // Saturating lower bound. The saturing samples exactly hit the clipping + // level. This method achieves has the lowest harmonic distorsion, but it + // may reduce the amplitude of the non-saturating samples too much. + return 32768.f / input_level; + } + + // Knee and limiter regions; find the linear piece index. Spelling + // out the complete type was the only way to silence both the clang + // plugin and the windows compilers. + std::array::const_iterator it = + std::lower_bound(approximation_params_x_.begin(), + approximation_params_x_.end(), input_level); + const size_t index = std::distance(approximation_params_x_.begin(), it) - 1; + RTC_DCHECK_LE(0, index); + RTC_DCHECK_LT(index, approximation_params_m_.size()); + RTC_DCHECK_LE(approximation_params_x_[index], input_level); + if (index < approximation_params_m_.size() - 1) { + RTC_DCHECK_LE(input_level, approximation_params_x_[index + 1]); + } + + // Piece-wise linear interploation. + const float gain = approximation_params_m_[index] * input_level + + approximation_params_q_[index]; + RTC_DCHECK_LE(0.f, gain); + return gain; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.h b/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.h new file mode 100644 index 0000000..ef1c027 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/interpolated_gain_curve.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_INTERPOLATED_GAIN_CURVE_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_INTERPOLATED_GAIN_CURVE_H_ + +#include +#include + +#include "modules/audio_processing/agc2/agc2_common.h" +#include "rtc_base/constructor_magic.h" +#include "rtc_base/gtest_prod_util.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +class ApmDataDumper; + +constexpr float kInputLevelScalingFactor = 32768.0f; + +// Defined as DbfsToLinear(kLimiterMaxInputLevelDbFs) +constexpr float kMaxInputLevelLinear = static_cast(36766.300710566735); + +// Interpolated gain curve using under-approximation to avoid saturation. +// +// The goal of this class is allowing fast look ups to get an accurate +// estimates of the gain to apply given an estimated input level. +class InterpolatedGainCurve { + public: + enum class GainCurveRegion { + kIdentity = 0, + kKnee = 1, + kLimiter = 2, + kSaturation = 3 + }; + + struct Stats { + // Region in which the output level equals the input one. + size_t look_ups_identity_region = 0; + // Smoothing between the identity and the limiter regions. + size_t look_ups_knee_region = 0; + // Limiter region in which the output and input levels are linearly related. + size_t look_ups_limiter_region = 0; + // Region in which saturation may occur since the input level is beyond the + // maximum expected by the limiter. + size_t look_ups_saturation_region = 0; + // True if stats have been populated. + bool available = false; + + // The current region, and for how many frames the level has been + // in that region. + GainCurveRegion region = GainCurveRegion::kIdentity; + int64_t region_duration_frames = 0; + }; + + InterpolatedGainCurve(ApmDataDumper* apm_data_dumper, + std::string histogram_name_prefix); + ~InterpolatedGainCurve(); + + Stats get_stats() const { return stats_; } + + // Given a non-negative input level (linear scale), a scalar factor to apply + // to a sub-frame is returned. + // Levels above kLimiterMaxInputLevelDbFs will be reduced to 0 dBFS + // after applying this gain + float LookUpGainToApply(float input_level) const; + + private: + // For comparing 'approximation_params_*_' with ones computed by + // ComputeInterpolatedGainCurve. + FRIEND_TEST_ALL_PREFIXES(AutomaticGainController2InterpolatedGainCurve, + CheckApproximationParams); + + struct RegionLogger { + metrics::Histogram* identity_histogram; + metrics::Histogram* knee_histogram; + metrics::Histogram* limiter_histogram; + metrics::Histogram* saturation_histogram; + + RegionLogger(std::string identity_histogram_name, + std::string knee_histogram_name, + std::string limiter_histogram_name, + std::string saturation_histogram_name); + + ~RegionLogger(); + + void LogRegionStats(const InterpolatedGainCurve::Stats& stats) const; + } region_logger_; + + void UpdateStats(float input_level) const; + + ApmDataDumper* const apm_data_dumper_; + + static constexpr std::array + approximation_params_x_ = { + {30057.296875, 30148.986328125, 30240.67578125, 30424.052734375, + 30607.4296875, 30790.806640625, 30974.18359375, 31157.560546875, + 31340.939453125, 31524.31640625, 31707.693359375, 31891.0703125, + 32074.447265625, 32257.82421875, 32441.201171875, 32624.580078125, + 32807.95703125, 32991.33203125, 33174.7109375, 33358.08984375, + 33541.46484375, 33724.84375, 33819.53515625, 34009.5390625, + 34200.05859375, 34389.81640625, 34674.48828125, 35054.375, + 35434.86328125, 35814.81640625, 36195.16796875, 36575.03125}}; + static constexpr std::array + approximation_params_m_ = { + {-3.515235675877192989e-07, -1.050251626111275982e-06, + -2.085213736791047268e-06, -3.443004743530764244e-06, + -4.773849468620028347e-06, -6.077375928725814447e-06, + -7.353257842623861507e-06, -8.601219633419532329e-06, + -9.821013009059242904e-06, -1.101243378798244521e-05, + -1.217532644659513608e-05, -1.330956911260727793e-05, + -1.441507538402220234e-05, -1.549179251014720649e-05, + -1.653970684856176376e-05, -1.755882840370759368e-05, + -1.854918446042574942e-05, -1.951086778717581183e-05, + -2.044398024736437947e-05, -2.1348627342376858e-05, + -2.222496914328075945e-05, -2.265374678245279938e-05, + -2.242570917587727308e-05, -2.220122041762806475e-05, + -2.19802095671184361e-05, -2.176260204578284174e-05, + -2.133731686626560986e-05, -2.092481918225530535e-05, + -2.052459603874012828e-05, -2.013615448959171772e-05, + -1.975903069251216948e-05, -1.939277899509761482e-05}}; + + static constexpr std::array + approximation_params_q_ = { + {1.010565876960754395, 1.031631827354431152, 1.062929749488830566, + 1.104239225387573242, 1.144973039627075195, 1.185109615325927734, + 1.224629044532775879, 1.263512492179870605, 1.301741957664489746, + 1.339300632476806641, 1.376173257827758789, 1.412345528602600098, + 1.447803974151611328, 1.482536554336547852, 1.516532182693481445, + 1.549780607223510742, 1.582272171974182129, 1.613999366760253906, + 1.644955039024353027, 1.675132393836975098, 1.704526185989379883, + 1.718986630439758301, 1.711274504661560059, 1.703639745712280273, + 1.696081161499023438, 1.688597679138183594, 1.673851132392883301, + 1.659391283988952637, 1.645209431648254395, 1.631297469139099121, + 1.617647409439086914, 1.604251742362976074}}; + + // Stats. + mutable Stats stats_; + + RTC_DISALLOW_COPY_AND_ASSIGN(InterpolatedGainCurve); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_INTERPOLATED_GAIN_CURVE_H_ diff --git a/webrtc/modules/audio_processing/agc2/limiter.cc b/webrtc/modules/audio_processing/agc2/limiter.cc new file mode 100644 index 0000000..1589f07 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/limiter.cc @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/limiter.h" + +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +// This constant affects the way scaling factors are interpolated for the first +// sub-frame of a frame. Only in the case in which the first sub-frame has an +// estimated level which is greater than the that of the previous analyzed +// sub-frame, linear interpolation is replaced with a power function which +// reduces the chances of over-shooting (and hence saturation), however reducing +// the fixed gain effectiveness. +constexpr float kAttackFirstSubframeInterpolationPower = 8.f; + +void InterpolateFirstSubframe(float last_factor, + float current_factor, + rtc::ArrayView subframe) { + const auto n = subframe.size(); + constexpr auto p = kAttackFirstSubframeInterpolationPower; + for (size_t i = 0; i < n; ++i) { + subframe[i] = std::pow(1.f - i / n, p) * (last_factor - current_factor) + + current_factor; + } +} + +void ComputePerSampleSubframeFactors( + const std::array& scaling_factors, + size_t samples_per_channel, + rtc::ArrayView per_sample_scaling_factors) { + const size_t num_subframes = scaling_factors.size() - 1; + const size_t subframe_size = + rtc::CheckedDivExact(samples_per_channel, num_subframes); + + // Handle first sub-frame differently in case of attack. + const bool is_attack = scaling_factors[0] > scaling_factors[1]; + if (is_attack) { + InterpolateFirstSubframe( + scaling_factors[0], scaling_factors[1], + rtc::ArrayView( + per_sample_scaling_factors.subview(0, subframe_size))); + } + + for (size_t i = is_attack ? 1 : 0; i < num_subframes; ++i) { + const size_t subframe_start = i * subframe_size; + const float scaling_start = scaling_factors[i]; + const float scaling_end = scaling_factors[i + 1]; + const float scaling_diff = (scaling_end - scaling_start) / subframe_size; + for (size_t j = 0; j < subframe_size; ++j) { + per_sample_scaling_factors[subframe_start + j] = + scaling_start + scaling_diff * j; + } + } +} + +void ScaleSamples(rtc::ArrayView per_sample_scaling_factors, + AudioFrameView signal) { + const size_t samples_per_channel = signal.samples_per_channel(); + RTC_DCHECK_EQ(samples_per_channel, per_sample_scaling_factors.size()); + for (size_t i = 0; i < signal.num_channels(); ++i) { + auto channel = signal.channel(i); + for (size_t j = 0; j < samples_per_channel; ++j) { + channel[j] = rtc::SafeClamp(channel[j] * per_sample_scaling_factors[j], + kMinFloatS16Value, kMaxFloatS16Value); + } + } +} + +void CheckLimiterSampleRate(size_t sample_rate_hz) { + // Check that per_sample_scaling_factors_ is large enough. + RTC_DCHECK_LE(sample_rate_hz, + kMaximalNumberOfSamplesPerChannel * 1000 / kFrameDurationMs); +} + +} // namespace + +Limiter::Limiter(size_t sample_rate_hz, + ApmDataDumper* apm_data_dumper, + std::string histogram_name) + : interp_gain_curve_(apm_data_dumper, histogram_name), + level_estimator_(sample_rate_hz, apm_data_dumper), + apm_data_dumper_(apm_data_dumper) { + CheckLimiterSampleRate(sample_rate_hz); +} + +Limiter::~Limiter() = default; + +void Limiter::Process(AudioFrameView signal) { + const auto level_estimate = level_estimator_.ComputeLevel(signal); + + RTC_DCHECK_EQ(level_estimate.size() + 1, scaling_factors_.size()); + scaling_factors_[0] = last_scaling_factor_; + std::transform(level_estimate.begin(), level_estimate.end(), + scaling_factors_.begin() + 1, [this](float x) { + return interp_gain_curve_.LookUpGainToApply(x); + }); + + const size_t samples_per_channel = signal.samples_per_channel(); + RTC_DCHECK_LE(samples_per_channel, kMaximalNumberOfSamplesPerChannel); + + auto per_sample_scaling_factors = rtc::ArrayView( + &per_sample_scaling_factors_[0], samples_per_channel); + ComputePerSampleSubframeFactors(scaling_factors_, samples_per_channel, + per_sample_scaling_factors); + ScaleSamples(per_sample_scaling_factors, signal); + + last_scaling_factor_ = scaling_factors_.back(); + + // Dump data for debug. + apm_data_dumper_->DumpRaw("agc2_gain_curve_applier_scaling_factors", + samples_per_channel, + per_sample_scaling_factors_.data()); +} + +InterpolatedGainCurve::Stats Limiter::GetGainCurveStats() const { + return interp_gain_curve_.get_stats(); +} + +void Limiter::SetSampleRate(size_t sample_rate_hz) { + CheckLimiterSampleRate(sample_rate_hz); + level_estimator_.SetSampleRate(sample_rate_hz); +} + +void Limiter::Reset() { + level_estimator_.Reset(); +} + +float Limiter::LastAudioLevel() const { + return level_estimator_.LastAudioLevel(); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/limiter.h b/webrtc/modules/audio_processing/agc2/limiter.h new file mode 100644 index 0000000..599fd0f --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/limiter.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_LIMITER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_LIMITER_H_ + +#include +#include + +#include "modules/audio_processing/agc2/fixed_digital_level_estimator.h" +#include "modules/audio_processing/agc2/interpolated_gain_curve.h" +#include "modules/audio_processing/include/audio_frame_view.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { +class ApmDataDumper; + +class Limiter { + public: + Limiter(size_t sample_rate_hz, + ApmDataDumper* apm_data_dumper, + std::string histogram_name_prefix); + Limiter(const Limiter& limiter) = delete; + Limiter& operator=(const Limiter& limiter) = delete; + ~Limiter(); + + // Applies limiter and hard-clipping to |signal|. + void Process(AudioFrameView signal); + InterpolatedGainCurve::Stats GetGainCurveStats() const; + + // Supported rates must be + // * supported by FixedDigitalLevelEstimator + // * below kMaximalNumberOfSamplesPerChannel*1000/kFrameDurationMs + // so that samples_per_channel fit in the + // per_sample_scaling_factors_ array. + void SetSampleRate(size_t sample_rate_hz); + + // Resets the internal state. + void Reset(); + + float LastAudioLevel() const; + + private: + const InterpolatedGainCurve interp_gain_curve_; + FixedDigitalLevelEstimator level_estimator_; + ApmDataDumper* const apm_data_dumper_ = nullptr; + + // Work array containing the sub-frame scaling factors to be interpolated. + std::array scaling_factors_ = {}; + std::array + per_sample_scaling_factors_ = {}; + float last_scaling_factor_ = 1.f; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_LIMITER_H_ diff --git a/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.cc b/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.cc new file mode 100644 index 0000000..d55ed5d --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/limiter_db_gain_curve.h" + +#include + +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +double ComputeKneeStart(double max_input_level_db, + double knee_smoothness_db, + double compression_ratio) { + RTC_CHECK_LT((compression_ratio - 1.0) * knee_smoothness_db / + (2.0 * compression_ratio), + max_input_level_db); + return -knee_smoothness_db / 2.0 - + max_input_level_db / (compression_ratio - 1.0); +} + +std::array ComputeKneeRegionPolynomial(double knee_start_dbfs, + double knee_smoothness_db, + double compression_ratio) { + const double a = (1.0 - compression_ratio) / + (2.0 * knee_smoothness_db * compression_ratio); + const double b = 1.0 - 2.0 * a * knee_start_dbfs; + const double c = a * knee_start_dbfs * knee_start_dbfs; + return {{a, b, c}}; +} + +double ComputeLimiterD1(double max_input_level_db, double compression_ratio) { + return (std::pow(10.0, -max_input_level_db / (20.0 * compression_ratio)) * + (1.0 - compression_ratio) / compression_ratio) / + kMaxAbsFloatS16Value; +} + +constexpr double ComputeLimiterD2(double compression_ratio) { + return (1.0 - 2.0 * compression_ratio) / compression_ratio; +} + +double ComputeLimiterI2(double max_input_level_db, + double compression_ratio, + double gain_curve_limiter_i1) { + RTC_CHECK_NE(gain_curve_limiter_i1, 0.f); + return std::pow(10.0, -max_input_level_db / (20.0 * compression_ratio)) / + gain_curve_limiter_i1 / + std::pow(kMaxAbsFloatS16Value, gain_curve_limiter_i1 - 1); +} + +} // namespace + +LimiterDbGainCurve::LimiterDbGainCurve() + : max_input_level_linear_(DbfsToFloatS16(max_input_level_db_)), + knee_start_dbfs_(ComputeKneeStart(max_input_level_db_, + knee_smoothness_db_, + compression_ratio_)), + knee_start_linear_(DbfsToFloatS16(knee_start_dbfs_)), + limiter_start_dbfs_(knee_start_dbfs_ + knee_smoothness_db_), + limiter_start_linear_(DbfsToFloatS16(limiter_start_dbfs_)), + knee_region_polynomial_(ComputeKneeRegionPolynomial(knee_start_dbfs_, + knee_smoothness_db_, + compression_ratio_)), + gain_curve_limiter_d1_( + ComputeLimiterD1(max_input_level_db_, compression_ratio_)), + gain_curve_limiter_d2_(ComputeLimiterD2(compression_ratio_)), + gain_curve_limiter_i1_(1.0 / compression_ratio_), + gain_curve_limiter_i2_(ComputeLimiterI2(max_input_level_db_, + compression_ratio_, + gain_curve_limiter_i1_)) { + static_assert(knee_smoothness_db_ > 0.0f, ""); + static_assert(compression_ratio_ > 1.0f, ""); + RTC_CHECK_GE(max_input_level_db_, knee_start_dbfs_ + knee_smoothness_db_); +} + +constexpr double LimiterDbGainCurve::max_input_level_db_; +constexpr double LimiterDbGainCurve::knee_smoothness_db_; +constexpr double LimiterDbGainCurve::compression_ratio_; + +double LimiterDbGainCurve::GetOutputLevelDbfs(double input_level_dbfs) const { + if (input_level_dbfs < knee_start_dbfs_) { + return input_level_dbfs; + } else if (input_level_dbfs < limiter_start_dbfs_) { + return GetKneeRegionOutputLevelDbfs(input_level_dbfs); + } + return GetCompressorRegionOutputLevelDbfs(input_level_dbfs); +} + +double LimiterDbGainCurve::GetGainLinear(double input_level_linear) const { + if (input_level_linear < knee_start_linear_) { + return 1.0; + } + return DbfsToFloatS16( + GetOutputLevelDbfs(FloatS16ToDbfs(input_level_linear))) / + input_level_linear; +} + +// Computes the first derivative of GetGainLinear() in |x|. +double LimiterDbGainCurve::GetGainFirstDerivativeLinear(double x) const { + // Beyond-knee region only. + RTC_CHECK_GE(x, limiter_start_linear_ - 1e-7 * kMaxAbsFloatS16Value); + return gain_curve_limiter_d1_ * + std::pow(x / kMaxAbsFloatS16Value, gain_curve_limiter_d2_); +} + +// Computes the integral of GetGainLinear() in the range [x0, x1]. +double LimiterDbGainCurve::GetGainIntegralLinear(double x0, double x1) const { + RTC_CHECK_LE(x0, x1); // Valid interval. + RTC_CHECK_GE(x0, limiter_start_linear_); // Beyond-knee region only. + auto limiter_integral = [this](const double& x) { + return gain_curve_limiter_i2_ * std::pow(x, gain_curve_limiter_i1_); + }; + return limiter_integral(x1) - limiter_integral(x0); +} + +double LimiterDbGainCurve::GetKneeRegionOutputLevelDbfs( + double input_level_dbfs) const { + return knee_region_polynomial_[0] * input_level_dbfs * input_level_dbfs + + knee_region_polynomial_[1] * input_level_dbfs + + knee_region_polynomial_[2]; +} + +double LimiterDbGainCurve::GetCompressorRegionOutputLevelDbfs( + double input_level_dbfs) const { + return (input_level_dbfs - max_input_level_db_) / compression_ratio_; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.h b/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.h new file mode 100644 index 0000000..9086e26 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/limiter_db_gain_curve.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_LIMITER_DB_GAIN_CURVE_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_LIMITER_DB_GAIN_CURVE_H_ + +#include + +#include "modules/audio_processing/agc2/agc2_testing_common.h" + +namespace webrtc { + +// A class for computing a limiter gain curve (in dB scale) given a set of +// hard-coded parameters (namely, kLimiterDbGainCurveMaxInputLevelDbFs, +// kLimiterDbGainCurveKneeSmoothnessDb, and +// kLimiterDbGainCurveCompressionRatio). The generated curve consists of four +// regions: identity (linear), knee (quadratic polynomial), compression +// (linear), saturation (linear). The aforementioned constants are used to shape +// the different regions. +class LimiterDbGainCurve { + public: + LimiterDbGainCurve(); + + double max_input_level_db() const { return max_input_level_db_; } + double max_input_level_linear() const { return max_input_level_linear_; } + double knee_start_linear() const { return knee_start_linear_; } + double limiter_start_linear() const { return limiter_start_linear_; } + + // These methods can be marked 'constexpr' in C++ 14. + double GetOutputLevelDbfs(double input_level_dbfs) const; + double GetGainLinear(double input_level_linear) const; + double GetGainFirstDerivativeLinear(double x) const; + double GetGainIntegralLinear(double x0, double x1) const; + + private: + double GetKneeRegionOutputLevelDbfs(double input_level_dbfs) const; + double GetCompressorRegionOutputLevelDbfs(double input_level_dbfs) const; + + static constexpr double max_input_level_db_ = test::kLimiterMaxInputLevelDbFs; + static constexpr double knee_smoothness_db_ = test::kLimiterKneeSmoothnessDb; + static constexpr double compression_ratio_ = test::kLimiterCompressionRatio; + + const double max_input_level_linear_; + + // Do not modify signal with level <= knee_start_dbfs_. + const double knee_start_dbfs_; + const double knee_start_linear_; + + // The upper end of the knee region, which is between knee_start_dbfs_ and + // limiter_start_dbfs_. + const double limiter_start_dbfs_; + const double limiter_start_linear_; + + // Coefficients {a, b, c} of the knee region polynomial + // ax^2 + bx + c in the DB scale. + const std::array knee_region_polynomial_; + + // Parameters for the computation of the first derivative of GetGainLinear(). + const double gain_curve_limiter_d1_; + const double gain_curve_limiter_d2_; + + // Parameters for the computation of the integral of GetGainLinear(). + const double gain_curve_limiter_i1_; + const double gain_curve_limiter_i2_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_LIMITER_DB_GAIN_CURVE_H_ diff --git a/webrtc/modules/audio_processing/agc2/noise_level_estimator.cc b/webrtc/modules/audio_processing/agc2/noise_level_estimator.cc new file mode 100644 index 0000000..2ca5034 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/noise_level_estimator.cc @@ -0,0 +1,114 @@ +/* + * 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 "modules/audio_processing/agc2/noise_level_estimator.h" + +#include + +#include +#include +#include + +#include "api/array_view.h" +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { +constexpr int kFramesPerSecond = 100; + +float FrameEnergy(const AudioFrameView& audio) { + float energy = 0.f; + for (size_t k = 0; k < audio.num_channels(); ++k) { + float channel_energy = + std::accumulate(audio.channel(k).begin(), audio.channel(k).end(), 0.f, + [](float a, float b) -> float { return a + b * b; }); + energy = std::max(channel_energy, energy); + } + return energy; +} + +float EnergyToDbfs(float signal_energy, size_t num_samples) { + const float rms = std::sqrt(signal_energy / num_samples); + return FloatS16ToDbfs(rms); +} +} // namespace + +NoiseLevelEstimator::NoiseLevelEstimator(ApmDataDumper* data_dumper) + : signal_classifier_(data_dumper) { + Initialize(48000); +} + +NoiseLevelEstimator::~NoiseLevelEstimator() {} + +void NoiseLevelEstimator::Initialize(int sample_rate_hz) { + sample_rate_hz_ = sample_rate_hz; + noise_energy_ = 1.f; + first_update_ = true; + min_noise_energy_ = sample_rate_hz * 2.f * 2.f / kFramesPerSecond; + noise_energy_hold_counter_ = 0; + signal_classifier_.Initialize(sample_rate_hz); +} + +float NoiseLevelEstimator::Analyze(const AudioFrameView& frame) { + const int rate = + static_cast(frame.samples_per_channel() * kFramesPerSecond); + if (rate != sample_rate_hz_) { + Initialize(rate); + } + const float frame_energy = FrameEnergy(frame); + if (frame_energy <= 0.f) { + RTC_DCHECK_GE(frame_energy, 0.f); + return EnergyToDbfs(noise_energy_, frame.samples_per_channel()); + } + + if (first_update_) { + // Initialize the noise energy to the frame energy. + first_update_ = false; + return EnergyToDbfs( + noise_energy_ = std::max(frame_energy, min_noise_energy_), + frame.samples_per_channel()); + } + + const SignalClassifier::SignalType signal_type = + signal_classifier_.Analyze(frame.channel(0)); + + // Update the noise estimate in a minimum statistics-type manner. + if (signal_type == SignalClassifier::SignalType::kStationary) { + if (frame_energy > noise_energy_) { + // Leak the estimate upwards towards the frame energy if no recent + // downward update. + noise_energy_hold_counter_ = std::max(noise_energy_hold_counter_ - 1, 0); + + if (noise_energy_hold_counter_ == 0) { + noise_energy_ = std::min(noise_energy_ * 1.01f, frame_energy); + } + } else { + // Update smoothly downwards with a limited maximum update magnitude. + noise_energy_ = + std::max(noise_energy_ * 0.9f, + noise_energy_ + 0.05f * (frame_energy - noise_energy_)); + noise_energy_hold_counter_ = 1000; + } + } else { + // For a non-stationary signal, leak the estimate downwards in order to + // avoid estimate locking due to incorrect signal classification. + noise_energy_ = noise_energy_ * 0.99f; + } + + // Ensure a minimum of the estimate. + return EnergyToDbfs( + noise_energy_ = std::max(noise_energy_, min_noise_energy_), + frame.samples_per_channel()); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/noise_level_estimator.h b/webrtc/modules/audio_processing/agc2/noise_level_estimator.h new file mode 100644 index 0000000..ca2f9f2 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/noise_level_estimator.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_NOISE_LEVEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_NOISE_LEVEL_ESTIMATOR_H_ + +#include "modules/audio_processing/agc2/signal_classifier.h" +#include "modules/audio_processing/include/audio_frame_view.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { +class ApmDataDumper; + +class NoiseLevelEstimator { + public: + NoiseLevelEstimator(ApmDataDumper* data_dumper); + ~NoiseLevelEstimator(); + // Returns the estimated noise level in dBFS. + float Analyze(const AudioFrameView& frame); + + private: + void Initialize(int sample_rate_hz); + + int sample_rate_hz_; + float min_noise_energy_; + bool first_update_; + float noise_energy_; + int noise_energy_hold_counter_; + SignalClassifier signal_classifier_; + + RTC_DISALLOW_COPY_AND_ASSIGN(NoiseLevelEstimator); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_NOISE_LEVEL_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/agc2/noise_spectrum_estimator.cc b/webrtc/modules/audio_processing/agc2/noise_spectrum_estimator.cc new file mode 100644 index 0000000..31438b1 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/noise_spectrum_estimator.cc @@ -0,0 +1,70 @@ +/* + * 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 "modules/audio_processing/agc2/noise_spectrum_estimator.h" + +#include + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/arraysize.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { +constexpr float kMinNoisePower = 100.f; +} // namespace + +NoiseSpectrumEstimator::NoiseSpectrumEstimator(ApmDataDumper* data_dumper) + : data_dumper_(data_dumper) { + Initialize(); +} + +void NoiseSpectrumEstimator::Initialize() { + std::fill(noise_spectrum_, noise_spectrum_ + arraysize(noise_spectrum_), + kMinNoisePower); +} + +void NoiseSpectrumEstimator::Update(rtc::ArrayView spectrum, + bool first_update) { + RTC_DCHECK_EQ(65, spectrum.size()); + + if (first_update) { + // Initialize the noise spectral estimate with the signal spectrum. + std::copy(spectrum.data(), spectrum.data() + spectrum.size(), + noise_spectrum_); + } else { + // Smoothly update the noise spectral estimate towards the signal spectrum + // such that the magnitude of the updates are limited. + for (size_t k = 0; k < spectrum.size(); ++k) { + if (noise_spectrum_[k] < spectrum[k]) { + noise_spectrum_[k] = std::min( + 1.01f * noise_spectrum_[k], + noise_spectrum_[k] + 0.05f * (spectrum[k] - noise_spectrum_[k])); + } else { + noise_spectrum_[k] = std::max( + 0.99f * noise_spectrum_[k], + noise_spectrum_[k] + 0.05f * (spectrum[k] - noise_spectrum_[k])); + } + } + } + + // Ensure that the noise spectal estimate does not become too low. + for (auto& v : noise_spectrum_) { + v = std::max(v, kMinNoisePower); + } + + data_dumper_->DumpRaw("lc_noise_spectrum", 65, noise_spectrum_); + data_dumper_->DumpRaw("lc_signal_spectrum", spectrum); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/noise_spectrum_estimator.h b/webrtc/modules/audio_processing/agc2/noise_spectrum_estimator.h new file mode 100644 index 0000000..e9895f0 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/noise_spectrum_estimator.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_NOISE_SPECTRUM_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_NOISE_SPECTRUM_ESTIMATOR_H_ + +#include "api/array_view.h" + +namespace webrtc { + +class ApmDataDumper; + +class NoiseSpectrumEstimator { + public: + explicit NoiseSpectrumEstimator(ApmDataDumper* data_dumper); + + NoiseSpectrumEstimator() = delete; + NoiseSpectrumEstimator(const NoiseSpectrumEstimator&) = delete; + NoiseSpectrumEstimator& operator=(const NoiseSpectrumEstimator&) = delete; + + void Initialize(); + void Update(rtc::ArrayView spectrum, bool first_update); + + rtc::ArrayView GetNoiseSpectrum() const { + return rtc::ArrayView(noise_spectrum_); + } + + private: + ApmDataDumper* data_dumper_; + float noise_spectrum_[65]; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_NOISE_SPECTRUM_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/BUILD.gn b/webrtc/modules/audio_processing/agc2/rnn_vad/BUILD.gn new file mode 100644 index 0000000..8b01122 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/BUILD.gn @@ -0,0 +1,233 @@ +# Copyright (c) 2018 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. + +import("../../../../webrtc.gni") + +rtc_library("rnn_vad") { + visibility = [ "../*" ] + sources = [ + "features_extraction.cc", + "features_extraction.h", + "rnn.cc", + "rnn.h", + ] + + if (rtc_build_with_neon && current_cpu != "arm64") { + suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] + cflags = [ "-mfpu=neon" ] + } + + deps = [ + ":rnn_vad_common", + ":rnn_vad_lp_residual", + ":rnn_vad_pitch", + ":rnn_vad_sequence_buffer", + ":rnn_vad_spectral_features", + "..:biquad_filter", + "../../../../api:array_view", + "../../../../api:function_view", + "../../../../rtc_base:checks", + "../../../../rtc_base:logging", + "../../../../rtc_base/system:arch", + "//third_party/rnnoise:rnn_vad", + ] +} + +rtc_library("rnn_vad_auto_correlation") { + sources = [ + "auto_correlation.cc", + "auto_correlation.h", + ] + deps = [ + ":rnn_vad_common", + "../../../../api:array_view", + "../../../../rtc_base:checks", + "../../utility:pffft_wrapper", + ] +} + +rtc_library("rnn_vad_common") { + # TODO(alessiob): Make this target visibility private. + visibility = [ + ":*", + "..:rnn_vad_with_level", + ] + sources = [ + "common.cc", + "common.h", + ] + deps = [ + "../../../../rtc_base/system:arch", + "../../../../system_wrappers", + ] +} + +rtc_library("rnn_vad_lp_residual") { + sources = [ + "lp_residual.cc", + "lp_residual.h", + ] + deps = [ + "../../../../api:array_view", + "../../../../rtc_base:checks", + ] +} + +rtc_library("rnn_vad_pitch") { + sources = [ + "pitch_info.h", + "pitch_search.cc", + "pitch_search.h", + "pitch_search_internal.cc", + "pitch_search_internal.h", + ] + deps = [ + ":rnn_vad_auto_correlation", + ":rnn_vad_common", + "../../../../api:array_view", + "../../../../rtc_base:checks", + ] +} + +rtc_source_set("rnn_vad_ring_buffer") { + sources = [ "ring_buffer.h" ] + deps = [ + "../../../../api:array_view", + "../../../../rtc_base:checks", + ] +} + +rtc_source_set("rnn_vad_sequence_buffer") { + sources = [ "sequence_buffer.h" ] + deps = [ + "../../../../api:array_view", + "../../../../rtc_base:checks", + ] +} + +rtc_library("rnn_vad_spectral_features") { + sources = [ + "spectral_features.cc", + "spectral_features.h", + "spectral_features_internal.cc", + "spectral_features_internal.h", + ] + deps = [ + ":rnn_vad_common", + ":rnn_vad_ring_buffer", + ":rnn_vad_symmetric_matrix_buffer", + "../../../../api:array_view", + "../../../../rtc_base:checks", + "../../utility:pffft_wrapper", + ] +} + +rtc_source_set("rnn_vad_symmetric_matrix_buffer") { + sources = [ "symmetric_matrix_buffer.h" ] + deps = [ + "../../../../api:array_view", + "../../../../rtc_base:checks", + ] +} + +if (rtc_include_tests) { + rtc_library("test_utils") { + testonly = true + sources = [ + "test_utils.cc", + "test_utils.h", + ] + deps = [ + ":rnn_vad", + ":rnn_vad_common", + "../../../../api:array_view", + "../../../../api:scoped_refptr", + "../../../../rtc_base:checks", + "../../../../rtc_base/system:arch", + "../../../../system_wrappers", + "../../../../test:fileutils", + "../../../../test:test_support", + ] + } + + unittest_resources = [ + "../../../../resources/audio_processing/agc2/rnn_vad/band_energies.dat", + "../../../../resources/audio_processing/agc2/rnn_vad/pitch_buf_24k.dat", + "../../../../resources/audio_processing/agc2/rnn_vad/pitch_lp_res.dat", + "../../../../resources/audio_processing/agc2/rnn_vad/pitch_search_int.dat", + "../../../../resources/audio_processing/agc2/rnn_vad/samples.pcm", + "../../../../resources/audio_processing/agc2/rnn_vad/vad_prob.dat", + ] + + if (is_ios) { + bundle_data("unittests_bundle_data") { + testonly = true + sources = unittest_resources + outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ] + } + } + + rtc_library("unittests") { + testonly = true + sources = [ + "auto_correlation_unittest.cc", + "features_extraction_unittest.cc", + "lp_residual_unittest.cc", + "pitch_search_internal_unittest.cc", + "pitch_search_unittest.cc", + "ring_buffer_unittest.cc", + "rnn_unittest.cc", + "rnn_vad_unittest.cc", + "sequence_buffer_unittest.cc", + "spectral_features_internal_unittest.cc", + "spectral_features_unittest.cc", + "symmetric_matrix_buffer_unittest.cc", + ] + deps = [ + ":rnn_vad", + ":rnn_vad_auto_correlation", + ":rnn_vad_common", + ":rnn_vad_lp_residual", + ":rnn_vad_pitch", + ":rnn_vad_ring_buffer", + ":rnn_vad_sequence_buffer", + ":rnn_vad_spectral_features", + ":rnn_vad_symmetric_matrix_buffer", + ":test_utils", + "../..:audioproc_test_utils", + "../../../../api:array_view", + "../../../../common_audio/", + "../../../../rtc_base:checks", + "../../../../rtc_base:logging", + "../../../../rtc_base/system:arch", + "../../../../test:test_support", + "../../utility:pffft_wrapper", + "//third_party/rnnoise:rnn_vad", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] + data = unittest_resources + if (is_ios) { + deps += [ ":unittests_bundle_data" ] + } + } + + rtc_executable("rnn_vad_tool") { + testonly = true + sources = [ "rnn_vad_tool.cc" ] + deps = [ + ":rnn_vad", + ":rnn_vad_common", + "../../../../api:array_view", + "../../../../common_audio", + "../../../../rtc_base:rtc_base_approved", + "../../../../test:test_support", + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/flags:parse", + ] + } +} diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc b/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc new file mode 100644 index 0000000..d932c78 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/agc2/rnn_vad/auto_correlation.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +constexpr int kAutoCorrelationFftOrder = 9; // Length-512 FFT. +static_assert(1 << kAutoCorrelationFftOrder > + kNumInvertedLags12kHz + kBufSize12kHz - kMaxPitch12kHz, + ""); + +} // namespace + +AutoCorrelationCalculator::AutoCorrelationCalculator() + : fft_(1 << kAutoCorrelationFftOrder, Pffft::FftType::kReal), + tmp_(fft_.CreateBuffer()), + X_(fft_.CreateBuffer()), + H_(fft_.CreateBuffer()) {} + +AutoCorrelationCalculator::~AutoCorrelationCalculator() = default; + +// The auto-correlations coefficients are computed as follows: +// |.........|...........| <- pitch buffer +// [ x (fixed) ] +// [ y_0 ] +// [ y_{m-1} ] +// x and y are sub-array of equal length; x is never moved, whereas y slides. +// The cross-correlation between y_0 and x corresponds to the auto-correlation +// for the maximum pitch period. Hence, the first value in |auto_corr| has an +// inverted lag equal to 0 that corresponds to a lag equal to the maximum +// pitch period. +void AutoCorrelationCalculator::ComputeOnPitchBuffer( + rtc::ArrayView pitch_buf, + rtc::ArrayView auto_corr) { + RTC_DCHECK_LT(auto_corr.size(), kMaxPitch12kHz); + RTC_DCHECK_GT(pitch_buf.size(), kMaxPitch12kHz); + constexpr size_t kFftFrameSize = 1 << kAutoCorrelationFftOrder; + constexpr size_t kConvolutionLength = kBufSize12kHz - kMaxPitch12kHz; + static_assert(kConvolutionLength == kFrameSize20ms12kHz, + "Mismatch between pitch buffer size, frame size and maximum " + "pitch period."); + static_assert(kFftFrameSize > kNumInvertedLags12kHz + kConvolutionLength, + "The FFT length is not sufficiently big to avoid cyclic " + "convolution errors."); + auto tmp = tmp_->GetView(); + + // Compute the FFT for the reversed reference frame - i.e., + // pitch_buf[-kConvolutionLength:]. + std::reverse_copy(pitch_buf.end() - kConvolutionLength, pitch_buf.end(), + tmp.begin()); + std::fill(tmp.begin() + kConvolutionLength, tmp.end(), 0.f); + fft_.ForwardTransform(*tmp_, H_.get(), /*ordered=*/false); + + // Compute the FFT for the sliding frames chunk. The sliding frames are + // defined as pitch_buf[i:i+kConvolutionLength] where i in + // [0, kNumInvertedLags12kHz). The chunk includes all of them, hence it is + // defined as pitch_buf[:kNumInvertedLags12kHz+kConvolutionLength]. + std::copy(pitch_buf.begin(), + pitch_buf.begin() + kConvolutionLength + kNumInvertedLags12kHz, + tmp.begin()); + std::fill(tmp.begin() + kNumInvertedLags12kHz + kConvolutionLength, tmp.end(), + 0.f); + fft_.ForwardTransform(*tmp_, X_.get(), /*ordered=*/false); + + // Convolve in the frequency domain. + constexpr float kScalingFactor = 1.f / static_cast(kFftFrameSize); + std::fill(tmp.begin(), tmp.end(), 0.f); + fft_.FrequencyDomainConvolve(*X_, *H_, tmp_.get(), kScalingFactor); + fft_.BackwardTransform(*tmp_, tmp_.get(), /*ordered=*/false); + + // Extract the auto-correlation coefficients. + std::copy(tmp.begin() + kConvolutionLength - 1, + tmp.begin() + kConvolutionLength + kNumInvertedLags12kHz - 1, + auto_corr.begin()); +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.h b/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.h new file mode 100644 index 0000000..de7f453 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/auto_correlation.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_AUTO_CORRELATION_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_AUTO_CORRELATION_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/utility/pffft_wrapper.h" + +namespace webrtc { +namespace rnn_vad { + +// Class to compute the auto correlation on the pitch buffer for a target pitch +// interval. +class AutoCorrelationCalculator { + public: + AutoCorrelationCalculator(); + AutoCorrelationCalculator(const AutoCorrelationCalculator&) = delete; + AutoCorrelationCalculator& operator=(const AutoCorrelationCalculator&) = + delete; + ~AutoCorrelationCalculator(); + + // Computes the auto-correlation coefficients for a target pitch interval. + // |auto_corr| indexes are inverted lags. + void ComputeOnPitchBuffer( + rtc::ArrayView pitch_buf, + rtc::ArrayView auto_corr); + + private: + Pffft fft_; + std::unique_ptr tmp_; + std::unique_ptr X_; + std::unique_ptr H_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_AUTO_CORRELATION_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/common.cc b/webrtc/modules/audio_processing/agc2/rnn_vad/common.cc new file mode 100644 index 0000000..5d76b52 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/common.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/agc2/rnn_vad/common.h" + +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" + +namespace webrtc { +namespace rnn_vad { + +Optimization DetectOptimization() { +#if defined(WEBRTC_ARCH_X86_FAMILY) + if (GetCPUInfo(kSSE2) != 0) { + return Optimization::kSse2; + } +#endif + +#if defined(WEBRTC_HAS_NEON) + return Optimization::kNeon; +#endif + + return Optimization::kNone; +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/common.h b/webrtc/modules/audio_processing/agc2/rnn_vad/common.h new file mode 100644 index 0000000..c2e8df6 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/common.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_COMMON_H_ + +#include + +namespace webrtc { +namespace rnn_vad { + +constexpr double kPi = 3.14159265358979323846; + +constexpr size_t kSampleRate24kHz = 24000; +constexpr size_t kFrameSize10ms24kHz = kSampleRate24kHz / 100; +constexpr size_t kFrameSize20ms24kHz = kFrameSize10ms24kHz * 2; + +// Pitch buffer. +constexpr size_t kMinPitch24kHz = kSampleRate24kHz / 800; // 0.00125 s. +constexpr size_t kMaxPitch24kHz = kSampleRate24kHz / 62.5; // 0.016 s. +constexpr size_t kBufSize24kHz = kMaxPitch24kHz + kFrameSize20ms24kHz; +static_assert((kBufSize24kHz & 1) == 0, "The buffer size must be even."); + +// 24 kHz analysis. +// Define a higher minimum pitch period for the initial search. This is used to +// avoid searching for very short periods, for which a refinement step is +// responsible. +constexpr size_t kInitialMinPitch24kHz = 3 * kMinPitch24kHz; +static_assert(kMinPitch24kHz < kInitialMinPitch24kHz, ""); +static_assert(kInitialMinPitch24kHz < kMaxPitch24kHz, ""); +static_assert(kMaxPitch24kHz > kInitialMinPitch24kHz, ""); +constexpr size_t kNumInvertedLags24kHz = kMaxPitch24kHz - kInitialMinPitch24kHz; + +// 12 kHz analysis. +constexpr size_t kSampleRate12kHz = 12000; +constexpr size_t kFrameSize10ms12kHz = kSampleRate12kHz / 100; +constexpr size_t kFrameSize20ms12kHz = kFrameSize10ms12kHz * 2; +constexpr size_t kBufSize12kHz = kBufSize24kHz / 2; +constexpr size_t kInitialMinPitch12kHz = kInitialMinPitch24kHz / 2; +constexpr size_t kMaxPitch12kHz = kMaxPitch24kHz / 2; +static_assert(kMaxPitch12kHz > kInitialMinPitch12kHz, ""); +// The inverted lags for the pitch interval [|kInitialMinPitch12kHz|, +// |kMaxPitch12kHz|] are in the range [0, |kNumInvertedLags12kHz|]. +constexpr size_t kNumInvertedLags12kHz = kMaxPitch12kHz - kInitialMinPitch12kHz; + +// 48 kHz constants. +constexpr size_t kMinPitch48kHz = kMinPitch24kHz * 2; +constexpr size_t kMaxPitch48kHz = kMaxPitch24kHz * 2; + +// Spectral features. +constexpr size_t kNumBands = 22; +constexpr size_t kNumLowerBands = 6; +static_assert((0 < kNumLowerBands) && (kNumLowerBands < kNumBands), ""); +constexpr size_t kCepstralCoeffsHistorySize = 8; +static_assert(kCepstralCoeffsHistorySize > 2, + "The history size must at least be 3 to compute first and second " + "derivatives."); + +constexpr size_t kFeatureVectorSize = 42; + +enum class Optimization { kNone, kSse2, kNeon }; + +// Detects what kind of optimizations to use for the code. +Optimization DetectOptimization(); + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_COMMON_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.cc b/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.cc new file mode 100644 index 0000000..e935179 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.cc @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/rnn_vad/features_extraction.h" + +#include + +#include "modules/audio_processing/agc2/rnn_vad/lp_residual.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +// Generated via "B, A = scipy.signal.butter(2, 30/12000, btype='highpass')" +const BiQuadFilter::BiQuadCoefficients kHpfConfig24k = { + {0.99446179f, -1.98892358f, 0.99446179f}, + {-1.98889291f, 0.98895425f}}; + +} // namespace + +FeaturesExtractor::FeaturesExtractor() + : use_high_pass_filter_(false), + pitch_buf_24kHz_(), + pitch_buf_24kHz_view_(pitch_buf_24kHz_.GetBufferView()), + lp_residual_(kBufSize24kHz), + lp_residual_view_(lp_residual_.data(), kBufSize24kHz), + pitch_estimator_(), + reference_frame_view_(pitch_buf_24kHz_.GetMostRecentValuesView()) { + RTC_DCHECK_EQ(kBufSize24kHz, lp_residual_.size()); + hpf_.Initialize(kHpfConfig24k); + Reset(); +} + +FeaturesExtractor::~FeaturesExtractor() = default; + +void FeaturesExtractor::Reset() { + pitch_buf_24kHz_.Reset(); + spectral_features_extractor_.Reset(); + if (use_high_pass_filter_) + hpf_.Reset(); +} + +bool FeaturesExtractor::CheckSilenceComputeFeatures( + rtc::ArrayView samples, + rtc::ArrayView feature_vector) { + // Pre-processing. + if (use_high_pass_filter_) { + std::array samples_filtered; + hpf_.Process(samples, samples_filtered); + // Feed buffer with the pre-processed version of |samples|. + pitch_buf_24kHz_.Push(samples_filtered); + } else { + // Feed buffer with |samples|. + pitch_buf_24kHz_.Push(samples); + } + // Extract the LP residual. + float lpc_coeffs[kNumLpcCoefficients]; + ComputeAndPostProcessLpcCoefficients(pitch_buf_24kHz_view_, lpc_coeffs); + ComputeLpResidual(lpc_coeffs, pitch_buf_24kHz_view_, lp_residual_view_); + // Estimate pitch on the LP-residual and write the normalized pitch period + // into the output vector (normalization based on training data stats). + pitch_info_48kHz_ = pitch_estimator_.Estimate(lp_residual_view_); + feature_vector[kFeatureVectorSize - 2] = + 0.01f * (static_cast(pitch_info_48kHz_.period) - 300); + // Extract lagged frames (according to the estimated pitch period). + RTC_DCHECK_LE(pitch_info_48kHz_.period / 2, kMaxPitch24kHz); + auto lagged_frame = pitch_buf_24kHz_view_.subview( + kMaxPitch24kHz - pitch_info_48kHz_.period / 2, kFrameSize20ms24kHz); + // Analyze reference and lagged frames checking if silence has been detected + // and write the feature vector. + return spectral_features_extractor_.CheckSilenceComputeFeatures( + reference_frame_view_, {lagged_frame.data(), kFrameSize20ms24kHz}, + {feature_vector.data() + kNumLowerBands, kNumBands - kNumLowerBands}, + {feature_vector.data(), kNumLowerBands}, + {feature_vector.data() + kNumBands, kNumLowerBands}, + {feature_vector.data() + kNumBands + kNumLowerBands, kNumLowerBands}, + {feature_vector.data() + kNumBands + 2 * kNumLowerBands, kNumLowerBands}, + &feature_vector[kFeatureVectorSize - 1]); +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.h b/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.h new file mode 100644 index 0000000..ce5cce1 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/features_extraction.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_FEATURES_EXTRACTION_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_FEATURES_EXTRACTION_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/biquad_filter.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/pitch_info.h" +#include "modules/audio_processing/agc2/rnn_vad/pitch_search.h" +#include "modules/audio_processing/agc2/rnn_vad/sequence_buffer.h" +#include "modules/audio_processing/agc2/rnn_vad/spectral_features.h" + +namespace webrtc { +namespace rnn_vad { + +// Feature extractor to feed the VAD RNN. +class FeaturesExtractor { + public: + FeaturesExtractor(); + FeaturesExtractor(const FeaturesExtractor&) = delete; + FeaturesExtractor& operator=(const FeaturesExtractor&) = delete; + ~FeaturesExtractor(); + void Reset(); + // Analyzes the samples, computes the feature vector and returns true if + // silence is detected (false if not). When silence is detected, + // |feature_vector| is partially written and therefore must not be used to + // feed the VAD RNN. + bool CheckSilenceComputeFeatures( + rtc::ArrayView samples, + rtc::ArrayView feature_vector); + + private: + const bool use_high_pass_filter_; + // TODO(bugs.webrtc.org/7494): Remove HPF depending on how AGC2 is used in APM + // and on whether an HPF is already used as pre-processing step in APM. + BiQuadFilter hpf_; + SequenceBuffer + pitch_buf_24kHz_; + rtc::ArrayView pitch_buf_24kHz_view_; + std::vector lp_residual_; + rtc::ArrayView lp_residual_view_; + PitchEstimator pitch_estimator_; + rtc::ArrayView reference_frame_view_; + SpectralFeaturesExtractor spectral_features_extractor_; + PitchInfo pitch_info_48kHz_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_FEATURES_EXTRACTION_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.cc b/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.cc new file mode 100644 index 0000000..1a124a3 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/rnn_vad/lp_residual.h" + +#include +#include +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +// Computes cross-correlation coefficients between |x| and |y| and writes them +// in |x_corr|. The lag values are in {0, ..., max_lag - 1}, where max_lag +// equals the size of |x_corr|. +// The |x| and |y| sub-arrays used to compute a cross-correlation coefficients +// for a lag l have both size "size of |x| - l" - i.e., the longest sub-array is +// used. |x| and |y| must have the same size. +void ComputeCrossCorrelation( + rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView x_corr) { + constexpr size_t max_lag = x_corr.size(); + RTC_DCHECK_EQ(x.size(), y.size()); + RTC_DCHECK_LT(max_lag, x.size()); + for (size_t lag = 0; lag < max_lag; ++lag) { + x_corr[lag] = + std::inner_product(x.begin(), x.end() - lag, y.begin() + lag, 0.f); + } +} + +// Applies denoising to the auto-correlation coefficients. +void DenoiseAutoCorrelation( + rtc::ArrayView auto_corr) { + // Assume -40 dB white noise floor. + auto_corr[0] *= 1.0001f; + for (size_t i = 1; i < kNumLpcCoefficients; ++i) { + auto_corr[i] -= auto_corr[i] * (0.008f * i) * (0.008f * i); + } +} + +// Computes the initial inverse filter coefficients given the auto-correlation +// coefficients of an input frame. +void ComputeInitialInverseFilterCoefficients( + rtc::ArrayView auto_corr, + rtc::ArrayView lpc_coeffs) { + float error = auto_corr[0]; + for (size_t i = 0; i < kNumLpcCoefficients - 1; ++i) { + float reflection_coeff = 0.f; + for (size_t j = 0; j < i; ++j) { + reflection_coeff += lpc_coeffs[j] * auto_corr[i - j]; + } + reflection_coeff += auto_corr[i + 1]; + + // Avoid division by numbers close to zero. + constexpr float kMinErrorMagnitude = 1e-6f; + if (std::fabs(error) < kMinErrorMagnitude) { + error = std::copysign(kMinErrorMagnitude, error); + } + + reflection_coeff /= -error; + // Update LPC coefficients and total error. + lpc_coeffs[i] = reflection_coeff; + for (size_t j = 0; j<(i + 1)>> 1; ++j) { + const float tmp1 = lpc_coeffs[j]; + const float tmp2 = lpc_coeffs[i - 1 - j]; + lpc_coeffs[j] = tmp1 + reflection_coeff * tmp2; + lpc_coeffs[i - 1 - j] = tmp2 + reflection_coeff * tmp1; + } + error -= reflection_coeff * reflection_coeff * error; + if (error < 0.001f * auto_corr[0]) { + break; + } + } +} + +} // namespace + +void ComputeAndPostProcessLpcCoefficients( + rtc::ArrayView x, + rtc::ArrayView lpc_coeffs) { + std::array auto_corr; + ComputeCrossCorrelation(x, x, {auto_corr.data(), auto_corr.size()}); + if (auto_corr[0] == 0.f) { // Empty frame. + std::fill(lpc_coeffs.begin(), lpc_coeffs.end(), 0); + return; + } + DenoiseAutoCorrelation({auto_corr.data(), auto_corr.size()}); + std::array lpc_coeffs_pre{}; + ComputeInitialInverseFilterCoefficients(auto_corr, lpc_coeffs_pre); + // LPC coefficients post-processing. + // TODO(bugs.webrtc.org/9076): Consider removing these steps. + float c1 = 1.f; + for (size_t i = 0; i < kNumLpcCoefficients - 1; ++i) { + c1 *= 0.9f; + lpc_coeffs_pre[i] *= c1; + } + const float c2 = 0.8f; + lpc_coeffs[0] = lpc_coeffs_pre[0] + c2; + lpc_coeffs[1] = lpc_coeffs_pre[1] + c2 * lpc_coeffs_pre[0]; + lpc_coeffs[2] = lpc_coeffs_pre[2] + c2 * lpc_coeffs_pre[1]; + lpc_coeffs[3] = lpc_coeffs_pre[3] + c2 * lpc_coeffs_pre[2]; + lpc_coeffs[4] = c2 * lpc_coeffs_pre[3]; +} + +void ComputeLpResidual( + rtc::ArrayView lpc_coeffs, + rtc::ArrayView x, + rtc::ArrayView y) { + RTC_DCHECK_LT(kNumLpcCoefficients, x.size()); + RTC_DCHECK_EQ(x.size(), y.size()); + std::array input_chunk; + input_chunk.fill(0.f); + for (size_t i = 0; i < y.size(); ++i) { + const float sum = std::inner_product(input_chunk.begin(), input_chunk.end(), + lpc_coeffs.begin(), x[i]); + // Circular shift and add a new sample. + for (size_t j = kNumLpcCoefficients - 1; j > 0; --j) + input_chunk[j] = input_chunk[j - 1]; + input_chunk[0] = x[i]; + // Copy result. + y[i] = sum; + } +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.h b/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.h new file mode 100644 index 0000000..cddedca --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/lp_residual.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_LP_RESIDUAL_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_LP_RESIDUAL_H_ + +#include + +#include "api/array_view.h" + +namespace webrtc { +namespace rnn_vad { + +// LPC inverse filter length. +constexpr size_t kNumLpcCoefficients = 5; + +// Given a frame |x|, computes a post-processed version of LPC coefficients +// tailored for pitch estimation. +void ComputeAndPostProcessLpcCoefficients( + rtc::ArrayView x, + rtc::ArrayView lpc_coeffs); + +// Computes the LP residual for the input frame |x| and the LPC coefficients +// |lpc_coeffs|. |y| and |x| can point to the same array for in-place +// computation. +void ComputeLpResidual( + rtc::ArrayView lpc_coeffs, + rtc::ArrayView x, + rtc::ArrayView y); + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_LP_RESIDUAL_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_info.h b/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_info.h new file mode 100644 index 0000000..c9fdd18 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_info.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_INFO_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_INFO_H_ + +namespace webrtc { +namespace rnn_vad { + +// Stores pitch period and gain information. The pitch gain measures the +// strength of the pitch (the higher, the stronger). +struct PitchInfo { + PitchInfo() : period(0), gain(0.f) {} + PitchInfo(int p, float g) : period(p), gain(g) {} + int period; + float gain; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_INFO_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.cc b/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.cc new file mode 100644 index 0000000..1b3b459 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.cc @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/rnn_vad/pitch_search.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { + +PitchEstimator::PitchEstimator() + : pitch_buf_decimated_(kBufSize12kHz), + pitch_buf_decimated_view_(pitch_buf_decimated_.data(), kBufSize12kHz), + auto_corr_(kNumInvertedLags12kHz), + auto_corr_view_(auto_corr_.data(), kNumInvertedLags12kHz) { + RTC_DCHECK_EQ(kBufSize12kHz, pitch_buf_decimated_.size()); + RTC_DCHECK_EQ(kNumInvertedLags12kHz, auto_corr_view_.size()); +} + +PitchEstimator::~PitchEstimator() = default; + +PitchInfo PitchEstimator::Estimate( + rtc::ArrayView pitch_buf) { + // Perform the initial pitch search at 12 kHz. + Decimate2x(pitch_buf, pitch_buf_decimated_view_); + auto_corr_calculator_.ComputeOnPitchBuffer(pitch_buf_decimated_view_, + auto_corr_view_); + std::array pitch_candidates_inv_lags = FindBestPitchPeriods( + auto_corr_view_, pitch_buf_decimated_view_, kMaxPitch12kHz); + // Refine the pitch period estimation. + // The refinement is done using the pitch buffer that contains 24 kHz samples. + // Therefore, adapt the inverted lags in |pitch_candidates_inv_lags| from 12 + // to 24 kHz. + pitch_candidates_inv_lags[0] *= 2; + pitch_candidates_inv_lags[1] *= 2; + size_t pitch_inv_lag_48kHz = + RefinePitchPeriod48kHz(pitch_buf, pitch_candidates_inv_lags); + // Look for stronger harmonics to find the final pitch period and its gain. + RTC_DCHECK_LT(pitch_inv_lag_48kHz, kMaxPitch48kHz); + last_pitch_48kHz_ = CheckLowerPitchPeriodsAndComputePitchGain( + pitch_buf, kMaxPitch48kHz - pitch_inv_lag_48kHz, last_pitch_48kHz_); + return last_pitch_48kHz_; +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.h b/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.h new file mode 100644 index 0000000..74133d0 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/rnn_vad/auto_correlation.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/pitch_info.h" +#include "modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h" + +namespace webrtc { +namespace rnn_vad { + +// Pitch estimator. +class PitchEstimator { + public: + PitchEstimator(); + PitchEstimator(const PitchEstimator&) = delete; + PitchEstimator& operator=(const PitchEstimator&) = delete; + ~PitchEstimator(); + // Estimates the pitch period and gain. Returns the pitch estimation data for + // 48 kHz. + PitchInfo Estimate(rtc::ArrayView pitch_buf); + + private: + PitchInfo last_pitch_48kHz_; + AutoCorrelationCalculator auto_corr_calculator_; + std::vector pitch_buf_decimated_; + rtc::ArrayView pitch_buf_decimated_view_; + std::vector auto_corr_; + rtc::ArrayView auto_corr_view_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc b/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc new file mode 100644 index 0000000..f24a76f --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h" + +#include + +#include +#include +#include +#include + +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +// Converts a lag to an inverted lag (only for 24kHz). +size_t GetInvertedLag(size_t lag) { + RTC_DCHECK_LE(lag, kMaxPitch24kHz); + return kMaxPitch24kHz - lag; +} + +float ComputeAutoCorrelationCoeff(rtc::ArrayView pitch_buf, + size_t inv_lag, + size_t max_pitch_period) { + RTC_DCHECK_LT(inv_lag, pitch_buf.size()); + RTC_DCHECK_LT(max_pitch_period, pitch_buf.size()); + RTC_DCHECK_LE(inv_lag, max_pitch_period); + // TODO(bugs.webrtc.org/9076): Maybe optimize using vectorization. + return std::inner_product(pitch_buf.begin() + max_pitch_period, + pitch_buf.end(), pitch_buf.begin() + inv_lag, 0.f); +} + +// Computes a pseudo-interpolation offset for an estimated pitch period |lag| by +// looking at the auto-correlation coefficients in the neighborhood of |lag|. +// (namely, |prev_auto_corr|, |lag_auto_corr| and |next_auto_corr|). The output +// is a lag in {-1, 0, +1}. +// TODO(bugs.webrtc.org/9076): Consider removing pseudo-i since it +// is relevant only if the spectral analysis works at a sample rate that is +// twice as that of the pitch buffer (not so important instead for the estimated +// pitch period feature fed into the RNN). +int GetPitchPseudoInterpolationOffset(size_t lag, + float prev_auto_corr, + float lag_auto_corr, + float next_auto_corr) { + const float& a = prev_auto_corr; + const float& b = lag_auto_corr; + const float& c = next_auto_corr; + + int offset = 0; + if ((c - a) > 0.7f * (b - a)) { + offset = 1; // |c| is the largest auto-correlation coefficient. + } else if ((a - c) > 0.7f * (b - c)) { + offset = -1; // |a| is the largest auto-correlation coefficient. + } + return offset; +} + +// Refines a pitch period |lag| encoded as lag with pseudo-interpolation. The +// output sample rate is twice as that of |lag|. +size_t PitchPseudoInterpolationLagPitchBuf( + size_t lag, + rtc::ArrayView pitch_buf) { + int offset = 0; + // Cannot apply pseudo-interpolation at the boundaries. + if (lag > 0 && lag < kMaxPitch24kHz) { + offset = GetPitchPseudoInterpolationOffset( + lag, + ComputeAutoCorrelationCoeff(pitch_buf, GetInvertedLag(lag - 1), + kMaxPitch24kHz), + ComputeAutoCorrelationCoeff(pitch_buf, GetInvertedLag(lag), + kMaxPitch24kHz), + ComputeAutoCorrelationCoeff(pitch_buf, GetInvertedLag(lag + 1), + kMaxPitch24kHz)); + } + return 2 * lag + offset; +} + +// Refines a pitch period |inv_lag| encoded as inverted lag with +// pseudo-interpolation. The output sample rate is twice as that of +// |inv_lag|. +size_t PitchPseudoInterpolationInvLagAutoCorr( + size_t inv_lag, + rtc::ArrayView auto_corr) { + int offset = 0; + // Cannot apply pseudo-interpolation at the boundaries. + if (inv_lag > 0 && inv_lag < auto_corr.size() - 1) { + offset = GetPitchPseudoInterpolationOffset(inv_lag, auto_corr[inv_lag + 1], + auto_corr[inv_lag], + auto_corr[inv_lag - 1]); + } + // TODO(bugs.webrtc.org/9076): When retraining, check if |offset| below should + // be subtracted since |inv_lag| is an inverted lag but offset is a lag. + return 2 * inv_lag + offset; +} + +// Integer multipliers used in CheckLowerPitchPeriodsAndComputePitchGain() when +// looking for sub-harmonics. +// The values have been chosen to serve the following algorithm. Given the +// initial pitch period T, we examine whether one of its harmonics is the true +// fundamental frequency. We consider T/k with k in {2, ..., 15}. For each of +// these harmonics, in addition to the pitch gain of itself, we choose one +// multiple of its pitch period, n*T/k, to validate it (by averaging their pitch +// gains). The multiplier n is chosen so that n*T/k is used only one time over +// all k. When for example k = 4, we should also expect a peak at 3*T/4. When +// k = 8 instead we don't want to look at 2*T/8, since we have already checked +// T/4 before. Instead, we look at T*3/8. +// The array can be generate in Python as follows: +// from fractions import Fraction +// # Smallest positive integer not in X. +// def mex(X): +// for i in range(1, int(max(X)+2)): +// if i not in X: +// return i +// # Visited multiples of the period. +// S = {1} +// for n in range(2, 16): +// sn = mex({n * i for i in S} | {1}) +// S = S | {Fraction(1, n), Fraction(sn, n)} +// print(sn, end=', ') +constexpr std::array kSubHarmonicMultipliers = { + {3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2}}; + +// Initial pitch period candidate thresholds for ComputePitchGainThreshold() for +// a sample rate of 24 kHz. Computed as [5*k*k for k in range(16)]. +constexpr std::array kInitialPitchPeriodThresholds = { + {20, 45, 80, 125, 180, 245, 320, 405, 500, 605, 720, 845, 980, 1125}}; + +} // namespace + +void Decimate2x(rtc::ArrayView src, + rtc::ArrayView dst) { + // TODO(bugs.webrtc.org/9076): Consider adding anti-aliasing filter. + static_assert(2 * dst.size() == src.size(), ""); + for (size_t i = 0; i < dst.size(); ++i) { + dst[i] = src[2 * i]; + } +} + +float ComputePitchGainThreshold(int candidate_pitch_period, + int pitch_period_ratio, + int initial_pitch_period, + float initial_pitch_gain, + int prev_pitch_period, + float prev_pitch_gain) { + // Map arguments to more compact aliases. + const int& t1 = candidate_pitch_period; + const int& k = pitch_period_ratio; + const int& t0 = initial_pitch_period; + const float& g0 = initial_pitch_gain; + const int& t_prev = prev_pitch_period; + const float& g_prev = prev_pitch_gain; + + // Validate input. + RTC_DCHECK_GE(t1, 0); + RTC_DCHECK_GE(k, 2); + RTC_DCHECK_GE(t0, 0); + RTC_DCHECK_GE(t_prev, 0); + + // Compute a term that lowers the threshold when |t1| is close to the last + // estimated period |t_prev| - i.e., pitch tracking. + float lower_threshold_term = 0; + if (abs(t1 - t_prev) <= 1) { + // The candidate pitch period is within 1 sample from the previous one. + // Make the candidate at |t1| very easy to be accepted. + lower_threshold_term = g_prev; + } else if (abs(t1 - t_prev) == 2 && + t0 > kInitialPitchPeriodThresholds[k - 2]) { + // The candidate pitch period is 2 samples far from the previous one and the + // period |t0| (from which |t1| has been derived) is greater than a + // threshold. Make |t1| easy to be accepted. + lower_threshold_term = 0.5f * g_prev; + } + // Set the threshold based on the gain of the initial estimate |t0|. Also + // reduce the chance of false positives caused by a bias towards high + // frequencies (originating from short-term correlations). + float threshold = std::max(0.3f, 0.7f * g0 - lower_threshold_term); + if (static_cast(t1) < 3 * kMinPitch24kHz) { + // High frequency. + threshold = std::max(0.4f, 0.85f * g0 - lower_threshold_term); + } else if (static_cast(t1) < 2 * kMinPitch24kHz) { + // Even higher frequency. + threshold = std::max(0.5f, 0.9f * g0 - lower_threshold_term); + } + return threshold; +} + +void ComputeSlidingFrameSquareEnergies( + rtc::ArrayView pitch_buf, + rtc::ArrayView yy_values) { + float yy = + ComputeAutoCorrelationCoeff(pitch_buf, kMaxPitch24kHz, kMaxPitch24kHz); + yy_values[0] = yy; + for (size_t i = 1; i < yy_values.size(); ++i) { + RTC_DCHECK_LE(i, kMaxPitch24kHz + kFrameSize20ms24kHz); + RTC_DCHECK_LE(i, kMaxPitch24kHz); + const float old_coeff = pitch_buf[kMaxPitch24kHz + kFrameSize20ms24kHz - i]; + const float new_coeff = pitch_buf[kMaxPitch24kHz - i]; + yy -= old_coeff * old_coeff; + yy += new_coeff * new_coeff; + yy = std::max(0.f, yy); + yy_values[i] = yy; + } +} + +std::array FindBestPitchPeriods( + rtc::ArrayView auto_corr, + rtc::ArrayView pitch_buf, + size_t max_pitch_period) { + // Stores a pitch candidate period and strength information. + struct PitchCandidate { + // Pitch period encoded as inverted lag. + size_t period_inverted_lag = 0; + // Pitch strength encoded as a ratio. + float strength_numerator = -1.f; + float strength_denominator = 0.f; + // Compare the strength of two pitch candidates. + bool HasStrongerPitchThan(const PitchCandidate& b) const { + // Comparing the numerator/denominator ratios without using divisions. + return strength_numerator * b.strength_denominator > + b.strength_numerator * strength_denominator; + } + }; + + RTC_DCHECK_GT(max_pitch_period, auto_corr.size()); + RTC_DCHECK_LT(max_pitch_period, pitch_buf.size()); + const size_t frame_size = pitch_buf.size() - max_pitch_period; + // TODO(bugs.webrtc.org/9076): Maybe optimize using vectorization. + float yy = + std::inner_product(pitch_buf.begin(), pitch_buf.begin() + frame_size + 1, + pitch_buf.begin(), 1.f); + // Search best and second best pitches by looking at the scaled + // auto-correlation. + PitchCandidate candidate; + PitchCandidate best; + PitchCandidate second_best; + second_best.period_inverted_lag = 1; + for (size_t inv_lag = 0; inv_lag < auto_corr.size(); ++inv_lag) { + // A pitch candidate must have positive correlation. + if (auto_corr[inv_lag] > 0) { + candidate.period_inverted_lag = inv_lag; + candidate.strength_numerator = auto_corr[inv_lag] * auto_corr[inv_lag]; + candidate.strength_denominator = yy; + if (candidate.HasStrongerPitchThan(second_best)) { + if (candidate.HasStrongerPitchThan(best)) { + second_best = best; + best = candidate; + } else { + second_best = candidate; + } + } + } + // Update |squared_energy_y| for the next inverted lag. + const float old_coeff = pitch_buf[inv_lag]; + const float new_coeff = pitch_buf[inv_lag + frame_size]; + yy -= old_coeff * old_coeff; + yy += new_coeff * new_coeff; + yy = std::max(0.f, yy); + } + return {{best.period_inverted_lag, second_best.period_inverted_lag}}; +} + +size_t RefinePitchPeriod48kHz( + rtc::ArrayView pitch_buf, + rtc::ArrayView inv_lags) { + // Compute the auto-correlation terms only for neighbors of the given pitch + // candidates (similar to what is done in ComputePitchAutoCorrelation(), but + // for a few lag values). + std::array auto_corr; + auto_corr.fill(0.f); // Zeros become ignored lags in FindBestPitchPeriods(). + auto is_neighbor = [](size_t i, size_t j) { + return ((i > j) ? (i - j) : (j - i)) <= 2; + }; + for (size_t inv_lag = 0; inv_lag < auto_corr.size(); ++inv_lag) { + if (is_neighbor(inv_lag, inv_lags[0]) || is_neighbor(inv_lag, inv_lags[1])) + auto_corr[inv_lag] = + ComputeAutoCorrelationCoeff(pitch_buf, inv_lag, kMaxPitch24kHz); + } + // Find best pitch at 24 kHz. + const auto pitch_candidates_inv_lags = FindBestPitchPeriods( + {auto_corr.data(), auto_corr.size()}, + {pitch_buf.data(), pitch_buf.size()}, kMaxPitch24kHz); + const auto inv_lag = pitch_candidates_inv_lags[0]; // Refine the best. + // Pseudo-interpolation. + return PitchPseudoInterpolationInvLagAutoCorr(inv_lag, auto_corr); +} + +PitchInfo CheckLowerPitchPeriodsAndComputePitchGain( + rtc::ArrayView pitch_buf, + int initial_pitch_period_48kHz, + PitchInfo prev_pitch_48kHz) { + RTC_DCHECK_LE(kMinPitch48kHz, initial_pitch_period_48kHz); + RTC_DCHECK_LE(initial_pitch_period_48kHz, kMaxPitch48kHz); + // Stores information for a refined pitch candidate. + struct RefinedPitchCandidate { + RefinedPitchCandidate() {} + RefinedPitchCandidate(int period_24kHz, float gain, float xy, float yy) + : period_24kHz(period_24kHz), gain(gain), xy(xy), yy(yy) {} + int period_24kHz; + // Pitch strength information. + float gain; + // Additional pitch strength information used for the final estimation of + // pitch gain. + float xy; // Cross-correlation. + float yy; // Auto-correlation. + }; + + // Initialize. + std::array yy_values; + ComputeSlidingFrameSquareEnergies(pitch_buf, + {yy_values.data(), yy_values.size()}); + const float xx = yy_values[0]; + // Helper lambdas. + const auto pitch_gain = [](float xy, float yy, float xx) { + RTC_DCHECK_LE(0.f, xx * yy); + return xy / std::sqrt(1.f + xx * yy); + }; + // Initial pitch candidate gain. + RefinedPitchCandidate best_pitch; + best_pitch.period_24kHz = std::min(initial_pitch_period_48kHz / 2, + static_cast(kMaxPitch24kHz - 1)); + best_pitch.xy = ComputeAutoCorrelationCoeff( + pitch_buf, GetInvertedLag(best_pitch.period_24kHz), kMaxPitch24kHz); + best_pitch.yy = yy_values[best_pitch.period_24kHz]; + best_pitch.gain = pitch_gain(best_pitch.xy, best_pitch.yy, xx); + + // Store the initial pitch period information. + const size_t initial_pitch_period = best_pitch.period_24kHz; + const float initial_pitch_gain = best_pitch.gain; + + // Given the initial pitch estimation, check lower periods (i.e., harmonics). + const auto alternative_period = [](int period, int k, int n) -> int { + RTC_DCHECK_GT(k, 0); + return (2 * n * period + k) / (2 * k); // Same as round(n*period/k). + }; + for (int k = 2; k < static_cast(kSubHarmonicMultipliers.size() + 2); + ++k) { + int candidate_pitch_period = alternative_period(initial_pitch_period, k, 1); + if (static_cast(candidate_pitch_period) < kMinPitch24kHz) { + break; + } + // When looking at |candidate_pitch_period|, we also look at one of its + // sub-harmonics. |kSubHarmonicMultipliers| is used to know where to look. + // |k| == 2 is a special case since |candidate_pitch_secondary_period| might + // be greater than the maximum pitch period. + int candidate_pitch_secondary_period = alternative_period( + initial_pitch_period, k, kSubHarmonicMultipliers[k - 2]); + RTC_DCHECK_GT(candidate_pitch_secondary_period, 0); + if (k == 2 && + candidate_pitch_secondary_period > static_cast(kMaxPitch24kHz)) { + candidate_pitch_secondary_period = initial_pitch_period; + } + RTC_DCHECK_NE(candidate_pitch_period, candidate_pitch_secondary_period) + << "The lower pitch period and the additional sub-harmonic must not " + "coincide."; + // Compute an auto-correlation score for the primary pitch candidate + // |candidate_pitch_period| by also looking at its possible sub-harmonic + // |candidate_pitch_secondary_period|. + float xy_primary_period = ComputeAutoCorrelationCoeff( + pitch_buf, GetInvertedLag(candidate_pitch_period), kMaxPitch24kHz); + float xy_secondary_period = ComputeAutoCorrelationCoeff( + pitch_buf, GetInvertedLag(candidate_pitch_secondary_period), + kMaxPitch24kHz); + float xy = 0.5f * (xy_primary_period + xy_secondary_period); + float yy = 0.5f * (yy_values[candidate_pitch_period] + + yy_values[candidate_pitch_secondary_period]); + float candidate_pitch_gain = pitch_gain(xy, yy, xx); + + // Maybe update best period. + float threshold = ComputePitchGainThreshold( + candidate_pitch_period, k, initial_pitch_period, initial_pitch_gain, + prev_pitch_48kHz.period / 2, prev_pitch_48kHz.gain); + if (candidate_pitch_gain > threshold) { + best_pitch = {candidate_pitch_period, candidate_pitch_gain, xy, yy}; + } + } + + // Final pitch gain and period. + best_pitch.xy = std::max(0.f, best_pitch.xy); + RTC_DCHECK_LE(0.f, best_pitch.yy); + float final_pitch_gain = (best_pitch.yy <= best_pitch.xy) + ? 1.f + : best_pitch.xy / (best_pitch.yy + 1.f); + final_pitch_gain = std::min(best_pitch.gain, final_pitch_gain); + int final_pitch_period_48kHz = std::max( + kMinPitch48kHz, + PitchPseudoInterpolationLagPitchBuf(best_pitch.period_24kHz, pitch_buf)); + + return {final_pitch_period_48kHz, final_pitch_gain}; +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h b/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h new file mode 100644 index 0000000..2cc5ce6 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_INTERNAL_H_ + +#include + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/pitch_info.h" + +namespace webrtc { +namespace rnn_vad { + +// Performs 2x decimation without any anti-aliasing filter. +void Decimate2x(rtc::ArrayView src, + rtc::ArrayView dst); + +// Computes a gain threshold for a candidate pitch period given the initial and +// the previous pitch period and gain estimates and the pitch period ratio used +// to derive the candidate pitch period from the initial period. +float ComputePitchGainThreshold(int candidate_pitch_period, + int pitch_period_ratio, + int initial_pitch_period, + float initial_pitch_gain, + int prev_pitch_period, + float prev_pitch_gain); + +// Computes the sum of squared samples for every sliding frame in the pitch +// buffer. |yy_values| indexes are lags. +// +// The pitch buffer is structured as depicted below: +// |.........|...........| +// a b +// The part on the left, named "a" contains the oldest samples, whereas "b" the +// most recent ones. The size of "a" corresponds to the maximum pitch period, +// that of "b" to the frame size (e.g., 16 ms and 20 ms respectively). +void ComputeSlidingFrameSquareEnergies( + rtc::ArrayView pitch_buf, + rtc::ArrayView yy_values); + +// Given the auto-correlation coefficients stored according to +// ComputePitchAutoCorrelation() (i.e., using inverted lags), returns the best +// and the second best pitch periods. +std::array FindBestPitchPeriods( + rtc::ArrayView auto_corr, + rtc::ArrayView pitch_buf, + size_t max_pitch_period); + +// Refines the pitch period estimation given the pitch buffer |pitch_buf| and +// the initial pitch period estimation |inv_lags|. Returns an inverted lag at +// 48 kHz. +size_t RefinePitchPeriod48kHz( + rtc::ArrayView pitch_buf, + rtc::ArrayView inv_lags); + +// Refines the pitch period estimation and compute the pitch gain. Returns the +// refined pitch estimation data at 48 kHz. +PitchInfo CheckLowerPitchPeriodsAndComputePitchGain( + rtc::ArrayView pitch_buf, + int initial_pitch_period_48kHz, + PitchInfo prev_pitch_48kHz); + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_INTERNAL_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/ring_buffer.h b/webrtc/modules/audio_processing/agc2/rnn_vad/ring_buffer.h new file mode 100644 index 0000000..294b0c0 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/ring_buffer.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RING_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RING_BUFFER_H_ + +#include +#include +#include + +#include "api/array_view.h" + +namespace webrtc { +namespace rnn_vad { + +// Ring buffer for N arrays of type T each one with size S. +template +class RingBuffer { + static_assert(S > 0, ""); + static_assert(N > 0, ""); + static_assert(std::is_arithmetic::value, + "Integral or floating point required."); + + public: + RingBuffer() : tail_(0) {} + RingBuffer(const RingBuffer&) = delete; + RingBuffer& operator=(const RingBuffer&) = delete; + ~RingBuffer() = default; + // Set the ring buffer values to zero. + void Reset() { buffer_.fill(0); } + // Replace the least recently pushed array in the buffer with |new_values|. + void Push(rtc::ArrayView new_values) { + std::memcpy(buffer_.data() + S * tail_, new_values.data(), S * sizeof(T)); + tail_ += 1; + if (tail_ == N) + tail_ = 0; + } + // Return an array view onto the array with a given delay. A view on the last + // and least recently push array is returned when |delay| is 0 and N - 1 + // respectively. + rtc::ArrayView GetArrayView(size_t delay) const { + const int delay_int = static_cast(delay); + RTC_DCHECK_LE(0, delay_int); + RTC_DCHECK_LT(delay_int, N); + int offset = tail_ - 1 - delay_int; + if (offset < 0) + offset += N; + return {buffer_.data() + S * offset, S}; + } + + private: + int tail_; // Index of the least recently pushed sub-array. + std::array buffer_{}; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RING_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.cc b/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.cc new file mode 100644 index 0000000..55a51ff --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.cc @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/rnn_vad/rnn.h" + +// Defines WEBRTC_ARCH_X86_FAMILY, used below. +#include "rtc_base/system/arch.h" + +#if defined(WEBRTC_HAS_NEON) +#include +#endif +#if defined(WEBRTC_ARCH_X86_FAMILY) +#include +#endif +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "third_party/rnnoise/src/rnn_activations.h" +#include "third_party/rnnoise/src/rnn_vad_weights.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +using rnnoise::kWeightsScale; + +using rnnoise::kInputLayerInputSize; +static_assert(kFeatureVectorSize == kInputLayerInputSize, ""); +using rnnoise::kInputDenseBias; +using rnnoise::kInputDenseWeights; +using rnnoise::kInputLayerOutputSize; +static_assert(kInputLayerOutputSize <= kFullyConnectedLayersMaxUnits, + "Increase kFullyConnectedLayersMaxUnits."); + +using rnnoise::kHiddenGruBias; +using rnnoise::kHiddenGruRecurrentWeights; +using rnnoise::kHiddenGruWeights; +using rnnoise::kHiddenLayerOutputSize; +static_assert(kHiddenLayerOutputSize <= kRecurrentLayersMaxUnits, + "Increase kRecurrentLayersMaxUnits."); + +using rnnoise::kOutputDenseBias; +using rnnoise::kOutputDenseWeights; +using rnnoise::kOutputLayerOutputSize; +static_assert(kOutputLayerOutputSize <= kFullyConnectedLayersMaxUnits, + "Increase kFullyConnectedLayersMaxUnits."); + +using rnnoise::SigmoidApproximated; +using rnnoise::TansigApproximated; + +inline float RectifiedLinearUnit(float x) { + return x < 0.f ? 0.f : x; +} + +std::vector GetScaledParams(rtc::ArrayView params) { + std::vector scaled_params(params.size()); + std::transform(params.begin(), params.end(), scaled_params.begin(), + [](int8_t x) -> float { + return rnnoise::kWeightsScale * static_cast(x); + }); + return scaled_params; +} + +// TODO(bugs.chromium.org/10480): Hard-code optimized layout and remove this +// function to improve setup time. +// Casts and scales |weights| and re-arranges the layout. +std::vector GetPreprocessedFcWeights( + rtc::ArrayView weights, + size_t output_size) { + if (output_size == 1) { + return GetScaledParams(weights); + } + // Transpose, scale and cast. + const size_t input_size = rtc::CheckedDivExact(weights.size(), output_size); + std::vector w(weights.size()); + for (size_t o = 0; o < output_size; ++o) { + for (size_t i = 0; i < input_size; ++i) { + w[o * input_size + i] = rnnoise::kWeightsScale * + static_cast(weights[i * output_size + o]); + } + } + return w; +} + +constexpr size_t kNumGruGates = 3; // Update, reset, output. + +// TODO(bugs.chromium.org/10480): Hard-coded optimized layout and remove this +// function to improve setup time. +// Casts and scales |tensor_src| for a GRU layer and re-arranges the layout. +// It works both for weights, recurrent weights and bias. +std::vector GetPreprocessedGruTensor( + rtc::ArrayView tensor_src, + size_t output_size) { + // Transpose, cast and scale. + // |n| is the size of the first dimension of the 3-dim tensor |weights|. + const size_t n = + rtc::CheckedDivExact(tensor_src.size(), output_size * kNumGruGates); + const size_t stride_src = kNumGruGates * output_size; + const size_t stride_dst = n * output_size; + std::vector tensor_dst(tensor_src.size()); + for (size_t g = 0; g < kNumGruGates; ++g) { + for (size_t o = 0; o < output_size; ++o) { + for (size_t i = 0; i < n; ++i) { + tensor_dst[g * stride_dst + o * n + i] = + rnnoise::kWeightsScale * + static_cast( + tensor_src[i * stride_src + g * output_size + o]); + } + } + } + return tensor_dst; +} + +void ComputeGruUpdateResetGates(size_t input_size, + size_t output_size, + rtc::ArrayView weights, + rtc::ArrayView recurrent_weights, + rtc::ArrayView bias, + rtc::ArrayView input, + rtc::ArrayView state, + rtc::ArrayView gate) { + for (size_t o = 0; o < output_size; ++o) { + gate[o] = bias[o]; + for (size_t i = 0; i < input_size; ++i) { + gate[o] += input[i] * weights[o * input_size + i]; + } + for (size_t s = 0; s < output_size; ++s) { + gate[o] += state[s] * recurrent_weights[o * output_size + s]; + } + gate[o] = SigmoidApproximated(gate[o]); + } +} + +void ComputeGruOutputGate(size_t input_size, + size_t output_size, + rtc::ArrayView weights, + rtc::ArrayView recurrent_weights, + rtc::ArrayView bias, + rtc::ArrayView input, + rtc::ArrayView state, + rtc::ArrayView reset, + rtc::ArrayView gate) { + for (size_t o = 0; o < output_size; ++o) { + gate[o] = bias[o]; + for (size_t i = 0; i < input_size; ++i) { + gate[o] += input[i] * weights[o * input_size + i]; + } + for (size_t s = 0; s < output_size; ++s) { + gate[o] += state[s] * recurrent_weights[o * output_size + s] * reset[s]; + } + gate[o] = RectifiedLinearUnit(gate[o]); + } +} + +// Gated recurrent unit (GRU) layer un-optimized implementation. +void ComputeGruLayerOutput(size_t input_size, + size_t output_size, + rtc::ArrayView input, + rtc::ArrayView weights, + rtc::ArrayView recurrent_weights, + rtc::ArrayView bias, + rtc::ArrayView state) { + RTC_DCHECK_EQ(input_size, input.size()); + // Stride and offset used to read parameter arrays. + const size_t stride_in = input_size * output_size; + const size_t stride_out = output_size * output_size; + + // Update gate. + std::array update; + ComputeGruUpdateResetGates( + input_size, output_size, weights.subview(0, stride_in), + recurrent_weights.subview(0, stride_out), bias.subview(0, output_size), + input, state, update); + + // Reset gate. + std::array reset; + ComputeGruUpdateResetGates( + input_size, output_size, weights.subview(stride_in, stride_in), + recurrent_weights.subview(stride_out, stride_out), + bias.subview(output_size, output_size), input, state, reset); + + // Output gate. + std::array output; + ComputeGruOutputGate( + input_size, output_size, weights.subview(2 * stride_in, stride_in), + recurrent_weights.subview(2 * stride_out, stride_out), + bias.subview(2 * output_size, output_size), input, state, reset, output); + + // Update output through the update gates and update the state. + for (size_t o = 0; o < output_size; ++o) { + output[o] = update[o] * state[o] + (1.f - update[o]) * output[o]; + state[o] = output[o]; + } +} + +// Fully connected layer un-optimized implementation. +void ComputeFullyConnectedLayerOutput( + size_t input_size, + size_t output_size, + rtc::ArrayView input, + rtc::ArrayView bias, + rtc::ArrayView weights, + rtc::FunctionView activation_function, + rtc::ArrayView output) { + RTC_DCHECK_EQ(input.size(), input_size); + RTC_DCHECK_EQ(bias.size(), output_size); + RTC_DCHECK_EQ(weights.size(), input_size * output_size); + for (size_t o = 0; o < output_size; ++o) { + output[o] = bias[o]; + // TODO(bugs.chromium.org/9076): Benchmark how different layouts for + // |weights_| change the performance across different platforms. + for (size_t i = 0; i < input_size; ++i) { + output[o] += input[i] * weights[o * input_size + i]; + } + output[o] = activation_function(output[o]); + } +} + +#if defined(WEBRTC_ARCH_X86_FAMILY) +// Fully connected layer SSE2 implementation. +void ComputeFullyConnectedLayerOutputSse2( + size_t input_size, + size_t output_size, + rtc::ArrayView input, + rtc::ArrayView bias, + rtc::ArrayView weights, + rtc::FunctionView activation_function, + rtc::ArrayView output) { + RTC_DCHECK_EQ(input.size(), input_size); + RTC_DCHECK_EQ(bias.size(), output_size); + RTC_DCHECK_EQ(weights.size(), input_size * output_size); + const size_t input_size_by_4 = input_size >> 2; + const size_t offset = input_size & ~3; + __m128 sum_wx_128; + const float* v = reinterpret_cast(&sum_wx_128); + for (size_t o = 0; o < output_size; ++o) { + // Perform 128 bit vector operations. + sum_wx_128 = _mm_set1_ps(0); + const float* x_p = input.data(); + const float* w_p = weights.data() + o * input_size; + for (size_t i = 0; i < input_size_by_4; ++i, x_p += 4, w_p += 4) { + sum_wx_128 = _mm_add_ps(sum_wx_128, + _mm_mul_ps(_mm_loadu_ps(x_p), _mm_loadu_ps(w_p))); + } + // Perform non-vector operations for any remaining items, sum up bias term + // and results from the vectorized code, and apply the activation function. + output[o] = activation_function( + std::inner_product(input.begin() + offset, input.end(), + weights.begin() + o * input_size + offset, + bias[o] + v[0] + v[1] + v[2] + v[3])); + } +} +#endif + +} // namespace + +FullyConnectedLayer::FullyConnectedLayer( + const size_t input_size, + const size_t output_size, + const rtc::ArrayView bias, + const rtc::ArrayView weights, + rtc::FunctionView activation_function, + Optimization optimization) + : input_size_(input_size), + output_size_(output_size), + bias_(GetScaledParams(bias)), + weights_(GetPreprocessedFcWeights(weights, output_size)), + activation_function_(activation_function), + optimization_(optimization) { + RTC_DCHECK_LE(output_size_, kFullyConnectedLayersMaxUnits) + << "Static over-allocation of fully-connected layers output vectors is " + "not sufficient."; + RTC_DCHECK_EQ(output_size_, bias_.size()) + << "Mismatching output size and bias terms array size."; + RTC_DCHECK_EQ(input_size_ * output_size_, weights_.size()) + << "Mismatching input-output size and weight coefficients array size."; +} + +FullyConnectedLayer::~FullyConnectedLayer() = default; + +rtc::ArrayView FullyConnectedLayer::GetOutput() const { + return rtc::ArrayView(output_.data(), output_size_); +} + +void FullyConnectedLayer::ComputeOutput(rtc::ArrayView input) { + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Optimization::kSse2: + ComputeFullyConnectedLayerOutputSse2(input_size_, output_size_, input, + bias_, weights_, + activation_function_, output_); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Optimization::kNeon: + // TODO(bugs.chromium.org/10480): Handle Optimization::kNeon. + ComputeFullyConnectedLayerOutput(input_size_, output_size_, input, bias_, + weights_, activation_function_, output_); + break; +#endif + default: + ComputeFullyConnectedLayerOutput(input_size_, output_size_, input, bias_, + weights_, activation_function_, output_); + } +} + +GatedRecurrentLayer::GatedRecurrentLayer( + const size_t input_size, + const size_t output_size, + const rtc::ArrayView bias, + const rtc::ArrayView weights, + const rtc::ArrayView recurrent_weights, + Optimization optimization) + : input_size_(input_size), + output_size_(output_size), + bias_(GetPreprocessedGruTensor(bias, output_size)), + weights_(GetPreprocessedGruTensor(weights, output_size)), + recurrent_weights_( + GetPreprocessedGruTensor(recurrent_weights, output_size)), + optimization_(optimization) { + RTC_DCHECK_LE(output_size_, kRecurrentLayersMaxUnits) + << "Static over-allocation of recurrent layers state vectors is not " + "sufficient."; + RTC_DCHECK_EQ(kNumGruGates * output_size_, bias_.size()) + << "Mismatching output size and bias terms array size."; + RTC_DCHECK_EQ(kNumGruGates * input_size_ * output_size_, weights_.size()) + << "Mismatching input-output size and weight coefficients array size."; + RTC_DCHECK_EQ(kNumGruGates * output_size_ * output_size_, + recurrent_weights_.size()) + << "Mismatching input-output size and recurrent weight coefficients array" + " size."; + Reset(); +} + +GatedRecurrentLayer::~GatedRecurrentLayer() = default; + +rtc::ArrayView GatedRecurrentLayer::GetOutput() const { + return rtc::ArrayView(state_.data(), output_size_); +} + +void GatedRecurrentLayer::Reset() { + state_.fill(0.f); +} + +void GatedRecurrentLayer::ComputeOutput(rtc::ArrayView input) { + switch (optimization_) { +#if defined(WEBRTC_ARCH_X86_FAMILY) + case Optimization::kSse2: + // TODO(bugs.chromium.org/10480): Handle Optimization::kSse2. + ComputeGruLayerOutput(input_size_, output_size_, input, weights_, + recurrent_weights_, bias_, state_); + break; +#endif +#if defined(WEBRTC_HAS_NEON) + case Optimization::kNeon: + // TODO(bugs.chromium.org/10480): Handle Optimization::kNeon. + ComputeGruLayerOutput(input_size_, output_size_, input, weights_, + recurrent_weights_, bias_, state_); + break; +#endif + default: + ComputeGruLayerOutput(input_size_, output_size_, input, weights_, + recurrent_weights_, bias_, state_); + } +} + +RnnBasedVad::RnnBasedVad() + : input_layer_(kInputLayerInputSize, + kInputLayerOutputSize, + kInputDenseBias, + kInputDenseWeights, + TansigApproximated, + DetectOptimization()), + hidden_layer_(kInputLayerOutputSize, + kHiddenLayerOutputSize, + kHiddenGruBias, + kHiddenGruWeights, + kHiddenGruRecurrentWeights, + DetectOptimization()), + output_layer_(kHiddenLayerOutputSize, + kOutputLayerOutputSize, + kOutputDenseBias, + kOutputDenseWeights, + SigmoidApproximated, + DetectOptimization()) { + // Input-output chaining size checks. + RTC_DCHECK_EQ(input_layer_.output_size(), hidden_layer_.input_size()) + << "The input and the hidden layers sizes do not match."; + RTC_DCHECK_EQ(hidden_layer_.output_size(), output_layer_.input_size()) + << "The hidden and the output layers sizes do not match."; +} + +RnnBasedVad::~RnnBasedVad() = default; + +void RnnBasedVad::Reset() { + hidden_layer_.Reset(); +} + +float RnnBasedVad::ComputeVadProbability( + rtc::ArrayView feature_vector, + bool is_silence) { + if (is_silence) { + Reset(); + return 0.f; + } + input_layer_.ComputeOutput(feature_vector); + hidden_layer_.ComputeOutput(input_layer_.GetOutput()); + output_layer_.ComputeOutput(hidden_layer_.GetOutput()); + const auto vad_output = output_layer_.GetOutput(); + return vad_output[0]; +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.h b/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.h new file mode 100644 index 0000000..58274b2 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/rnn.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_H_ + +#include +#include + +#include +#include + +#include "api/array_view.h" +#include "api/function_view.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace rnn_vad { + +// Maximum number of units for a fully-connected layer. This value is used to +// over-allocate space for fully-connected layers output vectors (implemented as +// std::array). The value should equal the number of units of the largest +// fully-connected layer. +constexpr size_t kFullyConnectedLayersMaxUnits = 24; + +// Maximum number of units for a recurrent layer. This value is used to +// over-allocate space for recurrent layers state vectors (implemented as +// std::array). The value should equal the number of units of the largest +// recurrent layer. +constexpr size_t kRecurrentLayersMaxUnits = 24; + +// Fully-connected layer. +class FullyConnectedLayer { + public: + FullyConnectedLayer(size_t input_size, + size_t output_size, + rtc::ArrayView bias, + rtc::ArrayView weights, + rtc::FunctionView activation_function, + Optimization optimization); + FullyConnectedLayer(const FullyConnectedLayer&) = delete; + FullyConnectedLayer& operator=(const FullyConnectedLayer&) = delete; + ~FullyConnectedLayer(); + size_t input_size() const { return input_size_; } + size_t output_size() const { return output_size_; } + Optimization optimization() const { return optimization_; } + rtc::ArrayView GetOutput() const; + // Computes the fully-connected layer output. + void ComputeOutput(rtc::ArrayView input); + + private: + const size_t input_size_; + const size_t output_size_; + const std::vector bias_; + const std::vector weights_; + rtc::FunctionView activation_function_; + // The output vector of a recurrent layer has length equal to |output_size_|. + // However, for efficiency, over-allocation is used. + std::array output_; + const Optimization optimization_; +}; + +// Recurrent layer with gated recurrent units (GRUs) with sigmoid and ReLU as +// activation functions for the update/reset and output gates respectively. +class GatedRecurrentLayer { + public: + GatedRecurrentLayer(size_t input_size, + size_t output_size, + rtc::ArrayView bias, + rtc::ArrayView weights, + rtc::ArrayView recurrent_weights, + Optimization optimization); + GatedRecurrentLayer(const GatedRecurrentLayer&) = delete; + GatedRecurrentLayer& operator=(const GatedRecurrentLayer&) = delete; + ~GatedRecurrentLayer(); + size_t input_size() const { return input_size_; } + size_t output_size() const { return output_size_; } + Optimization optimization() const { return optimization_; } + rtc::ArrayView GetOutput() const; + void Reset(); + // Computes the recurrent layer output and updates the status. + void ComputeOutput(rtc::ArrayView input); + + private: + const size_t input_size_; + const size_t output_size_; + const std::vector bias_; + const std::vector weights_; + const std::vector recurrent_weights_; + // The state vector of a recurrent layer has length equal to |output_size_|. + // However, to avoid dynamic allocation, over-allocation is used. + std::array state_; + const Optimization optimization_; +}; + +// Recurrent network based VAD. +class RnnBasedVad { + public: + RnnBasedVad(); + RnnBasedVad(const RnnBasedVad&) = delete; + RnnBasedVad& operator=(const RnnBasedVad&) = delete; + ~RnnBasedVad(); + void Reset(); + // Compute and returns the probability of voice (range: [0.0, 1.0]). + float ComputeVadProbability( + rtc::ArrayView feature_vector, + bool is_silence); + + private: + FullyConnectedLayer input_layer_; + GatedRecurrentLayer hidden_layer_; + FullyConnectedLayer output_layer_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RNN_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_tool.cc b/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_tool.cc new file mode 100644 index 0000000..c5293be --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/rnn_vad_tool.cc @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018 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 +#include +#include + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "common_audio/resampler/push_sinc_resampler.h" +#include "common_audio/wav_file.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/features_extraction.h" +#include "modules/audio_processing/agc2/rnn_vad/rnn.h" +#include "rtc_base/logging.h" + +ABSL_FLAG(std::string, i, "", "Path to the input wav file"); +ABSL_FLAG(std::string, f, "", "Path to the output features file"); +ABSL_FLAG(std::string, o, "", "Path to the output VAD probabilities file"); + +namespace webrtc { +namespace rnn_vad { +namespace test { + +int main(int argc, char* argv[]) { + absl::ParseCommandLine(argc, argv); + rtc::LogMessage::LogToDebug(rtc::LS_INFO); + + // Open wav input file and check properties. + const std::string input_wav_file = absl::GetFlag(FLAGS_i); + WavReader wav_reader(input_wav_file); + if (wav_reader.num_channels() != 1) { + RTC_LOG(LS_ERROR) << "Only mono wav files are supported"; + return 1; + } + if (wav_reader.sample_rate() % 100 != 0) { + RTC_LOG(LS_ERROR) << "The sample rate rate must allow 10 ms frames."; + return 1; + } + RTC_LOG(LS_INFO) << "Input sample rate: " << wav_reader.sample_rate(); + + // Init output files. + const std::string output_vad_probs_file = absl::GetFlag(FLAGS_o); + FILE* vad_probs_file = fopen(output_vad_probs_file.c_str(), "wb"); + FILE* features_file = nullptr; + const std::string output_feature_file = absl::GetFlag(FLAGS_f); + if (!output_feature_file.empty()) { + features_file = fopen(output_feature_file.c_str(), "wb"); + } + + // Initialize. + const size_t frame_size_10ms = + rtc::CheckedDivExact(wav_reader.sample_rate(), 100); + std::vector samples_10ms; + samples_10ms.resize(frame_size_10ms); + std::array samples_10ms_24kHz; + PushSincResampler resampler(frame_size_10ms, kFrameSize10ms24kHz); + FeaturesExtractor features_extractor; + std::array feature_vector; + RnnBasedVad rnn_vad; + + // Compute VAD probabilities. + while (true) { + // Read frame at the input sample rate. + const auto read_samples = + wav_reader.ReadSamples(frame_size_10ms, samples_10ms.data()); + if (read_samples < frame_size_10ms) { + break; // EOF. + } + // Resample input. + resampler.Resample(samples_10ms.data(), samples_10ms.size(), + samples_10ms_24kHz.data(), samples_10ms_24kHz.size()); + + // Extract features and feed the RNN. + bool is_silence = features_extractor.CheckSilenceComputeFeatures( + samples_10ms_24kHz, feature_vector); + float vad_probability = + rnn_vad.ComputeVadProbability(feature_vector, is_silence); + // Write voice probability. + RTC_DCHECK_GE(vad_probability, 0.f); + RTC_DCHECK_GE(1.f, vad_probability); + fwrite(&vad_probability, sizeof(float), 1, vad_probs_file); + // Write features. + if (features_file) { + const float float_is_silence = is_silence ? 1.f : 0.f; + fwrite(&float_is_silence, sizeof(float), 1, features_file); + if (is_silence) { + // Do not write uninitialized values. + feature_vector.fill(0.f); + } + fwrite(feature_vector.data(), sizeof(float), kFeatureVectorSize, + features_file); + } + } + + // Close output file(s). + fclose(vad_probs_file); + RTC_LOG(LS_INFO) << "VAD probabilities written to " << output_vad_probs_file; + if (features_file) { + fclose(features_file); + RTC_LOG(LS_INFO) << "features written to " << output_feature_file; + } + + return 0; +} + +} // namespace test +} // namespace rnn_vad +} // namespace webrtc + +int main(int argc, char* argv[]) { + return webrtc::rnn_vad::test::main(argc, argv); +} diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h b/webrtc/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h new file mode 100644 index 0000000..75d3d9b --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SEQUENCE_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SEQUENCE_BUFFER_H_ + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { + +// Linear buffer implementation to (i) push fixed size chunks of sequential data +// and (ii) view contiguous parts of the buffer. The buffer and the pushed +// chunks have size S and N respectively. For instance, when S = 2N the first +// half of the sequence buffer is replaced with its second half, and the new N +// values are written at the end of the buffer. +// The class also provides a view on the most recent M values, where 0 < M <= S +// and by default M = N. +template +class SequenceBuffer { + static_assert(N <= S, + "The new chunk size cannot be larger than the sequence buffer " + "size."); + static_assert(std::is_arithmetic::value, + "Integral or floating point required."); + + public: + SequenceBuffer() : buffer_(S) { + RTC_DCHECK_EQ(S, buffer_.size()); + Reset(); + } + SequenceBuffer(const SequenceBuffer&) = delete; + SequenceBuffer& operator=(const SequenceBuffer&) = delete; + ~SequenceBuffer() = default; + size_t size() const { return S; } + size_t chunks_size() const { return N; } + // Sets the sequence buffer values to zero. + void Reset() { std::fill(buffer_.begin(), buffer_.end(), 0); } + // Returns a view on the whole buffer. + rtc::ArrayView GetBufferView() const { + return {buffer_.data(), S}; + } + // Returns a view on the M most recent values of the buffer. + rtc::ArrayView GetMostRecentValuesView() const { + static_assert(M <= S, + "The number of most recent values cannot be larger than the " + "sequence buffer size."); + return {buffer_.data() + S - M, M}; + } + // Shifts left the buffer by N items and add new N items at the end. + void Push(rtc::ArrayView new_values) { + // Make space for the new values. + if (S > N) + std::memmove(buffer_.data(), buffer_.data() + N, (S - N) * sizeof(T)); + // Copy the new values at the end of the buffer. + std::memcpy(buffer_.data() + S - N, new_values.data(), N * sizeof(T)); + } + + private: + std::vector buffer_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SEQUENCE_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.cc b/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.cc new file mode 100644 index 0000000..81e3339 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.cc @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/rnn_vad/spectral_features.h" + +#include +#include +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +constexpr float kSilenceThreshold = 0.04f; + +// Computes the new cepstral difference stats and pushes them into the passed +// symmetric matrix buffer. +void UpdateCepstralDifferenceStats( + rtc::ArrayView new_cepstral_coeffs, + const RingBuffer& ring_buf, + SymmetricMatrixBuffer* sym_matrix_buf) { + RTC_DCHECK(sym_matrix_buf); + // Compute the new cepstral distance stats. + std::array distances; + for (size_t i = 0; i < kCepstralCoeffsHistorySize - 1; ++i) { + const size_t delay = i + 1; + auto old_cepstral_coeffs = ring_buf.GetArrayView(delay); + distances[i] = 0.f; + for (size_t k = 0; k < kNumBands; ++k) { + const float c = new_cepstral_coeffs[k] - old_cepstral_coeffs[k]; + distances[i] += c * c; + } + } + // Push the new spectral distance stats into the symmetric matrix buffer. + sym_matrix_buf->Push(distances); +} + +// Computes the first half of the Vorbis window. +std::array ComputeScaledHalfVorbisWindow( + float scaling = 1.f) { + constexpr size_t kHalfSize = kFrameSize20ms24kHz / 2; + std::array half_window{}; + for (size_t i = 0; i < kHalfSize; ++i) { + half_window[i] = + scaling * + std::sin(0.5 * kPi * std::sin(0.5 * kPi * (i + 0.5) / kHalfSize) * + std::sin(0.5 * kPi * (i + 0.5) / kHalfSize)); + } + return half_window; +} + +// Computes the forward FFT on a 20 ms frame to which a given window function is +// applied. The Fourier coefficient corresponding to the Nyquist frequency is +// set to zero (it is never used and this allows to simplify the code). +void ComputeWindowedForwardFft( + rtc::ArrayView frame, + const std::array& half_window, + Pffft::FloatBuffer* fft_input_buffer, + Pffft::FloatBuffer* fft_output_buffer, + Pffft* fft) { + RTC_DCHECK_EQ(frame.size(), 2 * half_window.size()); + // Apply windowing. + auto in = fft_input_buffer->GetView(); + for (size_t i = 0, j = kFrameSize20ms24kHz - 1; i < half_window.size(); + ++i, --j) { + in[i] = frame[i] * half_window[i]; + in[j] = frame[j] * half_window[i]; + } + fft->ForwardTransform(*fft_input_buffer, fft_output_buffer, /*ordered=*/true); + // Set the Nyquist frequency coefficient to zero. + auto out = fft_output_buffer->GetView(); + out[1] = 0.f; +} + +} // namespace + +SpectralFeaturesExtractor::SpectralFeaturesExtractor() + : half_window_(ComputeScaledHalfVorbisWindow( + 1.f / static_cast(kFrameSize20ms24kHz))), + fft_(kFrameSize20ms24kHz, Pffft::FftType::kReal), + fft_buffer_(fft_.CreateBuffer()), + reference_frame_fft_(fft_.CreateBuffer()), + lagged_frame_fft_(fft_.CreateBuffer()), + dct_table_(ComputeDctTable()) {} + +SpectralFeaturesExtractor::~SpectralFeaturesExtractor() = default; + +void SpectralFeaturesExtractor::Reset() { + cepstral_coeffs_ring_buf_.Reset(); + cepstral_diffs_buf_.Reset(); +} + +bool SpectralFeaturesExtractor::CheckSilenceComputeFeatures( + rtc::ArrayView reference_frame, + rtc::ArrayView lagged_frame, + rtc::ArrayView higher_bands_cepstrum, + rtc::ArrayView average, + rtc::ArrayView first_derivative, + rtc::ArrayView second_derivative, + rtc::ArrayView bands_cross_corr, + float* variability) { + // Compute the Opus band energies for the reference frame. + ComputeWindowedForwardFft(reference_frame, half_window_, fft_buffer_.get(), + reference_frame_fft_.get(), &fft_); + spectral_correlator_.ComputeAutoCorrelation( + reference_frame_fft_->GetConstView(), reference_frame_bands_energy_); + // Check if the reference frame has silence. + const float tot_energy = + std::accumulate(reference_frame_bands_energy_.begin(), + reference_frame_bands_energy_.end(), 0.f); + if (tot_energy < kSilenceThreshold) { + return true; + } + // Compute the Opus band energies for the lagged frame. + ComputeWindowedForwardFft(lagged_frame, half_window_, fft_buffer_.get(), + lagged_frame_fft_.get(), &fft_); + spectral_correlator_.ComputeAutoCorrelation(lagged_frame_fft_->GetConstView(), + lagged_frame_bands_energy_); + // Log of the band energies for the reference frame. + std::array log_bands_energy; + ComputeSmoothedLogMagnitudeSpectrum(reference_frame_bands_energy_, + log_bands_energy); + // Reference frame cepstrum. + std::array cepstrum; + ComputeDct(log_bands_energy, dct_table_, cepstrum); + // Ad-hoc correction terms for the first two cepstral coefficients. + cepstrum[0] -= 12.f; + cepstrum[1] -= 4.f; + // Update the ring buffer and the cepstral difference stats. + cepstral_coeffs_ring_buf_.Push(cepstrum); + UpdateCepstralDifferenceStats(cepstrum, cepstral_coeffs_ring_buf_, + &cepstral_diffs_buf_); + // Write the higher bands cepstral coefficients. + RTC_DCHECK_EQ(cepstrum.size() - kNumLowerBands, higher_bands_cepstrum.size()); + std::copy(cepstrum.begin() + kNumLowerBands, cepstrum.end(), + higher_bands_cepstrum.begin()); + // Compute and write remaining features. + ComputeAvgAndDerivatives(average, first_derivative, second_derivative); + ComputeNormalizedCepstralCorrelation(bands_cross_corr); + RTC_DCHECK(variability); + *variability = ComputeVariability(); + return false; +} + +void SpectralFeaturesExtractor::ComputeAvgAndDerivatives( + rtc::ArrayView average, + rtc::ArrayView first_derivative, + rtc::ArrayView second_derivative) const { + auto curr = cepstral_coeffs_ring_buf_.GetArrayView(0); + auto prev1 = cepstral_coeffs_ring_buf_.GetArrayView(1); + auto prev2 = cepstral_coeffs_ring_buf_.GetArrayView(2); + RTC_DCHECK_EQ(average.size(), first_derivative.size()); + RTC_DCHECK_EQ(first_derivative.size(), second_derivative.size()); + RTC_DCHECK_LE(average.size(), curr.size()); + for (size_t i = 0; i < average.size(); ++i) { + // Average, kernel: [1, 1, 1]. + average[i] = curr[i] + prev1[i] + prev2[i]; + // First derivative, kernel: [1, 0, - 1]. + first_derivative[i] = curr[i] - prev2[i]; + // Second derivative, Laplacian kernel: [1, -2, 1]. + second_derivative[i] = curr[i] - 2 * prev1[i] + prev2[i]; + } +} + +void SpectralFeaturesExtractor::ComputeNormalizedCepstralCorrelation( + rtc::ArrayView bands_cross_corr) { + spectral_correlator_.ComputeCrossCorrelation( + reference_frame_fft_->GetConstView(), lagged_frame_fft_->GetConstView(), + bands_cross_corr_); + // Normalize. + for (size_t i = 0; i < bands_cross_corr_.size(); ++i) { + bands_cross_corr_[i] = + bands_cross_corr_[i] / + std::sqrt(0.001f + reference_frame_bands_energy_[i] * + lagged_frame_bands_energy_[i]); + } + // Cepstrum. + ComputeDct(bands_cross_corr_, dct_table_, bands_cross_corr); + // Ad-hoc correction terms for the first two cepstral coefficients. + bands_cross_corr[0] -= 1.3f; + bands_cross_corr[1] -= 0.9f; +} + +float SpectralFeaturesExtractor::ComputeVariability() const { + // Compute cepstral variability score. + float variability = 0.f; + for (size_t delay1 = 0; delay1 < kCepstralCoeffsHistorySize; ++delay1) { + float min_dist = std::numeric_limits::max(); + for (size_t delay2 = 0; delay2 < kCepstralCoeffsHistorySize; ++delay2) { + if (delay1 == delay2) // The distance would be 0. + continue; + min_dist = + std::min(min_dist, cepstral_diffs_buf_.GetValue(delay1, delay2)); + } + variability += min_dist; + } + // Normalize (based on training set stats). + // TODO(bugs.webrtc.org/10480): Isolate normalization from feature extraction. + return variability / kCepstralCoeffsHistorySize - 2.1f; +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.h b/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.h new file mode 100644 index 0000000..d327ef8 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_H_ + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/ring_buffer.h" +#include "modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h" +#include "modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h" +#include "modules/audio_processing/utility/pffft_wrapper.h" + +namespace webrtc { +namespace rnn_vad { + +// Class to compute spectral features. +class SpectralFeaturesExtractor { + public: + SpectralFeaturesExtractor(); + SpectralFeaturesExtractor(const SpectralFeaturesExtractor&) = delete; + SpectralFeaturesExtractor& operator=(const SpectralFeaturesExtractor&) = + delete; + ~SpectralFeaturesExtractor(); + // Resets the internal state of the feature extractor. + void Reset(); + // Analyzes a pair of reference and lagged frames from the pitch buffer, + // detects silence and computes features. If silence is detected, the output + // is neither computed nor written. + bool CheckSilenceComputeFeatures( + rtc::ArrayView reference_frame, + rtc::ArrayView lagged_frame, + rtc::ArrayView higher_bands_cepstrum, + rtc::ArrayView average, + rtc::ArrayView first_derivative, + rtc::ArrayView second_derivative, + rtc::ArrayView bands_cross_corr, + float* variability); + + private: + void ComputeAvgAndDerivatives( + rtc::ArrayView average, + rtc::ArrayView first_derivative, + rtc::ArrayView second_derivative) const; + void ComputeNormalizedCepstralCorrelation( + rtc::ArrayView bands_cross_corr); + float ComputeVariability() const; + + const std::array half_window_; + Pffft fft_; + std::unique_ptr fft_buffer_; + std::unique_ptr reference_frame_fft_; + std::unique_ptr lagged_frame_fft_; + SpectralCorrelator spectral_correlator_; + std::array reference_frame_bands_energy_; + std::array lagged_frame_bands_energy_; + std::array bands_cross_corr_; + const std::array dct_table_; + RingBuffer + cepstral_coeffs_ring_buf_; + SymmetricMatrixBuffer cepstral_diffs_buf_; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc b/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc new file mode 100644 index 0000000..29192a0 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h" + +#include +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { +namespace { + +// Weights for each FFT coefficient for each Opus band (Nyquist frequency +// excluded). The size of each band is specified in +// |kOpusScaleNumBins24kHz20ms|. +constexpr std::array kOpusBandWeights24kHz20ms = + {{ + 0.f, 0.25f, 0.5f, 0.75f, // Band 0 + 0.f, 0.25f, 0.5f, 0.75f, // Band 1 + 0.f, 0.25f, 0.5f, 0.75f, // Band 2 + 0.f, 0.25f, 0.5f, 0.75f, // Band 3 + 0.f, 0.25f, 0.5f, 0.75f, // Band 4 + 0.f, 0.25f, 0.5f, 0.75f, // Band 5 + 0.f, 0.25f, 0.5f, 0.75f, // Band 6 + 0.f, 0.25f, 0.5f, 0.75f, // Band 7 + 0.f, 0.125f, 0.25f, 0.375f, 0.5f, + 0.625f, 0.75f, 0.875f, // Band 8 + 0.f, 0.125f, 0.25f, 0.375f, 0.5f, + 0.625f, 0.75f, 0.875f, // Band 9 + 0.f, 0.125f, 0.25f, 0.375f, 0.5f, + 0.625f, 0.75f, 0.875f, // Band 10 + 0.f, 0.125f, 0.25f, 0.375f, 0.5f, + 0.625f, 0.75f, 0.875f, // Band 11 + 0.f, 0.0625f, 0.125f, 0.1875f, 0.25f, + 0.3125f, 0.375f, 0.4375f, 0.5f, 0.5625f, + 0.625f, 0.6875f, 0.75f, 0.8125f, 0.875f, + 0.9375f, // Band 12 + 0.f, 0.0625f, 0.125f, 0.1875f, 0.25f, + 0.3125f, 0.375f, 0.4375f, 0.5f, 0.5625f, + 0.625f, 0.6875f, 0.75f, 0.8125f, 0.875f, + 0.9375f, // Band 13 + 0.f, 0.0625f, 0.125f, 0.1875f, 0.25f, + 0.3125f, 0.375f, 0.4375f, 0.5f, 0.5625f, + 0.625f, 0.6875f, 0.75f, 0.8125f, 0.875f, + 0.9375f, // Band 14 + 0.f, 0.0416667f, 0.0833333f, 0.125f, 0.166667f, + 0.208333f, 0.25f, 0.291667f, 0.333333f, 0.375f, + 0.416667f, 0.458333f, 0.5f, 0.541667f, 0.583333f, + 0.625f, 0.666667f, 0.708333f, 0.75f, 0.791667f, + 0.833333f, 0.875f, 0.916667f, 0.958333f, // Band 15 + 0.f, 0.0416667f, 0.0833333f, 0.125f, 0.166667f, + 0.208333f, 0.25f, 0.291667f, 0.333333f, 0.375f, + 0.416667f, 0.458333f, 0.5f, 0.541667f, 0.583333f, + 0.625f, 0.666667f, 0.708333f, 0.75f, 0.791667f, + 0.833333f, 0.875f, 0.916667f, 0.958333f, // Band 16 + 0.f, 0.03125f, 0.0625f, 0.09375f, 0.125f, + 0.15625f, 0.1875f, 0.21875f, 0.25f, 0.28125f, + 0.3125f, 0.34375f, 0.375f, 0.40625f, 0.4375f, + 0.46875f, 0.5f, 0.53125f, 0.5625f, 0.59375f, + 0.625f, 0.65625f, 0.6875f, 0.71875f, 0.75f, + 0.78125f, 0.8125f, 0.84375f, 0.875f, 0.90625f, + 0.9375f, 0.96875f, // Band 17 + 0.f, 0.0208333f, 0.0416667f, 0.0625f, 0.0833333f, + 0.104167f, 0.125f, 0.145833f, 0.166667f, 0.1875f, + 0.208333f, 0.229167f, 0.25f, 0.270833f, 0.291667f, + 0.3125f, 0.333333f, 0.354167f, 0.375f, 0.395833f, + 0.416667f, 0.4375f, 0.458333f, 0.479167f, 0.5f, + 0.520833f, 0.541667f, 0.5625f, 0.583333f, 0.604167f, + 0.625f, 0.645833f, 0.666667f, 0.6875f, 0.708333f, + 0.729167f, 0.75f, 0.770833f, 0.791667f, 0.8125f, + 0.833333f, 0.854167f, 0.875f, 0.895833f, 0.916667f, + 0.9375f, 0.958333f, 0.979167f // Band 18 + }}; + +} // namespace + +SpectralCorrelator::SpectralCorrelator() + : weights_(kOpusBandWeights24kHz20ms.begin(), + kOpusBandWeights24kHz20ms.end()) {} + +SpectralCorrelator::~SpectralCorrelator() = default; + +void SpectralCorrelator::ComputeAutoCorrelation( + rtc::ArrayView x, + rtc::ArrayView auto_corr) const { + ComputeCrossCorrelation(x, x, auto_corr); +} + +void SpectralCorrelator::ComputeCrossCorrelation( + rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView cross_corr) const { + RTC_DCHECK_EQ(x.size(), kFrameSize20ms24kHz); + RTC_DCHECK_EQ(x.size(), y.size()); + RTC_DCHECK_EQ(x[1], 0.f) << "The Nyquist coefficient must be zeroed."; + RTC_DCHECK_EQ(y[1], 0.f) << "The Nyquist coefficient must be zeroed."; + constexpr auto kOpusScaleNumBins24kHz20ms = GetOpusScaleNumBins24kHz20ms(); + size_t k = 0; // Next Fourier coefficient index. + cross_corr[0] = 0.f; + for (size_t i = 0; i < kOpusBands24kHz - 1; ++i) { + cross_corr[i + 1] = 0.f; + for (int j = 0; j < kOpusScaleNumBins24kHz20ms[i]; ++j) { // Band size. + const float v = x[2 * k] * y[2 * k] + x[2 * k + 1] * y[2 * k + 1]; + const float tmp = weights_[k] * v; + cross_corr[i] += v - tmp; + cross_corr[i + 1] += tmp; + k++; + } + } + cross_corr[0] *= 2.f; // The first band only gets half contribution. + RTC_DCHECK_EQ(k, kFrameSize20ms24kHz / 2); // Nyquist coefficient never used. +} + +void ComputeSmoothedLogMagnitudeSpectrum( + rtc::ArrayView bands_energy, + rtc::ArrayView log_bands_energy) { + RTC_DCHECK_LE(bands_energy.size(), kNumBands); + constexpr float kOneByHundred = 1e-2f; + constexpr float kLogOneByHundred = -2.f; + // Init. + float log_max = kLogOneByHundred; + float follow = kLogOneByHundred; + const auto smooth = [&log_max, &follow](float x) { + x = std::max(log_max - 7.f, std::max(follow - 1.5f, x)); + log_max = std::max(log_max, x); + follow = std::max(follow - 1.5f, x); + return x; + }; + // Smoothing over the bands for which the band energy is defined. + for (size_t i = 0; i < bands_energy.size(); ++i) { + log_bands_energy[i] = smooth(std::log10(kOneByHundred + bands_energy[i])); + } + // Smoothing over the remaining bands (zero energy). + for (size_t i = bands_energy.size(); i < kNumBands; ++i) { + log_bands_energy[i] = smooth(kLogOneByHundred); + } +} + +std::array ComputeDctTable() { + std::array dct_table; + const double k = std::sqrt(0.5); + for (size_t i = 0; i < kNumBands; ++i) { + for (size_t j = 0; j < kNumBands; ++j) + dct_table[i * kNumBands + j] = std::cos((i + 0.5) * j * kPi / kNumBands); + dct_table[i * kNumBands] *= k; + } + return dct_table; +} + +void ComputeDct(rtc::ArrayView in, + rtc::ArrayView dct_table, + rtc::ArrayView out) { + // DCT scaling factor - i.e., sqrt(2 / kNumBands). + constexpr float kDctScalingFactor = 0.301511345f; + constexpr float kDctScalingFactorError = + kDctScalingFactor * kDctScalingFactor - + 2.f / static_cast(kNumBands); + static_assert( + (kDctScalingFactorError >= 0.f && kDctScalingFactorError < 1e-1f) || + (kDctScalingFactorError < 0.f && kDctScalingFactorError > -1e-1f), + "kNumBands changed and kDctScalingFactor has not been updated."); + RTC_DCHECK_NE(in.data(), out.data()) << "In-place DCT is not supported."; + RTC_DCHECK_LE(in.size(), kNumBands); + RTC_DCHECK_LE(1, out.size()); + RTC_DCHECK_LE(out.size(), in.size()); + for (size_t i = 0; i < out.size(); ++i) { + out[i] = 0.f; + for (size_t j = 0; j < in.size(); ++j) { + out[i] += in[j] * dct_table[j * kNumBands + i]; + } + // TODO(bugs.webrtc.org/10480): Scaling factor in the DCT table. + out[i] *= kDctScalingFactor; + } +} + +} // namespace rnn_vad +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h b/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h new file mode 100644 index 0000000..ed4caad --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_INTERNAL_H_ + +#include + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" + +namespace webrtc { +namespace rnn_vad { + +// At a sample rate of 24 kHz, the last 3 Opus bands are beyond the Nyquist +// frequency. However, band #19 gets the contributions from band #18 because +// of the symmetric triangular filter with peak response at 12 kHz. +constexpr size_t kOpusBands24kHz = 20; +static_assert(kOpusBands24kHz < kNumBands, + "The number of bands at 24 kHz must be less than those defined " + "in the Opus scale at 48 kHz."); + +// Number of FFT frequency bins covered by each band in the Opus scale at a +// sample rate of 24 kHz for 20 ms frames. +// Declared here for unit testing. +constexpr std::array GetOpusScaleNumBins24kHz20ms() { + return {4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 16, 16, 16, 24, 24, 32, 48}; +} + +// TODO(bugs.webrtc.org/10480): Move to a separate file. +// Class to compute band-wise spectral features in the Opus perceptual scale +// for 20 ms frames sampled at 24 kHz. The analysis methods apply triangular +// filters with peak response at the each band boundary. +class SpectralCorrelator { + public: + // Ctor. + SpectralCorrelator(); + SpectralCorrelator(const SpectralCorrelator&) = delete; + SpectralCorrelator& operator=(const SpectralCorrelator&) = delete; + ~SpectralCorrelator(); + + // Computes the band-wise spectral auto-correlations. + // |x| must: + // - have size equal to |kFrameSize20ms24kHz|; + // - be encoded as vectors of interleaved real-complex FFT coefficients + // where x[1] = y[1] = 0 (the Nyquist frequency coefficient is omitted). + void ComputeAutoCorrelation( + rtc::ArrayView x, + rtc::ArrayView auto_corr) const; + + // Computes the band-wise spectral cross-correlations. + // |x| and |y| must: + // - have size equal to |kFrameSize20ms24kHz|; + // - be encoded as vectors of interleaved real-complex FFT coefficients where + // x[1] = y[1] = 0 (the Nyquist frequency coefficient is omitted). + void ComputeCrossCorrelation( + rtc::ArrayView x, + rtc::ArrayView y, + rtc::ArrayView cross_corr) const; + + private: + const std::vector weights_; // Weights for each Fourier coefficient. +}; + +// TODO(bugs.webrtc.org/10480): Move to anonymous namespace in +// spectral_features.cc. Given a vector of Opus-bands energy coefficients, +// computes the log magnitude spectrum applying smoothing both over time and +// over frequency. Declared here for unit testing. +void ComputeSmoothedLogMagnitudeSpectrum( + rtc::ArrayView bands_energy, + rtc::ArrayView log_bands_energy); + +// TODO(bugs.webrtc.org/10480): Move to anonymous namespace in +// spectral_features.cc. Creates a DCT table for arrays having size equal to +// |kNumBands|. Declared here for unit testing. +std::array ComputeDctTable(); + +// TODO(bugs.webrtc.org/10480): Move to anonymous namespace in +// spectral_features.cc. Computes DCT for |in| given a pre-computed DCT table. +// In-place computation is not allowed and |out| can be smaller than |in| in +// order to only compute the first DCT coefficients. Declared here for unit +// testing. +void ComputeDct(rtc::ArrayView in, + rtc::ArrayView dct_table, + rtc::ArrayView out); + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SPECTRAL_FEATURES_INTERNAL_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h b/webrtc/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h new file mode 100644 index 0000000..f0282aa --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SYMMETRIC_MATRIX_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SYMMETRIC_MATRIX_BUFFER_H_ + +#include +#include +#include +#include + +#include "api/array_view.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { + +// Data structure to buffer the results of pair-wise comparisons between items +// stored in a ring buffer. Every time that the oldest item is replaced in the +// ring buffer, the new one is compared to the remaining items in the ring +// buffer. The results of such comparisons need to be buffered and automatically +// removed when one of the two corresponding items that have been compared is +// removed from the ring buffer. It is assumed that the comparison is symmetric +// and that comparing an item with itself is not needed. +template +class SymmetricMatrixBuffer { + static_assert(S > 2, ""); + + public: + SymmetricMatrixBuffer() = default; + SymmetricMatrixBuffer(const SymmetricMatrixBuffer&) = delete; + SymmetricMatrixBuffer& operator=(const SymmetricMatrixBuffer&) = delete; + ~SymmetricMatrixBuffer() = default; + // Sets the buffer values to zero. + void Reset() { + static_assert(std::is_arithmetic::value, + "Integral or floating point required."); + buf_.fill(0); + } + // Pushes the results from the comparison between the most recent item and + // those that are still in the ring buffer. The first element in |values| must + // correspond to the comparison between the most recent item and the second + // most recent one in the ring buffer, whereas the last element in |values| + // must correspond to the comparison between the most recent item and the + // oldest one in the ring buffer. + void Push(rtc::ArrayView values) { + // Move the lower-right sub-matrix of size (S-2) x (S-2) one row up and one + // column left. + std::memmove(buf_.data(), buf_.data() + S, (buf_.size() - S) * sizeof(T)); + // Copy new values in the last column in the right order. + for (size_t i = 0; i < values.size(); ++i) { + const size_t index = (S - 1 - i) * (S - 1) - 1; + RTC_DCHECK_LE(static_cast(0), index); + RTC_DCHECK_LT(index, buf_.size()); + buf_[index] = values[i]; + } + } + // Reads the value that corresponds to comparison of two items in the ring + // buffer having delay |delay1| and |delay2|. The two arguments must not be + // equal and both must be in {0, ..., S - 1}. + T GetValue(size_t delay1, size_t delay2) const { + int row = S - 1 - static_cast(delay1); + int col = S - 1 - static_cast(delay2); + RTC_DCHECK_NE(row, col) << "The diagonal cannot be accessed."; + if (row > col) + std::swap(row, col); // Swap to access the upper-right triangular part. + RTC_DCHECK_LE(0, row); + RTC_DCHECK_LT(row, S - 1) << "Not enforcing row < col and row != col."; + RTC_DCHECK_LE(1, col) << "Not enforcing row < col and row != col."; + RTC_DCHECK_LT(col, S); + const int index = row * (S - 1) + (col - 1); + RTC_DCHECK_LE(0, index); + RTC_DCHECK_LT(index, buf_.size()); + return buf_[index]; + } + + private: + // Encode an upper-right triangular matrix (excluding its diagonal) using a + // square matrix. This allows to move the data in Push() with one single + // operation. + std::array buf_{}; +}; + +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SYMMETRIC_MATRIX_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.cc b/webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.cc new file mode 100644 index 0000000..c7bf02e --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.cc @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/rnn_vad/test_utils.h" + +#include + +#include "rtc_base/checks.h" +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { +namespace rnn_vad { +namespace test { +namespace { + +using ReaderPairType = + std::pair>, const size_t>; + +} // namespace + +using webrtc::test::ResourcePath; + +void ExpectEqualFloatArray(rtc::ArrayView expected, + rtc::ArrayView computed) { + ASSERT_EQ(expected.size(), computed.size()); + for (size_t i = 0; i < expected.size(); ++i) { + SCOPED_TRACE(i); + EXPECT_FLOAT_EQ(expected[i], computed[i]); + } +} + +void ExpectNearAbsolute(rtc::ArrayView expected, + rtc::ArrayView computed, + float tolerance) { + ASSERT_EQ(expected.size(), computed.size()); + for (size_t i = 0; i < expected.size(); ++i) { + SCOPED_TRACE(i); + EXPECT_NEAR(expected[i], computed[i], tolerance); + } +} + +std::pair>, const size_t> +CreatePcmSamplesReader(const size_t frame_length) { + auto ptr = std::make_unique>( + test::ResourcePath("audio_processing/agc2/rnn_vad/samples", "pcm"), + frame_length); + // The last incomplete frame is ignored. + return {std::move(ptr), ptr->data_length() / frame_length}; +} + +ReaderPairType CreatePitchBuffer24kHzReader() { + constexpr size_t cols = 864; + auto ptr = std::make_unique>( + ResourcePath("audio_processing/agc2/rnn_vad/pitch_buf_24k", "dat"), cols); + return {std::move(ptr), rtc::CheckedDivExact(ptr->data_length(), cols)}; +} + +ReaderPairType CreateLpResidualAndPitchPeriodGainReader() { + constexpr size_t num_lp_residual_coeffs = 864; + auto ptr = std::make_unique>( + ResourcePath("audio_processing/agc2/rnn_vad/pitch_lp_res", "dat"), + num_lp_residual_coeffs); + return {std::move(ptr), + rtc::CheckedDivExact(ptr->data_length(), 2 + num_lp_residual_coeffs)}; +} + +ReaderPairType CreateVadProbsReader() { + auto ptr = std::make_unique>( + test::ResourcePath("audio_processing/agc2/rnn_vad/vad_prob", "dat")); + return {std::move(ptr), ptr->data_length()}; +} + +PitchTestData::PitchTestData() { + BinaryFileReader test_data_reader( + ResourcePath("audio_processing/agc2/rnn_vad/pitch_search_int", "dat"), + static_cast(1396)); + test_data_reader.ReadChunk(test_data_); +} + +PitchTestData::~PitchTestData() = default; + +rtc::ArrayView PitchTestData::GetPitchBufView() + const { + return {test_data_.data(), kBufSize24kHz}; +} + +rtc::ArrayView +PitchTestData::GetPitchBufSquareEnergiesView() const { + return {test_data_.data() + kBufSize24kHz, kNumPitchBufSquareEnergies}; +} + +rtc::ArrayView +PitchTestData::GetPitchBufAutoCorrCoeffsView() const { + return {test_data_.data() + kBufSize24kHz + kNumPitchBufSquareEnergies, + kNumPitchBufAutoCorrCoeffs}; +} + +bool IsOptimizationAvailable(Optimization optimization) { + switch (optimization) { + case Optimization::kSse2: +#if defined(WEBRTC_ARCH_X86_FAMILY) + return GetCPUInfo(kSSE2) != 0; +#else + return false; +#endif + case Optimization::kNeon: +#if defined(WEBRTC_HAS_NEON) + return true; +#else + return false; +#endif + case Optimization::kNone: + return true; + } +} + +} // namespace test +} // namespace rnn_vad +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.h b/webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.h new file mode 100644 index 0000000..db155e6 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/rnn_vad/test_utils.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_TEST_UTILS_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_TEST_UTILS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace rnn_vad { +namespace test { + +constexpr float kFloatMin = std::numeric_limits::min(); + +// Fails for every pair from two equally sized rtc::ArrayView views such +// that the values in the pair do not match. +void ExpectEqualFloatArray(rtc::ArrayView expected, + rtc::ArrayView computed); + +// Fails for every pair from two equally sized rtc::ArrayView views such +// that their absolute error is above a given threshold. +void ExpectNearAbsolute(rtc::ArrayView expected, + rtc::ArrayView computed, + float tolerance); + +// Reader for binary files consisting of an arbitrary long sequence of elements +// having type T. It is possible to read and cast to another type D at once. +template +class BinaryFileReader { + public: + explicit BinaryFileReader(const std::string& file_path, size_t chunk_size = 0) + : is_(file_path, std::ios::binary | std::ios::ate), + data_length_(is_.tellg() / sizeof(T)), + chunk_size_(chunk_size) { + RTC_CHECK(is_); + SeekBeginning(); + buf_.resize(chunk_size_); + } + BinaryFileReader(const BinaryFileReader&) = delete; + BinaryFileReader& operator=(const BinaryFileReader&) = delete; + ~BinaryFileReader() = default; + size_t data_length() const { return data_length_; } + bool ReadValue(D* dst) { + if (std::is_same::value) { + is_.read(reinterpret_cast(dst), sizeof(T)); + } else { + T v; + is_.read(reinterpret_cast(&v), sizeof(T)); + *dst = static_cast(v); + } + return is_.gcount() == sizeof(T); + } + // If |chunk_size| was specified in the ctor, it will check that the size of + // |dst| equals |chunk_size|. + bool ReadChunk(rtc::ArrayView dst) { + RTC_DCHECK((chunk_size_ == 0) || (chunk_size_ == dst.size())); + const std::streamsize bytes_to_read = dst.size() * sizeof(T); + if (std::is_same::value) { + is_.read(reinterpret_cast(dst.data()), bytes_to_read); + } else { + is_.read(reinterpret_cast(buf_.data()), bytes_to_read); + std::transform(buf_.begin(), buf_.end(), dst.begin(), + [](const T& v) -> D { return static_cast(v); }); + } + return is_.gcount() == bytes_to_read; + } + void SeekForward(size_t items) { is_.seekg(items * sizeof(T), is_.cur); } + void SeekBeginning() { is_.seekg(0, is_.beg); } + + private: + std::ifstream is_; + const size_t data_length_; + const size_t chunk_size_; + std::vector buf_; +}; + +// Writer for binary files. +template +class BinaryFileWriter { + public: + explicit BinaryFileWriter(const std::string& file_path) + : os_(file_path, std::ios::binary) {} + BinaryFileWriter(const BinaryFileWriter&) = delete; + BinaryFileWriter& operator=(const BinaryFileWriter&) = delete; + ~BinaryFileWriter() = default; + static_assert(std::is_arithmetic::value, ""); + void WriteChunk(rtc::ArrayView value) { + const std::streamsize bytes_to_write = value.size() * sizeof(T); + os_.write(reinterpret_cast(value.data()), bytes_to_write); + } + + private: + std::ofstream os_; +}; + +// Factories for resource file readers. +// The functions below return a pair where the first item is a reader unique +// pointer and the second the number of chunks that can be read from the file. +// Creates a reader for the PCM samples that casts from S16 to float and reads +// chunks with length |frame_length|. +std::pair>, const size_t> +CreatePcmSamplesReader(const size_t frame_length); +// Creates a reader for the pitch buffer content at 24 kHz. +std::pair>, const size_t> +CreatePitchBuffer24kHzReader(); +// Creates a reader for the the LP residual coefficients and the pitch period +// and gain values. +std::pair>, const size_t> +CreateLpResidualAndPitchPeriodGainReader(); +// Creates a reader for the VAD probabilities. +std::pair>, const size_t> +CreateVadProbsReader(); + +constexpr size_t kNumPitchBufAutoCorrCoeffs = 147; +constexpr size_t kNumPitchBufSquareEnergies = 385; +constexpr size_t kPitchTestDataSize = + kBufSize24kHz + kNumPitchBufSquareEnergies + kNumPitchBufAutoCorrCoeffs; + +// Class to retrieve a test pitch buffer content and the expected output for the +// analysis steps. +class PitchTestData { + public: + PitchTestData(); + ~PitchTestData(); + rtc::ArrayView GetPitchBufView() const; + rtc::ArrayView + GetPitchBufSquareEnergiesView() const; + rtc::ArrayView + GetPitchBufAutoCorrCoeffsView() const; + + private: + std::array test_data_; +}; + +// Returns true if the given optimization is available. +bool IsOptimizationAvailable(Optimization optimization); + +} // namespace test +} // namespace rnn_vad +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_TEST_UTILS_H_ diff --git a/webrtc/modules/audio_processing/agc2/saturation_protector.cc b/webrtc/modules/audio_processing/agc2/saturation_protector.cc new file mode 100644 index 0000000..b64fcdb --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/saturation_protector.cc @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/saturation_protector.h" + +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +constexpr float kMinLevelDbfs = -90.f; + +// Min/max margins are based on speech crest-factor. +constexpr float kMinMarginDb = 12.f; +constexpr float kMaxMarginDb = 25.f; + +using saturation_protector_impl::RingBuffer; + +} // namespace + +bool RingBuffer::operator==(const RingBuffer& b) const { + RTC_DCHECK_LE(size_, buffer_.size()); + RTC_DCHECK_LE(b.size_, b.buffer_.size()); + if (size_ != b.size_) { + return false; + } + for (int i = 0, i0 = FrontIndex(), i1 = b.FrontIndex(); i < size_; + ++i, ++i0, ++i1) { + if (buffer_[i0 % buffer_.size()] != b.buffer_[i1 % b.buffer_.size()]) { + return false; + } + } + return true; +} + +void RingBuffer::Reset() { + next_ = 0; + size_ = 0; +} + +void RingBuffer::PushBack(float v) { + RTC_DCHECK_GE(next_, 0); + RTC_DCHECK_GE(size_, 0); + RTC_DCHECK_LT(next_, buffer_.size()); + RTC_DCHECK_LE(size_, buffer_.size()); + buffer_[next_++] = v; + if (rtc::SafeEq(next_, buffer_.size())) { + next_ = 0; + } + if (rtc::SafeLt(size_, buffer_.size())) { + size_++; + } +} + +absl::optional RingBuffer::Front() const { + if (size_ == 0) { + return absl::nullopt; + } + RTC_DCHECK_LT(FrontIndex(), buffer_.size()); + return buffer_[FrontIndex()]; +} + +bool SaturationProtectorState::operator==( + const SaturationProtectorState& b) const { + return margin_db == b.margin_db && peak_delay_buffer == b.peak_delay_buffer && + max_peaks_dbfs == b.max_peaks_dbfs && + time_since_push_ms == b.time_since_push_ms; +} + +void ResetSaturationProtectorState(float initial_margin_db, + SaturationProtectorState& state) { + state.margin_db = initial_margin_db; + state.peak_delay_buffer.Reset(); + state.max_peaks_dbfs = kMinLevelDbfs; + state.time_since_push_ms = 0; +} + +void UpdateSaturationProtectorState(float speech_peak_dbfs, + float speech_level_dbfs, + SaturationProtectorState& state) { + // Get the max peak over `kPeakEnveloperSuperFrameLengthMs` ms. + state.max_peaks_dbfs = std::max(state.max_peaks_dbfs, speech_peak_dbfs); + state.time_since_push_ms += kFrameDurationMs; + if (rtc::SafeGt(state.time_since_push_ms, kPeakEnveloperSuperFrameLengthMs)) { + // Push `max_peaks_dbfs` back into the ring buffer. + state.peak_delay_buffer.PushBack(state.max_peaks_dbfs); + // Reset. + state.max_peaks_dbfs = kMinLevelDbfs; + state.time_since_push_ms = 0; + } + + // Update margin by comparing the estimated speech level and the delayed max + // speech peak power. + // TODO(alessiob): Check with aleloi@ why we use a delay and how to tune it. + const float delayed_peak_dbfs = + state.peak_delay_buffer.Front().value_or(state.max_peaks_dbfs); + const float difference_db = delayed_peak_dbfs - speech_level_dbfs; + if (difference_db > state.margin_db) { + // Attack. + state.margin_db = + state.margin_db * kSaturationProtectorAttackConstant + + difference_db * (1.f - kSaturationProtectorAttackConstant); + } else { + // Decay. + state.margin_db = state.margin_db * kSaturationProtectorDecayConstant + + difference_db * (1.f - kSaturationProtectorDecayConstant); + } + + state.margin_db = + rtc::SafeClamp(state.margin_db, kMinMarginDb, kMaxMarginDb); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/saturation_protector.h b/webrtc/modules/audio_processing/agc2/saturation_protector.h new file mode 100644 index 0000000..88be91a --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/saturation_protector.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_SATURATION_PROTECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_SATURATION_PROTECTOR_H_ + +#include + +#include "absl/types/optional.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "rtc_base/numerics/safe_compare.h" + +namespace webrtc { +namespace saturation_protector_impl { + +// Ring buffer which only supports (i) push back and (ii) read oldest item. +class RingBuffer { + public: + bool operator==(const RingBuffer& b) const; + inline bool operator!=(const RingBuffer& b) const { return !(*this == b); } + + // Maximum number of values that the buffer can contain. + int Capacity() const { return buffer_.size(); } + // Number of values in the buffer. + int Size() const { return size_; } + + void Reset(); + // Pushes back `v`. If the buffer is full, the oldest value is replaced. + void PushBack(float v); + // Returns the oldest item in the buffer. Returns an empty value if the + // buffer is empty. + absl::optional Front() const; + + private: + inline int FrontIndex() const { + return rtc::SafeEq(size_, buffer_.size()) ? next_ : 0; + } + // `buffer_` has `size_` elements (up to the size of `buffer_`) and `next_` is + // the position where the next new value is written in `buffer_`. + std::array buffer_; + int next_ = 0; + int size_ = 0; +}; + +} // namespace saturation_protector_impl + +// Saturation protector state. Exposed publicly for check-pointing and restore +// ops. +struct SaturationProtectorState { + bool operator==(const SaturationProtectorState& s) const; + inline bool operator!=(const SaturationProtectorState& s) const { + return !(*this == s); + } + + float margin_db; // Recommended margin. + saturation_protector_impl::RingBuffer peak_delay_buffer; + float max_peaks_dbfs; + int time_since_push_ms; // Time since the last ring buffer push operation. +}; + +// Resets the saturation protector state. +void ResetSaturationProtectorState(float initial_margin_db, + SaturationProtectorState& state); + +// Updates `state` by analyzing the estimated speech level `speech_level_dbfs` +// and the peak power `speech_peak_dbfs` for an observed frame which is +// reliably classified as "speech". `state` must not be modified without calling +// this function. +void UpdateSaturationProtectorState(float speech_peak_dbfs, + float speech_level_dbfs, + SaturationProtectorState& state); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_SATURATION_PROTECTOR_H_ diff --git a/webrtc/modules/audio_processing/agc2/signal_classifier.cc b/webrtc/modules/audio_processing/agc2/signal_classifier.cc new file mode 100644 index 0000000..a06413d --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/signal_classifier.cc @@ -0,0 +1,177 @@ +/* + * 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 "modules/audio_processing/agc2/signal_classifier.h" + +#include +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/agc2/down_sampler.h" +#include "modules/audio_processing/agc2/noise_spectrum_estimator.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "system_wrappers/include/cpu_features_wrapper.h" + +namespace webrtc { +namespace { + +bool IsSse2Available() { +#if defined(WEBRTC_ARCH_X86_FAMILY) + return GetCPUInfo(kSSE2) != 0; +#else + return false; +#endif +} + +void RemoveDcLevel(rtc::ArrayView x) { + RTC_DCHECK_LT(0, x.size()); + float mean = std::accumulate(x.data(), x.data() + x.size(), 0.f); + mean /= x.size(); + + for (float& v : x) { + v -= mean; + } +} + +void PowerSpectrum(const OouraFft* ooura_fft, + rtc::ArrayView x, + rtc::ArrayView spectrum) { + RTC_DCHECK_EQ(65, spectrum.size()); + RTC_DCHECK_EQ(128, x.size()); + float X[128]; + std::copy(x.data(), x.data() + x.size(), X); + ooura_fft->Fft(X); + + float* X_p = X; + RTC_DCHECK_EQ(X_p, &X[0]); + spectrum[0] = (*X_p) * (*X_p); + ++X_p; + RTC_DCHECK_EQ(X_p, &X[1]); + spectrum[64] = (*X_p) * (*X_p); + for (int k = 1; k < 64; ++k) { + ++X_p; + RTC_DCHECK_EQ(X_p, &X[2 * k]); + spectrum[k] = (*X_p) * (*X_p); + ++X_p; + RTC_DCHECK_EQ(X_p, &X[2 * k + 1]); + spectrum[k] += (*X_p) * (*X_p); + } +} + +webrtc::SignalClassifier::SignalType ClassifySignal( + rtc::ArrayView signal_spectrum, + rtc::ArrayView noise_spectrum, + ApmDataDumper* data_dumper) { + int num_stationary_bands = 0; + int num_highly_nonstationary_bands = 0; + + // Detect stationary and highly nonstationary bands. + for (size_t k = 1; k < 40; k++) { + if (signal_spectrum[k] < 3 * noise_spectrum[k] && + signal_spectrum[k] * 3 > noise_spectrum[k]) { + ++num_stationary_bands; + } else if (signal_spectrum[k] > 9 * noise_spectrum[k]) { + ++num_highly_nonstationary_bands; + } + } + + data_dumper->DumpRaw("lc_num_stationary_bands", 1, &num_stationary_bands); + data_dumper->DumpRaw("lc_num_highly_nonstationary_bands", 1, + &num_highly_nonstationary_bands); + + // Use the detected number of bands to classify the overall signal + // stationarity. + if (num_stationary_bands > 15) { + return SignalClassifier::SignalType::kStationary; + } else { + return SignalClassifier::SignalType::kNonStationary; + } +} + +} // namespace + +SignalClassifier::FrameExtender::FrameExtender(size_t frame_size, + size_t extended_frame_size) + : x_old_(extended_frame_size - frame_size, 0.f) {} + +SignalClassifier::FrameExtender::~FrameExtender() = default; + +void SignalClassifier::FrameExtender::ExtendFrame( + rtc::ArrayView x, + rtc::ArrayView x_extended) { + RTC_DCHECK_EQ(x_old_.size() + x.size(), x_extended.size()); + std::copy(x_old_.data(), x_old_.data() + x_old_.size(), x_extended.data()); + std::copy(x.data(), x.data() + x.size(), x_extended.data() + x_old_.size()); + std::copy(x_extended.data() + x_extended.size() - x_old_.size(), + x_extended.data() + x_extended.size(), x_old_.data()); +} + +SignalClassifier::SignalClassifier(ApmDataDumper* data_dumper) + : data_dumper_(data_dumper), + down_sampler_(data_dumper_), + noise_spectrum_estimator_(data_dumper_), + ooura_fft_(IsSse2Available()) { + Initialize(48000); +} +SignalClassifier::~SignalClassifier() {} + +void SignalClassifier::Initialize(int sample_rate_hz) { + down_sampler_.Initialize(sample_rate_hz); + noise_spectrum_estimator_.Initialize(); + frame_extender_.reset(new FrameExtender(80, 128)); + sample_rate_hz_ = sample_rate_hz; + initialization_frames_left_ = 2; + consistent_classification_counter_ = 3; + last_signal_type_ = SignalClassifier::SignalType::kNonStationary; +} + +SignalClassifier::SignalType SignalClassifier::Analyze( + rtc::ArrayView signal) { + RTC_DCHECK_EQ(signal.size(), sample_rate_hz_ / 100); + + // Compute the signal power spectrum. + float downsampled_frame[80]; + down_sampler_.DownSample(signal, downsampled_frame); + float extended_frame[128]; + frame_extender_->ExtendFrame(downsampled_frame, extended_frame); + RemoveDcLevel(extended_frame); + float signal_spectrum[65]; + PowerSpectrum(&ooura_fft_, extended_frame, signal_spectrum); + + // Classify the signal based on the estimate of the noise spectrum and the + // signal spectrum estimate. + const SignalType signal_type = ClassifySignal( + signal_spectrum, noise_spectrum_estimator_.GetNoiseSpectrum(), + data_dumper_); + + // Update the noise spectrum based on the signal spectrum. + noise_spectrum_estimator_.Update(signal_spectrum, + initialization_frames_left_ > 0); + + // Update the number of frames until a reliable signal spectrum is achieved. + initialization_frames_left_ = std::max(0, initialization_frames_left_ - 1); + + if (last_signal_type_ == signal_type) { + consistent_classification_counter_ = + std::max(0, consistent_classification_counter_ - 1); + } else { + last_signal_type_ = signal_type; + consistent_classification_counter_ = 3; + } + + if (consistent_classification_counter_ > 0) { + return SignalClassifier::SignalType::kNonStationary; + } + return signal_type; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/signal_classifier.h b/webrtc/modules/audio_processing/agc2/signal_classifier.h new file mode 100644 index 0000000..20cce92 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/signal_classifier.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_SIGNAL_CLASSIFIER_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_SIGNAL_CLASSIFIER_H_ + +#include +#include + +#include "api/array_view.h" +#include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" +#include "modules/audio_processing/agc2/down_sampler.h" +#include "modules/audio_processing/agc2/noise_spectrum_estimator.h" + +namespace webrtc { + +class ApmDataDumper; +class AudioBuffer; + +class SignalClassifier { + public: + enum class SignalType { kNonStationary, kStationary }; + + explicit SignalClassifier(ApmDataDumper* data_dumper); + + SignalClassifier() = delete; + SignalClassifier(const SignalClassifier&) = delete; + SignalClassifier& operator=(const SignalClassifier&) = delete; + + ~SignalClassifier(); + + void Initialize(int sample_rate_hz); + SignalType Analyze(rtc::ArrayView signal); + + private: + class FrameExtender { + public: + FrameExtender(size_t frame_size, size_t extended_frame_size); + + FrameExtender() = delete; + FrameExtender(const FrameExtender&) = delete; + FrameExtender& operator=(const FrameExtender&) = delete; + + ~FrameExtender(); + + void ExtendFrame(rtc::ArrayView x, + rtc::ArrayView x_extended); + + private: + std::vector x_old_; + }; + + ApmDataDumper* const data_dumper_; + DownSampler down_sampler_; + std::unique_ptr frame_extender_; + NoiseSpectrumEstimator noise_spectrum_estimator_; + int sample_rate_hz_; + int initialization_frames_left_; + int consistent_classification_counter_; + SignalType last_signal_type_; + const OouraFft ooura_fft_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_SIGNAL_CLASSIFIER_H_ diff --git a/webrtc/modules/audio_processing/agc2/vad_with_level.cc b/webrtc/modules/audio_processing/agc2/vad_with_level.cc new file mode 100644 index 0000000..3dbb557 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/vad_with_level.cc @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/vad_with_level.h" + +#include +#include +#include + +#include "api/array_view.h" +#include "common_audio/include/audio_util.h" +#include "common_audio/resampler/include/push_resampler.h" +#include "modules/audio_processing/agc2/agc2_common.h" +#include "modules/audio_processing/agc2/rnn_vad/common.h" +#include "modules/audio_processing/agc2/rnn_vad/features_extraction.h" +#include "modules/audio_processing/agc2/rnn_vad/rnn.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +using VoiceActivityDetector = VadLevelAnalyzer::VoiceActivityDetector; + +// Default VAD that combines a resampler and the RNN VAD. +// Computes the speech probability on the first channel. +class Vad : public VoiceActivityDetector { + public: + Vad() = default; + Vad(const Vad&) = delete; + Vad& operator=(const Vad&) = delete; + ~Vad() = default; + + float ComputeProbability(AudioFrameView frame) override { + // The source number of channels is 1, because we always use the 1st + // channel. + resampler_.InitializeIfNeeded( + /*sample_rate_hz=*/static_cast(frame.samples_per_channel() * 100), + rnn_vad::kSampleRate24kHz, + /*num_channels=*/1); + + std::array work_frame; + // Feed the 1st channel to the resampler. + resampler_.Resample(frame.channel(0).data(), frame.samples_per_channel(), + work_frame.data(), rnn_vad::kFrameSize10ms24kHz); + + std::array feature_vector; + const bool is_silence = features_extractor_.CheckSilenceComputeFeatures( + work_frame, feature_vector); + return rnn_vad_.ComputeVadProbability(feature_vector, is_silence); + } + + private: + PushResampler resampler_; + rnn_vad::FeaturesExtractor features_extractor_; + rnn_vad::RnnBasedVad rnn_vad_; +}; + +// Returns an updated version of `p_old` by using instant decay and the given +// `attack` on a new VAD probability value `p_new`. +float SmoothedVadProbability(float p_old, float p_new, float attack) { + RTC_DCHECK_GT(attack, 0.f); + RTC_DCHECK_LE(attack, 1.f); + if (p_new < p_old || attack == 1.f) { + // Instant decay (or no smoothing). + return p_new; + } else { + // Attack phase. + return attack * p_new + (1.f - attack) * p_old; + } +} + +} // namespace + +VadLevelAnalyzer::VadLevelAnalyzer() + : VadLevelAnalyzer(kDefaultSmoothedVadProbabilityAttack, + std::make_unique()) {} + +VadLevelAnalyzer::VadLevelAnalyzer(float vad_probability_attack) + : VadLevelAnalyzer(vad_probability_attack, std::make_unique()) {} + +VadLevelAnalyzer::VadLevelAnalyzer(float vad_probability_attack, + std::unique_ptr vad) + : vad_(std::move(vad)), vad_probability_attack_(vad_probability_attack) { + RTC_DCHECK(vad_); +} + +VadLevelAnalyzer::~VadLevelAnalyzer() = default; + +VadLevelAnalyzer::Result VadLevelAnalyzer::AnalyzeFrame( + AudioFrameView frame) { + // Compute levels. + float peak = 0.f; + float rms = 0.f; + for (const auto& x : frame.channel(0)) { + peak = std::max(std::fabs(x), peak); + rms += x * x; + } + // Compute smoothed speech probability. + vad_probability_ = SmoothedVadProbability( + /*p_old=*/vad_probability_, /*p_new=*/vad_->ComputeProbability(frame), + vad_probability_attack_); + return {vad_probability_, + FloatS16ToDbfs(std::sqrt(rms / frame.samples_per_channel())), + FloatS16ToDbfs(peak)}; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/vad_with_level.h b/webrtc/modules/audio_processing/agc2/vad_with_level.h new file mode 100644 index 0000000..ce72cdc --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/vad_with_level.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_VAD_WITH_LEVEL_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_VAD_WITH_LEVEL_H_ + +#include + +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { + +// Class to analyze voice activity and audio levels. +class VadLevelAnalyzer { + public: + struct Result { + float speech_probability; // Range: [0, 1]. + float rms_dbfs; // Root mean square power (dBFS). + float peak_dbfs; // Peak power (dBFS). + }; + + // Voice Activity Detector (VAD) interface. + class VoiceActivityDetector { + public: + virtual ~VoiceActivityDetector() = default; + // Analyzes an audio frame and returns the speech probability. + virtual float ComputeProbability(AudioFrameView frame) = 0; + }; + + // Ctor. Uses the default VAD. + VadLevelAnalyzer(); + explicit VadLevelAnalyzer(float vad_probability_attack); + // Ctor. Uses a custom `vad`. + VadLevelAnalyzer(float vad_probability_attack, + std::unique_ptr vad); + VadLevelAnalyzer(const VadLevelAnalyzer&) = delete; + VadLevelAnalyzer& operator=(const VadLevelAnalyzer&) = delete; + ~VadLevelAnalyzer(); + + // Computes the speech probability and the level for `frame`. + Result AnalyzeFrame(AudioFrameView frame); + + private: + std::unique_ptr vad_; + const float vad_probability_attack_; + float vad_probability_ = 0.f; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_VAD_WITH_LEVEL_H_ diff --git a/webrtc/modules/audio_processing/agc2/vector_float_frame.cc b/webrtc/modules/audio_processing/agc2/vector_float_frame.cc new file mode 100644 index 0000000..a70d815 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/vector_float_frame.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 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 "modules/audio_processing/agc2/vector_float_frame.h" + +namespace webrtc { + +namespace { + +std::vector ConstructChannelPointers( + std::vector>* x) { + std::vector channel_ptrs; + for (auto& v : *x) { + channel_ptrs.push_back(v.data()); + } + return channel_ptrs; +} +} // namespace + +VectorFloatFrame::VectorFloatFrame(int num_channels, + int samples_per_channel, + float start_value) + : channels_(num_channels, + std::vector(samples_per_channel, start_value)), + channel_ptrs_(ConstructChannelPointers(&channels_)), + float_frame_view_(channel_ptrs_.data(), + channels_.size(), + samples_per_channel) {} + +VectorFloatFrame::~VectorFloatFrame() = default; + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/agc2/vector_float_frame.h b/webrtc/modules/audio_processing/agc2/vector_float_frame.h new file mode 100644 index 0000000..b521f34 --- /dev/null +++ b/webrtc/modules/audio_processing/agc2/vector_float_frame.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AGC2_VECTOR_FLOAT_FRAME_H_ +#define MODULES_AUDIO_PROCESSING_AGC2_VECTOR_FLOAT_FRAME_H_ + +#include + +#include "modules/audio_processing/include/audio_frame_view.h" + +namespace webrtc { + +// A construct consisting of a multi-channel audio frame, and a FloatFrame view +// of it. +class VectorFloatFrame { + public: + VectorFloatFrame(int num_channels, + int samples_per_channel, + float start_value); + const AudioFrameView& float_frame_view() { return float_frame_view_; } + AudioFrameView float_frame_view() const { + return float_frame_view_; + } + + ~VectorFloatFrame(); + + private: + std::vector> channels_; + std::vector channel_ptrs_; + AudioFrameView float_frame_view_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AGC2_VECTOR_FLOAT_FRAME_H_ diff --git a/webrtc/modules/audio_processing/audio_buffer.cc b/webrtc/modules/audio_processing/audio_buffer.cc index 81790a1..ff6636d 100644 --- a/webrtc/modules/audio_processing/audio_buffer.cc +++ b/webrtc/modules/audio_processing/audio_buffer.cc @@ -8,446 +8,364 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/audio_buffer.h" -#include "webrtc/common_audio/include/audio_util.h" -#include "webrtc/common_audio/resampler/push_sinc_resampler.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/common_audio/channel_buffer.h" -#include "webrtc/modules/audio_processing/common.h" +#include + +#include + +#include "common_audio/channel_buffer.h" +#include "common_audio/include/audio_util.h" +#include "common_audio/resampler/push_sinc_resampler.h" +#include "modules/audio_processing/splitting_filter.h" +#include "rtc_base/checks.h" namespace webrtc { namespace { -const size_t kSamplesPer16kHzChannel = 160; -const size_t kSamplesPer32kHzChannel = 320; -const size_t kSamplesPer48kHzChannel = 480; +constexpr size_t kSamplesPer32kHzChannel = 320; +constexpr size_t kSamplesPer48kHzChannel = 480; +constexpr size_t kMaxSamplesPerChannel = AudioBuffer::kMaxSampleRate / 100; -int KeyboardChannelIndex(const StreamConfig& stream_config) { - if (!stream_config.has_keyboard()) { - assert(false); - return -1; +size_t NumBandsFromFramesPerChannel(size_t num_frames) { + if (num_frames == kSamplesPer32kHzChannel) { + return 2; } - - return stream_config.num_channels(); -} - -size_t NumBandsFromSamplesPerChannel(size_t num_frames) { - size_t num_bands = 1; - if (num_frames == kSamplesPer32kHzChannel || - num_frames == kSamplesPer48kHzChannel) { - num_bands = rtc::CheckedDivExact(num_frames, kSamplesPer16kHzChannel); + if (num_frames == kSamplesPer48kHzChannel) { + return 3; } - return num_bands; + return 1; } } // namespace +AudioBuffer::AudioBuffer(size_t input_rate, + size_t input_num_channels, + size_t buffer_rate, + size_t buffer_num_channels, + size_t output_rate, + size_t output_num_channels) + : AudioBuffer(static_cast(input_rate) / 100, + input_num_channels, + static_cast(buffer_rate) / 100, + buffer_num_channels, + static_cast(output_rate) / 100) {} + AudioBuffer::AudioBuffer(size_t input_num_frames, - int num_input_channels, - size_t process_num_frames, - int num_process_channels, + size_t input_num_channels, + size_t buffer_num_frames, + size_t buffer_num_channels, size_t output_num_frames) - : input_num_frames_(input_num_frames), - num_input_channels_(num_input_channels), - proc_num_frames_(process_num_frames), - num_proc_channels_(num_process_channels), - output_num_frames_(output_num_frames), - num_channels_(num_process_channels), - num_bands_(NumBandsFromSamplesPerChannel(proc_num_frames_)), - num_split_frames_(rtc::CheckedDivExact(proc_num_frames_, num_bands_)), - mixed_low_pass_valid_(false), - reference_copied_(false), - activity_(AudioFrame::kVadUnknown), - keyboard_data_(NULL), - data_(new IFChannelBuffer(proc_num_frames_, num_proc_channels_)) { - assert(input_num_frames_ > 0); - assert(proc_num_frames_ > 0); - assert(output_num_frames_ > 0); - assert(num_input_channels_ > 0); - assert(num_proc_channels_ > 0 && num_proc_channels_ <= num_input_channels_); + : input_num_frames_(input_num_frames), + input_num_channels_(input_num_channels), + buffer_num_frames_(buffer_num_frames), + buffer_num_channels_(buffer_num_channels), + output_num_frames_(output_num_frames), + output_num_channels_(0), + num_channels_(buffer_num_channels), + num_bands_(NumBandsFromFramesPerChannel(buffer_num_frames_)), + num_split_frames_(rtc::CheckedDivExact(buffer_num_frames_, num_bands_)), + data_( + new ChannelBuffer(buffer_num_frames_, buffer_num_channels_)) { + RTC_DCHECK_GT(input_num_frames_, 0); + RTC_DCHECK_GT(buffer_num_frames_, 0); + RTC_DCHECK_GT(output_num_frames_, 0); + RTC_DCHECK_GT(input_num_channels_, 0); + RTC_DCHECK_GT(buffer_num_channels_, 0); + RTC_DCHECK_LE(buffer_num_channels_, input_num_channels_); - if (input_num_frames_ != proc_num_frames_ || - output_num_frames_ != proc_num_frames_) { - // Create an intermediate buffer for resampling. - process_buffer_.reset(new ChannelBuffer(proc_num_frames_, - num_proc_channels_)); - - if (input_num_frames_ != proc_num_frames_) { - for (int i = 0; i < num_proc_channels_; ++i) { - input_resamplers_.push_back( - new PushSincResampler(input_num_frames_, - proc_num_frames_)); - } + const bool input_resampling_needed = input_num_frames_ != buffer_num_frames_; + const bool output_resampling_needed = + output_num_frames_ != buffer_num_frames_; + if (input_resampling_needed) { + for (size_t i = 0; i < buffer_num_channels_; ++i) { + input_resamplers_.push_back(std::unique_ptr( + new PushSincResampler(input_num_frames_, buffer_num_frames_))); } + } - if (output_num_frames_ != proc_num_frames_) { - for (int i = 0; i < num_proc_channels_; ++i) { - output_resamplers_.push_back( - new PushSincResampler(proc_num_frames_, - output_num_frames_)); - } + if (output_resampling_needed) { + for (size_t i = 0; i < buffer_num_channels_; ++i) { + output_resamplers_.push_back(std::unique_ptr( + new PushSincResampler(buffer_num_frames_, output_num_frames_))); } } if (num_bands_ > 1) { - split_data_.reset(new IFChannelBuffer(proc_num_frames_, - num_proc_channels_, - num_bands_)); - splitting_filter_.reset(new SplittingFilter(num_proc_channels_, - num_bands_, - proc_num_frames_)); + split_data_.reset(new ChannelBuffer( + buffer_num_frames_, buffer_num_channels_, num_bands_)); + splitting_filter_.reset(new SplittingFilter( + buffer_num_channels_, num_bands_, buffer_num_frames_)); } } AudioBuffer::~AudioBuffer() {} -void AudioBuffer::CopyFrom(const float* const* data, +void AudioBuffer::set_downmixing_to_specific_channel(size_t channel) { + downmix_by_averaging_ = false; + RTC_DCHECK_GT(input_num_channels_, channel); + channel_for_downmixing_ = std::min(channel, input_num_channels_ - 1); +} + +void AudioBuffer::set_downmixing_by_averaging() { + downmix_by_averaging_ = true; +} + +void AudioBuffer::CopyFrom(const float* const* stacked_data, const StreamConfig& stream_config) { - assert(stream_config.num_frames() == input_num_frames_); - assert(stream_config.num_channels() == num_input_channels_); - InitForNewData(); - // Initialized lazily because there's a different condition in - // DeinterleaveFrom. - const bool need_to_downmix = - num_input_channels_ > 1 && num_proc_channels_ == 1; - if (need_to_downmix && !input_buffer_) { - input_buffer_.reset( - new IFChannelBuffer(input_num_frames_, num_proc_channels_)); - } + RTC_DCHECK_EQ(stream_config.num_frames(), input_num_frames_); + RTC_DCHECK_EQ(stream_config.num_channels(), input_num_channels_); + RestoreNumChannels(); + const bool downmix_needed = input_num_channels_ > 1 && num_channels_ == 1; - if (stream_config.has_keyboard()) { - keyboard_data_ = data[KeyboardChannelIndex(stream_config)]; - } + const bool resampling_needed = input_num_frames_ != buffer_num_frames_; - // Downmix. - const float* const* data_ptr = data; - if (need_to_downmix) { - DownmixToMono(data, input_num_frames_, num_input_channels_, - input_buffer_->fbuf()->channels()[0]); - data_ptr = input_buffer_->fbuf_const()->channels(); - } + if (downmix_needed) { + RTC_DCHECK_GE(kMaxSamplesPerChannel, input_num_frames_); - // Resample. - if (input_num_frames_ != proc_num_frames_) { - for (int i = 0; i < num_proc_channels_; ++i) { - input_resamplers_[i]->Resample(data_ptr[i], - input_num_frames_, - process_buffer_->channels()[i], - proc_num_frames_); + std::array downmix; + if (downmix_by_averaging_) { + const float kOneByNumChannels = 1.f / input_num_channels_; + for (size_t i = 0; i < input_num_frames_; ++i) { + float value = stacked_data[0][i]; + for (size_t j = 1; j < input_num_channels_; ++j) { + value += stacked_data[j][i]; + } + downmix[i] = value * kOneByNumChannels; + } } - data_ptr = process_buffer_->channels(); - } + const float* downmixed_data = downmix_by_averaging_ + ? downmix.data() + : stacked_data[channel_for_downmixing_]; - // Convert to the S16 range. - for (int i = 0; i < num_proc_channels_; ++i) { - FloatToFloatS16(data_ptr[i], - proc_num_frames_, - data_->fbuf()->channels()[i]); + if (resampling_needed) { + input_resamplers_[0]->Resample(downmixed_data, input_num_frames_, + data_->channels()[0], buffer_num_frames_); + } + const float* data_to_convert = + resampling_needed ? data_->channels()[0] : downmixed_data; + FloatToFloatS16(data_to_convert, buffer_num_frames_, data_->channels()[0]); + } else { + if (resampling_needed) { + for (size_t i = 0; i < num_channels_; ++i) { + input_resamplers_[i]->Resample(stacked_data[i], input_num_frames_, + data_->channels()[i], + buffer_num_frames_); + FloatToFloatS16(data_->channels()[i], buffer_num_frames_, + data_->channels()[i]); + } + } else { + for (size_t i = 0; i < num_channels_; ++i) { + FloatToFloatS16(stacked_data[i], buffer_num_frames_, + data_->channels()[i]); + } + } } } void AudioBuffer::CopyTo(const StreamConfig& stream_config, - float* const* data) { - assert(stream_config.num_frames() == output_num_frames_); - assert(stream_config.num_channels() == num_channels_); + float* const* stacked_data) { + RTC_DCHECK_EQ(stream_config.num_frames(), output_num_frames_); - // Convert to the float range. - float* const* data_ptr = data; - if (output_num_frames_ != proc_num_frames_) { - // Convert to an intermediate buffer for subsequent resampling. - data_ptr = process_buffer_->channels(); - } - for (int i = 0; i < num_channels_; ++i) { - FloatS16ToFloat(data_->fbuf()->channels()[i], - proc_num_frames_, - data_ptr[i]); - } - - // Resample. - if (output_num_frames_ != proc_num_frames_) { - for (int i = 0; i < num_channels_; ++i) { - output_resamplers_[i]->Resample(data_ptr[i], - proc_num_frames_, - data[i], - output_num_frames_); + const bool resampling_needed = output_num_frames_ != buffer_num_frames_; + if (resampling_needed) { + for (size_t i = 0; i < num_channels_; ++i) { + FloatS16ToFloat(data_->channels()[i], buffer_num_frames_, + data_->channels()[i]); + output_resamplers_[i]->Resample(data_->channels()[i], buffer_num_frames_, + stacked_data[i], output_num_frames_); + } + } else { + for (size_t i = 0; i < num_channels_; ++i) { + FloatS16ToFloat(data_->channels()[i], buffer_num_frames_, + stacked_data[i]); } } -} -void AudioBuffer::InitForNewData() { - keyboard_data_ = NULL; - mixed_low_pass_valid_ = false; - reference_copied_ = false; - activity_ = AudioFrame::kVadUnknown; - num_channels_ = num_proc_channels_; -} - -const int16_t* const* AudioBuffer::channels_const() const { - return data_->ibuf_const()->channels(); -} - -int16_t* const* AudioBuffer::channels() { - mixed_low_pass_valid_ = false; - return data_->ibuf()->channels(); -} - -const int16_t* const* AudioBuffer::split_bands_const(int channel) const { - return split_data_.get() ? - split_data_->ibuf_const()->bands(channel) : - data_->ibuf_const()->bands(channel); -} - -int16_t* const* AudioBuffer::split_bands(int channel) { - mixed_low_pass_valid_ = false; - return split_data_.get() ? - split_data_->ibuf()->bands(channel) : - data_->ibuf()->bands(channel); -} - -const int16_t* const* AudioBuffer::split_channels_const(Band band) const { - if (split_data_.get()) { - return split_data_->ibuf_const()->channels(band); - } else { - return band == kBand0To8kHz ? data_->ibuf_const()->channels() : nullptr; + for (size_t i = num_channels_; i < stream_config.num_channels(); ++i) { + memcpy(stacked_data[i], stacked_data[0], + output_num_frames_ * sizeof(**stacked_data)); } } -int16_t* const* AudioBuffer::split_channels(Band band) { - mixed_low_pass_valid_ = false; - if (split_data_.get()) { - return split_data_->ibuf()->channels(band); - } else { - return band == kBand0To8kHz ? data_->ibuf()->channels() : nullptr; - } -} +void AudioBuffer::CopyTo(AudioBuffer* buffer) const { + RTC_DCHECK_EQ(buffer->num_frames(), output_num_frames_); -ChannelBuffer* AudioBuffer::data() { - mixed_low_pass_valid_ = false; - return data_->ibuf(); -} - -const ChannelBuffer* AudioBuffer::data() const { - return data_->ibuf_const(); -} - -ChannelBuffer* AudioBuffer::split_data() { - mixed_low_pass_valid_ = false; - return split_data_.get() ? split_data_->ibuf() : data_->ibuf(); -} - -const ChannelBuffer* AudioBuffer::split_data() const { - return split_data_.get() ? split_data_->ibuf_const() : data_->ibuf_const(); -} - -const float* const* AudioBuffer::channels_const_f() const { - return data_->fbuf_const()->channels(); -} - -float* const* AudioBuffer::channels_f() { - mixed_low_pass_valid_ = false; - return data_->fbuf()->channels(); -} - -const float* const* AudioBuffer::split_bands_const_f(int channel) const { - return split_data_.get() ? - split_data_->fbuf_const()->bands(channel) : - data_->fbuf_const()->bands(channel); -} - -float* const* AudioBuffer::split_bands_f(int channel) { - mixed_low_pass_valid_ = false; - return split_data_.get() ? - split_data_->fbuf()->bands(channel) : - data_->fbuf()->bands(channel); -} - -const float* const* AudioBuffer::split_channels_const_f(Band band) const { - if (split_data_.get()) { - return split_data_->fbuf_const()->channels(band); - } else { - return band == kBand0To8kHz ? data_->fbuf_const()->channels() : nullptr; - } -} - -float* const* AudioBuffer::split_channels_f(Band band) { - mixed_low_pass_valid_ = false; - if (split_data_.get()) { - return split_data_->fbuf()->channels(band); - } else { - return band == kBand0To8kHz ? data_->fbuf()->channels() : nullptr; - } -} - -ChannelBuffer* AudioBuffer::data_f() { - mixed_low_pass_valid_ = false; - return data_->fbuf(); -} - -const ChannelBuffer* AudioBuffer::data_f() const { - return data_->fbuf_const(); -} - -ChannelBuffer* AudioBuffer::split_data_f() { - mixed_low_pass_valid_ = false; - return split_data_.get() ? split_data_->fbuf() : data_->fbuf(); -} - -const ChannelBuffer* AudioBuffer::split_data_f() const { - return split_data_.get() ? split_data_->fbuf_const() : data_->fbuf_const(); -} - -const int16_t* AudioBuffer::mixed_low_pass_data() { - if (num_proc_channels_ == 1) { - return split_bands_const(0)[kBand0To8kHz]; - } - - if (!mixed_low_pass_valid_) { - if (!mixed_low_pass_channels_.get()) { - mixed_low_pass_channels_.reset( - new ChannelBuffer(num_split_frames_, 1)); + const bool resampling_needed = output_num_frames_ != buffer_num_frames_; + if (resampling_needed) { + for (size_t i = 0; i < num_channels_; ++i) { + output_resamplers_[i]->Resample(data_->channels()[i], buffer_num_frames_, + buffer->channels()[i], + buffer->num_frames()); + } + } else { + for (size_t i = 0; i < num_channels_; ++i) { + memcpy(buffer->channels()[i], data_->channels()[i], + buffer_num_frames_ * sizeof(**buffer->channels())); } - - DownmixToMono(split_channels_const(kBand0To8kHz), - num_split_frames_, num_channels_, - mixed_low_pass_channels_->channels()[0]); - mixed_low_pass_valid_ = true; - } - return mixed_low_pass_channels_->channels()[0]; -} - -const int16_t* AudioBuffer::low_pass_reference(int channel) const { - if (!reference_copied_) { - return NULL; } - return low_pass_reference_channels_->channels()[channel]; + for (size_t i = num_channels_; i < buffer->num_channels(); ++i) { + memcpy(buffer->channels()[i], buffer->channels()[0], + output_num_frames_ * sizeof(**buffer->channels())); + } } -const float* AudioBuffer::keyboard_data() const { - return keyboard_data_; +void AudioBuffer::RestoreNumChannels() { + num_channels_ = buffer_num_channels_; + data_->set_num_channels(buffer_num_channels_); + if (split_data_.get()) { + split_data_->set_num_channels(buffer_num_channels_); + } } -void AudioBuffer::set_activity(AudioFrame::VADActivity activity) { - activity_ = activity; -} - -AudioFrame::VADActivity AudioBuffer::activity() const { - return activity_; -} - -int AudioBuffer::num_channels() const { - return num_channels_; -} - -void AudioBuffer::set_num_channels(int num_channels) { +void AudioBuffer::set_num_channels(size_t num_channels) { + RTC_DCHECK_GE(buffer_num_channels_, num_channels); num_channels_ = num_channels; -} - -size_t AudioBuffer::num_frames() const { - return proc_num_frames_; -} - -size_t AudioBuffer::num_frames_per_band() const { - return num_split_frames_; -} - -size_t AudioBuffer::num_keyboard_frames() const { - // We don't resample the keyboard channel. - return input_num_frames_; -} - -size_t AudioBuffer::num_bands() const { - return num_bands_; + data_->set_num_channels(num_channels); + if (split_data_.get()) { + split_data_->set_num_channels(num_channels); + } } // The resampler is only for supporting 48kHz to 16kHz in the reverse stream. -void AudioBuffer::DeinterleaveFrom(AudioFrame* frame) { - assert(frame->num_channels_ == num_input_channels_); - assert(frame->samples_per_channel_ == input_num_frames_); - InitForNewData(); - // Initialized lazily because there's a different condition in CopyFrom. - if ((input_num_frames_ != proc_num_frames_) && !input_buffer_) { - input_buffer_.reset( - new IFChannelBuffer(input_num_frames_, num_proc_channels_)); - } - activity_ = frame->vad_activity_; +void AudioBuffer::CopyFrom(const int16_t* const interleaved_data, + const StreamConfig& stream_config) { + RTC_DCHECK_EQ(stream_config.num_channels(), input_num_channels_); + RTC_DCHECK_EQ(stream_config.num_frames(), input_num_frames_); + RestoreNumChannels(); - int16_t* const* deinterleaved; - if (input_num_frames_ == proc_num_frames_) { - deinterleaved = data_->ibuf()->channels(); - } else { - deinterleaved = input_buffer_->ibuf()->channels(); - } - if (num_proc_channels_ == 1) { - // Downmix and deinterleave simultaneously. - DownmixInterleavedToMono(frame->data_, input_num_frames_, - num_input_channels_, deinterleaved[0]); - } else { - assert(num_proc_channels_ == num_input_channels_); - Deinterleave(frame->data_, - input_num_frames_, - num_proc_channels_, - deinterleaved); - } + const bool resampling_required = input_num_frames_ != buffer_num_frames_; - // Resample. - if (input_num_frames_ != proc_num_frames_) { - for (int i = 0; i < num_proc_channels_; ++i) { - input_resamplers_[i]->Resample(input_buffer_->fbuf_const()->channels()[i], - input_num_frames_, - data_->fbuf()->channels()[i], - proc_num_frames_); + const int16_t* interleaved = interleaved_data; + if (num_channels_ == 1) { + if (input_num_channels_ == 1) { + if (resampling_required) { + std::array float_buffer; + S16ToFloatS16(interleaved, input_num_frames_, float_buffer.data()); + input_resamplers_[0]->Resample(float_buffer.data(), input_num_frames_, + data_->channels()[0], + buffer_num_frames_); + } else { + S16ToFloatS16(interleaved, input_num_frames_, data_->channels()[0]); + } + } else { + std::array float_buffer; + float* downmixed_data = + resampling_required ? float_buffer.data() : data_->channels()[0]; + if (downmix_by_averaging_) { + for (size_t j = 0, k = 0; j < input_num_frames_; ++j) { + int32_t sum = 0; + for (size_t i = 0; i < input_num_channels_; ++i, ++k) { + sum += interleaved[k]; + } + downmixed_data[j] = sum / static_cast(input_num_channels_); + } + } else { + for (size_t j = 0, k = channel_for_downmixing_; j < input_num_frames_; + ++j, k += input_num_channels_) { + downmixed_data[j] = interleaved[k]; + } + } + + if (resampling_required) { + input_resamplers_[0]->Resample(downmixed_data, input_num_frames_, + data_->channels()[0], + buffer_num_frames_); + } + } + } else { + auto deinterleave_channel = [](size_t channel, size_t num_channels, + size_t samples_per_channel, const int16_t* x, + float* y) { + for (size_t j = 0, k = channel; j < samples_per_channel; + ++j, k += num_channels) { + y[j] = x[k]; + } + }; + + if (resampling_required) { + std::array float_buffer; + for (size_t i = 0; i < num_channels_; ++i) { + deinterleave_channel(i, num_channels_, input_num_frames_, interleaved, + float_buffer.data()); + input_resamplers_[i]->Resample(float_buffer.data(), input_num_frames_, + data_->channels()[i], + buffer_num_frames_); + } + } else { + for (size_t i = 0; i < num_channels_; ++i) { + deinterleave_channel(i, num_channels_, input_num_frames_, interleaved, + data_->channels()[i]); + } } } } -void AudioBuffer::InterleaveTo(AudioFrame* frame, bool data_changed) { - frame->vad_activity_ = activity_; - if (!data_changed) { - return; - } +void AudioBuffer::CopyTo(const StreamConfig& stream_config, + int16_t* const interleaved_data) { + const size_t config_num_channels = stream_config.num_channels(); - assert(frame->num_channels_ == num_channels_ || num_channels_ == 1); - assert(frame->samples_per_channel_ == output_num_frames_); + RTC_DCHECK(config_num_channels == num_channels_ || num_channels_ == 1); + RTC_DCHECK_EQ(stream_config.num_frames(), output_num_frames_); - // Resample if necessary. - IFChannelBuffer* data_ptr = data_.get(); - if (proc_num_frames_ != output_num_frames_) { - if (!output_buffer_) { - output_buffer_.reset( - new IFChannelBuffer(output_num_frames_, num_channels_)); + const bool resampling_required = buffer_num_frames_ != output_num_frames_; + + int16_t* interleaved = interleaved_data; + if (num_channels_ == 1) { + std::array float_buffer; + + if (resampling_required) { + output_resamplers_[0]->Resample(data_->channels()[0], buffer_num_frames_, + float_buffer.data(), output_num_frames_); } - for (int i = 0; i < num_channels_; ++i) { - output_resamplers_[i]->Resample( - data_->fbuf()->channels()[i], proc_num_frames_, - output_buffer_->fbuf()->channels()[i], output_num_frames_); - } - data_ptr = output_buffer_.get(); - } + const float* deinterleaved = + resampling_required ? float_buffer.data() : data_->channels()[0]; - if (frame->num_channels_ == num_channels_) { - Interleave(data_ptr->ibuf()->channels(), proc_num_frames_, num_channels_, - frame->data_); + if (config_num_channels == 1) { + for (size_t j = 0; j < output_num_frames_; ++j) { + interleaved[j] = FloatS16ToS16(deinterleaved[j]); + } + } else { + for (size_t i = 0, k = 0; i < output_num_frames_; ++i) { + float tmp = FloatS16ToS16(deinterleaved[i]); + for (size_t j = 0; j < config_num_channels; ++j, ++k) { + interleaved[k] = tmp; + } + } + } } else { - UpmixMonoToInterleaved(data_ptr->ibuf()->channels()[0], proc_num_frames_, - frame->num_channels_, frame->data_); - } -} + auto interleave_channel = [](size_t channel, size_t num_channels, + size_t samples_per_channel, const float* x, + int16_t* y) { + for (size_t k = 0, j = channel; k < samples_per_channel; + ++k, j += num_channels) { + y[j] = FloatS16ToS16(x[k]); + } + }; -void AudioBuffer::CopyLowPassToReference() { - reference_copied_ = true; - if (!low_pass_reference_channels_.get() || - low_pass_reference_channels_->num_channels() != num_channels_) { - low_pass_reference_channels_.reset( - new ChannelBuffer(num_split_frames_, - num_proc_channels_)); - } - for (int i = 0; i < num_proc_channels_; i++) { - memcpy(low_pass_reference_channels_->channels()[i], - split_bands_const(i)[kBand0To8kHz], - low_pass_reference_channels_->num_frames_per_band() * - sizeof(split_bands_const(i)[kBand0To8kHz][0])); + if (resampling_required) { + for (size_t i = 0; i < num_channels_; ++i) { + std::array float_buffer; + output_resamplers_[i]->Resample(data_->channels()[i], + buffer_num_frames_, float_buffer.data(), + output_num_frames_); + interleave_channel(i, config_num_channels, output_num_frames_, + float_buffer.data(), interleaved); + } + } else { + for (size_t i = 0; i < num_channels_; ++i) { + interleave_channel(i, config_num_channels, output_num_frames_, + data_->channels()[i], interleaved); + } + } + + for (size_t i = num_channels_; i < config_num_channels; ++i) { + for (size_t j = 0, k = i, n = num_channels_; j < output_num_frames_; + ++j, k += config_num_channels, n += config_num_channels) { + interleaved[k] = interleaved[n]; + } + } } } @@ -459,4 +377,31 @@ void AudioBuffer::MergeFrequencyBands() { splitting_filter_->Synthesis(split_data_.get(), data_.get()); } +void AudioBuffer::ExportSplitChannelData( + size_t channel, + int16_t* const* split_band_data) const { + for (size_t k = 0; k < num_bands(); ++k) { + const float* band_data = split_bands_const(channel)[k]; + + RTC_DCHECK(split_band_data[k]); + RTC_DCHECK(band_data); + for (size_t i = 0; i < num_frames_per_band(); ++i) { + split_band_data[k][i] = FloatS16ToS16(band_data[i]); + } + } +} + +void AudioBuffer::ImportSplitChannelData( + size_t channel, + const int16_t* const* split_band_data) { + for (size_t k = 0; k < num_bands(); ++k) { + float* band_data = split_bands(channel)[k]; + RTC_DCHECK(split_band_data[k]); + RTC_DCHECK(band_data); + for (size_t i = 0; i < num_frames_per_band(); ++i) { + band_data[i] = split_band_data[k][i]; + } + } +} + } // namespace webrtc diff --git a/webrtc/modules/audio_processing/audio_buffer.h b/webrtc/modules/audio_processing/audio_buffer.h index 864633f..3eecf0d 100644 --- a/webrtc/modules/audio_processing/audio_buffer.h +++ b/webrtc/modules/audio_processing/audio_buffer.h @@ -8,156 +8,171 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ +#ifndef MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/channel_buffer.h" -#include "webrtc/modules/audio_processing/include/audio_processing.h" -#include "webrtc/modules/audio_processing/splitting_filter.h" -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/system_wrappers/include/scoped_vector.h" -#include "webrtc/typedefs.h" +#include +#include + +#include +#include + +#include "common_audio/channel_buffer.h" +#include "modules/audio_processing/include/audio_processing.h" namespace webrtc { class PushSincResampler; -class IFChannelBuffer; +class SplittingFilter; -enum Band { - kBand0To8kHz = 0, - kBand8To16kHz = 1, - kBand16To24kHz = 2 -}; +enum Band { kBand0To8kHz = 0, kBand8To16kHz = 1, kBand16To24kHz = 2 }; +// Stores any audio data in a way that allows the audio processing module to +// operate on it in a controlled manner. class AudioBuffer { public: - // TODO(ajm): Switch to take ChannelLayouts. + static const int kSplitBandSize = 160; + static const size_t kMaxSampleRate = 384000; + AudioBuffer(size_t input_rate, + size_t input_num_channels, + size_t buffer_rate, + size_t buffer_num_channels, + size_t output_rate, + size_t output_num_channels); + + // The constructor below will be deprecated. AudioBuffer(size_t input_num_frames, - int num_input_channels, - size_t process_num_frames, - int num_process_channels, + size_t input_num_channels, + size_t buffer_num_frames, + size_t buffer_num_channels, size_t output_num_frames); virtual ~AudioBuffer(); - int num_channels() const; - void set_num_channels(int num_channels); - size_t num_frames() const; - size_t num_frames_per_band() const; - size_t num_keyboard_frames() const; - size_t num_bands() const; + AudioBuffer(const AudioBuffer&) = delete; + AudioBuffer& operator=(const AudioBuffer&) = delete; - // Returns a pointer array to the full-band channels. + // Specify that downmixing should be done by selecting a single channel. + void set_downmixing_to_specific_channel(size_t channel); + + // Specify that downmixing should be done by averaging all channels,. + void set_downmixing_by_averaging(); + + // Set the number of channels in the buffer. The specified number of channels + // cannot be larger than the specified buffer_num_channels. The number is also + // reset at each call to CopyFrom or InterleaveFrom. + void set_num_channels(size_t num_channels); + + size_t num_channels() const { return num_channels_; } + size_t num_frames() const { return buffer_num_frames_; } + size_t num_frames_per_band() const { return num_split_frames_; } + size_t num_bands() const { return num_bands_; } + + // Returns pointer arrays to the full-band channels. // Usage: // channels()[channel][sample]. // Where: - // 0 <= channel < |num_proc_channels_| - // 0 <= sample < |proc_num_frames_| - int16_t* const* channels(); - const int16_t* const* channels_const() const; - float* const* channels_f(); - const float* const* channels_const_f() const; + // 0 <= channel < |buffer_num_channels_| + // 0 <= sample < |buffer_num_frames_| + float* const* channels() { return data_->channels(); } + const float* const* channels_const() const { return data_->channels(); } - // Returns a pointer array to the bands for a specific channel. + // Returns pointer arrays to the bands for a specific channel. // Usage: // split_bands(channel)[band][sample]. // Where: - // 0 <= channel < |num_proc_channels_| + // 0 <= channel < |buffer_num_channels_| // 0 <= band < |num_bands_| // 0 <= sample < |num_split_frames_| - int16_t* const* split_bands(int channel); - const int16_t* const* split_bands_const(int channel) const; - float* const* split_bands_f(int channel); - const float* const* split_bands_const_f(int channel) const; + const float* const* split_bands_const(size_t channel) const { + return split_data_.get() ? split_data_->bands(channel) + : data_->bands(channel); + } + float* const* split_bands(size_t channel) { + return split_data_.get() ? split_data_->bands(channel) + : data_->bands(channel); + } // Returns a pointer array to the channels for a specific band. // Usage: // split_channels(band)[channel][sample]. // Where: // 0 <= band < |num_bands_| - // 0 <= channel < |num_proc_channels_| + // 0 <= channel < |buffer_num_channels_| // 0 <= sample < |num_split_frames_| - int16_t* const* split_channels(Band band); - const int16_t* const* split_channels_const(Band band) const; - float* const* split_channels_f(Band band); - const float* const* split_channels_const_f(Band band) const; + const float* const* split_channels_const(Band band) const { + if (split_data_.get()) { + return split_data_->channels(band); + } else { + return band == kBand0To8kHz ? data_->channels() : nullptr; + } + } - // Returns a pointer to the ChannelBuffer that encapsulates the full-band - // data. - ChannelBuffer* data(); - const ChannelBuffer* data() const; - ChannelBuffer* data_f(); - const ChannelBuffer* data_f() const; + // Copies data into the buffer. + void CopyFrom(const int16_t* const interleaved_data, + const StreamConfig& stream_config); + void CopyFrom(const float* const* stacked_data, + const StreamConfig& stream_config); - // Returns a pointer to the ChannelBuffer that encapsulates the split data. - ChannelBuffer* split_data(); - const ChannelBuffer* split_data() const; - ChannelBuffer* split_data_f(); - const ChannelBuffer* split_data_f() const; + // Copies data from the buffer. + void CopyTo(const StreamConfig& stream_config, + int16_t* const interleaved_data); + void CopyTo(const StreamConfig& stream_config, float* const* stacked_data); + void CopyTo(AudioBuffer* buffer) const; - // Returns a pointer to the low-pass data downmixed to mono. If this data - // isn't already available it re-calculates it. - const int16_t* mixed_low_pass_data(); - const int16_t* low_pass_reference(int channel) const; - - const float* keyboard_data() const; - - void set_activity(AudioFrame::VADActivity activity); - AudioFrame::VADActivity activity() const; - - // Use for int16 interleaved data. - void DeinterleaveFrom(AudioFrame* audioFrame); - // If |data_changed| is false, only the non-audio data members will be copied - // to |frame|. - void InterleaveTo(AudioFrame* frame, bool data_changed); - - // Use for float deinterleaved data. - void CopyFrom(const float* const* data, const StreamConfig& stream_config); - void CopyTo(const StreamConfig& stream_config, float* const* data); - void CopyLowPassToReference(); - - // Splits the signal into different bands. + // Splits the buffer data into frequency bands. void SplitIntoFrequencyBands(); - // Recombine the different bands into one signal. + + // Recombines the frequency bands into a full-band signal. void MergeFrequencyBands(); + // Copies the split bands data into the integer two-dimensional array. + void ExportSplitChannelData(size_t channel, + int16_t* const* split_band_data) const; + + // Copies the data in the integer two-dimensional array into the split_bands + // data. + void ImportSplitChannelData(size_t channel, + const int16_t* const* split_band_data); + + static const size_t kMaxSplitFrameLength = 160; + static const size_t kMaxNumBands = 3; + + // Deprecated methods, will be removed soon. + float* const* channels_f() { return channels(); } + const float* const* channels_const_f() const { return channels_const(); } + const float* const* split_bands_const_f(size_t channel) const { + return split_bands_const(channel); + } + float* const* split_bands_f(size_t channel) { return split_bands(channel); } + const float* const* split_channels_const_f(Band band) const { + return split_channels_const(band); + } + private: - // Called from DeinterleaveFrom() and CopyFrom(). - void InitForNewData(); + FRIEND_TEST_ALL_PREFIXES(AudioBufferTest, + SetNumChannelsSetsChannelBuffersNumChannels); + void RestoreNumChannels(); - // The audio is passed into DeinterleaveFrom() or CopyFrom() with input - // format (samples per channel and number of channels). const size_t input_num_frames_; - const int num_input_channels_; - // The audio is stored by DeinterleaveFrom() or CopyFrom() with processing - // format. - const size_t proc_num_frames_; - const int num_proc_channels_; - // The audio is returned by InterleaveTo() and CopyTo() with output samples - // per channels and the current number of channels. This last one can be - // changed at any time using set_num_channels(). + const size_t input_num_channels_; + const size_t buffer_num_frames_; + const size_t buffer_num_channels_; const size_t output_num_frames_; - int num_channels_; + const size_t output_num_channels_; + size_t num_channels_; size_t num_bands_; size_t num_split_frames_; - bool mixed_low_pass_valid_; - bool reference_copied_; - AudioFrame::VADActivity activity_; - const float* keyboard_data_; - rtc::scoped_ptr data_; - rtc::scoped_ptr split_data_; - rtc::scoped_ptr splitting_filter_; - rtc::scoped_ptr > mixed_low_pass_channels_; - rtc::scoped_ptr > low_pass_reference_channels_; - rtc::scoped_ptr input_buffer_; - rtc::scoped_ptr output_buffer_; - rtc::scoped_ptr > process_buffer_; - ScopedVector input_resamplers_; - ScopedVector output_resamplers_; + std::unique_ptr> data_; + std::unique_ptr> split_data_; + std::unique_ptr splitting_filter_; + std::vector> input_resamplers_; + std::vector> output_resamplers_; + bool downmix_by_averaging_ = true; + size_t channel_for_downmixing_ = 0; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ +#endif // MODULES_AUDIO_PROCESSING_AUDIO_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/audio_processing_builder_impl.cc b/webrtc/modules/audio_processing/audio_processing_builder_impl.cc new file mode 100644 index 0000000..f55c915 --- /dev/null +++ b/webrtc/modules/audio_processing/audio_processing_builder_impl.cc @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 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 "modules/audio_processing/include/audio_processing.h" + +#include + +#include "modules/audio_processing/audio_processing_impl.h" +#include "rtc_base/ref_counted_object.h" + +namespace webrtc { + +AudioProcessingBuilder::AudioProcessingBuilder() = default; +AudioProcessingBuilder::~AudioProcessingBuilder() = default; + +AudioProcessing* AudioProcessingBuilder::Create() { + webrtc::Config config; + return Create(config); +} + +AudioProcessing* AudioProcessingBuilder::Create(const webrtc::Config& config) { +#ifdef WEBRTC_EXCLUDE_AUDIO_PROCESSING_MODULE + + // Implementation returning a null pointer for using when the APM is excluded + // from the build.. + return nullptr; + +#else + + // Standard implementation. + return new rtc::RefCountedObject( + config, std::move(capture_post_processing_), + std::move(render_pre_processing_), std::move(echo_control_factory_), + std::move(echo_detector_), std::move(capture_analyzer_)); +#endif +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/audio_processing_impl.cc b/webrtc/modules/audio_processing/audio_processing_impl.cc index c657415..67208df 100644 --- a/webrtc/modules/audio_processing/audio_processing_impl.cc +++ b/webrtc/modules/audio_processing/audio_processing_impl.cc @@ -8,48 +8,36 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/audio_processing_impl.h" +#include "modules/audio_processing/audio_processing_impl.h" -#include #include +#include +#include +#include +#include +#include -#include "webrtc/base/checks.h" -#include "webrtc/base/platform_file.h" -#include "webrtc/common_audio/audio_converter.h" -#include "webrtc/common_audio/channel_buffer.h" -#include "webrtc/common_audio/include/audio_util.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -extern "C" { -#include "webrtc/modules/audio_processing/aec/aec_core.h" -} -#include "webrtc/modules/audio_processing/agc/agc_manager_direct.h" -#include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h" -#include "webrtc/modules/audio_processing/common.h" -#include "webrtc/modules/audio_processing/echo_cancellation_impl.h" -#include "webrtc/modules/audio_processing/echo_control_mobile_impl.h" -#include "webrtc/modules/audio_processing/gain_control_impl.h" -#include "webrtc/modules/audio_processing/high_pass_filter_impl.h" -#include "webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.h" -#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/transient/transient_suppressor.h" -#include "webrtc/modules/audio_processing/voice_detection_impl.h" -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" -#include "webrtc/system_wrappers/include/file_wrapper.h" -#include "webrtc/system_wrappers/include/logging.h" -#include "webrtc/system_wrappers/include/metrics.h" - -#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP -// Files generated at build-time by the protobuf compiler. -#ifdef WEBRTC_ANDROID_PLATFORM_BUILD -#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h" -#else -#include "webrtc/audio_processing/debug.pb.h" -#endif -#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/audio/audio_frame.h" +#include "common_audio/audio_converter.h" +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/aec_dump/aec_dump_factory.h" +#include "modules/audio_processing/agc2/gain_applier.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/common.h" +#include "modules/audio_processing/include/audio_frame_view.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "modules/audio_processing/optionally_built_submodule_creators.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" +#include "rtc_base/constructor_magic.h" +#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/time_utils.h" +#include "rtc_base/trace_event.h" +#include "system_wrappers/include/field_trial.h" +#include "system_wrappers/include/metrics.h" #define RETURN_ON_ERR(expr) \ do { \ @@ -60,6 +48,9 @@ extern "C" { } while (0) namespace webrtc { + +constexpr int kRuntimeSettingQueueSize = 100; + namespace { static bool LayoutHasKeyboard(AudioProcessing::ChannelLayout layout) { @@ -72,316 +63,384 @@ static bool LayoutHasKeyboard(AudioProcessing::ChannelLayout layout) { return true; } - assert(false); + RTC_NOTREACHED(); return false; } +bool SampleRateSupportsMultiBand(int sample_rate_hz) { + return sample_rate_hz == AudioProcessing::kSampleRate32kHz || + sample_rate_hz == AudioProcessing::kSampleRate48kHz; +} + +// Checks whether the high-pass filter should be done in the full-band. +bool EnforceSplitBandHpf() { + return field_trial::IsEnabled("WebRTC-FullBandHpfKillSwitch"); +} + +// Checks whether AEC3 should be allowed to decide what the default +// configuration should be based on the render and capture channel configuration +// at hand. +bool UseSetupSpecificDefaultAec3Congfig() { + return !field_trial::IsEnabled( + "WebRTC-Aec3SetupSpecificDefaultConfigDefaultsKillSwitch"); +} + +// Identify the native processing rate that best handles a sample rate. +int SuitableProcessRate(int minimum_rate, + int max_splitting_rate, + bool band_splitting_required) { + const int uppermost_native_rate = + band_splitting_required ? max_splitting_rate : 48000; + for (auto rate : {16000, 32000, 48000}) { + if (rate >= uppermost_native_rate) { + return uppermost_native_rate; + } + if (rate >= minimum_rate) { + return rate; + } + } + RTC_NOTREACHED(); + return uppermost_native_rate; +} + +GainControl::Mode Agc1ConfigModeToInterfaceMode( + AudioProcessing::Config::GainController1::Mode mode) { + using Agc1Config = AudioProcessing::Config::GainController1; + switch (mode) { + case Agc1Config::kAdaptiveAnalog: + return GainControl::kAdaptiveAnalog; + case Agc1Config::kAdaptiveDigital: + return GainControl::kAdaptiveDigital; + case Agc1Config::kFixedDigital: + return GainControl::kFixedDigital; + } +} + +// Maximum lengths that frame of samples being passed from the render side to +// the capture side can have (does not apply to AEC3). +static const size_t kMaxAllowedValuesOfSamplesPerBand = 160; +static const size_t kMaxAllowedValuesOfSamplesPerFrame = 480; + +// Maximum number of frames to buffer in the render queue. +// TODO(peah): Decrease this once we properly handle hugely unbalanced +// reverse and forward call numbers. +static const size_t kMaxNumFramesToBuffer = 100; } // namespace // Throughout webrtc, it's assumed that success is represented by zero. static_assert(AudioProcessing::kNoError == 0, "kNoError must be zero"); -// This class has two main functionalities: -// -// 1) It is returned instead of the real GainControl after the new AGC has been -// enabled in order to prevent an outside user from overriding compression -// settings. It doesn't do anything in its implementation, except for -// delegating the const methods and Enable calls to the real GainControl, so -// AGC can still be disabled. -// -// 2) It is injected into AgcManagerDirect and implements volume callbacks for -// getting and setting the volume level. It just caches this value to be used -// in VoiceEngine later. -class GainControlForNewAgc : public GainControl, public VolumeCallbacks { - public: - explicit GainControlForNewAgc(GainControlImpl* gain_control) - : real_gain_control_(gain_control), volume_(0) {} +AudioProcessingImpl::SubmoduleStates::SubmoduleStates( + bool capture_post_processor_enabled, + bool render_pre_processor_enabled, + bool capture_analyzer_enabled) + : capture_post_processor_enabled_(capture_post_processor_enabled), + render_pre_processor_enabled_(render_pre_processor_enabled), + capture_analyzer_enabled_(capture_analyzer_enabled) {} - // GainControl implementation. - int Enable(bool enable) override { - return real_gain_control_->Enable(enable); - } - bool is_enabled() const override { return real_gain_control_->is_enabled(); } - int set_stream_analog_level(int level) override { - volume_ = level; - return AudioProcessing::kNoError; - } - int stream_analog_level() override { return volume_; } - int set_mode(Mode mode) override { return AudioProcessing::kNoError; } - Mode mode() const override { return GainControl::kAdaptiveAnalog; } - int set_target_level_dbfs(int level) override { - return AudioProcessing::kNoError; - } - int target_level_dbfs() const override { - return real_gain_control_->target_level_dbfs(); - } - int set_compression_gain_db(int gain) override { - return AudioProcessing::kNoError; - } - int compression_gain_db() const override { - return real_gain_control_->compression_gain_db(); - } - int enable_limiter(bool enable) override { return AudioProcessing::kNoError; } - bool is_limiter_enabled() const override { - return real_gain_control_->is_limiter_enabled(); - } - int set_analog_level_limits(int minimum, int maximum) override { - return AudioProcessing::kNoError; - } - int analog_level_minimum() const override { - return real_gain_control_->analog_level_minimum(); - } - int analog_level_maximum() const override { - return real_gain_control_->analog_level_maximum(); - } - bool stream_is_saturated() const override { - return real_gain_control_->stream_is_saturated(); +bool AudioProcessingImpl::SubmoduleStates::Update( + bool high_pass_filter_enabled, + bool mobile_echo_controller_enabled, + bool residual_echo_detector_enabled, + bool noise_suppressor_enabled, + bool adaptive_gain_controller_enabled, + bool gain_controller2_enabled, + bool pre_amplifier_enabled, + bool echo_controller_enabled, + bool voice_detector_enabled, + bool transient_suppressor_enabled) { + bool changed = false; + changed |= (high_pass_filter_enabled != high_pass_filter_enabled_); + changed |= + (mobile_echo_controller_enabled != mobile_echo_controller_enabled_); + changed |= + (residual_echo_detector_enabled != residual_echo_detector_enabled_); + changed |= (noise_suppressor_enabled != noise_suppressor_enabled_); + changed |= + (adaptive_gain_controller_enabled != adaptive_gain_controller_enabled_); + changed |= (gain_controller2_enabled != gain_controller2_enabled_); + changed |= (pre_amplifier_enabled_ != pre_amplifier_enabled); + changed |= (echo_controller_enabled != echo_controller_enabled_); + changed |= (voice_detector_enabled != voice_detector_enabled_); + changed |= (transient_suppressor_enabled != transient_suppressor_enabled_); + if (changed) { + high_pass_filter_enabled_ = high_pass_filter_enabled; + mobile_echo_controller_enabled_ = mobile_echo_controller_enabled; + residual_echo_detector_enabled_ = residual_echo_detector_enabled; + noise_suppressor_enabled_ = noise_suppressor_enabled; + adaptive_gain_controller_enabled_ = adaptive_gain_controller_enabled; + gain_controller2_enabled_ = gain_controller2_enabled; + pre_amplifier_enabled_ = pre_amplifier_enabled; + echo_controller_enabled_ = echo_controller_enabled; + voice_detector_enabled_ = voice_detector_enabled; + transient_suppressor_enabled_ = transient_suppressor_enabled; } - // VolumeCallbacks implementation. - void SetMicVolume(int volume) override { volume_ = volume; } - int GetMicVolume() override { return volume_; } - - private: - GainControl* real_gain_control_; - int volume_; -}; - -const int AudioProcessing::kNativeSampleRatesHz[] = { - AudioProcessing::kSampleRate8kHz, - AudioProcessing::kSampleRate16kHz, - AudioProcessing::kSampleRate32kHz, - AudioProcessing::kSampleRate48kHz}; -const size_t AudioProcessing::kNumNativeSampleRates = - arraysize(AudioProcessing::kNativeSampleRatesHz); -const int AudioProcessing::kMaxNativeSampleRateHz = AudioProcessing:: - kNativeSampleRatesHz[AudioProcessing::kNumNativeSampleRates - 1]; -const int AudioProcessing::kMaxAECMSampleRateHz = kSampleRate16kHz; - -AudioProcessing* AudioProcessing::Create() { - Config config; - return Create(config, nullptr); + changed |= first_update_; + first_update_ = false; + return changed; } -AudioProcessing* AudioProcessing::Create(const Config& config) { - return Create(config, nullptr); +bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandSubModulesActive() + const { + return CaptureMultiBandProcessingPresent() || voice_detector_enabled_; } -AudioProcessing* AudioProcessing::Create(const Config& config, - Beamformer* beamformer) { - AudioProcessingImpl* apm = new AudioProcessingImpl(config, beamformer); - if (apm->Initialize() != kNoError) { - delete apm; - apm = NULL; +bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandProcessingPresent() + const { + // If echo controller is present, assume it performs active processing. + return CaptureMultiBandProcessingActive(/*ec_processing_active=*/true); +} + +bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandProcessingActive( + bool ec_processing_active) const { + return high_pass_filter_enabled_ || mobile_echo_controller_enabled_ || + noise_suppressor_enabled_ || adaptive_gain_controller_enabled_ || + (echo_controller_enabled_ && ec_processing_active); +} + +bool AudioProcessingImpl::SubmoduleStates::CaptureFullBandProcessingActive() + const { + return gain_controller2_enabled_ || capture_post_processor_enabled_ || + pre_amplifier_enabled_; +} + +bool AudioProcessingImpl::SubmoduleStates::CaptureAnalyzerActive() const { + return capture_analyzer_enabled_; +} + +bool AudioProcessingImpl::SubmoduleStates::RenderMultiBandSubModulesActive() + const { + return RenderMultiBandProcessingActive() || mobile_echo_controller_enabled_ || + adaptive_gain_controller_enabled_ || echo_controller_enabled_; +} + +bool AudioProcessingImpl::SubmoduleStates::RenderFullBandProcessingActive() + const { + return render_pre_processor_enabled_; +} + +bool AudioProcessingImpl::SubmoduleStates::RenderMultiBandProcessingActive() + const { + return false; +} + +bool AudioProcessingImpl::SubmoduleStates::HighPassFilteringRequired() const { + return high_pass_filter_enabled_ || mobile_echo_controller_enabled_ || + noise_suppressor_enabled_; +} + +AudioProcessingImpl::AudioProcessingImpl(const webrtc::Config& config) + : AudioProcessingImpl(config, + /*capture_post_processor=*/nullptr, + /*render_pre_processor=*/nullptr, + /*echo_control_factory=*/nullptr, + /*echo_detector=*/nullptr, + /*capture_analyzer=*/nullptr) {} + +int AudioProcessingImpl::instance_count_ = 0; + +AudioProcessingImpl::AudioProcessingImpl( + const webrtc::Config& config, + std::unique_ptr capture_post_processor, + std::unique_ptr render_pre_processor, + std::unique_ptr echo_control_factory, + rtc::scoped_refptr echo_detector, + std::unique_ptr capture_analyzer) + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + use_setup_specific_default_aec3_config_( + UseSetupSpecificDefaultAec3Congfig()), + capture_runtime_settings_(kRuntimeSettingQueueSize), + render_runtime_settings_(kRuntimeSettingQueueSize), + capture_runtime_settings_enqueuer_(&capture_runtime_settings_), + render_runtime_settings_enqueuer_(&render_runtime_settings_), + echo_control_factory_(std::move(echo_control_factory)), + submodule_states_(!!capture_post_processor, + !!render_pre_processor, + !!capture_analyzer), + submodules_(std::move(capture_post_processor), + std::move(render_pre_processor), + std::move(echo_detector), + std::move(capture_analyzer)), + constants_(!field_trial::IsEnabled( + "WebRTC-ApmExperimentalMultiChannelRenderKillSwitch"), + !field_trial::IsEnabled( + "WebRTC-ApmExperimentalMultiChannelCaptureKillSwitch"), + EnforceSplitBandHpf()), + capture_nonlocked_() { + RTC_LOG(LS_INFO) << "Injected APM submodules:" + "\nEcho control factory: " + << !!echo_control_factory_ + << "\nEcho detector: " << !!submodules_.echo_detector + << "\nCapture analyzer: " << !!submodules_.capture_analyzer + << "\nCapture post processor: " + << !!submodules_.capture_post_processor + << "\nRender pre processor: " + << !!submodules_.render_pre_processor; + + // Mark Echo Controller enabled if a factory is injected. + capture_nonlocked_.echo_controller_enabled = + static_cast(echo_control_factory_); + + // If no echo detector is injected, use the ResidualEchoDetector. + if (!submodules_.echo_detector) { + submodules_.echo_detector = + new rtc::RefCountedObject(); } - return apm; -} +#if !(defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)) + // TODO(webrtc:5298): Remove once the use of ExperimentalNs has been + // deprecated. + config_.transient_suppression.enabled = config.Get().enabled; -AudioProcessingImpl::AudioProcessingImpl(const Config& config) - : AudioProcessingImpl(config, nullptr) {} - -AudioProcessingImpl::AudioProcessingImpl(const Config& config, - Beamformer* beamformer) - : 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()), -#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP - debug_file_(FileWrapper::Create()), - event_msg_(new audioproc::Event()), + // TODO(webrtc:5298): Remove once the use of ExperimentalAgc has been + // deprecated. + config_.gain_controller1.analog_gain_controller.enabled = + config.Get().enabled; + config_.gain_controller1.analog_gain_controller.startup_min_volume = + config.Get().startup_min_volume; + config_.gain_controller1.analog_gain_controller.clipped_level_min = + config.Get().clipped_level_min; + config_.gain_controller1.analog_gain_controller.enable_agc2_level_estimator = + config.Get().enabled_agc2_level_estimator; + config_.gain_controller1.analog_gain_controller.enable_digital_adaptive = + !config.Get().digital_adaptive_disabled; #endif - api_format_({{{kSampleRate16kHz, 1, false}, - {kSampleRate16kHz, 1, false}, - {kSampleRate16kHz, 1, false}, - {kSampleRate16kHz, 1, false}}}), - fwd_proc_format_(kSampleRate16kHz), - rev_proc_format_(kSampleRate16kHz, 1), - split_rate_(kSampleRate16kHz), - stream_delay_ms_(0), - delay_offset_ms_(0), - was_stream_delay_set_(false), - last_stream_delay_ms_(0), - last_aec_system_delay_ms_(0), - stream_delay_jumps_(-1), - aec_system_delay_jumps_(-1), - output_will_be_muted_(false), - key_pressed_(false), -#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) - use_new_agc_(false), -#else - use_new_agc_(config.Get().enabled), -#endif - agc_startup_min_volume_(config.Get().startup_min_volume), -#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) - transient_suppressor_enabled_(false), -#else - transient_suppressor_enabled_(config.Get().enabled), -#endif - beamformer_enabled_(config.Get().enabled), - beamformer_(beamformer), - array_geometry_(config.Get().array_geometry), - target_direction_(config.Get().target_direction), - intelligibility_enabled_(config.Get().enabled) { - echo_cancellation_ = new EchoCancellationImpl(this, crit_); - component_list_.push_back(echo_cancellation_); - echo_control_mobile_ = new EchoControlMobileImpl(this, crit_); - component_list_.push_back(echo_control_mobile_); - - gain_control_ = new GainControlImpl(this, crit_); - component_list_.push_back(gain_control_); - - high_pass_filter_ = new HighPassFilterImpl(this, crit_); - component_list_.push_back(high_pass_filter_); - - level_estimator_ = new LevelEstimatorImpl(this, crit_); - component_list_.push_back(level_estimator_); - - noise_suppression_ = new NoiseSuppressionImpl(this, crit_); - component_list_.push_back(noise_suppression_); - - voice_detection_ = new VoiceDetectionImpl(this, crit_); - component_list_.push_back(voice_detection_); - - gain_control_for_new_agc_.reset(new GainControlForNewAgc(gain_control_)); - - SetExtraOptions(config); + Initialize(); } -AudioProcessingImpl::~AudioProcessingImpl() { - { - CriticalSectionScoped crit_scoped(crit_); - // Depends on gain_control_ and gain_control_for_new_agc_. - agc_manager_.reset(); - // Depends on gain_control_. - gain_control_for_new_agc_.reset(); - 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; -} +AudioProcessingImpl::~AudioProcessingImpl() = default; int AudioProcessingImpl::Initialize() { - CriticalSectionScoped crit_scoped(crit_); - return InitializeLocked(); + // Run in a single-threaded manner during initialization. + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + InitializeLocked(); + return kNoError; } -int AudioProcessingImpl::Initialize(int input_sample_rate_hz, - int output_sample_rate_hz, - int reverse_sample_rate_hz, - ChannelLayout input_layout, - ChannelLayout output_layout, - ChannelLayout reverse_layout) { +int AudioProcessingImpl::Initialize(int capture_input_sample_rate_hz, + int capture_output_sample_rate_hz, + int render_input_sample_rate_hz, + ChannelLayout capture_input_layout, + ChannelLayout capture_output_layout, + ChannelLayout render_input_layout) { const ProcessingConfig processing_config = { - {{input_sample_rate_hz, - ChannelsFromLayout(input_layout), - LayoutHasKeyboard(input_layout)}, - {output_sample_rate_hz, - ChannelsFromLayout(output_layout), - LayoutHasKeyboard(output_layout)}, - {reverse_sample_rate_hz, - ChannelsFromLayout(reverse_layout), - LayoutHasKeyboard(reverse_layout)}, - {reverse_sample_rate_hz, - ChannelsFromLayout(reverse_layout), - LayoutHasKeyboard(reverse_layout)}}}; + {{capture_input_sample_rate_hz, ChannelsFromLayout(capture_input_layout), + LayoutHasKeyboard(capture_input_layout)}, + {capture_output_sample_rate_hz, + ChannelsFromLayout(capture_output_layout), + LayoutHasKeyboard(capture_output_layout)}, + {render_input_sample_rate_hz, ChannelsFromLayout(render_input_layout), + LayoutHasKeyboard(render_input_layout)}, + {render_input_sample_rate_hz, ChannelsFromLayout(render_input_layout), + LayoutHasKeyboard(render_input_layout)}}}; return Initialize(processing_config); } int AudioProcessingImpl::Initialize(const ProcessingConfig& processing_config) { - CriticalSectionScoped crit_scoped(crit_); + // Run in a single-threaded manner during initialization. + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); return InitializeLocked(processing_config); } -int AudioProcessingImpl::InitializeLocked() { - const int fwd_audio_buffer_channels = - beamformer_enabled_ ? api_format_.input_stream().num_channels() - : api_format_.output_stream().num_channels(); - const int rev_audio_buffer_out_num_frames = - api_format_.reverse_output_stream().num_frames() == 0 - ? rev_proc_format_.num_frames() - : api_format_.reverse_output_stream().num_frames(); - if (api_format_.reverse_input_stream().num_channels() > 0) { - render_audio_.reset(new AudioBuffer( - api_format_.reverse_input_stream().num_frames(), - api_format_.reverse_input_stream().num_channels(), - rev_proc_format_.num_frames(), rev_proc_format_.num_channels(), - rev_audio_buffer_out_num_frames)); - if (rev_conversion_needed()) { - render_converter_ = AudioConverter::Create( - api_format_.reverse_input_stream().num_channels(), - api_format_.reverse_input_stream().num_frames(), - api_format_.reverse_output_stream().num_channels(), - api_format_.reverse_output_stream().num_frames()); +int AudioProcessingImpl::MaybeInitializeRender( + const ProcessingConfig& processing_config) { + // Called from both threads. Thread check is therefore not possible. + if (processing_config == formats_.api_format) { + return kNoError; + } + + MutexLock lock_capture(&mutex_capture_); + return InitializeLocked(processing_config); +} + +void AudioProcessingImpl::InitializeLocked() { + UpdateActiveSubmoduleStates(); + + const int render_audiobuffer_sample_rate_hz = + formats_.api_format.reverse_output_stream().num_frames() == 0 + ? formats_.render_processing_format.sample_rate_hz() + : formats_.api_format.reverse_output_stream().sample_rate_hz(); + if (formats_.api_format.reverse_input_stream().num_channels() > 0) { + render_.render_audio.reset(new AudioBuffer( + formats_.api_format.reverse_input_stream().sample_rate_hz(), + formats_.api_format.reverse_input_stream().num_channels(), + formats_.render_processing_format.sample_rate_hz(), + formats_.render_processing_format.num_channels(), + render_audiobuffer_sample_rate_hz, + formats_.render_processing_format.num_channels())); + if (formats_.api_format.reverse_input_stream() != + formats_.api_format.reverse_output_stream()) { + render_.render_converter = AudioConverter::Create( + formats_.api_format.reverse_input_stream().num_channels(), + formats_.api_format.reverse_input_stream().num_frames(), + formats_.api_format.reverse_output_stream().num_channels(), + formats_.api_format.reverse_output_stream().num_frames()); } else { - render_converter_.reset(nullptr); + render_.render_converter.reset(nullptr); } } else { - render_audio_.reset(nullptr); - render_converter_.reset(nullptr); - } - capture_audio_.reset(new AudioBuffer( - api_format_.input_stream().num_frames(), - api_format_.input_stream().num_channels(), fwd_proc_format_.num_frames(), - fwd_audio_buffer_channels, api_format_.output_stream().num_frames())); - - // Initialize all components. - for (auto item : component_list_) { - int err = item->Initialize(); - if (err != kNoError) { - return err; - } + render_.render_audio.reset(nullptr); + render_.render_converter.reset(nullptr); } - InitializeExperimentalAgc(); + capture_.capture_audio.reset(new AudioBuffer( + formats_.api_format.input_stream().sample_rate_hz(), + formats_.api_format.input_stream().num_channels(), + capture_nonlocked_.capture_processing_format.sample_rate_hz(), + formats_.api_format.output_stream().num_channels(), + formats_.api_format.output_stream().sample_rate_hz(), + formats_.api_format.output_stream().num_channels())); - InitializeTransient(); - - InitializeBeamformer(); - - InitializeIntelligibility(); - -#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP - if (debug_file_->Open()) { - int err = WriteInitMessage(); - if (err != kNoError) { - return err; - } + if (capture_nonlocked_.capture_processing_format.sample_rate_hz() < + formats_.api_format.output_stream().sample_rate_hz() && + formats_.api_format.output_stream().sample_rate_hz() == 48000) { + capture_.capture_fullband_audio.reset( + new AudioBuffer(formats_.api_format.input_stream().sample_rate_hz(), + formats_.api_format.input_stream().num_channels(), + formats_.api_format.output_stream().sample_rate_hz(), + formats_.api_format.output_stream().num_channels(), + formats_.api_format.output_stream().sample_rate_hz(), + formats_.api_format.output_stream().num_channels())); + } else { + capture_.capture_fullband_audio.reset(); } -#endif - return kNoError; + AllocateRenderQueue(); + + InitializeGainController1(); + InitializeTransientSuppressor(); + InitializeHighPassFilter(true); + InitializeVoiceDetector(); + InitializeResidualEchoDetector(); + InitializeEchoController(); + InitializeGainController2(); + InitializeNoiseSuppressor(); + InitializeAnalyzer(); + InitializePostProcessor(); + InitializePreProcessor(); + + if (aec_dump_) { + aec_dump_->WriteInitMessage(formats_.api_format, rtc::TimeUTCMillis()); + } } int AudioProcessingImpl::InitializeLocked(const ProcessingConfig& config) { + UpdateActiveSubmoduleStates(); + for (const auto& stream : config.streams) { - if (stream.num_channels() < 0) { - return kBadNumberChannelsError; - } if (stream.num_channels() > 0 && stream.sample_rate_hz() <= 0) { return kBadSampleRateError; } } - const int num_in_channels = config.input_stream().num_channels(); - const int num_out_channels = config.output_stream().num_channels(); + const size_t num_in_channels = config.input_stream().num_channels(); + const size_t num_out_channels = config.output_stream().num_channels(); // Need at least one input channel. // Need either one output channel or as many outputs as there are inputs. @@ -390,480 +449,1023 @@ int AudioProcessingImpl::InitializeLocked(const ProcessingConfig& config) { return kBadNumberChannelsError; } - if (beamformer_enabled_ && - (static_cast(num_in_channels) != array_geometry_.size() || - num_out_channels > 1)) { - return kBadNumberChannelsError; + formats_.api_format = config; + + // Choose maximum rate to use for the split filtering. + RTC_DCHECK(config_.pipeline.maximum_internal_processing_rate == 48000 || + config_.pipeline.maximum_internal_processing_rate == 32000); + int max_splitting_rate = 48000; + if (config_.pipeline.maximum_internal_processing_rate == 32000) { + max_splitting_rate = config_.pipeline.maximum_internal_processing_rate; } - api_format_ = config; + int capture_processing_rate = SuitableProcessRate( + std::min(formats_.api_format.input_stream().sample_rate_hz(), + formats_.api_format.output_stream().sample_rate_hz()), + max_splitting_rate, + submodule_states_.CaptureMultiBandSubModulesActive() || + submodule_states_.RenderMultiBandSubModulesActive()); + RTC_DCHECK_NE(8000, capture_processing_rate); - // We process at the closest native rate >= min(input rate, output rate)... - const int min_proc_rate = - std::min(api_format_.input_stream().sample_rate_hz(), - api_format_.output_stream().sample_rate_hz()); - int fwd_proc_rate; - for (size_t i = 0; i < kNumNativeSampleRates; ++i) { - fwd_proc_rate = kNativeSampleRatesHz[i]; - if (fwd_proc_rate >= min_proc_rate) { - break; - } - } - // ...with one exception. - if (echo_control_mobile_->is_enabled() && - min_proc_rate > kMaxAECMSampleRateHz) { - fwd_proc_rate = kMaxAECMSampleRateHz; - } + capture_nonlocked_.capture_processing_format = + StreamConfig(capture_processing_rate); - fwd_proc_format_ = StreamConfig(fwd_proc_rate); - - // We normally process the reverse stream at 16 kHz. Unless... - int rev_proc_rate = kSampleRate16kHz; - if (fwd_proc_format_.sample_rate_hz() == kSampleRate8kHz) { - // ...the forward stream is at 8 kHz. - rev_proc_rate = kSampleRate8kHz; + int render_processing_rate; + if (!capture_nonlocked_.echo_controller_enabled) { + render_processing_rate = SuitableProcessRate( + std::min(formats_.api_format.reverse_input_stream().sample_rate_hz(), + formats_.api_format.reverse_output_stream().sample_rate_hz()), + max_splitting_rate, + submodule_states_.CaptureMultiBandSubModulesActive() || + submodule_states_.RenderMultiBandSubModulesActive()); } else { - if (api_format_.reverse_input_stream().sample_rate_hz() == - kSampleRate32kHz) { - // ...or the input is at 32 kHz, in which case we use the splitting - // filter rather than the resampler. - rev_proc_rate = kSampleRate32kHz; - } + render_processing_rate = capture_processing_rate; } - // Always downmix the reverse stream to mono for analysis. This has been - // demonstrated to work well for AEC in most practical scenarios. - rev_proc_format_ = StreamConfig(rev_proc_rate, 1); - - if (fwd_proc_format_.sample_rate_hz() == kSampleRate32kHz || - fwd_proc_format_.sample_rate_hz() == kSampleRate48kHz) { - split_rate_ = kSampleRate16kHz; + // If the forward sample rate is 8 kHz, the render stream is also processed + // at this rate. + if (capture_nonlocked_.capture_processing_format.sample_rate_hz() == + kSampleRate8kHz) { + render_processing_rate = kSampleRate8kHz; } else { - split_rate_ = fwd_proc_format_.sample_rate_hz(); + render_processing_rate = + std::max(render_processing_rate, static_cast(kSampleRate16kHz)); } - return InitializeLocked(); + RTC_DCHECK_NE(8000, render_processing_rate); + + if (submodule_states_.RenderMultiBandSubModulesActive()) { + // By default, downmix the render stream to mono for analysis. This has been + // demonstrated to work well for AEC in most practical scenarios. + const bool multi_channel_render = config_.pipeline.multi_channel_render && + constants_.multi_channel_render_support; + int render_processing_num_channels = + multi_channel_render + ? formats_.api_format.reverse_input_stream().num_channels() + : 1; + formats_.render_processing_format = + StreamConfig(render_processing_rate, render_processing_num_channels); + } else { + formats_.render_processing_format = StreamConfig( + formats_.api_format.reverse_input_stream().sample_rate_hz(), + formats_.api_format.reverse_input_stream().num_channels()); + } + + if (capture_nonlocked_.capture_processing_format.sample_rate_hz() == + kSampleRate32kHz || + capture_nonlocked_.capture_processing_format.sample_rate_hz() == + kSampleRate48kHz) { + capture_nonlocked_.split_rate = kSampleRate16kHz; + } else { + capture_nonlocked_.split_rate = + capture_nonlocked_.capture_processing_format.sample_rate_hz(); + } + + InitializeLocked(); + return kNoError; } -// Calls InitializeLocked() if any of the audio parameters have changed from -// their current values. -int AudioProcessingImpl::MaybeInitializeLocked( - const ProcessingConfig& processing_config) { - if (processing_config == api_format_) { - return kNoError; - } - return InitializeLocked(processing_config); -} +void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) { + RTC_LOG(LS_INFO) << "AudioProcessing::ApplyConfig: " << config.ToString(); -void AudioProcessingImpl::SetExtraOptions(const Config& config) { - CriticalSectionScoped crit_scoped(crit_); - for (auto item : component_list_) { - item->SetExtraOptions(config); + // Run in a single-threaded manner when applying the settings. + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + + const bool pipeline_config_changed = + config_.pipeline.multi_channel_render != + config.pipeline.multi_channel_render || + config_.pipeline.multi_channel_capture != + config.pipeline.multi_channel_capture || + config_.pipeline.maximum_internal_processing_rate != + config.pipeline.maximum_internal_processing_rate; + + const bool aec_config_changed = + config_.echo_canceller.enabled != config.echo_canceller.enabled || + config_.echo_canceller.mobile_mode != config.echo_canceller.mobile_mode; + + const bool agc1_config_changed = + config_.gain_controller1.enabled != config.gain_controller1.enabled || + config_.gain_controller1.mode != config.gain_controller1.mode || + config_.gain_controller1.target_level_dbfs != + config.gain_controller1.target_level_dbfs || + config_.gain_controller1.compression_gain_db != + config.gain_controller1.compression_gain_db || + config_.gain_controller1.enable_limiter != + config.gain_controller1.enable_limiter || + config_.gain_controller1.analog_level_minimum != + config.gain_controller1.analog_level_minimum || + config_.gain_controller1.analog_level_maximum != + config.gain_controller1.analog_level_maximum || + config_.gain_controller1.analog_gain_controller.enabled != + config.gain_controller1.analog_gain_controller.enabled || + config_.gain_controller1.analog_gain_controller.startup_min_volume != + config.gain_controller1.analog_gain_controller.startup_min_volume || + config_.gain_controller1.analog_gain_controller.clipped_level_min != + config.gain_controller1.analog_gain_controller.clipped_level_min || + config_.gain_controller1.analog_gain_controller + .enable_agc2_level_estimator != + config.gain_controller1.analog_gain_controller + .enable_agc2_level_estimator || + config_.gain_controller1.analog_gain_controller.enable_digital_adaptive != + config.gain_controller1.analog_gain_controller + .enable_digital_adaptive; + + const bool agc2_config_changed = + config_.gain_controller2.enabled != config.gain_controller2.enabled; + + const bool voice_detection_config_changed = + config_.voice_detection.enabled != config.voice_detection.enabled; + + const bool ns_config_changed = + config_.noise_suppression.enabled != config.noise_suppression.enabled || + config_.noise_suppression.level != config.noise_suppression.level; + + const bool ts_config_changed = config_.transient_suppression.enabled != + config.transient_suppression.enabled; + + const bool pre_amplifier_config_changed = + config_.pre_amplifier.enabled != config.pre_amplifier.enabled || + config_.pre_amplifier.fixed_gain_factor != + config.pre_amplifier.fixed_gain_factor; + + config_ = config; + + if (aec_config_changed) { + InitializeEchoController(); } - if (transient_suppressor_enabled_ != config.Get().enabled) { - transient_suppressor_enabled_ = config.Get().enabled; - InitializeTransient(); + if (ns_config_changed) { + InitializeNoiseSuppressor(); + } + + if (ts_config_changed) { + InitializeTransientSuppressor(); + } + + InitializeHighPassFilter(false); + + if (agc1_config_changed) { + InitializeGainController1(); + } + + const bool config_ok = GainController2::Validate(config_.gain_controller2); + if (!config_ok) { + RTC_LOG(LS_ERROR) << "AudioProcessing module config error\n" + "Gain Controller 2: " + << GainController2::ToString(config_.gain_controller2) + << "\nReverting to default parameter set"; + config_.gain_controller2 = AudioProcessing::Config::GainController2(); + } + + if (agc2_config_changed) { + InitializeGainController2(); + } + + if (pre_amplifier_config_changed) { + InitializePreAmplifier(); + } + + if (config_.level_estimation.enabled && !submodules_.output_level_estimator) { + submodules_.output_level_estimator = std::make_unique(); + } + + if (voice_detection_config_changed) { + InitializeVoiceDetector(); + } + + // Reinitialization must happen after all submodule configuration to avoid + // additional reinitializations on the next capture / render processing call. + if (pipeline_config_changed) { + InitializeLocked(formats_.api_format); } } +void AudioProcessingImpl::OverrideSubmoduleCreationForTesting( + const ApmSubmoduleCreationOverrides& overrides) { + MutexLock lock(&mutex_capture_); + submodule_creation_overrides_ = overrides; +} int AudioProcessingImpl::proc_sample_rate_hz() const { - return fwd_proc_format_.sample_rate_hz(); + // Used as callback from submodules, hence locking is not allowed. + return capture_nonlocked_.capture_processing_format.sample_rate_hz(); +} + +int AudioProcessingImpl::proc_fullband_sample_rate_hz() const { + return capture_.capture_fullband_audio + ? capture_.capture_fullband_audio->num_frames() * 100 + : capture_nonlocked_.capture_processing_format.sample_rate_hz(); } int AudioProcessingImpl::proc_split_sample_rate_hz() const { - return split_rate_; + // Used as callback from submodules, hence locking is not allowed. + return capture_nonlocked_.split_rate; } -int AudioProcessingImpl::num_reverse_channels() const { - return rev_proc_format_.num_channels(); +size_t AudioProcessingImpl::num_reverse_channels() const { + // Used as callback from submodules, hence locking is not allowed. + return formats_.render_processing_format.num_channels(); } -int AudioProcessingImpl::num_input_channels() const { - return api_format_.input_stream().num_channels(); +size_t AudioProcessingImpl::num_input_channels() const { + // Used as callback from submodules, hence locking is not allowed. + return formats_.api_format.input_stream().num_channels(); } -int AudioProcessingImpl::num_output_channels() const { - return api_format_.output_stream().num_channels(); +size_t AudioProcessingImpl::num_proc_channels() const { + // Used as callback from submodules, hence locking is not allowed. + const bool multi_channel_capture = config_.pipeline.multi_channel_capture && + constants_.multi_channel_capture_support; + if (capture_nonlocked_.echo_controller_enabled && !multi_channel_capture) { + return 1; + } + return num_output_channels(); +} + +size_t AudioProcessingImpl::num_output_channels() const { + // Used as callback from submodules, hence locking is not allowed. + return formats_.api_format.output_stream().num_channels(); } void AudioProcessingImpl::set_output_will_be_muted(bool muted) { - CriticalSectionScoped lock(crit_); - output_will_be_muted_ = muted; - if (agc_manager_.get()) { - agc_manager_->SetCaptureMuted(output_will_be_muted_); + MutexLock lock(&mutex_capture_); + capture_.output_will_be_muted = muted; + if (submodules_.agc_manager.get()) { + submodules_.agc_manager->SetCaptureMuted(capture_.output_will_be_muted); } } - -int AudioProcessingImpl::ProcessStream(const float* const* src, - size_t samples_per_channel, - int input_sample_rate_hz, - ChannelLayout input_layout, - int output_sample_rate_hz, - ChannelLayout output_layout, - float* const* dest) { - CriticalSectionScoped crit_scoped(crit_); - StreamConfig input_stream = api_format_.input_stream(); - input_stream.set_sample_rate_hz(input_sample_rate_hz); - input_stream.set_num_channels(ChannelsFromLayout(input_layout)); - input_stream.set_has_keyboard(LayoutHasKeyboard(input_layout)); - - StreamConfig output_stream = api_format_.output_stream(); - output_stream.set_sample_rate_hz(output_sample_rate_hz); - output_stream.set_num_channels(ChannelsFromLayout(output_layout)); - output_stream.set_has_keyboard(LayoutHasKeyboard(output_layout)); - - if (samples_per_channel != input_stream.num_frames()) { - return kBadDataLengthError; +void AudioProcessingImpl::SetRuntimeSetting(RuntimeSetting setting) { + switch (setting.type()) { + case RuntimeSetting::Type::kCustomRenderProcessingRuntimeSetting: + case RuntimeSetting::Type::kPlayoutAudioDeviceChange: + render_runtime_settings_enqueuer_.Enqueue(setting); + return; + case RuntimeSetting::Type::kCapturePreGain: + case RuntimeSetting::Type::kCaptureCompressionGain: + case RuntimeSetting::Type::kCaptureFixedPostGain: + case RuntimeSetting::Type::kCaptureOutputUsed: + capture_runtime_settings_enqueuer_.Enqueue(setting); + return; + case RuntimeSetting::Type::kPlayoutVolumeChange: + capture_runtime_settings_enqueuer_.Enqueue(setting); + render_runtime_settings_enqueuer_.Enqueue(setting); + return; + case RuntimeSetting::Type::kNotSpecified: + RTC_NOTREACHED(); + return; } - return ProcessStream(src, input_stream, output_stream, dest); + // The language allows the enum to have a non-enumerator + // value. Check that this doesn't happen. + RTC_NOTREACHED(); +} + +AudioProcessingImpl::RuntimeSettingEnqueuer::RuntimeSettingEnqueuer( + SwapQueue* runtime_settings) + : runtime_settings_(*runtime_settings) { + RTC_DCHECK(runtime_settings); +} + +AudioProcessingImpl::RuntimeSettingEnqueuer::~RuntimeSettingEnqueuer() = + default; + +void AudioProcessingImpl::RuntimeSettingEnqueuer::Enqueue( + RuntimeSetting setting) { + size_t remaining_attempts = 10; + while (!runtime_settings_.Insert(&setting) && remaining_attempts-- > 0) { + RuntimeSetting setting_to_discard; + if (runtime_settings_.Remove(&setting_to_discard)) + RTC_LOG(LS_ERROR) + << "The runtime settings queue is full. Oldest setting discarded."; + } + if (remaining_attempts == 0) + RTC_LOG(LS_ERROR) << "Cannot enqueue a new runtime setting."; +} + +int AudioProcessingImpl::MaybeInitializeCapture( + const StreamConfig& input_config, + const StreamConfig& output_config) { + ProcessingConfig processing_config; + bool reinitialization_required = false; + { + // Acquire the capture lock in order to access api_format. The lock is + // released immediately, as we may need to acquire the render lock as part + // of the conditional reinitialization. + MutexLock lock_capture(&mutex_capture_); + processing_config = formats_.api_format; + reinitialization_required = UpdateActiveSubmoduleStates(); + } + + if (processing_config.input_stream() != input_config) { + processing_config.input_stream() = input_config; + reinitialization_required = true; + } + + if (processing_config.output_stream() != output_config) { + processing_config.output_stream() = output_config; + reinitialization_required = true; + } + + if (reinitialization_required) { + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + RETURN_ON_ERR(InitializeLocked(processing_config)); + } + return kNoError; } int AudioProcessingImpl::ProcessStream(const float* const* src, const StreamConfig& input_config, const StreamConfig& output_config, float* const* dest) { - CriticalSectionScoped crit_scoped(crit_); + TRACE_EVENT0("webrtc", "AudioProcessing::ProcessStream_StreamConfig"); if (!src || !dest) { return kNullPointerError; } - ProcessingConfig processing_config = api_format_; - processing_config.input_stream() = input_config; - processing_config.output_stream() = output_config; + RETURN_ON_ERR(MaybeInitializeCapture(input_config, output_config)); - RETURN_ON_ERR(MaybeInitializeLocked(processing_config)); - assert(processing_config.input_stream().num_frames() == - api_format_.input_stream().num_frames()); + MutexLock lock_capture(&mutex_capture_); -#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP - if (debug_file_->Open()) { - RETURN_ON_ERR(WriteConfigMessage(false)); - - event_msg_->set_type(audioproc::Event::STREAM); - audioproc::Stream* msg = event_msg_->mutable_stream(); - const size_t channel_size = - sizeof(float) * api_format_.input_stream().num_frames(); - for (int i = 0; i < api_format_.input_stream().num_channels(); ++i) - msg->add_input_channel(src[i], channel_size); + if (aec_dump_) { + RecordUnprocessedCaptureStream(src); } -#endif - capture_audio_->CopyFrom(src, api_format_.input_stream()); - RETURN_ON_ERR(ProcessStreamLocked()); - capture_audio_->CopyTo(api_format_.output_stream(), dest); - -#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP - if (debug_file_->Open()) { - audioproc::Stream* msg = event_msg_->mutable_stream(); - const size_t channel_size = - sizeof(float) * api_format_.output_stream().num_frames(); - for (int i = 0; i < api_format_.output_stream().num_channels(); ++i) - msg->add_output_channel(dest[i], channel_size); - RETURN_ON_ERR(WriteMessageToDebugFile()); + capture_.keyboard_info.Extract(src, formats_.api_format.input_stream()); + capture_.capture_audio->CopyFrom(src, formats_.api_format.input_stream()); + if (capture_.capture_fullband_audio) { + capture_.capture_fullband_audio->CopyFrom( + src, formats_.api_format.input_stream()); + } + RETURN_ON_ERR(ProcessCaptureStreamLocked()); + if (capture_.capture_fullband_audio) { + capture_.capture_fullband_audio->CopyTo(formats_.api_format.output_stream(), + dest); + } else { + capture_.capture_audio->CopyTo(formats_.api_format.output_stream(), dest); + } + + if (aec_dump_) { + RecordProcessedCaptureStream(dest); + } + return kNoError; +} + +void AudioProcessingImpl::HandleCaptureRuntimeSettings() { + RuntimeSetting setting; + while (capture_runtime_settings_.Remove(&setting)) { + if (aec_dump_) { + aec_dump_->WriteRuntimeSetting(setting); + } + switch (setting.type()) { + case RuntimeSetting::Type::kCapturePreGain: + if (config_.pre_amplifier.enabled) { + float value; + setting.GetFloat(&value); + config_.pre_amplifier.fixed_gain_factor = value; + submodules_.pre_amplifier->SetGainFactor(value); + } + // TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump. + break; + case RuntimeSetting::Type::kCaptureCompressionGain: { + if (!submodules_.agc_manager) { + float value; + setting.GetFloat(&value); + int int_value = static_cast(value + .5f); + config_.gain_controller1.compression_gain_db = int_value; + if (submodules_.gain_control) { + int error = + submodules_.gain_control->set_compression_gain_db(int_value); + RTC_DCHECK_EQ(kNoError, error); + } + } + break; + } + case RuntimeSetting::Type::kCaptureFixedPostGain: { + if (submodules_.gain_controller2) { + float value; + setting.GetFloat(&value); + config_.gain_controller2.fixed_digital.gain_db = value; + submodules_.gain_controller2->ApplyConfig(config_.gain_controller2); + } + break; + } + case RuntimeSetting::Type::kPlayoutVolumeChange: { + int value; + setting.GetInt(&value); + capture_.playout_volume = value; + break; + } + case RuntimeSetting::Type::kPlayoutAudioDeviceChange: + RTC_NOTREACHED(); + break; + case RuntimeSetting::Type::kCustomRenderProcessingRuntimeSetting: + RTC_NOTREACHED(); + break; + case RuntimeSetting::Type::kNotSpecified: + RTC_NOTREACHED(); + break; + case RuntimeSetting::Type::kCaptureOutputUsed: + // TODO(b/154437967): Add support for reducing complexity when it is + // known that the capture output will not be used. + break; + } + } +} + +void AudioProcessingImpl::HandleRenderRuntimeSettings() { + RuntimeSetting setting; + while (render_runtime_settings_.Remove(&setting)) { + if (aec_dump_) { + aec_dump_->WriteRuntimeSetting(setting); + } + switch (setting.type()) { + case RuntimeSetting::Type::kPlayoutAudioDeviceChange: // fall-through + case RuntimeSetting::Type::kPlayoutVolumeChange: // fall-through + case RuntimeSetting::Type::kCustomRenderProcessingRuntimeSetting: + if (submodules_.render_pre_processor) { + submodules_.render_pre_processor->SetRuntimeSetting(setting); + } + break; + case RuntimeSetting::Type::kCapturePreGain: // fall-through + case RuntimeSetting::Type::kCaptureCompressionGain: // fall-through + case RuntimeSetting::Type::kCaptureFixedPostGain: // fall-through + case RuntimeSetting::Type::kCaptureOutputUsed: // fall-through + case RuntimeSetting::Type::kNotSpecified: + RTC_NOTREACHED(); + break; + } + } +} + +void AudioProcessingImpl::QueueBandedRenderAudio(AudioBuffer* audio) { + RTC_DCHECK_GE(160, audio->num_frames_per_band()); + + if (submodules_.echo_control_mobile) { + EchoControlMobileImpl::PackRenderAudioBuffer(audio, num_output_channels(), + num_reverse_channels(), + &aecm_render_queue_buffer_); + RTC_DCHECK(aecm_render_signal_queue_); + // Insert the samples into the queue. + if (!aecm_render_signal_queue_->Insert(&aecm_render_queue_buffer_)) { + // The data queue is full and needs to be emptied. + EmptyQueuedRenderAudio(); + + // Retry the insert (should always work). + bool result = + aecm_render_signal_queue_->Insert(&aecm_render_queue_buffer_); + RTC_DCHECK(result); + } + } + + if (!submodules_.agc_manager && submodules_.gain_control) { + GainControlImpl::PackRenderAudioBuffer(*audio, &agc_render_queue_buffer_); + // Insert the samples into the queue. + if (!agc_render_signal_queue_->Insert(&agc_render_queue_buffer_)) { + // The data queue is full and needs to be emptied. + EmptyQueuedRenderAudio(); + + // Retry the insert (should always work). + bool result = agc_render_signal_queue_->Insert(&agc_render_queue_buffer_); + RTC_DCHECK(result); + } + } +} + +void AudioProcessingImpl::QueueNonbandedRenderAudio(AudioBuffer* audio) { + ResidualEchoDetector::PackRenderAudioBuffer(audio, &red_render_queue_buffer_); + + // Insert the samples into the queue. + if (!red_render_signal_queue_->Insert(&red_render_queue_buffer_)) { + // The data queue is full and needs to be emptied. + EmptyQueuedRenderAudio(); + + // Retry the insert (should always work). + bool result = red_render_signal_queue_->Insert(&red_render_queue_buffer_); + RTC_DCHECK(result); + } +} + +void AudioProcessingImpl::AllocateRenderQueue() { + const size_t new_agc_render_queue_element_max_size = + std::max(static_cast(1), kMaxAllowedValuesOfSamplesPerBand); + + const size_t new_red_render_queue_element_max_size = + std::max(static_cast(1), kMaxAllowedValuesOfSamplesPerFrame); + + // Reallocate the queues if the queue item sizes are too small to fit the + // data to put in the queues. + + if (agc_render_queue_element_max_size_ < + new_agc_render_queue_element_max_size) { + agc_render_queue_element_max_size_ = new_agc_render_queue_element_max_size; + + std::vector template_queue_element( + agc_render_queue_element_max_size_); + + agc_render_signal_queue_.reset( + new SwapQueue, RenderQueueItemVerifier>( + kMaxNumFramesToBuffer, template_queue_element, + RenderQueueItemVerifier( + agc_render_queue_element_max_size_))); + + agc_render_queue_buffer_.resize(agc_render_queue_element_max_size_); + agc_capture_queue_buffer_.resize(agc_render_queue_element_max_size_); + } else { + agc_render_signal_queue_->Clear(); + } + + if (red_render_queue_element_max_size_ < + new_red_render_queue_element_max_size) { + red_render_queue_element_max_size_ = new_red_render_queue_element_max_size; + + std::vector template_queue_element( + red_render_queue_element_max_size_); + + red_render_signal_queue_.reset( + new SwapQueue, RenderQueueItemVerifier>( + kMaxNumFramesToBuffer, template_queue_element, + RenderQueueItemVerifier( + red_render_queue_element_max_size_))); + + red_render_queue_buffer_.resize(red_render_queue_element_max_size_); + red_capture_queue_buffer_.resize(red_render_queue_element_max_size_); + } else { + red_render_signal_queue_->Clear(); + } +} + +void AudioProcessingImpl::EmptyQueuedRenderAudio() { + MutexLock lock_capture(&mutex_capture_); + EmptyQueuedRenderAudioLocked(); +} + +void AudioProcessingImpl::EmptyQueuedRenderAudioLocked() { + if (submodules_.echo_control_mobile) { + RTC_DCHECK(aecm_render_signal_queue_); + while (aecm_render_signal_queue_->Remove(&aecm_capture_queue_buffer_)) { + submodules_.echo_control_mobile->ProcessRenderAudio( + aecm_capture_queue_buffer_); + } + } + + if (submodules_.gain_control) { + while (agc_render_signal_queue_->Remove(&agc_capture_queue_buffer_)) { + submodules_.gain_control->ProcessRenderAudio(agc_capture_queue_buffer_); + } + } + + while (red_render_signal_queue_->Remove(&red_capture_queue_buffer_)) { + RTC_DCHECK(submodules_.echo_detector); + submodules_.echo_detector->AnalyzeRenderAudio(red_capture_queue_buffer_); + } +} + +int AudioProcessingImpl::ProcessStream(const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest) { + TRACE_EVENT0("webrtc", "AudioProcessing::ProcessStream_AudioFrame"); + RETURN_ON_ERR(MaybeInitializeCapture(input_config, output_config)); + + MutexLock lock_capture(&mutex_capture_); + + if (aec_dump_) { + RecordUnprocessedCaptureStream(src, input_config); + } + + capture_.capture_audio->CopyFrom(src, input_config); + if (capture_.capture_fullband_audio) { + capture_.capture_fullband_audio->CopyFrom(src, input_config); + } + RETURN_ON_ERR(ProcessCaptureStreamLocked()); + if (submodule_states_.CaptureMultiBandProcessingPresent() || + submodule_states_.CaptureFullBandProcessingActive()) { + if (capture_.capture_fullband_audio) { + capture_.capture_fullband_audio->CopyTo(output_config, dest); + } else { + capture_.capture_audio->CopyTo(output_config, dest); + } + } + + if (aec_dump_) { + RecordProcessedCaptureStream(dest, output_config); } -#endif return kNoError; } -int AudioProcessingImpl::ProcessStream(AudioFrame* frame) { - CriticalSectionScoped crit_scoped(crit_); - if (!frame) { - return kNullPointerError; - } - // Must be a native rate. - if (frame->sample_rate_hz_ != kSampleRate8kHz && - frame->sample_rate_hz_ != kSampleRate16kHz && - frame->sample_rate_hz_ != kSampleRate32kHz && - frame->sample_rate_hz_ != kSampleRate48kHz) { - return kBadSampleRateError; - } - if (echo_control_mobile_->is_enabled() && - frame->sample_rate_hz_ > kMaxAECMSampleRateHz) { - LOG(LS_ERROR) << "AECM only supports 16 or 8 kHz sample rates"; - return kUnsupportedComponentError; +int AudioProcessingImpl::ProcessCaptureStreamLocked() { + EmptyQueuedRenderAudioLocked(); + HandleCaptureRuntimeSettings(); + + // Ensure that not both the AEC and AECM are active at the same time. + // TODO(peah): Simplify once the public API Enable functions for these + // are moved to APM. + RTC_DCHECK_LE( + !!submodules_.echo_controller + !!submodules_.echo_control_mobile, 1); + + AudioBuffer* capture_buffer = capture_.capture_audio.get(); // For brevity. + AudioBuffer* linear_aec_buffer = capture_.linear_aec_output.get(); + + if (submodules_.high_pass_filter && + config_.high_pass_filter.apply_in_full_band && + !constants_.enforce_split_band_hpf) { + submodules_.high_pass_filter->Process(capture_buffer, + /*use_split_band_data=*/false); } - // TODO(ajm): The input and output rates and channels are currently - // constrained to be identical in the int16 interface. - ProcessingConfig processing_config = api_format_; - processing_config.input_stream().set_sample_rate_hz(frame->sample_rate_hz_); - processing_config.input_stream().set_num_channels(frame->num_channels_); - processing_config.output_stream().set_sample_rate_hz(frame->sample_rate_hz_); - processing_config.output_stream().set_num_channels(frame->num_channels_); - - RETURN_ON_ERR(MaybeInitializeLocked(processing_config)); - if (frame->samples_per_channel_ != api_format_.input_stream().num_frames()) { - return kBadDataLengthError; + if (submodules_.pre_amplifier) { + submodules_.pre_amplifier->ApplyGain(AudioFrameView( + capture_buffer->channels(), capture_buffer->num_channels(), + capture_buffer->num_frames())); } -#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->samples_per_channel_ * frame->num_channels_; - msg->set_input_data(frame->data_, data_size); - } -#endif - - capture_audio_->DeinterleaveFrom(frame); - RETURN_ON_ERR(ProcessStreamLocked()); - capture_audio_->InterleaveTo(frame, output_copy_needed(is_data_processed())); - -#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->samples_per_channel_ * frame->num_channels_; - msg->set_output_data(frame->data_, data_size); - RETURN_ON_ERR(WriteMessageToDebugFile()); - } -#endif - - return kNoError; -} - -int AudioProcessingImpl::ProcessStreamLocked() { -#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP - if (debug_file_->Open()) { - audioproc::Stream* msg = event_msg_->mutable_stream(); - msg->set_delay(stream_delay_ms_); - msg->set_drift(echo_cancellation_->stream_drift_samples()); - msg->set_level(gain_control()->stream_analog_level()); - msg->set_keypress(key_pressed_); - } -#endif - - MaybeUpdateHistograms(); - - AudioBuffer* ca = capture_audio_.get(); // For brevity. - - if (use_new_agc_ && gain_control_->is_enabled()) { - agc_manager_->AnalyzePreProcess(ca->channels()[0], ca->num_channels(), - fwd_proc_format_.num_frames()); + capture_input_rms_.Analyze(rtc::ArrayView( + capture_buffer->channels_const()[0], + capture_nonlocked_.capture_processing_format.num_frames())); + const bool log_rms = ++capture_rms_interval_counter_ >= 1000; + if (log_rms) { + capture_rms_interval_counter_ = 0; + RmsLevel::Levels levels = capture_input_rms_.AverageAndPeak(); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.ApmCaptureInputLevelAverageRms", + levels.average, 1, RmsLevel::kMinLevelDb, 64); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.ApmCaptureInputLevelPeakRms", + levels.peak, 1, RmsLevel::kMinLevelDb, 64); } - bool data_processed = is_data_processed(); - if (analysis_needed(data_processed)) { - ca->SplitIntoFrequencyBands(); + if (submodules_.echo_controller) { + // Detect and flag any change in the analog gain. + int analog_mic_level = recommended_stream_analog_level_locked(); + capture_.echo_path_gain_change = + capture_.prev_analog_mic_level != analog_mic_level && + capture_.prev_analog_mic_level != -1; + capture_.prev_analog_mic_level = analog_mic_level; + + // Detect and flag any change in the pre-amplifier gain. + if (submodules_.pre_amplifier) { + float pre_amp_gain = submodules_.pre_amplifier->GetGainFactor(); + capture_.echo_path_gain_change = + capture_.echo_path_gain_change || + (capture_.prev_pre_amp_gain != pre_amp_gain && + capture_.prev_pre_amp_gain >= 0.f); + capture_.prev_pre_amp_gain = pre_amp_gain; + } + + // Detect volume change. + capture_.echo_path_gain_change = + capture_.echo_path_gain_change || + (capture_.prev_playout_volume != capture_.playout_volume && + capture_.prev_playout_volume >= 0); + capture_.prev_playout_volume = capture_.playout_volume; + + submodules_.echo_controller->AnalyzeCapture(capture_buffer); } - if (intelligibility_enabled_) { - intelligibility_enhancer_->AnalyzeCaptureAudio( - ca->split_channels_f(kBand0To8kHz), split_rate_, ca->num_channels()); + if (submodules_.agc_manager) { + submodules_.agc_manager->AnalyzePreProcess(capture_buffer); } - if (beamformer_enabled_) { - beamformer_->ProcessChunk(*ca->split_data_f(), ca->split_data_f()); - ca->set_num_channels(1); + if (submodule_states_.CaptureMultiBandSubModulesActive() && + SampleRateSupportsMultiBand( + capture_nonlocked_.capture_processing_format.sample_rate_hz())) { + capture_buffer->SplitIntoFrequencyBands(); } - RETURN_ON_ERR(high_pass_filter_->ProcessCaptureAudio(ca)); - RETURN_ON_ERR(gain_control_->AnalyzeCaptureAudio(ca)); - RETURN_ON_ERR(noise_suppression_->AnalyzeCaptureAudio(ca)); - RETURN_ON_ERR(echo_cancellation_->ProcessCaptureAudio(ca)); - - if (echo_control_mobile_->is_enabled() && noise_suppression_->is_enabled()) { - ca->CopyLowPassToReference(); + const bool multi_channel_capture = config_.pipeline.multi_channel_capture && + constants_.multi_channel_capture_support; + if (submodules_.echo_controller && !multi_channel_capture) { + // Force down-mixing of the number of channels after the detection of + // capture signal saturation. + // TODO(peah): Look into ensuring that this kind of tampering with the + // AudioBuffer functionality should not be needed. + capture_buffer->set_num_channels(1); } - RETURN_ON_ERR(noise_suppression_->ProcessCaptureAudio(ca)); - RETURN_ON_ERR(echo_control_mobile_->ProcessCaptureAudio(ca)); - RETURN_ON_ERR(voice_detection_->ProcessCaptureAudio(ca)); - if (use_new_agc_ && gain_control_->is_enabled() && - (!beamformer_enabled_ || beamformer_->is_target_present())) { - agc_manager_->Process(ca->split_bands_const(0)[kBand0To8kHz], - ca->num_frames_per_band(), split_rate_); + if (submodules_.high_pass_filter && + (!config_.high_pass_filter.apply_in_full_band || + constants_.enforce_split_band_hpf)) { + submodules_.high_pass_filter->Process(capture_buffer, + /*use_split_band_data=*/true); } - RETURN_ON_ERR(gain_control_->ProcessCaptureAudio(ca)); - if (synthesis_needed(data_processed)) { - ca->MergeFrequencyBands(); + if (submodules_.gain_control) { + RETURN_ON_ERR( + submodules_.gain_control->AnalyzeCaptureAudio(*capture_buffer)); + } + + if ((!config_.noise_suppression.analyze_linear_aec_output_when_available || + !linear_aec_buffer || submodules_.echo_control_mobile) && + submodules_.noise_suppressor) { + submodules_.noise_suppressor->Analyze(*capture_buffer); + } + + if (submodules_.echo_control_mobile) { + // Ensure that the stream delay was set before the call to the + // AECM ProcessCaptureAudio function. + if (!capture_.was_stream_delay_set) { + return AudioProcessing::kStreamParameterNotSetError; + } + + if (submodules_.noise_suppressor) { + submodules_.noise_suppressor->Process(capture_buffer); + } + + RETURN_ON_ERR(submodules_.echo_control_mobile->ProcessCaptureAudio( + capture_buffer, stream_delay_ms())); + } else { + if (submodules_.echo_controller) { + data_dumper_->DumpRaw("stream_delay", stream_delay_ms()); + + if (capture_.was_stream_delay_set) { + submodules_.echo_controller->SetAudioBufferDelay(stream_delay_ms()); + } + + submodules_.echo_controller->ProcessCapture( + capture_buffer, linear_aec_buffer, capture_.echo_path_gain_change); + } + + if (config_.noise_suppression.analyze_linear_aec_output_when_available && + linear_aec_buffer && submodules_.noise_suppressor) { + submodules_.noise_suppressor->Analyze(*linear_aec_buffer); + } + + if (submodules_.noise_suppressor) { + submodules_.noise_suppressor->Process(capture_buffer); + } + } + + if (config_.voice_detection.enabled) { + capture_.stats.voice_detected = + submodules_.voice_detector->ProcessCaptureAudio(capture_buffer); + } else { + capture_.stats.voice_detected = absl::nullopt; + } + + if (submodules_.agc_manager) { + submodules_.agc_manager->Process(capture_buffer); + + absl::optional new_digital_gain = + submodules_.agc_manager->GetDigitalComressionGain(); + if (new_digital_gain && submodules_.gain_control) { + submodules_.gain_control->set_compression_gain_db(*new_digital_gain); + } + } + + if (submodules_.gain_control) { + // TODO(peah): Add reporting from AEC3 whether there is echo. + RETURN_ON_ERR(submodules_.gain_control->ProcessCaptureAudio( + capture_buffer, /*stream_has_echo*/ false)); + } + + if (submodule_states_.CaptureMultiBandProcessingPresent() && + SampleRateSupportsMultiBand( + capture_nonlocked_.capture_processing_format.sample_rate_hz())) { + capture_buffer->MergeFrequencyBands(); + } + + if (capture_.capture_fullband_audio) { + const auto& ec = submodules_.echo_controller; + bool ec_active = ec ? ec->ActiveProcessing() : false; + // Only update the fullband buffer if the multiband processing has changed + // the signal. Keep the original signal otherwise. + if (submodule_states_.CaptureMultiBandProcessingActive(ec_active)) { + capture_buffer->CopyTo(capture_.capture_fullband_audio.get()); + } + capture_buffer = capture_.capture_fullband_audio.get(); + } + + if (config_.residual_echo_detector.enabled) { + RTC_DCHECK(submodules_.echo_detector); + submodules_.echo_detector->AnalyzeCaptureAudio(rtc::ArrayView( + capture_buffer->channels()[0], capture_buffer->num_frames())); } // TODO(aluebs): Investigate if the transient suppression placement should be // before or after the AGC. - if (transient_suppressor_enabled_) { - float voice_probability = - agc_manager_.get() ? agc_manager_->voice_probability() : 1.f; + if (submodules_.transient_suppressor) { + float voice_probability = submodules_.agc_manager.get() + ? submodules_.agc_manager->voice_probability() + : 1.f; - transient_suppressor_->Suppress( - ca->channels_f()[0], ca->num_frames(), ca->num_channels(), - ca->split_bands_const_f(0)[kBand0To8kHz], ca->num_frames_per_band(), - ca->keyboard_data(), ca->num_keyboard_frames(), voice_probability, - key_pressed_); + submodules_.transient_suppressor->Suppress( + capture_buffer->channels()[0], capture_buffer->num_frames(), + capture_buffer->num_channels(), + capture_buffer->split_bands_const(0)[kBand0To8kHz], + capture_buffer->num_frames_per_band(), + capture_.keyboard_info.keyboard_data, + capture_.keyboard_info.num_keyboard_frames, voice_probability, + capture_.key_pressed); + } + + // Experimental APM sub-module that analyzes |capture_buffer|. + if (submodules_.capture_analyzer) { + submodules_.capture_analyzer->Analyze(capture_buffer); + } + + if (submodules_.gain_controller2) { + submodules_.gain_controller2->NotifyAnalogLevel( + recommended_stream_analog_level_locked()); + submodules_.gain_controller2->Process(capture_buffer); + } + + if (submodules_.capture_post_processor) { + submodules_.capture_post_processor->Process(capture_buffer); } // The level estimator operates on the recombined data. - RETURN_ON_ERR(level_estimator_->ProcessStream(ca)); - - was_stream_delay_set_ = false; - return kNoError; -} - -int AudioProcessingImpl::AnalyzeReverseStream(const float* const* data, - size_t samples_per_channel, - int rev_sample_rate_hz, - ChannelLayout layout) { - const StreamConfig reverse_config = { - rev_sample_rate_hz, ChannelsFromLayout(layout), LayoutHasKeyboard(layout), - }; - if (samples_per_channel != reverse_config.num_frames()) { - return kBadDataLengthError; - } - return AnalyzeReverseStream(data, reverse_config, reverse_config); -} - -int AudioProcessingImpl::ProcessReverseStream( - const float* const* src, - const StreamConfig& reverse_input_config, - const StreamConfig& reverse_output_config, - float* const* dest) { - RETURN_ON_ERR( - AnalyzeReverseStream(src, reverse_input_config, reverse_output_config)); - if (is_rev_processed()) { - render_audio_->CopyTo(api_format_.reverse_output_stream(), dest); - } else if (rev_conversion_needed()) { - render_converter_->Convert(src, reverse_input_config.num_samples(), dest, - reverse_output_config.num_samples()); + if (config_.level_estimation.enabled) { + submodules_.output_level_estimator->ProcessStream(*capture_buffer); + capture_.stats.output_rms_dbfs = submodules_.output_level_estimator->RMS(); } else { - CopyAudioIfNeeded(src, reverse_input_config.num_frames(), - reverse_input_config.num_channels(), dest); + capture_.stats.output_rms_dbfs = absl::nullopt; } + capture_output_rms_.Analyze(rtc::ArrayView( + capture_buffer->channels_const()[0], + capture_nonlocked_.capture_processing_format.num_frames())); + if (log_rms) { + RmsLevel::Levels levels = capture_output_rms_.AverageAndPeak(); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.ApmCaptureOutputLevelAverageRms", + levels.average, 1, RmsLevel::kMinLevelDb, 64); + RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.ApmCaptureOutputLevelPeakRms", + levels.peak, 1, RmsLevel::kMinLevelDb, 64); + } + + if (submodules_.agc_manager) { + int level = recommended_stream_analog_level_locked(); + data_dumper_->DumpRaw("experimental_gain_control_stream_analog_level", 1, + &level); + } + + // Compute echo-related stats. + if (submodules_.echo_controller) { + auto ec_metrics = submodules_.echo_controller->GetMetrics(); + capture_.stats.echo_return_loss = ec_metrics.echo_return_loss; + capture_.stats.echo_return_loss_enhancement = + ec_metrics.echo_return_loss_enhancement; + capture_.stats.delay_ms = ec_metrics.delay_ms; + } + if (config_.residual_echo_detector.enabled) { + RTC_DCHECK(submodules_.echo_detector); + auto ed_metrics = submodules_.echo_detector->GetMetrics(); + capture_.stats.residual_echo_likelihood = ed_metrics.echo_likelihood; + capture_.stats.residual_echo_likelihood_recent_max = + ed_metrics.echo_likelihood_recent_max; + } + + // Pass stats for reporting. + stats_reporter_.UpdateStatistics(capture_.stats); + + capture_.was_stream_delay_set = false; return kNoError; } int AudioProcessingImpl::AnalyzeReverseStream( - const float* const* src, - const StreamConfig& reverse_input_config, - const StreamConfig& reverse_output_config) { - CriticalSectionScoped crit_scoped(crit_); - if (src == NULL) { - return kNullPointerError; - } - - if (reverse_input_config.num_channels() <= 0) { - return kBadNumberChannelsError; - } - - ProcessingConfig processing_config = api_format_; - processing_config.reverse_input_stream() = reverse_input_config; - processing_config.reverse_output_stream() = reverse_output_config; - - RETURN_ON_ERR(MaybeInitializeLocked(processing_config)); - assert(reverse_input_config.num_frames() == - api_format_.reverse_input_stream().num_frames()); - -#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 channel_size = - sizeof(float) * api_format_.reverse_input_stream().num_frames(); - for (int i = 0; i < api_format_.reverse_input_stream().num_channels(); ++i) - msg->add_channel(src[i], channel_size); - RETURN_ON_ERR(WriteMessageToDebugFile()); - } -#endif - - render_audio_->CopyFrom(src, api_format_.reverse_input_stream()); - return ProcessReverseStreamLocked(); + const float* const* data, + const StreamConfig& reverse_config) { + TRACE_EVENT0("webrtc", "AudioProcessing::AnalyzeReverseStream_StreamConfig"); + MutexLock lock(&mutex_render_); + return AnalyzeReverseStreamLocked(data, reverse_config, reverse_config); } -int AudioProcessingImpl::ProcessReverseStream(AudioFrame* frame) { - RETURN_ON_ERR(AnalyzeReverseStream(frame)); - if (is_rev_processed()) { - render_audio_->InterleaveTo(frame, true); +int AudioProcessingImpl::ProcessReverseStream(const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config, + float* const* dest) { + TRACE_EVENT0("webrtc", "AudioProcessing::ProcessReverseStream_StreamConfig"); + MutexLock lock(&mutex_render_); + RETURN_ON_ERR(AnalyzeReverseStreamLocked(src, input_config, output_config)); + if (submodule_states_.RenderMultiBandProcessingActive() || + submodule_states_.RenderFullBandProcessingActive()) { + render_.render_audio->CopyTo(formats_.api_format.reverse_output_stream(), + dest); + } else if (formats_.api_format.reverse_input_stream() != + formats_.api_format.reverse_output_stream()) { + render_.render_converter->Convert(src, input_config.num_samples(), dest, + output_config.num_samples()); + } else { + CopyAudioIfNeeded(src, input_config.num_frames(), + input_config.num_channels(), dest); } return kNoError; } -int AudioProcessingImpl::AnalyzeReverseStream(AudioFrame* frame) { - CriticalSectionScoped crit_scoped(crit_); - if (frame == NULL) { +int AudioProcessingImpl::AnalyzeReverseStreamLocked( + const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config) { + if (src == nullptr) { return kNullPointerError; } - // Must be a native rate. - if (frame->sample_rate_hz_ != kSampleRate8kHz && - frame->sample_rate_hz_ != kSampleRate16kHz && - frame->sample_rate_hz_ != kSampleRate32kHz && - frame->sample_rate_hz_ != kSampleRate48kHz) { - return kBadSampleRateError; - } - // This interface does not tolerate different forward and reverse rates. - if (frame->sample_rate_hz_ != api_format_.input_stream().sample_rate_hz()) { - return kBadSampleRateError; - } - if (frame->num_channels_ <= 0) { + if (input_config.num_channels() == 0) { return kBadNumberChannelsError; } - ProcessingConfig processing_config = api_format_; - processing_config.reverse_input_stream().set_sample_rate_hz( - frame->sample_rate_hz_); - processing_config.reverse_input_stream().set_num_channels( - frame->num_channels_); - processing_config.reverse_output_stream().set_sample_rate_hz( - frame->sample_rate_hz_); - processing_config.reverse_output_stream().set_num_channels( - frame->num_channels_); + ProcessingConfig processing_config = formats_.api_format; + processing_config.reverse_input_stream() = input_config; + processing_config.reverse_output_stream() = output_config; - RETURN_ON_ERR(MaybeInitializeLocked(processing_config)); - if (frame->samples_per_channel_ != - api_format_.reverse_input_stream().num_frames()) { + RETURN_ON_ERR(MaybeInitializeRender(processing_config)); + RTC_DCHECK_EQ(input_config.num_frames(), + formats_.api_format.reverse_input_stream().num_frames()); + + if (aec_dump_) { + const size_t channel_size = + formats_.api_format.reverse_input_stream().num_frames(); + const size_t num_channels = + formats_.api_format.reverse_input_stream().num_channels(); + aec_dump_->WriteRenderStreamMessage( + AudioFrameView(src, num_channels, channel_size)); + } + render_.render_audio->CopyFrom(src, + formats_.api_format.reverse_input_stream()); + return ProcessRenderStreamLocked(); +} + +int AudioProcessingImpl::ProcessReverseStream(const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest) { + TRACE_EVENT0("webrtc", "AudioProcessing::ProcessReverseStream_AudioFrame"); + + if (input_config.num_channels() <= 0) { + return AudioProcessing::Error::kBadNumberChannelsError; + } + + MutexLock lock(&mutex_render_); + ProcessingConfig processing_config = formats_.api_format; + processing_config.reverse_input_stream().set_sample_rate_hz( + input_config.sample_rate_hz()); + processing_config.reverse_input_stream().set_num_channels( + input_config.num_channels()); + processing_config.reverse_output_stream().set_sample_rate_hz( + output_config.sample_rate_hz()); + processing_config.reverse_output_stream().set_num_channels( + output_config.num_channels()); + + RETURN_ON_ERR(MaybeInitializeRender(processing_config)); + if (input_config.num_frames() != + formats_.api_format.reverse_input_stream().num_frames()) { 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->samples_per_channel_ * frame->num_channels_; - msg->set_data(frame->data_, data_size); - RETURN_ON_ERR(WriteMessageToDebugFile()); + if (aec_dump_) { + aec_dump_->WriteRenderStreamMessage(src, input_config.num_frames(), + input_config.num_channels()); } -#endif - render_audio_->DeinterleaveFrom(frame); - return ProcessReverseStreamLocked(); + + render_.render_audio->CopyFrom(src, input_config); + RETURN_ON_ERR(ProcessRenderStreamLocked()); + if (submodule_states_.RenderMultiBandProcessingActive() || + submodule_states_.RenderFullBandProcessingActive()) { + render_.render_audio->CopyTo(output_config, dest); + } + return kNoError; } -int AudioProcessingImpl::ProcessReverseStreamLocked() { - AudioBuffer* ra = render_audio_.get(); // For brevity. - if (rev_proc_format_.sample_rate_hz() == kSampleRate32kHz) { - ra->SplitIntoFrequencyBands(); +int AudioProcessingImpl::ProcessRenderStreamLocked() { + AudioBuffer* render_buffer = render_.render_audio.get(); // For brevity. + + HandleRenderRuntimeSettings(); + + if (submodules_.render_pre_processor) { + submodules_.render_pre_processor->Process(render_buffer); } - if (intelligibility_enabled_) { - intelligibility_enhancer_->ProcessRenderAudio( - ra->split_channels_f(kBand0To8kHz), split_rate_, ra->num_channels()); + QueueNonbandedRenderAudio(render_buffer); + + if (submodule_states_.RenderMultiBandSubModulesActive() && + SampleRateSupportsMultiBand( + formats_.render_processing_format.sample_rate_hz())) { + render_buffer->SplitIntoFrequencyBands(); } - RETURN_ON_ERR(echo_cancellation_->ProcessRenderAudio(ra)); - RETURN_ON_ERR(echo_control_mobile_->ProcessRenderAudio(ra)); - if (!use_new_agc_) { - RETURN_ON_ERR(gain_control_->ProcessRenderAudio(ra)); + if (submodule_states_.RenderMultiBandSubModulesActive()) { + QueueBandedRenderAudio(render_buffer); } - if (rev_proc_format_.sample_rate_hz() == kSampleRate32kHz && - is_rev_processed()) { - ra->MergeFrequencyBands(); + // TODO(peah): Perform the queuing inside QueueRenderAudiuo(). + if (submodules_.echo_controller) { + submodules_.echo_controller->AnalyzeRender(render_buffer); + } + + if (submodule_states_.RenderMultiBandProcessingActive() && + SampleRateSupportsMultiBand( + formats_.render_processing_format.sample_rate_hz())) { + render_buffer->MergeFrequencyBands(); } return kNoError; } int AudioProcessingImpl::set_stream_delay_ms(int delay) { + MutexLock lock(&mutex_capture_); Error retval = kNoError; - was_stream_delay_set_ = true; - delay += delay_offset_ms_; + capture_.was_stream_delay_set = true; if (delay < 0) { delay = 0; @@ -876,400 +1478,602 @@ int AudioProcessingImpl::set_stream_delay_ms(int delay) { retval = kBadStreamParameterWarning; } - stream_delay_ms_ = delay; + capture_nonlocked_.stream_delay_ms = delay; return retval; } -int AudioProcessingImpl::stream_delay_ms() const { - return stream_delay_ms_; -} +bool AudioProcessingImpl::GetLinearAecOutput( + rtc::ArrayView> linear_output) const { + MutexLock lock(&mutex_capture_); + AudioBuffer* linear_aec_buffer = capture_.linear_aec_output.get(); -bool AudioProcessingImpl::was_stream_delay_set() const { - return was_stream_delay_set_; -} + RTC_DCHECK(linear_aec_buffer); + if (linear_aec_buffer) { + RTC_DCHECK_EQ(1, linear_aec_buffer->num_bands()); + RTC_DCHECK_EQ(linear_output.size(), linear_aec_buffer->num_channels()); -void AudioProcessingImpl::set_stream_key_pressed(bool key_pressed) { - key_pressed_ = key_pressed; -} - -void AudioProcessingImpl::set_delay_offset_ms(int offset) { - CriticalSectionScoped crit_scoped(crit_); - delay_offset_ms_ = offset; -} - -int AudioProcessingImpl::delay_offset_ms() const { - return delay_offset_ms_; -} - -int AudioProcessingImpl::StartDebugRecording( - const char filename[AudioProcessing::kMaxFilenameSize]) { - CriticalSectionScoped crit_scoped(crit_); - static_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; + for (size_t ch = 0; ch < linear_aec_buffer->num_channels(); ++ch) { + RTC_DCHECK_EQ(linear_output[ch].size(), linear_aec_buffer->num_frames()); + rtc::ArrayView channel_view = + rtc::ArrayView(linear_aec_buffer->channels_const()[ch], + linear_aec_buffer->num_frames()); + std::copy(channel_view.begin(), channel_view.end(), + linear_output[ch].begin()); } - } - - if (debug_file_->OpenFile(filename, false) == -1) { - debug_file_->CloseFile(); - return kFileError; - } - - RETURN_ON_ERR(WriteConfigMessage(true)); - RETURN_ON_ERR(WriteInitMessage()); - return kNoError; -#else - return kUnsupportedFunctionError; -#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP -} - -int AudioProcessingImpl::StartDebugRecording(FILE* handle) { - CriticalSectionScoped crit_scoped(crit_); - - if (handle == 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_->OpenFromFileHandle(handle, true, false) == -1) { - return kFileError; - } - - RETURN_ON_ERR(WriteConfigMessage(true)); - RETURN_ON_ERR(WriteInitMessage()); - return kNoError; -#else - return kUnsupportedFunctionError; -#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP -} - -int AudioProcessingImpl::StartDebugRecordingForPlatformFile( - rtc::PlatformFile handle) { - FILE* stream = rtc::FdopenPlatformFileForWriting(handle); - return StartDebugRecording(stream); -} - -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 { - if (use_new_agc_) { - return gain_control_for_new_agc_.get(); - } - 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_; -} - -bool AudioProcessingImpl::is_data_processed() const { - if (beamformer_enabled_) { - return true; - } - - int enabled_count = 0; - for (auto item : component_list_) { - if (item->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::output_copy_needed(bool is_data_processed) const { - // Check if we've upmixed or downmixed the audio. - return ((api_format_.output_stream().num_channels() != - api_format_.input_stream().num_channels()) || - is_data_processed || transient_suppressor_enabled_); -} - -bool AudioProcessingImpl::synthesis_needed(bool is_data_processed) const { - return (is_data_processed && - (fwd_proc_format_.sample_rate_hz() == kSampleRate32kHz || - fwd_proc_format_.sample_rate_hz() == kSampleRate48kHz)); -} - -bool AudioProcessingImpl::analysis_needed(bool is_data_processed) const { - if (!is_data_processed && !voice_detection_->is_enabled() && - !transient_suppressor_enabled_) { - // Only level_estimator_ is enabled. - return false; - } else if (fwd_proc_format_.sample_rate_hz() == kSampleRate32kHz || - fwd_proc_format_.sample_rate_hz() == kSampleRate48kHz) { - // Something besides level_estimator_ is enabled, and we have super-wb. return true; } + RTC_LOG(LS_ERROR) << "No linear AEC output available"; + RTC_NOTREACHED(); return false; } -bool AudioProcessingImpl::is_rev_processed() const { - return intelligibility_enabled_ && intelligibility_enhancer_->active(); +int AudioProcessingImpl::stream_delay_ms() const { + // Used as callback from submodules, hence locking is not allowed. + return capture_nonlocked_.stream_delay_ms; } -bool AudioProcessingImpl::rev_conversion_needed() const { - return (api_format_.reverse_input_stream() != - api_format_.reverse_output_stream()); +void AudioProcessingImpl::set_stream_key_pressed(bool key_pressed) { + MutexLock lock(&mutex_capture_); + capture_.key_pressed = key_pressed; } -void AudioProcessingImpl::InitializeExperimentalAgc() { - if (use_new_agc_) { - if (!agc_manager_.get()) { - agc_manager_.reset(new AgcManagerDirect(gain_control_, - gain_control_for_new_agc_.get(), - agc_startup_min_volume_)); - } - agc_manager_->Initialize(); - agc_manager_->SetCaptureMuted(output_will_be_muted_); +void AudioProcessingImpl::set_stream_analog_level(int level) { + MutexLock lock_capture(&mutex_capture_); + + if (submodules_.agc_manager) { + submodules_.agc_manager->set_stream_analog_level(level); + data_dumper_->DumpRaw("experimental_gain_control_set_stream_analog_level", + 1, &level); + } else if (submodules_.gain_control) { + int error = submodules_.gain_control->set_stream_analog_level(level); + RTC_DCHECK_EQ(kNoError, error); + } else { + capture_.cached_stream_analog_level_ = level; } } -void AudioProcessingImpl::InitializeTransient() { - if (transient_suppressor_enabled_) { - if (!transient_suppressor_.get()) { - transient_suppressor_.reset(new TransientSuppressor()); - } - transient_suppressor_->Initialize( - fwd_proc_format_.sample_rate_hz(), split_rate_, - api_format_.output_stream().num_channels()); +int AudioProcessingImpl::recommended_stream_analog_level() const { + MutexLock lock_capture(&mutex_capture_); + return recommended_stream_analog_level_locked(); +} + +int AudioProcessingImpl::recommended_stream_analog_level_locked() const { + if (submodules_.agc_manager) { + return submodules_.agc_manager->stream_analog_level(); + } else if (submodules_.gain_control) { + return submodules_.gain_control->stream_analog_level(); + } else { + return capture_.cached_stream_analog_level_; } } -void AudioProcessingImpl::InitializeBeamformer() { - if (beamformer_enabled_) { - if (!beamformer_) { - beamformer_.reset( - new NonlinearBeamformer(array_geometry_, target_direction_)); - } - beamformer_->Initialize(kChunkSizeMs, split_rate_); +bool AudioProcessingImpl::CreateAndAttachAecDump(const std::string& file_name, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue) { + std::unique_ptr aec_dump = + AecDumpFactory::Create(file_name, max_log_size_bytes, worker_queue); + if (!aec_dump) { + return false; + } + + AttachAecDump(std::move(aec_dump)); + return true; +} + +bool AudioProcessingImpl::CreateAndAttachAecDump(FILE* handle, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue) { + std::unique_ptr aec_dump = + AecDumpFactory::Create(handle, max_log_size_bytes, worker_queue); + if (!aec_dump) { + return false; + } + + AttachAecDump(std::move(aec_dump)); + return true; +} + +void AudioProcessingImpl::AttachAecDump(std::unique_ptr aec_dump) { + RTC_DCHECK(aec_dump); + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + + // The previously attached AecDump will be destroyed with the + // 'aec_dump' parameter, which is after locks are released. + aec_dump_.swap(aec_dump); + WriteAecDumpConfigMessage(true); + aec_dump_->WriteInitMessage(formats_.api_format, rtc::TimeUTCMillis()); +} + +void AudioProcessingImpl::DetachAecDump() { + // The d-tor of a task-queue based AecDump blocks until all pending + // tasks are done. This construction avoids blocking while holding + // the render and capture locks. + std::unique_ptr aec_dump = nullptr; + { + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + aec_dump = std::move(aec_dump_); } } -void AudioProcessingImpl::InitializeIntelligibility() { - if (intelligibility_enabled_) { - IntelligibilityEnhancer::Config config; - config.sample_rate_hz = split_rate_; - config.num_capture_channels = capture_audio_->num_channels(); - config.num_render_channels = render_audio_->num_channels(); - intelligibility_enhancer_.reset(new IntelligibilityEnhancer(config)); +void AudioProcessingImpl::MutateConfig( + rtc::FunctionView mutator) { + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + mutator(&config_); + ApplyConfig(config_); +} + +AudioProcessing::Config AudioProcessingImpl::GetConfig() const { + MutexLock lock_render(&mutex_render_); + MutexLock lock_capture(&mutex_capture_); + return config_; +} + +bool AudioProcessingImpl::UpdateActiveSubmoduleStates() { + return submodule_states_.Update( + config_.high_pass_filter.enabled, !!submodules_.echo_control_mobile, + config_.residual_echo_detector.enabled, !!submodules_.noise_suppressor, + !!submodules_.gain_control, !!submodules_.gain_controller2, + config_.pre_amplifier.enabled, capture_nonlocked_.echo_controller_enabled, + config_.voice_detection.enabled, !!submodules_.transient_suppressor); +} + +void AudioProcessingImpl::InitializeTransientSuppressor() { + if (config_.transient_suppression.enabled) { + // Attempt to create a transient suppressor, if one is not already created. + if (!submodules_.transient_suppressor) { + submodules_.transient_suppressor = + CreateTransientSuppressor(submodule_creation_overrides_); + } + if (submodules_.transient_suppressor) { + submodules_.transient_suppressor->Initialize( + proc_fullband_sample_rate_hz(), capture_nonlocked_.split_rate, + num_proc_channels()); + } else { + RTC_LOG(LS_WARNING) + << "No transient suppressor created (probably disabled)"; + } + } else { + submodules_.transient_suppressor.reset(); } } -void AudioProcessingImpl::MaybeUpdateHistograms() { - static const int kMinDiffDelayMs = 60; +void AudioProcessingImpl::InitializeHighPassFilter(bool forced_reset) { + bool high_pass_filter_needed_by_aec = + config_.echo_canceller.enabled && + config_.echo_canceller.enforce_high_pass_filtering && + !config_.echo_canceller.mobile_mode; + if (submodule_states_.HighPassFilteringRequired() || + high_pass_filter_needed_by_aec) { + bool use_full_band = config_.high_pass_filter.apply_in_full_band && + !constants_.enforce_split_band_hpf; + int rate = use_full_band ? proc_fullband_sample_rate_hz() + : proc_split_sample_rate_hz(); + size_t num_channels = + use_full_band ? num_output_channels() : num_proc_channels(); - if (echo_cancellation()->is_enabled()) { - // Activate delay_jumps_ counters if we know echo_cancellation is runnning. - // If a stream has echo we know that the echo_cancellation is in process. - if (stream_delay_jumps_ == -1 && echo_cancellation()->stream_has_echo()) { - stream_delay_jumps_ = 0; + if (!submodules_.high_pass_filter || + rate != submodules_.high_pass_filter->sample_rate_hz() || + forced_reset || + num_channels != submodules_.high_pass_filter->num_channels()) { + submodules_.high_pass_filter.reset( + new HighPassFilter(rate, num_channels)); } - if (aec_system_delay_jumps_ == -1 && - echo_cancellation()->stream_has_echo()) { - aec_system_delay_jumps_ = 0; + } else { + submodules_.high_pass_filter.reset(); + } +} + +void AudioProcessingImpl::InitializeVoiceDetector() { + if (config_.voice_detection.enabled) { + submodules_.voice_detector = std::make_unique( + proc_split_sample_rate_hz(), VoiceDetection::kVeryLowLikelihood); + } else { + submodules_.voice_detector.reset(); + } +} +void AudioProcessingImpl::InitializeEchoController() { + bool use_echo_controller = + echo_control_factory_ || + (config_.echo_canceller.enabled && !config_.echo_canceller.mobile_mode); + + if (use_echo_controller) { + // Create and activate the echo controller. + if (echo_control_factory_) { + submodules_.echo_controller = echo_control_factory_->Create( + proc_sample_rate_hz(), num_reverse_channels(), num_proc_channels()); + RTC_DCHECK(submodules_.echo_controller); + } else { + EchoCanceller3Config config = + use_setup_specific_default_aec3_config_ + ? EchoCanceller3::CreateDefaultConfig(num_reverse_channels(), + num_proc_channels()) + : EchoCanceller3Config(); + submodules_.echo_controller = std::make_unique( + config, proc_sample_rate_hz(), num_reverse_channels(), + num_proc_channels()); } - // Detect a jump in platform reported system delay and log the difference. - const int diff_stream_delay_ms = stream_delay_ms_ - last_stream_delay_ms_; - if (diff_stream_delay_ms > kMinDiffDelayMs && last_stream_delay_ms_ != 0) { - RTC_HISTOGRAM_COUNTS("WebRTC.Audio.PlatformReportedStreamDelayJump", - diff_stream_delay_ms, kMinDiffDelayMs, 1000, 100); - if (stream_delay_jumps_ == -1) { - stream_delay_jumps_ = 0; // Activate counter if needed. - } - stream_delay_jumps_++; + // Setup the storage for returning the linear AEC output. + if (config_.echo_canceller.export_linear_aec_output) { + constexpr int kLinearOutputRateHz = 16000; + capture_.linear_aec_output = std::make_unique( + kLinearOutputRateHz, num_proc_channels(), kLinearOutputRateHz, + num_proc_channels(), kLinearOutputRateHz, num_proc_channels()); + } else { + capture_.linear_aec_output.reset(); } - last_stream_delay_ms_ = stream_delay_ms_; - // Detect a jump in AEC system delay and log the difference. - const int frames_per_ms = rtc::CheckedDivExact(split_rate_, 1000); - const int aec_system_delay_ms = - WebRtcAec_system_delay(echo_cancellation()->aec_core()) / frames_per_ms; - const int diff_aec_system_delay_ms = - aec_system_delay_ms - last_aec_system_delay_ms_; - if (diff_aec_system_delay_ms > kMinDiffDelayMs && - last_aec_system_delay_ms_ != 0) { - RTC_HISTOGRAM_COUNTS("WebRTC.Audio.AecSystemDelayJump", - diff_aec_system_delay_ms, kMinDiffDelayMs, 1000, - 100); - if (aec_system_delay_jumps_ == -1) { - aec_system_delay_jumps_ = 0; // Activate counter if needed. - } - aec_system_delay_jumps_++; + capture_nonlocked_.echo_controller_enabled = true; + + submodules_.echo_control_mobile.reset(); + aecm_render_signal_queue_.reset(); + return; + } + + submodules_.echo_controller.reset(); + capture_nonlocked_.echo_controller_enabled = false; + capture_.linear_aec_output.reset(); + + if (!config_.echo_canceller.enabled) { + submodules_.echo_control_mobile.reset(); + aecm_render_signal_queue_.reset(); + return; + } + + if (config_.echo_canceller.mobile_mode) { + // Create and activate AECM. + size_t max_element_size = + std::max(static_cast(1), + kMaxAllowedValuesOfSamplesPerBand * + EchoControlMobileImpl::NumCancellersRequired( + num_output_channels(), num_reverse_channels())); + + std::vector template_queue_element(max_element_size); + + aecm_render_signal_queue_.reset( + new SwapQueue, RenderQueueItemVerifier>( + kMaxNumFramesToBuffer, template_queue_element, + RenderQueueItemVerifier(max_element_size))); + + aecm_render_queue_buffer_.resize(max_element_size); + aecm_capture_queue_buffer_.resize(max_element_size); + + submodules_.echo_control_mobile.reset(new EchoControlMobileImpl()); + + submodules_.echo_control_mobile->Initialize(proc_split_sample_rate_hz(), + num_reverse_channels(), + num_output_channels()); + return; + } + + submodules_.echo_control_mobile.reset(); + aecm_render_signal_queue_.reset(); +} + +void AudioProcessingImpl::InitializeGainController1() { + if (!config_.gain_controller1.enabled) { + submodules_.agc_manager.reset(); + submodules_.gain_control.reset(); + return; + } + + if (!submodules_.gain_control) { + submodules_.gain_control.reset(new GainControlImpl()); + } + + submodules_.gain_control->Initialize(num_proc_channels(), + proc_sample_rate_hz()); + + if (!config_.gain_controller1.analog_gain_controller.enabled) { + int error = submodules_.gain_control->set_mode( + Agc1ConfigModeToInterfaceMode(config_.gain_controller1.mode)); + RTC_DCHECK_EQ(kNoError, error); + error = submodules_.gain_control->set_target_level_dbfs( + config_.gain_controller1.target_level_dbfs); + RTC_DCHECK_EQ(kNoError, error); + error = submodules_.gain_control->set_compression_gain_db( + config_.gain_controller1.compression_gain_db); + RTC_DCHECK_EQ(kNoError, error); + error = submodules_.gain_control->enable_limiter( + config_.gain_controller1.enable_limiter); + RTC_DCHECK_EQ(kNoError, error); + error = submodules_.gain_control->set_analog_level_limits( + config_.gain_controller1.analog_level_minimum, + config_.gain_controller1.analog_level_maximum); + RTC_DCHECK_EQ(kNoError, error); + + submodules_.agc_manager.reset(); + return; + } + + if (!submodules_.agc_manager.get() || + submodules_.agc_manager->num_channels() != + static_cast(num_proc_channels()) || + submodules_.agc_manager->sample_rate_hz() != + capture_nonlocked_.split_rate) { + int stream_analog_level = -1; + const bool re_creation = !!submodules_.agc_manager; + if (re_creation) { + stream_analog_level = submodules_.agc_manager->stream_analog_level(); } - last_aec_system_delay_ms_ = aec_system_delay_ms; + submodules_.agc_manager.reset(new AgcManagerDirect( + num_proc_channels(), + config_.gain_controller1.analog_gain_controller.startup_min_volume, + config_.gain_controller1.analog_gain_controller.clipped_level_min, + config_.gain_controller1.analog_gain_controller + .enable_agc2_level_estimator, + !config_.gain_controller1.analog_gain_controller + .enable_digital_adaptive, + capture_nonlocked_.split_rate)); + if (re_creation) { + submodules_.agc_manager->set_stream_analog_level(stream_analog_level); + } + } + submodules_.agc_manager->Initialize(); + submodules_.agc_manager->SetupDigitalGainControl( + submodules_.gain_control.get()); + submodules_.agc_manager->SetCaptureMuted(capture_.output_will_be_muted); +} + +void AudioProcessingImpl::InitializeGainController2() { + if (config_.gain_controller2.enabled) { + if (!submodules_.gain_controller2) { + // TODO(alessiob): Move the injected gain controller once injection is + // implemented. + submodules_.gain_controller2.reset(new GainController2()); + } + + submodules_.gain_controller2->Initialize(proc_fullband_sample_rate_hz()); + submodules_.gain_controller2->ApplyConfig(config_.gain_controller2); + } else { + submodules_.gain_controller2.reset(); } } -void AudioProcessingImpl::UpdateHistogramsOnCallEnd() { - CriticalSectionScoped crit_scoped(crit_); - if (stream_delay_jumps_ > -1) { - RTC_HISTOGRAM_ENUMERATION( - "WebRTC.Audio.NumOfPlatformReportedStreamDelayJumps", - stream_delay_jumps_, 51); - } - stream_delay_jumps_ = -1; - last_stream_delay_ms_ = 0; +void AudioProcessingImpl::InitializeNoiseSuppressor() { + submodules_.noise_suppressor.reset(); - if (aec_system_delay_jumps_ > -1) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.Audio.NumOfAecSystemDelayJumps", - aec_system_delay_jumps_, 51); + if (config_.noise_suppression.enabled) { + auto map_level = + [](AudioProcessing::Config::NoiseSuppression::Level level) { + using NoiseSuppresionConfig = + AudioProcessing::Config::NoiseSuppression; + switch (level) { + case NoiseSuppresionConfig::kLow: + return NsConfig::SuppressionLevel::k6dB; + case NoiseSuppresionConfig::kModerate: + return NsConfig::SuppressionLevel::k12dB; + case NoiseSuppresionConfig::kHigh: + return NsConfig::SuppressionLevel::k18dB; + case NoiseSuppresionConfig::kVeryHigh: + return NsConfig::SuppressionLevel::k21dB; + default: + RTC_NOTREACHED(); + } + }; + + NsConfig cfg; + cfg.target_level = map_level(config_.noise_suppression.level); + submodules_.noise_suppressor = std::make_unique( + cfg, proc_sample_rate_hz(), num_proc_channels()); } - aec_system_delay_jumps_ = -1; - last_aec_system_delay_ms_ = 0; } -#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP -int AudioProcessingImpl::WriteMessageToDebugFile() { - int32_t size = event_msg_->ByteSize(); - if (size <= 0) { - return kUnspecifiedError; +void AudioProcessingImpl::InitializePreAmplifier() { + if (config_.pre_amplifier.enabled) { + submodules_.pre_amplifier.reset( + new GainApplier(true, config_.pre_amplifier.fixed_gain_factor)); + } else { + submodules_.pre_amplifier.reset(); } -#if defined(WEBRTC_ARCH_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 kNoError; } -int AudioProcessingImpl::WriteInitMessage() { - event_msg_->set_type(audioproc::Event::INIT); - audioproc::Init* msg = event_msg_->mutable_init(); - msg->set_sample_rate(api_format_.input_stream().sample_rate_hz()); - msg->set_num_input_channels(api_format_.input_stream().num_channels()); - msg->set_num_output_channels(api_format_.output_stream().num_channels()); - msg->set_num_reverse_channels( - api_format_.reverse_input_stream().num_channels()); - msg->set_reverse_sample_rate( - api_format_.reverse_input_stream().sample_rate_hz()); - msg->set_output_sample_rate(api_format_.output_stream().sample_rate_hz()); - // TODO(ekmeyerson): Add reverse output fields to event_msg_. - - RETURN_ON_ERR(WriteMessageToDebugFile()); - return kNoError; +void AudioProcessingImpl::InitializeResidualEchoDetector() { + RTC_DCHECK(submodules_.echo_detector); + submodules_.echo_detector->Initialize( + proc_fullband_sample_rate_hz(), 1, + formats_.render_processing_format.sample_rate_hz(), 1); } -int AudioProcessingImpl::WriteConfigMessage(bool forced) { - audioproc::Config config; +void AudioProcessingImpl::InitializeAnalyzer() { + if (submodules_.capture_analyzer) { + submodules_.capture_analyzer->Initialize(proc_fullband_sample_rate_hz(), + num_proc_channels()); + } +} - config.set_aec_enabled(echo_cancellation_->is_enabled()); - config.set_aec_delay_agnostic_enabled( - echo_cancellation_->is_delay_agnostic_enabled()); - config.set_aec_drift_compensation_enabled( - echo_cancellation_->is_drift_compensation_enabled()); - config.set_aec_extended_filter_enabled( - echo_cancellation_->is_extended_filter_enabled()); - config.set_aec_suppression_level( - static_cast(echo_cancellation_->suppression_level())); +void AudioProcessingImpl::InitializePostProcessor() { + if (submodules_.capture_post_processor) { + submodules_.capture_post_processor->Initialize( + proc_fullband_sample_rate_hz(), num_proc_channels()); + } +} - config.set_aecm_enabled(echo_control_mobile_->is_enabled()); - config.set_aecm_comfort_noise_enabled( - echo_control_mobile_->is_comfort_noise_enabled()); - config.set_aecm_routing_mode( - static_cast(echo_control_mobile_->routing_mode())); +void AudioProcessingImpl::InitializePreProcessor() { + if (submodules_.render_pre_processor) { + submodules_.render_pre_processor->Initialize( + formats_.render_processing_format.sample_rate_hz(), + formats_.render_processing_format.num_channels()); + } +} - config.set_agc_enabled(gain_control_->is_enabled()); - config.set_agc_mode(static_cast(gain_control_->mode())); - config.set_agc_limiter_enabled(gain_control_->is_limiter_enabled()); - config.set_noise_robust_agc_enabled(use_new_agc_); - - config.set_hpf_enabled(high_pass_filter_->is_enabled()); - - config.set_ns_enabled(noise_suppression_->is_enabled()); - config.set_ns_level(static_cast(noise_suppression_->level())); - - config.set_transient_suppression_enabled(transient_suppressor_enabled_); - - std::string serialized_config = config.SerializeAsString(); - if (!forced && last_serialized_config_ == serialized_config) { - return kNoError; +void AudioProcessingImpl::WriteAecDumpConfigMessage(bool forced) { + if (!aec_dump_) { + return; } - last_serialized_config_ = serialized_config; + std::string experiments_description = ""; + // TODO(peah): Add semicolon-separated concatenations of experiment + // descriptions for other submodules. + if (config_.gain_controller1.analog_gain_controller.clipped_level_min != + kClippedLevelMin) { + experiments_description += "AgcClippingLevelExperiment;"; + } + if (!!submodules_.capture_post_processor) { + experiments_description += "CapturePostProcessor;"; + } + if (!!submodules_.render_pre_processor) { + experiments_description += "RenderPreProcessor;"; + } + if (capture_nonlocked_.echo_controller_enabled) { + experiments_description += "EchoController;"; + } + if (config_.gain_controller2.enabled) { + experiments_description += "GainController2;"; + } - event_msg_->set_type(audioproc::Event::CONFIG); - event_msg_->mutable_config()->CopyFrom(config); + InternalAPMConfig apm_config; - RETURN_ON_ERR(WriteMessageToDebugFile()); - return kNoError; + apm_config.aec_enabled = config_.echo_canceller.enabled; + apm_config.aec_delay_agnostic_enabled = false; + apm_config.aec_extended_filter_enabled = false; + apm_config.aec_suppression_level = 0; + + apm_config.aecm_enabled = !!submodules_.echo_control_mobile; + apm_config.aecm_comfort_noise_enabled = + submodules_.echo_control_mobile && + submodules_.echo_control_mobile->is_comfort_noise_enabled(); + apm_config.aecm_routing_mode = + submodules_.echo_control_mobile + ? static_cast(submodules_.echo_control_mobile->routing_mode()) + : 0; + + apm_config.agc_enabled = !!submodules_.gain_control; + + apm_config.agc_mode = submodules_.gain_control + ? static_cast(submodules_.gain_control->mode()) + : GainControl::kAdaptiveAnalog; + apm_config.agc_limiter_enabled = + submodules_.gain_control ? submodules_.gain_control->is_limiter_enabled() + : false; + apm_config.noise_robust_agc_enabled = !!submodules_.agc_manager; + + apm_config.hpf_enabled = config_.high_pass_filter.enabled; + + apm_config.ns_enabled = config_.noise_suppression.enabled; + apm_config.ns_level = static_cast(config_.noise_suppression.level); + + apm_config.transient_suppression_enabled = + config_.transient_suppression.enabled; + apm_config.experiments_description = experiments_description; + apm_config.pre_amplifier_enabled = config_.pre_amplifier.enabled; + apm_config.pre_amplifier_fixed_gain_factor = + config_.pre_amplifier.fixed_gain_factor; + + if (!forced && apm_config == apm_config_for_aec_dump_) { + return; + } + aec_dump_->WriteConfig(apm_config); + apm_config_for_aec_dump_ = apm_config; +} + +void AudioProcessingImpl::RecordUnprocessedCaptureStream( + const float* const* src) { + RTC_DCHECK(aec_dump_); + WriteAecDumpConfigMessage(false); + + const size_t channel_size = formats_.api_format.input_stream().num_frames(); + const size_t num_channels = formats_.api_format.input_stream().num_channels(); + aec_dump_->AddCaptureStreamInput( + AudioFrameView(src, num_channels, channel_size)); + RecordAudioProcessingState(); +} + +void AudioProcessingImpl::RecordUnprocessedCaptureStream( + const int16_t* const data, + const StreamConfig& config) { + RTC_DCHECK(aec_dump_); + WriteAecDumpConfigMessage(false); + + aec_dump_->AddCaptureStreamInput(data, config.num_channels(), + config.num_frames()); + RecordAudioProcessingState(); +} + +void AudioProcessingImpl::RecordProcessedCaptureStream( + const float* const* processed_capture_stream) { + RTC_DCHECK(aec_dump_); + + const size_t channel_size = formats_.api_format.output_stream().num_frames(); + const size_t num_channels = + formats_.api_format.output_stream().num_channels(); + aec_dump_->AddCaptureStreamOutput(AudioFrameView( + processed_capture_stream, num_channels, channel_size)); + aec_dump_->WriteCaptureStreamMessage(); +} + +void AudioProcessingImpl::RecordProcessedCaptureStream( + const int16_t* const data, + const StreamConfig& config) { + RTC_DCHECK(aec_dump_); + + aec_dump_->AddCaptureStreamOutput(data, config.num_channels(), + config.num_frames()); + aec_dump_->WriteCaptureStreamMessage(); +} + +void AudioProcessingImpl::RecordAudioProcessingState() { + RTC_DCHECK(aec_dump_); + AecDump::AudioProcessingState audio_proc_state; + audio_proc_state.delay = capture_nonlocked_.stream_delay_ms; + audio_proc_state.drift = 0; + audio_proc_state.level = recommended_stream_analog_level_locked(); + audio_proc_state.keypress = capture_.key_pressed; + aec_dump_->AddAudioProcessingState(audio_proc_state); +} + +AudioProcessingImpl::ApmCaptureState::ApmCaptureState() + : was_stream_delay_set(false), + output_will_be_muted(false), + key_pressed(false), + capture_processing_format(kSampleRate16kHz), + split_rate(kSampleRate16kHz), + echo_path_gain_change(false), + prev_analog_mic_level(-1), + prev_pre_amp_gain(-1.f), + playout_volume(-1), + prev_playout_volume(-1) {} + +AudioProcessingImpl::ApmCaptureState::~ApmCaptureState() = default; + +void AudioProcessingImpl::ApmCaptureState::KeyboardInfo::Extract( + const float* const* data, + const StreamConfig& stream_config) { + if (stream_config.has_keyboard()) { + keyboard_data = data[stream_config.num_channels()]; + } else { + keyboard_data = NULL; + } + num_keyboard_frames = stream_config.num_frames(); +} + +AudioProcessingImpl::ApmRenderState::ApmRenderState() = default; + +AudioProcessingImpl::ApmRenderState::~ApmRenderState() = default; + +AudioProcessingImpl::ApmStatsReporter::ApmStatsReporter() + : stats_message_queue_(1) {} + +AudioProcessingImpl::ApmStatsReporter::~ApmStatsReporter() = default; + +AudioProcessingStats AudioProcessingImpl::ApmStatsReporter::GetStatistics() { + MutexLock lock_stats(&mutex_stats_); + bool new_stats_available = stats_message_queue_.Remove(&cached_stats_); + // If the message queue is full, return the cached stats. + static_cast(new_stats_available); + + return cached_stats_; +} + +void AudioProcessingImpl::ApmStatsReporter::UpdateStatistics( + const AudioProcessingStats& new_stats) { + AudioProcessingStats stats_to_queue = new_stats; + bool stats_message_passed = stats_message_queue_.Insert(&stats_to_queue); + // If the message queue is full, discard the new stats. + static_cast(stats_message_passed); } -#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP } // namespace webrtc diff --git a/webrtc/modules/audio_processing/audio_processing_impl.h b/webrtc/modules/audio_processing/audio_processing_impl.h index 542886e..d0eec0e 100644 --- a/webrtc/modules/audio_processing/audio_processing_impl.h +++ b/webrtc/modules/audio_processing/audio_processing_impl.h @@ -8,212 +8,514 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AUDIO_PROCESSING_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AUDIO_PROCESSING_IMPL_H_ +#ifndef MODULES_AUDIO_PROCESSING_AUDIO_PROCESSING_IMPL_H_ +#define MODULES_AUDIO_PROCESSING_AUDIO_PROCESSING_IMPL_H_ + +#include #include +#include #include #include -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/base/thread_annotations.h" -#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "api/function_view.h" +#include "modules/audio_processing/aec3/echo_canceller3.h" +#include "modules/audio_processing/agc/agc_manager_direct.h" +#include "modules/audio_processing/agc/gain_control.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/echo_control_mobile_impl.h" +#include "modules/audio_processing/gain_control_impl.h" +#include "modules/audio_processing/gain_controller2.h" +#include "modules/audio_processing/high_pass_filter.h" +#include "modules/audio_processing/include/aec_dump.h" +#include "modules/audio_processing/include/audio_frame_proxies.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "modules/audio_processing/include/audio_processing_statistics.h" +#include "modules/audio_processing/level_estimator.h" +#include "modules/audio_processing/ns/noise_suppressor.h" +#include "modules/audio_processing/optionally_built_submodule_creators.h" +#include "modules/audio_processing/render_queue_item_verifier.h" +#include "modules/audio_processing/residual_echo_detector.h" +#include "modules/audio_processing/rms_level.h" +#include "modules/audio_processing/transient/transient_suppressor.h" +#include "modules/audio_processing/voice_detection.h" +#include "rtc_base/gtest_prod_util.h" +#include "rtc_base/ignore_wundef.h" +#include "rtc_base/swap_queue.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { -class AgcManagerDirect; -class AudioBuffer; +class ApmDataDumper; class AudioConverter; -template -class Beamformer; - -class CriticalSectionWrapper; -class EchoCancellationImpl; -class EchoControlMobileImpl; -class FileWrapper; -class GainControlImpl; -class GainControlForNewAgc; -class HighPassFilterImpl; -class LevelEstimatorImpl; -class NoiseSuppressionImpl; -class ProcessingComponent; -class TransientSuppressor; -class VoiceDetectionImpl; -class IntelligibilityEnhancer; - -#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP -namespace audioproc { - -class Event; - -} // namespace audioproc -#endif - class AudioProcessingImpl : public AudioProcessing { public: - explicit AudioProcessingImpl(const Config& config); - - // AudioProcessingImpl takes ownership of beamformer. - AudioProcessingImpl(const Config& config, Beamformer* beamformer); - virtual ~AudioProcessingImpl(); - - // AudioProcessing methods. + // Methods forcing APM to run in a single-threaded manner. + // Acquires both the render and capture locks. + explicit AudioProcessingImpl(const webrtc::Config& config); + // AudioProcessingImpl takes ownership of capture post processor. + AudioProcessingImpl(const webrtc::Config& config, + std::unique_ptr capture_post_processor, + std::unique_ptr render_pre_processor, + std::unique_ptr echo_control_factory, + rtc::scoped_refptr echo_detector, + std::unique_ptr capture_analyzer); + ~AudioProcessingImpl() override; int Initialize() override; - int Initialize(int input_sample_rate_hz, - int output_sample_rate_hz, - int reverse_sample_rate_hz, - ChannelLayout input_layout, - ChannelLayout output_layout, - ChannelLayout reverse_layout) override; + int Initialize(int capture_input_sample_rate_hz, + int capture_output_sample_rate_hz, + int render_sample_rate_hz, + ChannelLayout capture_input_layout, + ChannelLayout capture_output_layout, + ChannelLayout render_input_layout) override; int Initialize(const ProcessingConfig& processing_config) override; - void SetExtraOptions(const Config& config) override; - int proc_sample_rate_hz() const override; - int proc_split_sample_rate_hz() const override; - int num_input_channels() const override; - int num_output_channels() const override; - int num_reverse_channels() const override; - void set_output_will_be_muted(bool muted) override; - int ProcessStream(AudioFrame* frame) override; - int ProcessStream(const float* const* src, - size_t samples_per_channel, - int input_sample_rate_hz, - ChannelLayout input_layout, - int output_sample_rate_hz, - ChannelLayout output_layout, - float* const* dest) override; + void ApplyConfig(const AudioProcessing::Config& config) override; + bool CreateAndAttachAecDump(const std::string& file_name, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue) override; + bool CreateAndAttachAecDump(FILE* handle, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue) override; + // TODO(webrtc:5298) Deprecated variant. + void AttachAecDump(std::unique_ptr aec_dump) override; + void DetachAecDump() override; + void SetRuntimeSetting(RuntimeSetting setting) override; + + // Capture-side exclusive methods possibly running APM in a + // multi-threaded manner. Acquire the capture lock. + int ProcessStream(const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest) override; int ProcessStream(const float* const* src, const StreamConfig& input_config, const StreamConfig& output_config, float* const* dest) override; - int AnalyzeReverseStream(AudioFrame* frame) override; - int ProcessReverseStream(AudioFrame* frame) override; - int AnalyzeReverseStream(const float* const* data, - size_t samples_per_channel, - int sample_rate_hz, - ChannelLayout layout) override; - int ProcessReverseStream(const float* const* src, - const StreamConfig& reverse_input_config, - const StreamConfig& reverse_output_config, - float* const* dest) override; + bool GetLinearAecOutput( + rtc::ArrayView> linear_output) const override; + void set_output_will_be_muted(bool muted) override; int set_stream_delay_ms(int delay) override; - int stream_delay_ms() const override; - bool was_stream_delay_set() const override; - void set_delay_offset_ms(int offset) override; - int delay_offset_ms() const override; void set_stream_key_pressed(bool key_pressed) override; - int StartDebugRecording(const char filename[kMaxFilenameSize]) override; - int StartDebugRecording(FILE* handle) override; - int StartDebugRecordingForPlatformFile(rtc::PlatformFile handle) override; - int StopDebugRecording() override; - void UpdateHistogramsOnCallEnd() override; - EchoCancellation* echo_cancellation() const override; - EchoControlMobile* echo_control_mobile() const override; - GainControl* gain_control() const override; - HighPassFilter* high_pass_filter() const override; - LevelEstimator* level_estimator() const override; - NoiseSuppression* noise_suppression() const override; - VoiceDetection* voice_detection() const override; + void set_stream_analog_level(int level) override; + int recommended_stream_analog_level() const + RTC_LOCKS_EXCLUDED(mutex_capture_) override; + + // Render-side exclusive methods possibly running APM in a + // multi-threaded manner. Acquire the render lock. + int ProcessReverseStream(const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest) override; + int AnalyzeReverseStream(const float* const* data, + const StreamConfig& reverse_config) override; + int ProcessReverseStream(const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config, + float* const* dest) override; + + // Methods only accessed from APM submodules or + // from AudioProcessing tests in a single-threaded manner. + // Hence there is no need for locks in these. + int proc_sample_rate_hz() const override; + int proc_split_sample_rate_hz() const override; + size_t num_input_channels() const override; + size_t num_proc_channels() const override; + size_t num_output_channels() const override; + size_t num_reverse_channels() const override; + int stream_delay_ms() const override; + + AudioProcessingStats GetStatistics(bool has_remote_tracks) override { + return GetStatistics(); + } + AudioProcessingStats GetStatistics() override { + return stats_reporter_.GetStatistics(); + } + + // TODO(peah): Remove MutateConfig once the new API allows that. + void MutateConfig(rtc::FunctionView mutator); + AudioProcessing::Config GetConfig() const override; protected: // Overridden in a mock. - virtual int InitializeLocked() EXCLUSIVE_LOCKS_REQUIRED(crit_); + virtual void InitializeLocked() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_, mutex_capture_); private: + // TODO(peah): These friend classes should be removed as soon as the new + // parameter setting scheme allows. + FRIEND_TEST_ALL_PREFIXES(ApmConfiguration, DefaultBehavior); + FRIEND_TEST_ALL_PREFIXES(ApmConfiguration, ValidConfigBehavior); + FRIEND_TEST_ALL_PREFIXES(ApmConfiguration, InValidConfigBehavior); + FRIEND_TEST_ALL_PREFIXES(ApmWithSubmodulesExcludedTest, + ToggleTransientSuppressor); + FRIEND_TEST_ALL_PREFIXES(ApmWithSubmodulesExcludedTest, + ReinitializeTransientSuppressor); + FRIEND_TEST_ALL_PREFIXES(ApmWithSubmodulesExcludedTest, + BitexactWithDisabledModules); + + int recommended_stream_analog_level_locked() const + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + void OverrideSubmoduleCreationForTesting( + const ApmSubmoduleCreationOverrides& overrides); + + // Class providing thread-safe message pipe functionality for + // |runtime_settings_|. + class RuntimeSettingEnqueuer { + public: + explicit RuntimeSettingEnqueuer( + SwapQueue* runtime_settings); + ~RuntimeSettingEnqueuer(); + void Enqueue(RuntimeSetting setting); + + private: + SwapQueue& runtime_settings_; + }; + + std::unique_ptr data_dumper_; + static int instance_count_; + const bool use_setup_specific_default_aec3_config_; + + SwapQueue capture_runtime_settings_; + SwapQueue render_runtime_settings_; + + RuntimeSettingEnqueuer capture_runtime_settings_enqueuer_; + RuntimeSettingEnqueuer render_runtime_settings_enqueuer_; + + // EchoControl factory. + std::unique_ptr echo_control_factory_; + + class SubmoduleStates { + public: + SubmoduleStates(bool capture_post_processor_enabled, + bool render_pre_processor_enabled, + bool capture_analyzer_enabled); + // Updates the submodule state and returns true if it has changed. + bool Update(bool high_pass_filter_enabled, + bool mobile_echo_controller_enabled, + bool residual_echo_detector_enabled, + bool noise_suppressor_enabled, + bool adaptive_gain_controller_enabled, + bool gain_controller2_enabled, + bool pre_amplifier_enabled, + bool echo_controller_enabled, + bool voice_detector_enabled, + bool transient_suppressor_enabled); + bool CaptureMultiBandSubModulesActive() const; + bool CaptureMultiBandProcessingPresent() const; + bool CaptureMultiBandProcessingActive(bool ec_processing_active) const; + bool CaptureFullBandProcessingActive() const; + bool CaptureAnalyzerActive() const; + bool RenderMultiBandSubModulesActive() const; + bool RenderFullBandProcessingActive() const; + bool RenderMultiBandProcessingActive() const; + bool HighPassFilteringRequired() const; + + private: + const bool capture_post_processor_enabled_ = false; + const bool render_pre_processor_enabled_ = false; + const bool capture_analyzer_enabled_ = false; + bool high_pass_filter_enabled_ = false; + bool mobile_echo_controller_enabled_ = false; + bool residual_echo_detector_enabled_ = false; + bool noise_suppressor_enabled_ = false; + bool adaptive_gain_controller_enabled_ = false; + bool gain_controller2_enabled_ = false; + bool pre_amplifier_enabled_ = false; + bool echo_controller_enabled_ = false; + bool voice_detector_enabled_ = false; + bool transient_suppressor_enabled_ = false; + bool first_update_ = true; + }; + + // Methods for modifying the formats struct that is used by both + // the render and capture threads. The check for whether modifications are + // needed is done while holding a single lock only, thereby avoiding that the + // capture thread blocks the render thread. + // Called by render: Holds the render lock when reading the format struct and + // acquires both locks if reinitialization is required. + int MaybeInitializeRender(const ProcessingConfig& processing_config) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + // Called by capture: Holds the capture lock when reading the format struct + // and acquires both locks if reinitialization is needed. + int MaybeInitializeCapture(const StreamConfig& input_config, + const StreamConfig& output_config); + + // Method for updating the state keeping track of the active submodules. + // Returns a bool indicating whether the state has changed. + bool UpdateActiveSubmoduleStates() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Methods requiring APM running in a single-threaded manner, requiring both + // the render and capture lock to be acquired. int InitializeLocked(const ProcessingConfig& config) - EXCLUSIVE_LOCKS_REQUIRED(crit_); - int MaybeInitializeLocked(const ProcessingConfig& config) - EXCLUSIVE_LOCKS_REQUIRED(crit_); + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_, mutex_capture_); + void InitializeResidualEchoDetector() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_, mutex_capture_); + void InitializeEchoController() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_, mutex_capture_); + + // Initializations of capture-only submodules, requiring the capture lock + // already acquired. + void InitializeHighPassFilter(bool forced_reset) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializeVoiceDetector() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializeGainController1() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializeTransientSuppressor() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializeGainController2() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializeNoiseSuppressor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializePreAmplifier() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializePostProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void InitializeAnalyzer() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Initializations of render-only submodules, requiring the render lock + // already acquired. + void InitializePreProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + + // Sample rate used for the fullband processing. + int proc_fullband_sample_rate_hz() const + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Empties and handles the respective RuntimeSetting queues. + void HandleCaptureRuntimeSettings() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void HandleRenderRuntimeSettings() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + + void EmptyQueuedRenderAudio() RTC_LOCKS_EXCLUDED(mutex_capture_); + void EmptyQueuedRenderAudioLocked() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + void AllocateRenderQueue() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_, mutex_capture_); + void QueueBandedRenderAudio(AudioBuffer* audio) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + void QueueNonbandedRenderAudio(AudioBuffer* audio) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + + // Capture-side exclusive methods possibly running APM in a multi-threaded + // manner that are called with the render lock already acquired. + int ProcessCaptureStreamLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); + + // Render-side exclusive methods possibly running APM in a multi-threaded + // manner that are called with the render lock already acquired. // TODO(ekm): Remove once all clients updated to new interface. - int AnalyzeReverseStream(const float* const* src, - const StreamConfig& input_config, - const StreamConfig& output_config); - int ProcessStreamLocked() EXCLUSIVE_LOCKS_REQUIRED(crit_); - int ProcessReverseStreamLocked() EXCLUSIVE_LOCKS_REQUIRED(crit_); + int AnalyzeReverseStreamLocked(const float* const* src, + const StreamConfig& input_config, + const StreamConfig& output_config) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); + int ProcessRenderStreamLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_render_); - bool is_data_processed() const; - bool output_copy_needed(bool is_data_processed) const; - bool synthesis_needed(bool is_data_processed) const; - bool analysis_needed(bool is_data_processed) const; - bool is_rev_processed() const; - bool rev_conversion_needed() const; - void InitializeExperimentalAgc() EXCLUSIVE_LOCKS_REQUIRED(crit_); - void InitializeTransient() EXCLUSIVE_LOCKS_REQUIRED(crit_); - void InitializeBeamformer() EXCLUSIVE_LOCKS_REQUIRED(crit_); - void InitializeIntelligibility() EXCLUSIVE_LOCKS_REQUIRED(crit_); - void MaybeUpdateHistograms() EXCLUSIVE_LOCKS_REQUIRED(crit_); + // Collects configuration settings from public and private + // submodules to be saved as an audioproc::Config message on the + // AecDump if it is attached. If not |forced|, only writes the current + // config if it is different from the last saved one; if |forced|, + // writes the config regardless of the last saved. + void WriteAecDumpConfigMessage(bool forced) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); - EchoCancellationImpl* echo_cancellation_; - EchoControlMobileImpl* echo_control_mobile_; - GainControlImpl* gain_control_; - HighPassFilterImpl* high_pass_filter_; - LevelEstimatorImpl* level_estimator_; - NoiseSuppressionImpl* noise_suppression_; - VoiceDetectionImpl* voice_detection_; - rtc::scoped_ptr gain_control_for_new_agc_; + // Notifies attached AecDump of current configuration and capture data. + void RecordUnprocessedCaptureStream(const float* const* capture_stream) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); - std::list component_list_; - CriticalSectionWrapper* crit_; - rtc::scoped_ptr render_audio_; - rtc::scoped_ptr capture_audio_; - rtc::scoped_ptr render_converter_; -#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP - // TODO(andrew): make this more graceful. Ideally we would split this stuff - // out into a separate class with an "enabled" and "disabled" implementation. - int WriteMessageToDebugFile(); - int WriteInitMessage(); + void RecordUnprocessedCaptureStream(const int16_t* const data, + const StreamConfig& config) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); - // Writes Config message. If not |forced|, only writes the current config if - // it is different from the last saved one; if |forced|, writes the config - // regardless of the last saved. - int WriteConfigMessage(bool forced); + // Notifies attached AecDump of current configuration and + // processed capture data and issues a capture stream recording + // request. + void RecordProcessedCaptureStream( + const float* const* processed_capture_stream) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); - rtc::scoped_ptr debug_file_; - rtc::scoped_ptr event_msg_; // Protobuf message. - std::string event_str_; // Memory for protobuf serialization. + void RecordProcessedCaptureStream(const int16_t* const data, + const StreamConfig& config) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); - // Serialized string of last saved APM configuration. - std::string last_serialized_config_; -#endif + // Notifies attached AecDump about current state (delay, drift, etc). + void RecordAudioProcessingState() + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_); - // Format of processing streams at input/output call sites. - ProcessingConfig api_format_; + // AecDump instance used for optionally logging APM config, input + // and output to file in the AEC-dump format defined in debug.proto. + std::unique_ptr aec_dump_; - // Only the rate and samples fields of fwd_proc_format_ are used because the - // forward processing number of channels is mutable and is tracked by the - // capture_audio_. - StreamConfig fwd_proc_format_; - StreamConfig rev_proc_format_; - int split_rate_; + // Hold the last config written with AecDump for avoiding writing + // the same config twice. + InternalAPMConfig apm_config_for_aec_dump_ RTC_GUARDED_BY(mutex_capture_); - int stream_delay_ms_; - int delay_offset_ms_; - bool was_stream_delay_set_; - int last_stream_delay_ms_; - int last_aec_system_delay_ms_; - int stream_delay_jumps_; - int aec_system_delay_jumps_; + // Critical sections. + mutable Mutex mutex_render_ RTC_ACQUIRED_BEFORE(mutex_capture_); + mutable Mutex mutex_capture_; - bool output_will_be_muted_ GUARDED_BY(crit_); + // Struct containing the Config specifying the behavior of APM. + AudioProcessing::Config config_; - bool key_pressed_; + // Overrides for testing the exclusion of some submodules from the build. + ApmSubmoduleCreationOverrides submodule_creation_overrides_ + RTC_GUARDED_BY(mutex_capture_); - // Only set through the constructor's Config parameter. - const bool use_new_agc_; - rtc::scoped_ptr agc_manager_ GUARDED_BY(crit_); - int agc_startup_min_volume_; + // Class containing information about what submodules are active. + SubmoduleStates submodule_states_; - bool transient_suppressor_enabled_; - rtc::scoped_ptr transient_suppressor_; - const bool beamformer_enabled_; - rtc::scoped_ptr> beamformer_; - const std::vector array_geometry_; - const SphericalPointf target_direction_; + // Struct containing the pointers to the submodules. + struct Submodules { + Submodules(std::unique_ptr capture_post_processor, + std::unique_ptr render_pre_processor, + rtc::scoped_refptr echo_detector, + std::unique_ptr capture_analyzer) + : echo_detector(std::move(echo_detector)), + capture_post_processor(std::move(capture_post_processor)), + render_pre_processor(std::move(render_pre_processor)), + capture_analyzer(std::move(capture_analyzer)) {} + // Accessed internally from capture or during initialization. + std::unique_ptr agc_manager; + std::unique_ptr gain_control; + std::unique_ptr gain_controller2; + std::unique_ptr high_pass_filter; + rtc::scoped_refptr echo_detector; + std::unique_ptr echo_controller; + std::unique_ptr echo_control_mobile; + std::unique_ptr noise_suppressor; + std::unique_ptr transient_suppressor; + std::unique_ptr capture_post_processor; + std::unique_ptr render_pre_processor; + std::unique_ptr pre_amplifier; + std::unique_ptr capture_analyzer; + std::unique_ptr output_level_estimator; + std::unique_ptr voice_detector; + } submodules_; - bool intelligibility_enabled_; - rtc::scoped_ptr intelligibility_enhancer_; + // State that is written to while holding both the render and capture locks + // but can be read without any lock being held. + // As this is only accessed internally of APM, and all internal methods in APM + // either are holding the render or capture locks, this construct is safe as + // it is not possible to read the variables while writing them. + struct ApmFormatState { + ApmFormatState() + : // Format of processing streams at input/output call sites. + api_format({{{kSampleRate16kHz, 1, false}, + {kSampleRate16kHz, 1, false}, + {kSampleRate16kHz, 1, false}, + {kSampleRate16kHz, 1, false}}}), + render_processing_format(kSampleRate16kHz, 1) {} + ProcessingConfig api_format; + StreamConfig render_processing_format; + } formats_; + + // APM constants. + const struct ApmConstants { + ApmConstants(bool multi_channel_render_support, + bool multi_channel_capture_support, + bool enforce_split_band_hpf) + : multi_channel_render_support(multi_channel_render_support), + multi_channel_capture_support(multi_channel_capture_support), + enforce_split_band_hpf(enforce_split_band_hpf) {} + bool multi_channel_render_support; + bool multi_channel_capture_support; + bool enforce_split_band_hpf; + } constants_; + + struct ApmCaptureState { + ApmCaptureState(); + ~ApmCaptureState(); + bool was_stream_delay_set; + bool output_will_be_muted; + bool key_pressed; + std::unique_ptr capture_audio; + std::unique_ptr capture_fullband_audio; + std::unique_ptr linear_aec_output; + // Only the rate and samples fields of capture_processing_format_ are used + // because the capture processing number of channels is mutable and is + // tracked by the capture_audio_. + StreamConfig capture_processing_format; + int split_rate; + bool echo_path_gain_change; + int prev_analog_mic_level; + float prev_pre_amp_gain; + int playout_volume; + int prev_playout_volume; + AudioProcessingStats stats; + struct KeyboardInfo { + void Extract(const float* const* data, const StreamConfig& stream_config); + size_t num_keyboard_frames = 0; + const float* keyboard_data = nullptr; + } keyboard_info; + int cached_stream_analog_level_ = 0; + } capture_ RTC_GUARDED_BY(mutex_capture_); + + struct ApmCaptureNonLockedState { + ApmCaptureNonLockedState() + : capture_processing_format(kSampleRate16kHz), + split_rate(kSampleRate16kHz), + stream_delay_ms(0) {} + // Only the rate and samples fields of capture_processing_format_ are used + // because the forward processing number of channels is mutable and is + // tracked by the capture_audio_. + StreamConfig capture_processing_format; + int split_rate; + int stream_delay_ms; + bool echo_controller_enabled = false; + } capture_nonlocked_; + + struct ApmRenderState { + ApmRenderState(); + ~ApmRenderState(); + std::unique_ptr render_converter; + std::unique_ptr render_audio; + } render_ RTC_GUARDED_BY(mutex_render_); + + // Class for statistics reporting. The class is thread-safe and no lock is + // needed when accessing it. + class ApmStatsReporter { + public: + ApmStatsReporter(); + ~ApmStatsReporter(); + + // Returns the most recently reported statistics. + AudioProcessingStats GetStatistics(); + + // Update the cached statistics. + void UpdateStatistics(const AudioProcessingStats& new_stats); + + private: + Mutex mutex_stats_; + AudioProcessingStats cached_stats_ RTC_GUARDED_BY(mutex_stats_); + SwapQueue stats_message_queue_; + } stats_reporter_; + + std::vector aecm_render_queue_buffer_ RTC_GUARDED_BY(mutex_render_); + std::vector aecm_capture_queue_buffer_ + RTC_GUARDED_BY(mutex_capture_); + + size_t agc_render_queue_element_max_size_ RTC_GUARDED_BY(mutex_render_) + RTC_GUARDED_BY(mutex_capture_) = 0; + std::vector agc_render_queue_buffer_ RTC_GUARDED_BY(mutex_render_); + std::vector agc_capture_queue_buffer_ RTC_GUARDED_BY(mutex_capture_); + + size_t red_render_queue_element_max_size_ RTC_GUARDED_BY(mutex_render_) + RTC_GUARDED_BY(mutex_capture_) = 0; + std::vector red_render_queue_buffer_ RTC_GUARDED_BY(mutex_render_); + std::vector red_capture_queue_buffer_ RTC_GUARDED_BY(mutex_capture_); + + RmsLevel capture_input_rms_ RTC_GUARDED_BY(mutex_capture_); + RmsLevel capture_output_rms_ RTC_GUARDED_BY(mutex_capture_); + int capture_rms_interval_counter_ RTC_GUARDED_BY(mutex_capture_) = 0; + + // Lock protection not needed. + std::unique_ptr< + SwapQueue, RenderQueueItemVerifier>> + aecm_render_signal_queue_; + std::unique_ptr< + SwapQueue, RenderQueueItemVerifier>> + agc_render_signal_queue_; + std::unique_ptr, RenderQueueItemVerifier>> + red_render_signal_queue_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AUDIO_PROCESSING_IMPL_H_ +#endif // MODULES_AUDIO_PROCESSING_AUDIO_PROCESSING_IMPL_H_ diff --git a/webrtc/modules/audio_processing/beamformer/array_util.cc b/webrtc/modules/audio_processing/beamformer/array_util.cc deleted file mode 100644 index d97beea..0000000 --- a/webrtc/modules/audio_processing/beamformer/array_util.cc +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2015 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 "webrtc/modules/audio_processing/beamformer/array_util.h" - -#include -#include - -#include "webrtc/base/checks.h" - -namespace webrtc { -namespace { - -const float kMaxDotProduct = 1e-6f; - -} // namespace - -float GetMinimumSpacing(const std::vector& array_geometry) { - RTC_CHECK_GT(array_geometry.size(), 1u); - float mic_spacing = std::numeric_limits::max(); - for (size_t i = 0; i < (array_geometry.size() - 1); ++i) { - for (size_t j = i + 1; j < array_geometry.size(); ++j) { - mic_spacing = - std::min(mic_spacing, Distance(array_geometry[i], array_geometry[j])); - } - } - return mic_spacing; -} - -Point PairDirection(const Point& a, const Point& b) { - return {b.x() - a.x(), b.y() - a.y(), b.z() - a.z()}; -} - -float DotProduct(const Point& a, const Point& b) { - return a.x() * b.x() + a.y() * b.y() + a.z() * b.z(); -} - -Point CrossProduct(const Point& a, const Point& b) { - return {a.y() * b.z() - a.z() * b.y(), a.z() * b.x() - a.x() * b.z(), - a.x() * b.y() - a.y() * b.x()}; -} - -bool AreParallel(const Point& a, const Point& b) { - Point cross_product = CrossProduct(a, b); - return DotProduct(cross_product, cross_product) < kMaxDotProduct; -} - -bool ArePerpendicular(const Point& a, const Point& b) { - return std::abs(DotProduct(a, b)) < kMaxDotProduct; -} - -rtc::Maybe GetDirectionIfLinear( - const std::vector& array_geometry) { - RTC_DCHECK_GT(array_geometry.size(), 1u); - const Point first_pair_direction = - PairDirection(array_geometry[0], array_geometry[1]); - for (size_t i = 2u; i < array_geometry.size(); ++i) { - const Point pair_direction = - PairDirection(array_geometry[i - 1], array_geometry[i]); - if (!AreParallel(first_pair_direction, pair_direction)) { - return rtc::Maybe(); - } - } - return rtc::Maybe(first_pair_direction); -} - -rtc::Maybe GetNormalIfPlanar(const std::vector& array_geometry) { - RTC_DCHECK_GT(array_geometry.size(), 1u); - const Point first_pair_direction = - PairDirection(array_geometry[0], array_geometry[1]); - Point pair_direction(0.f, 0.f, 0.f); - size_t i = 2u; - bool is_linear = true; - for (; i < array_geometry.size() && is_linear; ++i) { - pair_direction = PairDirection(array_geometry[i - 1], array_geometry[i]); - if (!AreParallel(first_pair_direction, pair_direction)) { - is_linear = false; - } - } - if (is_linear) { - return rtc::Maybe(); - } - const Point normal_direction = - CrossProduct(first_pair_direction, pair_direction); - for (; i < array_geometry.size(); ++i) { - pair_direction = PairDirection(array_geometry[i - 1], array_geometry[i]); - if (!ArePerpendicular(normal_direction, pair_direction)) { - return rtc::Maybe(); - } - } - return rtc::Maybe(normal_direction); -} - -rtc::Maybe GetArrayNormalIfExists( - const std::vector& array_geometry) { - const rtc::Maybe direction = GetDirectionIfLinear(array_geometry); - if (direction) { - return rtc::Maybe(Point(direction->y(), -direction->x(), 0.f)); - } - const rtc::Maybe normal = GetNormalIfPlanar(array_geometry); - if (normal && normal->z() < kMaxDotProduct) { - return normal; - } - return rtc::Maybe(); -} - -Point AzimuthToPoint(float azimuth) { - return Point(std::cos(azimuth), std::sin(azimuth), 0.f); -} - -} // namespace webrtc diff --git a/webrtc/modules/audio_processing/beamformer/array_util.h b/webrtc/modules/audio_processing/beamformer/array_util.h deleted file mode 100644 index 7fff973..0000000 --- a/webrtc/modules/audio_processing/beamformer/array_util.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2015 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_ARRAY_UTIL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_ARRAY_UTIL_H_ - -#include -#include - -#include "webrtc/base/maybe.h" - -namespace webrtc { - -// Coordinates in meters. The convention used is: -// x: the horizontal dimension, with positive to the right from the camera's -// perspective. -// y: the depth dimension, with positive forward from the camera's -// perspective. -// z: the vertical dimension, with positive upwards. -template -struct CartesianPoint { - CartesianPoint() { - c[0] = 0; - c[1] = 0; - c[2] = 0; - } - CartesianPoint(T x, T y, T z) { - c[0] = x; - c[1] = y; - c[2] = z; - } - T x() const { return c[0]; } - T y() const { return c[1]; } - T z() const { return c[2]; } - T c[3]; -}; - -using Point = CartesianPoint; - -// Calculates the direction from a to b. -Point PairDirection(const Point& a, const Point& b); - -float DotProduct(const Point& a, const Point& b); -Point CrossProduct(const Point& a, const Point& b); - -bool AreParallel(const Point& a, const Point& b); -bool ArePerpendicular(const Point& a, const Point& b); - -// Returns the minimum distance between any two Points in the given -// |array_geometry|. -float GetMinimumSpacing(const std::vector& array_geometry); - -// If the given array geometry is linear it returns the direction without -// normalizing. -rtc::Maybe GetDirectionIfLinear( - const std::vector& array_geometry); - -// If the given array geometry is planar it returns the normal without -// normalizing. -rtc::Maybe GetNormalIfPlanar(const std::vector& array_geometry); - -// Returns the normal of an array if it has one and it is in the xy-plane. -rtc::Maybe GetArrayNormalIfExists( - const std::vector& array_geometry); - -// The resulting Point will be in the xy-plane. -Point AzimuthToPoint(float azimuth); - -template -float Distance(CartesianPoint a, CartesianPoint b) { - return std::sqrt((a.x() - b.x()) * (a.x() - b.x()) + - (a.y() - b.y()) * (a.y() - b.y()) + - (a.z() - b.z()) * (a.z() - b.z())); -} - -// The convention used: -// azimuth: zero is to the right from the camera's perspective, with positive -// angles in radians counter-clockwise. -// elevation: zero is horizontal, with positive angles in radians upwards. -// radius: distance from the camera in meters. -template -struct SphericalPoint { - SphericalPoint(T azimuth, T elevation, T radius) { - s[0] = azimuth; - s[1] = elevation; - s[2] = radius; - } - T azimuth() const { return s[0]; } - T elevation() const { return s[1]; } - T distance() const { return s[2]; } - T s[3]; -}; - -using SphericalPointf = SphericalPoint; - -// Helper functions to transform degrees to radians and the inverse. -template -T DegreesToRadians(T angle_degrees) { - return M_PI * angle_degrees / 180; -} - -template -T RadiansToDegrees(T angle_radians) { - return 180 * angle_radians / M_PI; -} - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_ARRAY_UTIL_H_ diff --git a/webrtc/modules/audio_processing/beamformer/beamformer.h b/webrtc/modules/audio_processing/beamformer/beamformer.h deleted file mode 100644 index 6a9ff45..0000000 --- a/webrtc/modules/audio_processing/beamformer/beamformer.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2015 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_BEAMFORMER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_BEAMFORMER_H_ - -#include "webrtc/common_audio/channel_buffer.h" -#include "webrtc/modules/audio_processing/beamformer/array_util.h" - -namespace webrtc { - -template -class Beamformer { - public: - virtual ~Beamformer() {} - - // Process one time-domain chunk of audio. The audio is expected to be split - // into frequency bands inside the ChannelBuffer. The number of frames and - // channels must correspond to the constructor parameters. The same - // ChannelBuffer can be passed in as |input| and |output|. - virtual void ProcessChunk(const ChannelBuffer& input, - ChannelBuffer* output) = 0; - - // Sample rate corresponds to the lower band. - // Needs to be called before the the Beamformer can be used. - virtual void Initialize(int chunk_size_ms, int sample_rate_hz) = 0; - - // Aim the beamformer at a point in space. - virtual void AimAt(const SphericalPointf& spherical_point) = 0; - - // Indicates whether a given point is inside of the beam. - virtual bool IsInBeam(const SphericalPointf& spherical_point) { return true; } - - // Returns true if the current data contains the target signal. - // Which signals are considered "targets" is implementation dependent. - virtual bool is_target_present() = 0; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_BEAMFORMER_H_ diff --git a/webrtc/modules/audio_processing/beamformer/complex_matrix.h b/webrtc/modules/audio_processing/beamformer/complex_matrix.h deleted file mode 100644 index bfa3563..0000000 --- a/webrtc/modules/audio_processing/beamformer/complex_matrix.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_COMPLEX_MATRIX_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_COMPLEX_MATRIX_H_ - -#include - -#include "webrtc/base/checks.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/modules/audio_processing/beamformer/matrix.h" - -namespace webrtc { - -using std::complex; - -// An extension of Matrix for operations that only work on a complex type. -template -class ComplexMatrix : public Matrix > { - public: - ComplexMatrix() : Matrix >() {} - - ComplexMatrix(int num_rows, int num_columns) - : Matrix >(num_rows, num_columns) {} - - ComplexMatrix(const complex* data, int num_rows, int num_columns) - : Matrix >(data, num_rows, num_columns) {} - - // Complex Matrix operations. - ComplexMatrix& PointwiseConjugate() { - complex* const data = this->data(); - size_t size = this->num_rows() * this->num_columns(); - for (size_t i = 0; i < size; ++i) { - data[i] = conj(data[i]); - } - - return *this; - } - - ComplexMatrix& PointwiseConjugate(const ComplexMatrix& operand) { - this->CopyFrom(operand); - return PointwiseConjugate(); - } - - ComplexMatrix& ConjugateTranspose() { - this->CopyDataToScratch(); - int num_rows = this->num_rows(); - this->SetNumRows(this->num_columns()); - this->SetNumColumns(num_rows); - this->Resize(); - return ConjugateTranspose(this->scratch_elements()); - } - - ComplexMatrix& ConjugateTranspose(const ComplexMatrix& operand) { - RTC_CHECK_EQ(operand.num_rows(), this->num_columns()); - RTC_CHECK_EQ(operand.num_columns(), this->num_rows()); - return ConjugateTranspose(operand.elements()); - } - - ComplexMatrix& ZeroImag() { - complex* const data = this->data(); - size_t size = this->num_rows() * this->num_columns(); - for (size_t i = 0; i < size; ++i) { - data[i] = complex(data[i].real(), 0); - } - - return *this; - } - - ComplexMatrix& ZeroImag(const ComplexMatrix& operand) { - this->CopyFrom(operand); - return ZeroImag(); - } - - private: - ComplexMatrix& ConjugateTranspose(const complex* const* src) { - complex* const* elements = this->elements(); - for (int i = 0; i < this->num_rows(); ++i) { - for (int j = 0; j < this->num_columns(); ++j) { - elements[i][j] = conj(src[j][i]); - } - } - - return *this; - } -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_COMPLEX_MATRIX_H_ diff --git a/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.cc b/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.cc deleted file mode 100644 index d072832..0000000 --- a/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.cc +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#define _USE_MATH_DEFINES - -#include "webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.h" - -#include - -namespace webrtc { -namespace { - -float BesselJ0(float x) { -#if WEBRTC_WIN - return _j0(x); -#else - return j0(x); -#endif -} - -// Calculates the Euclidean norm for a row vector. -float Norm(const ComplexMatrix& x) { - RTC_CHECK_EQ(1, x.num_rows()); - const size_t length = x.num_columns(); - const complex* elems = x.elements()[0]; - float result = 0.f; - for (size_t i = 0u; i < length; ++i) { - result += std::norm(elems[i]); - } - return std::sqrt(result); -} - -} // namespace - -void CovarianceMatrixGenerator::UniformCovarianceMatrix( - float wave_number, - const std::vector& geometry, - ComplexMatrix* mat) { - RTC_CHECK_EQ(static_cast(geometry.size()), mat->num_rows()); - RTC_CHECK_EQ(static_cast(geometry.size()), mat->num_columns()); - - complex* const* mat_els = mat->elements(); - for (size_t i = 0; i < geometry.size(); ++i) { - for (size_t j = 0; j < geometry.size(); ++j) { - if (wave_number > 0.f) { - mat_els[i][j] = - BesselJ0(wave_number * Distance(geometry[i], geometry[j])); - } else { - mat_els[i][j] = i == j ? 1.f : 0.f; - } - } - } -} - -void CovarianceMatrixGenerator::AngledCovarianceMatrix( - float sound_speed, - float angle, - size_t frequency_bin, - size_t fft_size, - size_t num_freq_bins, - int sample_rate, - const std::vector& geometry, - ComplexMatrix* mat) { - RTC_CHECK_EQ(static_cast(geometry.size()), mat->num_rows()); - RTC_CHECK_EQ(static_cast(geometry.size()), mat->num_columns()); - - ComplexMatrix interf_cov_vector(1, geometry.size()); - ComplexMatrix interf_cov_vector_transposed(geometry.size(), 1); - PhaseAlignmentMasks(frequency_bin, - fft_size, - sample_rate, - sound_speed, - geometry, - angle, - &interf_cov_vector); - interf_cov_vector.Scale(1.f / Norm(interf_cov_vector)); - interf_cov_vector_transposed.Transpose(interf_cov_vector); - interf_cov_vector.PointwiseConjugate(); - mat->Multiply(interf_cov_vector_transposed, interf_cov_vector); -} - -void CovarianceMatrixGenerator::PhaseAlignmentMasks( - size_t frequency_bin, - size_t fft_size, - int sample_rate, - float sound_speed, - const std::vector& geometry, - float angle, - ComplexMatrix* mat) { - RTC_CHECK_EQ(1, mat->num_rows()); - RTC_CHECK_EQ(static_cast(geometry.size()), mat->num_columns()); - - float freq_in_hertz = - (static_cast(frequency_bin) / fft_size) * sample_rate; - - complex* const* mat_els = mat->elements(); - for (size_t c_ix = 0; c_ix < geometry.size(); ++c_ix) { - float distance = std::cos(angle) * geometry[c_ix].x() + - std::sin(angle) * geometry[c_ix].y(); - float phase_shift = -2.f * M_PI * distance * freq_in_hertz / sound_speed; - - // Euler's formula for mat[0][c_ix] = e^(j * phase_shift). - mat_els[0][c_ix] = complex(cos(phase_shift), sin(phase_shift)); - } -} - -} // namespace webrtc diff --git a/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.h b/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.h deleted file mode 100644 index 5375518..0000000 --- a/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_COVARIANCE_MATRIX_GENERATOR_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_COVARIANCE_MATRIX_GENERATOR_H_ - -#include "webrtc/modules/audio_processing/beamformer/complex_matrix.h" -#include "webrtc/modules/audio_processing/beamformer/array_util.h" - -namespace webrtc { - -// Helper class for Beamformer in charge of generating covariance matrices. For -// each function, the passed-in ComplexMatrix is expected to be of size -// |num_input_channels| x |num_input_channels|. -class CovarianceMatrixGenerator { - public: - // A uniform covariance matrix with a gap at the target location. WARNING: - // The target angle is assumed to be 0. - static void UniformCovarianceMatrix(float wave_number, - const std::vector& geometry, - ComplexMatrix* mat); - - // The covariance matrix of a source at the given angle. - static void AngledCovarianceMatrix(float sound_speed, - float angle, - size_t frequency_bin, - size_t fft_size, - size_t num_freq_bins, - int sample_rate, - const std::vector& geometry, - ComplexMatrix* mat); - - // Calculates phase shifts that, when applied to a multichannel signal and - // added together, cause constructive interferernce for sources located at - // the given angle. - static void PhaseAlignmentMasks(size_t frequency_bin, - size_t fft_size, - int sample_rate, - float sound_speed, - const std::vector& geometry, - float angle, - ComplexMatrix* mat); -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_BF_HELPERS_H_ diff --git a/webrtc/modules/audio_processing/beamformer/matrix.h b/webrtc/modules/audio_processing/beamformer/matrix.h deleted file mode 100644 index 162aef1..0000000 --- a/webrtc/modules/audio_processing/beamformer/matrix.h +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_MATRIX_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_MATRIX_H_ - -#include -#include -#include -#include - -#include "webrtc/base/checks.h" -#include "webrtc/base/constructormagic.h" -#include "webrtc/base/scoped_ptr.h" - -namespace { - -// Wrappers to get around the compiler warning resulting from the fact that -// there's no std::sqrt overload for ints. We cast all non-complex types to -// a double for the sqrt method. -template -T sqrt_wrapper(T x) { - return sqrt(static_cast(x)); -} - -template -std::complex sqrt_wrapper(std::complex x) { - return sqrt(x); -} -} // namespace - -namespace webrtc { - -// Matrix is a class for doing standard matrix operations on 2 dimensional -// matrices of any size. Results of matrix operations are stored in the -// calling object. Function overloads exist for both in-place (the calling -// object is used as both an operand and the result) and out-of-place (all -// operands are passed in as parameters) operations. If operand dimensions -// mismatch, the program crashes. Out-of-place operations change the size of -// the calling object, if necessary, before operating. -// -// 'In-place' operations that inherently change the size of the matrix (eg. -// Transpose, Multiply on different-sized matrices) must make temporary copies -// (|scratch_elements_| and |scratch_data_|) of existing data to complete the -// operations. -// -// The data is stored contiguously. Data can be accessed internally as a flat -// array, |data_|, or as an array of row pointers, |elements_|, but is -// available to users only as an array of row pointers through |elements()|. -// Memory for storage is allocated when a matrix is resized only if the new -// size overflows capacity. Memory needed temporarily for any operations is -// similarly resized only if the new size overflows capacity. -// -// If you pass in storage through the ctor, that storage is copied into the -// matrix. TODO(claguna): albeit tricky, allow for data to be referenced -// instead of copied, and owned by the user. -template -class Matrix { - public: - Matrix() : num_rows_(0), num_columns_(0) {} - - // Allocates space for the elements and initializes all values to zero. - Matrix(int num_rows, int num_columns) - : num_rows_(num_rows), num_columns_(num_columns) { - Resize(); - scratch_data_.resize(num_rows_ * num_columns_); - scratch_elements_.resize(num_rows_); - } - - // Copies |data| into the new Matrix. - Matrix(const T* data, int num_rows, int num_columns) - : num_rows_(0), num_columns_(0) { - CopyFrom(data, num_rows, num_columns); - scratch_data_.resize(num_rows_ * num_columns_); - scratch_elements_.resize(num_rows_); - } - - virtual ~Matrix() {} - - // Deep copy an existing matrix. - void CopyFrom(const Matrix& other) { - CopyFrom(&other.data_[0], other.num_rows_, other.num_columns_); - } - - // Copy |data| into the Matrix. The current data is lost. - void CopyFrom(const T* const data, int num_rows, int num_columns) { - Resize(num_rows, num_columns); - memcpy(&data_[0], data, num_rows_ * num_columns_ * sizeof(data_[0])); - } - - Matrix& CopyFromColumn(const T* const* src, - size_t column_index, - int num_rows) { - Resize(1, num_rows); - for (int i = 0; i < num_columns_; ++i) { - data_[i] = src[i][column_index]; - } - - return *this; - } - - void Resize(int num_rows, int num_columns) { - if (num_rows != num_rows_ || num_columns != num_columns_) { - num_rows_ = num_rows; - num_columns_ = num_columns; - Resize(); - } - } - - // Accessors and mutators. - int num_rows() const { return num_rows_; } - int num_columns() const { return num_columns_; } - T* const* elements() { return &elements_[0]; } - const T* const* elements() const { return &elements_[0]; } - - T Trace() { - RTC_CHECK_EQ(num_rows_, num_columns_); - - T trace = 0; - for (int i = 0; i < num_rows_; ++i) { - trace += elements_[i][i]; - } - return trace; - } - - // Matrix Operations. Returns *this to support method chaining. - Matrix& Transpose() { - CopyDataToScratch(); - Resize(num_columns_, num_rows_); - return Transpose(scratch_elements()); - } - - Matrix& Transpose(const Matrix& operand) { - RTC_CHECK_EQ(operand.num_rows_, num_columns_); - RTC_CHECK_EQ(operand.num_columns_, num_rows_); - - return Transpose(operand.elements()); - } - - template - Matrix& Scale(const S& scalar) { - for (size_t i = 0; i < data_.size(); ++i) { - data_[i] *= scalar; - } - - return *this; - } - - template - Matrix& Scale(const Matrix& operand, const S& scalar) { - CopyFrom(operand); - return Scale(scalar); - } - - Matrix& Add(const Matrix& operand) { - RTC_CHECK_EQ(num_rows_, operand.num_rows_); - RTC_CHECK_EQ(num_columns_, operand.num_columns_); - - for (size_t i = 0; i < data_.size(); ++i) { - data_[i] += operand.data_[i]; - } - - return *this; - } - - Matrix& Add(const Matrix& lhs, const Matrix& rhs) { - CopyFrom(lhs); - return Add(rhs); - } - - Matrix& Subtract(const Matrix& operand) { - RTC_CHECK_EQ(num_rows_, operand.num_rows_); - RTC_CHECK_EQ(num_columns_, operand.num_columns_); - - for (size_t i = 0; i < data_.size(); ++i) { - data_[i] -= operand.data_[i]; - } - - return *this; - } - - Matrix& Subtract(const Matrix& lhs, const Matrix& rhs) { - CopyFrom(lhs); - return Subtract(rhs); - } - - Matrix& PointwiseMultiply(const Matrix& operand) { - RTC_CHECK_EQ(num_rows_, operand.num_rows_); - RTC_CHECK_EQ(num_columns_, operand.num_columns_); - - for (size_t i = 0; i < data_.size(); ++i) { - data_[i] *= operand.data_[i]; - } - - return *this; - } - - Matrix& PointwiseMultiply(const Matrix& lhs, const Matrix& rhs) { - CopyFrom(lhs); - return PointwiseMultiply(rhs); - } - - Matrix& PointwiseDivide(const Matrix& operand) { - RTC_CHECK_EQ(num_rows_, operand.num_rows_); - RTC_CHECK_EQ(num_columns_, operand.num_columns_); - - for (size_t i = 0; i < data_.size(); ++i) { - data_[i] /= operand.data_[i]; - } - - return *this; - } - - Matrix& PointwiseDivide(const Matrix& lhs, const Matrix& rhs) { - CopyFrom(lhs); - return PointwiseDivide(rhs); - } - - Matrix& PointwiseSquareRoot() { - for (size_t i = 0; i < data_.size(); ++i) { - data_[i] = sqrt_wrapper(data_[i]); - } - - return *this; - } - - Matrix& PointwiseSquareRoot(const Matrix& operand) { - CopyFrom(operand); - return PointwiseSquareRoot(); - } - - Matrix& PointwiseAbsoluteValue() { - for (size_t i = 0; i < data_.size(); ++i) { - data_[i] = abs(data_[i]); - } - - return *this; - } - - Matrix& PointwiseAbsoluteValue(const Matrix& operand) { - CopyFrom(operand); - return PointwiseAbsoluteValue(); - } - - Matrix& PointwiseSquare() { - for (size_t i = 0; i < data_.size(); ++i) { - data_[i] *= data_[i]; - } - - return *this; - } - - Matrix& PointwiseSquare(const Matrix& operand) { - CopyFrom(operand); - return PointwiseSquare(); - } - - Matrix& Multiply(const Matrix& lhs, const Matrix& rhs) { - RTC_CHECK_EQ(lhs.num_columns_, rhs.num_rows_); - RTC_CHECK_EQ(num_rows_, lhs.num_rows_); - RTC_CHECK_EQ(num_columns_, rhs.num_columns_); - - return Multiply(lhs.elements(), rhs.num_rows_, rhs.elements()); - } - - Matrix& Multiply(const Matrix& rhs) { - RTC_CHECK_EQ(num_columns_, rhs.num_rows_); - - CopyDataToScratch(); - Resize(num_rows_, rhs.num_columns_); - return Multiply(scratch_elements(), rhs.num_rows_, rhs.elements()); - } - - std::string ToString() const { - std::ostringstream ss; - ss << std::endl << "Matrix" << std::endl; - - for (int i = 0; i < num_rows_; ++i) { - for (int j = 0; j < num_columns_; ++j) { - ss << elements_[i][j] << " "; - } - ss << std::endl; - } - ss << std::endl; - - return ss.str(); - } - - protected: - void SetNumRows(const int num_rows) { num_rows_ = num_rows; } - void SetNumColumns(const int num_columns) { num_columns_ = num_columns; } - T* data() { return &data_[0]; } - const T* data() const { return &data_[0]; } - const T* const* scratch_elements() const { return &scratch_elements_[0]; } - - // Resize the matrix. If an increase in capacity is required, the current - // data is lost. - void Resize() { - size_t size = num_rows_ * num_columns_; - data_.resize(size); - elements_.resize(num_rows_); - - for (int i = 0; i < num_rows_; ++i) { - elements_[i] = &data_[i * num_columns_]; - } - } - - // Copies data_ into scratch_data_ and updates scratch_elements_ accordingly. - void CopyDataToScratch() { - scratch_data_ = data_; - scratch_elements_.resize(num_rows_); - - for (int i = 0; i < num_rows_; ++i) { - scratch_elements_[i] = &scratch_data_[i * num_columns_]; - } - } - - private: - int num_rows_; - int num_columns_; - std::vector data_; - std::vector elements_; - - // Stores temporary copies of |data_| and |elements_| for in-place operations - // where referring to original data is necessary. - std::vector scratch_data_; - std::vector scratch_elements_; - - // Helpers for Transpose and Multiply operations that unify in-place and - // out-of-place solutions. - Matrix& Transpose(const T* const* src) { - for (int i = 0; i < num_rows_; ++i) { - for (int j = 0; j < num_columns_; ++j) { - elements_[i][j] = src[j][i]; - } - } - - return *this; - } - - Matrix& Multiply(const T* const* lhs, int num_rows_rhs, const T* const* rhs) { - for (int row = 0; row < num_rows_; ++row) { - for (int col = 0; col < num_columns_; ++col) { - T cur_element = 0; - for (int i = 0; i < num_rows_rhs; ++i) { - cur_element += lhs[row][i] * rhs[i][col]; - } - - elements_[row][col] = cur_element; - } - } - - return *this; - } - - RTC_DISALLOW_COPY_AND_ASSIGN(Matrix); -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_MATRIX_H_ diff --git a/webrtc/modules/audio_processing/beamformer/matrix_test_helpers.h b/webrtc/modules/audio_processing/beamformer/matrix_test_helpers.h deleted file mode 100644 index 7c58670..0000000 --- a/webrtc/modules/audio_processing/beamformer/matrix_test_helpers.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_MATRIX_TEST_HELPERS_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_MATRIX_TEST_HELPERS_H_ - -#include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/modules/audio_processing/beamformer/complex_matrix.h" -#include "webrtc/modules/audio_processing/beamformer/matrix.h" - -namespace { -const float kTolerance = 0.001f; -} - -namespace webrtc { - -using std::complex; - -// Functions used in both matrix_unittest and complex_matrix_unittest. -class MatrixTestHelpers { - public: - template - static void ValidateMatrixEquality(const Matrix& expected, - const Matrix& actual) { - EXPECT_EQ(expected.num_rows(), actual.num_rows()); - EXPECT_EQ(expected.num_columns(), actual.num_columns()); - - const T* const* expected_elements = expected.elements(); - const T* const* actual_elements = actual.elements(); - for (int i = 0; i < expected.num_rows(); ++i) { - for (int j = 0; j < expected.num_columns(); ++j) { - EXPECT_EQ(expected_elements[i][j], actual_elements[i][j]); - } - } - } - - static void ValidateMatrixEqualityFloat(const Matrix& expected, - const Matrix& actual) { - EXPECT_EQ(expected.num_rows(), actual.num_rows()); - EXPECT_EQ(expected.num_columns(), actual.num_columns()); - - const float* const* expected_elements = expected.elements(); - const float* const* actual_elements = actual.elements(); - for (int i = 0; i < expected.num_rows(); ++i) { - for (int j = 0; j < expected.num_columns(); ++j) { - EXPECT_NEAR(expected_elements[i][j], actual_elements[i][j], kTolerance); - } - } - } - - static void ValidateMatrixEqualityComplexFloat( - const Matrix >& expected, - const Matrix >& actual) { - EXPECT_EQ(expected.num_rows(), actual.num_rows()); - EXPECT_EQ(expected.num_columns(), actual.num_columns()); - - const complex* const* expected_elements = expected.elements(); - const complex* const* actual_elements = actual.elements(); - for (int i = 0; i < expected.num_rows(); ++i) { - for (int j = 0; j < expected.num_columns(); ++j) { - EXPECT_NEAR(expected_elements[i][j].real(), - actual_elements[i][j].real(), - kTolerance); - EXPECT_NEAR(expected_elements[i][j].imag(), - actual_elements[i][j].imag(), - kTolerance); - } - } - } - - static void ValidateMatrixNearEqualityComplexFloat( - const Matrix >& expected, - const Matrix >& actual, - float tolerance) { - EXPECT_EQ(expected.num_rows(), actual.num_rows()); - EXPECT_EQ(expected.num_columns(), actual.num_columns()); - - const complex* const* expected_elements = expected.elements(); - const complex* const* actual_elements = actual.elements(); - for (int i = 0; i < expected.num_rows(); ++i) { - for (int j = 0; j < expected.num_columns(); ++j) { - EXPECT_NEAR(expected_elements[i][j].real(), - actual_elements[i][j].real(), - tolerance); - EXPECT_NEAR(expected_elements[i][j].imag(), - actual_elements[i][j].imag(), - tolerance); - } - } - } -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_MATRIX_TEST_HELPERS_H_ diff --git a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc deleted file mode 100644 index 029fa08..0000000 --- a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.cc +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#define _USE_MATH_DEFINES - -#include "webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h" - -#include -#include -#include -#include - -#include "webrtc/base/arraysize.h" -#include "webrtc/common_audio/window_generator.h" -#include "webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.h" - -namespace webrtc { -namespace { - -// Alpha for the Kaiser Bessel Derived window. -const float kKbdAlpha = 1.5f; - -const float kSpeedOfSoundMeterSeconds = 343; - -// The minimum separation in radians between the target direction and an -// interferer scenario. -const float kMinAwayRadians = 0.2f; - -// The separation between the target direction and the closest interferer -// scenario is proportional to this constant. -const float kAwaySlope = 0.008f; - -// When calculating the interference covariance matrix, this is the weight for -// the weighted average between the uniform covariance matrix and the angled -// covariance matrix. -// Rpsi = Rpsi_angled * kBalance + Rpsi_uniform * (1 - kBalance) -const float kBalance = 0.95f; - -// Alpha coefficients for mask smoothing. -const float kMaskTimeSmoothAlpha = 0.2f; -const float kMaskFrequencySmoothAlpha = 0.6f; - -// The average mask is computed from masks in this mid-frequency range. If these -// ranges are changed |kMaskQuantile| might need to be adjusted. -const int kLowMeanStartHz = 200; -const int kLowMeanEndHz = 400; - -// Range limiter for subtractive terms in the nominator and denominator of the -// postfilter expression. It handles the scenario mismatch between the true and -// model sources (target and interference). -const float kCutOffConstant = 0.9999f; - -// Quantile of mask values which is used to estimate target presence. -const float kMaskQuantile = 0.7f; -// Mask threshold over which the data is considered signal and not interference. -// It has to be updated every time the postfilter calculation is changed -// significantly. -// TODO(aluebs): Write a tool to tune the target threshold automatically based -// on files annotated with target and interference ground truth. -const float kMaskTargetThreshold = 0.01f; -// Time in seconds after which the data is considered interference if the mask -// does not pass |kMaskTargetThreshold|. -const float kHoldTargetSeconds = 0.25f; - -// To compensate for the attenuation this algorithm introduces to the target -// signal. It was estimated empirically from a low-noise low-reverberation -// recording from broadside. -const float kCompensationGain = 2.f; - -// Does conjugate(|norm_mat|) * |mat| * transpose(|norm_mat|). No extra space is -// used; to accomplish this, we compute both multiplications in the same loop. -// The returned norm is clamped to be non-negative. -float Norm(const ComplexMatrix& mat, - const ComplexMatrix& norm_mat) { - RTC_CHECK_EQ(norm_mat.num_rows(), 1); - RTC_CHECK_EQ(norm_mat.num_columns(), mat.num_rows()); - RTC_CHECK_EQ(norm_mat.num_columns(), mat.num_columns()); - - complex first_product = complex(0.f, 0.f); - complex second_product = complex(0.f, 0.f); - - const complex* const* mat_els = mat.elements(); - const complex* const* norm_mat_els = norm_mat.elements(); - - for (int i = 0; i < norm_mat.num_columns(); ++i) { - for (int j = 0; j < norm_mat.num_columns(); ++j) { - first_product += conj(norm_mat_els[0][j]) * mat_els[j][i]; - } - second_product += first_product * norm_mat_els[0][i]; - first_product = 0.f; - } - return std::max(second_product.real(), 0.f); -} - -// Does conjugate(|lhs|) * |rhs| for row vectors |lhs| and |rhs|. -complex ConjugateDotProduct(const ComplexMatrix& lhs, - const ComplexMatrix& rhs) { - RTC_CHECK_EQ(lhs.num_rows(), 1); - RTC_CHECK_EQ(rhs.num_rows(), 1); - RTC_CHECK_EQ(lhs.num_columns(), rhs.num_columns()); - - const complex* const* lhs_elements = lhs.elements(); - const complex* const* rhs_elements = rhs.elements(); - - complex result = complex(0.f, 0.f); - for (int i = 0; i < lhs.num_columns(); ++i) { - result += conj(lhs_elements[0][i]) * rhs_elements[0][i]; - } - - return result; -} - -// Works for positive numbers only. -size_t Round(float x) { - return static_cast(std::floor(x + 0.5f)); -} - -// Calculates the sum of absolute values of a complex matrix. -float SumAbs(const ComplexMatrix& mat) { - float sum_abs = 0.f; - const complex* const* mat_els = mat.elements(); - for (int i = 0; i < mat.num_rows(); ++i) { - for (int j = 0; j < mat.num_columns(); ++j) { - sum_abs += std::abs(mat_els[i][j]); - } - } - return sum_abs; -} - -// Calculates the sum of squares of a complex matrix. -float SumSquares(const ComplexMatrix& mat) { - float sum_squares = 0.f; - const complex* const* mat_els = mat.elements(); - for (int i = 0; i < mat.num_rows(); ++i) { - for (int j = 0; j < mat.num_columns(); ++j) { - float abs_value = std::abs(mat_els[i][j]); - sum_squares += abs_value * abs_value; - } - } - return sum_squares; -} - -// Does |out| = |in|.' * conj(|in|) for row vector |in|. -void TransposedConjugatedProduct(const ComplexMatrix& in, - ComplexMatrix* out) { - RTC_CHECK_EQ(in.num_rows(), 1); - RTC_CHECK_EQ(out->num_rows(), in.num_columns()); - RTC_CHECK_EQ(out->num_columns(), in.num_columns()); - const complex* in_elements = in.elements()[0]; - complex* const* out_elements = out->elements(); - for (int i = 0; i < out->num_rows(); ++i) { - for (int j = 0; j < out->num_columns(); ++j) { - out_elements[i][j] = in_elements[i] * conj(in_elements[j]); - } - } -} - -std::vector GetCenteredArray(std::vector array_geometry) { - for (int dim = 0; dim < 3; ++dim) { - float center = 0.f; - for (size_t i = 0; i < array_geometry.size(); ++i) { - center += array_geometry[i].c[dim]; - } - center /= array_geometry.size(); - for (size_t i = 0; i < array_geometry.size(); ++i) { - array_geometry[i].c[dim] -= center; - } - } - return array_geometry; -} - -} // namespace - -const float NonlinearBeamformer::kHalfBeamWidthRadians = DegreesToRadians(20.f); - -// static -const size_t NonlinearBeamformer::kNumFreqBins; - -NonlinearBeamformer::NonlinearBeamformer( - const std::vector& array_geometry, - SphericalPointf target_direction) - : num_input_channels_(array_geometry.size()), - array_geometry_(GetCenteredArray(array_geometry)), - array_normal_(GetArrayNormalIfExists(array_geometry)), - min_mic_spacing_(GetMinimumSpacing(array_geometry)), - target_angle_radians_(target_direction.azimuth()), - away_radians_(std::min( - static_cast(M_PI), - std::max(kMinAwayRadians, - kAwaySlope * static_cast(M_PI) / min_mic_spacing_))) { - WindowGenerator::KaiserBesselDerived(kKbdAlpha, kFftSize, window_); -} - -void NonlinearBeamformer::Initialize(int chunk_size_ms, int sample_rate_hz) { - chunk_length_ = - static_cast(sample_rate_hz / (1000.f / chunk_size_ms)); - sample_rate_hz_ = sample_rate_hz; - - high_pass_postfilter_mask_ = 1.f; - is_target_present_ = false; - hold_target_blocks_ = kHoldTargetSeconds * 2 * sample_rate_hz / kFftSize; - interference_blocks_count_ = hold_target_blocks_; - - lapped_transform_.reset(new LappedTransform(num_input_channels_, - 1, - chunk_length_, - window_, - kFftSize, - kFftSize / 2, - this)); - for (size_t i = 0; i < kNumFreqBins; ++i) { - time_smooth_mask_[i] = 1.f; - final_mask_[i] = 1.f; - float freq_hz = (static_cast(i) / kFftSize) * sample_rate_hz_; - wave_numbers_[i] = 2 * M_PI * freq_hz / kSpeedOfSoundMeterSeconds; - } - - InitLowFrequencyCorrectionRanges(); - InitDiffuseCovMats(); - AimAt(SphericalPointf(target_angle_radians_, 0.f, 1.f)); -} - -// These bin indexes determine the regions over which a mean is taken. This is -// applied as a constant value over the adjacent end "frequency correction" -// regions. -// -// low_mean_start_bin_ high_mean_start_bin_ -// v v constant -// |----------------|--------|----------------|-------|----------------| -// constant ^ ^ -// low_mean_end_bin_ high_mean_end_bin_ -// -void NonlinearBeamformer::InitLowFrequencyCorrectionRanges() { - low_mean_start_bin_ = Round(kLowMeanStartHz * kFftSize / sample_rate_hz_); - low_mean_end_bin_ = Round(kLowMeanEndHz * kFftSize / sample_rate_hz_); - - RTC_DCHECK_GT(low_mean_start_bin_, 0U); - RTC_DCHECK_LT(low_mean_start_bin_, low_mean_end_bin_); -} - -void NonlinearBeamformer::InitHighFrequencyCorrectionRanges() { - const float kAliasingFreqHz = - kSpeedOfSoundMeterSeconds / - (min_mic_spacing_ * (1.f + std::abs(std::cos(target_angle_radians_)))); - const float kHighMeanStartHz = std::min(0.5f * kAliasingFreqHz, - sample_rate_hz_ / 2.f); - const float kHighMeanEndHz = std::min(0.75f * kAliasingFreqHz, - sample_rate_hz_ / 2.f); - high_mean_start_bin_ = Round(kHighMeanStartHz * kFftSize / sample_rate_hz_); - high_mean_end_bin_ = Round(kHighMeanEndHz * kFftSize / sample_rate_hz_); - - RTC_DCHECK_LT(low_mean_end_bin_, high_mean_end_bin_); - RTC_DCHECK_LT(high_mean_start_bin_, high_mean_end_bin_); - RTC_DCHECK_LT(high_mean_end_bin_, kNumFreqBins - 1); -} - -void NonlinearBeamformer::InitInterfAngles() { - interf_angles_radians_.clear(); - const Point target_direction = AzimuthToPoint(target_angle_radians_); - const Point clockwise_interf_direction = - AzimuthToPoint(target_angle_radians_ - away_radians_); - if (!array_normal_ || - DotProduct(*array_normal_, target_direction) * - DotProduct(*array_normal_, clockwise_interf_direction) >= - 0.f) { - // The target and clockwise interferer are in the same half-plane defined - // by the array. - interf_angles_radians_.push_back(target_angle_radians_ - away_radians_); - } else { - // Otherwise, the interferer will begin reflecting back at the target. - // Instead rotate it away 180 degrees. - interf_angles_radians_.push_back(target_angle_radians_ - away_radians_ + - M_PI); - } - const Point counterclock_interf_direction = - AzimuthToPoint(target_angle_radians_ + away_radians_); - if (!array_normal_ || - DotProduct(*array_normal_, target_direction) * - DotProduct(*array_normal_, counterclock_interf_direction) >= - 0.f) { - // The target and counter-clockwise interferer are in the same half-plane - // defined by the array. - interf_angles_radians_.push_back(target_angle_radians_ + away_radians_); - } else { - // Otherwise, the interferer will begin reflecting back at the target. - // Instead rotate it away 180 degrees. - interf_angles_radians_.push_back(target_angle_radians_ + away_radians_ - - M_PI); - } -} - -void NonlinearBeamformer::InitDelaySumMasks() { - for (size_t f_ix = 0; f_ix < kNumFreqBins; ++f_ix) { - delay_sum_masks_[f_ix].Resize(1, num_input_channels_); - CovarianceMatrixGenerator::PhaseAlignmentMasks( - f_ix, kFftSize, sample_rate_hz_, kSpeedOfSoundMeterSeconds, - array_geometry_, target_angle_radians_, &delay_sum_masks_[f_ix]); - - complex_f norm_factor = sqrt( - ConjugateDotProduct(delay_sum_masks_[f_ix], delay_sum_masks_[f_ix])); - delay_sum_masks_[f_ix].Scale(1.f / norm_factor); - normalized_delay_sum_masks_[f_ix].CopyFrom(delay_sum_masks_[f_ix]); - normalized_delay_sum_masks_[f_ix].Scale(1.f / SumAbs( - normalized_delay_sum_masks_[f_ix])); - } -} - -void NonlinearBeamformer::InitTargetCovMats() { - for (size_t i = 0; i < kNumFreqBins; ++i) { - target_cov_mats_[i].Resize(num_input_channels_, num_input_channels_); - TransposedConjugatedProduct(delay_sum_masks_[i], &target_cov_mats_[i]); - } -} - -void NonlinearBeamformer::InitDiffuseCovMats() { - for (size_t i = 0; i < kNumFreqBins; ++i) { - uniform_cov_mat_[i].Resize(num_input_channels_, num_input_channels_); - CovarianceMatrixGenerator::UniformCovarianceMatrix( - wave_numbers_[i], array_geometry_, &uniform_cov_mat_[i]); - complex_f normalization_factor = uniform_cov_mat_[i].elements()[0][0]; - uniform_cov_mat_[i].Scale(1.f / normalization_factor); - uniform_cov_mat_[i].Scale(1 - kBalance); - } -} - -void NonlinearBeamformer::InitInterfCovMats() { - for (size_t i = 0; i < kNumFreqBins; ++i) { - interf_cov_mats_[i].clear(); - for (size_t j = 0; j < interf_angles_radians_.size(); ++j) { - interf_cov_mats_[i].push_back(new ComplexMatrixF(num_input_channels_, - num_input_channels_)); - ComplexMatrixF angled_cov_mat(num_input_channels_, num_input_channels_); - CovarianceMatrixGenerator::AngledCovarianceMatrix( - kSpeedOfSoundMeterSeconds, - interf_angles_radians_[j], - i, - kFftSize, - kNumFreqBins, - sample_rate_hz_, - array_geometry_, - &angled_cov_mat); - // Normalize matrices before averaging them. - complex_f normalization_factor = angled_cov_mat.elements()[0][0]; - angled_cov_mat.Scale(1.f / normalization_factor); - // Weighted average of matrices. - angled_cov_mat.Scale(kBalance); - interf_cov_mats_[i][j]->Add(uniform_cov_mat_[i], angled_cov_mat); - } - } -} - -void NonlinearBeamformer::NormalizeCovMats() { - for (size_t i = 0; i < kNumFreqBins; ++i) { - rxiws_[i] = Norm(target_cov_mats_[i], delay_sum_masks_[i]); - rpsiws_[i].clear(); - for (size_t j = 0; j < interf_angles_radians_.size(); ++j) { - rpsiws_[i].push_back(Norm(*interf_cov_mats_[i][j], delay_sum_masks_[i])); - } - } -} - -void NonlinearBeamformer::ProcessChunk(const ChannelBuffer& input, - ChannelBuffer* output) { - RTC_DCHECK_EQ(input.num_channels(), num_input_channels_); - RTC_DCHECK_EQ(input.num_frames_per_band(), chunk_length_); - - float old_high_pass_mask = high_pass_postfilter_mask_; - lapped_transform_->ProcessChunk(input.channels(0), output->channels(0)); - // Ramp up/down for smoothing. 1 mask per 10ms results in audible - // discontinuities. - const float ramp_increment = - (high_pass_postfilter_mask_ - old_high_pass_mask) / - input.num_frames_per_band(); - // Apply the smoothed high-pass mask to the first channel of each band. - // This can be done because the effct of the linear beamformer is negligible - // compared to the post-filter. - for (size_t i = 1; i < input.num_bands(); ++i) { - float smoothed_mask = old_high_pass_mask; - for (size_t j = 0; j < input.num_frames_per_band(); ++j) { - smoothed_mask += ramp_increment; - output->channels(i)[0][j] = input.channels(i)[0][j] * smoothed_mask; - } - } -} - -void NonlinearBeamformer::AimAt(const SphericalPointf& target_direction) { - target_angle_radians_ = target_direction.azimuth(); - InitHighFrequencyCorrectionRanges(); - InitInterfAngles(); - InitDelaySumMasks(); - InitTargetCovMats(); - InitInterfCovMats(); - NormalizeCovMats(); -} - -bool NonlinearBeamformer::IsInBeam(const SphericalPointf& spherical_point) { - // If more than half-beamwidth degrees away from the beam's center, - // you are out of the beam. - return fabs(spherical_point.azimuth() - target_angle_radians_) < - kHalfBeamWidthRadians; -} - -void NonlinearBeamformer::ProcessAudioBlock(const complex_f* const* input, - int num_input_channels, - size_t num_freq_bins, - int num_output_channels, - complex_f* const* output) { - RTC_CHECK_EQ(num_freq_bins, kNumFreqBins); - RTC_CHECK_EQ(num_input_channels, num_input_channels_); - RTC_CHECK_EQ(num_output_channels, 1); - - // Calculating the post-filter masks. Note that we need two for each - // frequency bin to account for the positive and negative interferer - // angle. - for (size_t i = low_mean_start_bin_; i <= high_mean_end_bin_; ++i) { - eig_m_.CopyFromColumn(input, i, num_input_channels_); - float eig_m_norm_factor = std::sqrt(SumSquares(eig_m_)); - if (eig_m_norm_factor != 0.f) { - eig_m_.Scale(1.f / eig_m_norm_factor); - } - - float rxim = Norm(target_cov_mats_[i], eig_m_); - float ratio_rxiw_rxim = 0.f; - if (rxim > 0.f) { - ratio_rxiw_rxim = rxiws_[i] / rxim; - } - - complex_f rmw = abs(ConjugateDotProduct(delay_sum_masks_[i], eig_m_)); - rmw *= rmw; - float rmw_r = rmw.real(); - - new_mask_[i] = CalculatePostfilterMask(*interf_cov_mats_[i][0], - rpsiws_[i][0], - ratio_rxiw_rxim, - rmw_r); - for (size_t j = 1; j < interf_angles_radians_.size(); ++j) { - float tmp_mask = CalculatePostfilterMask(*interf_cov_mats_[i][j], - rpsiws_[i][j], - ratio_rxiw_rxim, - rmw_r); - if (tmp_mask < new_mask_[i]) { - new_mask_[i] = tmp_mask; - } - } - } - - ApplyMaskTimeSmoothing(); - EstimateTargetPresence(); - ApplyLowFrequencyCorrection(); - ApplyHighFrequencyCorrection(); - ApplyMaskFrequencySmoothing(); - ApplyMasks(input, output); -} - -float NonlinearBeamformer::CalculatePostfilterMask( - const ComplexMatrixF& interf_cov_mat, - float rpsiw, - float ratio_rxiw_rxim, - float rmw_r) { - float rpsim = Norm(interf_cov_mat, eig_m_); - - float ratio = 0.f; - if (rpsim > 0.f) { - ratio = rpsiw / rpsim; - } - - return (1.f - std::min(kCutOffConstant, ratio / rmw_r)) / - (1.f - std::min(kCutOffConstant, ratio / ratio_rxiw_rxim)); -} - -void NonlinearBeamformer::ApplyMasks(const complex_f* const* input, - complex_f* const* output) { - complex_f* output_channel = output[0]; - for (size_t f_ix = 0; f_ix < kNumFreqBins; ++f_ix) { - output_channel[f_ix] = complex_f(0.f, 0.f); - - const complex_f* delay_sum_mask_els = - normalized_delay_sum_masks_[f_ix].elements()[0]; - for (int c_ix = 0; c_ix < num_input_channels_; ++c_ix) { - output_channel[f_ix] += input[c_ix][f_ix] * delay_sum_mask_els[c_ix]; - } - - output_channel[f_ix] *= kCompensationGain * final_mask_[f_ix]; - } -} - -// Smooth new_mask_ into time_smooth_mask_. -void NonlinearBeamformer::ApplyMaskTimeSmoothing() { - for (size_t i = low_mean_start_bin_; i <= high_mean_end_bin_; ++i) { - time_smooth_mask_[i] = kMaskTimeSmoothAlpha * new_mask_[i] + - (1 - kMaskTimeSmoothAlpha) * time_smooth_mask_[i]; - } -} - -// Copy time_smooth_mask_ to final_mask_ and smooth over frequency. -void NonlinearBeamformer::ApplyMaskFrequencySmoothing() { - // Smooth over frequency in both directions. The "frequency correction" - // regions have constant value, but we enter them to smooth over the jump - // that exists at the boundary. However, this does mean when smoothing "away" - // from the region that we only need to use the last element. - // - // Upward smoothing: - // low_mean_start_bin_ - // v - // |------|------------|------| - // ^------------------>^ - // - // Downward smoothing: - // high_mean_end_bin_ - // v - // |------|------------|------| - // ^<------------------^ - std::copy(time_smooth_mask_, time_smooth_mask_ + kNumFreqBins, final_mask_); - for (size_t i = low_mean_start_bin_; i < kNumFreqBins; ++i) { - final_mask_[i] = kMaskFrequencySmoothAlpha * final_mask_[i] + - (1 - kMaskFrequencySmoothAlpha) * final_mask_[i - 1]; - } - for (size_t i = high_mean_end_bin_ + 1; i > 0; --i) { - final_mask_[i - 1] = kMaskFrequencySmoothAlpha * final_mask_[i - 1] + - (1 - kMaskFrequencySmoothAlpha) * final_mask_[i]; - } -} - -// Apply low frequency correction to time_smooth_mask_. -void NonlinearBeamformer::ApplyLowFrequencyCorrection() { - const float low_frequency_mask = - MaskRangeMean(low_mean_start_bin_, low_mean_end_bin_ + 1); - std::fill(time_smooth_mask_, time_smooth_mask_ + low_mean_start_bin_, - low_frequency_mask); -} - -// Apply high frequency correction to time_smooth_mask_. Update -// high_pass_postfilter_mask_ to use for the high frequency time-domain bands. -void NonlinearBeamformer::ApplyHighFrequencyCorrection() { - high_pass_postfilter_mask_ = - MaskRangeMean(high_mean_start_bin_, high_mean_end_bin_ + 1); - std::fill(time_smooth_mask_ + high_mean_end_bin_ + 1, - time_smooth_mask_ + kNumFreqBins, high_pass_postfilter_mask_); -} - -// Compute mean over the given range of time_smooth_mask_, [first, last). -float NonlinearBeamformer::MaskRangeMean(size_t first, size_t last) { - RTC_DCHECK_GT(last, first); - const float sum = std::accumulate(time_smooth_mask_ + first, - time_smooth_mask_ + last, 0.f); - return sum / (last - first); -} - -void NonlinearBeamformer::EstimateTargetPresence() { - const size_t quantile = static_cast( - (high_mean_end_bin_ - low_mean_start_bin_) * kMaskQuantile + - low_mean_start_bin_); - std::nth_element(new_mask_ + low_mean_start_bin_, new_mask_ + quantile, - new_mask_ + high_mean_end_bin_ + 1); - if (new_mask_[quantile] > kMaskTargetThreshold) { - is_target_present_ = true; - interference_blocks_count_ = 0; - } else { - is_target_present_ = interference_blocks_count_++ < hold_target_blocks_; - } -} - -} // namespace webrtc diff --git a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h deleted file mode 100644 index 4289cce..0000000 --- a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_NONLINEAR_BEAMFORMER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_NONLINEAR_BEAMFORMER_H_ - -// MSVC++ requires this to be set before any other includes to get M_PI. -#define _USE_MATH_DEFINES - -#include -#include - -#include "webrtc/common_audio/lapped_transform.h" -#include "webrtc/common_audio/channel_buffer.h" -#include "webrtc/modules/audio_processing/beamformer/beamformer.h" -#include "webrtc/modules/audio_processing/beamformer/complex_matrix.h" -#include "webrtc/system_wrappers/include/scoped_vector.h" - -namespace webrtc { - -// Enhances sound sources coming directly in front of a uniform linear array -// and suppresses sound sources coming from all other directions. Operates on -// multichannel signals and produces single-channel output. -// -// The implemented nonlinear postfilter algorithm taken from "A Robust Nonlinear -// Beamforming Postprocessor" by Bastiaan Kleijn. -class NonlinearBeamformer - : public Beamformer, - public LappedTransform::Callback { - public: - static const float kHalfBeamWidthRadians; - - explicit NonlinearBeamformer( - const std::vector& array_geometry, - SphericalPointf target_direction = - SphericalPointf(static_cast(M_PI) / 2.f, 0.f, 1.f)); - - // Sample rate corresponds to the lower band. - // Needs to be called before the NonlinearBeamformer can be used. - void Initialize(int chunk_size_ms, int sample_rate_hz) override; - - // Process one time-domain chunk of audio. The audio is expected to be split - // into frequency bands inside the ChannelBuffer. The number of frames and - // channels must correspond to the constructor parameters. The same - // ChannelBuffer can be passed in as |input| and |output|. - void ProcessChunk(const ChannelBuffer& input, - ChannelBuffer* output) override; - - void AimAt(const SphericalPointf& target_direction) override; - - bool IsInBeam(const SphericalPointf& spherical_point) override; - - // After processing each block |is_target_present_| is set to true if the - // target signal es present and to false otherwise. This methods can be called - // to know if the data is target signal or interference and process it - // accordingly. - bool is_target_present() override { return is_target_present_; } - - protected: - // Process one frequency-domain block of audio. This is where the fun - // happens. Implements LappedTransform::Callback. - void ProcessAudioBlock(const complex* const* input, - int num_input_channels, - size_t num_freq_bins, - int num_output_channels, - complex* const* output) override; - - private: -#ifndef WEBRTC_AUDIO_PROCESSING_ONLY_BUILD - FRIEND_TEST_ALL_PREFIXES(NonlinearBeamformerTest, - InterfAnglesTakeAmbiguityIntoAccount); -#endif - - typedef Matrix MatrixF; - typedef ComplexMatrix ComplexMatrixF; - typedef complex complex_f; - - void InitLowFrequencyCorrectionRanges(); - void InitHighFrequencyCorrectionRanges(); - void InitInterfAngles(); - void InitDelaySumMasks(); - void InitTargetCovMats(); - void InitDiffuseCovMats(); - void InitInterfCovMats(); - void NormalizeCovMats(); - - // Calculates postfilter masks that minimize the mean squared error of our - // estimation of the desired signal. - float CalculatePostfilterMask(const ComplexMatrixF& interf_cov_mat, - float rpsiw, - float ratio_rxiw_rxim, - float rmxi_r); - - // Prevents the postfilter masks from degenerating too quickly (a cause of - // musical noise). - void ApplyMaskTimeSmoothing(); - void ApplyMaskFrequencySmoothing(); - - // The postfilter masks are unreliable at low frequencies. Calculates a better - // mask by averaging mid-low frequency values. - void ApplyLowFrequencyCorrection(); - - // Postfilter masks are also unreliable at high frequencies. Average mid-high - // frequency masks to calculate a single mask per block which can be applied - // in the time-domain. Further, we average these block-masks over a chunk, - // resulting in one postfilter mask per audio chunk. This allows us to skip - // both transforming and blocking the high-frequency signal. - void ApplyHighFrequencyCorrection(); - - // Compute the means needed for the above frequency correction. - float MaskRangeMean(size_t start_bin, size_t end_bin); - - // Applies both sets of masks to |input| and store in |output|. - void ApplyMasks(const complex_f* const* input, complex_f* const* output); - - void EstimateTargetPresence(); - - static const size_t kFftSize = 256; - static const size_t kNumFreqBins = kFftSize / 2 + 1; - - // Deals with the fft transform and blocking. - size_t chunk_length_; - rtc::scoped_ptr lapped_transform_; - float window_[kFftSize]; - - // Parameters exposed to the user. - const int num_input_channels_; - int sample_rate_hz_; - - const std::vector array_geometry_; - // The normal direction of the array if it has one and it is in the xy-plane. - const rtc::Maybe array_normal_; - - // Minimum spacing between microphone pairs. - const float min_mic_spacing_; - - // Calculated based on user-input and constants in the .cc file. - size_t low_mean_start_bin_; - size_t low_mean_end_bin_; - size_t high_mean_start_bin_; - size_t high_mean_end_bin_; - - // Quickly varying mask updated every block. - float new_mask_[kNumFreqBins]; - // Time smoothed mask. - float time_smooth_mask_[kNumFreqBins]; - // Time and frequency smoothed mask. - float final_mask_[kNumFreqBins]; - - float target_angle_radians_; - // Angles of the interferer scenarios. - std::vector interf_angles_radians_; - // The angle between the target and the interferer scenarios. - const float away_radians_; - - // Array of length |kNumFreqBins|, Matrix of size |1| x |num_channels_|. - ComplexMatrixF delay_sum_masks_[kNumFreqBins]; - ComplexMatrixF normalized_delay_sum_masks_[kNumFreqBins]; - - // Arrays of length |kNumFreqBins|, Matrix of size |num_input_channels_| x - // |num_input_channels_|. - ComplexMatrixF target_cov_mats_[kNumFreqBins]; - ComplexMatrixF uniform_cov_mat_[kNumFreqBins]; - // Array of length |kNumFreqBins|, Matrix of size |num_input_channels_| x - // |num_input_channels_|. ScopedVector has a size equal to the number of - // interferer scenarios. - ScopedVector interf_cov_mats_[kNumFreqBins]; - - // Of length |kNumFreqBins|. - float wave_numbers_[kNumFreqBins]; - - // Preallocated for ProcessAudioBlock() - // Of length |kNumFreqBins|. - float rxiws_[kNumFreqBins]; - // The vector has a size equal to the number of interferer scenarios. - std::vector rpsiws_[kNumFreqBins]; - - // The microphone normalization factor. - ComplexMatrixF eig_m_; - - // For processing the high-frequency input signal. - float high_pass_postfilter_mask_; - - // True when the target signal is present. - bool is_target_present_; - // Number of blocks after which the data is considered interference if the - // mask does not pass |kMaskSignalThreshold|. - size_t hold_target_blocks_; - // Number of blocks since the last mask that passed |kMaskSignalThreshold|. - size_t interference_blocks_count_; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_NONLINEAR_BEAMFORMER_H_ diff --git a/webrtc/modules/audio_processing/common.h b/webrtc/modules/audio_processing/common.h index ed8a054..d8532c5 100644 --- a/webrtc/modules/audio_processing/common.h +++ b/webrtc/modules/audio_processing/common.h @@ -8,16 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_COMMON_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_COMMON_H_ +#ifndef MODULES_AUDIO_PROCESSING_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_COMMON_H_ -#include - -#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "rtc_base/checks.h" namespace webrtc { -static inline int ChannelsFromLayout(AudioProcessing::ChannelLayout layout) { +static inline size_t ChannelsFromLayout(AudioProcessing::ChannelLayout layout) { switch (layout) { case AudioProcessing::kMono: case AudioProcessing::kMonoAndKeyboard: @@ -26,10 +25,10 @@ static inline int ChannelsFromLayout(AudioProcessing::ChannelLayout layout) { case AudioProcessing::kStereoAndKeyboard: return 2; } - assert(false); - return -1; + RTC_NOTREACHED(); + return 0; } } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_COMMON_H_ +#endif // MODULES_AUDIO_PROCESSING_COMMON_H_ diff --git a/webrtc/modules/audio_processing/debug.proto b/webrtc/modules/audio_processing/debug.proto index 2272712..07cce23 100644 --- a/webrtc/modules/audio_processing/debug.proto +++ b/webrtc/modules/audio_processing/debug.proto @@ -12,6 +12,9 @@ message Init { optional int32 num_reverse_channels = 5; optional int32 reverse_sample_rate = 6; optional int32 output_sample_rate = 7; + optional int32 reverse_output_sample_rate = 8; + optional int32 num_reverse_output_channels = 9; + optional int64 timestamp_ms = 10; } // May contain interleaved or deinterleaved data, but don't store both formats. @@ -44,7 +47,6 @@ message Stream { // Contains the configurations of various APM component. A Config message is // added when any of the fields are changed. message Config { - // Next field number 17. // Acoustic echo canceler. optional bool aec_enabled = 1; optional bool aec_delay_agnostic_enabled = 2; @@ -53,8 +55,8 @@ message Config { optional int32 aec_suppression_level = 5; // Mobile AEC. optional bool aecm_enabled = 6; - optional bool aecm_comfort_noise_enabled = 7; - optional int32 aecm_routing_mode = 8; + optional bool aecm_comfort_noise_enabled = 7 [deprecated = true]; + optional int32 aecm_routing_mode = 8 [deprecated = true]; // Automatic gain controller. optional bool agc_enabled = 9; optional int32 agc_mode = 10; @@ -67,6 +69,29 @@ message Config { optional int32 ns_level = 15; // Transient suppression. optional bool transient_suppression_enabled = 16; + // Semicolon-separated string containing experimental feature + // descriptions. + optional string experiments_description = 17; + reserved 18; // Intelligibility enhancer enabled (deprecated). + // Pre amplifier. + optional bool pre_amplifier_enabled = 19; + optional float pre_amplifier_fixed_gain_factor = 20; + + // Next field number 21. +} + +message PlayoutAudioDeviceInfo { + optional int32 id = 1; + optional int32 max_volume = 2; +} + +message RuntimeSetting { + optional float capture_pre_gain = 1; + optional float custom_render_processing_setting = 2; + optional float capture_fixed_post_gain = 3; + optional int32 playout_volume_change = 4; + optional PlayoutAudioDeviceInfo playout_audio_device_change = 5; + optional bool capture_output_used = 6; } message Event { @@ -76,6 +101,7 @@ message Event { STREAM = 2; CONFIG = 3; UNKNOWN_EVENT = 4; + RUNTIME_SETTING = 5; } required Type type = 1; @@ -84,4 +110,5 @@ message Event { optional ReverseStream reverse_stream = 3; optional Stream stream = 4; optional Config config = 5; + optional RuntimeSetting runtime_setting = 6; } diff --git a/webrtc/modules/audio_processing/echo_cancellation_impl.cc b/webrtc/modules/audio_processing/echo_cancellation_impl.cc deleted file mode 100644 index 56ee9e0..0000000 --- a/webrtc/modules/audio_processing/echo_cancellation_impl.cc +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_processing/echo_cancellation_impl.h" - -#include -#include - -extern "C" { -#include "webrtc/modules/audio_processing/aec/aec_core.h" -} -#include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h" -#include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" - -namespace webrtc { - -typedef void Handle; - -namespace { -int16_t MapSetting(EchoCancellation::SuppressionLevel level) { - switch (level) { - case EchoCancellation::kLowSuppression: - return kAecNlpConservative; - case EchoCancellation::kModerateSuppression: - return kAecNlpModerate; - case EchoCancellation::kHighSuppression: - return kAecNlpAggressive; - } - assert(false); - return -1; -} - -AudioProcessing::Error MapError(int err) { - switch (err) { - case AEC_UNSUPPORTED_FUNCTION_ERROR: - return AudioProcessing::kUnsupportedFunctionError; - case AEC_BAD_PARAMETER_ERROR: - return AudioProcessing::kBadParameterError; - case AEC_BAD_PARAMETER_WARNING: - return AudioProcessing::kBadStreamParameterWarning; - default: - // AEC_UNSPECIFIED_ERROR - // AEC_UNINITIALIZED_ERROR - // AEC_NULL_POINTER_ERROR - return AudioProcessing::kUnspecifiedError; - } -} -} // namespace - -EchoCancellationImpl::EchoCancellationImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit) - : ProcessingComponent(), - apm_(apm), - crit_(crit), - drift_compensation_enabled_(false), - metrics_enabled_(false), - suppression_level_(kModerateSuppression), - stream_drift_samples_(0), - was_stream_drift_set_(false), - stream_has_echo_(false), - delay_logging_enabled_(false), - extended_filter_enabled_(false), - delay_agnostic_enabled_(false) { -} - -EchoCancellationImpl::~EchoCancellationImpl() {} - -int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - assert(audio->num_frames_per_band() <= 160); - assert(audio->num_channels() == apm_->num_reverse_channels()); - - int err = apm_->kNoError; - - // The ordering convention must be followed to pass to the correct AEC. - size_t handle_index = 0; - for (int i = 0; i < apm_->num_output_channels(); i++) { - for (int j = 0; j < audio->num_channels(); j++) { - Handle* my_handle = static_cast(handle(handle_index)); - err = WebRtcAec_BufferFarend( - my_handle, - audio->split_bands_const_f(j)[kBand0To8kHz], - audio->num_frames_per_band()); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); // TODO(ajm): warning possible? - } - - handle_index++; - } - } - - return apm_->kNoError; -} - -int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - if (!apm_->was_stream_delay_set()) { - return apm_->kStreamParameterNotSetError; - } - - if (drift_compensation_enabled_ && !was_stream_drift_set_) { - return apm_->kStreamParameterNotSetError; - } - - assert(audio->num_frames_per_band() <= 160); - assert(audio->num_channels() == apm_->num_output_channels()); - - int err = apm_->kNoError; - - // The ordering convention must be followed to pass to the correct AEC. - size_t handle_index = 0; - stream_has_echo_ = false; - for (int i = 0; i < audio->num_channels(); i++) { - for (int j = 0; j < apm_->num_reverse_channels(); j++) { - Handle* my_handle = handle(handle_index); - err = WebRtcAec_Process( - my_handle, - audio->split_bands_const_f(i), - audio->num_bands(), - audio->split_bands_f(i), - audio->num_frames_per_band(), - apm_->stream_delay_ms(), - stream_drift_samples_); - - if (err != apm_->kNoError) { - err = GetHandleError(my_handle); - // TODO(ajm): Figure out how to return warnings properly. - if (err != apm_->kBadStreamParameterWarning) { - return err; - } - } - - int status = 0; - err = WebRtcAec_get_echo_status(my_handle, &status); - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } - - if (status == 1) { - stream_has_echo_ = true; - } - - handle_index++; - } - } - - was_stream_drift_set_ = false; - return apm_->kNoError; -} - -int EchoCancellationImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(crit_); - // Ensure AEC and AECM are not both enabled. - if (enable && apm_->echo_control_mobile()->is_enabled()) { - return apm_->kBadParameterError; - } - - return EnableComponent(enable); -} - -bool EchoCancellationImpl::is_enabled() const { - return is_component_enabled(); -} - -int EchoCancellationImpl::set_suppression_level(SuppressionLevel level) { - CriticalSectionScoped crit_scoped(crit_); - if (MapSetting(level) == -1) { - return apm_->kBadParameterError; - } - - suppression_level_ = level; - return Configure(); -} - -EchoCancellation::SuppressionLevel EchoCancellationImpl::suppression_level() - const { - return suppression_level_; -} - -int EchoCancellationImpl::enable_drift_compensation(bool enable) { - CriticalSectionScoped crit_scoped(crit_); - drift_compensation_enabled_ = enable; - return Configure(); -} - -bool EchoCancellationImpl::is_drift_compensation_enabled() const { - return drift_compensation_enabled_; -} - -void EchoCancellationImpl::set_stream_drift_samples(int drift) { - was_stream_drift_set_ = true; - stream_drift_samples_ = drift; -} - -int EchoCancellationImpl::stream_drift_samples() const { - return stream_drift_samples_; -} - -int EchoCancellationImpl::enable_metrics(bool enable) { - CriticalSectionScoped crit_scoped(crit_); - metrics_enabled_ = enable; - return Configure(); -} - -bool EchoCancellationImpl::are_metrics_enabled() const { - return metrics_enabled_; -} - -// TODO(ajm): we currently just use the metrics from the first AEC. Think more -// aboue the best way to extend this to multi-channel. -int EchoCancellationImpl::GetMetrics(Metrics* metrics) { - CriticalSectionScoped crit_scoped(crit_); - if (metrics == NULL) { - return apm_->kNullPointerError; - } - - if (!is_component_enabled() || !metrics_enabled_) { - return apm_->kNotEnabledError; - } - - AecMetrics my_metrics; - memset(&my_metrics, 0, sizeof(my_metrics)); - memset(metrics, 0, sizeof(Metrics)); - - Handle* my_handle = static_cast(handle(0)); - int err = WebRtcAec_GetMetrics(my_handle, &my_metrics); - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } - - metrics->residual_echo_return_loss.instant = my_metrics.rerl.instant; - metrics->residual_echo_return_loss.average = my_metrics.rerl.average; - metrics->residual_echo_return_loss.maximum = my_metrics.rerl.max; - metrics->residual_echo_return_loss.minimum = my_metrics.rerl.min; - - metrics->echo_return_loss.instant = my_metrics.erl.instant; - metrics->echo_return_loss.average = my_metrics.erl.average; - metrics->echo_return_loss.maximum = my_metrics.erl.max; - metrics->echo_return_loss.minimum = my_metrics.erl.min; - - metrics->echo_return_loss_enhancement.instant = my_metrics.erle.instant; - metrics->echo_return_loss_enhancement.average = my_metrics.erle.average; - metrics->echo_return_loss_enhancement.maximum = my_metrics.erle.max; - metrics->echo_return_loss_enhancement.minimum = my_metrics.erle.min; - - metrics->a_nlp.instant = my_metrics.aNlp.instant; - metrics->a_nlp.average = my_metrics.aNlp.average; - metrics->a_nlp.maximum = my_metrics.aNlp.max; - metrics->a_nlp.minimum = my_metrics.aNlp.min; - - return apm_->kNoError; -} - -bool EchoCancellationImpl::stream_has_echo() const { - return stream_has_echo_; -} - -int EchoCancellationImpl::enable_delay_logging(bool enable) { - CriticalSectionScoped crit_scoped(crit_); - delay_logging_enabled_ = enable; - return Configure(); -} - -bool EchoCancellationImpl::is_delay_logging_enabled() const { - return delay_logging_enabled_; -} - -bool EchoCancellationImpl::is_delay_agnostic_enabled() const { - return delay_agnostic_enabled_; -} - -bool EchoCancellationImpl::is_extended_filter_enabled() const { - return extended_filter_enabled_; -} - -// TODO(bjornv): How should we handle the multi-channel case? -int EchoCancellationImpl::GetDelayMetrics(int* median, int* std) { - float fraction_poor_delays = 0; - return GetDelayMetrics(median, std, &fraction_poor_delays); -} - -int EchoCancellationImpl::GetDelayMetrics(int* median, int* std, - float* fraction_poor_delays) { - CriticalSectionScoped crit_scoped(crit_); - if (median == NULL) { - return apm_->kNullPointerError; - } - if (std == NULL) { - return apm_->kNullPointerError; - } - - if (!is_component_enabled() || !delay_logging_enabled_) { - return apm_->kNotEnabledError; - } - - Handle* my_handle = static_cast(handle(0)); - if (WebRtcAec_GetDelayMetrics(my_handle, median, std, fraction_poor_delays) != - apm_->kNoError) { - return GetHandleError(my_handle); - } - - return apm_->kNoError; -} - -struct AecCore* EchoCancellationImpl::aec_core() const { - CriticalSectionScoped crit_scoped(crit_); - if (!is_component_enabled()) { - return NULL; - } - Handle* my_handle = static_cast(handle(0)); - return WebRtcAec_aec_core(my_handle); -} - -int EchoCancellationImpl::Initialize() { - int err = ProcessingComponent::Initialize(); - if (err != apm_->kNoError || !is_component_enabled()) { - return err; - } - - return apm_->kNoError; -} - -void EchoCancellationImpl::SetExtraOptions(const Config& config) { - extended_filter_enabled_ = config.Get().enabled; - delay_agnostic_enabled_ = config.Get().enabled; - Configure(); -} - -void* EchoCancellationImpl::CreateHandle() const { - return WebRtcAec_Create(); -} - -void EchoCancellationImpl::DestroyHandle(void* handle) const { - assert(handle != NULL); - WebRtcAec_Free(static_cast(handle)); -} - -int EchoCancellationImpl::InitializeHandle(void* handle) const { - assert(handle != NULL); - // TODO(ajm): Drift compensation is disabled in practice. If restored, it - // should be managed internally and not depend on the hardware sample rate. - // For now, just hardcode a 48 kHz value. - return WebRtcAec_Init(static_cast(handle), - apm_->proc_sample_rate_hz(), - 48000); -} - -int EchoCancellationImpl::ConfigureHandle(void* handle) const { - assert(handle != NULL); - AecConfig config; - config.metricsMode = metrics_enabled_; - config.nlpMode = MapSetting(suppression_level_); - config.skewMode = drift_compensation_enabled_; - config.delay_logging = delay_logging_enabled_; - - WebRtcAec_enable_extended_filter( - WebRtcAec_aec_core(static_cast(handle)), - extended_filter_enabled_ ? 1 : 0); - WebRtcAec_enable_delay_agnostic( - WebRtcAec_aec_core(static_cast(handle)), - delay_agnostic_enabled_ ? 1 : 0); - return WebRtcAec_set_config(static_cast(handle), config); -} - -int EchoCancellationImpl::num_handles_required() const { - return apm_->num_output_channels() * - apm_->num_reverse_channels(); -} - -int EchoCancellationImpl::GetHandleError(void* handle) const { - assert(handle != NULL); - return MapError(WebRtcAec_get_error_code(static_cast(handle))); -} -} // namespace webrtc diff --git a/webrtc/modules/audio_processing/echo_cancellation_impl.h b/webrtc/modules/audio_processing/echo_cancellation_impl.h deleted file mode 100644 index 070dcab..0000000 --- a/webrtc/modules/audio_processing/echo_cancellation_impl.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_ - -#include "webrtc/modules/audio_processing/include/audio_processing.h" -#include "webrtc/modules/audio_processing/processing_component.h" - -namespace webrtc { - -class AudioBuffer; -class CriticalSectionWrapper; - -class EchoCancellationImpl : public EchoCancellation, - public ProcessingComponent { - public: - EchoCancellationImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit); - virtual ~EchoCancellationImpl(); - - int ProcessRenderAudio(const AudioBuffer* audio); - int ProcessCaptureAudio(AudioBuffer* audio); - - // EchoCancellation implementation. - bool is_enabled() const override; - int stream_drift_samples() const override; - SuppressionLevel suppression_level() const override; - bool is_drift_compensation_enabled() const override; - - // ProcessingComponent implementation. - int Initialize() override; - void SetExtraOptions(const Config& config) override; - - bool is_delay_agnostic_enabled() const; - bool is_extended_filter_enabled() const; - - private: - // EchoCancellation implementation. - int Enable(bool enable) override; - int enable_drift_compensation(bool enable) override; - void set_stream_drift_samples(int drift) override; - int set_suppression_level(SuppressionLevel level) override; - int enable_metrics(bool enable) override; - bool are_metrics_enabled() const override; - bool stream_has_echo() const override; - int GetMetrics(Metrics* metrics) override; - int enable_delay_logging(bool enable) override; - bool is_delay_logging_enabled() const override; - int GetDelayMetrics(int* median, int* std) override; - int GetDelayMetrics(int* median, - int* std, - float* fraction_poor_delays) override; - struct AecCore* aec_core() const override; - - // ProcessingComponent implementation. - void* CreateHandle() const override; - int InitializeHandle(void* handle) const override; - int ConfigureHandle(void* handle) const override; - void DestroyHandle(void* handle) const override; - int num_handles_required() const override; - int GetHandleError(void* handle) const override; - - const AudioProcessing* apm_; - CriticalSectionWrapper* crit_; - bool drift_compensation_enabled_; - bool metrics_enabled_; - SuppressionLevel suppression_level_; - int stream_drift_samples_; - bool was_stream_drift_set_; - bool stream_has_echo_; - bool delay_logging_enabled_; - bool extended_filter_enabled_; - bool delay_agnostic_enabled_; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_ diff --git a/webrtc/modules/audio_processing/echo_control_mobile_impl.cc b/webrtc/modules/audio_processing/echo_control_mobile_impl.cc index 954aac7..8116608 100644 --- a/webrtc/modules/audio_processing/echo_control_mobile_impl.cc +++ b/webrtc/modules/audio_processing/echo_control_mobile_impl.cc @@ -8,35 +8,35 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/echo_control_mobile_impl.h" +#include "modules/audio_processing/echo_control_mobile_impl.h" -#include #include -#include "webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h" -#include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" -#include "webrtc/system_wrappers/include/logging.h" +#include + +#include "modules/audio_processing/aecm/echo_control_mobile.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "rtc_base/checks.h" +#include "rtc_base/constructor_magic.h" namespace webrtc { -typedef void Handle; - namespace { -int16_t MapSetting(EchoControlMobile::RoutingMode mode) { +int16_t MapSetting(EchoControlMobileImpl::RoutingMode mode) { switch (mode) { - case EchoControlMobile::kQuietEarpieceOrHeadset: + case EchoControlMobileImpl::kQuietEarpieceOrHeadset: return 0; - case EchoControlMobile::kEarpiece: + case EchoControlMobileImpl::kEarpiece: return 1; - case EchoControlMobile::kLoudEarpiece: + case EchoControlMobileImpl::kLoudEarpiece: return 2; - case EchoControlMobile::kSpeakerphone: + case EchoControlMobileImpl::kSpeakerphone: return 3; - case EchoControlMobile::kLoudSpeakerphone: + case EchoControlMobileImpl::kLoudSpeakerphone: return 4; } - assert(false); + RTC_NOTREACHED(); return -1; } @@ -56,136 +56,183 @@ AudioProcessing::Error MapError(int err) { return AudioProcessing::kUnspecifiedError; } } + } // namespace -size_t EchoControlMobile::echo_path_size_bytes() { - return WebRtcAecm_echo_path_size_bytes(); -} +struct EchoControlMobileImpl::StreamProperties { + StreamProperties() = delete; + StreamProperties(int sample_rate_hz, + size_t num_reverse_channels, + size_t num_output_channels) + : sample_rate_hz(sample_rate_hz), + num_reverse_channels(num_reverse_channels), + num_output_channels(num_output_channels) {} -EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit) - : ProcessingComponent(), - apm_(apm), - crit_(crit), - routing_mode_(kSpeakerphone), - comfort_noise_enabled_(true), - external_echo_path_(NULL) {} + int sample_rate_hz; + size_t num_reverse_channels; + size_t num_output_channels; +}; -EchoControlMobileImpl::~EchoControlMobileImpl() { - if (external_echo_path_ != NULL) { - delete [] external_echo_path_; - external_echo_path_ = NULL; - } -} - -int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; +class EchoControlMobileImpl::Canceller { + public: + Canceller() { + state_ = WebRtcAecm_Create(); + RTC_CHECK(state_); } - assert(audio->num_frames_per_band() <= 160); - assert(audio->num_channels() == apm_->num_reverse_channels()); + ~Canceller() { + RTC_DCHECK(state_); + WebRtcAecm_Free(state_); + } - int err = apm_->kNoError; + void* state() { + RTC_DCHECK(state_); + return state_; + } + + void Initialize(int sample_rate_hz) { + RTC_DCHECK(state_); + int error = WebRtcAecm_Init(state_, sample_rate_hz); + RTC_DCHECK_EQ(AudioProcessing::kNoError, error); + } + + private: + void* state_; + RTC_DISALLOW_COPY_AND_ASSIGN(Canceller); +}; + +EchoControlMobileImpl::EchoControlMobileImpl() + : routing_mode_(kSpeakerphone), comfort_noise_enabled_(false) {} + +EchoControlMobileImpl::~EchoControlMobileImpl() {} + +void EchoControlMobileImpl::ProcessRenderAudio( + rtc::ArrayView packed_render_audio) { + RTC_DCHECK(stream_properties_); + + size_t buffer_index = 0; + size_t num_frames_per_band = + packed_render_audio.size() / (stream_properties_->num_output_channels * + stream_properties_->num_reverse_channels); + + for (auto& canceller : cancellers_) { + WebRtcAecm_BufferFarend(canceller->state(), + &packed_render_audio[buffer_index], + num_frames_per_band); + + buffer_index += num_frames_per_band; + } +} + +void EchoControlMobileImpl::PackRenderAudioBuffer( + const AudioBuffer* audio, + size_t num_output_channels, + size_t num_channels, + std::vector* packed_buffer) { + RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, + audio->num_frames_per_band()); + RTC_DCHECK_EQ(num_channels, audio->num_channels()); + + // The ordering convention must be followed to pass to the correct AECM. + packed_buffer->clear(); + int render_channel = 0; + for (size_t i = 0; i < num_output_channels; i++) { + for (size_t j = 0; j < audio->num_channels(); j++) { + std::array data_to_buffer; + FloatS16ToS16(audio->split_bands_const(render_channel)[kBand0To8kHz], + audio->num_frames_per_band(), data_to_buffer.data()); + + // Buffer the samples in the render queue. + packed_buffer->insert( + packed_buffer->end(), data_to_buffer.data(), + data_to_buffer.data() + audio->num_frames_per_band()); + render_channel = (render_channel + 1) % audio->num_channels(); + } + } +} + +size_t EchoControlMobileImpl::NumCancellersRequired( + size_t num_output_channels, + size_t num_reverse_channels) { + return num_output_channels * num_reverse_channels; +} + +int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio, + int stream_delay_ms) { + RTC_DCHECK(stream_properties_); + RTC_DCHECK_GE(160, audio->num_frames_per_band()); + RTC_DCHECK_EQ(audio->num_channels(), stream_properties_->num_output_channels); + RTC_DCHECK_GE(cancellers_.size(), stream_properties_->num_reverse_channels * + audio->num_channels()); + + int err = AudioProcessing::kNoError; // The ordering convention must be followed to pass to the correct AECM. size_t handle_index = 0; - for (int i = 0; i < apm_->num_output_channels(); i++) { - for (int j = 0; j < audio->num_channels(); j++) { - Handle* my_handle = static_cast(handle(handle_index)); - err = WebRtcAecm_BufferFarend( - my_handle, - audio->split_bands_const(j)[kBand0To8kHz], - audio->num_frames_per_band()); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); // TODO(ajm): warning possible? - } - - handle_index++; - } - } - - return apm_->kNoError; -} - -int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - if (!apm_->was_stream_delay_set()) { - return apm_->kStreamParameterNotSetError; - } - - assert(audio->num_frames_per_band() <= 160); - assert(audio->num_channels() == apm_->num_output_channels()); - - int err = apm_->kNoError; - - // The ordering convention must be followed to pass to the correct AECM. - size_t handle_index = 0; - for (int i = 0; i < audio->num_channels(); i++) { + for (size_t capture = 0; capture < audio->num_channels(); ++capture) { // TODO(ajm): improve how this works, possibly inside AECM. // This is kind of hacked up. - const int16_t* noisy = audio->low_pass_reference(i); - const int16_t* clean = audio->split_bands_const(i)[kBand0To8kHz]; + RTC_DCHECK_LT(capture, low_pass_reference_.size()); + const int16_t* noisy = + reference_copied_ ? low_pass_reference_[capture].data() : nullptr; + + RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, + audio->num_frames_per_band()); + + std::array split_bands_data; + int16_t* split_bands = split_bands_data.data(); + const int16_t* clean = split_bands_data.data(); + if (audio->split_bands(capture)[kBand0To8kHz]) { + FloatS16ToS16(audio->split_bands(capture)[kBand0To8kHz], + audio->num_frames_per_band(), split_bands_data.data()); + } else { + clean = nullptr; + split_bands = nullptr; + } + if (noisy == NULL) { noisy = clean; clean = NULL; } - for (int j = 0; j < apm_->num_reverse_channels(); j++) { - Handle* my_handle = static_cast(handle(handle_index)); - err = WebRtcAecm_Process( - my_handle, - noisy, - clean, - audio->split_bands(i)[kBand0To8kHz], - audio->num_frames_per_band(), - apm_->stream_delay_ms()); + for (size_t render = 0; render < stream_properties_->num_reverse_channels; + ++render) { + err = WebRtcAecm_Process(cancellers_[handle_index]->state(), noisy, clean, + split_bands, audio->num_frames_per_band(), + stream_delay_ms); - if (err != apm_->kNoError) { - return GetHandleError(my_handle); // TODO(ajm): warning possible? + if (split_bands) { + S16ToFloatS16(split_bands, audio->num_frames_per_band(), + audio->split_bands(capture)[kBand0To8kHz]); } - handle_index++; + if (err != AudioProcessing::kNoError) { + return MapError(err); + } + + ++handle_index; + } + for (size_t band = 1u; band < audio->num_bands(); ++band) { + memset(audio->split_bands_f(capture)[band], 0, + audio->num_frames_per_band() * + sizeof(audio->split_bands_f(capture)[band][0])); } } - - return apm_->kNoError; -} - -int EchoControlMobileImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(crit_); - // Ensure AEC and AECM are not both enabled. - if (enable && apm_->echo_cancellation()->is_enabled()) { - return apm_->kBadParameterError; - } - - return EnableComponent(enable); -} - -bool EchoControlMobileImpl::is_enabled() const { - return is_component_enabled(); + return AudioProcessing::kNoError; } int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) { - CriticalSectionScoped crit_scoped(crit_); if (MapSetting(mode) == -1) { - return apm_->kBadParameterError; + return AudioProcessing::kBadParameterError; } - routing_mode_ = mode; return Configure(); } -EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode() - const { +EchoControlMobileImpl::RoutingMode EchoControlMobileImpl::routing_mode() const { return routing_mode_; } int EchoControlMobileImpl::enable_comfort_noise(bool enable) { - CriticalSectionScoped crit_scoped(crit_); comfort_noise_enabled_ = enable; return Configure(); } @@ -194,101 +241,46 @@ bool EchoControlMobileImpl::is_comfort_noise_enabled() const { return comfort_noise_enabled_; } -int EchoControlMobileImpl::SetEchoPath(const void* echo_path, - size_t size_bytes) { - CriticalSectionScoped crit_scoped(crit_); - if (echo_path == NULL) { - return apm_->kNullPointerError; - } - if (size_bytes != echo_path_size_bytes()) { - // Size mismatch - return apm_->kBadParameterError; +void EchoControlMobileImpl::Initialize(int sample_rate_hz, + size_t num_reverse_channels, + size_t num_output_channels) { + low_pass_reference_.resize(num_output_channels); + for (auto& reference : low_pass_reference_) { + reference.fill(0); } - if (external_echo_path_ == NULL) { - external_echo_path_ = new unsigned char[size_bytes]; - } - memcpy(external_echo_path_, echo_path, size_bytes); + stream_properties_.reset(new StreamProperties( + sample_rate_hz, num_reverse_channels, num_output_channels)); - return Initialize(); -} + // AECM only supports 16 kHz or lower sample rates. + RTC_DCHECK_LE(stream_properties_->sample_rate_hz, + AudioProcessing::kSampleRate16kHz); -int EchoControlMobileImpl::GetEchoPath(void* echo_path, - size_t size_bytes) const { - CriticalSectionScoped crit_scoped(crit_); - if (echo_path == NULL) { - return apm_->kNullPointerError; - } - if (size_bytes != echo_path_size_bytes()) { - // Size mismatch - return apm_->kBadParameterError; - } - if (!is_component_enabled()) { - return apm_->kNotEnabledError; - } + cancellers_.resize( + NumCancellersRequired(stream_properties_->num_output_channels, + stream_properties_->num_reverse_channels)); - // Get the echo path from the first channel - Handle* my_handle = static_cast(handle(0)); - if (WebRtcAecm_GetEchoPath(my_handle, echo_path, size_bytes) != 0) { - return GetHandleError(my_handle); - } - - return apm_->kNoError; -} - -int EchoControlMobileImpl::Initialize() { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - if (apm_->proc_sample_rate_hz() > apm_->kSampleRate16kHz) { - LOG(LS_ERROR) << "AECM only supports 16 kHz or lower sample rates"; - return apm_->kBadSampleRateError; - } - - return ProcessingComponent::Initialize(); -} - -void* EchoControlMobileImpl::CreateHandle() const { - return WebRtcAecm_Create(); -} - -void EchoControlMobileImpl::DestroyHandle(void* handle) const { - WebRtcAecm_Free(static_cast(handle)); -} - -int EchoControlMobileImpl::InitializeHandle(void* handle) const { - assert(handle != NULL); - Handle* my_handle = static_cast(handle); - if (WebRtcAecm_Init(my_handle, apm_->proc_sample_rate_hz()) != 0) { - return GetHandleError(my_handle); - } - if (external_echo_path_ != NULL) { - if (WebRtcAecm_InitEchoPath(my_handle, - external_echo_path_, - echo_path_size_bytes()) != 0) { - return GetHandleError(my_handle); + for (auto& canceller : cancellers_) { + if (!canceller) { + canceller.reset(new Canceller()); } + canceller->Initialize(sample_rate_hz); } - - return apm_->kNoError; + Configure(); } -int EchoControlMobileImpl::ConfigureHandle(void* handle) const { +int EchoControlMobileImpl::Configure() { AecmConfig config; config.cngMode = comfort_noise_enabled_; config.echoMode = MapSetting(routing_mode_); - - return WebRtcAecm_set_config(static_cast(handle), config); + int error = AudioProcessing::kNoError; + for (auto& canceller : cancellers_) { + int handle_error = WebRtcAecm_set_config(canceller->state(), config); + if (handle_error != AudioProcessing::kNoError) { + error = handle_error; + } + } + return error; } -int EchoControlMobileImpl::num_handles_required() const { - return apm_->num_output_channels() * - apm_->num_reverse_channels(); -} - -int EchoControlMobileImpl::GetHandleError(void* handle) const { - assert(handle != NULL); - return MapError(WebRtcAecm_get_error_code(static_cast(handle))); -} } // namespace webrtc diff --git a/webrtc/modules/audio_processing/echo_control_mobile_impl.h b/webrtc/modules/audio_processing/echo_control_mobile_impl.h index da70225..23f3c06 100644 --- a/webrtc/modules/audio_processing/echo_control_mobile_impl.h +++ b/webrtc/modules/audio_processing/echo_control_mobile_impl.h @@ -8,57 +8,79 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ +#ifndef MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ +#define MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ -#include "webrtc/modules/audio_processing/include/audio_processing.h" -#include "webrtc/modules/audio_processing/processing_component.h" +#include +#include + +#include +#include + +#include "api/array_view.h" namespace webrtc { class AudioBuffer; -class CriticalSectionWrapper; -class EchoControlMobileImpl : public EchoControlMobile, - public ProcessingComponent { +// The acoustic echo control for mobile (AECM) component is a low complexity +// robust option intended for use on mobile devices. +class EchoControlMobileImpl { public: - EchoControlMobileImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit); - virtual ~EchoControlMobileImpl(); + EchoControlMobileImpl(); - int ProcessRenderAudio(const AudioBuffer* audio); - int ProcessCaptureAudio(AudioBuffer* audio); + ~EchoControlMobileImpl(); - // EchoControlMobile implementation. - bool is_enabled() const override; - RoutingMode routing_mode() const override; - bool is_comfort_noise_enabled() const override; + // Recommended settings for particular audio routes. In general, the louder + // the echo is expected to be, the higher this value should be set. The + // preferred setting may vary from device to device. + enum RoutingMode { + kQuietEarpieceOrHeadset, + kEarpiece, + kLoudEarpiece, + kSpeakerphone, + kLoudSpeakerphone + }; - // ProcessingComponent implementation. - int Initialize() override; + // Sets echo control appropriate for the audio routing |mode| on the device. + // It can and should be updated during a call if the audio routing changes. + int set_routing_mode(RoutingMode mode); + RoutingMode routing_mode() const; + + // Comfort noise replaces suppressed background noise to maintain a + // consistent signal level. + int enable_comfort_noise(bool enable); + bool is_comfort_noise_enabled() const; + + void ProcessRenderAudio(rtc::ArrayView packed_render_audio); + int ProcessCaptureAudio(AudioBuffer* audio, int stream_delay_ms); + + void Initialize(int sample_rate_hz, + size_t num_reverse_channels, + size_t num_output_channels); + + static void PackRenderAudioBuffer(const AudioBuffer* audio, + size_t num_output_channels, + size_t num_channels, + std::vector* packed_buffer); + + static size_t NumCancellersRequired(size_t num_output_channels, + size_t num_reverse_channels); private: - // EchoControlMobile implementation. - int Enable(bool enable) override; - int set_routing_mode(RoutingMode mode) override; - int enable_comfort_noise(bool enable) override; - int SetEchoPath(const void* echo_path, size_t size_bytes) override; - int GetEchoPath(void* echo_path, size_t size_bytes) const override; + class Canceller; + struct StreamProperties; - // ProcessingComponent implementation. - void* CreateHandle() const override; - int InitializeHandle(void* handle) const override; - int ConfigureHandle(void* handle) const override; - void DestroyHandle(void* handle) const override; - int num_handles_required() const override; - int GetHandleError(void* handle) const override; + int Configure(); - const AudioProcessing* apm_; - CriticalSectionWrapper* crit_; RoutingMode routing_mode_; bool comfort_noise_enabled_; - unsigned char* external_echo_path_; + + std::vector> cancellers_; + std::unique_ptr stream_properties_; + std::vector> low_pass_reference_; + bool reference_copied_ = false; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ +#endif // MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ diff --git a/webrtc/modules/audio_processing/echo_detector/circular_buffer.cc b/webrtc/modules/audio_processing/echo_detector/circular_buffer.cc new file mode 100644 index 0000000..a6d10ed --- /dev/null +++ b/webrtc/modules/audio_processing/echo_detector/circular_buffer.cc @@ -0,0 +1,49 @@ +/* + * 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 "modules/audio_processing/echo_detector/circular_buffer.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +CircularBuffer::CircularBuffer(size_t size) : buffer_(size) {} +CircularBuffer::~CircularBuffer() = default; + +void CircularBuffer::Push(float value) { + buffer_[next_insertion_index_] = value; + ++next_insertion_index_; + next_insertion_index_ %= buffer_.size(); + RTC_DCHECK_LT(next_insertion_index_, buffer_.size()); + nr_elements_in_buffer_ = std::min(nr_elements_in_buffer_ + 1, buffer_.size()); + RTC_DCHECK_LE(nr_elements_in_buffer_, buffer_.size()); +} + +absl::optional CircularBuffer::Pop() { + if (nr_elements_in_buffer_ == 0) { + return absl::nullopt; + } + const size_t index = + (buffer_.size() + next_insertion_index_ - nr_elements_in_buffer_) % + buffer_.size(); + RTC_DCHECK_LT(index, buffer_.size()); + --nr_elements_in_buffer_; + return buffer_[index]; +} + +void CircularBuffer::Clear() { + std::fill(buffer_.begin(), buffer_.end(), 0.f); + next_insertion_index_ = 0; + nr_elements_in_buffer_ = 0; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/echo_detector/circular_buffer.h b/webrtc/modules/audio_processing/echo_detector/circular_buffer.h new file mode 100644 index 0000000..db1aeae --- /dev/null +++ b/webrtc/modules/audio_processing/echo_detector/circular_buffer.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_CIRCULAR_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_CIRCULAR_BUFFER_H_ + +#include + +#include + +#include "absl/types/optional.h" + +namespace webrtc { + +// Ring buffer containing floating point values. +struct CircularBuffer { + public: + explicit CircularBuffer(size_t size); + ~CircularBuffer(); + + void Push(float value); + absl::optional Pop(); + size_t Size() const { return nr_elements_in_buffer_; } + // This function fills the buffer with zeros, but does not change its size. + void Clear(); + + private: + std::vector buffer_; + size_t next_insertion_index_ = 0; + // This is the number of elements that have been pushed into the circular + // buffer, not the allocated buffer size. + size_t nr_elements_in_buffer_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_CIRCULAR_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.cc b/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.cc new file mode 100644 index 0000000..a857403 --- /dev/null +++ b/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.cc @@ -0,0 +1,47 @@ +/* + * 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 "modules/audio_processing/echo_detector/mean_variance_estimator.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// Parameter controlling the adaptation speed. +constexpr float kAlpha = 0.001f; + +} // namespace + +void MeanVarianceEstimator::Update(float value) { + mean_ = (1.f - kAlpha) * mean_ + kAlpha * value; + variance_ = + (1.f - kAlpha) * variance_ + kAlpha * (value - mean_) * (value - mean_); + RTC_DCHECK(isfinite(mean_)); + RTC_DCHECK(isfinite(variance_)); +} + +float MeanVarianceEstimator::std_deviation() const { + RTC_DCHECK_GE(variance_, 0.f); + return sqrtf(variance_); +} + +float MeanVarianceEstimator::mean() const { + return mean_; +} + +void MeanVarianceEstimator::Clear() { + mean_ = 0.f; + variance_ = 0.f; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.h b/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.h new file mode 100644 index 0000000..7f793df --- /dev/null +++ b/webrtc/modules/audio_processing/echo_detector/mean_variance_estimator.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MEAN_VARIANCE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MEAN_VARIANCE_ESTIMATOR_H_ + +namespace webrtc { + +// This class iteratively estimates the mean and variance of a signal. +class MeanVarianceEstimator { + public: + void Update(float value); + float std_deviation() const; + float mean() const; + void Clear(); + + private: + // Estimate of the expected value of the input values. + float mean_ = 0.f; + // Estimate of the variance of the input values. + float variance_ = 0.f; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MEAN_VARIANCE_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/echo_detector/moving_max.cc b/webrtc/modules/audio_processing/echo_detector/moving_max.cc new file mode 100644 index 0000000..3054e98 --- /dev/null +++ b/webrtc/modules/audio_processing/echo_detector/moving_max.cc @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/echo_detector/moving_max.h" + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// Parameter for controlling how fast the estimated maximum decays after the +// previous maximum is no longer valid. With a value of 0.99, the maximum will +// decay to 1% of its former value after 460 updates. +constexpr float kDecayFactor = 0.99f; + +} // namespace + +MovingMax::MovingMax(size_t window_size) : window_size_(window_size) { + RTC_DCHECK_GT(window_size, 0); +} + +MovingMax::~MovingMax() {} + +void MovingMax::Update(float value) { + if (counter_ >= window_size_ - 1) { + max_value_ *= kDecayFactor; + } else { + ++counter_; + } + if (value > max_value_) { + max_value_ = value; + counter_ = 0; + } +} + +float MovingMax::max() const { + return max_value_; +} + +void MovingMax::Clear() { + max_value_ = 0.f; + counter_ = 0; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/echo_detector/moving_max.h b/webrtc/modules/audio_processing/echo_detector/moving_max.h new file mode 100644 index 0000000..f7d8ee8 --- /dev/null +++ b/webrtc/modules/audio_processing/echo_detector/moving_max.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MOVING_MAX_H_ +#define MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MOVING_MAX_H_ + +#include + +namespace webrtc { + +class MovingMax { + public: + explicit MovingMax(size_t window_size); + ~MovingMax(); + + void Update(float value); + float max() const; + // Reset all of the state in this class. + void Clear(); + + private: + float max_value_ = 0.f; + size_t counter_ = 0; + size_t window_size_ = 1; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_MOVING_MAX_H_ diff --git a/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc b/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc new file mode 100644 index 0000000..8ec9fe9 --- /dev/null +++ b/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc @@ -0,0 +1,43 @@ +/* + * 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 "modules/audio_processing/echo_detector/normalized_covariance_estimator.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// Parameter controlling the adaptation speed. +constexpr float kAlpha = 0.001f; + +} // namespace + +void NormalizedCovarianceEstimator::Update(float x, + float x_mean, + float x_sigma, + float y, + float y_mean, + float y_sigma) { + covariance_ = + (1.f - kAlpha) * covariance_ + kAlpha * (x - x_mean) * (y - y_mean); + normalized_cross_correlation_ = covariance_ / (x_sigma * y_sigma + .0001f); + RTC_DCHECK(isfinite(covariance_)); + RTC_DCHECK(isfinite(normalized_cross_correlation_)); +} + +void NormalizedCovarianceEstimator::Clear() { + covariance_ = 0.f; + normalized_cross_correlation_ = 0.f; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.h b/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.h new file mode 100644 index 0000000..e3c36d8 --- /dev/null +++ b/webrtc/modules/audio_processing/echo_detector/normalized_covariance_estimator.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_NORMALIZED_COVARIANCE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_NORMALIZED_COVARIANCE_ESTIMATOR_H_ + +namespace webrtc { + +// This class iteratively estimates the normalized covariance between two +// signals. +class NormalizedCovarianceEstimator { + public: + void Update(float x, + float x_mean, + float x_var, + float y, + float y_mean, + float y_var); + // This function returns an estimate of the Pearson product-moment correlation + // coefficient of the two signals. + float normalized_cross_correlation() const { + return normalized_cross_correlation_; + } + float covariance() const { return covariance_; } + // This function resets the estimated values to zero. + void Clear(); + + private: + float normalized_cross_correlation_ = 0.f; + // Estimate of the covariance value. + float covariance_ = 0.f; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_ECHO_DETECTOR_NORMALIZED_COVARIANCE_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/gain_control_impl.cc b/webrtc/modules/audio_processing/gain_control_impl.cc index 3b1537e..b5454c0 100644 --- a/webrtc/modules/audio_processing/gain_control_impl.cc +++ b/webrtc/modules/audio_processing/gain_control_impl.cc @@ -8,13 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/gain_control_impl.h" +#include "modules/audio_processing/gain_control_impl.h" -#include +#include -#include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/agc/legacy/gain_control.h" -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "absl/types/optional.h" +#include "modules/audio_processing/agc/legacy/gain_control.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/field_trial.h" namespace webrtc { @@ -30,314 +35,361 @@ int16_t MapSetting(GainControl::Mode mode) { case GainControl::kFixedDigital: return kAgcModeFixedDigital; } - assert(false); + RTC_NOTREACHED(); return -1; } -} // namespace -GainControlImpl::GainControlImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit) - : ProcessingComponent(), - apm_(apm), - crit_(crit), - mode_(kAdaptiveAnalog), - minimum_capture_level_(0), - maximum_capture_level_(255), - limiter_enabled_(true), - target_level_dbfs_(3), - compression_gain_db_(9), - analog_capture_level_(0), - was_analog_level_set_(false), - stream_is_saturated_(false) {} +// Checks whether the legacy digital gain application should be used. +bool UseLegacyDigitalGainApplier() { + return field_trial::IsEnabled("WebRTC-UseLegacyDigitalGainApplier"); +} -GainControlImpl::~GainControlImpl() {} +// Floating point variant of WebRtcAgc_Process. +void ApplyDigitalGain(const int32_t gains[11], + size_t num_bands, + float* const* out) { + constexpr float kScaling = 1.f / 65536.f; + constexpr int kNumSubSections = 16; + constexpr float kOneByNumSubSections = 1.f / kNumSubSections; -int GainControlImpl::ProcessRenderAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; + float gains_scaled[11]; + for (int k = 0; k < 11; ++k) { + gains_scaled[k] = gains[k] * kScaling; } - assert(audio->num_frames_per_band() <= 160); + for (size_t b = 0; b < num_bands; ++b) { + float* out_band = out[b]; + for (int k = 0, sample = 0; k < 10; ++k) { + const float delta = + (gains_scaled[k + 1] - gains_scaled[k]) * kOneByNumSubSections; + float gain = gains_scaled[k]; + for (int n = 0; n < kNumSubSections; ++n, ++sample) { + RTC_DCHECK_EQ(k * kNumSubSections + n, sample); + out_band[sample] *= gain; + out_band[sample] = + std::min(32767.f, std::max(-32768.f, out_band[sample])); + gain += delta; + } + } + } +} - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); - int err = WebRtcAgc_AddFarend( - my_handle, - audio->mixed_low_pass_data(), - audio->num_frames_per_band()); +} // namespace - if (err != apm_->kNoError) { - return GetHandleError(my_handle); +struct GainControlImpl::MonoAgcState { + MonoAgcState() { + state = WebRtcAgc_Create(); + RTC_CHECK(state); + } + + ~MonoAgcState() { + RTC_DCHECK(state); + WebRtcAgc_Free(state); + } + + MonoAgcState(const MonoAgcState&) = delete; + MonoAgcState& operator=(const MonoAgcState&) = delete; + int32_t gains[11]; + Handle* state; +}; + +int GainControlImpl::instance_counter_ = 0; + +GainControlImpl::GainControlImpl() + : data_dumper_(new ApmDataDumper(instance_counter_)), + use_legacy_gain_applier_(UseLegacyDigitalGainApplier()), + mode_(kAdaptiveAnalog), + minimum_capture_level_(0), + maximum_capture_level_(255), + limiter_enabled_(true), + target_level_dbfs_(3), + compression_gain_db_(9), + analog_capture_level_(0), + was_analog_level_set_(false), + stream_is_saturated_(false) {} + +GainControlImpl::~GainControlImpl() = default; + +void GainControlImpl::ProcessRenderAudio( + rtc::ArrayView packed_render_audio) { + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + WebRtcAgc_AddFarend(mono_agcs_[ch]->state, packed_render_audio.data(), + packed_render_audio.size()); + } +} + +void GainControlImpl::PackRenderAudioBuffer( + const AudioBuffer& audio, + std::vector* packed_buffer) { + RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, audio.num_frames_per_band()); + std::array + mixed_16_kHz_render_data; + rtc::ArrayView mixed_16_kHz_render( + mixed_16_kHz_render_data.data(), audio.num_frames_per_band()); + if (audio.num_channels() == 1) { + FloatS16ToS16(audio.split_bands_const(0)[kBand0To8kHz], + audio.num_frames_per_band(), mixed_16_kHz_render_data.data()); + } else { + const int num_channels = static_cast(audio.num_channels()); + for (size_t i = 0; i < audio.num_frames_per_band(); ++i) { + int32_t sum = 0; + for (int ch = 0; ch < num_channels; ++ch) { + sum += FloatS16ToS16(audio.split_channels_const(kBand0To8kHz)[ch][i]); + } + mixed_16_kHz_render_data[i] = sum / num_channels; } } - return apm_->kNoError; + packed_buffer->clear(); + packed_buffer->insert( + packed_buffer->end(), mixed_16_kHz_render.data(), + (mixed_16_kHz_render.data() + audio.num_frames_per_band())); } -int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } +int GainControlImpl::AnalyzeCaptureAudio(const AudioBuffer& audio) { + RTC_DCHECK(num_proc_channels_); + RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, audio.num_frames_per_band()); + RTC_DCHECK_EQ(audio.num_channels(), *num_proc_channels_); + RTC_DCHECK_LE(*num_proc_channels_, mono_agcs_.size()); - assert(audio->num_frames_per_band() <= 160); - assert(audio->num_channels() == num_handles()); - - int err = apm_->kNoError; + int16_t split_band_data[AudioBuffer::kMaxNumBands] + [AudioBuffer::kMaxSplitFrameLength]; + int16_t* split_bands[AudioBuffer::kMaxNumBands] = { + split_band_data[0], split_band_data[1], split_band_data[2]}; if (mode_ == kAdaptiveAnalog) { - capture_levels_.assign(num_handles(), analog_capture_level_); - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); - err = WebRtcAgc_AddMic( - my_handle, - audio->split_bands(i), - audio->num_bands(), - audio->num_frames_per_band()); + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + capture_levels_[ch] = analog_capture_level_; - if (err != apm_->kNoError) { - return GetHandleError(my_handle); + audio.ExportSplitChannelData(ch, split_bands); + + int err = + WebRtcAgc_AddMic(mono_agcs_[ch]->state, split_bands, + audio.num_bands(), audio.num_frames_per_band()); + + if (err != AudioProcessing::kNoError) { + return AudioProcessing::kUnspecifiedError; } } } else if (mode_ == kAdaptiveDigital) { - - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { int32_t capture_level_out = 0; - err = WebRtcAgc_VirtualMic( - my_handle, - audio->split_bands(i), - audio->num_bands(), - audio->num_frames_per_band(), - analog_capture_level_, - &capture_level_out); + audio.ExportSplitChannelData(ch, split_bands); - capture_levels_[i] = capture_level_out; + int err = + WebRtcAgc_VirtualMic(mono_agcs_[ch]->state, split_bands, + audio.num_bands(), audio.num_frames_per_band(), + analog_capture_level_, &capture_level_out); - if (err != apm_->kNoError) { - return GetHandleError(my_handle); + capture_levels_[ch] = capture_level_out; + + if (err != AudioProcessing::kNoError) { + return AudioProcessing::kUnspecifiedError; } - } } - return apm_->kNoError; + return AudioProcessing::kNoError; } -int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - +int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio, + bool stream_has_echo) { if (mode_ == kAdaptiveAnalog && !was_analog_level_set_) { - return apm_->kStreamParameterNotSetError; + return AudioProcessing::kStreamParameterNotSetError; } - assert(audio->num_frames_per_band() <= 160); - assert(audio->num_channels() == num_handles()); + RTC_DCHECK(num_proc_channels_); + RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, + audio->num_frames_per_band()); + RTC_DCHECK_EQ(audio->num_channels(), *num_proc_channels_); stream_is_saturated_ = false; - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); - int32_t capture_level_out = 0; + bool error_reported = false; + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + int16_t split_band_data[AudioBuffer::kMaxNumBands] + [AudioBuffer::kMaxSplitFrameLength]; + int16_t* split_bands[AudioBuffer::kMaxNumBands] = { + split_band_data[0], split_band_data[1], split_band_data[2]}; + audio->ExportSplitChannelData(ch, split_bands); + + // The call to stream_has_echo() is ok from a deadlock perspective + // as the capture lock is allready held. + int32_t new_capture_level = 0; uint8_t saturation_warning = 0; + int err_analyze = WebRtcAgc_Analyze( + mono_agcs_[ch]->state, split_bands, audio->num_bands(), + audio->num_frames_per_band(), capture_levels_[ch], &new_capture_level, + stream_has_echo, &saturation_warning, mono_agcs_[ch]->gains); + capture_levels_[ch] = new_capture_level; - int err = WebRtcAgc_Process( - my_handle, - audio->split_bands_const(i), - audio->num_bands(), - audio->num_frames_per_band(), - audio->split_bands(i), - capture_levels_[i], - &capture_level_out, - apm_->echo_cancellation()->stream_has_echo(), - &saturation_warning); + error_reported = error_reported || err_analyze != AudioProcessing::kNoError; - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } + stream_is_saturated_ = stream_is_saturated_ || saturation_warning == 1; + } - capture_levels_[i] = capture_level_out; - if (saturation_warning == 1) { - stream_is_saturated_ = true; + // Choose the minimun gain for application + size_t index_to_apply = 0; + for (size_t ch = 1; ch < mono_agcs_.size(); ++ch) { + if (mono_agcs_[index_to_apply]->gains[10] < mono_agcs_[ch]->gains[10]) { + index_to_apply = ch; } } - if (mode_ == kAdaptiveAnalog) { - // Take the analog level to be the average across the handles. - analog_capture_level_ = 0; - for (int i = 0; i < num_handles(); i++) { - analog_capture_level_ += capture_levels_[i]; - } + if (use_legacy_gain_applier_) { + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + int16_t split_band_data[AudioBuffer::kMaxNumBands] + [AudioBuffer::kMaxSplitFrameLength]; + int16_t* split_bands[AudioBuffer::kMaxNumBands] = { + split_band_data[0], split_band_data[1], split_band_data[2]}; + audio->ExportSplitChannelData(ch, split_bands); - analog_capture_level_ /= num_handles(); + int err_process = WebRtcAgc_Process( + mono_agcs_[ch]->state, mono_agcs_[index_to_apply]->gains, split_bands, + audio->num_bands(), split_bands); + RTC_DCHECK_EQ(err_process, 0); + + audio->ImportSplitChannelData(ch, split_bands); + } + } else { + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + ApplyDigitalGain(mono_agcs_[index_to_apply]->gains, audio->num_bands(), + audio->split_bands(ch)); + } + } + + RTC_DCHECK_LT(0ul, *num_proc_channels_); + if (mode_ == kAdaptiveAnalog) { + // Take the analog level to be the minimum accross all channels. + analog_capture_level_ = capture_levels_[0]; + for (size_t ch = 1; ch < mono_agcs_.size(); ++ch) { + analog_capture_level_ = + std::min(analog_capture_level_, capture_levels_[ch]); + } + } + + if (error_reported) { + return AudioProcessing::kUnspecifiedError; } was_analog_level_set_ = false; - return apm_->kNoError; + + return AudioProcessing::kNoError; } + // TODO(ajm): ensure this is called under kAdaptiveAnalog. int GainControlImpl::set_stream_analog_level(int level) { - CriticalSectionScoped crit_scoped(crit_); + data_dumper_->DumpRaw("gain_control_set_stream_analog_level", 1, &level); + was_analog_level_set_ = true; if (level < minimum_capture_level_ || level > maximum_capture_level_) { - return apm_->kBadParameterError; + return AudioProcessing::kBadParameterError; } analog_capture_level_ = level; - return apm_->kNoError; + return AudioProcessing::kNoError; } -int GainControlImpl::stream_analog_level() { - // TODO(ajm): enable this assertion? - //assert(mode_ == kAdaptiveAnalog); - +int GainControlImpl::stream_analog_level() const { + data_dumper_->DumpRaw("gain_control_stream_analog_level", 1, + &analog_capture_level_); return analog_capture_level_; } -int GainControlImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(crit_); - return EnableComponent(enable); -} - -bool GainControlImpl::is_enabled() const { - return is_component_enabled(); -} - int GainControlImpl::set_mode(Mode mode) { - CriticalSectionScoped crit_scoped(crit_); if (MapSetting(mode) == -1) { - return apm_->kBadParameterError; + return AudioProcessing::kBadParameterError; } mode_ = mode; - return Initialize(); + RTC_DCHECK(num_proc_channels_); + RTC_DCHECK(sample_rate_hz_); + Initialize(*num_proc_channels_, *sample_rate_hz_); + return AudioProcessing::kNoError; } -GainControl::Mode GainControlImpl::mode() const { - return mode_; -} -int GainControlImpl::set_analog_level_limits(int minimum, - int maximum) { - CriticalSectionScoped crit_scoped(crit_); - if (minimum < 0) { - return apm_->kBadParameterError; - } - - if (maximum > 65535) { - return apm_->kBadParameterError; - } - - if (maximum < minimum) { - return apm_->kBadParameterError; +int GainControlImpl::set_analog_level_limits(int minimum, int maximum) { + if (minimum < 0 || maximum > 65535 || maximum < minimum) { + return AudioProcessing::kBadParameterError; } minimum_capture_level_ = minimum; maximum_capture_level_ = maximum; - return Initialize(); + RTC_DCHECK(num_proc_channels_); + RTC_DCHECK(sample_rate_hz_); + Initialize(*num_proc_channels_, *sample_rate_hz_); + return AudioProcessing::kNoError; } -int GainControlImpl::analog_level_minimum() const { - return minimum_capture_level_; -} - -int GainControlImpl::analog_level_maximum() const { - return maximum_capture_level_; -} - -bool GainControlImpl::stream_is_saturated() const { - return stream_is_saturated_; -} int GainControlImpl::set_target_level_dbfs(int level) { - CriticalSectionScoped crit_scoped(crit_); if (level > 31 || level < 0) { - return apm_->kBadParameterError; + return AudioProcessing::kBadParameterError; } - target_level_dbfs_ = level; return Configure(); } -int GainControlImpl::target_level_dbfs() const { - return target_level_dbfs_; -} - int GainControlImpl::set_compression_gain_db(int gain) { - CriticalSectionScoped crit_scoped(crit_); if (gain < 0 || gain > 90) { - return apm_->kBadParameterError; + RTC_LOG(LS_ERROR) << "set_compression_gain_db(" << gain << ") failed."; + return AudioProcessing::kBadParameterError; } - compression_gain_db_ = gain; return Configure(); } -int GainControlImpl::compression_gain_db() const { - return compression_gain_db_; -} - int GainControlImpl::enable_limiter(bool enable) { - CriticalSectionScoped crit_scoped(crit_); limiter_enabled_ = enable; return Configure(); } -bool GainControlImpl::is_limiter_enabled() const { - return limiter_enabled_; -} +void GainControlImpl::Initialize(size_t num_proc_channels, int sample_rate_hz) { + data_dumper_->InitiateNewSetOfRecordings(); -int GainControlImpl::Initialize() { - int err = ProcessingComponent::Initialize(); - if (err != apm_->kNoError || !is_component_enabled()) { - return err; + RTC_DCHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000 || + sample_rate_hz == 48000); + + num_proc_channels_ = num_proc_channels; + sample_rate_hz_ = sample_rate_hz; + + mono_agcs_.resize(*num_proc_channels_); + capture_levels_.resize(*num_proc_channels_); + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + if (!mono_agcs_[ch]) { + mono_agcs_[ch].reset(new MonoAgcState()); + } + + int error = WebRtcAgc_Init(mono_agcs_[ch]->state, minimum_capture_level_, + maximum_capture_level_, MapSetting(mode_), + *sample_rate_hz_); + RTC_DCHECK_EQ(error, 0); + capture_levels_[ch] = analog_capture_level_; } - capture_levels_.assign(num_handles(), analog_capture_level_); - return apm_->kNoError; + Configure(); } -void* GainControlImpl::CreateHandle() const { - return WebRtcAgc_Create(); -} - -void GainControlImpl::DestroyHandle(void* handle) const { - WebRtcAgc_Free(static_cast(handle)); -} - -int GainControlImpl::InitializeHandle(void* handle) const { - return WebRtcAgc_Init(static_cast(handle), - minimum_capture_level_, - maximum_capture_level_, - MapSetting(mode_), - apm_->proc_sample_rate_hz()); -} - -int GainControlImpl::ConfigureHandle(void* handle) const { +int GainControlImpl::Configure() { WebRtcAgcConfig config; // TODO(ajm): Flip the sign here (since AGC expects a positive value) if we // change the interface. - //assert(target_level_dbfs_ <= 0); - //config.targetLevelDbfs = static_cast(-target_level_dbfs_); + // RTC_DCHECK_LE(target_level_dbfs_, 0); + // config.targetLevelDbfs = static_cast(-target_level_dbfs_); config.targetLevelDbfs = static_cast(target_level_dbfs_); - config.compressionGaindB = - static_cast(compression_gain_db_); + config.compressionGaindB = static_cast(compression_gain_db_); config.limiterEnable = limiter_enabled_; - return WebRtcAgc_set_config(static_cast(handle), config); -} - -int GainControlImpl::num_handles_required() const { - return apm_->num_output_channels(); -} - -int GainControlImpl::GetHandleError(void* handle) const { - // The AGC has no get_error() function. - // (Despite listing errors in its interface...) - assert(handle != NULL); - return apm_->kUnspecifiedError; + int error = AudioProcessing::kNoError; + for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { + int error_ch = WebRtcAgc_set_config(mono_agcs_[ch]->state, config); + if (error_ch != AudioProcessing::kNoError) { + error = error_ch; + } + } + return error; } } // namespace webrtc diff --git a/webrtc/modules/audio_processing/gain_control_impl.h b/webrtc/modules/audio_processing/gain_control_impl.h index f24d200..b65d697 100644 --- a/webrtc/modules/audio_processing/gain_control_impl.h +++ b/webrtc/modules/audio_processing/gain_control_impl.h @@ -8,75 +8,85 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_GAIN_CONTROL_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_GAIN_CONTROL_IMPL_H_ +#ifndef MODULES_AUDIO_PROCESSING_GAIN_CONTROL_IMPL_H_ +#define MODULES_AUDIO_PROCESSING_GAIN_CONTROL_IMPL_H_ +#include +#include + +#include #include -#include "webrtc/modules/audio_processing/include/audio_processing.h" -#include "webrtc/modules/audio_processing/processing_component.h" +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "modules/audio_processing/agc/gain_control.h" namespace webrtc { +class ApmDataDumper; class AudioBuffer; -class CriticalSectionWrapper; -class GainControlImpl : public GainControl, - public ProcessingComponent { +class GainControlImpl : public GainControl { public: - GainControlImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit); - virtual ~GainControlImpl(); + GainControlImpl(); + GainControlImpl(const GainControlImpl&) = delete; + GainControlImpl& operator=(const GainControlImpl&) = delete; - int ProcessRenderAudio(AudioBuffer* audio); - int AnalyzeCaptureAudio(AudioBuffer* audio); - int ProcessCaptureAudio(AudioBuffer* audio); + ~GainControlImpl() override; - // ProcessingComponent implementation. - int Initialize() override; + void ProcessRenderAudio(rtc::ArrayView packed_render_audio); + int AnalyzeCaptureAudio(const AudioBuffer& audio); + int ProcessCaptureAudio(AudioBuffer* audio, bool stream_has_echo); + + void Initialize(size_t num_proc_channels, int sample_rate_hz); + + static void PackRenderAudioBuffer(const AudioBuffer& audio, + std::vector* packed_buffer); // GainControl implementation. - bool is_enabled() const override; - int stream_analog_level() override; - bool is_limiter_enabled() const override; - Mode mode() const override; + int stream_analog_level() const override; + bool is_limiter_enabled() const override { return limiter_enabled_; } + Mode mode() const override { return mode_; } + int set_mode(Mode mode) override; + int compression_gain_db() const override { return compression_gain_db_; } + int set_analog_level_limits(int minimum, int maximum) override; + int set_compression_gain_db(int gain) override; + int set_target_level_dbfs(int level) override; + int enable_limiter(bool enable) override; + int set_stream_analog_level(int level) override; private: + struct MonoAgcState; + // GainControl implementation. - int Enable(bool enable) override; - int set_stream_analog_level(int level) override; - int set_mode(Mode mode) override; - int set_target_level_dbfs(int level) override; - int target_level_dbfs() const override; - int set_compression_gain_db(int gain) override; - int compression_gain_db() const override; - int enable_limiter(bool enable) override; - int set_analog_level_limits(int minimum, int maximum) override; - int analog_level_minimum() const override; - int analog_level_maximum() const override; - bool stream_is_saturated() const override; + int target_level_dbfs() const override { return target_level_dbfs_; } + int analog_level_minimum() const override { return minimum_capture_level_; } + int analog_level_maximum() const override { return maximum_capture_level_; } + bool stream_is_saturated() const override { return stream_is_saturated_; } - // ProcessingComponent implementation. - void* CreateHandle() const override; - int InitializeHandle(void* handle) const override; - int ConfigureHandle(void* handle) const override; - void DestroyHandle(void* handle) const override; - int num_handles_required() const override; - int GetHandleError(void* handle) const override; + int Configure(); - const AudioProcessing* apm_; - CriticalSectionWrapper* crit_; + std::unique_ptr data_dumper_; + + const bool use_legacy_gain_applier_; Mode mode_; int minimum_capture_level_; int maximum_capture_level_; bool limiter_enabled_; int target_level_dbfs_; int compression_gain_db_; - std::vector capture_levels_; - int analog_capture_level_; + int analog_capture_level_ = 0; bool was_analog_level_set_; bool stream_is_saturated_; + + std::vector> mono_agcs_; + std::vector capture_levels_; + + absl::optional num_proc_channels_; + absl::optional sample_rate_hz_; + + static int instance_counter_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_GAIN_CONTROL_IMPL_H_ +#endif // MODULES_AUDIO_PROCESSING_GAIN_CONTROL_IMPL_H_ diff --git a/webrtc/modules/audio_processing/gain_controller2.cc b/webrtc/modules/audio_processing/gain_controller2.cc new file mode 100644 index 0000000..8e71d40 --- /dev/null +++ b/webrtc/modules/audio_processing/gain_controller2.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/gain_controller2.h" + +#include "common_audio/include/audio_util.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/include/audio_frame_view.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { + +int GainController2::instance_count_ = 0; + +GainController2::GainController2() + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + gain_applier_(/*hard_clip_samples=*/false, + /*initial_gain_factor=*/0.f), + limiter_(static_cast(48000), data_dumper_.get(), "Agc2") { + if (config_.adaptive_digital.enabled) { + adaptive_agc_.reset(new AdaptiveAgc(data_dumper_.get())); + } +} + +GainController2::~GainController2() = default; + +void GainController2::Initialize(int sample_rate_hz) { + RTC_DCHECK(sample_rate_hz == AudioProcessing::kSampleRate8kHz || + sample_rate_hz == AudioProcessing::kSampleRate16kHz || + sample_rate_hz == AudioProcessing::kSampleRate32kHz || + sample_rate_hz == AudioProcessing::kSampleRate48kHz); + limiter_.SetSampleRate(sample_rate_hz); + data_dumper_->InitiateNewSetOfRecordings(); + data_dumper_->DumpRaw("sample_rate_hz", sample_rate_hz); +} + +void GainController2::Process(AudioBuffer* audio) { + AudioFrameView float_frame(audio->channels(), audio->num_channels(), + audio->num_frames()); + // Apply fixed gain first, then the adaptive one. + gain_applier_.ApplyGain(float_frame); + if (adaptive_agc_) { + adaptive_agc_->Process(float_frame, limiter_.LastAudioLevel()); + } + limiter_.Process(float_frame); +} + +void GainController2::NotifyAnalogLevel(int level) { + if (analog_level_ != level && adaptive_agc_) { + adaptive_agc_->Reset(); + } + analog_level_ = level; +} + +void GainController2::ApplyConfig( + const AudioProcessing::Config::GainController2& config) { + RTC_DCHECK(Validate(config)) + << " the invalid config was " << ToString(config); + + config_ = config; + if (config.fixed_digital.gain_db != config_.fixed_digital.gain_db) { + // Reset the limiter to quickly react on abrupt level changes caused by + // large changes of the fixed gain. + limiter_.Reset(); + } + gain_applier_.SetGainFactor(DbToRatio(config_.fixed_digital.gain_db)); + if (config_.adaptive_digital.enabled) { + adaptive_agc_.reset(new AdaptiveAgc(data_dumper_.get(), config_)); + } else { + adaptive_agc_.reset(); + } +} + +bool GainController2::Validate( + const AudioProcessing::Config::GainController2& config) { + return config.fixed_digital.gain_db >= 0.f && + config.fixed_digital.gain_db < 50.f && + config.adaptive_digital.extra_saturation_margin_db >= 0.f && + config.adaptive_digital.extra_saturation_margin_db <= 100.f; +} + +std::string GainController2::ToString( + const AudioProcessing::Config::GainController2& config) { + rtc::StringBuilder ss; + std::string adaptive_digital_level_estimator; + using LevelEstimatorType = + AudioProcessing::Config::GainController2::LevelEstimator; + switch (config.adaptive_digital.level_estimator) { + case LevelEstimatorType::kRms: + adaptive_digital_level_estimator = "RMS"; + break; + case LevelEstimatorType::kPeak: + adaptive_digital_level_estimator = "peak"; + break; + } + // clang-format off + // clang formatting doesn't respect custom nested style. + ss << "{" + "enabled: " << (config.enabled ? "true" : "false") << ", " + "fixed_digital: {gain_db: " << config.fixed_digital.gain_db << "}, " + "adaptive_digital: {" + "enabled: " + << (config.adaptive_digital.enabled ? "true" : "false") << ", " + "level_estimator: {" + "type: " << adaptive_digital_level_estimator << ", " + "adjacent_speech_frames_threshold: " + << config.adaptive_digital + .level_estimator_adjacent_speech_frames_threshold << ", " + "initial_saturation_margin_db: " + << config.adaptive_digital.initial_saturation_margin_db << ", " + "extra_saturation_margin_db: " + << config.adaptive_digital.extra_saturation_margin_db << "}, " + "gain_applier: {" + "adjacent_speech_frames_threshold: " + << config.adaptive_digital + .gain_applier_adjacent_speech_frames_threshold << ", " + "max_gain_change_db_per_second: " + << config.adaptive_digital.max_gain_change_db_per_second << ", " + "max_output_noise_level_dbfs: " + << config.adaptive_digital.max_output_noise_level_dbfs << "}" + "}" + "}"; + // clang-format on + return ss.Release(); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/gain_controller2.h b/webrtc/modules/audio_processing/gain_controller2.h new file mode 100644 index 0000000..7ed310e --- /dev/null +++ b/webrtc/modules/audio_processing/gain_controller2.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_GAIN_CONTROLLER2_H_ +#define MODULES_AUDIO_PROCESSING_GAIN_CONTROLLER2_H_ + +#include +#include + +#include "modules/audio_processing/agc2/adaptive_agc.h" +#include "modules/audio_processing/agc2/gain_applier.h" +#include "modules/audio_processing/agc2/limiter.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "rtc_base/constructor_magic.h" + +namespace webrtc { + +class ApmDataDumper; +class AudioBuffer; + +// Gain Controller 2 aims to automatically adjust levels by acting on the +// microphone gain and/or applying digital gain. +class GainController2 { + public: + GainController2(); + ~GainController2(); + + void Initialize(int sample_rate_hz); + void Process(AudioBuffer* audio); + void NotifyAnalogLevel(int level); + + void ApplyConfig(const AudioProcessing::Config::GainController2& config); + static bool Validate(const AudioProcessing::Config::GainController2& config); + static std::string ToString( + const AudioProcessing::Config::GainController2& config); + + private: + static int instance_count_; + std::unique_ptr data_dumper_; + AudioProcessing::Config::GainController2 config_; + GainApplier gain_applier_; + std::unique_ptr adaptive_agc_; + Limiter limiter_; + int analog_level_ = -1; + + RTC_DISALLOW_COPY_AND_ASSIGN(GainController2); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_GAIN_CONTROLLER2_H_ diff --git a/webrtc/modules/audio_processing/high_pass_filter.cc b/webrtc/modules/audio_processing/high_pass_filter.cc new file mode 100644 index 0000000..bff7209 --- /dev/null +++ b/webrtc/modules/audio_processing/high_pass_filter.cc @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2012 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 "modules/audio_processing/high_pass_filter.h" + +#include "api/array_view.h" +#include "modules/audio_processing/audio_buffer.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { +// [B,A] = butter(2,100/8000,'high') +constexpr CascadedBiQuadFilter::BiQuadCoefficients + kHighPassFilterCoefficients16kHz = {{0.97261f, -1.94523f, 0.97261f}, + {-1.94448f, 0.94598f}}; + +// [B,A] = butter(2,100/16000,'high') +constexpr CascadedBiQuadFilter::BiQuadCoefficients + kHighPassFilterCoefficients32kHz = {{0.98621f, -1.97242f, 0.98621f}, + {-1.97223f, 0.97261f}}; + +// [B,A] = butter(2,100/24000,'high') +constexpr CascadedBiQuadFilter::BiQuadCoefficients + kHighPassFilterCoefficients48kHz = {{0.99079f, -1.98157f, 0.99079f}, + {-1.98149f, 0.98166f}}; + +constexpr size_t kNumberOfHighPassBiQuads = 1; + +const CascadedBiQuadFilter::BiQuadCoefficients& ChooseCoefficients( + int sample_rate_hz) { + switch (sample_rate_hz) { + case 16000: + return kHighPassFilterCoefficients16kHz; + case 32000: + return kHighPassFilterCoefficients32kHz; + case 48000: + return kHighPassFilterCoefficients48kHz; + default: + RTC_NOTREACHED(); + } + RTC_NOTREACHED(); + return kHighPassFilterCoefficients16kHz; +} + +} // namespace + +HighPassFilter::HighPassFilter(int sample_rate_hz, size_t num_channels) + : sample_rate_hz_(sample_rate_hz) { + filters_.resize(num_channels); + const auto& coefficients = ChooseCoefficients(sample_rate_hz_); + for (size_t k = 0; k < filters_.size(); ++k) { + filters_[k].reset( + new CascadedBiQuadFilter(coefficients, kNumberOfHighPassBiQuads)); + } +} + +HighPassFilter::~HighPassFilter() = default; + +void HighPassFilter::Process(AudioBuffer* audio, bool use_split_band_data) { + RTC_DCHECK(audio); + RTC_DCHECK_EQ(filters_.size(), audio->num_channels()); + if (use_split_band_data) { + for (size_t k = 0; k < audio->num_channels(); ++k) { + rtc::ArrayView channel_data = rtc::ArrayView( + audio->split_bands(k)[0], audio->num_frames_per_band()); + filters_[k]->Process(channel_data); + } + } else { + for (size_t k = 0; k < audio->num_channels(); ++k) { + rtc::ArrayView channel_data = + rtc::ArrayView(&audio->channels()[k][0], audio->num_frames()); + filters_[k]->Process(channel_data); + } + } +} + +void HighPassFilter::Process(std::vector>* audio) { + RTC_DCHECK_EQ(filters_.size(), audio->size()); + for (size_t k = 0; k < audio->size(); ++k) { + filters_[k]->Process((*audio)[k]); + } +} + +void HighPassFilter::Reset() { + for (size_t k = 0; k < filters_.size(); ++k) { + filters_[k]->Reset(); + } +} + +void HighPassFilter::Reset(size_t num_channels) { + const size_t old_num_channels = filters_.size(); + filters_.resize(num_channels); + if (filters_.size() < old_num_channels) { + Reset(); + } else { + for (size_t k = 0; k < old_num_channels; ++k) { + filters_[k]->Reset(); + } + const auto& coefficients = ChooseCoefficients(sample_rate_hz_); + for (size_t k = old_num_channels; k < filters_.size(); ++k) { + filters_[k].reset( + new CascadedBiQuadFilter(coefficients, kNumberOfHighPassBiQuads)); + } + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/high_pass_filter.h b/webrtc/modules/audio_processing/high_pass_filter.h new file mode 100644 index 0000000..7e7c370 --- /dev/null +++ b/webrtc/modules/audio_processing/high_pass_filter.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/utility/cascaded_biquad_filter.h" + +namespace webrtc { + +class AudioBuffer; + +class HighPassFilter { + public: + HighPassFilter(int sample_rate_hz, size_t num_channels); + ~HighPassFilter(); + HighPassFilter(const HighPassFilter&) = delete; + HighPassFilter& operator=(const HighPassFilter&) = delete; + + void Process(AudioBuffer* audio, bool use_split_band_data); + void Process(std::vector>* audio); + void Reset(); + void Reset(size_t num_channels); + + int sample_rate_hz() const { return sample_rate_hz_; } + size_t num_channels() const { return filters_.size(); } + + private: + const int sample_rate_hz_; + std::vector> filters_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_H_ diff --git a/webrtc/modules/audio_processing/high_pass_filter_impl.cc b/webrtc/modules/audio_processing/high_pass_filter_impl.cc deleted file mode 100644 index 29e4820..0000000 --- a/webrtc/modules/audio_processing/high_pass_filter_impl.cc +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_processing/high_pass_filter_impl.h" - -#include - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" -#include "webrtc/typedefs.h" - - -namespace webrtc { -namespace { -const int16_t kFilterCoefficients8kHz[5] = - {3798, -7596, 3798, 7807, -3733}; - -const int16_t kFilterCoefficients[5] = - {4012, -8024, 4012, 8002, -3913}; - -struct FilterState { - int16_t y[4]; - int16_t x[2]; - const int16_t* ba; -}; - -int InitializeFilter(FilterState* hpf, int sample_rate_hz) { - assert(hpf != NULL); - - if (sample_rate_hz == AudioProcessing::kSampleRate8kHz) { - hpf->ba = kFilterCoefficients8kHz; - } else { - hpf->ba = kFilterCoefficients; - } - - WebRtcSpl_MemSetW16(hpf->x, 0, 2); - WebRtcSpl_MemSetW16(hpf->y, 0, 4); - - return AudioProcessing::kNoError; -} - -int Filter(FilterState* hpf, int16_t* data, size_t length) { - assert(hpf != NULL); - - int32_t tmp_int32 = 0; - int16_t* y = hpf->y; - int16_t* x = hpf->x; - const int16_t* ba = hpf->ba; - - for (size_t i = 0; i < length; i++) { - // y[i] = b[0] * x[i] + b[1] * x[i-1] + b[2] * x[i-2] - // + -a[1] * y[i-1] + -a[2] * y[i-2]; - - tmp_int32 = y[1] * ba[3]; // -a[1] * y[i-1] (low part) - tmp_int32 += y[3] * ba[4]; // -a[2] * y[i-2] (low part) - tmp_int32 = (tmp_int32 >> 15); - tmp_int32 += y[0] * ba[3]; // -a[1] * y[i-1] (high part) - tmp_int32 += y[2] * ba[4]; // -a[2] * y[i-2] (high part) - tmp_int32 = (tmp_int32 << 1); - - tmp_int32 += data[i] * ba[0]; // b[0]*x[0] - tmp_int32 += x[0] * ba[1]; // b[1]*x[i-1] - tmp_int32 += x[1] * ba[2]; // b[2]*x[i-2] - - // Update state (input part) - x[1] = x[0]; - x[0] = data[i]; - - // Update state (filtered part) - y[2] = y[0]; - y[3] = y[1]; - y[0] = static_cast(tmp_int32 >> 13); - y[1] = static_cast( - (tmp_int32 - (static_cast(y[0]) << 13)) << 2); - - // Rounding in Q12, i.e. add 2^11 - tmp_int32 += 2048; - - // Saturate (to 2^27) so that the HP filtered signal does not overflow - tmp_int32 = WEBRTC_SPL_SAT(static_cast(134217727), - tmp_int32, - static_cast(-134217728)); - - // Convert back to Q0 and use rounding. - data[i] = (int16_t)(tmp_int32 >> 12); - } - - return AudioProcessing::kNoError; -} -} // namespace - -typedef FilterState Handle; - -HighPassFilterImpl::HighPassFilterImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit) - : ProcessingComponent(), - apm_(apm), - crit_(crit) {} - -HighPassFilterImpl::~HighPassFilterImpl() {} - -int HighPassFilterImpl::ProcessCaptureAudio(AudioBuffer* audio) { - int err = apm_->kNoError; - - if (!is_component_enabled()) { - return apm_->kNoError; - } - - assert(audio->num_frames_per_band() <= 160); - - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); - err = Filter(my_handle, - audio->split_bands(i)[kBand0To8kHz], - audio->num_frames_per_band()); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } - } - - return apm_->kNoError; -} - -int HighPassFilterImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(crit_); - return EnableComponent(enable); -} - -bool HighPassFilterImpl::is_enabled() const { - return is_component_enabled(); -} - -void* HighPassFilterImpl::CreateHandle() const { - return new FilterState; -} - -void HighPassFilterImpl::DestroyHandle(void* handle) const { - delete static_cast(handle); -} - -int HighPassFilterImpl::InitializeHandle(void* handle) const { - return InitializeFilter(static_cast(handle), - apm_->proc_sample_rate_hz()); -} - -int HighPassFilterImpl::ConfigureHandle(void* /*handle*/) const { - return apm_->kNoError; // Not configurable. -} - -int HighPassFilterImpl::num_handles_required() const { - return apm_->num_output_channels(); -} - -int HighPassFilterImpl::GetHandleError(void* handle) const { - // The component has no detailed errors. - assert(handle != NULL); - return apm_->kUnspecifiedError; -} -} // namespace webrtc diff --git a/webrtc/modules/audio_processing/high_pass_filter_impl.h b/webrtc/modules/audio_processing/high_pass_filter_impl.h deleted file mode 100644 index 90b393e..0000000 --- a/webrtc/modules/audio_processing/high_pass_filter_impl.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_IMPL_H_ - -#include "webrtc/modules/audio_processing/include/audio_processing.h" -#include "webrtc/modules/audio_processing/processing_component.h" - -namespace webrtc { - -class AudioBuffer; -class CriticalSectionWrapper; - -class HighPassFilterImpl : public HighPassFilter, - public ProcessingComponent { - public: - HighPassFilterImpl(const AudioProcessing* apm, CriticalSectionWrapper* crit); - virtual ~HighPassFilterImpl(); - - int ProcessCaptureAudio(AudioBuffer* audio); - - // HighPassFilter implementation. - bool is_enabled() const override; - - private: - // HighPassFilter implementation. - int Enable(bool enable) override; - - // ProcessingComponent implementation. - void* CreateHandle() const override; - int InitializeHandle(void* handle) const override; - int ConfigureHandle(void* handle) const override; - void DestroyHandle(void* handle) const override; - int num_handles_required() const override; - int GetHandleError(void* handle) const override; - - const AudioProcessing* apm_; - CriticalSectionWrapper* crit_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_IMPL_H_ diff --git a/webrtc/modules/audio_processing/include/aec_dump.cc b/webrtc/modules/audio_processing/include/aec_dump.cc new file mode 100644 index 0000000..67809d0 --- /dev/null +++ b/webrtc/modules/audio_processing/include/aec_dump.cc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 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 "modules/audio_processing/include/aec_dump.h" + +namespace webrtc { +InternalAPMConfig::InternalAPMConfig() = default; +InternalAPMConfig::InternalAPMConfig(const InternalAPMConfig&) = default; +InternalAPMConfig::InternalAPMConfig(InternalAPMConfig&&) = default; +InternalAPMConfig& InternalAPMConfig::operator=(const InternalAPMConfig&) = + default; + +bool InternalAPMConfig::operator==(const InternalAPMConfig& other) { + return aec_enabled == other.aec_enabled && + aec_delay_agnostic_enabled == other.aec_delay_agnostic_enabled && + aec_drift_compensation_enabled == + other.aec_drift_compensation_enabled && + aec_extended_filter_enabled == other.aec_extended_filter_enabled && + aec_suppression_level == other.aec_suppression_level && + aecm_enabled == other.aecm_enabled && + aecm_comfort_noise_enabled == other.aecm_comfort_noise_enabled && + aecm_routing_mode == other.aecm_routing_mode && + agc_enabled == other.agc_enabled && agc_mode == other.agc_mode && + agc_limiter_enabled == other.agc_limiter_enabled && + hpf_enabled == other.hpf_enabled && ns_enabled == other.ns_enabled && + ns_level == other.ns_level && + transient_suppression_enabled == other.transient_suppression_enabled && + noise_robust_agc_enabled == other.noise_robust_agc_enabled && + pre_amplifier_enabled == other.pre_amplifier_enabled && + pre_amplifier_fixed_gain_factor == + other.pre_amplifier_fixed_gain_factor && + experiments_description == other.experiments_description; +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/include/aec_dump.h b/webrtc/modules/audio_processing/include/aec_dump.h new file mode 100644 index 0000000..ed5acb0 --- /dev/null +++ b/webrtc/modules/audio_processing/include/aec_dump.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ + +#include + +#include + +#include "modules/audio_processing/include/audio_frame_view.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "rtc_base/deprecation.h" + +namespace webrtc { + +// Struct for passing current config from APM without having to +// include protobuf headers. +struct InternalAPMConfig { + InternalAPMConfig(); + InternalAPMConfig(const InternalAPMConfig&); + InternalAPMConfig(InternalAPMConfig&&); + + InternalAPMConfig& operator=(const InternalAPMConfig&); + InternalAPMConfig& operator=(InternalAPMConfig&&) = delete; + + bool operator==(const InternalAPMConfig& other); + + bool aec_enabled = false; + bool aec_delay_agnostic_enabled = false; + bool aec_drift_compensation_enabled = false; + bool aec_extended_filter_enabled = false; + int aec_suppression_level = 0; + bool aecm_enabled = false; + bool aecm_comfort_noise_enabled = false; + int aecm_routing_mode = 0; + bool agc_enabled = false; + int agc_mode = 0; + bool agc_limiter_enabled = false; + bool hpf_enabled = false; + bool ns_enabled = false; + int ns_level = 0; + bool transient_suppression_enabled = false; + bool noise_robust_agc_enabled = false; + bool pre_amplifier_enabled = false; + float pre_amplifier_fixed_gain_factor = 1.f; + std::string experiments_description = ""; +}; + +// An interface for recording configuration and input/output streams +// of the Audio Processing Module. The recordings are called +// 'aec-dumps' and are stored in a protobuf format defined in +// debug.proto. +// The Write* methods are always safe to call concurrently or +// otherwise for all implementing subclasses. The intended mode of +// operation is to create a protobuf object from the input, and send +// it away to be written to file asynchronously. +class AecDump { + public: + struct AudioProcessingState { + int delay; + int drift; + int level; + bool keypress; + }; + + virtual ~AecDump() = default; + + // Logs Event::Type INIT message. + virtual void WriteInitMessage(const ProcessingConfig& api_format, + int64_t time_now_ms) = 0; + RTC_DEPRECATED void WriteInitMessage(const ProcessingConfig& api_format) { + WriteInitMessage(api_format, 0); + } + + // Logs Event::Type STREAM message. To log an input/output pair, + // call the AddCapture* and AddAudioProcessingState methods followed + // by a WriteCaptureStreamMessage call. + virtual void AddCaptureStreamInput( + const AudioFrameView& src) = 0; + virtual void AddCaptureStreamOutput( + const AudioFrameView& src) = 0; + virtual void AddCaptureStreamInput(const int16_t* const data, + int num_channels, + int samples_per_channel) = 0; + virtual void AddCaptureStreamOutput(const int16_t* const data, + int num_channels, + int samples_per_channel) = 0; + virtual void AddAudioProcessingState(const AudioProcessingState& state) = 0; + virtual void WriteCaptureStreamMessage() = 0; + + // Logs Event::Type REVERSE_STREAM message. + virtual void WriteRenderStreamMessage(const int16_t* const data, + int num_channels, + int samples_per_channel) = 0; + virtual void WriteRenderStreamMessage( + const AudioFrameView& src) = 0; + + virtual void WriteRuntimeSetting( + const AudioProcessing::RuntimeSetting& runtime_setting) = 0; + + // Logs Event::Type CONFIG message. + virtual void WriteConfig(const InternalAPMConfig& config) = 0; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ diff --git a/webrtc/modules/audio_processing/include/audio_frame_proxies.cc b/webrtc/modules/audio_processing/include/audio_frame_proxies.cc new file mode 100644 index 0000000..b960e72 --- /dev/null +++ b/webrtc/modules/audio_processing/include/audio_frame_proxies.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 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 "modules/audio_processing/include/audio_frame_proxies.h" + +#include "api/audio/audio_frame.h" +#include "modules/audio_processing/include/audio_processing.h" + +namespace webrtc { + +int ProcessAudioFrame(AudioProcessing* ap, AudioFrame* frame) { + if (!frame || !ap) { + return AudioProcessing::Error::kNullPointerError; + } + + StreamConfig input_config(frame->sample_rate_hz_, frame->num_channels_, + /*has_keyboard=*/false); + StreamConfig output_config(frame->sample_rate_hz_, frame->num_channels_, + /*has_keyboard=*/false); + RTC_DCHECK_EQ(frame->samples_per_channel(), input_config.num_frames()); + + int result = ap->ProcessStream(frame->data(), input_config, output_config, + frame->mutable_data()); + + AudioProcessingStats stats = ap->GetStatistics(); + + if (stats.voice_detected) { + frame->vad_activity_ = *stats.voice_detected + ? AudioFrame::VADActivity::kVadActive + : AudioFrame::VADActivity::kVadPassive; + } + + return result; +} + +int ProcessReverseAudioFrame(AudioProcessing* ap, AudioFrame* frame) { + if (!frame || !ap) { + return AudioProcessing::Error::kNullPointerError; + } + + // Must be a native rate. + if (frame->sample_rate_hz_ != AudioProcessing::NativeRate::kSampleRate8kHz && + frame->sample_rate_hz_ != AudioProcessing::NativeRate::kSampleRate16kHz && + frame->sample_rate_hz_ != AudioProcessing::NativeRate::kSampleRate32kHz && + frame->sample_rate_hz_ != AudioProcessing::NativeRate::kSampleRate48kHz) { + return AudioProcessing::Error::kBadSampleRateError; + } + + if (frame->num_channels_ <= 0) { + return AudioProcessing::Error::kBadNumberChannelsError; + } + + StreamConfig input_config(frame->sample_rate_hz_, frame->num_channels_, + /*has_keyboard=*/false); + StreamConfig output_config(frame->sample_rate_hz_, frame->num_channels_, + /*has_keyboard=*/false); + + int result = ap->ProcessReverseStream(frame->data(), input_config, + output_config, frame->mutable_data()); + return result; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/include/audio_frame_proxies.h b/webrtc/modules/audio_processing/include/audio_frame_proxies.h new file mode 100644 index 0000000..2d0f5b5 --- /dev/null +++ b/webrtc/modules/audio_processing/include/audio_frame_proxies.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_PROXIES_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_PROXIES_H_ + +namespace webrtc { + +class AudioFrame; +class AudioProcessing; + +// Processes a 10 ms |frame| of the primary audio stream using the provided +// AudioProcessing object. On the client-side, this is the near-end (or +// captured) audio. The |sample_rate_hz_|, |num_channels_|, and +// |samples_per_channel_| members of |frame| must be valid. If changed from the +// previous call to this function, it will trigger an initialization of the +// provided AudioProcessing object. +// The function returns any error codes passed from the AudioProcessing +// ProcessStream method. +int ProcessAudioFrame(AudioProcessing* ap, AudioFrame* frame); + +// Processes a 10 ms |frame| of the reverse direction audio stream using the +// provided AudioProcessing object. The frame may be modified. On the +// client-side, this is the far-end (or to be rendered) audio. The +// |sample_rate_hz_|, |num_channels_|, and |samples_per_channel_| members of +// |frame| must be valid. If changed from the previous call to this function, it +// will trigger an initialization of the provided AudioProcessing object. +// The function returns any error codes passed from the AudioProcessing +// ProcessReverseStream method. +int ProcessReverseAudioFrame(AudioProcessing* ap, AudioFrame* frame); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_PROXIES_H_ diff --git a/webrtc/modules/audio_processing/include/audio_frame_view.h b/webrtc/modules/audio_processing/include/audio_frame_view.h new file mode 100644 index 0000000..ab5779a --- /dev/null +++ b/webrtc/modules/audio_processing/include/audio_frame_view.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_VIEW_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_VIEW_H_ + +#include "api/array_view.h" + +namespace webrtc { + +// Class to pass audio data in T** format, where T is a numeric type. +template +class AudioFrameView { + public: + // |num_channels| and |channel_size| describe the T** + // |audio_samples|. |audio_samples| is assumed to point to a + // two-dimensional |num_channels * channel_size| array of floats. + AudioFrameView(T* const* audio_samples, + size_t num_channels, + size_t channel_size) + : audio_samples_(audio_samples), + num_channels_(num_channels), + channel_size_(channel_size) {} + + // Implicit cast to allow converting Frame to + // Frame. + template + AudioFrameView(AudioFrameView other) + : audio_samples_(other.data()), + num_channels_(other.num_channels()), + channel_size_(other.samples_per_channel()) {} + + AudioFrameView() = delete; + + size_t num_channels() const { return num_channels_; } + + size_t samples_per_channel() const { return channel_size_; } + + rtc::ArrayView channel(size_t idx) { + RTC_DCHECK_LE(0, idx); + RTC_DCHECK_LE(idx, num_channels_); + return rtc::ArrayView(audio_samples_[idx], channel_size_); + } + + rtc::ArrayView channel(size_t idx) const { + RTC_DCHECK_LE(0, idx); + RTC_DCHECK_LE(idx, num_channels_); + return rtc::ArrayView(audio_samples_[idx], channel_size_); + } + + T* const* data() { return audio_samples_; } + + private: + T* const* audio_samples_; + size_t num_channels_; + size_t channel_size_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_VIEW_H_ diff --git a/webrtc/modules/audio_processing/include/audio_processing.cc b/webrtc/modules/audio_processing/include/audio_processing.cc new file mode 100644 index 0000000..8854415 --- /dev/null +++ b/webrtc/modules/audio_processing/include/audio_processing.cc @@ -0,0 +1,124 @@ +/* + * 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 "modules/audio_processing/include/audio_processing.h" + +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/system/arch.h" + +namespace webrtc { +namespace { + +std::string NoiseSuppressionLevelToString( + const AudioProcessing::Config::NoiseSuppression::Level& level) { + switch (level) { + case AudioProcessing::Config::NoiseSuppression::Level::kLow: + return "Low"; + case AudioProcessing::Config::NoiseSuppression::Level::kModerate: + return "Moderate"; + case AudioProcessing::Config::NoiseSuppression::Level::kHigh: + return "High"; + case AudioProcessing::Config::NoiseSuppression::Level::kVeryHigh: + return "VeryHigh"; + } +} + +std::string GainController1ModeToString( + const AudioProcessing::Config::GainController1::Mode& mode) { + switch (mode) { + case AudioProcessing::Config::GainController1::Mode::kAdaptiveAnalog: + return "AdaptiveAnalog"; + case AudioProcessing::Config::GainController1::Mode::kAdaptiveDigital: + return "AdaptiveDigital"; + case AudioProcessing::Config::GainController1::Mode::kFixedDigital: + return "FixedDigital"; + } +} + +std::string GainController2LevelEstimatorToString( + const AudioProcessing::Config::GainController2::LevelEstimator& level) { + switch (level) { + case AudioProcessing::Config::GainController2::LevelEstimator::kRms: + return "Rms"; + case AudioProcessing::Config::GainController2::LevelEstimator::kPeak: + return "Peak"; + } +} + +int GetDefaultMaxInternalRate() { +#ifdef WEBRTC_ARCH_ARM_FAMILY + return 32000; +#else + return 48000; +#endif +} + +} // namespace + +constexpr int AudioProcessing::kNativeSampleRatesHz[]; + +void CustomProcessing::SetRuntimeSetting( + AudioProcessing::RuntimeSetting setting) {} + +AudioProcessing::Config::Pipeline::Pipeline() + : maximum_internal_processing_rate(GetDefaultMaxInternalRate()) {} + +std::string AudioProcessing::Config::ToString() const { + char buf[1024]; + rtc::SimpleStringBuilder builder(buf); + builder << "AudioProcessing::Config{ " + "pipeline: {" + "maximum_internal_processing_rate: " + << pipeline.maximum_internal_processing_rate + << ", multi_channel_render: " << pipeline.multi_channel_render + << ", " + ", multi_channel_capture: " + << pipeline.multi_channel_capture + << "}, " + "pre_amplifier: { enabled: " + << pre_amplifier.enabled + << ", fixed_gain_factor: " << pre_amplifier.fixed_gain_factor + << " }, high_pass_filter: { enabled: " << high_pass_filter.enabled + << " }, echo_canceller: { enabled: " << echo_canceller.enabled + << ", mobile_mode: " << echo_canceller.mobile_mode + << ", enforce_high_pass_filtering: " + << echo_canceller.enforce_high_pass_filtering + << " }, noise_suppression: { enabled: " << noise_suppression.enabled + << ", level: " + << NoiseSuppressionLevelToString(noise_suppression.level) + << " }, transient_suppression: { enabled: " + << transient_suppression.enabled + << " }, voice_detection: { enabled: " << voice_detection.enabled + << " }, gain_controller1: { enabled: " << gain_controller1.enabled + << ", mode: " << GainController1ModeToString(gain_controller1.mode) + << ", target_level_dbfs: " << gain_controller1.target_level_dbfs + << ", compression_gain_db: " << gain_controller1.compression_gain_db + << ", enable_limiter: " << gain_controller1.enable_limiter + << ", analog_level_minimum: " << gain_controller1.analog_level_minimum + << ", analog_level_maximum: " << gain_controller1.analog_level_maximum + << " }, gain_controller2: { enabled: " << gain_controller2.enabled + << ", fixed_digital: { gain_db: " + << gain_controller2.fixed_digital.gain_db + << " }, adaptive_digital: { enabled: " + << gain_controller2.adaptive_digital.enabled << ", level_estimator: " + << GainController2LevelEstimatorToString( + gain_controller2.adaptive_digital.level_estimator) + << ", use_saturation_protector: " + << gain_controller2.adaptive_digital.use_saturation_protector + << ", extra_saturation_margin_db: " + << gain_controller2.adaptive_digital.extra_saturation_margin_db + << " } }, residual_echo_detector: { enabled: " + << residual_echo_detector.enabled + << " }, level_estimation: { enabled: " << level_estimation.enabled + << " } }"; + return builder.str(); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/include/audio_processing.h b/webrtc/modules/audio_processing/include/audio_processing.h index c8ddc6a..d09e2ba 100644 --- a/webrtc/modules/audio_processing/include/audio_processing.h +++ b/webrtc/modules/audio_processing/include/audio_processing.h @@ -8,139 +8,109 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ // MSVC++ requires this to be set before any other includes to get M_PI. +#ifndef _USE_MATH_DEFINES #define _USE_MATH_DEFINES +#endif #include #include // size_t -#include // FILE +#include // FILE +#include + #include -#include "webrtc/base/arraysize.h" -#include "webrtc/base/platform_file.h" -#include "webrtc/common.h" -#include "webrtc/modules/audio_processing/beamformer/array_util.h" -#include "webrtc/typedefs.h" +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" +#include "api/audio/echo_control.h" +#include "api/scoped_refptr.h" +#include "modules/audio_processing/include/audio_processing_statistics.h" +#include "modules/audio_processing/include/config.h" +#include "rtc_base/arraysize.h" +#include "rtc_base/constructor_magic.h" +#include "rtc_base/deprecation.h" +#include "rtc_base/ref_count.h" +#include "rtc_base/system/file_wrapper.h" +#include "rtc_base/system/rtc_export.h" -struct AecCore; +namespace rtc { +class TaskQueue; +} // namespace rtc namespace webrtc { -class AudioFrame; - -template -class Beamformer; +class AecDump; +class AudioBuffer; class StreamConfig; class ProcessingConfig; -class EchoCancellation; -class EchoControlMobile; -class GainControl; -class HighPassFilter; -class LevelEstimator; -class NoiseSuppression; -class VoiceDetection; - -// Use to enable the extended filter mode in the AEC, along with robustness -// measures around the reported system delays. It comes with a significant -// increase in AEC complexity, but is much more robust to unreliable reported -// delays. -// -// Detailed changes to the algorithm: -// - The filter length is changed from 48 to 128 ms. This comes with tuning of -// several parameters: i) filter adaptation stepsize and error threshold; -// ii) non-linear processing smoothing and overdrive. -// - Option to ignore the reported delays on platforms which we deem -// sufficiently unreliable. See WEBRTC_UNTRUSTED_DELAY in echo_cancellation.c. -// - Faster startup times by removing the excessive "startup phase" processing -// of reported delays. -// - Much more conservative adjustments to the far-end read pointer. We smooth -// the delay difference more heavily, and back off from the difference more. -// Adjustments force a readaptation of the filter, so they should be avoided -// except when really necessary. -struct ExtendedFilter { - ExtendedFilter() : enabled(false) {} - explicit ExtendedFilter(bool enabled) : enabled(enabled) {} - bool enabled; -}; - -// Enables delay-agnostic echo cancellation. This feature relies on internally -// estimated delays between the process and reverse streams, thus not relying -// on reported system delays. This configuration only applies to -// EchoCancellation and not EchoControlMobile. It can be set in the constructor -// or using AudioProcessing::SetExtraOptions(). -struct DelayAgnostic { - DelayAgnostic() : enabled(false) {} - explicit DelayAgnostic(bool enabled) : enabled(enabled) {} - bool enabled; -}; +class EchoDetector; +class CustomAudioAnalyzer; +class CustomProcessing; // Use to enable experimental gain control (AGC). At startup the experimental // AGC moves the microphone volume up to |startup_min_volume| if the current // microphone volume is set too low. The value is clamped to its operating range // [12, 255]. Here, 255 maps to 100%. // -// Must be provided through AudioProcessing::Create(Confg&). +// Must be provided through AudioProcessingBuilder().Create(config). #if defined(WEBRTC_CHROMIUM_BUILD) static const int kAgcStartupMinVolume = 85; #else static const int kAgcStartupMinVolume = 0; #endif // defined(WEBRTC_CHROMIUM_BUILD) +static constexpr int kClippedLevelMin = 70; + +// To be deprecated: Please instead use the flag in the +// AudioProcessing::Config::AnalogGainController. +// TODO(webrtc:5298): Remove. struct ExperimentalAgc { - ExperimentalAgc() : enabled(true), startup_min_volume(kAgcStartupMinVolume) {} - explicit ExperimentalAgc(bool enabled) - : enabled(enabled), startup_min_volume(kAgcStartupMinVolume) {} + ExperimentalAgc() = default; + explicit ExperimentalAgc(bool enabled) : enabled(enabled) {} + ExperimentalAgc(bool enabled, + bool enabled_agc2_level_estimator, + bool digital_adaptive_disabled) + : enabled(enabled), + enabled_agc2_level_estimator(enabled_agc2_level_estimator), + digital_adaptive_disabled(digital_adaptive_disabled) {} + // Deprecated constructor: will be removed. + ExperimentalAgc(bool enabled, + bool enabled_agc2_level_estimator, + bool digital_adaptive_disabled, + bool analyze_before_aec) + : enabled(enabled), + enabled_agc2_level_estimator(enabled_agc2_level_estimator), + digital_adaptive_disabled(digital_adaptive_disabled) {} ExperimentalAgc(bool enabled, int startup_min_volume) : enabled(enabled), startup_min_volume(startup_min_volume) {} - bool enabled; - int startup_min_volume; + ExperimentalAgc(bool enabled, int startup_min_volume, int clipped_level_min) + : enabled(enabled), + startup_min_volume(startup_min_volume), + clipped_level_min(clipped_level_min) {} + static const ConfigOptionID identifier = ConfigOptionID::kExperimentalAgc; + bool enabled = true; + int startup_min_volume = kAgcStartupMinVolume; + // Lowest microphone level that will be applied in response to clipping. + int clipped_level_min = kClippedLevelMin; + bool enabled_agc2_level_estimator = false; + bool digital_adaptive_disabled = false; }; +// To be deprecated: Please instead use the flag in the +// AudioProcessing::Config::TransientSuppression. +// // Use to enable experimental noise suppression. It can be set in the -// constructor or using AudioProcessing::SetExtraOptions(). +// constructor. +// TODO(webrtc:5298): Remove. struct ExperimentalNs { ExperimentalNs() : enabled(false) {} explicit ExperimentalNs(bool enabled) : enabled(enabled) {} - bool enabled; -}; - -// Use to enable beamforming. Must be provided through the constructor. It will -// have no impact if used with AudioProcessing::SetExtraOptions(). -struct Beamforming { - Beamforming() - : enabled(false), - array_geometry(), - target_direction( - SphericalPointf(static_cast(M_PI) / 2.f, 0.f, 1.f)) {} - Beamforming(bool enabled, const std::vector& array_geometry) - : Beamforming(enabled, - array_geometry, - SphericalPointf(static_cast(M_PI) / 2.f, 0.f, 1.f)) { - } - Beamforming(bool enabled, - const std::vector& array_geometry, - SphericalPointf target_direction) - : enabled(enabled), - array_geometry(array_geometry), - target_direction(target_direction) {} - const bool enabled; - const std::vector array_geometry; - const SphericalPointf target_direction; -}; - -// Use to enable intelligibility enhancer in audio processing. Must be provided -// though the constructor. It will have no impact if used with -// AudioProcessing::SetExtraOptions(). -// -// Note: If enabled and the reverse stream has more than one output channel, -// the reverse stream will become an upmixed mono signal. -struct Intelligibility { - Intelligibility() : enabled(false) {} - explicit Intelligibility(bool enabled) : enabled(enabled) {} + static const ConfigOptionID identifier = ConfigOptionID::kExperimentalNs; bool enabled; }; @@ -149,11 +119,11 @@ struct Intelligibility { // // APM operates on two audio streams on a frame-by-frame basis. Frames of the // primary stream, on which all processing is applied, are passed to -// |ProcessStream()|. Frames of the reverse direction stream, which are used for -// analysis by some components, are passed to |AnalyzeReverseStream()|. On the -// client-side, this will typically be the near-end (capture) and far-end -// (render) streams, respectively. APM should be placed in the signal chain as -// close to the audio hardware abstraction layer (HAL) as possible. +// |ProcessStream()|. Frames of the reverse direction stream are passed to +// |ProcessReverseStream()|. On the client-side, this will typically be the +// near-end (capture) and far-end (render) streams, respectively. APM should be +// placed in the signal chain as close to the audio hardware abstraction layer +// (HAL) as possible. // // On the server-side, the reverse stream will normally not be used, with // processing occurring on each incoming stream. @@ -178,36 +148,43 @@ struct Intelligibility { // data. // // Usage example, omitting error checking: -// AudioProcessing* apm = AudioProcessing::Create(0); +// AudioProcessing* apm = AudioProcessingBuilder().Create(); // -// apm->high_pass_filter()->Enable(true); +// AudioProcessing::Config config; +// config.echo_canceller.enabled = true; +// config.echo_canceller.mobile_mode = false; // -// apm->echo_cancellation()->enable_drift_compensation(false); -// apm->echo_cancellation()->Enable(true); +// config.gain_controller1.enabled = true; +// config.gain_controller1.mode = +// AudioProcessing::Config::GainController1::kAdaptiveAnalog; +// config.gain_controller1.analog_level_minimum = 0; +// config.gain_controller1.analog_level_maximum = 255; +// +// config.gain_controller2.enabled = true; +// +// config.high_pass_filter.enabled = true; +// +// config.voice_detection.enabled = true; +// +// apm->ApplyConfig(config) // // apm->noise_reduction()->set_level(kHighSuppression); // apm->noise_reduction()->Enable(true); // -// apm->gain_control()->set_analog_level_limits(0, 255); -// apm->gain_control()->set_mode(kAdaptiveAnalog); -// apm->gain_control()->Enable(true); -// -// apm->voice_detection()->Enable(true); -// // // Start a voice call... // // // ... Render frame arrives bound for the audio HAL ... -// apm->AnalyzeReverseStream(render_frame); +// apm->ProcessReverseStream(render_frame); // // // ... Capture frame arrives from the audio HAL ... // // Call required set_stream_ functions. // apm->set_stream_delay_ms(delay_ms); -// apm->gain_control()->set_stream_analog_level(analog_level); +// apm->set_stream_analog_level(analog_level); // // apm->ProcessStream(capture_frame); // // // Call required stream_ functions. -// analog_level = apm->gain_control()->stream_analog_level(); +// analog_level = apm->recommended_stream_analog_level(); // has_voice = apm->stream_has_voice(); // // // Repeate render and capture processing for the duration of the call... @@ -217,31 +194,298 @@ struct Intelligibility { // // Close the application... // delete apm; // -class AudioProcessing { +class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { public: + // The struct below constitutes the new parameter scheme for the audio + // processing. It is being introduced gradually and until it is fully + // introduced, it is prone to change. + // TODO(peah): Remove this comment once the new config scheme is fully rolled + // out. + // + // The parameters and behavior of the audio processing module are controlled + // by changing the default values in the AudioProcessing::Config struct. + // The config is applied by passing the struct to the ApplyConfig method. + // + // This config is intended to be used during setup, and to enable/disable + // top-level processing effects. Use during processing may cause undesired + // submodule resets, affecting the audio quality. Use the RuntimeSetting + // construct for runtime configuration. + struct RTC_EXPORT Config { + + // Sets the properties of the audio processing pipeline. + struct RTC_EXPORT Pipeline { + Pipeline(); + + // Maximum allowed processing rate used internally. May only be set to + // 32000 or 48000 and any differing values will be treated as 48000. The + // default rate is currently selected based on the CPU architecture, but + // that logic may change. + int maximum_internal_processing_rate; + // Allow multi-channel processing of render audio. + bool multi_channel_render = false; + // Allow multi-channel processing of capture audio when AEC3 is active + // or a custom AEC is injected.. + bool multi_channel_capture = false; + } pipeline; + + // Enabled the pre-amplifier. It amplifies the capture signal + // before any other processing is done. + struct PreAmplifier { + bool enabled = false; + float fixed_gain_factor = 1.f; + } pre_amplifier; + + struct HighPassFilter { + bool enabled = false; + bool apply_in_full_band = true; + } high_pass_filter; + + struct EchoCanceller { + bool enabled = false; + bool mobile_mode = false; + bool export_linear_aec_output = false; + // Enforce the highpass filter to be on (has no effect for the mobile + // mode). + bool enforce_high_pass_filtering = true; + } echo_canceller; + + // Enables background noise suppression. + struct NoiseSuppression { + bool enabled = false; + enum Level { kLow, kModerate, kHigh, kVeryHigh }; + Level level = kModerate; + bool analyze_linear_aec_output_when_available = false; + } noise_suppression; + + // Enables transient suppression. + struct TransientSuppression { + bool enabled = false; + } transient_suppression; + + // Enables reporting of |voice_detected| in webrtc::AudioProcessingStats. + struct VoiceDetection { + bool enabled = false; + } voice_detection; + + // Enables automatic gain control (AGC) functionality. + // The automatic gain control (AGC) component brings the signal to an + // appropriate range. This is done by applying a digital gain directly and, + // in the analog mode, prescribing an analog gain to be applied at the audio + // HAL. + // Recommended to be enabled on the client-side. + struct GainController1 { + bool enabled = false; + enum Mode { + // Adaptive mode intended for use if an analog volume control is + // available on the capture device. It will require the user to provide + // coupling between the OS mixer controls and AGC through the + // stream_analog_level() functions. + // It consists of an analog gain prescription for the audio device and a + // digital compression stage. + kAdaptiveAnalog, + // Adaptive mode intended for situations in which an analog volume + // control is unavailable. It operates in a similar fashion to the + // adaptive analog mode, but with scaling instead applied in the digital + // domain. As with the analog mode, it additionally uses a digital + // compression stage. + kAdaptiveDigital, + // Fixed mode which enables only the digital compression stage also used + // by the two adaptive modes. + // It is distinguished from the adaptive modes by considering only a + // short time-window of the input signal. It applies a fixed gain + // through most of the input level range, and compresses (gradually + // reduces gain with increasing level) the input signal at higher + // levels. This mode is preferred on embedded devices where the capture + // signal level is predictable, so that a known gain can be applied. + kFixedDigital + }; + Mode mode = kAdaptiveAnalog; + // Sets the target peak level (or envelope) of the AGC in dBFs (decibels + // from digital full-scale). The convention is to use positive values. For + // instance, passing in a value of 3 corresponds to -3 dBFs, or a target + // level 3 dB below full-scale. Limited to [0, 31]. + int target_level_dbfs = 3; + // Sets the maximum gain the digital compression stage may apply, in dB. A + // higher number corresponds to greater compression, while a value of 0 + // will leave the signal uncompressed. Limited to [0, 90]. + // For updates after APM setup, use a RuntimeSetting instead. + int compression_gain_db = 9; + // When enabled, the compression stage will hard limit the signal to the + // target level. Otherwise, the signal will be compressed but not limited + // above the target level. + bool enable_limiter = true; + // Sets the minimum and maximum analog levels of the audio capture device. + // Must be set if an analog mode is used. Limited to [0, 65535]. + int analog_level_minimum = 0; + int analog_level_maximum = 255; + + // Enables the analog gain controller functionality. + struct AnalogGainController { + bool enabled = true; + int startup_min_volume = kAgcStartupMinVolume; + // Lowest analog microphone level that will be applied in response to + // clipping. + int clipped_level_min = kClippedLevelMin; + bool enable_agc2_level_estimator = false; + bool enable_digital_adaptive = true; + } analog_gain_controller; + } gain_controller1; + + // Enables the next generation AGC functionality. This feature replaces the + // standard methods of gain control in the previous AGC. Enabling this + // submodule enables an adaptive digital AGC followed by a limiter. By + // setting |fixed_gain_db|, the limiter can be turned into a compressor that + // first applies a fixed gain. The adaptive digital AGC can be turned off by + // setting |adaptive_digital_mode=false|. + struct GainController2 { + enum LevelEstimator { kRms, kPeak }; + bool enabled = false; + struct { + float gain_db = 0.f; + } fixed_digital; + struct { + bool enabled = false; + float vad_probability_attack = 1.f; + LevelEstimator level_estimator = kRms; + int level_estimator_adjacent_speech_frames_threshold = 1; + // TODO(crbug.com/webrtc/7494): Remove `use_saturation_protector`. + bool use_saturation_protector = true; + float initial_saturation_margin_db = 20.f; + float extra_saturation_margin_db = 2.f; + int gain_applier_adjacent_speech_frames_threshold = 1; + float max_gain_change_db_per_second = 3.f; + float max_output_noise_level_dbfs = -50.f; + } adaptive_digital; + } gain_controller2; + + struct ResidualEchoDetector { + bool enabled = true; + } residual_echo_detector; + + // Enables reporting of |output_rms_dbfs| in webrtc::AudioProcessingStats. + struct LevelEstimation { + bool enabled = false; + } level_estimation; + + std::string ToString() const; + }; + // TODO(mgraczyk): Remove once all methods that use ChannelLayout are gone. enum ChannelLayout { kMono, // Left, right. kStereo, - // Mono, keyboard mic. + // Mono, keyboard, and mic. kMonoAndKeyboard, - // Left, right, keyboard mic. + // Left, right, keyboard, and mic. kStereoAndKeyboard }; - // Creates an APM instance. Use one instance for every primary audio stream - // requiring processing. On the client-side, this would typically be one - // instance for the near-end stream, and additional instances for each far-end - // stream which requires processing. On the server-side, this would typically - // be one instance for every incoming stream. - static AudioProcessing* Create(); - // Allows passing in an optional configuration at create-time. - static AudioProcessing* Create(const Config& config); - // Only for testing. - static AudioProcessing* Create(const Config& config, - Beamformer* beamformer); - virtual ~AudioProcessing() {} + // Specifies the properties of a setting to be passed to AudioProcessing at + // runtime. + class RuntimeSetting { + public: + enum class Type { + kNotSpecified, + kCapturePreGain, + kCaptureCompressionGain, + kCaptureFixedPostGain, + kPlayoutVolumeChange, + kCustomRenderProcessingRuntimeSetting, + kPlayoutAudioDeviceChange, + kCaptureOutputUsed + }; + + // Play-out audio device properties. + struct PlayoutAudioDeviceInfo { + int id; // Identifies the audio device. + int max_volume; // Maximum play-out volume. + }; + + RuntimeSetting() : type_(Type::kNotSpecified), value_(0.f) {} + ~RuntimeSetting() = default; + + static RuntimeSetting CreateCapturePreGain(float gain) { + RTC_DCHECK_GE(gain, 1.f) << "Attenuation is not allowed."; + return {Type::kCapturePreGain, gain}; + } + + // Corresponds to Config::GainController1::compression_gain_db, but for + // runtime configuration. + static RuntimeSetting CreateCompressionGainDb(int gain_db) { + RTC_DCHECK_GE(gain_db, 0); + RTC_DCHECK_LE(gain_db, 90); + return {Type::kCaptureCompressionGain, static_cast(gain_db)}; + } + + // Corresponds to Config::GainController2::fixed_digital::gain_db, but for + // runtime configuration. + static RuntimeSetting CreateCaptureFixedPostGain(float gain_db) { + RTC_DCHECK_GE(gain_db, 0.f); + RTC_DCHECK_LE(gain_db, 90.f); + return {Type::kCaptureFixedPostGain, gain_db}; + } + + // Creates a runtime setting to notify play-out (aka render) audio device + // changes. + static RuntimeSetting CreatePlayoutAudioDeviceChange( + PlayoutAudioDeviceInfo audio_device) { + return {Type::kPlayoutAudioDeviceChange, audio_device}; + } + + // Creates a runtime setting to notify play-out (aka render) volume changes. + // |volume| is the unnormalized volume, the maximum of which + static RuntimeSetting CreatePlayoutVolumeChange(int volume) { + return {Type::kPlayoutVolumeChange, volume}; + } + + static RuntimeSetting CreateCustomRenderSetting(float payload) { + return {Type::kCustomRenderProcessingRuntimeSetting, payload}; + } + + static RuntimeSetting CreateCaptureOutputUsedSetting(bool payload) { + return {Type::kCaptureOutputUsed, payload}; + } + + Type type() const { return type_; } + // Getters do not return a value but instead modify the argument to protect + // from implicit casting. + void GetFloat(float* value) const { + RTC_DCHECK(value); + *value = value_.float_value; + } + void GetInt(int* value) const { + RTC_DCHECK(value); + *value = value_.int_value; + } + void GetBool(bool* value) const { + RTC_DCHECK(value); + *value = value_.bool_value; + } + void GetPlayoutAudioDeviceInfo(PlayoutAudioDeviceInfo* value) const { + RTC_DCHECK(value); + *value = value_.playout_audio_device_info; + } + + private: + RuntimeSetting(Type id, float value) : type_(id), value_(value) {} + RuntimeSetting(Type id, int value) : type_(id), value_(value) {} + RuntimeSetting(Type id, PlayoutAudioDeviceInfo value) + : type_(id), value_(value) {} + Type type_; + union U { + U() {} + U(int value) : int_value(value) {} + U(float value) : float_value(value) {} + U(PlayoutAudioDeviceInfo value) : playout_audio_device_info(value) {} + float float_value; + int int_value; + bool bool_value; + PlayoutAudioDeviceInfo playout_audio_device_info; + } value_; + }; + + ~AudioProcessing() override {} // Initializes internal states, while retaining all user settings. This // should be called before beginning to process a new audio stream. However, @@ -250,8 +494,9 @@ class AudioProcessing { // // It is also not necessary to call if the audio parameters (sample // rate and number of channels) have changed. Passing updated parameters - // directly to |ProcessStream()| and |AnalyzeReverseStream()| is permissible. + // directly to |ProcessStream()| and |ProcessReverseStream()| is permissible. // If the parameters are known at init-time though, they may be provided. + // TODO(webrtc:5298): Change to return void. virtual int Initialize() = 0; // The int16 interfaces require: @@ -268,24 +513,25 @@ class AudioProcessing { // Initialize with unpacked parameters. See Initialize() above for details. // // TODO(mgraczyk): Remove once clients are updated to use the new interface. - virtual int Initialize(int input_sample_rate_hz, - int output_sample_rate_hz, - int reverse_sample_rate_hz, - ChannelLayout input_layout, - ChannelLayout output_layout, - ChannelLayout reverse_layout) = 0; + virtual int Initialize(int capture_input_sample_rate_hz, + int capture_output_sample_rate_hz, + int render_sample_rate_hz, + ChannelLayout capture_input_layout, + ChannelLayout capture_output_layout, + ChannelLayout render_input_layout) = 0; - // Pass down additional options which don't have explicit setters. This - // ensures the options are applied immediately. - virtual void SetExtraOptions(const Config& config) = 0; + // TODO(peah): This method is a temporary solution used to take control + // over the parameters in the audio processing module and is likely to change. + virtual void ApplyConfig(const Config& config) = 0; // TODO(ajm): Only intended for internal use. Make private and friend the // necessary classes? virtual int proc_sample_rate_hz() const = 0; virtual int proc_split_sample_rate_hz() const = 0; - virtual int num_input_channels() const = 0; - virtual int num_output_channels() const = 0; - virtual int num_reverse_channels() const = 0; + virtual size_t num_input_channels() const = 0; + virtual size_t num_proc_channels() const = 0; + virtual size_t num_output_channels() const = 0; + virtual size_t num_reverse_channels() const = 0; // Set to true when the output of AudioProcessing will be muted or in some // other way not used. Ideally, the captured audio would still be processed, @@ -293,34 +539,16 @@ class AudioProcessing { // Default false. virtual void set_output_will_be_muted(bool muted) = 0; - // Processes a 10 ms |frame| of the primary audio stream. On the client-side, - // this is the near-end (or captured) audio. - // - // If needed for enabled functionality, any function with the set_stream_ tag - // must be called prior to processing the current frame. Any getter function - // with the stream_ tag which is needed should be called after processing. - // - // The |sample_rate_hz_|, |num_channels_|, and |samples_per_channel_| - // members of |frame| must be valid. If changed from the previous call to this - // method, it will trigger an initialization. - virtual int ProcessStream(AudioFrame* frame) = 0; + // Enqueue a runtime setting. + virtual void SetRuntimeSetting(RuntimeSetting setting) = 0; - // Accepts deinterleaved float audio with the range [-1, 1]. Each element - // of |src| points to a channel buffer, arranged according to - // |input_layout|. At output, the channels will be arranged according to - // |output_layout| at |output_sample_rate_hz| in |dest|. - // - // The output layout must have one channel or as many channels as the input. - // |src| and |dest| may use the same memory, if desired. - // - // TODO(mgraczyk): Remove once clients are updated to use the new interface. - virtual int ProcessStream(const float* const* src, - size_t samples_per_channel, - int input_sample_rate_hz, - ChannelLayout input_layout, - int output_sample_rate_hz, - ChannelLayout output_layout, - float* const* dest) = 0; + // Accepts and produces a 10 ms frame interleaved 16 bit integer audio as + // specified in |input_config| and |output_config|. |src| and |dest| may use + // the same memory, if desired. + virtual int ProcessStream(const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest) = 0; // Accepts deinterleaved float audio with the range [-1, 1]. Each element of // |src| points to a channel buffer, arranged according to |input_stream|. At @@ -334,116 +562,106 @@ class AudioProcessing { const StreamConfig& output_config, float* const* dest) = 0; - // Analyzes a 10 ms |frame| of the reverse direction audio stream. The frame - // will not be modified. On the client-side, this is the far-end (or to be - // rendered) audio. - // - // It is only necessary to provide this if echo processing is enabled, as the - // reverse stream forms the echo reference signal. It is recommended, but not - // necessary, to provide if gain control is enabled. On the server-side this - // typically will not be used. If you're not sure what to pass in here, - // chances are you don't need to use it. - // - // The |sample_rate_hz_|, |num_channels_|, and |samples_per_channel_| - // members of |frame| must be valid. |sample_rate_hz_| must correspond to - // |input_sample_rate_hz()| - // - // TODO(ajm): add const to input; requires an implementation fix. - // DEPRECATED: Use |ProcessReverseStream| instead. - // TODO(ekm): Remove once all users have updated to |ProcessReverseStream|. - virtual int AnalyzeReverseStream(AudioFrame* frame) = 0; - - // Same as |AnalyzeReverseStream|, but may modify |frame| if intelligibility - // is enabled. - virtual int ProcessReverseStream(AudioFrame* frame) = 0; - - // Accepts deinterleaved float audio with the range [-1, 1]. Each element - // of |data| points to a channel buffer, arranged according to |layout|. - // TODO(mgraczyk): Remove once clients are updated to use the new interface. - virtual int AnalyzeReverseStream(const float* const* data, - size_t samples_per_channel, - int rev_sample_rate_hz, - ChannelLayout layout) = 0; + // Accepts and produces a 10 ms frame of interleaved 16 bit integer audio for + // the reverse direction audio stream as specified in |input_config| and + // |output_config|. |src| and |dest| may use the same memory, if desired. + virtual int ProcessReverseStream(const int16_t* const src, + const StreamConfig& input_config, + const StreamConfig& output_config, + int16_t* const dest) = 0; // Accepts deinterleaved float audio with the range [-1, 1]. Each element of // |data| points to a channel buffer, arranged according to |reverse_config|. virtual int ProcessReverseStream(const float* const* src, - const StreamConfig& reverse_input_config, - const StreamConfig& reverse_output_config, + const StreamConfig& input_config, + const StreamConfig& output_config, float* const* dest) = 0; + // Accepts deinterleaved float audio with the range [-1, 1]. Each element + // of |data| points to a channel buffer, arranged according to + // |reverse_config|. + virtual int AnalyzeReverseStream(const float* const* data, + const StreamConfig& reverse_config) = 0; + + // Returns the most recently produced 10 ms of the linear AEC output at a rate + // of 16 kHz. If there is more than one capture channel, a mono representation + // of the input is returned. Returns true/false to indicate whether an output + // returned. + virtual bool GetLinearAecOutput( + rtc::ArrayView> linear_output) const = 0; + + // This must be called prior to ProcessStream() if and only if adaptive analog + // gain control is enabled, to pass the current analog level from the audio + // HAL. Must be within the range provided in Config::GainController1. + virtual void set_stream_analog_level(int level) = 0; + + // When an analog mode is set, this should be called after ProcessStream() + // to obtain the recommended new analog level for the audio HAL. It is the + // user's responsibility to apply this level. + virtual int recommended_stream_analog_level() const = 0; + // This must be called if and only if echo processing is enabled. // - // Sets the |delay| in ms between AnalyzeReverseStream() receiving a far-end + // Sets the |delay| in ms between ProcessReverseStream() receiving a far-end // frame and ProcessStream() receiving a near-end frame containing the // corresponding echo. On the client-side this can be expressed as // delay = (t_render - t_analyze) + (t_process - t_capture) // where, - // - t_analyze is the time a frame is passed to AnalyzeReverseStream() and + // - t_analyze is the time a frame is passed to ProcessReverseStream() and // t_render is the time the first sample of the same frame is rendered by // the audio hardware. // - t_capture is the time the first sample of a frame is captured by the - // audio hardware and t_pull is the time the same frame is passed to + // audio hardware and t_process is the time the same frame is passed to // ProcessStream(). virtual int set_stream_delay_ms(int delay) = 0; virtual int stream_delay_ms() const = 0; - virtual bool was_stream_delay_set() const = 0; // Call to signal that a key press occurred (true) or did not occur (false) // with this chunk of audio. virtual void set_stream_key_pressed(bool key_pressed) = 0; - // Sets a delay |offset| in ms to add to the values passed in through - // set_stream_delay_ms(). May be positive or negative. - // - // Note that this could cause an otherwise valid value passed to - // set_stream_delay_ms() to return an error. - virtual void set_delay_offset_ms(int offset) = 0; - virtual int delay_offset_ms() const = 0; + // Creates and attaches an webrtc::AecDump for recording debugging + // information. + // The |worker_queue| may not be null and must outlive the created + // AecDump instance. |max_log_size_bytes == -1| means the log size + // will be unlimited. |handle| may not be null. The AecDump takes + // responsibility for |handle| and closes it in the destructor. A + // return value of true indicates that the file has been + // sucessfully opened, while a value of false indicates that + // opening the file failed. + virtual bool CreateAndAttachAecDump(const std::string& file_name, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue) = 0; + virtual bool CreateAndAttachAecDump(FILE* handle, + int64_t max_log_size_bytes, + rtc::TaskQueue* worker_queue) = 0; - // Starts recording debugging information to a file specified by |filename|, - // a NULL-terminated string. If there is an ongoing recording, the old file - // will be closed, and recording will continue in the newly specified file. - // An already existing file will be overwritten without warning. - static const size_t kMaxFilenameSize = 1024; - virtual int StartDebugRecording(const char filename[kMaxFilenameSize]) = 0; + // TODO(webrtc:5298) Deprecated variant. + // Attaches provided webrtc::AecDump for recording debugging + // information. Log file and maximum file size logic is supposed to + // be handled by implementing instance of AecDump. Calling this + // method when another AecDump is attached resets the active AecDump + // with a new one. This causes the d-tor of the earlier AecDump to + // be called. The d-tor call may block until all pending logging + // tasks are completed. + virtual void AttachAecDump(std::unique_ptr aec_dump) = 0; - // Same as above but uses an existing file handle. Takes ownership - // of |handle| and closes it at StopDebugRecording(). - virtual int StartDebugRecording(FILE* handle) = 0; + // If no AecDump is attached, this has no effect. If an AecDump is + // attached, it's destructor is called. The d-tor may block until + // all pending logging tasks are completed. + virtual void DetachAecDump() = 0; - // Same as above but uses an existing PlatformFile handle. Takes ownership - // of |handle| and closes it at StopDebugRecording(). - // TODO(xians): Make this interface pure virtual. - virtual int StartDebugRecordingForPlatformFile(rtc::PlatformFile handle) { - return -1; - } + // Get audio processing statistics. + virtual AudioProcessingStats GetStatistics() = 0; + // TODO(webrtc:5298) Deprecated variant. The |has_remote_tracks| argument + // should be set if there are active remote tracks (this would usually be true + // during a call). If there are no remote tracks some of the stats will not be + // set by AudioProcessing, because they only make sense if there is at least + // one remote track. + virtual AudioProcessingStats GetStatistics(bool has_remote_tracks) = 0; - // Stops recording debugging information, and closes the file. Recording - // cannot be resumed in the same file (without overwriting it). - virtual int StopDebugRecording() = 0; - - // Use to send UMA histograms at end of a call. Note that all histogram - // specific member variables are reset. - virtual void UpdateHistogramsOnCallEnd() = 0; - - // These provide access to the component interfaces and should never return - // NULL. The pointers will be valid for the lifetime of the APM instance. - // The memory for these objects is entirely managed internally. - virtual EchoCancellation* echo_cancellation() const = 0; - virtual EchoControlMobile* echo_control_mobile() const = 0; - virtual GainControl* gain_control() const = 0; - virtual HighPassFilter* high_pass_filter() const = 0; - virtual LevelEstimator* level_estimator() const = 0; - virtual NoiseSuppression* noise_suppression() const = 0; - virtual VoiceDetection* voice_detection() const = 0; - - struct Statistic { - int instant; // Instantaneous value. - int average; // Long-term average. - int maximum; // Long-term maximum. - int minimum; // Long-term minimum. - }; + // Returns the last applied configuration. + virtual AudioProcessing::Config GetConfig() const = 0; enum Error { // Fatal errors. @@ -467,6 +685,7 @@ class AudioProcessing { kBadStreamParameterWarning = -13 }; + // Native rates supported by the integer interfaces. enum NativeRate { kSampleRate8kHz = 8000, kSampleRate16kHz = 16000, @@ -474,14 +693,67 @@ class AudioProcessing { kSampleRate48kHz = 48000 }; - static const int kNativeSampleRatesHz[]; - static const size_t kNumNativeSampleRates; - static const int kMaxNativeSampleRateHz; - static const int kMaxAECMSampleRateHz; + // TODO(kwiberg): We currently need to support a compiler (Visual C++) that + // complains if we don't explicitly state the size of the array here. Remove + // the size when that's no longer the case. + static constexpr int kNativeSampleRatesHz[4] = { + kSampleRate8kHz, kSampleRate16kHz, kSampleRate32kHz, kSampleRate48kHz}; + static constexpr size_t kNumNativeSampleRates = + arraysize(kNativeSampleRatesHz); + static constexpr int kMaxNativeSampleRateHz = + kNativeSampleRatesHz[kNumNativeSampleRates - 1]; static const int kChunkSizeMs = 10; }; +class RTC_EXPORT AudioProcessingBuilder { + public: + AudioProcessingBuilder(); + ~AudioProcessingBuilder(); + // The AudioProcessingBuilder takes ownership of the echo_control_factory. + AudioProcessingBuilder& SetEchoControlFactory( + std::unique_ptr echo_control_factory) { + echo_control_factory_ = std::move(echo_control_factory); + return *this; + } + // The AudioProcessingBuilder takes ownership of the capture_post_processing. + AudioProcessingBuilder& SetCapturePostProcessing( + std::unique_ptr capture_post_processing) { + capture_post_processing_ = std::move(capture_post_processing); + return *this; + } + // The AudioProcessingBuilder takes ownership of the render_pre_processing. + AudioProcessingBuilder& SetRenderPreProcessing( + std::unique_ptr render_pre_processing) { + render_pre_processing_ = std::move(render_pre_processing); + return *this; + } + // The AudioProcessingBuilder takes ownership of the echo_detector. + AudioProcessingBuilder& SetEchoDetector( + rtc::scoped_refptr echo_detector) { + echo_detector_ = std::move(echo_detector); + return *this; + } + // The AudioProcessingBuilder takes ownership of the capture_analyzer. + AudioProcessingBuilder& SetCaptureAnalyzer( + std::unique_ptr capture_analyzer) { + capture_analyzer_ = std::move(capture_analyzer); + return *this; + } + // This creates an APM instance using the previously set components. Calling + // the Create function resets the AudioProcessingBuilder to its initial state. + AudioProcessing* Create(); + AudioProcessing* Create(const webrtc::Config& config); + + private: + std::unique_ptr echo_control_factory_; + std::unique_ptr capture_post_processing_; + std::unique_ptr render_pre_processing_; + rtc::scoped_refptr echo_detector_; + std::unique_ptr capture_analyzer_; + RTC_DISALLOW_COPY_AND_ASSIGN(AudioProcessingBuilder); +}; + class StreamConfig { public: // sample_rate_hz: The sampling rate of the stream. @@ -497,7 +769,7 @@ class StreamConfig { // is true, the last channel in any corresponding list of // channels is the keyboard channel. StreamConfig(int sample_rate_hz = 0, - int num_channels = 0, + size_t num_channels = 0, bool has_keyboard = false) : sample_rate_hz_(sample_rate_hz), num_channels_(num_channels), @@ -508,14 +780,14 @@ class StreamConfig { sample_rate_hz_ = value; num_frames_ = calculate_frames(value); } - void set_num_channels(int value) { num_channels_ = value; } + void set_num_channels(size_t value) { num_channels_ = value; } void set_has_keyboard(bool value) { has_keyboard_ = value; } int sample_rate_hz() const { return sample_rate_hz_; } // The number of channels in the stream, not including the keyboard channel if // present. - int num_channels() const { return num_channels_; } + size_t num_channels() const { return num_channels_; } bool has_keyboard() const { return has_keyboard_; } size_t num_frames() const { return num_frames_; } @@ -531,12 +803,12 @@ class StreamConfig { private: static size_t calculate_frames(int sample_rate_hz) { - return static_cast( - AudioProcessing::kChunkSizeMs * sample_rate_hz / 1000); + return static_cast(AudioProcessing::kChunkSizeMs * sample_rate_hz / + 1000); } int sample_rate_hz_; - int num_channels_; + size_t num_channels_; bool has_keyboard_; size_t num_frames_; }; @@ -589,365 +861,64 @@ class ProcessingConfig { StreamConfig streams[StreamName::kNumStreamNames]; }; -// The acoustic echo cancellation (AEC) component provides better performance -// than AECM but also requires more processing power and is dependent on delay -// stability and reporting accuracy. As such it is well-suited and recommended -// for PC and IP phone applications. -// -// Not recommended to be enabled on the server-side. -class EchoCancellation { +// Experimental interface for a custom analysis submodule. +class CustomAudioAnalyzer { public: - // EchoCancellation and EchoControlMobile may not be enabled simultaneously. - // Enabling one will disable the other. - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; + // (Re-) Initializes the submodule. + virtual void Initialize(int sample_rate_hz, int num_channels) = 0; + // Analyzes the given capture or render signal. + virtual void Analyze(const AudioBuffer* audio) = 0; + // Returns a string representation of the module state. + virtual std::string ToString() const = 0; - // Differences in clock speed on the primary and reverse streams can impact - // the AEC performance. On the client-side, this could be seen when different - // render and capture devices are used, particularly with webcams. - // - // This enables a compensation mechanism, and requires that - // set_stream_drift_samples() be called. - virtual int enable_drift_compensation(bool enable) = 0; - virtual bool is_drift_compensation_enabled() const = 0; + virtual ~CustomAudioAnalyzer() {} +}; - // Sets the difference between the number of samples rendered and captured by - // the audio devices since the last call to |ProcessStream()|. Must be called - // if drift compensation is enabled, prior to |ProcessStream()|. - virtual void set_stream_drift_samples(int drift) = 0; - virtual int stream_drift_samples() const = 0; +// Interface for a custom processing submodule. +class CustomProcessing { + public: + // (Re-)Initializes the submodule. + virtual void Initialize(int sample_rate_hz, int num_channels) = 0; + // Processes the given capture or render signal. + virtual void Process(AudioBuffer* audio) = 0; + // Returns a string representation of the module state. + virtual std::string ToString() const = 0; + // Handles RuntimeSettings. TODO(webrtc:9262): make pure virtual + // after updating dependencies. + virtual void SetRuntimeSetting(AudioProcessing::RuntimeSetting setting); - enum SuppressionLevel { - kLowSuppression, - kModerateSuppression, - kHighSuppression - }; + virtual ~CustomProcessing() {} +}; - // Sets the aggressiveness of the suppressor. A higher level trades off - // double-talk performance for increased echo suppression. - virtual int set_suppression_level(SuppressionLevel level) = 0; - virtual SuppressionLevel suppression_level() const = 0; +// Interface for an echo detector submodule. +class EchoDetector : public rtc::RefCountInterface { + public: + // (Re-)Initializes the submodule. + virtual void Initialize(int capture_sample_rate_hz, + int num_capture_channels, + int render_sample_rate_hz, + int num_render_channels) = 0; - // Returns false if the current frame almost certainly contains no echo - // and true if it _might_ contain echo. - virtual bool stream_has_echo() const = 0; + // Analysis (not changing) of the render signal. + virtual void AnalyzeRenderAudio(rtc::ArrayView render_audio) = 0; - // Enables the computation of various echo metrics. These are obtained - // through |GetMetrics()|. - virtual int enable_metrics(bool enable) = 0; - virtual bool are_metrics_enabled() const = 0; + // Analysis (not changing) of the capture signal. + virtual void AnalyzeCaptureAudio( + rtc::ArrayView capture_audio) = 0; + + // Pack an AudioBuffer into a vector. + static void PackRenderAudioBuffer(AudioBuffer* audio, + std::vector* packed_buffer); - // Each statistic is reported in dB. - // P_far: Far-end (render) signal power. - // P_echo: Near-end (capture) echo signal power. - // P_out: Signal power at the output of the AEC. - // P_a: Internal signal power at the point before the AEC's non-linear - // processor. struct Metrics { - // RERL = ERL + ERLE - AudioProcessing::Statistic residual_echo_return_loss; - - // ERL = 10log_10(P_far / P_echo) - AudioProcessing::Statistic echo_return_loss; - - // ERLE = 10log_10(P_echo / P_out) - AudioProcessing::Statistic echo_return_loss_enhancement; - - // (Pre non-linear processing suppression) A_NLP = 10log_10(P_echo / P_a) - AudioProcessing::Statistic a_nlp; + absl::optional echo_likelihood; + absl::optional echo_likelihood_recent_max; }; - // TODO(ajm): discuss the metrics update period. - virtual int GetMetrics(Metrics* metrics) = 0; - - // Enables computation and logging of delay values. Statistics are obtained - // through |GetDelayMetrics()|. - virtual int enable_delay_logging(bool enable) = 0; - virtual bool is_delay_logging_enabled() const = 0; - - // The delay metrics consists of the delay |median| and the delay standard - // deviation |std|. It also consists of the fraction of delay estimates - // |fraction_poor_delays| that can make the echo cancellation perform poorly. - // The values are aggregated until the first call to |GetDelayMetrics()| and - // afterwards aggregated and updated every second. - // Note that if there are several clients pulling metrics from - // |GetDelayMetrics()| during a session the first call from any of them will - // change to one second aggregation window for all. - // TODO(bjornv): Deprecated, remove. - virtual int GetDelayMetrics(int* median, int* std) = 0; - virtual int GetDelayMetrics(int* median, int* std, - float* fraction_poor_delays) = 0; - - // Returns a pointer to the low level AEC component. In case of multiple - // channels, the pointer to the first one is returned. A NULL pointer is - // returned when the AEC component is disabled or has not been initialized - // successfully. - virtual struct AecCore* aec_core() const = 0; - - protected: - virtual ~EchoCancellation() {} + // Collect current metrics from the echo detector. + virtual Metrics GetMetrics() const = 0; }; -// The acoustic echo control for mobile (AECM) component is a low complexity -// robust option intended for use on mobile devices. -// -// Not recommended to be enabled on the server-side. -class EchoControlMobile { - public: - // EchoCancellation and EchoControlMobile may not be enabled simultaneously. - // Enabling one will disable the other. - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - // Recommended settings for particular audio routes. In general, the louder - // the echo is expected to be, the higher this value should be set. The - // preferred setting may vary from device to device. - enum RoutingMode { - kQuietEarpieceOrHeadset, - kEarpiece, - kLoudEarpiece, - kSpeakerphone, - kLoudSpeakerphone - }; - - // Sets echo control appropriate for the audio routing |mode| on the device. - // It can and should be updated during a call if the audio routing changes. - virtual int set_routing_mode(RoutingMode mode) = 0; - virtual RoutingMode routing_mode() const = 0; - - // Comfort noise replaces suppressed background noise to maintain a - // consistent signal level. - virtual int enable_comfort_noise(bool enable) = 0; - virtual bool is_comfort_noise_enabled() const = 0; - - // A typical use case is to initialize the component with an echo path from a - // previous call. The echo path is retrieved using |GetEchoPath()|, typically - // at the end of a call. The data can then be stored for later use as an - // initializer before the next call, using |SetEchoPath()|. - // - // Controlling the echo path this way requires the data |size_bytes| to match - // the internal echo path size. This size can be acquired using - // |echo_path_size_bytes()|. |SetEchoPath()| causes an entire reset, worth - // noting if it is to be called during an ongoing call. - // - // It is possible that version incompatibilities may result in a stored echo - // path of the incorrect size. In this case, the stored path should be - // discarded. - virtual int SetEchoPath(const void* echo_path, size_t size_bytes) = 0; - virtual int GetEchoPath(void* echo_path, size_t size_bytes) const = 0; - - // The returned path size is guaranteed not to change for the lifetime of - // the application. - static size_t echo_path_size_bytes(); - - protected: - virtual ~EchoControlMobile() {} -}; - -// The automatic gain control (AGC) component brings the signal to an -// appropriate range. This is done by applying a digital gain directly and, in -// the analog mode, prescribing an analog gain to be applied at the audio HAL. -// -// Recommended to be enabled on the client-side. -class GainControl { - public: - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - // When an analog mode is set, this must be called prior to |ProcessStream()| - // to pass the current analog level from the audio HAL. Must be within the - // range provided to |set_analog_level_limits()|. - virtual int set_stream_analog_level(int level) = 0; - - // When an analog mode is set, this should be called after |ProcessStream()| - // to obtain the recommended new analog level for the audio HAL. It is the - // users responsibility to apply this level. - virtual int stream_analog_level() = 0; - - enum Mode { - // Adaptive mode intended for use if an analog volume control is available - // on the capture device. It will require the user to provide coupling - // between the OS mixer controls and AGC through the |stream_analog_level()| - // functions. - // - // It consists of an analog gain prescription for the audio device and a - // digital compression stage. - kAdaptiveAnalog, - - // Adaptive mode intended for situations in which an analog volume control - // is unavailable. It operates in a similar fashion to the adaptive analog - // mode, but with scaling instead applied in the digital domain. As with - // the analog mode, it additionally uses a digital compression stage. - kAdaptiveDigital, - - // Fixed mode which enables only the digital compression stage also used by - // the two adaptive modes. - // - // It is distinguished from the adaptive modes by considering only a - // short time-window of the input signal. It applies a fixed gain through - // most of the input level range, and compresses (gradually reduces gain - // with increasing level) the input signal at higher levels. This mode is - // preferred on embedded devices where the capture signal level is - // predictable, so that a known gain can be applied. - kFixedDigital - }; - - virtual int set_mode(Mode mode) = 0; - virtual Mode mode() const = 0; - - // Sets the target peak |level| (or envelope) of the AGC in dBFs (decibels - // from digital full-scale). The convention is to use positive values. For - // instance, passing in a value of 3 corresponds to -3 dBFs, or a target - // level 3 dB below full-scale. Limited to [0, 31]. - // - // TODO(ajm): use a negative value here instead, if/when VoE will similarly - // update its interface. - virtual int set_target_level_dbfs(int level) = 0; - virtual int target_level_dbfs() const = 0; - - // Sets the maximum |gain| the digital compression stage may apply, in dB. A - // higher number corresponds to greater compression, while a value of 0 will - // leave the signal uncompressed. Limited to [0, 90]. - virtual int set_compression_gain_db(int gain) = 0; - virtual int compression_gain_db() const = 0; - - // When enabled, the compression stage will hard limit the signal to the - // target level. Otherwise, the signal will be compressed but not limited - // above the target level. - virtual int enable_limiter(bool enable) = 0; - virtual bool is_limiter_enabled() const = 0; - - // Sets the |minimum| and |maximum| analog levels of the audio capture device. - // Must be set if and only if an analog mode is used. Limited to [0, 65535]. - virtual int set_analog_level_limits(int minimum, - int maximum) = 0; - virtual int analog_level_minimum() const = 0; - virtual int analog_level_maximum() const = 0; - - // Returns true if the AGC has detected a saturation event (period where the - // signal reaches digital full-scale) in the current frame and the analog - // level cannot be reduced. - // - // This could be used as an indicator to reduce or disable analog mic gain at - // the audio HAL. - virtual bool stream_is_saturated() const = 0; - - protected: - virtual ~GainControl() {} -}; - -// A filtering component which removes DC offset and low-frequency noise. -// Recommended to be enabled on the client-side. -class HighPassFilter { - public: - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - protected: - virtual ~HighPassFilter() {} -}; - -// An estimation component used to retrieve level metrics. -class LevelEstimator { - public: - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - // Returns the root mean square (RMS) level in dBFs (decibels from digital - // full-scale), or alternately dBov. It is computed over all primary stream - // frames since the last call to RMS(). The returned value is positive but - // should be interpreted as negative. It is constrained to [0, 127]. - // - // The computation follows: https://tools.ietf.org/html/rfc6465 - // with the intent that it can provide the RTP audio level indication. - // - // Frames passed to ProcessStream() with an |_energy| of zero are considered - // to have been muted. The RMS of the frame will be interpreted as -127. - virtual int RMS() = 0; - - protected: - virtual ~LevelEstimator() {} -}; - -// The noise suppression (NS) component attempts to remove noise while -// retaining speech. Recommended to be enabled on the client-side. -// -// Recommended to be enabled on the client-side. -class NoiseSuppression { - public: - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - // Determines the aggressiveness of the suppression. Increasing the level - // will reduce the noise level at the expense of a higher speech distortion. - enum Level { - kLow, - kModerate, - kHigh, - kVeryHigh - }; - - virtual int set_level(Level level) = 0; - virtual Level level() const = 0; - - // Returns the internally computed prior speech probability of current frame - // averaged over output channels. This is not supported in fixed point, for - // which |kUnsupportedFunctionError| is returned. - virtual float speech_probability() const = 0; - - protected: - virtual ~NoiseSuppression() {} -}; - -// The voice activity detection (VAD) component analyzes the stream to -// determine if voice is present. A facility is also provided to pass in an -// external VAD decision. -// -// In addition to |stream_has_voice()| the VAD decision is provided through the -// |AudioFrame| passed to |ProcessStream()|. The |vad_activity_| member will be -// modified to reflect the current decision. -class VoiceDetection { - public: - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - // Returns true if voice is detected in the current frame. Should be called - // after |ProcessStream()|. - virtual bool stream_has_voice() const = 0; - - // Some of the APM functionality requires a VAD decision. In the case that - // a decision is externally available for the current frame, it can be passed - // in here, before |ProcessStream()| is called. - // - // VoiceDetection does _not_ need to be enabled to use this. If it happens to - // be enabled, detection will be skipped for any frame in which an external - // VAD decision is provided. - virtual int set_stream_has_voice(bool has_voice) = 0; - - // Specifies the likelihood that a frame will be declared to contain voice. - // A higher value makes it more likely that speech will not be clipped, at - // the expense of more noise being detected as voice. - enum Likelihood { - kVeryLowLikelihood, - kLowLikelihood, - kModerateLikelihood, - kHighLikelihood - }; - - virtual int set_likelihood(Likelihood likelihood) = 0; - virtual Likelihood likelihood() const = 0; - - // Sets the |size| of the frames in ms on which the VAD will operate. Larger - // frames will improve detection accuracy, but reduce the frequency of - // updates. - // - // This does not impact the size of frames passed to |ProcessStream()|. - virtual int set_frame_size_ms(int size) = 0; - virtual int frame_size_ms() const = 0; - - protected: - virtual ~VoiceDetection() {} -}; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_H_ diff --git a/webrtc/modules/audio_processing/include/audio_processing_statistics.cc b/webrtc/modules/audio_processing/include/audio_processing_statistics.cc new file mode 100644 index 0000000..7139ee5 --- /dev/null +++ b/webrtc/modules/audio_processing/include/audio_processing_statistics.cc @@ -0,0 +1,22 @@ +/* + * Copyright 2017 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 "modules/audio_processing/include/audio_processing_statistics.h" + +namespace webrtc { + +AudioProcessingStats::AudioProcessingStats() = default; + +AudioProcessingStats::AudioProcessingStats(const AudioProcessingStats& other) = + default; + +AudioProcessingStats::~AudioProcessingStats() = default; + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/include/audio_processing_statistics.h b/webrtc/modules/audio_processing/include/audio_processing_statistics.h new file mode 100644 index 0000000..87babee --- /dev/null +++ b/webrtc/modules/audio_processing/include/audio_processing_statistics.h @@ -0,0 +1,73 @@ +/* + * Copyright 2017 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_STATISTICS_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_STATISTICS_H_ + +#include + +#include "absl/types/optional.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { +// This version of the stats uses Optionals, it will replace the regular +// AudioProcessingStatistics struct. +struct RTC_EXPORT AudioProcessingStats { + AudioProcessingStats(); + AudioProcessingStats(const AudioProcessingStats& other); + ~AudioProcessingStats(); + + // The root mean square (RMS) level in dBFS (decibels from digital + // full-scale) of the last capture frame, after processing. It is + // constrained to [-127, 0]. + // The computation follows: https://tools.ietf.org/html/rfc6465 + // with the intent that it can provide the RTP audio level indication. + // Only reported if level estimation is enabled in AudioProcessing::Config. + absl::optional output_rms_dbfs; + + // True if voice is detected in the last capture frame, after processing. + // It is conservative in flagging audio as speech, with low likelihood of + // incorrectly flagging a frame as voice. + // Only reported if voice detection is enabled in AudioProcessing::Config. + absl::optional voice_detected; + + // AEC Statistics. + // ERL = 10log_10(P_far / P_echo) + absl::optional echo_return_loss; + // ERLE = 10log_10(P_echo / P_out) + absl::optional echo_return_loss_enhancement; + // Fraction of time that the AEC linear filter is divergent, in a 1-second + // non-overlapped aggregation window. + absl::optional divergent_filter_fraction; + + // The delay metrics consists of the delay median and standard deviation. It + // also consists of the fraction of delay estimates that can make the echo + // cancellation perform poorly. The values are aggregated until the first + // call to |GetStatistics()| and afterwards aggregated and updated every + // second. Note that if there are several clients pulling metrics from + // |GetStatistics()| during a session the first call from any of them will + // change to one second aggregation window for all. + absl::optional delay_median_ms; + absl::optional delay_standard_deviation_ms; + + // Residual echo detector likelihood. + absl::optional residual_echo_likelihood; + // Maximum residual echo likelihood from the last time period. + absl::optional residual_echo_likelihood_recent_max; + + // The instantaneous delay estimate produced in the AEC. The unit is in + // milliseconds and the value is the instantaneous value at the time of the + // call to |GetStatistics()|. + absl::optional delay_ms; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_PROCESSING_STATISTICS_H_ diff --git a/webrtc/modules/audio_processing/include/config.cc b/webrtc/modules/audio_processing/include/config.cc new file mode 100644 index 0000000..14240db --- /dev/null +++ b/webrtc/modules/audio_processing/include/config.cc @@ -0,0 +1,23 @@ +/* + * 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 "modules/audio_processing/include/config.h" + +namespace webrtc { + +Config::Config() {} + +Config::~Config() { + for (OptionMap::iterator it = options_.begin(); it != options_.end(); ++it) { + delete it->second; + } +} + +} // namespace webrtc diff --git a/webrtc/common.h b/webrtc/modules/audio_processing/include/config.h similarity index 61% rename from webrtc/common.h rename to webrtc/modules/audio_processing/include/config.h index dda045e..7fab178 100644 --- a/webrtc/common.h +++ b/webrtc/modules/audio_processing/include/config.h @@ -8,15 +8,35 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_COMMON_H_ -#define WEBRTC_COMMON_H_ +#ifndef MODULES_AUDIO_PROCESSING_INCLUDE_CONFIG_H_ +#define MODULES_AUDIO_PROCESSING_INCLUDE_CONFIG_H_ #include -#include "webrtc/base/basictypes.h" +#include "rtc_base/system/rtc_export.h" namespace webrtc { +// Only add new values to the end of the enumeration and never remove (only +// deprecate) to maintain binary compatibility. +enum class ConfigOptionID { + kMyExperimentForTest, + kAlgo1CostFunctionForTest, + kTemporalLayersFactory, // Deprecated + kNetEqCapacityConfig, // Deprecated + kNetEqFastAccelerate, // Deprecated + kVoicePacing, // Deprecated + kExtendedFilter, // Deprecated + kDelayAgnostic, // Deprecated + kExperimentalAgc, + kExperimentalNs, + kBeamforming, // Deprecated + kIntelligibility, // Deprecated + kEchoCanceller3, // Deprecated + kAecRefinedAdaptiveFilter, // Deprecated + kLevelControl // Deprecated +}; + // Class Config is designed to ease passing a set of options across webrtc code. // Options are identified by typename in order to avoid incorrect casts. // @@ -37,70 +57,58 @@ namespace webrtc { // config.Set(new SqrCost()); // // Note: This class is thread-compatible (like STL containers). -class Config { +class RTC_EXPORT Config { public: // Returns the option if set or a default constructed one. // Callers that access options too often are encouraged to cache the result. // Returned references are owned by this. // // Requires std::is_default_constructible - template const T& Get() const; + template + const T& Get() const; // Set the option, deleting any previous instance of the same. // This instance gets ownership of the newly set value. - template void Set(T* value); + template + void Set(T* value); - Config() {} - ~Config() { - // Note: this method is inline so webrtc public API depends only - // on the headers. - for (OptionMap::iterator it = options_.begin(); - it != options_.end(); ++it) { - delete it->second; - } - } + Config(); + ~Config(); private: - typedef void* OptionIdentifier; - struct BaseOption { virtual ~BaseOption() {} }; - template + template struct Option : BaseOption { - explicit Option(T* v): value(v) {} - ~Option() { - delete value; - } + explicit Option(T* v) : value(v) {} + ~Option() { delete value; } T* value; }; - // Own implementation of rtti-subset to avoid depending on rtti and its costs. - template - static OptionIdentifier identifier() { - static char id_placeholder; - return &id_placeholder; + template + static ConfigOptionID identifier() { + return T::identifier; } // Used to instantiate a default constructed object that doesn't needs to be // owned. This allows Get to be implemented without requiring explicitly // locks. - template + template static const T& default_value() { - RTC_DEFINE_STATIC_LOCAL(const T, def, ()); - return def; + static const T* const def = new T(); + return *def; } - typedef std::map OptionMap; + typedef std::map OptionMap; OptionMap options_; - // RTC_DISALLOW_COPY_AND_ASSIGN Config(const Config&); void operator=(const Config&); }; -template +template const T& Config::Get() const { OptionMap::const_iterator it = options_.find(identifier()); if (it != options_.end()) { @@ -112,13 +120,12 @@ const T& Config::Get() const { return default_value(); } -template +template void Config::Set(T* value) { BaseOption*& it = options_[identifier()]; delete it; it = new Option(value); } - } // namespace webrtc -#endif // WEBRTC_COMMON_H_ +#endif // MODULES_AUDIO_PROCESSING_INCLUDE_CONFIG_H_ diff --git a/webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.cc b/webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.cc deleted file mode 100644 index d014ce0..0000000 --- a/webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.cc +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -// -// Implements core class for intelligibility enhancer. -// -// Details of the model and algorithm can be found in the original paper: -// http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=6882788 -// - -#include "webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.h" - -#include -#include -#include -#include - -#include "webrtc/base/checks.h" -#include "webrtc/common_audio/include/audio_util.h" -#include "webrtc/common_audio/window_generator.h" - -namespace webrtc { - -namespace { - -const size_t kErbResolution = 2; -const int kWindowSizeMs = 2; -const int kChunkSizeMs = 10; // Size provided by APM. -const float kClipFreq = 200.0f; -const float kConfigRho = 0.02f; // Default production and interpretation SNR. -const float kKbdAlpha = 1.5f; -const float kLambdaBot = -1.0f; // Extreme values in bisection -const float kLambdaTop = -10e-18f; // search for lamda. - -} // namespace - -using std::complex; -using std::max; -using std::min; -using VarianceType = intelligibility::VarianceArray::StepType; - -IntelligibilityEnhancer::TransformCallback::TransformCallback( - IntelligibilityEnhancer* parent, - IntelligibilityEnhancer::AudioSource source) - : parent_(parent), source_(source) { -} - -void IntelligibilityEnhancer::TransformCallback::ProcessAudioBlock( - const complex* const* in_block, - int in_channels, - size_t frames, - int /* out_channels */, - complex* const* out_block) { - RTC_DCHECK_EQ(parent_->freqs_, frames); - for (int i = 0; i < in_channels; ++i) { - parent_->DispatchAudio(source_, in_block[i], out_block[i]); - } -} - -IntelligibilityEnhancer::IntelligibilityEnhancer() - : IntelligibilityEnhancer(IntelligibilityEnhancer::Config()) { -} - -IntelligibilityEnhancer::IntelligibilityEnhancer(const Config& config) - : freqs_(RealFourier::ComplexLength( - RealFourier::FftOrder(config.sample_rate_hz * kWindowSizeMs / 1000))), - window_size_(static_cast(1 << RealFourier::FftOrder(freqs_))), - chunk_length_( - static_cast(config.sample_rate_hz * kChunkSizeMs / 1000)), - bank_size_(GetBankSize(config.sample_rate_hz, kErbResolution)), - sample_rate_hz_(config.sample_rate_hz), - erb_resolution_(kErbResolution), - num_capture_channels_(config.num_capture_channels), - num_render_channels_(config.num_render_channels), - analysis_rate_(config.analysis_rate), - active_(true), - clear_variance_(freqs_, - config.var_type, - config.var_window_size, - config.var_decay_rate), - noise_variance_(freqs_, - config.var_type, - config.var_window_size, - config.var_decay_rate), - filtered_clear_var_(new float[bank_size_]), - filtered_noise_var_(new float[bank_size_]), - filter_bank_(bank_size_), - center_freqs_(new float[bank_size_]), - rho_(new float[bank_size_]), - gains_eq_(new float[bank_size_]), - gain_applier_(freqs_, config.gain_change_limit), - temp_render_out_buffer_(chunk_length_, num_render_channels_), - temp_capture_out_buffer_(chunk_length_, num_capture_channels_), - kbd_window_(new float[window_size_]), - render_callback_(this, AudioSource::kRenderStream), - capture_callback_(this, AudioSource::kCaptureStream), - block_count_(0), - analysis_step_(0) { - RTC_DCHECK_LE(config.rho, 1.0f); - - CreateErbBank(); - - // Assumes all rho equal. - for (size_t i = 0; i < bank_size_; ++i) { - rho_[i] = config.rho * config.rho; - } - - float freqs_khz = kClipFreq / 1000.0f; - size_t erb_index = static_cast(ceilf( - 11.17f * logf((freqs_khz + 0.312f) / (freqs_khz + 14.6575f)) + 43.0f)); - start_freq_ = std::max(static_cast(1), erb_index * erb_resolution_); - - WindowGenerator::KaiserBesselDerived(kKbdAlpha, window_size_, - kbd_window_.get()); - render_mangler_.reset(new LappedTransform( - num_render_channels_, num_render_channels_, chunk_length_, - kbd_window_.get(), window_size_, window_size_ / 2, &render_callback_)); - capture_mangler_.reset(new LappedTransform( - num_capture_channels_, num_capture_channels_, chunk_length_, - kbd_window_.get(), window_size_, window_size_ / 2, &capture_callback_)); -} - -void IntelligibilityEnhancer::ProcessRenderAudio(float* const* audio, - int sample_rate_hz, - int num_channels) { - RTC_CHECK_EQ(sample_rate_hz_, sample_rate_hz); - RTC_CHECK_EQ(num_render_channels_, num_channels); - - if (active_) { - render_mangler_->ProcessChunk(audio, temp_render_out_buffer_.channels()); - } - - if (active_) { - for (int i = 0; i < num_render_channels_; ++i) { - memcpy(audio[i], temp_render_out_buffer_.channels()[i], - chunk_length_ * sizeof(**audio)); - } - } -} - -void IntelligibilityEnhancer::AnalyzeCaptureAudio(float* const* audio, - int sample_rate_hz, - int num_channels) { - RTC_CHECK_EQ(sample_rate_hz_, sample_rate_hz); - RTC_CHECK_EQ(num_capture_channels_, num_channels); - - capture_mangler_->ProcessChunk(audio, temp_capture_out_buffer_.channels()); -} - -void IntelligibilityEnhancer::DispatchAudio( - IntelligibilityEnhancer::AudioSource source, - const complex* in_block, - complex* out_block) { - switch (source) { - case kRenderStream: - ProcessClearBlock(in_block, out_block); - break; - case kCaptureStream: - ProcessNoiseBlock(in_block, out_block); - break; - } -} - -void IntelligibilityEnhancer::ProcessClearBlock(const complex* in_block, - complex* out_block) { - if (block_count_ < 2) { - memset(out_block, 0, freqs_ * sizeof(*out_block)); - ++block_count_; - return; - } - - // TODO(ekm): Use VAD to |Step| and |AnalyzeClearBlock| only if necessary. - if (true) { - clear_variance_.Step(in_block, false); - if (block_count_ % analysis_rate_ == analysis_rate_ - 1) { - const float power_target = std::accumulate( - clear_variance_.variance(), clear_variance_.variance() + freqs_, 0.f); - AnalyzeClearBlock(power_target); - ++analysis_step_; - } - ++block_count_; - } - - if (active_) { - gain_applier_.Apply(in_block, out_block); - } -} - -void IntelligibilityEnhancer::AnalyzeClearBlock(float power_target) { - FilterVariance(clear_variance_.variance(), filtered_clear_var_.get()); - FilterVariance(noise_variance_.variance(), filtered_noise_var_.get()); - - SolveForGainsGivenLambda(kLambdaTop, start_freq_, gains_eq_.get()); - const float power_top = - DotProduct(gains_eq_.get(), filtered_clear_var_.get(), bank_size_); - SolveForGainsGivenLambda(kLambdaBot, start_freq_, gains_eq_.get()); - const float power_bot = - DotProduct(gains_eq_.get(), filtered_clear_var_.get(), bank_size_); - if (power_target >= power_bot && power_target <= power_top) { - SolveForLambda(power_target, power_bot, power_top); - UpdateErbGains(); - } // Else experiencing variance underflow, so do nothing. -} - -void IntelligibilityEnhancer::SolveForLambda(float power_target, - float power_bot, - float power_top) { - const float kConvergeThresh = 0.001f; // TODO(ekmeyerson): Find best values - const int kMaxIters = 100; // for these, based on experiments. - - const float reciprocal_power_target = 1.f / power_target; - float lambda_bot = kLambdaBot; - float lambda_top = kLambdaTop; - float power_ratio = 2.0f; // Ratio of achieved power to target power. - int iters = 0; - while (std::fabs(power_ratio - 1.0f) > kConvergeThresh && - iters <= kMaxIters) { - const float lambda = lambda_bot + (lambda_top - lambda_bot) / 2.0f; - SolveForGainsGivenLambda(lambda, start_freq_, gains_eq_.get()); - const float power = - DotProduct(gains_eq_.get(), filtered_clear_var_.get(), bank_size_); - if (power < power_target) { - lambda_bot = lambda; - } else { - lambda_top = lambda; - } - power_ratio = std::fabs(power * reciprocal_power_target); - ++iters; - } -} - -void IntelligibilityEnhancer::UpdateErbGains() { - // (ERB gain) = filterbank' * (freq gain) - float* gains = gain_applier_.target(); - for (size_t i = 0; i < freqs_; ++i) { - gains[i] = 0.0f; - for (size_t j = 0; j < bank_size_; ++j) { - gains[i] = fmaf(filter_bank_[j][i], gains_eq_[j], gains[i]); - } - } -} - -void IntelligibilityEnhancer::ProcessNoiseBlock(const complex* in_block, - complex* /*out_block*/) { - noise_variance_.Step(in_block); -} - -size_t IntelligibilityEnhancer::GetBankSize(int sample_rate, - size_t erb_resolution) { - float freq_limit = sample_rate / 2000.0f; - size_t erb_scale = static_cast(ceilf( - 11.17f * logf((freq_limit + 0.312f) / (freq_limit + 14.6575f)) + 43.0f)); - return erb_scale * erb_resolution; -} - -void IntelligibilityEnhancer::CreateErbBank() { - size_t lf = 1, rf = 4; - - for (size_t i = 0; i < bank_size_; ++i) { - float abs_temp = fabsf((i + 1.0f) / static_cast(erb_resolution_)); - center_freqs_[i] = 676170.4f / (47.06538f - expf(0.08950404f * abs_temp)); - center_freqs_[i] -= 14678.49f; - } - float last_center_freq = center_freqs_[bank_size_ - 1]; - for (size_t i = 0; i < bank_size_; ++i) { - center_freqs_[i] *= 0.5f * sample_rate_hz_ / last_center_freq; - } - - for (size_t i = 0; i < bank_size_; ++i) { - filter_bank_[i].resize(freqs_); - } - - for (size_t i = 1; i <= bank_size_; ++i) { - size_t lll, ll, rr, rrr; - static const size_t kOne = 1; // Avoids repeated static_cast<>s below. - lll = static_cast(round( - center_freqs_[max(kOne, i - lf) - 1] * freqs_ / - (0.5f * sample_rate_hz_))); - ll = static_cast(round( - center_freqs_[max(kOne, i) - 1] * freqs_ / (0.5f * sample_rate_hz_))); - lll = min(freqs_, max(lll, kOne)) - 1; - ll = min(freqs_, max(ll, kOne)) - 1; - - rrr = static_cast(round( - center_freqs_[min(bank_size_, i + rf) - 1] * freqs_ / - (0.5f * sample_rate_hz_))); - rr = static_cast(round( - center_freqs_[min(bank_size_, i + 1) - 1] * freqs_ / - (0.5f * sample_rate_hz_))); - rrr = min(freqs_, max(rrr, kOne)) - 1; - rr = min(freqs_, max(rr, kOne)) - 1; - - float step, element; - - step = 1.0f / (ll - lll); - element = 0.0f; - for (size_t j = lll; j <= ll; ++j) { - filter_bank_[i - 1][j] = element; - element += step; - } - step = 1.0f / (rrr - rr); - element = 1.0f; - for (size_t j = rr; j <= rrr; ++j) { - filter_bank_[i - 1][j] = element; - element -= step; - } - for (size_t j = ll; j <= rr; ++j) { - filter_bank_[i - 1][j] = 1.0f; - } - } - - float sum; - for (size_t i = 0; i < freqs_; ++i) { - sum = 0.0f; - for (size_t j = 0; j < bank_size_; ++j) { - sum += filter_bank_[j][i]; - } - for (size_t j = 0; j < bank_size_; ++j) { - filter_bank_[j][i] /= sum; - } - } -} - -void IntelligibilityEnhancer::SolveForGainsGivenLambda(float lambda, - size_t start_freq, - float* sols) { - bool quadratic = (kConfigRho < 1.0f); - const float* var_x0 = filtered_clear_var_.get(); - const float* var_n0 = filtered_noise_var_.get(); - - for (size_t n = 0; n < start_freq; ++n) { - sols[n] = 1.0f; - } - - // Analytic solution for optimal gains. See paper for derivation. - for (size_t n = start_freq - 1; n < bank_size_; ++n) { - float alpha0, beta0, gamma0; - gamma0 = 0.5f * rho_[n] * var_x0[n] * var_n0[n] + - lambda * var_x0[n] * var_n0[n] * var_n0[n]; - beta0 = lambda * var_x0[n] * (2 - rho_[n]) * var_x0[n] * var_n0[n]; - if (quadratic) { - alpha0 = lambda * var_x0[n] * (1 - rho_[n]) * var_x0[n] * var_x0[n]; - sols[n] = - (-beta0 - sqrtf(beta0 * beta0 - 4 * alpha0 * gamma0)) / (2 * alpha0); - } else { - sols[n] = -gamma0 / beta0; - } - sols[n] = fmax(0, sols[n]); - } -} - -void IntelligibilityEnhancer::FilterVariance(const float* var, float* result) { - RTC_DCHECK_GT(freqs_, 0u); - for (size_t i = 0; i < bank_size_; ++i) { - result[i] = DotProduct(&filter_bank_[i][0], var, freqs_); - } -} - -float IntelligibilityEnhancer::DotProduct(const float* a, - const float* b, - size_t length) { - float ret = 0.0f; - - for (size_t i = 0; i < length; ++i) { - ret = fmaf(a[i], b[i], ret); - } - return ret; -} - -bool IntelligibilityEnhancer::active() const { - return active_; -} - -} // namespace webrtc diff --git a/webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.h b/webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.h deleted file mode 100644 index 0215426..0000000 --- a/webrtc/modules/audio_processing/intelligibility/intelligibility_enhancer.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -// -// Specifies core class for intelligbility enhancement. -// - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_INTELLIGIBILITY_INTELLIGIBILITY_ENHANCER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_INTELLIGIBILITY_INTELLIGIBILITY_ENHANCER_H_ - -#include -#include - -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/lapped_transform.h" -#include "webrtc/common_audio/channel_buffer.h" -#include "webrtc/modules/audio_processing/intelligibility/intelligibility_utils.h" - -namespace webrtc { - -// Speech intelligibility enhancement module. Reads render and capture -// audio streams and modifies the render stream with a set of gains per -// frequency bin to enhance speech against the noise background. -// Note: assumes speech and noise streams are already separated. -class IntelligibilityEnhancer { - public: - struct Config { - // |var_*| are parameters for the VarianceArray constructor for the - // clear speech stream. - // TODO(bercic): the |var_*|, |*_rate| and |gain_limit| parameters should - // probably go away once fine tuning is done. - Config() - : sample_rate_hz(16000), - num_capture_channels(1), - num_render_channels(1), - var_type(intelligibility::VarianceArray::kStepDecaying), - var_decay_rate(0.9f), - var_window_size(10), - analysis_rate(800), - gain_change_limit(0.1f), - rho(0.02f) {} - int sample_rate_hz; - int num_capture_channels; - int num_render_channels; - intelligibility::VarianceArray::StepType var_type; - float var_decay_rate; - size_t var_window_size; - int analysis_rate; - float gain_change_limit; - float rho; - }; - - explicit IntelligibilityEnhancer(const Config& config); - IntelligibilityEnhancer(); // Initialize with default config. - - // Reads and processes chunk of noise stream in time domain. - void AnalyzeCaptureAudio(float* const* audio, - int sample_rate_hz, - int num_channels); - - // Reads chunk of speech in time domain and updates with modified signal. - void ProcessRenderAudio(float* const* audio, - int sample_rate_hz, - int num_channels); - bool active() const; - - private: - enum AudioSource { - kRenderStream = 0, // Clear speech stream. - kCaptureStream, // Noise stream. - }; - - // Provides access point to the frequency domain. - class TransformCallback : public LappedTransform::Callback { - public: - TransformCallback(IntelligibilityEnhancer* parent, AudioSource source); - - // All in frequency domain, receives input |in_block|, applies - // intelligibility enhancement, and writes result to |out_block|. - void ProcessAudioBlock(const std::complex* const* in_block, - int in_channels, - size_t frames, - int out_channels, - std::complex* const* out_block) override; - - private: - IntelligibilityEnhancer* parent_; - AudioSource source_; - }; - friend class TransformCallback; -#ifndef WEBRTC_AUDIO_PROCESSING_ONLY_BUILD - FRIEND_TEST_ALL_PREFIXES(IntelligibilityEnhancerTest, TestErbCreation); - FRIEND_TEST_ALL_PREFIXES(IntelligibilityEnhancerTest, TestSolveForGains); -#endif - - // Sends streams to ProcessClearBlock or ProcessNoiseBlock based on source. - void DispatchAudio(AudioSource source, - const std::complex* in_block, - std::complex* out_block); - - // Updates variance computation and analysis with |in_block_|, - // and writes modified speech to |out_block|. - void ProcessClearBlock(const std::complex* in_block, - std::complex* out_block); - - // Computes and sets modified gains. - void AnalyzeClearBlock(float power_target); - - // Bisection search for optimal |lambda|. - void SolveForLambda(float power_target, float power_bot, float power_top); - - // Transforms freq gains to ERB gains. - void UpdateErbGains(); - - // Updates variance calculation for noise input with |in_block|. - void ProcessNoiseBlock(const std::complex* in_block, - std::complex* out_block); - - // Returns number of ERB filters. - static size_t GetBankSize(int sample_rate, size_t erb_resolution); - - // Initializes ERB filterbank. - void CreateErbBank(); - - // Analytically solves quadratic for optimal gains given |lambda|. - // Negative gains are set to 0. Stores the results in |sols|. - void SolveForGainsGivenLambda(float lambda, size_t start_freq, float* sols); - - // Computes variance across ERB filters from freq variance |var|. - // Stores in |result|. - void FilterVariance(const float* var, float* result); - - // Returns dot product of vectors specified by size |length| arrays |a|,|b|. - static float DotProduct(const float* a, const float* b, size_t length); - - const size_t freqs_; // Num frequencies in frequency domain. - const size_t window_size_; // Window size in samples; also the block size. - const size_t chunk_length_; // Chunk size in samples. - const size_t bank_size_; // Num ERB filters. - const int sample_rate_hz_; - const int erb_resolution_; - const int num_capture_channels_; - const int num_render_channels_; - const int analysis_rate_; // Num blocks before gains recalculated. - - const bool active_; // Whether render gains are being updated. - // TODO(ekm): Add logic for updating |active_|. - - intelligibility::VarianceArray clear_variance_; - intelligibility::VarianceArray noise_variance_; - rtc::scoped_ptr filtered_clear_var_; - rtc::scoped_ptr filtered_noise_var_; - std::vector> filter_bank_; - rtc::scoped_ptr center_freqs_; - size_t start_freq_; - rtc::scoped_ptr rho_; // Production and interpretation SNR. - // for each ERB band. - rtc::scoped_ptr gains_eq_; // Pre-filter modified gains. - intelligibility::GainApplier gain_applier_; - - // Destination buffers used to reassemble blocked chunks before overwriting - // the original input array with modifications. - ChannelBuffer temp_render_out_buffer_; - ChannelBuffer temp_capture_out_buffer_; - - rtc::scoped_ptr kbd_window_; - TransformCallback render_callback_; - TransformCallback capture_callback_; - rtc::scoped_ptr render_mangler_; - rtc::scoped_ptr capture_mangler_; - int block_count_; - int analysis_step_; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_INTELLIGIBILITY_INTELLIGIBILITY_ENHANCER_H_ diff --git a/webrtc/modules/audio_processing/intelligibility/intelligibility_utils.cc b/webrtc/modules/audio_processing/intelligibility/intelligibility_utils.cc deleted file mode 100644 index 7da9b95..0000000 --- a/webrtc/modules/audio_processing/intelligibility/intelligibility_utils.cc +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -// -// Implements helper functions and classes for intelligibility enhancement. -// - -#include "webrtc/modules/audio_processing/intelligibility/intelligibility_utils.h" - -#include -#include -#include -#include - -using std::complex; -using std::min; - -namespace webrtc { - -namespace intelligibility { - -float UpdateFactor(float target, float current, float limit) { - float delta = fabsf(target - current); - float sign = copysign(1.0f, target - current); - return current + sign * fminf(delta, limit); -} - -float AddDitherIfZero(float value) { - return value == 0.f ? std::rand() * 0.01f / RAND_MAX : value; -} - -complex zerofudge(complex c) { - return complex(AddDitherIfZero(c.real()), AddDitherIfZero(c.imag())); -} - -complex NewMean(complex mean, complex data, size_t count) { - return mean + (data - mean) / static_cast(count); -} - -void AddToMean(complex data, size_t count, complex* mean) { - (*mean) = NewMean(*mean, data, count); -} - - -static const size_t kWindowBlockSize = 10; - -VarianceArray::VarianceArray(size_t num_freqs, - StepType type, - size_t window_size, - float decay) - : running_mean_(new complex[num_freqs]()), - running_mean_sq_(new complex[num_freqs]()), - sub_running_mean_(new complex[num_freqs]()), - sub_running_mean_sq_(new complex[num_freqs]()), - variance_(new float[num_freqs]()), - conj_sum_(new float[num_freqs]()), - num_freqs_(num_freqs), - window_size_(window_size), - decay_(decay), - history_cursor_(0), - count_(0), - array_mean_(0.0f), - buffer_full_(false) { - history_.reset(new rtc::scoped_ptr[]>[num_freqs_]()); - for (size_t i = 0; i < num_freqs_; ++i) { - history_[i].reset(new complex[window_size_]()); - } - subhistory_.reset(new rtc::scoped_ptr[]>[num_freqs_]()); - for (size_t i = 0; i < num_freqs_; ++i) { - subhistory_[i].reset(new complex[window_size_]()); - } - subhistory_sq_.reset(new rtc::scoped_ptr[]>[num_freqs_]()); - for (size_t i = 0; i < num_freqs_; ++i) { - subhistory_sq_[i].reset(new complex[window_size_]()); - } - switch (type) { - case kStepInfinite: - step_func_ = &VarianceArray::InfiniteStep; - break; - case kStepDecaying: - step_func_ = &VarianceArray::DecayStep; - break; - case kStepWindowed: - step_func_ = &VarianceArray::WindowedStep; - break; - case kStepBlocked: - step_func_ = &VarianceArray::BlockedStep; - break; - case kStepBlockBasedMovingAverage: - step_func_ = &VarianceArray::BlockBasedMovingAverage; - break; - } -} - -// Compute the variance with Welford's algorithm, adding some fudge to -// the input in case of all-zeroes. -void VarianceArray::InfiniteStep(const complex* data, bool skip_fudge) { - array_mean_ = 0.0f; - ++count_; - for (size_t i = 0; i < num_freqs_; ++i) { - complex sample = data[i]; - if (!skip_fudge) { - sample = zerofudge(sample); - } - if (count_ == 1) { - running_mean_[i] = sample; - variance_[i] = 0.0f; - } else { - float old_sum = conj_sum_[i]; - complex old_mean = running_mean_[i]; - running_mean_[i] = - old_mean + (sample - old_mean) / static_cast(count_); - conj_sum_[i] = - (old_sum + std::conj(sample - old_mean) * (sample - running_mean_[i])) - .real(); - variance_[i] = - conj_sum_[i] / (count_ - 1); - } - array_mean_ += (variance_[i] - array_mean_) / (i + 1); - } -} - -// Compute the variance from the beginning, with exponential decaying of the -// series data. -void VarianceArray::DecayStep(const complex* data, bool /*dummy*/) { - array_mean_ = 0.0f; - ++count_; - for (size_t i = 0; i < num_freqs_; ++i) { - complex sample = data[i]; - sample = zerofudge(sample); - - if (count_ == 1) { - running_mean_[i] = sample; - running_mean_sq_[i] = sample * std::conj(sample); - variance_[i] = 0.0f; - } else { - complex prev = running_mean_[i]; - complex prev2 = running_mean_sq_[i]; - running_mean_[i] = decay_ * prev + (1.0f - decay_) * sample; - running_mean_sq_[i] = - decay_ * prev2 + (1.0f - decay_) * sample * std::conj(sample); - variance_[i] = (running_mean_sq_[i] - - running_mean_[i] * std::conj(running_mean_[i])).real(); - } - - array_mean_ += (variance_[i] - array_mean_) / (i + 1); - } -} - -// Windowed variance computation. On each step, the variances for the -// window are recomputed from scratch, using Welford's algorithm. -void VarianceArray::WindowedStep(const complex* data, bool /*dummy*/) { - size_t num = min(count_ + 1, window_size_); - array_mean_ = 0.0f; - for (size_t i = 0; i < num_freqs_; ++i) { - complex mean; - float conj_sum = 0.0f; - - history_[i][history_cursor_] = data[i]; - - mean = history_[i][history_cursor_]; - variance_[i] = 0.0f; - for (size_t j = 1; j < num; ++j) { - complex sample = - zerofudge(history_[i][(history_cursor_ + j) % window_size_]); - sample = history_[i][(history_cursor_ + j) % window_size_]; - float old_sum = conj_sum; - complex old_mean = mean; - - mean = old_mean + (sample - old_mean) / static_cast(j + 1); - conj_sum = - (old_sum + std::conj(sample - old_mean) * (sample - mean)).real(); - variance_[i] = conj_sum / (j); - } - array_mean_ += (variance_[i] - array_mean_) / (i + 1); - } - history_cursor_ = (history_cursor_ + 1) % window_size_; - ++count_; -} - -// Variance with a window of blocks. Within each block, the variances are -// recomputed from scratch at every stp, using |Var(X) = E(X^2) - E^2(X)|. -// Once a block is filled with kWindowBlockSize samples, it is added to the -// history window and a new block is started. The variances for the window -// are recomputed from scratch at each of these transitions. -void VarianceArray::BlockedStep(const complex* data, bool /*dummy*/) { - size_t blocks = min(window_size_, history_cursor_ + 1); - for (size_t i = 0; i < num_freqs_; ++i) { - AddToMean(data[i], count_ + 1, &sub_running_mean_[i]); - AddToMean(data[i] * std::conj(data[i]), count_ + 1, - &sub_running_mean_sq_[i]); - subhistory_[i][history_cursor_ % window_size_] = sub_running_mean_[i]; - subhistory_sq_[i][history_cursor_ % window_size_] = sub_running_mean_sq_[i]; - - variance_[i] = - (NewMean(running_mean_sq_[i], sub_running_mean_sq_[i], blocks) - - NewMean(running_mean_[i], sub_running_mean_[i], blocks) * - std::conj(NewMean(running_mean_[i], sub_running_mean_[i], blocks))) - .real(); - if (count_ == kWindowBlockSize - 1) { - sub_running_mean_[i] = complex(0.0f, 0.0f); - sub_running_mean_sq_[i] = complex(0.0f, 0.0f); - running_mean_[i] = complex(0.0f, 0.0f); - running_mean_sq_[i] = complex(0.0f, 0.0f); - for (size_t j = 0; j < min(window_size_, history_cursor_); ++j) { - AddToMean(subhistory_[i][j], j + 1, &running_mean_[i]); - AddToMean(subhistory_sq_[i][j], j + 1, &running_mean_sq_[i]); - } - ++history_cursor_; - } - } - ++count_; - if (count_ == kWindowBlockSize) { - count_ = 0; - } -} - -// Recomputes variances for each window from scratch based on previous window. -void VarianceArray::BlockBasedMovingAverage(const std::complex* data, - bool /*dummy*/) { - // TODO(ekmeyerson) To mitigate potential divergence, add counter so that - // after every so often sums are computed scratch by summing over all - // elements instead of subtracting oldest and adding newest. - for (size_t i = 0; i < num_freqs_; ++i) { - sub_running_mean_[i] += data[i]; - sub_running_mean_sq_[i] += data[i] * std::conj(data[i]); - } - ++count_; - - // TODO(ekmeyerson) Make kWindowBlockSize nonconstant to allow - // experimentation with different block size,window size pairs. - if (count_ >= kWindowBlockSize) { - count_ = 0; - - for (size_t i = 0; i < num_freqs_; ++i) { - running_mean_[i] -= subhistory_[i][history_cursor_]; - running_mean_sq_[i] -= subhistory_sq_[i][history_cursor_]; - - float scale = 1.f / kWindowBlockSize; - subhistory_[i][history_cursor_] = sub_running_mean_[i] * scale; - subhistory_sq_[i][history_cursor_] = sub_running_mean_sq_[i] * scale; - - sub_running_mean_[i] = std::complex(0.0f, 0.0f); - sub_running_mean_sq_[i] = std::complex(0.0f, 0.0f); - - running_mean_[i] += subhistory_[i][history_cursor_]; - running_mean_sq_[i] += subhistory_sq_[i][history_cursor_]; - - scale = 1.f / (buffer_full_ ? window_size_ : history_cursor_ + 1); - variance_[i] = std::real(running_mean_sq_[i] * scale - - running_mean_[i] * scale * - std::conj(running_mean_[i]) * scale); - } - - ++history_cursor_; - if (history_cursor_ >= window_size_) { - buffer_full_ = true; - history_cursor_ = 0; - } - } -} - -void VarianceArray::Clear() { - memset(running_mean_.get(), 0, sizeof(*running_mean_.get()) * num_freqs_); - memset(running_mean_sq_.get(), 0, - sizeof(*running_mean_sq_.get()) * num_freqs_); - memset(variance_.get(), 0, sizeof(*variance_.get()) * num_freqs_); - memset(conj_sum_.get(), 0, sizeof(*conj_sum_.get()) * num_freqs_); - history_cursor_ = 0; - count_ = 0; - array_mean_ = 0.0f; -} - -void VarianceArray::ApplyScale(float scale) { - array_mean_ = 0.0f; - for (size_t i = 0; i < num_freqs_; ++i) { - variance_[i] *= scale * scale; - array_mean_ += (variance_[i] - array_mean_) / (i + 1); - } -} - -GainApplier::GainApplier(size_t freqs, float change_limit) - : num_freqs_(freqs), - change_limit_(change_limit), - target_(new float[freqs]()), - current_(new float[freqs]()) { - for (size_t i = 0; i < freqs; ++i) { - target_[i] = 1.0f; - current_[i] = 1.0f; - } -} - -void GainApplier::Apply(const complex* in_block, - complex* out_block) { - for (size_t i = 0; i < num_freqs_; ++i) { - float factor = sqrtf(fabsf(current_[i])); - if (!std::isnormal(factor)) { - factor = 1.0f; - } - out_block[i] = factor * in_block[i]; - current_[i] = UpdateFactor(target_[i], current_[i], change_limit_); - } -} - -} // namespace intelligibility - -} // namespace webrtc diff --git a/webrtc/modules/audio_processing/intelligibility/intelligibility_utils.h b/webrtc/modules/audio_processing/intelligibility/intelligibility_utils.h deleted file mode 100644 index 4ac1167..0000000 --- a/webrtc/modules/audio_processing/intelligibility/intelligibility_utils.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -// -// Specifies helper classes for intelligibility enhancement. -// - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_INTELLIGIBILITY_INTELLIGIBILITY_UTILS_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_INTELLIGIBILITY_INTELLIGIBILITY_UTILS_H_ - -#include - -#include "webrtc/base/scoped_ptr.h" - -namespace webrtc { - -namespace intelligibility { - -// Return |current| changed towards |target|, with the change being at most -// |limit|. -float UpdateFactor(float target, float current, float limit); - -// Apply a small fudge to degenerate complex values. The numbers in the array -// were chosen randomly, so that even a series of all zeroes has some small -// variability. -std::complex zerofudge(std::complex c); - -// Incremental mean computation. Return the mean of the series with the -// mean |mean| with added |data|. -std::complex NewMean(std::complex mean, - std::complex data, - size_t count); - -// Updates |mean| with added |data|; -void AddToMean(std::complex data, - size_t count, - std::complex* mean); - -// Internal helper for computing the variances of a stream of arrays. -// The result is an array of variances per position: the i-th variance -// is the variance of the stream of data on the i-th positions in the -// input arrays. -// There are four methods of computation: -// * kStepInfinite computes variances from the beginning onwards -// * kStepDecaying uses a recursive exponential decay formula with a -// settable forgetting factor -// * kStepWindowed computes variances within a moving window -// * kStepBlocked is similar to kStepWindowed, but history is kept -// as a rolling window of blocks: multiple input elements are used for -// one block and the history then consists of the variances of these blocks -// with the same effect as kStepWindowed, but less storage, so the window -// can be longer -class VarianceArray { - public: - enum StepType { - kStepInfinite = 0, - kStepDecaying, - kStepWindowed, - kStepBlocked, - kStepBlockBasedMovingAverage - }; - - // Construct an instance for the given input array length (|freqs|) and - // computation algorithm (|type|), with the appropriate parameters. - // |window_size| is the number of samples for kStepWindowed and - // the number of blocks for kStepBlocked. |decay| is the forgetting factor - // for kStepDecaying. - VarianceArray(size_t freqs, StepType type, size_t window_size, float decay); - - // Add a new data point to the series and compute the new variances. - // TODO(bercic) |skip_fudge| is a flag for kStepWindowed and kStepDecaying, - // whether they should skip adding some small dummy values to the input - // to prevent problems with all-zero inputs. Can probably be removed. - void Step(const std::complex* data, bool skip_fudge = false) { - (this->*step_func_)(data, skip_fudge); - } - // Reset variances to zero and forget all history. - void Clear(); - // Scale the input data by |scale|. Effectively multiply variances - // by |scale^2|. - void ApplyScale(float scale); - - // The current set of variances. - const float* variance() const { return variance_.get(); } - - // The mean value of the current set of variances. - float array_mean() const { return array_mean_; } - - private: - void InfiniteStep(const std::complex* data, bool dummy); - void DecayStep(const std::complex* data, bool dummy); - void WindowedStep(const std::complex* data, bool dummy); - void BlockedStep(const std::complex* data, bool dummy); - void BlockBasedMovingAverage(const std::complex* data, bool dummy); - - // TODO(ekmeyerson): Switch the following running means - // and histories from rtc::scoped_ptr to std::vector. - - // The current average X and X^2. - rtc::scoped_ptr[]> running_mean_; - rtc::scoped_ptr[]> running_mean_sq_; - - // Average X and X^2 for the current block in kStepBlocked. - rtc::scoped_ptr[]> sub_running_mean_; - rtc::scoped_ptr[]> sub_running_mean_sq_; - - // Sample history for the rolling window in kStepWindowed and block-wise - // histories for kStepBlocked. - rtc::scoped_ptr[]>[]> history_; - rtc::scoped_ptr[]>[]> subhistory_; - rtc::scoped_ptr[]>[]> subhistory_sq_; - - // The current set of variances and sums for Welford's algorithm. - rtc::scoped_ptr variance_; - rtc::scoped_ptr conj_sum_; - - const size_t num_freqs_; - const size_t window_size_; - const float decay_; - size_t history_cursor_; - size_t count_; - float array_mean_; - bool buffer_full_; - void (VarianceArray::*step_func_)(const std::complex*, bool); -}; - -// Helper class for smoothing gain changes. On each applicatiion step, the -// currently used gains are changed towards a set of settable target gains, -// constrained by a limit on the magnitude of the changes. -class GainApplier { - public: - GainApplier(size_t freqs, float change_limit); - - // Copy |in_block| to |out_block|, multiplied by the current set of gains, - // and step the current set of gains towards the target set. - void Apply(const std::complex* in_block, - std::complex* out_block); - - // Return the current target gain set. Modify this array to set the targets. - float* target() const { return target_.get(); } - - private: - const size_t num_freqs_; - const float change_limit_; - rtc::scoped_ptr target_; - rtc::scoped_ptr current_; -}; - -} // namespace intelligibility - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_INTELLIGIBILITY_INTELLIGIBILITY_UTILS_H_ diff --git a/webrtc/modules/audio_processing/level_estimator.cc b/webrtc/modules/audio_processing/level_estimator.cc new file mode 100644 index 0000000..e707288 --- /dev/null +++ b/webrtc/modules/audio_processing/level_estimator.cc @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/level_estimator.h" + +#include "api/array_view.h" + +namespace webrtc { + +LevelEstimator::LevelEstimator() { + rms_.Reset(); +} + +LevelEstimator::~LevelEstimator() = default; + +void LevelEstimator::ProcessStream(const AudioBuffer& audio) { + for (size_t i = 0; i < audio.num_channels(); i++) { + rms_.Analyze(rtc::ArrayView(audio.channels_const()[i], + audio.num_frames())); + } +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/level_estimator.h b/webrtc/modules/audio_processing/level_estimator.h new file mode 100644 index 0000000..1d8a071 --- /dev/null +++ b/webrtc/modules/audio_processing/level_estimator.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_H_ + +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/rms_level.h" + +namespace webrtc { + +// An estimation component used to retrieve level metrics. +class LevelEstimator { + public: + LevelEstimator(); + ~LevelEstimator(); + + LevelEstimator(LevelEstimator&) = delete; + LevelEstimator& operator=(LevelEstimator&) = delete; + + void ProcessStream(const AudioBuffer& audio); + + // Returns the root mean square (RMS) level in dBFs (decibels from digital + // full-scale), or alternately dBov. It is computed over all primary stream + // frames since the last call to RMS(). The returned value is positive but + // should be interpreted as negative. It is constrained to [0, 127]. + // + // The computation follows: https://tools.ietf.org/html/rfc6465 + // with the intent that it can provide the RTP audio level indication. + // + // Frames passed to ProcessStream() with an |_energy| of zero are considered + // to have been muted. The RMS of the frame will be interpreted as -127. + int RMS() { return rms_.Average(); } + + private: + RmsLevel rms_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/level_estimator_impl.cc b/webrtc/modules/audio_processing/level_estimator_impl.cc deleted file mode 100644 index 35fe697..0000000 --- a/webrtc/modules/audio_processing/level_estimator_impl.cc +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_processing/level_estimator_impl.h" - -#include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/modules/audio_processing/include/audio_processing.h" -#include "webrtc/modules/audio_processing/rms_level.h" -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" - -namespace webrtc { - -LevelEstimatorImpl::LevelEstimatorImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit) - : ProcessingComponent(), - crit_(crit) {} - -LevelEstimatorImpl::~LevelEstimatorImpl() {} - -int LevelEstimatorImpl::ProcessStream(AudioBuffer* audio) { - if (!is_component_enabled()) { - return AudioProcessing::kNoError; - } - - RMSLevel* rms_level = static_cast(handle(0)); - for (int i = 0; i < audio->num_channels(); ++i) { - rms_level->Process(audio->channels_const()[i], - audio->num_frames()); - } - - return AudioProcessing::kNoError; -} - -int LevelEstimatorImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(crit_); - return EnableComponent(enable); -} - -bool LevelEstimatorImpl::is_enabled() const { - return is_component_enabled(); -} - -int LevelEstimatorImpl::RMS() { - if (!is_component_enabled()) { - return AudioProcessing::kNotEnabledError; - } - - RMSLevel* rms_level = static_cast(handle(0)); - return rms_level->RMS(); -} - -// The ProcessingComponent implementation is pretty weird in this class since -// we have only a single instance of the trivial underlying component. -void* LevelEstimatorImpl::CreateHandle() const { - return new RMSLevel; -} - -void LevelEstimatorImpl::DestroyHandle(void* handle) const { - delete static_cast(handle); -} - -int LevelEstimatorImpl::InitializeHandle(void* handle) const { - static_cast(handle)->Reset(); - return AudioProcessing::kNoError; -} - -int LevelEstimatorImpl::ConfigureHandle(void* /*handle*/) const { - return AudioProcessing::kNoError; -} - -int LevelEstimatorImpl::num_handles_required() const { - return 1; -} - -int LevelEstimatorImpl::GetHandleError(void* /*handle*/) const { - return AudioProcessing::kUnspecifiedError; -} - -} // namespace webrtc diff --git a/webrtc/modules/audio_processing/level_estimator_impl.h b/webrtc/modules/audio_processing/level_estimator_impl.h deleted file mode 100644 index 0d0050c..0000000 --- a/webrtc/modules/audio_processing/level_estimator_impl.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_IMPL_H_ - -#include "webrtc/modules/audio_processing/include/audio_processing.h" -#include "webrtc/modules/audio_processing/processing_component.h" -#include "webrtc/modules/audio_processing/rms_level.h" - -namespace webrtc { - -class AudioBuffer; -class CriticalSectionWrapper; - -class LevelEstimatorImpl : public LevelEstimator, - public ProcessingComponent { - public: - LevelEstimatorImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit); - virtual ~LevelEstimatorImpl(); - - int ProcessStream(AudioBuffer* audio); - - // LevelEstimator implementation. - bool is_enabled() const override; - - private: - // LevelEstimator implementation. - int Enable(bool enable) override; - int RMS() override; - - // ProcessingComponent implementation. - void* CreateHandle() const override; - int InitializeHandle(void* handle) const override; - int ConfigureHandle(void* handle) const override; - void DestroyHandle(void* handle) const override; - int num_handles_required() const override; - int GetHandleError(void* handle) const override; - - CriticalSectionWrapper* crit_; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_IMPL_H_ diff --git a/webrtc/modules/audio_processing/logging/aec_logging.h b/webrtc/modules/audio_processing/logging/aec_logging.h deleted file mode 100644 index 3cf9ff8..0000000 --- a/webrtc/modules/audio_processing/logging/aec_logging.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2015 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_LOGGING_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_LOGGING_ - -#include - -#include "webrtc/modules/audio_processing/logging/aec_logging_file_handling.h" - -// To enable AEC logging, invoke GYP with -Daec_debug_dump=1. -#ifdef WEBRTC_AEC_DEBUG_DUMP -// Dumps a wav data to file. -#define RTC_AEC_DEBUG_WAV_WRITE(file, data, num_samples) \ - do { \ - rtc_WavWriteSamples(file, data, num_samples); \ - } while (0) - -// (Re)opens a wav file for writing using the specified sample rate. -#define RTC_AEC_DEBUG_WAV_REOPEN(name, instance_index, process_rate, \ - sample_rate, wav_file) \ - do { \ - WebRtcAec_ReopenWav(name, instance_index, process_rate, sample_rate, \ - wav_file); \ - } while (0) - -// Closes a wav file. -#define RTC_AEC_DEBUG_WAV_CLOSE(wav_file) \ - do { \ - rtc_WavClose(wav_file); \ - } while (0) - -// Dumps a raw data to file. -#define RTC_AEC_DEBUG_RAW_WRITE(file, data, data_size) \ - do { \ - (void) fwrite(data, data_size, 1, file); \ - } while (0) - -// Opens a raw data file for writing using the specified sample rate. -#define RTC_AEC_DEBUG_RAW_OPEN(name, instance_counter, file) \ - do { \ - WebRtcAec_RawFileOpen(name, instance_counter, file); \ - } while (0) - -// Closes a raw data file. -#define RTC_AEC_DEBUG_RAW_CLOSE(file) \ - do { \ - fclose(file); \ - } while (0) - -#else // RTC_AEC_DEBUG_DUMP -#define RTC_AEC_DEBUG_WAV_WRITE(file, data, num_samples) \ - do { \ - } while (0) - -#define RTC_AEC_DEBUG_WAV_REOPEN(wav_file, name, instance_index, process_rate, \ - sample_rate) \ - do { \ - } while (0) - -#define RTC_AEC_DEBUG_WAV_CLOSE(wav_file) \ - do { \ - } while (0) - -#define RTC_AEC_DEBUG_RAW_WRITE(file, data, data_size) \ - do { \ - } while (0) - -#define RTC_AEC_DEBUG_RAW_OPEN(file, name, instance_counter) \ - do { \ - } while (0) - -#define RTC_AEC_DEBUG_RAW_CLOSE(file) \ - do { \ - } while (0) - -#endif // WEBRTC_AEC_DEBUG_DUMP - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_LOGGING_ diff --git a/webrtc/modules/audio_processing/logging/aec_logging_file_handling.cc b/webrtc/modules/audio_processing/logging/aec_logging_file_handling.cc deleted file mode 100644 index 3a43471..0000000 --- a/webrtc/modules/audio_processing/logging/aec_logging_file_handling.cc +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2015 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 "webrtc/modules/audio_processing/logging/aec_logging_file_handling.h" - -#include -#include - -#include "webrtc/base/checks.h" -#include "webrtc/base/stringutils.h" -#include "webrtc/common_audio/wav_file.h" -#include "webrtc/typedefs.h" - -#ifdef WEBRTC_AEC_DEBUG_DUMP -void WebRtcAec_ReopenWav(const char* name, - int instance_index, - int process_rate, - int sample_rate, - rtc_WavWriter** wav_file) { - if (*wav_file) { - if (rtc_WavSampleRate(*wav_file) == sample_rate) - return; - rtc_WavClose(*wav_file); - } - char filename[64]; - int written = rtc::sprintfn(filename, sizeof(filename), "%s%d-%d.wav", name, - instance_index, process_rate); - - // Ensure there was no buffer output error. - RTC_DCHECK_GE(written, 0); - // Ensure that the buffer size was sufficient. - RTC_DCHECK_LT(static_cast(written), sizeof(filename)); - - *wav_file = rtc_WavOpen(filename, sample_rate, 1); -} - -void WebRtcAec_RawFileOpen(const char* name, int instance_index, FILE** file) { - char filename[64]; - int written = rtc::sprintfn(filename, sizeof(filename), "%s_%d.dat", name, - instance_index); - - // Ensure there was no buffer output error. - RTC_DCHECK_GE(written, 0); - // Ensure that the buffer size was sufficient. - RTC_DCHECK_LT(static_cast(written), sizeof(filename)); - - *file = fopen(filename, "wb"); -} - -#endif // WEBRTC_AEC_DEBUG_DUMP diff --git a/webrtc/modules/audio_processing/logging/aec_logging_file_handling.h b/webrtc/modules/audio_processing/logging/aec_logging_file_handling.h deleted file mode 100644 index 5ec8394..0000000 --- a/webrtc/modules/audio_processing/logging/aec_logging_file_handling.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2015 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_LOGGING_FILE_HANDLING_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_LOGGING_FILE_HANDLING_ - -#include - -#include "webrtc/common_audio/wav_file.h" -#include "webrtc/typedefs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef WEBRTC_AEC_DEBUG_DUMP -// Opens a new Wav file for writing. If it was already open with a different -// sample frequency, it closes it first. -void WebRtcAec_ReopenWav(const char* name, - int instance_index, - int process_rate, - int sample_rate, - rtc_WavWriter** wav_file); - -// Opens dumpfile with instance-specific filename. -void WebRtcAec_RawFileOpen(const char* name, int instance_index, FILE** file); - -#endif // WEBRTC_AEC_DEBUG_DUMP - -#ifdef __cplusplus -} -#endif - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_LOGGING_FILE_HANDLING_ diff --git a/webrtc/modules/audio_processing/logging/apm_data_dumper.cc b/webrtc/modules/audio_processing/logging/apm_data_dumper.cc new file mode 100644 index 0000000..917df60 --- /dev/null +++ b/webrtc/modules/audio_processing/logging/apm_data_dumper.cc @@ -0,0 +1,92 @@ +/* + * 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 "modules/audio_processing/logging/apm_data_dumper.h" + +#include "rtc_base/strings/string_builder.h" + +// Check to verify that the define is properly set. +#if !defined(WEBRTC_APM_DEBUG_DUMP) || \ + (WEBRTC_APM_DEBUG_DUMP != 0 && WEBRTC_APM_DEBUG_DUMP != 1) +#error "Set WEBRTC_APM_DEBUG_DUMP to either 0 or 1" +#endif + +namespace webrtc { +namespace { + +#if WEBRTC_APM_DEBUG_DUMP == 1 + +#if defined(WEBRTC_WIN) +constexpr char kPathDelimiter = '\\'; +#else +constexpr char kPathDelimiter = '/'; +#endif + +std::string FormFileName(const char* output_dir, + const char* name, + int instance_index, + int reinit_index, + const std::string& suffix) { + char buf[1024]; + rtc::SimpleStringBuilder ss(buf); + const size_t output_dir_size = strlen(output_dir); + if (output_dir_size > 0) { + ss << output_dir; + if (output_dir[output_dir_size - 1] != kPathDelimiter) { + ss << kPathDelimiter; + } + } + ss << name << "_" << instance_index << "-" << reinit_index << suffix; + return ss.str(); +} +#endif + +} // namespace + +#if WEBRTC_APM_DEBUG_DUMP == 1 +ApmDataDumper::ApmDataDumper(int instance_index) + : instance_index_(instance_index) {} +#else +ApmDataDumper::ApmDataDumper(int instance_index) {} +#endif + +ApmDataDumper::~ApmDataDumper() = default; + +#if WEBRTC_APM_DEBUG_DUMP == 1 +bool ApmDataDumper::recording_activated_ = false; +char ApmDataDumper::output_dir_[] = ""; + +FILE* ApmDataDumper::GetRawFile(const char* name) { + std::string filename = FormFileName(output_dir_, name, instance_index_, + recording_set_index_, ".dat"); + auto& f = raw_files_[filename]; + if (!f) { + f.reset(fopen(filename.c_str(), "wb")); + RTC_CHECK(f.get()) << "Cannot write to " << filename << "."; + } + return f.get(); +} + +WavWriter* ApmDataDumper::GetWavFile(const char* name, + int sample_rate_hz, + int num_channels, + WavFile::SampleFormat format) { + std::string filename = FormFileName(output_dir_, name, instance_index_, + recording_set_index_, ".wav"); + auto& f = wav_files_[filename]; + if (!f) { + f.reset( + new WavWriter(filename.c_str(), sample_rate_hz, num_channels, format)); + } + return f.get(); +} +#endif + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/logging/apm_data_dumper.h b/webrtc/modules/audio_processing/logging/apm_data_dumper.h new file mode 100644 index 0000000..1824fdd --- /dev/null +++ b/webrtc/modules/audio_processing/logging/apm_data_dumper.h @@ -0,0 +1,287 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_LOGGING_APM_DATA_DUMPER_H_ +#define MODULES_AUDIO_PROCESSING_LOGGING_APM_DATA_DUMPER_H_ + +#include +#include +#include + +#include +#if WEBRTC_APM_DEBUG_DUMP == 1 +#include +#include +#endif + +#include "api/array_view.h" +#if WEBRTC_APM_DEBUG_DUMP == 1 +#include "common_audio/wav_file.h" +#include "rtc_base/checks.h" +#endif + +// Check to verify that the define is properly set. +#if !defined(WEBRTC_APM_DEBUG_DUMP) || \ + (WEBRTC_APM_DEBUG_DUMP != 0 && WEBRTC_APM_DEBUG_DUMP != 1) +#error "Set WEBRTC_APM_DEBUG_DUMP to either 0 or 1" +#endif + +namespace webrtc { + +#if WEBRTC_APM_DEBUG_DUMP == 1 +// Functor used to use as a custom deleter in the map of file pointers to raw +// files. +struct RawFileCloseFunctor { + void operator()(FILE* f) const { fclose(f); } +}; +#endif + +// Class that handles dumping of variables into files. +class ApmDataDumper { + public: + // Constructor that takes an instance index that may + // be used to distinguish data dumped from different + // instances of the code. + explicit ApmDataDumper(int instance_index); + + ApmDataDumper() = delete; + ApmDataDumper(const ApmDataDumper&) = delete; + ApmDataDumper& operator=(const ApmDataDumper&) = delete; + + ~ApmDataDumper(); + + // Activates or deactivate the dumping functionality. + static void SetActivated(bool activated) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + recording_activated_ = activated; +#endif + } + + // Set an optional output directory. + static void SetOutputDirectory(const std::string& output_dir) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + RTC_CHECK_LT(output_dir.size(), kOutputDirMaxLength); + strncpy(output_dir_, output_dir.c_str(), output_dir.size()); +#endif + } + + // Reinitializes the data dumping such that new versions + // of all files being dumped to are created. + void InitiateNewSetOfRecordings() { +#if WEBRTC_APM_DEBUG_DUMP == 1 + ++recording_set_index_; +#endif + } + + // Methods for performing dumping of data of various types into + // various formats. + void DumpRaw(const char* name, double v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(&v, sizeof(v), 1, file); + } +#endif + } + + void DumpRaw(const char* name, size_t v_length, const double* v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(v, sizeof(v[0]), v_length, file); + } +#endif + } + + void DumpRaw(const char* name, rtc::ArrayView v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + DumpRaw(name, v.size(), v.data()); + } +#endif + } + + void DumpRaw(const char* name, float v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(&v, sizeof(v), 1, file); + } +#endif + } + + void DumpRaw(const char* name, size_t v_length, const float* v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(v, sizeof(v[0]), v_length, file); + } +#endif + } + + void DumpRaw(const char* name, rtc::ArrayView v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + DumpRaw(name, v.size(), v.data()); + } +#endif + } + + void DumpRaw(const char* name, bool v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + DumpRaw(name, static_cast(v)); + } +#endif + } + + void DumpRaw(const char* name, size_t v_length, const bool* v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + FILE* file = GetRawFile(name); + for (size_t k = 0; k < v_length; ++k) { + int16_t value = static_cast(v[k]); + fwrite(&value, sizeof(value), 1, file); + } + } +#endif + } + + void DumpRaw(const char* name, rtc::ArrayView v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + DumpRaw(name, v.size(), v.data()); + } +#endif + } + + void DumpRaw(const char* name, int16_t v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(&v, sizeof(v), 1, file); + } +#endif + } + + void DumpRaw(const char* name, size_t v_length, const int16_t* v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(v, sizeof(v[0]), v_length, file); + } +#endif + } + + void DumpRaw(const char* name, rtc::ArrayView v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + DumpRaw(name, v.size(), v.data()); + } +#endif + } + + void DumpRaw(const char* name, int32_t v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(&v, sizeof(v), 1, file); + } +#endif + } + + void DumpRaw(const char* name, size_t v_length, const int32_t* v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(v, sizeof(v[0]), v_length, file); + } +#endif + } + + void DumpRaw(const char* name, size_t v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(&v, sizeof(v), 1, file); + } +#endif + } + + void DumpRaw(const char* name, size_t v_length, const size_t* v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + FILE* file = GetRawFile(name); + fwrite(v, sizeof(v[0]), v_length, file); + } +#endif + } + + void DumpRaw(const char* name, rtc::ArrayView v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + DumpRaw(name, v.size(), v.data()); + } +#endif + } + + void DumpRaw(const char* name, rtc::ArrayView v) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + DumpRaw(name, v.size(), v.data()); +#endif + } + + void DumpWav(const char* name, + size_t v_length, + const float* v, + int sample_rate_hz, + int num_channels) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + WavWriter* file = GetWavFile(name, sample_rate_hz, num_channels, + WavFile::SampleFormat::kFloat); + file->WriteSamples(v, v_length); + } +#endif + } + + void DumpWav(const char* name, + rtc::ArrayView v, + int sample_rate_hz, + int num_channels) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + if (recording_activated_) { + DumpWav(name, v.size(), v.data(), sample_rate_hz, num_channels); + } +#endif + } + + private: +#if WEBRTC_APM_DEBUG_DUMP == 1 + static bool recording_activated_; + static constexpr size_t kOutputDirMaxLength = 1024; + static char output_dir_[kOutputDirMaxLength]; + const int instance_index_; + int recording_set_index_ = 0; + std::unordered_map> + raw_files_; + std::unordered_map> wav_files_; + + FILE* GetRawFile(const char* name); + WavWriter* GetWavFile(const char* name, + int sample_rate_hz, + int num_channels, + WavFile::SampleFormat format); +#endif +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_LOGGING_APM_DATA_DUMPER_H_ diff --git a/webrtc/modules/audio_processing/meson.build b/webrtc/modules/audio_processing/meson.build index 34e23e0..8229eb3 100644 --- a/webrtc/modules/audio_processing/meson.build +++ b/webrtc/modules/audio_processing/meson.build @@ -1,32 +1,147 @@ +apm_flags = ['-DWEBRTC_APM_DEBUG_DUMP=0'] + webrtc_audio_processing_sources = [ - 'aec/aec_core.c', - 'aec/aec_rdft.c', - 'aec/aec_resampler.c', - 'aec/echo_cancellation.c', - 'aecm/echo_control_mobile.c', - 'aecm/aecm_core.c', - 'aecm/aecm_core_c.c', - 'agc/legacy/analog_agc.c', - 'agc/legacy/digital_agc.c', + 'aec_dump/null_aec_dump_factory.cc', + 'aec3/adaptive_fir_filter.cc', + 'aec3/adaptive_fir_filter_erl.cc', + 'aec3/aec3_common.cc', + 'aec3/aec3_fft.cc', + 'aec3/aec_state.cc', + 'aec3/alignment_mixer.cc', + 'aec3/api_call_jitter_metrics.cc', + 'aec3/block_buffer.cc', + 'aec3/block_delay_buffer.cc', + 'aec3/block_framer.cc', + 'aec3/block_processor.cc', + 'aec3/block_processor_metrics.cc', + 'aec3/clockdrift_detector.cc', + 'aec3/coarse_filter_update_gain.cc', + 'aec3/comfort_noise_generator.cc', + 'aec3/decimator.cc', + 'aec3/dominant_nearend_detector.cc', + 'aec3/downsampled_render_buffer.cc', + 'aec3/echo_audibility.cc', + 'aec3/echo_canceller3.cc', + 'aec3/echo_path_delay_estimator.cc', + 'aec3/echo_path_variability.cc', + 'aec3/echo_remover.cc', + 'aec3/echo_remover_metrics.cc', + 'aec3/erle_estimator.cc', + 'aec3/erl_estimator.cc', + 'aec3/fft_buffer.cc', + 'aec3/filter_analyzer.cc', + 'aec3/frame_blocker.cc', + 'aec3/fullband_erle_estimator.cc', + 'aec3/matched_filter.cc', + 'aec3/matched_filter_lag_aggregator.cc', + 'aec3/moving_average.cc', + 'aec3/refined_filter_update_gain.cc', + 'aec3/render_buffer.cc', + 'aec3/render_delay_buffer.cc', + 'aec3/render_delay_controller.cc', + 'aec3/render_delay_controller_metrics.cc', + 'aec3/render_signal_analyzer.cc', + 'aec3/residual_echo_estimator.cc', + 'aec3/reverb_decay_estimator.cc', + 'aec3/reverb_frequency_response.cc', + 'aec3/reverb_model.cc', + 'aec3/reverb_model_estimator.cc', + 'aec3/signal_dependent_erle_estimator.cc', + 'aec3/spectrum_buffer.cc', + 'aec3/stationarity_estimator.cc', + 'aec3/subband_erle_estimator.cc', + 'aec3/subband_nearend_detector.cc', + 'aec3/subtractor.cc', + 'aec3/subtractor_output_analyzer.cc', + 'aec3/subtractor_output.cc', + 'aec3/suppression_filter.cc', + 'aec3/suppression_gain.cc', + 'aec3/transparent_mode.cc', + 'aecm/aecm_core.cc', + 'aecm/aecm_core_c.cc', + 'aecm/echo_control_mobile.cc', 'agc/agc.cc', 'agc/agc_manager_direct.cc', - 'agc/histogram.cc', + 'agc/legacy/analog_agc.cc', + 'agc/legacy/digital_agc.cc', + 'agc/loudness_histogram.cc', 'agc/utility.cc', - 'beamformer/array_util.cc', - 'beamformer/covariance_matrix_generator.cc', - 'beamformer/nonlinear_beamformer.cc', - 'intelligibility/intelligibility_enhancer.cc', - 'intelligibility/intelligibility_utils.cc', - 'logging/aec_logging_file_handling.cc', - 'transient/click_annotate.cc', + 'agc2/adaptive_agc.cc', + 'agc2/adaptive_digital_gain_applier.cc', + 'agc2/adaptive_mode_level_estimator_agc.cc', + 'agc2/adaptive_mode_level_estimator.cc', + 'agc2/agc2_testing_common.cc', + 'agc2/biquad_filter.cc', + 'agc2/compute_interpolated_gain_curve.cc', + 'agc2/down_sampler.cc', + 'agc2/fixed_digital_level_estimator.cc', + 'agc2/gain_applier.cc', + 'agc2/interpolated_gain_curve.cc', + 'agc2/limiter.cc', + 'agc2/limiter_db_gain_curve.cc', + 'agc2/noise_level_estimator.cc', + 'agc2/noise_spectrum_estimator.cc', + 'agc2/rnn_vad/auto_correlation.cc', + 'agc2/rnn_vad/common.cc', + 'agc2/rnn_vad/features_extraction.cc', + 'agc2/rnn_vad/lp_residual.cc', + 'agc2/rnn_vad/pitch_search.cc', + 'agc2/rnn_vad/pitch_search_internal.cc', + 'agc2/rnn_vad/rnn.cc', + 'agc2/rnn_vad/rnn_vad_tool.cc', + 'agc2/rnn_vad/spectral_features.cc', + 'agc2/rnn_vad/spectral_features_internal.cc', + 'agc2/saturation_protector.cc', + 'agc2/signal_classifier.cc', + 'agc2/vad_with_level.cc', + 'agc2/vector_float_frame.cc', + 'audio_buffer.cc', + 'audio_processing_builder_impl.cc', + 'audio_processing_impl.cc', + 'echo_control_mobile_impl.cc', + 'echo_detector/circular_buffer.cc', + 'echo_detector/mean_variance_estimator.cc', + 'echo_detector/moving_max.cc', + 'echo_detector/normalized_covariance_estimator.cc', + 'gain_control_impl.cc', + 'gain_controller2.cc', + 'high_pass_filter.cc', + 'include/aec_dump.cc', + 'include/audio_frame_proxies.cc', + 'include/audio_processing.cc', + 'include/audio_processing_statistics.cc', + 'include/config.cc', + 'level_estimator.cc', + 'logging/apm_data_dumper.cc', + 'ns/fast_math.cc', + 'ns/histograms.cc', + 'ns/noise_estimator.cc', + 'ns/noise_suppressor.cc', + 'ns/ns_fft.cc', + 'ns/prior_signal_model.cc', + 'ns/prior_signal_model_estimator.cc', + 'ns/quantile_noise_estimator.cc', + 'ns/signal_model.cc', + 'ns/signal_model_estimator.cc', + 'ns/speech_probability_estimator.cc', + 'ns/suppression_params.cc', + 'ns/wiener_filter.cc', + 'optionally_built_submodule_creators.cc', + 'residual_echo_detector.cc', + 'rms_level.cc', + 'splitting_filter.cc', + 'three_band_filter_bank.cc', 'transient/file_utils.cc', 'transient/moving_moments.cc', 'transient/transient_detector.cc', - 'transient/transient_suppressor.cc', + 'transient/transient_suppressor_impl.cc', 'transient/wpd_node.cc', 'transient/wpd_tree.cc', - 'utility/delay_estimator.c', - 'utility/delay_estimator_wrapper.c', + 'typing_detection.cc', + 'utility/cascaded_biquad_filter.cc', + 'utility/delay_estimator.cc', + 'utility/delay_estimator_wrapper.cc', + 'utility/pffft_wrapper.cc', 'vad/gmm.cc', 'vad/pitch_based_vad.cc', 'vad/pitch_internal.cc', @@ -35,85 +150,65 @@ webrtc_audio_processing_sources = [ 'vad/vad_audio_proc.cc', 'vad/vad_circular_buffer.cc', 'vad/voice_activity_detector.cc', - 'audio_buffer.cc', - 'audio_processing_impl.cc', - 'audio_processing_impl.h', - 'echo_cancellation_impl.cc', - 'echo_control_mobile_impl.cc', - 'gain_control_impl.cc', - 'high_pass_filter_impl.cc', - 'level_estimator_impl.cc', - 'noise_suppression_impl.cc', - 'rms_level.cc', - 'splitting_filter.cc', - 'processing_component.cc', - 'three_band_filter_bank.cc', - 'typing_detection.cc', - 'voice_detection_impl.cc', -] - -webrtc_audio_processing_beamformer_headers = [ - 'beamformer/array_util.h', + 'voice_detection.cc', ] webrtc_audio_processing_include_headers = [ 'include/audio_processing.h', + 'include/audio_processing_statistics.h', + 'include/config.h', ] -if get_option('ns_mode') == 'float' - webrtc_audio_processing_sources += [ - 'ns/noise_suppression.c', - 'ns/ns_core.c', - ] -else - webrtc_audio_processing_sources += [ - 'ns/noise_suppression_x.c', - 'ns/nsx_core.c', - 'ns/nsx_core_c.c', - ] - if have_neon - webrtc_audio_processing_sources += [ - 'ns/nsx_core_neon.c', - ] - endif -endif - extra_libs = [] if have_x86 extra_libs += [ static_library('webrtc_audio_processing_privatearch', - ['aec/aec_core_sse2.c', 'aec/aec_rdft_sse2.c'], + [ + 'aec3/adaptive_fir_filter_avx2.cc', + 'aec3/adaptive_fir_filter_erl_avx2.cc', + 'aec3/fft_data_avx2.cc', + 'aec3/matched_filter_avx2.cc', + 'aec3/vector_math_avx2.cc', + ], dependencies: common_deps, include_directories: webrtc_inc, - c_args: common_cflags + ['-msse2'], - cpp_args: common_cxxflags + ['-msse2'] + c_args: common_cflags + apm_flags + ['-mavx2', '-mfma'], + cpp_args: common_cxxflags + apm_flags + ['-mavx2', '-mfma'] ) ] endif +if have_mips + webrtc_audio_processing_sources += [ + 'aecm/aecm_core_mips.cc', + ] +endif + if have_neon webrtc_audio_processing_sources += [ - 'aec/aec_core_neon.c', - 'aec/aec_rdft_neon.c', 'aecm/aecm_core_neon.c', ] endif -install_headers(webrtc_audio_processing_beamformer_headers, - subdir: 'webrtc_audio_processing/webrtc/modules/audio_processing/beamformer' -) - install_headers(webrtc_audio_processing_include_headers, - subdir: 'webrtc_audio_processing/webrtc/modules/audio_processing/include' + subdir: 'webrtc_audio_processing/modules/audio_processing/include' ) libwebrtc_audio_processing = library('webrtc_audio_processing', webrtc_audio_processing_sources, - dependencies: [base_dep, webrtc_audio_coding_dep, system_wrappers_dep, common_audio_dep, webrtc_dep] + common_deps, + dependencies: [ + base_dep, + api_dep, + webrtc_audio_coding_dep, + system_wrappers_dep, + common_audio_dep, + pffft_dep, + rnnoise_dep, + ] + common_deps, link_with: extra_libs, include_directories: webrtc_inc, - c_args: common_cflags, - cpp_args: common_cxxflags, + c_args: common_cflags + apm_flags, + cpp_args: common_cxxflags + apm_flags, soversion: soversion, install: true ) diff --git a/webrtc/modules/audio_processing/noise_suppression_impl.cc b/webrtc/modules/audio_processing/noise_suppression_impl.cc deleted file mode 100644 index 65ec3c4..0000000 --- a/webrtc/modules/audio_processing/noise_suppression_impl.cc +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_processing/noise_suppression_impl.h" - -#include - -#include "webrtc/modules/audio_processing/audio_buffer.h" -#if defined(WEBRTC_NS_FLOAT) -#include "webrtc/modules/audio_processing/ns/include/noise_suppression.h" -#elif defined(WEBRTC_NS_FIXED) -#include "webrtc/modules/audio_processing/ns/include/noise_suppression_x.h" -#endif -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" - - -namespace webrtc { - -#if defined(WEBRTC_NS_FLOAT) -typedef NsHandle Handle; -#elif defined(WEBRTC_NS_FIXED) -typedef NsxHandle Handle; -#endif - -namespace { -int MapSetting(NoiseSuppression::Level level) { - switch (level) { - case NoiseSuppression::kLow: - return 0; - case NoiseSuppression::kModerate: - return 1; - case NoiseSuppression::kHigh: - return 2; - case NoiseSuppression::kVeryHigh: - return 3; - } - assert(false); - return -1; -} -} // namespace - -NoiseSuppressionImpl::NoiseSuppressionImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit) - : ProcessingComponent(), - apm_(apm), - crit_(crit), - level_(kModerate) {} - -NoiseSuppressionImpl::~NoiseSuppressionImpl() {} - -int NoiseSuppressionImpl::AnalyzeCaptureAudio(AudioBuffer* audio) { -#if defined(WEBRTC_NS_FLOAT) - if (!is_component_enabled()) { - return apm_->kNoError; - } - assert(audio->num_frames_per_band() <= 160); - assert(audio->num_channels() == num_handles()); - - for (int i = 0; i < num_handles(); ++i) { - Handle* my_handle = static_cast(handle(i)); - - WebRtcNs_Analyze(my_handle, audio->split_bands_const_f(i)[kBand0To8kHz]); - } -#endif - return apm_->kNoError; -} - -int NoiseSuppressionImpl::ProcessCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - assert(audio->num_frames_per_band() <= 160); - assert(audio->num_channels() == num_handles()); - - for (int i = 0; i < num_handles(); ++i) { - Handle* my_handle = static_cast(handle(i)); -#if defined(WEBRTC_NS_FLOAT) - WebRtcNs_Process(my_handle, - audio->split_bands_const_f(i), - audio->num_bands(), - audio->split_bands_f(i)); -#elif defined(WEBRTC_NS_FIXED) - WebRtcNsx_Process(my_handle, - audio->split_bands_const(i), - audio->num_bands(), - audio->split_bands(i)); -#endif - } - return apm_->kNoError; -} - -int NoiseSuppressionImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(crit_); - return EnableComponent(enable); -} - -bool NoiseSuppressionImpl::is_enabled() const { - return is_component_enabled(); -} - -int NoiseSuppressionImpl::set_level(Level level) { - CriticalSectionScoped crit_scoped(crit_); - if (MapSetting(level) == -1) { - return apm_->kBadParameterError; - } - - level_ = level; - return Configure(); -} - -NoiseSuppression::Level NoiseSuppressionImpl::level() const { - return level_; -} - -float NoiseSuppressionImpl::speech_probability() const { -#if defined(WEBRTC_NS_FLOAT) - float probability_average = 0.0f; - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); - probability_average += WebRtcNs_prior_speech_probability(my_handle); - } - return probability_average / num_handles(); -#elif defined(WEBRTC_NS_FIXED) - // Currently not available for the fixed point implementation. - return apm_->kUnsupportedFunctionError; -#endif -} - -void* NoiseSuppressionImpl::CreateHandle() const { -#if defined(WEBRTC_NS_FLOAT) - return WebRtcNs_Create(); -#elif defined(WEBRTC_NS_FIXED) - return WebRtcNsx_Create(); -#endif -} - -void NoiseSuppressionImpl::DestroyHandle(void* handle) const { -#if defined(WEBRTC_NS_FLOAT) - WebRtcNs_Free(static_cast(handle)); -#elif defined(WEBRTC_NS_FIXED) - WebRtcNsx_Free(static_cast(handle)); -#endif -} - -int NoiseSuppressionImpl::InitializeHandle(void* handle) const { -#if defined(WEBRTC_NS_FLOAT) - return WebRtcNs_Init(static_cast(handle), - apm_->proc_sample_rate_hz()); -#elif defined(WEBRTC_NS_FIXED) - return WebRtcNsx_Init(static_cast(handle), - apm_->proc_sample_rate_hz()); -#endif -} - -int NoiseSuppressionImpl::ConfigureHandle(void* handle) const { -#if defined(WEBRTC_NS_FLOAT) - return WebRtcNs_set_policy(static_cast(handle), - MapSetting(level_)); -#elif defined(WEBRTC_NS_FIXED) - return WebRtcNsx_set_policy(static_cast(handle), - MapSetting(level_)); -#endif -} - -int NoiseSuppressionImpl::num_handles_required() const { - return apm_->num_output_channels(); -} - -int NoiseSuppressionImpl::GetHandleError(void* handle) const { - // The NS has no get_error() function. - assert(handle != NULL); - return apm_->kUnspecifiedError; -} -} // namespace webrtc diff --git a/webrtc/modules/audio_processing/noise_suppression_impl.h b/webrtc/modules/audio_processing/noise_suppression_impl.h deleted file mode 100644 index 76a39b8..0000000 --- a/webrtc/modules/audio_processing/noise_suppression_impl.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_IMPL_H_ - -#include "webrtc/modules/audio_processing/include/audio_processing.h" -#include "webrtc/modules/audio_processing/processing_component.h" - -namespace webrtc { - -class AudioBuffer; -class CriticalSectionWrapper; - -class NoiseSuppressionImpl : public NoiseSuppression, - public ProcessingComponent { - public: - NoiseSuppressionImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit); - virtual ~NoiseSuppressionImpl(); - - int AnalyzeCaptureAudio(AudioBuffer* audio); - int ProcessCaptureAudio(AudioBuffer* audio); - - // NoiseSuppression implementation. - bool is_enabled() const override; - float speech_probability() const override; - Level level() const override; - - private: - // NoiseSuppression implementation. - int Enable(bool enable) override; - int set_level(Level level) override; - - // ProcessingComponent implementation. - void* CreateHandle() const override; - int InitializeHandle(void* handle) const override; - int ConfigureHandle(void* handle) const override; - void DestroyHandle(void* handle) const override; - int num_handles_required() const override; - int GetHandleError(void* handle) const override; - - const AudioProcessing* apm_; - CriticalSectionWrapper* crit_; - Level level_; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_IMPL_H_ diff --git a/webrtc/modules/audio_processing/ns/BUILD.gn b/webrtc/modules/audio_processing/ns/BUILD.gn new file mode 100644 index 0000000..f0842c5 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/BUILD.gn @@ -0,0 +1,102 @@ +# Copyright (c) 2019 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. + +import("../../../webrtc.gni") + +rtc_static_library("ns") { + visibility = [ "*" ] + configs += [ "..:apm_debug_dump" ] + sources = [ + "fast_math.cc", + "fast_math.h", + "histograms.cc", + "histograms.h", + "noise_estimator.cc", + "noise_estimator.h", + "noise_suppressor.cc", + "noise_suppressor.h", + "ns_common.h", + "ns_config.h", + "ns_fft.cc", + "ns_fft.h", + "prior_signal_model.cc", + "prior_signal_model.h", + "prior_signal_model_estimator.cc", + "prior_signal_model_estimator.h", + "quantile_noise_estimator.cc", + "quantile_noise_estimator.h", + "signal_model.cc", + "signal_model.h", + "signal_model_estimator.cc", + "signal_model_estimator.h", + "speech_probability_estimator.cc", + "speech_probability_estimator.h", + "suppression_params.cc", + "suppression_params.h", + "wiener_filter.cc", + "wiener_filter.h", + ] + + defines = [] + if (rtc_build_with_neon && current_cpu != "arm64") { + suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] + cflags = [ "-mfpu=neon" ] + } + + deps = [ + "..:apm_logging", + "..:audio_buffer", + "..:high_pass_filter", + "../../../api:array_view", + "../../../common_audio:common_audio_c", + "../../../common_audio/third_party/ooura:fft_size_128", + "../../../common_audio/third_party/ooura:fft_size_256", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_minmax", + "../../../rtc_base/system:arch", + "../../../system_wrappers", + "../../../system_wrappers:field_trial", + "../../../system_wrappers:metrics", + "../utility:cascaded_biquad_filter", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +if (rtc_include_tests) { + rtc_source_set("ns_unittests") { + testonly = true + + configs += [ "..:apm_debug_dump" ] + sources = [ "noise_suppressor_unittest.cc" ] + + deps = [ + ":ns", + "..:apm_logging", + "..:audio_buffer", + "..:audio_processing", + "..:audio_processing_unittests", + "..:high_pass_filter", + "../../../api:array_view", + "../../../rtc_base:checks", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_minmax", + "../../../rtc_base/system:arch", + "../../../system_wrappers", + "../../../test:test_support", + "../utility:cascaded_biquad_filter", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + + defines = [] + + if (rtc_enable_protobuf) { + sources += [] + } + } +} diff --git a/webrtc/modules/audio_processing/ns/defines.h b/webrtc/modules/audio_processing/ns/defines.h deleted file mode 100644 index 8271332..0000000 --- a/webrtc/modules/audio_processing/ns/defines.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_DEFINES_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_DEFINES_H_ - -#define BLOCKL_MAX 160 // max processing block length: 160 -#define ANAL_BLOCKL_MAX 256 // max analysis block length: 256 -#define HALF_ANAL_BLOCKL 129 // half max analysis block length + 1 -#define NUM_HIGH_BANDS_MAX 2 // max number of high bands: 2 - -#define QUANTILE (float)0.25 - -#define SIMULT 3 -#define END_STARTUP_LONG 200 -#define END_STARTUP_SHORT 50 -#define FACTOR (float)40.0 -#define WIDTH (float)0.01 - -// Length of fft work arrays. -#define IP_LENGTH (ANAL_BLOCKL_MAX >> 1) // must be at least ceil(2 + sqrt(ANAL_BLOCKL_MAX/2)) -#define W_LENGTH (ANAL_BLOCKL_MAX >> 1) - -//PARAMETERS FOR NEW METHOD -#define DD_PR_SNR (float)0.98 // DD update of prior SNR -#define LRT_TAVG (float)0.50 // tavg parameter for LRT (previously 0.90) -#define SPECT_FL_TAVG (float)0.30 // tavg parameter for spectral flatness measure -#define SPECT_DIFF_TAVG (float)0.30 // tavg parameter for spectral difference measure -#define PRIOR_UPDATE (float)0.10 // update parameter of prior model -#define NOISE_UPDATE (float)0.90 // update parameter for noise -#define SPEECH_UPDATE (float)0.99 // update parameter when likely speech -#define WIDTH_PR_MAP (float)4.0 // width parameter in sigmoid map for prior model -#define LRT_FEATURE_THR (float)0.5 // default threshold for LRT feature -#define SF_FEATURE_THR (float)0.5 // default threshold for Spectral Flatness feature -#define SD_FEATURE_THR (float)0.5 // default threshold for Spectral Difference feature -#define PROB_RANGE (float)0.20 // probability threshold for noise state in - // speech/noise likelihood -#define HIST_PAR_EST 1000 // histogram size for estimation of parameters -#define GAMMA_PAUSE (float)0.05 // update for conservative noise estimate -// -#define B_LIM (float)0.5 // threshold in final energy gain factor calculation -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_DEFINES_H_ diff --git a/webrtc/modules/audio_processing/ns/fast_math.cc b/webrtc/modules/audio_processing/ns/fast_math.cc new file mode 100644 index 0000000..d13110c --- /dev/null +++ b/webrtc/modules/audio_processing/ns/fast_math.cc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/fast_math.h" + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +float FastLog2f(float in) { + RTC_DCHECK_GT(in, .0f); + // Read and interpret float as uint32_t and then cast to float. + // This is done to extract the exponent (bits 30 - 23). + // "Right shift" of the exponent is then performed by multiplying + // with the constant (1/2^23). Finally, we subtract a constant to + // remove the bias (https://en.wikipedia.org/wiki/Exponent_bias). + union { + float dummy; + uint32_t a; + } x = {in}; + float out = x.a; + out *= 1.1920929e-7f; // 1/2^23 + out -= 126.942695f; // Remove bias. + return out; +} + +} // namespace + +float SqrtFastApproximation(float f) { + // TODO(peah): Add fast approximate implementation. + return sqrtf(f); +} + +float Pow2Approximation(float p) { + // TODO(peah): Add fast approximate implementation. + return powf(2.f, p); +} + +float PowApproximation(float x, float p) { + return Pow2Approximation(p * FastLog2f(x)); +} + +float LogApproximation(float x) { + constexpr float kLogOf2 = 0.69314718056f; + return FastLog2f(x) * kLogOf2; +} + +void LogApproximation(rtc::ArrayView x, rtc::ArrayView y) { + for (size_t k = 0; k < x.size(); ++k) { + y[k] = LogApproximation(x[k]); + } +} + +float ExpApproximation(float x) { + constexpr float kLog10Ofe = 0.4342944819f; + return PowApproximation(10.f, x * kLog10Ofe); +} + +void ExpApproximation(rtc::ArrayView x, rtc::ArrayView y) { + for (size_t k = 0; k < x.size(); ++k) { + y[k] = ExpApproximation(x[k]); + } +} + +void ExpApproximationSignFlip(rtc::ArrayView x, + rtc::ArrayView y) { + for (size_t k = 0; k < x.size(); ++k) { + y[k] = ExpApproximation(-x[k]); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/fast_math.h b/webrtc/modules/audio_processing/ns/fast_math.h new file mode 100644 index 0000000..0aefee9 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/fast_math.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_FAST_MATH_H_ +#define MODULES_AUDIO_PROCESSING_NS_FAST_MATH_H_ + +#include "api/array_view.h" + +namespace webrtc { + +// Sqrt approximation. +float SqrtFastApproximation(float f); + +// Log base conversion log(x) = log2(x)/log2(e). +float LogApproximation(float x); +void LogApproximation(rtc::ArrayView x, rtc::ArrayView y); + +// 2^x approximation. +float Pow2Approximation(float p); + +// x^p approximation. +float PowApproximation(float x, float p); + +// e^x approximation. +float ExpApproximation(float x); +void ExpApproximation(rtc::ArrayView x, rtc::ArrayView y); +void ExpApproximationSignFlip(rtc::ArrayView x, + rtc::ArrayView y); +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_FAST_MATH_H_ diff --git a/webrtc/modules/audio_processing/ns/histograms.cc b/webrtc/modules/audio_processing/ns/histograms.cc new file mode 100644 index 0000000..1d4f459 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/histograms.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/histograms.h" + +namespace webrtc { + +Histograms::Histograms() { + Clear(); +} + +void Histograms::Clear() { + lrt_.fill(0); + spectral_flatness_.fill(0); + spectral_diff_.fill(0); +} + +void Histograms::Update(const SignalModel& features_) { + // Update the histogram for the LRT. + constexpr float kOneByBinSizeLrt = 1.f / kBinSizeLrt; + if (features_.lrt < kHistogramSize * kBinSizeLrt && features_.lrt >= 0.f) { + ++lrt_[kOneByBinSizeLrt * features_.lrt]; + } + + // Update histogram for the spectral flatness. + constexpr float kOneByBinSizeSpecFlat = 1.f / kBinSizeSpecFlat; + if (features_.spectral_flatness < kHistogramSize * kBinSizeSpecFlat && + features_.spectral_flatness >= 0.f) { + ++spectral_flatness_[features_.spectral_flatness * kOneByBinSizeSpecFlat]; + } + + // Update histogram for the spectral difference. + constexpr float kOneByBinSizeSpecDiff = 1.f / kBinSizeSpecDiff; + if (features_.spectral_diff < kHistogramSize * kBinSizeSpecDiff && + features_.spectral_diff >= 0.f) { + ++spectral_diff_[features_.spectral_diff * kOneByBinSizeSpecDiff]; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/histograms.h b/webrtc/modules/audio_processing/ns/histograms.h new file mode 100644 index 0000000..9640e74 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/histograms.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_HISTOGRAMS_H_ +#define MODULES_AUDIO_PROCESSING_NS_HISTOGRAMS_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/signal_model.h" + +namespace webrtc { + +constexpr int kHistogramSize = 1000; + +// Class for handling the updating of histograms. +class Histograms { + public: + Histograms(); + Histograms(const Histograms&) = delete; + Histograms& operator=(const Histograms&) = delete; + + // Clears the histograms. + void Clear(); + + // Extracts thresholds for feature parameters and updates the corresponding + // histogram. + void Update(const SignalModel& features_); + + // Methods for accessing the histograms. + rtc::ArrayView get_lrt() const { return lrt_; } + rtc::ArrayView get_spectral_flatness() const { + return spectral_flatness_; + } + rtc::ArrayView get_spectral_diff() const { + return spectral_diff_; + } + + private: + std::array lrt_; + std::array spectral_flatness_; + std::array spectral_diff_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_HISTOGRAMS_H_ diff --git a/webrtc/modules/audio_processing/ns/include/noise_suppression.h b/webrtc/modules/audio_processing/ns/include/noise_suppression.h deleted file mode 100644 index 9dac56b..0000000 --- a/webrtc/modules/audio_processing/ns/include/noise_suppression.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_INCLUDE_NOISE_SUPPRESSION_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_INCLUDE_NOISE_SUPPRESSION_H_ - -#include - -#include "webrtc/typedefs.h" - -typedef struct NsHandleT NsHandle; - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * This function creates an instance of the floating point Noise Suppression. - */ -NsHandle* WebRtcNs_Create(); - -/* - * This function frees the dynamic memory of a specified noise suppression - * instance. - * - * Input: - * - NS_inst : Pointer to NS instance that should be freed - */ -void WebRtcNs_Free(NsHandle* NS_inst); - -/* - * This function initializes a NS instance and has to be called before any other - * processing is made. - * - * Input: - * - NS_inst : Instance that should be initialized - * - fs : sampling frequency - * - * Output: - * - NS_inst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNs_Init(NsHandle* NS_inst, uint32_t fs); - -/* - * This changes the aggressiveness of the noise suppression method. - * - * Input: - * - NS_inst : Noise suppression instance. - * - mode : 0: Mild, 1: Medium , 2: Aggressive - * - * Output: - * - NS_inst : Updated instance. - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNs_set_policy(NsHandle* NS_inst, int mode); - -/* - * This functions estimates the background noise for the inserted speech frame. - * The input and output signals should always be 10ms (80 or 160 samples). - * - * Input - * - NS_inst : Noise suppression instance. - * - spframe : Pointer to speech frame buffer for L band - * - * Output: - * - NS_inst : Updated NS instance - */ -void WebRtcNs_Analyze(NsHandle* NS_inst, const float* spframe); - -/* - * This functions does Noise Suppression for the inserted speech frame. The - * input and output signals should always be 10ms (80 or 160 samples). - * - * Input - * - NS_inst : Noise suppression instance. - * - spframe : Pointer to speech frame buffer for each band - * - num_bands : Number of bands - * - * Output: - * - NS_inst : Updated NS instance - * - outframe : Pointer to output frame for each band - */ -void WebRtcNs_Process(NsHandle* NS_inst, - const float* const* spframe, - size_t num_bands, - float* const* outframe); - -/* Returns the internally used prior speech probability of the current frame. - * There is a frequency bin based one as well, with which this should not be - * confused. - * - * Input - * - handle : Noise suppression instance. - * - * Return value : Prior speech probability in interval [0.0, 1.0]. - * -1 - NULL pointer or uninitialized instance. - */ -float WebRtcNs_prior_speech_probability(NsHandle* handle); - -#ifdef __cplusplus -} -#endif - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_INCLUDE_NOISE_SUPPRESSION_H_ diff --git a/webrtc/modules/audio_processing/ns/include/noise_suppression_x.h b/webrtc/modules/audio_processing/ns/include/noise_suppression_x.h deleted file mode 100644 index 88fe4cd..0000000 --- a/webrtc/modules/audio_processing/ns/include/noise_suppression_x.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_INCLUDE_NOISE_SUPPRESSION_X_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_INCLUDE_NOISE_SUPPRESSION_X_H_ - -#include "webrtc/typedefs.h" - -typedef struct NsxHandleT NsxHandle; - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * This function creates an instance of the fixed point Noise Suppression. - */ -NsxHandle* WebRtcNsx_Create(); - -/* - * This function frees the dynamic memory of a specified Noise Suppression - * instance. - * - * Input: - * - nsxInst : Pointer to NS instance that should be freed - */ -void WebRtcNsx_Free(NsxHandle* nsxInst); - -/* - * This function initializes a NS instance - * - * Input: - * - nsxInst : Instance that should be initialized - * - fs : sampling frequency - * - * Output: - * - nsxInst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNsx_Init(NsxHandle* nsxInst, uint32_t fs); - -/* - * This changes the aggressiveness of the noise suppression method. - * - * Input: - * - nsxInst : Instance that should be initialized - * - mode : 0: Mild, 1: Medium , 2: Aggressive - * - * Output: - * - nsxInst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNsx_set_policy(NsxHandle* nsxInst, int mode); - -/* - * This functions does noise suppression for the inserted speech frame. The - * input and output signals should always be 10ms (80 or 160 samples). - * - * Input - * - nsxInst : NSx instance. Needs to be initiated before call. - * - speechFrame : Pointer to speech frame buffer for each band - * - num_bands : Number of bands - * - * Output: - * - nsxInst : Updated NSx instance - * - outFrame : Pointer to output frame for each band - */ -void WebRtcNsx_Process(NsxHandle* nsxInst, - const short* const* speechFrame, - int num_bands, - short* const* outFrame); - -#ifdef __cplusplus -} -#endif - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_INCLUDE_NOISE_SUPPRESSION_X_H_ diff --git a/webrtc/modules/audio_processing/ns/noise_estimator.cc b/webrtc/modules/audio_processing/ns/noise_estimator.cc new file mode 100644 index 0000000..5367545 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/noise_estimator.cc @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/noise_estimator.h" + +#include + +#include "modules/audio_processing/ns/fast_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Log(i). +constexpr std::array log_table = { + 0.f, 0.f, 0.f, 0.f, 0.f, 1.609438f, 1.791759f, + 1.945910f, 2.079442f, 2.197225f, 2.302585f, 2.397895f, 2.484907f, 2.564949f, + 2.639057f, 2.708050f, 2.772589f, 2.833213f, 2.890372f, 2.944439f, 2.995732f, + 3.044522f, 3.091043f, 3.135494f, 3.178054f, 3.218876f, 3.258097f, 3.295837f, + 3.332205f, 3.367296f, 3.401197f, 3.433987f, 3.465736f, 3.496507f, 3.526361f, + 3.555348f, 3.583519f, 3.610918f, 3.637586f, 3.663562f, 3.688879f, 3.713572f, + 3.737669f, 3.761200f, 3.784190f, 3.806663f, 3.828641f, 3.850147f, 3.871201f, + 3.891820f, 3.912023f, 3.931826f, 3.951244f, 3.970292f, 3.988984f, 4.007333f, + 4.025352f, 4.043051f, 4.060443f, 4.077538f, 4.094345f, 4.110874f, 4.127134f, + 4.143135f, 4.158883f, 4.174387f, 4.189655f, 4.204693f, 4.219508f, 4.234107f, + 4.248495f, 4.262680f, 4.276666f, 4.290460f, 4.304065f, 4.317488f, 4.330733f, + 4.343805f, 4.356709f, 4.369448f, 4.382027f, 4.394449f, 4.406719f, 4.418841f, + 4.430817f, 4.442651f, 4.454347f, 4.465908f, 4.477337f, 4.488636f, 4.499810f, + 4.510859f, 4.521789f, 4.532599f, 4.543295f, 4.553877f, 4.564348f, 4.574711f, + 4.584968f, 4.595119f, 4.605170f, 4.615121f, 4.624973f, 4.634729f, 4.644391f, + 4.653960f, 4.663439f, 4.672829f, 4.682131f, 4.691348f, 4.700480f, 4.709530f, + 4.718499f, 4.727388f, 4.736198f, 4.744932f, 4.753591f, 4.762174f, 4.770685f, + 4.779124f, 4.787492f, 4.795791f, 4.804021f, 4.812184f, 4.820282f, 4.828314f, + 4.836282f, 4.844187f, 4.852030f}; + +} // namespace + +NoiseEstimator::NoiseEstimator(const SuppressionParams& suppression_params) + : suppression_params_(suppression_params) { + noise_spectrum_.fill(0.f); + prev_noise_spectrum_.fill(0.f); + conservative_noise_spectrum_.fill(0.f); + parametric_noise_spectrum_.fill(0.f); +} + +void NoiseEstimator::PrepareAnalysis() { + std::copy(noise_spectrum_.begin(), noise_spectrum_.end(), + prev_noise_spectrum_.begin()); +} + +void NoiseEstimator::PreUpdate( + int32_t num_analyzed_frames, + rtc::ArrayView signal_spectrum, + float signal_spectral_sum) { + quantile_noise_estimator_.Estimate(signal_spectrum, noise_spectrum_); + + if (num_analyzed_frames < kShortStartupPhaseBlocks) { + // Compute simplified noise model during startup. + const size_t kStartBand = 5; + float sum_log_i_log_magn = 0.f; + float sum_log_i = 0.f; + float sum_log_i_square = 0.f; + float sum_log_magn = 0.f; + for (size_t i = kStartBand; i < kFftSizeBy2Plus1; ++i) { + float log_i = log_table[i]; + sum_log_i += log_i; + sum_log_i_square += log_i * log_i; + float log_signal = LogApproximation(signal_spectrum[i]); + sum_log_magn += log_signal; + sum_log_i_log_magn += log_i * log_signal; + } + + // Estimate the parameter for the level of the white noise. + constexpr float kOneByFftSizeBy2Plus1 = 1.f / kFftSizeBy2Plus1; + white_noise_level_ += signal_spectral_sum * kOneByFftSizeBy2Plus1 * + suppression_params_.over_subtraction_factor; + + // Estimate pink noise parameters. + float denom = sum_log_i_square * (kFftSizeBy2Plus1 - kStartBand) - + sum_log_i * sum_log_i; + float num = + sum_log_i_square * sum_log_magn - sum_log_i * sum_log_i_log_magn; + RTC_DCHECK_NE(denom, 0.f); + float pink_noise_adjustment = num / denom; + + // Constrain the estimated spectrum to be positive. + pink_noise_adjustment = std::max(pink_noise_adjustment, 0.f); + pink_noise_numerator_ += pink_noise_adjustment; + num = sum_log_i * sum_log_magn - + (kFftSizeBy2Plus1 - kStartBand) * sum_log_i_log_magn; + RTC_DCHECK_NE(denom, 0.f); + pink_noise_adjustment = num / denom; + + // Constrain the pink noise power to be in the interval [0, 1]. + pink_noise_adjustment = std::max(std::min(pink_noise_adjustment, 1.f), 0.f); + + pink_noise_exp_ += pink_noise_adjustment; + + const float one_by_num_analyzed_frames_plus_1 = + 1.f / (num_analyzed_frames + 1.f); + + // Calculate the frequency-independent parts of parametric noise estimate. + float parametric_exp = 0.f; + float parametric_num = 0.f; + if (pink_noise_exp_ > 0.f) { + // Use pink noise estimate. + parametric_num = ExpApproximation(pink_noise_numerator_ * + one_by_num_analyzed_frames_plus_1); + parametric_num *= num_analyzed_frames + 1.f; + parametric_exp = pink_noise_exp_ * one_by_num_analyzed_frames_plus_1; + } + + constexpr float kOneByShortStartupPhaseBlocks = + 1.f / kShortStartupPhaseBlocks; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + // Estimate the background noise using the white and pink noise + // parameters. + if (pink_noise_exp_ == 0.f) { + // Use white noise estimate. + parametric_noise_spectrum_[i] = white_noise_level_; + } else { + // Use pink noise estimate. + float use_band = i < kStartBand ? kStartBand : i; + float denom = PowApproximation(use_band, parametric_exp); + RTC_DCHECK_NE(denom, 0.f); + parametric_noise_spectrum_[i] = parametric_num / denom; + } + } + + // Weight quantile noise with modeled noise. + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + noise_spectrum_[i] *= num_analyzed_frames; + float tmp = parametric_noise_spectrum_[i] * + (kShortStartupPhaseBlocks - num_analyzed_frames); + noise_spectrum_[i] += tmp * one_by_num_analyzed_frames_plus_1; + noise_spectrum_[i] *= kOneByShortStartupPhaseBlocks; + } + } +} + +void NoiseEstimator::PostUpdate( + rtc::ArrayView speech_probability, + rtc::ArrayView signal_spectrum) { + // Time-avg parameter for noise_spectrum update. + constexpr float kNoiseUpdate = 0.9f; + + float gamma = kNoiseUpdate; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + const float prob_speech = speech_probability[i]; + const float prob_non_speech = 1.f - prob_speech; + + // Temporary noise update used for speech frames if update value is less + // than previous. + float noise_update_tmp = + gamma * prev_noise_spectrum_[i] + + (1.f - gamma) * (prob_non_speech * signal_spectrum[i] + + prob_speech * prev_noise_spectrum_[i]); + + // Time-constant based on speech/noise_spectrum state. + float gamma_old = gamma; + + // Increase gamma for frame likely to be seech. + constexpr float kProbRange = .2f; + gamma = prob_speech > kProbRange ? .99f : kNoiseUpdate; + + // Conservative noise_spectrum update. + if (prob_speech < kProbRange) { + conservative_noise_spectrum_[i] += + 0.05f * (signal_spectrum[i] - conservative_noise_spectrum_[i]); + } + + // Noise_spectrum update. + if (gamma == gamma_old) { + noise_spectrum_[i] = noise_update_tmp; + } else { + noise_spectrum_[i] = + gamma * prev_noise_spectrum_[i] + + (1.f - gamma) * (prob_non_speech * signal_spectrum[i] + + prob_speech * prev_noise_spectrum_[i]); + // Allow for noise_spectrum update downwards: If noise_spectrum update + // decreases the noise_spectrum, it is safe, so allow it to happen. + noise_spectrum_[i] = std::min(noise_spectrum_[i], noise_update_tmp); + } + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/noise_estimator.h b/webrtc/modules/audio_processing/ns/noise_estimator.h new file mode 100644 index 0000000..0c0466a --- /dev/null +++ b/webrtc/modules/audio_processing/ns/noise_estimator.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_NOISE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_NOISE_ESTIMATOR_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/quantile_noise_estimator.h" +#include "modules/audio_processing/ns/suppression_params.h" + +namespace webrtc { + +// Class for estimating the spectral characteristics of the noise in an incoming +// signal. +class NoiseEstimator { + public: + explicit NoiseEstimator(const SuppressionParams& suppression_params); + + // Prepare the estimator for analysis of a new frame. + void PrepareAnalysis(); + + // Performs the first step of the estimator update. + void PreUpdate(int32_t num_analyzed_frames, + rtc::ArrayView signal_spectrum, + float signal_spectral_sum); + + // Performs the second step of the estimator update. + void PostUpdate( + rtc::ArrayView speech_probability, + rtc::ArrayView signal_spectrum); + + // Returns the noise spectral estimate. + rtc::ArrayView get_noise_spectrum() const { + return noise_spectrum_; + } + + // Returns the noise from the previous frame. + rtc::ArrayView get_prev_noise_spectrum() + const { + return prev_noise_spectrum_; + } + + // Returns a noise spectral estimate based on white and pink noise parameters. + rtc::ArrayView get_parametric_noise_spectrum() + const { + return parametric_noise_spectrum_; + } + rtc::ArrayView + get_conservative_noise_spectrum() const { + return conservative_noise_spectrum_; + } + + private: + const SuppressionParams& suppression_params_; + float white_noise_level_ = 0.f; + float pink_noise_numerator_ = 0.f; + float pink_noise_exp_ = 0.f; + std::array prev_noise_spectrum_; + std::array conservative_noise_spectrum_; + std::array parametric_noise_spectrum_; + std::array noise_spectrum_; + QuantileNoiseEstimator quantile_noise_estimator_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_NOISE_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/ns/noise_suppression.c b/webrtc/modules/audio_processing/ns/noise_suppression.c deleted file mode 100644 index 13f1b2d..0000000 --- a/webrtc/modules/audio_processing/ns/noise_suppression.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_processing/ns/include/noise_suppression.h" - -#include -#include - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/ns/defines.h" -#include "webrtc/modules/audio_processing/ns/ns_core.h" - -NsHandle* WebRtcNs_Create() { - NoiseSuppressionC* self = malloc(sizeof(NoiseSuppressionC)); - self->initFlag = 0; - return (NsHandle*)self; -} - -void WebRtcNs_Free(NsHandle* NS_inst) { - free(NS_inst); -} - -int WebRtcNs_Init(NsHandle* NS_inst, uint32_t fs) { - return WebRtcNs_InitCore((NoiseSuppressionC*)NS_inst, fs); -} - -int WebRtcNs_set_policy(NsHandle* NS_inst, int mode) { - return WebRtcNs_set_policy_core((NoiseSuppressionC*)NS_inst, mode); -} - -void WebRtcNs_Analyze(NsHandle* NS_inst, const float* spframe) { - WebRtcNs_AnalyzeCore((NoiseSuppressionC*)NS_inst, spframe); -} - -void WebRtcNs_Process(NsHandle* NS_inst, - const float* const* spframe, - size_t num_bands, - float* const* outframe) { - WebRtcNs_ProcessCore((NoiseSuppressionC*)NS_inst, spframe, num_bands, - outframe); -} - -float WebRtcNs_prior_speech_probability(NsHandle* handle) { - NoiseSuppressionC* self = (NoiseSuppressionC*)handle; - if (handle == NULL) { - return -1; - } - if (self->initFlag == 0) { - return -1; - } - return self->priorSpeechProb; -} diff --git a/webrtc/modules/audio_processing/ns/noise_suppression_x.c b/webrtc/modules/audio_processing/ns/noise_suppression_x.c deleted file mode 100644 index 150fe60..0000000 --- a/webrtc/modules/audio_processing/ns/noise_suppression_x.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_processing/ns/include/noise_suppression_x.h" - -#include - -#include "webrtc/common_audio/signal_processing/include/real_fft.h" -#include "webrtc/modules/audio_processing/ns/nsx_core.h" -#include "webrtc/modules/audio_processing/ns/nsx_defines.h" - -NsxHandle* WebRtcNsx_Create() { - NoiseSuppressionFixedC* self = malloc(sizeof(NoiseSuppressionFixedC)); - WebRtcSpl_Init(); - self->real_fft = NULL; - self->initFlag = 0; - return (NsxHandle*)self; -} - -void WebRtcNsx_Free(NsxHandle* nsxInst) { - WebRtcSpl_FreeRealFFT(((NoiseSuppressionFixedC*)nsxInst)->real_fft); - free(nsxInst); -} - -int WebRtcNsx_Init(NsxHandle* nsxInst, uint32_t fs) { - return WebRtcNsx_InitCore((NoiseSuppressionFixedC*)nsxInst, fs); -} - -int WebRtcNsx_set_policy(NsxHandle* nsxInst, int mode) { - return WebRtcNsx_set_policy_core((NoiseSuppressionFixedC*)nsxInst, mode); -} - -void WebRtcNsx_Process(NsxHandle* nsxInst, - const short* const* speechFrame, - int num_bands, - short* const* outFrame) { - WebRtcNsx_ProcessCore((NoiseSuppressionFixedC*)nsxInst, speechFrame, - num_bands, outFrame); -} diff --git a/webrtc/modules/audio_processing/ns/noise_suppressor.cc b/webrtc/modules/audio_processing/ns/noise_suppressor.cc new file mode 100644 index 0000000..89e1fe0 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/noise_suppressor.cc @@ -0,0 +1,549 @@ +/* + * Copyright (c) 2012 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 "modules/audio_processing/ns/noise_suppressor.h" + +#include +#include +#include +#include + +#include "modules/audio_processing/ns/fast_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Maps sample rate to number of bands. +size_t NumBandsForRate(size_t sample_rate_hz) { + RTC_DCHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000 || + sample_rate_hz == 48000); + return sample_rate_hz / 16000; +} + +// Maximum number of channels for which the channel data is stored on +// the stack. If the number of channels are larger than this, they are stored +// using scratch memory that is pre-allocated on the heap. The reason for this +// partitioning is not to waste heap space for handling the more common numbers +// of channels, while at the same time not limiting the support for higher +// numbers of channels by enforcing the channel data to be stored on the +// stack using a fixed maximum value. +constexpr size_t kMaxNumChannelsOnStack = 2; + +// Chooses the number of channels to store on the heap when that is required due +// to the number of channels being larger than the pre-defined number +// of channels to store on the stack. +size_t NumChannelsOnHeap(size_t num_channels) { + return num_channels > kMaxNumChannelsOnStack ? num_channels : 0; +} + +// Hybrib Hanning and flat window for the filterbank. +constexpr std::array kBlocks160w256FirstHalf = { + 0.00000000f, 0.01636173f, 0.03271908f, 0.04906767f, 0.06540313f, + 0.08172107f, 0.09801714f, 0.11428696f, 0.13052619f, 0.14673047f, + 0.16289547f, 0.17901686f, 0.19509032f, 0.21111155f, 0.22707626f, + 0.24298018f, 0.25881905f, 0.27458862f, 0.29028468f, 0.30590302f, + 0.32143947f, 0.33688985f, 0.35225005f, 0.36751594f, 0.38268343f, + 0.39774847f, 0.41270703f, 0.42755509f, 0.44228869f, 0.45690388f, + 0.47139674f, 0.48576339f, 0.50000000f, 0.51410274f, 0.52806785f, + 0.54189158f, 0.55557023f, 0.56910015f, 0.58247770f, 0.59569930f, + 0.60876143f, 0.62166057f, 0.63439328f, 0.64695615f, 0.65934582f, + 0.67155895f, 0.68359230f, 0.69544264f, 0.70710678f, 0.71858162f, + 0.72986407f, 0.74095113f, 0.75183981f, 0.76252720f, 0.77301045f, + 0.78328675f, 0.79335334f, 0.80320753f, 0.81284668f, 0.82226822f, + 0.83146961f, 0.84044840f, 0.84920218f, 0.85772861f, 0.86602540f, + 0.87409034f, 0.88192126f, 0.88951608f, 0.89687274f, 0.90398929f, + 0.91086382f, 0.91749450f, 0.92387953f, 0.93001722f, 0.93590593f, + 0.94154407f, 0.94693013f, 0.95206268f, 0.95694034f, 0.96156180f, + 0.96592583f, 0.97003125f, 0.97387698f, 0.97746197f, 0.98078528f, + 0.98384601f, 0.98664333f, 0.98917651f, 0.99144486f, 0.99344778f, + 0.99518473f, 0.99665524f, 0.99785892f, 0.99879546f, 0.99946459f, + 0.99986614f}; + +// Applies the filterbank window to a buffer. +void ApplyFilterBankWindow(rtc::ArrayView x) { + for (size_t i = 0; i < 96; ++i) { + x[i] = kBlocks160w256FirstHalf[i] * x[i]; + } + + for (size_t i = 161, k = 95; i < kFftSize; ++i, --k) { + RTC_DCHECK_NE(0, k); + x[i] = kBlocks160w256FirstHalf[k] * x[i]; + } +} + +// Extends a frame with previous data. +void FormExtendedFrame(rtc::ArrayView frame, + rtc::ArrayView old_data, + rtc::ArrayView extended_frame) { + std::copy(old_data.begin(), old_data.end(), extended_frame.begin()); + std::copy(frame.begin(), frame.end(), + extended_frame.begin() + old_data.size()); + std::copy(extended_frame.end() - old_data.size(), extended_frame.end(), + old_data.begin()); +} + +// Uses overlap-and-add to produce an output frame. +void OverlapAndAdd(rtc::ArrayView extended_frame, + rtc::ArrayView overlap_memory, + rtc::ArrayView output_frame) { + for (size_t i = 0; i < kOverlapSize; ++i) { + output_frame[i] = overlap_memory[i] + extended_frame[i]; + } + std::copy(extended_frame.begin() + kOverlapSize, + extended_frame.begin() + kNsFrameSize, + output_frame.begin() + kOverlapSize); + std::copy(extended_frame.begin() + kNsFrameSize, extended_frame.end(), + overlap_memory.begin()); +} + +// Produces a delayed frame. +void DelaySignal(rtc::ArrayView frame, + rtc::ArrayView delay_buffer, + rtc::ArrayView delayed_frame) { + constexpr size_t kSamplesFromFrame = kNsFrameSize - (kFftSize - kNsFrameSize); + std::copy(delay_buffer.begin(), delay_buffer.end(), delayed_frame.begin()); + std::copy(frame.begin(), frame.begin() + kSamplesFromFrame, + delayed_frame.begin() + delay_buffer.size()); + + std::copy(frame.begin() + kSamplesFromFrame, frame.end(), + delay_buffer.begin()); +} + +// Computes the energy of an extended frame. +float ComputeEnergyOfExtendedFrame(rtc::ArrayView x) { + float energy = 0.f; + for (float x_k : x) { + energy += x_k * x_k; + } + + return energy; +} + +// Computes the energy of an extended frame based on its subcomponents. +float ComputeEnergyOfExtendedFrame( + rtc::ArrayView frame, + rtc::ArrayView old_data) { + float energy = 0.f; + for (float v : old_data) { + energy += v * v; + } + for (float v : frame) { + energy += v * v; + } + + return energy; +} + +// Computes the magnitude spectrum based on an FFT output. +void ComputeMagnitudeSpectrum( + rtc::ArrayView real, + rtc::ArrayView imag, + rtc::ArrayView signal_spectrum) { + signal_spectrum[0] = fabsf(real[0]) + 1.f; + signal_spectrum[kFftSizeBy2Plus1 - 1] = + fabsf(real[kFftSizeBy2Plus1 - 1]) + 1.f; + + for (size_t i = 1; i < kFftSizeBy2Plus1 - 1; ++i) { + signal_spectrum[i] = + SqrtFastApproximation(real[i] * real[i] + imag[i] * imag[i]) + 1.f; + } +} + +// Compute prior and post SNR. +void ComputeSnr(rtc::ArrayView filter, + rtc::ArrayView prev_signal_spectrum, + rtc::ArrayView signal_spectrum, + rtc::ArrayView prev_noise_spectrum, + rtc::ArrayView noise_spectrum, + rtc::ArrayView prior_snr, + rtc::ArrayView post_snr) { + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + // Previous post SNR. + // Previous estimate: based on previous frame with gain filter. + float prev_estimate = prev_signal_spectrum[i] / + (prev_noise_spectrum[i] + 0.0001f) * filter[i]; + // Post SNR. + if (signal_spectrum[i] > noise_spectrum[i]) { + post_snr[i] = signal_spectrum[i] / (noise_spectrum[i] + 0.0001f) - 1.f; + } else { + post_snr[i] = 0.f; + } + // The directed decision estimate of the prior SNR is a sum the current and + // previous estimates. + prior_snr[i] = 0.98f * prev_estimate + (1.f - 0.98f) * post_snr[i]; + } +} + +// Computes the attenuating gain for the noise suppression of the upper bands. +float ComputeUpperBandsGain( + float minimum_attenuating_gain, + rtc::ArrayView filter, + rtc::ArrayView speech_probability, + rtc::ArrayView prev_analysis_signal_spectrum, + rtc::ArrayView signal_spectrum) { + // Average speech prob and filter gain for the end of the lowest band. + constexpr int kNumAvgBins = 32; + constexpr float kOneByNumAvgBins = 1.f / kNumAvgBins; + + float avg_prob_speech = 0.f; + float avg_filter_gain = 0.f; + for (size_t i = kFftSizeBy2Plus1 - kNumAvgBins - 1; i < kFftSizeBy2Plus1 - 1; + i++) { + avg_prob_speech += speech_probability[i]; + avg_filter_gain += filter[i]; + } + avg_prob_speech = avg_prob_speech * kOneByNumAvgBins; + avg_filter_gain = avg_filter_gain * kOneByNumAvgBins; + + // If the speech was suppressed by a component between Analyze and Process, an + // example being by an AEC, it should not be considered speech for the purpose + // of high band suppression. To that end, the speech probability is scaled + // accordingly. + float sum_analysis_spectrum = 0.f; + float sum_processing_spectrum = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + sum_analysis_spectrum += prev_analysis_signal_spectrum[i]; + sum_processing_spectrum += signal_spectrum[i]; + } + + // The magnitude spectrum computation enforces the spectrum to be strictly + // positive. + RTC_DCHECK_GT(sum_analysis_spectrum, 0.f); + avg_prob_speech *= sum_processing_spectrum / sum_analysis_spectrum; + + // Compute gain based on speech probability. + float gain = + 0.5f * (1.f + static_cast(tanh(2.f * avg_prob_speech - 1.f))); + + // Combine gain with low band gain. + if (avg_prob_speech >= 0.5f) { + gain = 0.25f * gain + 0.75f * avg_filter_gain; + } else { + gain = 0.5f * gain + 0.5f * avg_filter_gain; + } + + // Make sure gain is within flooring range. + return std::min(std::max(gain, minimum_attenuating_gain), 1.f); +} + +} // namespace + +NoiseSuppressor::ChannelState::ChannelState( + const SuppressionParams& suppression_params, + size_t num_bands) + : wiener_filter(suppression_params), + noise_estimator(suppression_params), + process_delay_memory(num_bands > 1 ? num_bands - 1 : 0) { + analyze_analysis_memory.fill(0.f); + prev_analysis_signal_spectrum.fill(1.f); + process_analysis_memory.fill(0.f); + process_synthesis_memory.fill(0.f); + for (auto& d : process_delay_memory) { + d.fill(0.f); + } +} + +NoiseSuppressor::NoiseSuppressor(const NsConfig& config, + size_t sample_rate_hz, + size_t num_channels) + : num_bands_(NumBandsForRate(sample_rate_hz)), + num_channels_(num_channels), + suppression_params_(config.target_level), + filter_bank_states_heap_(NumChannelsOnHeap(num_channels_)), + upper_band_gains_heap_(NumChannelsOnHeap(num_channels_)), + energies_before_filtering_heap_(NumChannelsOnHeap(num_channels_)), + gain_adjustments_heap_(NumChannelsOnHeap(num_channels_)), + channels_(num_channels_) { + for (size_t ch = 0; ch < num_channels_; ++ch) { + channels_[ch] = + std::make_unique(suppression_params_, num_bands_); + } +} + +void NoiseSuppressor::AggregateWienerFilters( + rtc::ArrayView filter) const { + rtc::ArrayView filter0 = + channels_[0]->wiener_filter.get_filter(); + std::copy(filter0.begin(), filter0.end(), filter.begin()); + + for (size_t ch = 1; ch < num_channels_; ++ch) { + rtc::ArrayView filter_ch = + channels_[ch]->wiener_filter.get_filter(); + + for (size_t k = 0; k < kFftSizeBy2Plus1; ++k) { + filter[k] = std::min(filter[k], filter_ch[k]); + } + } +} + +void NoiseSuppressor::Analyze(const AudioBuffer& audio) { + // Prepare the noise estimator for the analysis stage. + for (size_t ch = 0; ch < num_channels_; ++ch) { + channels_[ch]->noise_estimator.PrepareAnalysis(); + } + + // Check for zero frames. + bool zero_frame = true; + for (size_t ch = 0; ch < num_channels_; ++ch) { + rtc::ArrayView y_band0( + &audio.split_bands_const(ch)[0][0], kNsFrameSize); + float energy = ComputeEnergyOfExtendedFrame( + y_band0, channels_[ch]->analyze_analysis_memory); + if (energy > 0.f) { + zero_frame = false; + break; + } + } + + if (zero_frame) { + // We want to avoid updating statistics in this case: + // Updating feature statistics when we have zeros only will cause + // thresholds to move towards zero signal situations. This in turn has the + // effect that once the signal is "turned on" (non-zero values) everything + // will be treated as speech and there is no noise suppression effect. + // Depending on the duration of the inactive signal it takes a + // considerable amount of time for the system to learn what is noise and + // what is speech. + return; + } + + // Only update analysis counter for frames that are properly analyzed. + if (++num_analyzed_frames_ < 0) { + num_analyzed_frames_ = 0; + } + + // Analyze all channels. + for (size_t ch = 0; ch < num_channels_; ++ch) { + std::unique_ptr& ch_p = channels_[ch]; + rtc::ArrayView y_band0( + &audio.split_bands_const(ch)[0][0], kNsFrameSize); + + // Form an extended frame and apply analysis filter bank windowing. + std::array extended_frame; + FormExtendedFrame(y_band0, ch_p->analyze_analysis_memory, extended_frame); + ApplyFilterBankWindow(extended_frame); + + // Compute the magnitude spectrum. + std::array real; + std::array imag; + fft_.Fft(extended_frame, real, imag); + + std::array signal_spectrum; + ComputeMagnitudeSpectrum(real, imag, signal_spectrum); + + // Compute energies. + float signal_energy = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + signal_energy += real[i] * real[i] + imag[i] * imag[i]; + } + signal_energy /= kFftSizeBy2Plus1; + + float signal_spectral_sum = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + signal_spectral_sum += signal_spectrum[i]; + } + + // Estimate the noise spectra and the probability estimates of speech + // presence. + ch_p->noise_estimator.PreUpdate(num_analyzed_frames_, signal_spectrum, + signal_spectral_sum); + + std::array post_snr; + std::array prior_snr; + ComputeSnr(ch_p->wiener_filter.get_filter(), + ch_p->prev_analysis_signal_spectrum, signal_spectrum, + ch_p->noise_estimator.get_prev_noise_spectrum(), + ch_p->noise_estimator.get_noise_spectrum(), prior_snr, post_snr); + + ch_p->speech_probability_estimator.Update( + num_analyzed_frames_, prior_snr, post_snr, + ch_p->noise_estimator.get_conservative_noise_spectrum(), + signal_spectrum, signal_spectral_sum, signal_energy); + + ch_p->noise_estimator.PostUpdate( + ch_p->speech_probability_estimator.get_probability(), signal_spectrum); + + // Store the magnitude spectrum to make it avalilable for the process + // method. + std::copy(signal_spectrum.begin(), signal_spectrum.end(), + ch_p->prev_analysis_signal_spectrum.begin()); + } +} + +void NoiseSuppressor::Process(AudioBuffer* audio) { + // Select the space for storing data during the processing. + std::array filter_bank_states_stack; + rtc::ArrayView filter_bank_states( + filter_bank_states_stack.data(), num_channels_); + std::array upper_band_gains_stack; + rtc::ArrayView upper_band_gains(upper_band_gains_stack.data(), + num_channels_); + std::array energies_before_filtering_stack; + rtc::ArrayView energies_before_filtering( + energies_before_filtering_stack.data(), num_channels_); + std::array gain_adjustments_stack; + rtc::ArrayView gain_adjustments(gain_adjustments_stack.data(), + num_channels_); + if (NumChannelsOnHeap(num_channels_) > 0) { + // If the stack-allocated space is too small, use the heap for storing the + // data. + filter_bank_states = rtc::ArrayView( + filter_bank_states_heap_.data(), num_channels_); + upper_band_gains = + rtc::ArrayView(upper_band_gains_heap_.data(), num_channels_); + energies_before_filtering = rtc::ArrayView( + energies_before_filtering_heap_.data(), num_channels_); + gain_adjustments = + rtc::ArrayView(gain_adjustments_heap_.data(), num_channels_); + } + + // Compute the suppression filters for all channels. + for (size_t ch = 0; ch < num_channels_; ++ch) { + // Form an extended frame and apply analysis filter bank windowing. + rtc::ArrayView y_band0(&audio->split_bands(ch)[0][0], + kNsFrameSize); + + FormExtendedFrame(y_band0, channels_[ch]->process_analysis_memory, + filter_bank_states[ch].extended_frame); + + ApplyFilterBankWindow(filter_bank_states[ch].extended_frame); + + energies_before_filtering[ch] = + ComputeEnergyOfExtendedFrame(filter_bank_states[ch].extended_frame); + + // Perform filter bank analysis and compute the magnitude spectrum. + fft_.Fft(filter_bank_states[ch].extended_frame, filter_bank_states[ch].real, + filter_bank_states[ch].imag); + + std::array signal_spectrum; + ComputeMagnitudeSpectrum(filter_bank_states[ch].real, + filter_bank_states[ch].imag, signal_spectrum); + + // Compute the frequency domain gain filter for noise attenuation. + channels_[ch]->wiener_filter.Update( + num_analyzed_frames_, + channels_[ch]->noise_estimator.get_noise_spectrum(), + channels_[ch]->noise_estimator.get_prev_noise_spectrum(), + channels_[ch]->noise_estimator.get_parametric_noise_spectrum(), + signal_spectrum); + + if (num_bands_ > 1) { + // Compute the time-domain gain for attenuating the noise in the upper + // bands. + + upper_band_gains[ch] = ComputeUpperBandsGain( + suppression_params_.minimum_attenuating_gain, + channels_[ch]->wiener_filter.get_filter(), + channels_[ch]->speech_probability_estimator.get_probability(), + channels_[ch]->prev_analysis_signal_spectrum, signal_spectrum); + } + } + + // Aggregate the Wiener filters for all channels. + std::array filter_data; + rtc::ArrayView filter = filter_data; + if (num_channels_ == 1) { + filter = channels_[0]->wiener_filter.get_filter(); + } else { + AggregateWienerFilters(filter_data); + } + + for (size_t ch = 0; ch < num_channels_; ++ch) { + // Apply the filter to the lower band. + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + filter_bank_states[ch].real[i] *= filter[i]; + filter_bank_states[ch].imag[i] *= filter[i]; + } + } + + // Perform filter bank synthesis + for (size_t ch = 0; ch < num_channels_; ++ch) { + fft_.Ifft(filter_bank_states[ch].real, filter_bank_states[ch].imag, + filter_bank_states[ch].extended_frame); + } + + for (size_t ch = 0; ch < num_channels_; ++ch) { + const float energy_after_filtering = + ComputeEnergyOfExtendedFrame(filter_bank_states[ch].extended_frame); + + // Apply synthesis window. + ApplyFilterBankWindow(filter_bank_states[ch].extended_frame); + + // Compute the adjustment of the noise attenuation filter based on the + // effect of the attenuation. + gain_adjustments[ch] = + channels_[ch]->wiener_filter.ComputeOverallScalingFactor( + num_analyzed_frames_, + channels_[ch]->speech_probability_estimator.get_prior_probability(), + energies_before_filtering[ch], energy_after_filtering); + } + + // Select and apply adjustment of the noise attenuation filter based on the + // effect of the attenuation. + float gain_adjustment = gain_adjustments[0]; + for (size_t ch = 1; ch < num_channels_; ++ch) { + gain_adjustment = std::min(gain_adjustment, gain_adjustments[ch]); + } + for (size_t ch = 0; ch < num_channels_; ++ch) { + for (size_t i = 0; i < kFftSize; ++i) { + filter_bank_states[ch].extended_frame[i] = + gain_adjustment * filter_bank_states[ch].extended_frame[i]; + } + } + + // Use overlap-and-add to form the output frame of the lowest band. + for (size_t ch = 0; ch < num_channels_; ++ch) { + rtc::ArrayView y_band0(&audio->split_bands(ch)[0][0], + kNsFrameSize); + OverlapAndAdd(filter_bank_states[ch].extended_frame, + channels_[ch]->process_synthesis_memory, y_band0); + } + + if (num_bands_ > 1) { + // Select the noise attenuating gain to apply to the upper band. + float upper_band_gain = upper_band_gains[0]; + for (size_t ch = 1; ch < num_channels_; ++ch) { + upper_band_gain = std::min(upper_band_gain, upper_band_gains[ch]); + } + + // Process the upper bands. + for (size_t ch = 0; ch < num_channels_; ++ch) { + for (size_t b = 1; b < num_bands_; ++b) { + // Delay the upper bands to match the delay of the filterbank applied to + // the lowest band. + rtc::ArrayView y_band( + &audio->split_bands(ch)[b][0], kNsFrameSize); + std::array delayed_frame; + DelaySignal(y_band, channels_[ch]->process_delay_memory[b - 1], + delayed_frame); + + // Apply the time-domain noise-attenuating gain. + for (size_t j = 0; j < kNsFrameSize; j++) { + y_band[j] = upper_band_gain * delayed_frame[j]; + } + } + } + } + + // Limit the output the allowed range. + for (size_t ch = 0; ch < num_channels_; ++ch) { + for (size_t b = 0; b < num_bands_; ++b) { + rtc::ArrayView y_band(&audio->split_bands(ch)[b][0], + kNsFrameSize); + for (size_t j = 0; j < kNsFrameSize; j++) { + y_band[j] = std::min(std::max(y_band[j], -32768.f), 32767.f); + } + } + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/noise_suppressor.h b/webrtc/modules/audio_processing/ns/noise_suppressor.h new file mode 100644 index 0000000..d962886 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/noise_suppressor.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_NOISE_SUPPRESSOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_NOISE_SUPPRESSOR_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/ns/noise_estimator.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/ns_config.h" +#include "modules/audio_processing/ns/ns_fft.h" +#include "modules/audio_processing/ns/speech_probability_estimator.h" +#include "modules/audio_processing/ns/wiener_filter.h" + +namespace webrtc { + +// Class for suppressing noise in a signal. +class NoiseSuppressor { + public: + NoiseSuppressor(const NsConfig& config, + size_t sample_rate_hz, + size_t num_channels); + NoiseSuppressor(const NoiseSuppressor&) = delete; + NoiseSuppressor& operator=(const NoiseSuppressor&) = delete; + + // Analyses the signal (typically applied before the AEC to avoid analyzing + // any comfort noise signal). + void Analyze(const AudioBuffer& audio); + + // Applies noise suppression. + void Process(AudioBuffer* audio); + + private: + const size_t num_bands_; + const size_t num_channels_; + const SuppressionParams suppression_params_; + int32_t num_analyzed_frames_ = -1; + NrFft fft_; + + struct ChannelState { + ChannelState(const SuppressionParams& suppression_params, size_t num_bands); + + SpeechProbabilityEstimator speech_probability_estimator; + WienerFilter wiener_filter; + NoiseEstimator noise_estimator; + std::array prev_analysis_signal_spectrum; + std::array analyze_analysis_memory; + std::array process_analysis_memory; + std::array process_synthesis_memory; + std::vector> process_delay_memory; + }; + + struct FilterBankState { + std::array real; + std::array imag; + std::array extended_frame; + }; + + std::vector filter_bank_states_heap_; + std::vector upper_band_gains_heap_; + std::vector energies_before_filtering_heap_; + std::vector gain_adjustments_heap_; + std::vector> channels_; + + // Aggregates the Wiener filters into a single filter to use. + void AggregateWienerFilters( + rtc::ArrayView filter) const; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_NOISE_SUPPRESSOR_H_ diff --git a/webrtc/modules/audio_processing/ns/ns_common.h b/webrtc/modules/audio_processing/ns/ns_common.h new file mode 100644 index 0000000..d6149f7 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/ns_common.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_NS_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_NS_NS_COMMON_H_ + +#include + +namespace webrtc { + +constexpr size_t kFftSize = 256; +constexpr size_t kFftSizeBy2Plus1 = kFftSize / 2 + 1; +constexpr size_t kNsFrameSize = 160; +constexpr size_t kOverlapSize = kFftSize - kNsFrameSize; + +constexpr int kShortStartupPhaseBlocks = 50; +constexpr int kLongStartupPhaseBlocks = 200; +constexpr int kFeatureUpdateWindowSize = 500; + +constexpr float kLtrFeatureThr = 0.5f; +constexpr float kBinSizeLrt = 0.1f; +constexpr float kBinSizeSpecFlat = 0.05f; +constexpr float kBinSizeSpecDiff = 0.1f; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_NS_COMMON_H_ diff --git a/webrtc/modules/audio_processing/ns/ns_config.h b/webrtc/modules/audio_processing/ns/ns_config.h new file mode 100644 index 0000000..0a285e9 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/ns_config.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_NS_CONFIG_H_ +#define MODULES_AUDIO_PROCESSING_NS_NS_CONFIG_H_ + +namespace webrtc { + +// Config struct for the noise suppressor +struct NsConfig { + enum class SuppressionLevel { k6dB, k12dB, k18dB, k21dB }; + SuppressionLevel target_level = SuppressionLevel::k12dB; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_NS_CONFIG_H_ diff --git a/webrtc/modules/audio_processing/ns/ns_core.c b/webrtc/modules/audio_processing/ns/ns_core.c deleted file mode 100644 index 1d60914..0000000 --- a/webrtc/modules/audio_processing/ns/ns_core.c +++ /dev/null @@ -1,1416 +0,0 @@ -/* - * Copyright (c) 2012 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 -#include -#include -#include - -#include "webrtc/common_audio/fft4g.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/ns/include/noise_suppression.h" -#include "webrtc/modules/audio_processing/ns/ns_core.h" -#include "webrtc/modules/audio_processing/ns/windows_private.h" - -// Set Feature Extraction Parameters. -static void set_feature_extraction_parameters(NoiseSuppressionC* self) { - // Bin size of histogram. - self->featureExtractionParams.binSizeLrt = 0.1f; - self->featureExtractionParams.binSizeSpecFlat = 0.05f; - self->featureExtractionParams.binSizeSpecDiff = 0.1f; - - // Range of histogram over which LRT threshold is computed. - self->featureExtractionParams.rangeAvgHistLrt = 1.f; - - // Scale parameters: multiply dominant peaks of the histograms by scale factor - // to obtain thresholds for prior model. - // For LRT and spectral difference. - self->featureExtractionParams.factor1ModelPars = 1.2f; - // For spectral_flatness: used when noise is flatter than speech. - self->featureExtractionParams.factor2ModelPars = 0.9f; - - // Peak limit for spectral flatness (varies between 0 and 1). - self->featureExtractionParams.thresPosSpecFlat = 0.6f; - - // Limit on spacing of two highest peaks in histogram: spacing determined by - // bin size. - self->featureExtractionParams.limitPeakSpacingSpecFlat = - 2 * self->featureExtractionParams.binSizeSpecFlat; - self->featureExtractionParams.limitPeakSpacingSpecDiff = - 2 * self->featureExtractionParams.binSizeSpecDiff; - - // Limit on relevance of second peak. - self->featureExtractionParams.limitPeakWeightsSpecFlat = 0.5f; - self->featureExtractionParams.limitPeakWeightsSpecDiff = 0.5f; - - // Fluctuation limit of LRT feature. - self->featureExtractionParams.thresFluctLrt = 0.05f; - - // Limit on the max and min values for the feature thresholds. - self->featureExtractionParams.maxLrt = 1.f; - self->featureExtractionParams.minLrt = 0.2f; - - self->featureExtractionParams.maxSpecFlat = 0.95f; - self->featureExtractionParams.minSpecFlat = 0.1f; - - self->featureExtractionParams.maxSpecDiff = 1.f; - self->featureExtractionParams.minSpecDiff = 0.16f; - - // Criteria of weight of histogram peak to accept/reject feature. - self->featureExtractionParams.thresWeightSpecFlat = - (int)(0.3 * (self->modelUpdatePars[1])); // For spectral flatness. - self->featureExtractionParams.thresWeightSpecDiff = - (int)(0.3 * (self->modelUpdatePars[1])); // For spectral difference. -} - -// Initialize state. -int WebRtcNs_InitCore(NoiseSuppressionC* self, uint32_t fs) { - int i; - // Check for valid pointer. - if (self == NULL) { - return -1; - } - - // Initialization of struct. - if (fs == 8000 || fs == 16000 || fs == 32000 || fs == 48000) { - self->fs = fs; - } else { - return -1; - } - self->windShift = 0; - // We only support 10ms frames. - if (fs == 8000) { - self->blockLen = 80; - self->anaLen = 128; - self->window = kBlocks80w128; - } else { - self->blockLen = 160; - self->anaLen = 256; - self->window = kBlocks160w256; - } - self->magnLen = self->anaLen / 2 + 1; // Number of frequency bins. - - // Initialize FFT work arrays. - self->ip[0] = 0; // Setting this triggers initialization. - memset(self->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); - WebRtc_rdft(self->anaLen, 1, self->dataBuf, self->ip, self->wfft); - - memset(self->analyzeBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); - memset(self->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); - memset(self->syntBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); - - // For HB processing. - memset(self->dataBufHB, - 0, - sizeof(float) * NUM_HIGH_BANDS_MAX * ANAL_BLOCKL_MAX); - - // For quantile noise estimation. - memset(self->quantile, 0, sizeof(float) * HALF_ANAL_BLOCKL); - for (i = 0; i < SIMULT * HALF_ANAL_BLOCKL; i++) { - self->lquantile[i] = 8.f; - self->density[i] = 0.3f; - } - - for (i = 0; i < SIMULT; i++) { - self->counter[i] = - (int)floor((float)(END_STARTUP_LONG * (i + 1)) / (float)SIMULT); - } - - self->updates = 0; - - // Wiener filter initialization. - for (i = 0; i < HALF_ANAL_BLOCKL; i++) { - self->smooth[i] = 1.f; - } - - // Set the aggressiveness: default. - self->aggrMode = 0; - - // Initialize variables for new method. - self->priorSpeechProb = 0.5f; // Prior prob for speech/noise. - // Previous analyze mag spectrum. - memset(self->magnPrevAnalyze, 0, sizeof(float) * HALF_ANAL_BLOCKL); - // Previous process mag spectrum. - memset(self->magnPrevProcess, 0, sizeof(float) * HALF_ANAL_BLOCKL); - // Current noise-spectrum. - memset(self->noise, 0, sizeof(float) * HALF_ANAL_BLOCKL); - // Previous noise-spectrum. - memset(self->noisePrev, 0, sizeof(float) * HALF_ANAL_BLOCKL); - // Conservative noise spectrum estimate. - memset(self->magnAvgPause, 0, sizeof(float) * HALF_ANAL_BLOCKL); - // For estimation of HB in second pass. - memset(self->speechProb, 0, sizeof(float) * HALF_ANAL_BLOCKL); - // Initial average magnitude spectrum. - memset(self->initMagnEst, 0, sizeof(float) * HALF_ANAL_BLOCKL); - for (i = 0; i < HALF_ANAL_BLOCKL; i++) { - // Smooth LR (same as threshold). - self->logLrtTimeAvg[i] = LRT_FEATURE_THR; - } - - // Feature quantities. - // Spectral flatness (start on threshold). - self->featureData[0] = SF_FEATURE_THR; - self->featureData[1] = 0.f; // Spectral entropy: not used in this version. - self->featureData[2] = 0.f; // Spectral variance: not used in this version. - // Average LRT factor (start on threshold). - self->featureData[3] = LRT_FEATURE_THR; - // Spectral template diff (start on threshold). - self->featureData[4] = SF_FEATURE_THR; - self->featureData[5] = 0.f; // Normalization for spectral difference. - // Window time-average of input magnitude spectrum. - self->featureData[6] = 0.f; - - // Histogram quantities: used to estimate/update thresholds for features. - memset(self->histLrt, 0, sizeof(int) * HIST_PAR_EST); - memset(self->histSpecFlat, 0, sizeof(int) * HIST_PAR_EST); - memset(self->histSpecDiff, 0, sizeof(int) * HIST_PAR_EST); - - - self->blockInd = -1; // Frame counter. - // Default threshold for LRT feature. - self->priorModelPars[0] = LRT_FEATURE_THR; - // Threshold for spectral flatness: determined on-line. - self->priorModelPars[1] = 0.5f; - // sgn_map par for spectral measure: 1 for flatness measure. - self->priorModelPars[2] = 1.f; - // Threshold for template-difference feature: determined on-line. - self->priorModelPars[3] = 0.5f; - // Default weighting parameter for LRT feature. - self->priorModelPars[4] = 1.f; - // Default weighting parameter for spectral flatness feature. - self->priorModelPars[5] = 0.f; - // Default weighting parameter for spectral difference feature. - self->priorModelPars[6] = 0.f; - - // Update flag for parameters: - // 0 no update, 1 = update once, 2 = update every window. - self->modelUpdatePars[0] = 2; - self->modelUpdatePars[1] = 500; // Window for update. - // Counter for update of conservative noise spectrum. - self->modelUpdatePars[2] = 0; - // Counter if the feature thresholds are updated during the sequence. - self->modelUpdatePars[3] = self->modelUpdatePars[1]; - - self->signalEnergy = 0.0; - self->sumMagn = 0.0; - self->whiteNoiseLevel = 0.0; - self->pinkNoiseNumerator = 0.0; - self->pinkNoiseExp = 0.0; - - set_feature_extraction_parameters(self); - - // Default mode. - WebRtcNs_set_policy_core(self, 0); - - self->initFlag = 1; - return 0; -} - -// Estimate noise. -static void NoiseEstimation(NoiseSuppressionC* self, - float* magn, - float* noise) { - size_t i, s, offset; - float lmagn[HALF_ANAL_BLOCKL], delta; - - if (self->updates < END_STARTUP_LONG) { - self->updates++; - } - - for (i = 0; i < self->magnLen; i++) { - lmagn[i] = (float)log(magn[i]); - } - - // Loop over simultaneous estimates. - for (s = 0; s < SIMULT; s++) { - offset = s * self->magnLen; - - // newquantest(...) - for (i = 0; i < self->magnLen; i++) { - // Compute delta. - if (self->density[offset + i] > 1.0) { - delta = FACTOR * 1.f / self->density[offset + i]; - } else { - delta = FACTOR; - } - - // Update log quantile estimate. - if (lmagn[i] > self->lquantile[offset + i]) { - self->lquantile[offset + i] += - QUANTILE * delta / (float)(self->counter[s] + 1); - } else { - self->lquantile[offset + i] -= - (1.f - QUANTILE) * delta / (float)(self->counter[s] + 1); - } - - // Update density estimate. - if (fabs(lmagn[i] - self->lquantile[offset + i]) < WIDTH) { - self->density[offset + i] = - ((float)self->counter[s] * self->density[offset + i] + - 1.f / (2.f * WIDTH)) / - (float)(self->counter[s] + 1); - } - } // End loop over magnitude spectrum. - - if (self->counter[s] >= END_STARTUP_LONG) { - self->counter[s] = 0; - if (self->updates >= END_STARTUP_LONG) { - for (i = 0; i < self->magnLen; i++) { - self->quantile[i] = (float)exp(self->lquantile[offset + i]); - } - } - } - - self->counter[s]++; - } // End loop over simultaneous estimates. - - // Sequentially update the noise during startup. - if (self->updates < END_STARTUP_LONG) { - // Use the last "s" to get noise during startup that differ from zero. - for (i = 0; i < self->magnLen; i++) { - self->quantile[i] = (float)exp(self->lquantile[offset + i]); - } - } - - for (i = 0; i < self->magnLen; i++) { - noise[i] = self->quantile[i]; - } -} - -// Extract thresholds for feature parameters. -// Histograms are computed over some window size (given by -// self->modelUpdatePars[1]). -// Thresholds and weights are extracted every window. -// |flag| = 0 updates histogram only, |flag| = 1 computes the threshold/weights. -// Threshold and weights are returned in: self->priorModelPars. -static void FeatureParameterExtraction(NoiseSuppressionC* self, int flag) { - int i, useFeatureSpecFlat, useFeatureSpecDiff, numHistLrt; - int maxPeak1, maxPeak2; - int weightPeak1SpecFlat, weightPeak2SpecFlat, weightPeak1SpecDiff, - weightPeak2SpecDiff; - - float binMid, featureSum; - float posPeak1SpecFlat, posPeak2SpecFlat, posPeak1SpecDiff, posPeak2SpecDiff; - float fluctLrt, avgHistLrt, avgSquareHistLrt, avgHistLrtCompl; - - // 3 features: LRT, flatness, difference. - // lrt_feature = self->featureData[3]; - // flat_feature = self->featureData[0]; - // diff_feature = self->featureData[4]; - - // Update histograms. - if (flag == 0) { - // LRT - if ((self->featureData[3] < - HIST_PAR_EST * self->featureExtractionParams.binSizeLrt) && - (self->featureData[3] >= 0.0)) { - i = (int)(self->featureData[3] / - self->featureExtractionParams.binSizeLrt); - self->histLrt[i]++; - } - // Spectral flatness. - if ((self->featureData[0] < - HIST_PAR_EST * self->featureExtractionParams.binSizeSpecFlat) && - (self->featureData[0] >= 0.0)) { - i = (int)(self->featureData[0] / - self->featureExtractionParams.binSizeSpecFlat); - self->histSpecFlat[i]++; - } - // Spectral difference. - if ((self->featureData[4] < - HIST_PAR_EST * self->featureExtractionParams.binSizeSpecDiff) && - (self->featureData[4] >= 0.0)) { - i = (int)(self->featureData[4] / - self->featureExtractionParams.binSizeSpecDiff); - self->histSpecDiff[i]++; - } - } - - // Extract parameters for speech/noise probability. - if (flag == 1) { - // LRT feature: compute the average over - // self->featureExtractionParams.rangeAvgHistLrt. - avgHistLrt = 0.0; - avgHistLrtCompl = 0.0; - avgSquareHistLrt = 0.0; - numHistLrt = 0; - for (i = 0; i < HIST_PAR_EST; i++) { - binMid = ((float)i + 0.5f) * self->featureExtractionParams.binSizeLrt; - if (binMid <= self->featureExtractionParams.rangeAvgHistLrt) { - avgHistLrt += self->histLrt[i] * binMid; - numHistLrt += self->histLrt[i]; - } - avgSquareHistLrt += self->histLrt[i] * binMid * binMid; - avgHistLrtCompl += self->histLrt[i] * binMid; - } - if (numHistLrt > 0) { - avgHistLrt = avgHistLrt / ((float)numHistLrt); - } - avgHistLrtCompl = avgHistLrtCompl / ((float)self->modelUpdatePars[1]); - avgSquareHistLrt = avgSquareHistLrt / ((float)self->modelUpdatePars[1]); - fluctLrt = avgSquareHistLrt - avgHistLrt * avgHistLrtCompl; - // Get threshold for LRT feature. - if (fluctLrt < self->featureExtractionParams.thresFluctLrt) { - // Very low fluctuation, so likely noise. - self->priorModelPars[0] = self->featureExtractionParams.maxLrt; - } else { - self->priorModelPars[0] = - self->featureExtractionParams.factor1ModelPars * avgHistLrt; - // Check if value is within min/max range. - if (self->priorModelPars[0] < self->featureExtractionParams.minLrt) { - self->priorModelPars[0] = self->featureExtractionParams.minLrt; - } - if (self->priorModelPars[0] > self->featureExtractionParams.maxLrt) { - self->priorModelPars[0] = self->featureExtractionParams.maxLrt; - } - } - // Done with LRT feature. - - // For spectral flatness and spectral difference: compute the main peaks of - // histogram. - maxPeak1 = 0; - maxPeak2 = 0; - posPeak1SpecFlat = 0.0; - posPeak2SpecFlat = 0.0; - weightPeak1SpecFlat = 0; - weightPeak2SpecFlat = 0; - - // Peaks for flatness. - for (i = 0; i < HIST_PAR_EST; i++) { - binMid = - (i + 0.5f) * self->featureExtractionParams.binSizeSpecFlat; - if (self->histSpecFlat[i] > maxPeak1) { - // Found new "first" peak. - maxPeak2 = maxPeak1; - weightPeak2SpecFlat = weightPeak1SpecFlat; - posPeak2SpecFlat = posPeak1SpecFlat; - - maxPeak1 = self->histSpecFlat[i]; - weightPeak1SpecFlat = self->histSpecFlat[i]; - posPeak1SpecFlat = binMid; - } else if (self->histSpecFlat[i] > maxPeak2) { - // Found new "second" peak. - maxPeak2 = self->histSpecFlat[i]; - weightPeak2SpecFlat = self->histSpecFlat[i]; - posPeak2SpecFlat = binMid; - } - } - - // Compute two peaks for spectral difference. - maxPeak1 = 0; - maxPeak2 = 0; - posPeak1SpecDiff = 0.0; - posPeak2SpecDiff = 0.0; - weightPeak1SpecDiff = 0; - weightPeak2SpecDiff = 0; - // Peaks for spectral difference. - for (i = 0; i < HIST_PAR_EST; i++) { - binMid = - ((float)i + 0.5f) * self->featureExtractionParams.binSizeSpecDiff; - if (self->histSpecDiff[i] > maxPeak1) { - // Found new "first" peak. - maxPeak2 = maxPeak1; - weightPeak2SpecDiff = weightPeak1SpecDiff; - posPeak2SpecDiff = posPeak1SpecDiff; - - maxPeak1 = self->histSpecDiff[i]; - weightPeak1SpecDiff = self->histSpecDiff[i]; - posPeak1SpecDiff = binMid; - } else if (self->histSpecDiff[i] > maxPeak2) { - // Found new "second" peak. - maxPeak2 = self->histSpecDiff[i]; - weightPeak2SpecDiff = self->histSpecDiff[i]; - posPeak2SpecDiff = binMid; - } - } - - // For spectrum flatness feature. - useFeatureSpecFlat = 1; - // Merge the two peaks if they are close. - if ((fabs(posPeak2SpecFlat - posPeak1SpecFlat) < - self->featureExtractionParams.limitPeakSpacingSpecFlat) && - (weightPeak2SpecFlat > - self->featureExtractionParams.limitPeakWeightsSpecFlat * - weightPeak1SpecFlat)) { - weightPeak1SpecFlat += weightPeak2SpecFlat; - posPeak1SpecFlat = 0.5f * (posPeak1SpecFlat + posPeak2SpecFlat); - } - // Reject if weight of peaks is not large enough, or peak value too small. - if (weightPeak1SpecFlat < - self->featureExtractionParams.thresWeightSpecFlat || - posPeak1SpecFlat < self->featureExtractionParams.thresPosSpecFlat) { - useFeatureSpecFlat = 0; - } - // If selected, get the threshold. - if (useFeatureSpecFlat == 1) { - // Compute the threshold. - self->priorModelPars[1] = - self->featureExtractionParams.factor2ModelPars * posPeak1SpecFlat; - // Check if value is within min/max range. - if (self->priorModelPars[1] < self->featureExtractionParams.minSpecFlat) { - self->priorModelPars[1] = self->featureExtractionParams.minSpecFlat; - } - if (self->priorModelPars[1] > self->featureExtractionParams.maxSpecFlat) { - self->priorModelPars[1] = self->featureExtractionParams.maxSpecFlat; - } - } - // Done with flatness feature. - - // For template feature. - useFeatureSpecDiff = 1; - // Merge the two peaks if they are close. - if ((fabs(posPeak2SpecDiff - posPeak1SpecDiff) < - self->featureExtractionParams.limitPeakSpacingSpecDiff) && - (weightPeak2SpecDiff > - self->featureExtractionParams.limitPeakWeightsSpecDiff * - weightPeak1SpecDiff)) { - weightPeak1SpecDiff += weightPeak2SpecDiff; - posPeak1SpecDiff = 0.5f * (posPeak1SpecDiff + posPeak2SpecDiff); - } - // Get the threshold value. - self->priorModelPars[3] = - self->featureExtractionParams.factor1ModelPars * posPeak1SpecDiff; - // Reject if weight of peaks is not large enough. - if (weightPeak1SpecDiff < - self->featureExtractionParams.thresWeightSpecDiff) { - useFeatureSpecDiff = 0; - } - // Check if value is within min/max range. - if (self->priorModelPars[3] < self->featureExtractionParams.minSpecDiff) { - self->priorModelPars[3] = self->featureExtractionParams.minSpecDiff; - } - if (self->priorModelPars[3] > self->featureExtractionParams.maxSpecDiff) { - self->priorModelPars[3] = self->featureExtractionParams.maxSpecDiff; - } - // Done with spectral difference feature. - - // Don't use template feature if fluctuation of LRT feature is very low: - // most likely just noise state. - if (fluctLrt < self->featureExtractionParams.thresFluctLrt) { - useFeatureSpecDiff = 0; - } - - // Select the weights between the features. - // self->priorModelPars[4] is weight for LRT: always selected. - // self->priorModelPars[5] is weight for spectral flatness. - // self->priorModelPars[6] is weight for spectral difference. - featureSum = (float)(1 + useFeatureSpecFlat + useFeatureSpecDiff); - self->priorModelPars[4] = 1.f / featureSum; - self->priorModelPars[5] = ((float)useFeatureSpecFlat) / featureSum; - self->priorModelPars[6] = ((float)useFeatureSpecDiff) / featureSum; - - // Set hists to zero for next update. - if (self->modelUpdatePars[0] >= 1) { - for (i = 0; i < HIST_PAR_EST; i++) { - self->histLrt[i] = 0; - self->histSpecFlat[i] = 0; - self->histSpecDiff[i] = 0; - } - } - } // End of flag == 1. -} - -// Compute spectral flatness on input spectrum. -// |magnIn| is the magnitude spectrum. -// Spectral flatness is returned in self->featureData[0]. -static void ComputeSpectralFlatness(NoiseSuppressionC* self, - const float* magnIn) { - size_t i; - size_t shiftLP = 1; // Option to remove first bin(s) from spectral measures. - float avgSpectralFlatnessNum, avgSpectralFlatnessDen, spectralTmp; - - // Compute spectral measures. - // For flatness. - avgSpectralFlatnessNum = 0.0; - avgSpectralFlatnessDen = self->sumMagn; - for (i = 0; i < shiftLP; i++) { - avgSpectralFlatnessDen -= magnIn[i]; - } - // Compute log of ratio of the geometric to arithmetic mean: check for log(0) - // case. - for (i = shiftLP; i < self->magnLen; i++) { - if (magnIn[i] > 0.0) { - avgSpectralFlatnessNum += (float)log(magnIn[i]); - } else { - self->featureData[0] -= SPECT_FL_TAVG * self->featureData[0]; - return; - } - } - // Normalize. - avgSpectralFlatnessDen = avgSpectralFlatnessDen / self->magnLen; - avgSpectralFlatnessNum = avgSpectralFlatnessNum / self->magnLen; - - // Ratio and inverse log: check for case of log(0). - spectralTmp = (float)exp(avgSpectralFlatnessNum) / avgSpectralFlatnessDen; - - // Time-avg update of spectral flatness feature. - self->featureData[0] += SPECT_FL_TAVG * (spectralTmp - self->featureData[0]); - // Done with flatness feature. -} - -// Compute prior and post SNR based on quantile noise estimation. -// Compute DD estimate of prior SNR. -// Inputs: -// * |magn| is the signal magnitude spectrum estimate. -// * |noise| is the magnitude noise spectrum estimate. -// Outputs: -// * |snrLocPrior| is the computed prior SNR. -// * |snrLocPost| is the computed post SNR. -static void ComputeSnr(const NoiseSuppressionC* self, - const float* magn, - const float* noise, - float* snrLocPrior, - float* snrLocPost) { - size_t i; - - for (i = 0; i < self->magnLen; i++) { - // Previous post SNR. - // Previous estimate: based on previous frame with gain filter. - float previousEstimateStsa = self->magnPrevAnalyze[i] / - (self->noisePrev[i] + 0.0001f) * self->smooth[i]; - // Post SNR. - snrLocPost[i] = 0.f; - if (magn[i] > noise[i]) { - snrLocPost[i] = magn[i] / (noise[i] + 0.0001f) - 1.f; - } - // DD estimate is sum of two terms: current estimate and previous estimate. - // Directed decision update of snrPrior. - snrLocPrior[i] = - DD_PR_SNR * previousEstimateStsa + (1.f - DD_PR_SNR) * snrLocPost[i]; - } // End of loop over frequencies. -} - -// Compute the difference measure between input spectrum and a template/learned -// noise spectrum. -// |magnIn| is the input spectrum. -// The reference/template spectrum is self->magnAvgPause[i]. -// Returns (normalized) spectral difference in self->featureData[4]. -static void ComputeSpectralDifference(NoiseSuppressionC* self, - const float* magnIn) { - // avgDiffNormMagn = var(magnIn) - cov(magnIn, magnAvgPause)^2 / - // var(magnAvgPause) - size_t i; - float avgPause, avgMagn, covMagnPause, varPause, varMagn, avgDiffNormMagn; - - avgPause = 0.0; - avgMagn = self->sumMagn; - // Compute average quantities. - for (i = 0; i < self->magnLen; i++) { - // Conservative smooth noise spectrum from pause frames. - avgPause += self->magnAvgPause[i]; - } - avgPause /= self->magnLen; - avgMagn /= self->magnLen; - - covMagnPause = 0.0; - varPause = 0.0; - varMagn = 0.0; - // Compute variance and covariance quantities. - for (i = 0; i < self->magnLen; i++) { - covMagnPause += (magnIn[i] - avgMagn) * (self->magnAvgPause[i] - avgPause); - varPause += - (self->magnAvgPause[i] - avgPause) * (self->magnAvgPause[i] - avgPause); - varMagn += (magnIn[i] - avgMagn) * (magnIn[i] - avgMagn); - } - covMagnPause /= self->magnLen; - varPause /= self->magnLen; - varMagn /= self->magnLen; - // Update of average magnitude spectrum. - self->featureData[6] += self->signalEnergy; - - avgDiffNormMagn = - varMagn - (covMagnPause * covMagnPause) / (varPause + 0.0001f); - // Normalize and compute time-avg update of difference feature. - avgDiffNormMagn = (float)(avgDiffNormMagn / (self->featureData[5] + 0.0001f)); - self->featureData[4] += - SPECT_DIFF_TAVG * (avgDiffNormMagn - self->featureData[4]); -} - -// Compute speech/noise probability. -// Speech/noise probability is returned in |probSpeechFinal|. -// |magn| is the input magnitude spectrum. -// |noise| is the noise spectrum. -// |snrLocPrior| is the prior SNR for each frequency. -// |snrLocPost| is the post SNR for each frequency. -static void SpeechNoiseProb(NoiseSuppressionC* self, - float* probSpeechFinal, - const float* snrLocPrior, - const float* snrLocPost) { - size_t i; - int sgnMap; - float invLrt, gainPrior, indPrior; - float logLrtTimeAvgKsum, besselTmp; - float indicator0, indicator1, indicator2; - float tmpFloat1, tmpFloat2; - float weightIndPrior0, weightIndPrior1, weightIndPrior2; - float threshPrior0, threshPrior1, threshPrior2; - float widthPrior, widthPrior0, widthPrior1, widthPrior2; - - widthPrior0 = WIDTH_PR_MAP; - // Width for pause region: lower range, so increase width in tanh map. - widthPrior1 = 2.f * WIDTH_PR_MAP; - widthPrior2 = 2.f * WIDTH_PR_MAP; // For spectral-difference measure. - - // Threshold parameters for features. - threshPrior0 = self->priorModelPars[0]; - threshPrior1 = self->priorModelPars[1]; - threshPrior2 = self->priorModelPars[3]; - - // Sign for flatness feature. - sgnMap = (int)(self->priorModelPars[2]); - - // Weight parameters for features. - weightIndPrior0 = self->priorModelPars[4]; - weightIndPrior1 = self->priorModelPars[5]; - weightIndPrior2 = self->priorModelPars[6]; - - // Compute feature based on average LR factor. - // This is the average over all frequencies of the smooth log LRT. - logLrtTimeAvgKsum = 0.0; - for (i = 0; i < self->magnLen; i++) { - tmpFloat1 = 1.f + 2.f * snrLocPrior[i]; - tmpFloat2 = 2.f * snrLocPrior[i] / (tmpFloat1 + 0.0001f); - besselTmp = (snrLocPost[i] + 1.f) * tmpFloat2; - self->logLrtTimeAvg[i] += - LRT_TAVG * (besselTmp - (float)log(tmpFloat1) - self->logLrtTimeAvg[i]); - logLrtTimeAvgKsum += self->logLrtTimeAvg[i]; - } - logLrtTimeAvgKsum = (float)logLrtTimeAvgKsum / (self->magnLen); - self->featureData[3] = logLrtTimeAvgKsum; - // Done with computation of LR factor. - - // Compute the indicator functions. - // Average LRT feature. - widthPrior = widthPrior0; - // Use larger width in tanh map for pause regions. - if (logLrtTimeAvgKsum < threshPrior0) { - widthPrior = widthPrior1; - } - // Compute indicator function: sigmoid map. - indicator0 = - 0.5f * - ((float)tanh(widthPrior * (logLrtTimeAvgKsum - threshPrior0)) + 1.f); - - // Spectral flatness feature. - tmpFloat1 = self->featureData[0]; - widthPrior = widthPrior0; - // Use larger width in tanh map for pause regions. - if (sgnMap == 1 && (tmpFloat1 > threshPrior1)) { - widthPrior = widthPrior1; - } - if (sgnMap == -1 && (tmpFloat1 < threshPrior1)) { - widthPrior = widthPrior1; - } - // Compute indicator function: sigmoid map. - indicator1 = - 0.5f * - ((float)tanh((float)sgnMap * widthPrior * (threshPrior1 - tmpFloat1)) + - 1.f); - - // For template spectrum-difference. - tmpFloat1 = self->featureData[4]; - widthPrior = widthPrior0; - // Use larger width in tanh map for pause regions. - if (tmpFloat1 < threshPrior2) { - widthPrior = widthPrior2; - } - // Compute indicator function: sigmoid map. - indicator2 = - 0.5f * ((float)tanh(widthPrior * (tmpFloat1 - threshPrior2)) + 1.f); - - // Combine the indicator function with the feature weights. - indPrior = weightIndPrior0 * indicator0 + weightIndPrior1 * indicator1 + - weightIndPrior2 * indicator2; - // Done with computing indicator function. - - // Compute the prior probability. - self->priorSpeechProb += PRIOR_UPDATE * (indPrior - self->priorSpeechProb); - // Make sure probabilities are within range: keep floor to 0.01. - if (self->priorSpeechProb > 1.f) { - self->priorSpeechProb = 1.f; - } - if (self->priorSpeechProb < 0.01f) { - self->priorSpeechProb = 0.01f; - } - - // Final speech probability: combine prior model with LR factor:. - gainPrior = (1.f - self->priorSpeechProb) / (self->priorSpeechProb + 0.0001f); - for (i = 0; i < self->magnLen; i++) { - invLrt = (float)exp(-self->logLrtTimeAvg[i]); - invLrt = (float)gainPrior * invLrt; - probSpeechFinal[i] = 1.f / (1.f + invLrt); - } -} - -// Update the noise features. -// Inputs: -// * |magn| is the signal magnitude spectrum estimate. -// * |updateParsFlag| is an update flag for parameters. -static void FeatureUpdate(NoiseSuppressionC* self, - const float* magn, - int updateParsFlag) { - // Compute spectral flatness on input spectrum. - ComputeSpectralFlatness(self, magn); - // Compute difference of input spectrum with learned/estimated noise spectrum. - ComputeSpectralDifference(self, magn); - // Compute histograms for parameter decisions (thresholds and weights for - // features). - // Parameters are extracted once every window time. - // (=self->modelUpdatePars[1]) - if (updateParsFlag >= 1) { - // Counter update. - self->modelUpdatePars[3]--; - // Update histogram. - if (self->modelUpdatePars[3] > 0) { - FeatureParameterExtraction(self, 0); - } - // Compute model parameters. - if (self->modelUpdatePars[3] == 0) { - FeatureParameterExtraction(self, 1); - self->modelUpdatePars[3] = self->modelUpdatePars[1]; - // If wish to update only once, set flag to zero. - if (updateParsFlag == 1) { - self->modelUpdatePars[0] = 0; - } else { - // Update every window: - // Get normalization for spectral difference for next window estimate. - self->featureData[6] = - self->featureData[6] / ((float)self->modelUpdatePars[1]); - self->featureData[5] = - 0.5f * (self->featureData[6] + self->featureData[5]); - self->featureData[6] = 0.f; - } - } - } -} - -// Update the noise estimate. -// Inputs: -// * |magn| is the signal magnitude spectrum estimate. -// * |snrLocPrior| is the prior SNR. -// * |snrLocPost| is the post SNR. -// Output: -// * |noise| is the updated noise magnitude spectrum estimate. -static void UpdateNoiseEstimate(NoiseSuppressionC* self, - const float* magn, - const float* snrLocPrior, - const float* snrLocPost, - float* noise) { - size_t i; - float probSpeech, probNonSpeech; - // Time-avg parameter for noise update. - float gammaNoiseTmp = NOISE_UPDATE; - float gammaNoiseOld; - float noiseUpdateTmp; - - for (i = 0; i < self->magnLen; i++) { - probSpeech = self->speechProb[i]; - probNonSpeech = 1.f - probSpeech; - // Temporary noise update: - // Use it for speech frames if update value is less than previous. - noiseUpdateTmp = gammaNoiseTmp * self->noisePrev[i] + - (1.f - gammaNoiseTmp) * (probNonSpeech * magn[i] + - probSpeech * self->noisePrev[i]); - // Time-constant based on speech/noise state. - gammaNoiseOld = gammaNoiseTmp; - gammaNoiseTmp = NOISE_UPDATE; - // Increase gamma (i.e., less noise update) for frame likely to be speech. - if (probSpeech > PROB_RANGE) { - gammaNoiseTmp = SPEECH_UPDATE; - } - // Conservative noise update. - if (probSpeech < PROB_RANGE) { - self->magnAvgPause[i] += GAMMA_PAUSE * (magn[i] - self->magnAvgPause[i]); - } - // Noise update. - if (gammaNoiseTmp == gammaNoiseOld) { - noise[i] = noiseUpdateTmp; - } else { - noise[i] = gammaNoiseTmp * self->noisePrev[i] + - (1.f - gammaNoiseTmp) * (probNonSpeech * magn[i] + - probSpeech * self->noisePrev[i]); - // Allow for noise update downwards: - // If noise update decreases the noise, it is safe, so allow it to - // happen. - if (noiseUpdateTmp < noise[i]) { - noise[i] = noiseUpdateTmp; - } - } - } // End of freq loop. -} - -// Updates |buffer| with a new |frame|. -// Inputs: -// * |frame| is a new speech frame or NULL for setting to zero. -// * |frame_length| is the length of the new frame. -// * |buffer_length| is the length of the buffer. -// Output: -// * |buffer| is the updated buffer. -static void UpdateBuffer(const float* frame, - size_t frame_length, - size_t buffer_length, - float* buffer) { - assert(buffer_length < 2 * frame_length); - - memcpy(buffer, - buffer + frame_length, - sizeof(*buffer) * (buffer_length - frame_length)); - if (frame) { - memcpy(buffer + buffer_length - frame_length, - frame, - sizeof(*buffer) * frame_length); - } else { - memset(buffer + buffer_length - frame_length, - 0, - sizeof(*buffer) * frame_length); - } -} - -// Transforms the signal from time to frequency domain. -// Inputs: -// * |time_data| is the signal in the time domain. -// * |time_data_length| is the length of the analysis buffer. -// * |magnitude_length| is the length of the spectrum magnitude, which equals -// the length of both |real| and |imag| (time_data_length / 2 + 1). -// Outputs: -// * |time_data| is the signal in the frequency domain. -// * |real| is the real part of the frequency domain. -// * |imag| is the imaginary part of the frequency domain. -// * |magn| is the calculated signal magnitude in the frequency domain. -static void FFT(NoiseSuppressionC* self, - float* time_data, - size_t time_data_length, - size_t magnitude_length, - float* real, - float* imag, - float* magn) { - size_t i; - - assert(magnitude_length == time_data_length / 2 + 1); - - WebRtc_rdft(time_data_length, 1, time_data, self->ip, self->wfft); - - imag[0] = 0; - real[0] = time_data[0]; - magn[0] = fabsf(real[0]) + 1.f; - imag[magnitude_length - 1] = 0; - real[magnitude_length - 1] = time_data[1]; - magn[magnitude_length - 1] = fabsf(real[magnitude_length - 1]) + 1.f; - for (i = 1; i < magnitude_length - 1; ++i) { - real[i] = time_data[2 * i]; - imag[i] = time_data[2 * i + 1]; - // Magnitude spectrum. - magn[i] = sqrtf(real[i] * real[i] + imag[i] * imag[i]) + 1.f; - } -} - -// Transforms the signal from frequency to time domain. -// Inputs: -// * |real| is the real part of the frequency domain. -// * |imag| is the imaginary part of the frequency domain. -// * |magnitude_length| is the length of the spectrum magnitude, which equals -// the length of both |real| and |imag|. -// * |time_data_length| is the length of the analysis buffer -// (2 * (magnitude_length - 1)). -// Output: -// * |time_data| is the signal in the time domain. -static void IFFT(NoiseSuppressionC* self, - const float* real, - const float* imag, - size_t magnitude_length, - size_t time_data_length, - float* time_data) { - size_t i; - - assert(time_data_length == 2 * (magnitude_length - 1)); - - time_data[0] = real[0]; - time_data[1] = real[magnitude_length - 1]; - for (i = 1; i < magnitude_length - 1; ++i) { - time_data[2 * i] = real[i]; - time_data[2 * i + 1] = imag[i]; - } - WebRtc_rdft(time_data_length, -1, time_data, self->ip, self->wfft); - - for (i = 0; i < time_data_length; ++i) { - time_data[i] *= 2.f / time_data_length; // FFT scaling. - } -} - -// Calculates the energy of a buffer. -// Inputs: -// * |buffer| is the buffer over which the energy is calculated. -// * |length| is the length of the buffer. -// Returns the calculated energy. -static float Energy(const float* buffer, size_t length) { - size_t i; - float energy = 0.f; - - for (i = 0; i < length; ++i) { - energy += buffer[i] * buffer[i]; - } - - return energy; -} - -// Windows a buffer. -// Inputs: -// * |window| is the window by which to multiply. -// * |data| is the data without windowing. -// * |length| is the length of the window and data. -// Output: -// * |data_windowed| is the windowed data. -static void Windowing(const float* window, - const float* data, - size_t length, - float* data_windowed) { - size_t i; - - for (i = 0; i < length; ++i) { - data_windowed[i] = window[i] * data[i]; - } -} - -// Estimate prior SNR decision-directed and compute DD based Wiener Filter. -// Input: -// * |magn| is the signal magnitude spectrum estimate. -// Output: -// * |theFilter| is the frequency response of the computed Wiener filter. -static void ComputeDdBasedWienerFilter(const NoiseSuppressionC* self, - const float* magn, - float* theFilter) { - size_t i; - float snrPrior, previousEstimateStsa, currentEstimateStsa; - - for (i = 0; i < self->magnLen; i++) { - // Previous estimate: based on previous frame with gain filter. - previousEstimateStsa = self->magnPrevProcess[i] / - (self->noisePrev[i] + 0.0001f) * self->smooth[i]; - // Post and prior SNR. - currentEstimateStsa = 0.f; - if (magn[i] > self->noise[i]) { - currentEstimateStsa = magn[i] / (self->noise[i] + 0.0001f) - 1.f; - } - // DD estimate is sum of two terms: current estimate and previous estimate. - // Directed decision update of |snrPrior|. - snrPrior = DD_PR_SNR * previousEstimateStsa + - (1.f - DD_PR_SNR) * currentEstimateStsa; - // Gain filter. - theFilter[i] = snrPrior / (self->overdrive + snrPrior); - } // End of loop over frequencies. -} - -// Changes the aggressiveness of the noise suppression method. -// |mode| = 0 is mild (6dB), |mode| = 1 is medium (10dB) and |mode| = 2 is -// aggressive (15dB). -// Returns 0 on success and -1 otherwise. -int WebRtcNs_set_policy_core(NoiseSuppressionC* self, int mode) { - // Allow for modes: 0, 1, 2, 3. - if (mode < 0 || mode > 3) { - return (-1); - } - - self->aggrMode = mode; - if (mode == 0) { - self->overdrive = 1.f; - self->denoiseBound = 0.5f; - self->gainmap = 0; - } else if (mode == 1) { - // self->overdrive = 1.25f; - self->overdrive = 1.f; - self->denoiseBound = 0.25f; - self->gainmap = 1; - } else if (mode == 2) { - // self->overdrive = 1.25f; - self->overdrive = 1.1f; - self->denoiseBound = 0.125f; - self->gainmap = 1; - } else if (mode == 3) { - // self->overdrive = 1.3f; - self->overdrive = 1.25f; - self->denoiseBound = 0.09f; - self->gainmap = 1; - } - return 0; -} - -void WebRtcNs_AnalyzeCore(NoiseSuppressionC* self, const float* speechFrame) { - size_t i; - const size_t kStartBand = 5; // Skip first frequency bins during estimation. - int updateParsFlag; - float energy; - float signalEnergy = 0.f; - float sumMagn = 0.f; - float tmpFloat1, tmpFloat2, tmpFloat3; - float winData[ANAL_BLOCKL_MAX]; - float magn[HALF_ANAL_BLOCKL], noise[HALF_ANAL_BLOCKL]; - float snrLocPost[HALF_ANAL_BLOCKL], snrLocPrior[HALF_ANAL_BLOCKL]; - float real[ANAL_BLOCKL_MAX], imag[HALF_ANAL_BLOCKL]; - // Variables during startup. - float sum_log_i = 0.0; - float sum_log_i_square = 0.0; - float sum_log_magn = 0.0; - float sum_log_i_log_magn = 0.0; - float parametric_exp = 0.0; - float parametric_num = 0.0; - - // Check that initiation has been done. - assert(self->initFlag == 1); - updateParsFlag = self->modelUpdatePars[0]; - - // Update analysis buffer for L band. - UpdateBuffer(speechFrame, self->blockLen, self->anaLen, self->analyzeBuf); - - Windowing(self->window, self->analyzeBuf, self->anaLen, winData); - energy = Energy(winData, self->anaLen); - if (energy == 0.0) { - // We want to avoid updating statistics in this case: - // Updating feature statistics when we have zeros only will cause - // thresholds to move towards zero signal situations. This in turn has the - // effect that once the signal is "turned on" (non-zero values) everything - // will be treated as speech and there is no noise suppression effect. - // Depending on the duration of the inactive signal it takes a - // considerable amount of time for the system to learn what is noise and - // what is speech. - return; - } - - self->blockInd++; // Update the block index only when we process a block. - - FFT(self, winData, self->anaLen, self->magnLen, real, imag, magn); - - for (i = 0; i < self->magnLen; i++) { - signalEnergy += real[i] * real[i] + imag[i] * imag[i]; - sumMagn += magn[i]; - if (self->blockInd < END_STARTUP_SHORT) { - if (i >= kStartBand) { - tmpFloat2 = logf((float)i); - sum_log_i += tmpFloat2; - sum_log_i_square += tmpFloat2 * tmpFloat2; - tmpFloat1 = logf(magn[i]); - sum_log_magn += tmpFloat1; - sum_log_i_log_magn += tmpFloat2 * tmpFloat1; - } - } - } - signalEnergy /= self->magnLen; - self->signalEnergy = signalEnergy; - self->sumMagn = sumMagn; - - // Quantile noise estimate. - NoiseEstimation(self, magn, noise); - // Compute simplified noise model during startup. - if (self->blockInd < END_STARTUP_SHORT) { - // Estimate White noise. - self->whiteNoiseLevel += sumMagn / self->magnLen * self->overdrive; - // Estimate Pink noise parameters. - tmpFloat1 = sum_log_i_square * (self->magnLen - kStartBand); - tmpFloat1 -= (sum_log_i * sum_log_i); - tmpFloat2 = - (sum_log_i_square * sum_log_magn - sum_log_i * sum_log_i_log_magn); - tmpFloat3 = tmpFloat2 / tmpFloat1; - // Constrain the estimated spectrum to be positive. - if (tmpFloat3 < 0.f) { - tmpFloat3 = 0.f; - } - self->pinkNoiseNumerator += tmpFloat3; - tmpFloat2 = (sum_log_i * sum_log_magn); - tmpFloat2 -= (self->magnLen - kStartBand) * sum_log_i_log_magn; - tmpFloat3 = tmpFloat2 / tmpFloat1; - // Constrain the pink noise power to be in the interval [0, 1]. - if (tmpFloat3 < 0.f) { - tmpFloat3 = 0.f; - } - if (tmpFloat3 > 1.f) { - tmpFloat3 = 1.f; - } - self->pinkNoiseExp += tmpFloat3; - - // Calculate frequency independent parts of parametric noise estimate. - if (self->pinkNoiseExp > 0.f) { - // Use pink noise estimate. - parametric_num = - expf(self->pinkNoiseNumerator / (float)(self->blockInd + 1)); - parametric_num *= (float)(self->blockInd + 1); - parametric_exp = self->pinkNoiseExp / (float)(self->blockInd + 1); - } - for (i = 0; i < self->magnLen; i++) { - // Estimate the background noise using the white and pink noise - // parameters. - if (self->pinkNoiseExp == 0.f) { - // Use white noise estimate. - self->parametricNoise[i] = self->whiteNoiseLevel; - } else { - // Use pink noise estimate. - float use_band = (float)(i < kStartBand ? kStartBand : i); - self->parametricNoise[i] = - parametric_num / powf(use_band, parametric_exp); - } - // Weight quantile noise with modeled noise. - noise[i] *= (self->blockInd); - tmpFloat2 = - self->parametricNoise[i] * (END_STARTUP_SHORT - self->blockInd); - noise[i] += (tmpFloat2 / (float)(self->blockInd + 1)); - noise[i] /= END_STARTUP_SHORT; - } - } - // Compute average signal during END_STARTUP_LONG time: - // used to normalize spectral difference measure. - if (self->blockInd < END_STARTUP_LONG) { - self->featureData[5] *= self->blockInd; - self->featureData[5] += signalEnergy; - self->featureData[5] /= (self->blockInd + 1); - } - - // Post and prior SNR needed for SpeechNoiseProb. - ComputeSnr(self, magn, noise, snrLocPrior, snrLocPost); - - FeatureUpdate(self, magn, updateParsFlag); - SpeechNoiseProb(self, self->speechProb, snrLocPrior, snrLocPost); - UpdateNoiseEstimate(self, magn, snrLocPrior, snrLocPost, noise); - - // Keep track of noise spectrum for next frame. - memcpy(self->noise, noise, sizeof(*noise) * self->magnLen); - memcpy(self->magnPrevAnalyze, magn, sizeof(*magn) * self->magnLen); -} - -void WebRtcNs_ProcessCore(NoiseSuppressionC* self, - const float* const* speechFrame, - size_t num_bands, - float* const* outFrame) { - // Main routine for noise reduction. - int flagHB = 0; - size_t i, j; - - float energy1, energy2, gain, factor, factor1, factor2; - float fout[BLOCKL_MAX]; - float winData[ANAL_BLOCKL_MAX]; - float magn[HALF_ANAL_BLOCKL]; - float theFilter[HALF_ANAL_BLOCKL], theFilterTmp[HALF_ANAL_BLOCKL]; - float real[ANAL_BLOCKL_MAX], imag[HALF_ANAL_BLOCKL]; - - // SWB variables. - int deltaBweHB = 1; - int deltaGainHB = 1; - float decayBweHB = 1.0; - float gainMapParHB = 1.0; - float gainTimeDomainHB = 1.0; - float avgProbSpeechHB, avgProbSpeechHBTmp, avgFilterGainHB, gainModHB; - float sumMagnAnalyze, sumMagnProcess; - - // Check that initiation has been done. - assert(self->initFlag == 1); - assert((num_bands - 1) <= NUM_HIGH_BANDS_MAX); - - const float* const* speechFrameHB = NULL; - float* const* outFrameHB = NULL; - size_t num_high_bands = 0; - if (num_bands > 1) { - speechFrameHB = &speechFrame[1]; - outFrameHB = &outFrame[1]; - num_high_bands = num_bands - 1; - flagHB = 1; - // Range for averaging low band quantities for H band gain. - deltaBweHB = (int)self->magnLen / 4; - deltaGainHB = deltaBweHB; - } - - // Update analysis buffer for L band. - UpdateBuffer(speechFrame[0], self->blockLen, self->anaLen, self->dataBuf); - - if (flagHB == 1) { - // Update analysis buffer for H bands. - for (i = 0; i < num_high_bands; ++i) { - UpdateBuffer(speechFrameHB[i], - self->blockLen, - self->anaLen, - self->dataBufHB[i]); - } - } - - Windowing(self->window, self->dataBuf, self->anaLen, winData); - energy1 = Energy(winData, self->anaLen); - if (energy1 == 0.0) { - // Synthesize the special case of zero input. - // Read out fully processed segment. - for (i = self->windShift; i < self->blockLen + self->windShift; i++) { - fout[i - self->windShift] = self->syntBuf[i]; - } - // Update synthesis buffer. - UpdateBuffer(NULL, self->blockLen, self->anaLen, self->syntBuf); - - for (i = 0; i < self->blockLen; ++i) - outFrame[0][i] = - WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, fout[i], WEBRTC_SPL_WORD16_MIN); - - // For time-domain gain of HB. - if (flagHB == 1) { - for (i = 0; i < num_high_bands; ++i) { - for (j = 0; j < self->blockLen; ++j) { - outFrameHB[i][j] = WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, - self->dataBufHB[i][j], - WEBRTC_SPL_WORD16_MIN); - } - } - } - - return; - } - - FFT(self, winData, self->anaLen, self->magnLen, real, imag, magn); - - if (self->blockInd < END_STARTUP_SHORT) { - for (i = 0; i < self->magnLen; i++) { - self->initMagnEst[i] += magn[i]; - } - } - - ComputeDdBasedWienerFilter(self, magn, theFilter); - - for (i = 0; i < self->magnLen; i++) { - // Flooring bottom. - if (theFilter[i] < self->denoiseBound) { - theFilter[i] = self->denoiseBound; - } - // Flooring top. - if (theFilter[i] > 1.f) { - theFilter[i] = 1.f; - } - if (self->blockInd < END_STARTUP_SHORT) { - theFilterTmp[i] = - (self->initMagnEst[i] - self->overdrive * self->parametricNoise[i]); - theFilterTmp[i] /= (self->initMagnEst[i] + 0.0001f); - // Flooring bottom. - if (theFilterTmp[i] < self->denoiseBound) { - theFilterTmp[i] = self->denoiseBound; - } - // Flooring top. - if (theFilterTmp[i] > 1.f) { - theFilterTmp[i] = 1.f; - } - // Weight the two suppression filters. - theFilter[i] *= (self->blockInd); - theFilterTmp[i] *= (END_STARTUP_SHORT - self->blockInd); - theFilter[i] += theFilterTmp[i]; - theFilter[i] /= (END_STARTUP_SHORT); - } - - self->smooth[i] = theFilter[i]; - real[i] *= self->smooth[i]; - imag[i] *= self->smooth[i]; - } - // Keep track of |magn| spectrum for next frame. - memcpy(self->magnPrevProcess, magn, sizeof(*magn) * self->magnLen); - memcpy(self->noisePrev, self->noise, sizeof(self->noise[0]) * self->magnLen); - // Back to time domain. - IFFT(self, real, imag, self->magnLen, self->anaLen, winData); - - // Scale factor: only do it after END_STARTUP_LONG time. - factor = 1.f; - if (self->gainmap == 1 && self->blockInd > END_STARTUP_LONG) { - factor1 = 1.f; - factor2 = 1.f; - - energy2 = Energy(winData, self->anaLen); - gain = (float)sqrt(energy2 / (energy1 + 1.f)); - - // Scaling for new version. - if (gain > B_LIM) { - factor1 = 1.f + 1.3f * (gain - B_LIM); - if (gain * factor1 > 1.f) { - factor1 = 1.f / gain; - } - } - if (gain < B_LIM) { - // Don't reduce scale too much for pause regions: - // attenuation here should be controlled by flooring. - if (gain <= self->denoiseBound) { - gain = self->denoiseBound; - } - factor2 = 1.f - 0.3f * (B_LIM - gain); - } - // Combine both scales with speech/noise prob: - // note prior (priorSpeechProb) is not frequency dependent. - factor = self->priorSpeechProb * factor1 + - (1.f - self->priorSpeechProb) * factor2; - } // Out of self->gainmap == 1. - - Windowing(self->window, winData, self->anaLen, winData); - - // Synthesis. - for (i = 0; i < self->anaLen; i++) { - self->syntBuf[i] += factor * winData[i]; - } - // Read out fully processed segment. - for (i = self->windShift; i < self->blockLen + self->windShift; i++) { - fout[i - self->windShift] = self->syntBuf[i]; - } - // Update synthesis buffer. - UpdateBuffer(NULL, self->blockLen, self->anaLen, self->syntBuf); - - for (i = 0; i < self->blockLen; ++i) - outFrame[0][i] = - WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, fout[i], WEBRTC_SPL_WORD16_MIN); - - // For time-domain gain of HB. - if (flagHB == 1) { - // Average speech prob from low band. - // Average over second half (i.e., 4->8kHz) of frequencies spectrum. - avgProbSpeechHB = 0.0; - for (i = self->magnLen - deltaBweHB - 1; i < self->magnLen - 1; i++) { - avgProbSpeechHB += self->speechProb[i]; - } - avgProbSpeechHB = avgProbSpeechHB / ((float)deltaBweHB); - // If the speech was suppressed by a component between Analyze and - // Process, for example the AEC, then it should not be considered speech - // for high band suppression purposes. - sumMagnAnalyze = 0; - sumMagnProcess = 0; - for (i = 0; i < self->magnLen; ++i) { - sumMagnAnalyze += self->magnPrevAnalyze[i]; - sumMagnProcess += self->magnPrevProcess[i]; - } - avgProbSpeechHB *= sumMagnProcess / sumMagnAnalyze; - // Average filter gain from low band. - // Average over second half (i.e., 4->8kHz) of frequencies spectrum. - avgFilterGainHB = 0.0; - for (i = self->magnLen - deltaGainHB - 1; i < self->magnLen - 1; i++) { - avgFilterGainHB += self->smooth[i]; - } - avgFilterGainHB = avgFilterGainHB / ((float)(deltaGainHB)); - avgProbSpeechHBTmp = 2.f * avgProbSpeechHB - 1.f; - // Gain based on speech probability. - gainModHB = 0.5f * (1.f + (float)tanh(gainMapParHB * avgProbSpeechHBTmp)); - // Combine gain with low band gain. - gainTimeDomainHB = 0.5f * gainModHB + 0.5f * avgFilterGainHB; - if (avgProbSpeechHB >= 0.5f) { - gainTimeDomainHB = 0.25f * gainModHB + 0.75f * avgFilterGainHB; - } - gainTimeDomainHB = gainTimeDomainHB * decayBweHB; - // Make sure gain is within flooring range. - // Flooring bottom. - if (gainTimeDomainHB < self->denoiseBound) { - gainTimeDomainHB = self->denoiseBound; - } - // Flooring top. - if (gainTimeDomainHB > 1.f) { - gainTimeDomainHB = 1.f; - } - // Apply gain. - for (i = 0; i < num_high_bands; ++i) { - for (j = 0; j < self->blockLen; j++) { - outFrameHB[i][j] = - WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, - gainTimeDomainHB * self->dataBufHB[i][j], - WEBRTC_SPL_WORD16_MIN); - } - } - } // End of H band gain computation. -} diff --git a/webrtc/modules/audio_processing/ns/ns_core.h b/webrtc/modules/audio_processing/ns/ns_core.h deleted file mode 100644 index aba1c46..0000000 --- a/webrtc/modules/audio_processing/ns/ns_core.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_NS_CORE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_NS_CORE_H_ - -#include "webrtc/modules/audio_processing/ns/defines.h" - -typedef struct NSParaExtract_ { - // Bin size of histogram. - float binSizeLrt; - float binSizeSpecFlat; - float binSizeSpecDiff; - // Range of histogram over which LRT threshold is computed. - float rangeAvgHistLrt; - // Scale parameters: multiply dominant peaks of the histograms by scale factor - // to obtain thresholds for prior model. - float factor1ModelPars; // For LRT and spectral difference. - float factor2ModelPars; // For spectral_flatness: used when noise is flatter - // than speech. - // Peak limit for spectral flatness (varies between 0 and 1). - float thresPosSpecFlat; - // Limit on spacing of two highest peaks in histogram: spacing determined by - // bin size. - float limitPeakSpacingSpecFlat; - float limitPeakSpacingSpecDiff; - // Limit on relevance of second peak. - float limitPeakWeightsSpecFlat; - float limitPeakWeightsSpecDiff; - // Limit on fluctuation of LRT feature. - float thresFluctLrt; - // Limit on the max and min values for the feature thresholds. - float maxLrt; - float minLrt; - float maxSpecFlat; - float minSpecFlat; - float maxSpecDiff; - float minSpecDiff; - // Criteria of weight of histogram peak to accept/reject feature. - int thresWeightSpecFlat; - int thresWeightSpecDiff; - -} NSParaExtract; - -typedef struct NoiseSuppressionC_ { - uint32_t fs; - size_t blockLen; - size_t windShift; - size_t anaLen; - size_t magnLen; - int aggrMode; - const float* window; - float analyzeBuf[ANAL_BLOCKL_MAX]; - float dataBuf[ANAL_BLOCKL_MAX]; - float syntBuf[ANAL_BLOCKL_MAX]; - - int initFlag; - // Parameters for quantile noise estimation. - float density[SIMULT * HALF_ANAL_BLOCKL]; - float lquantile[SIMULT * HALF_ANAL_BLOCKL]; - float quantile[HALF_ANAL_BLOCKL]; - int counter[SIMULT]; - int updates; - // Parameters for Wiener filter. - float smooth[HALF_ANAL_BLOCKL]; - float overdrive; - float denoiseBound; - int gainmap; - // FFT work arrays. - size_t ip[IP_LENGTH]; - float wfft[W_LENGTH]; - - // Parameters for new method: some not needed, will reduce/cleanup later. - int32_t blockInd; // Frame index counter. - int modelUpdatePars[4]; // Parameters for updating or estimating. - // Thresholds/weights for prior model. - float priorModelPars[7]; // Parameters for prior model. - float noise[HALF_ANAL_BLOCKL]; // Noise spectrum from current frame. - float noisePrev[HALF_ANAL_BLOCKL]; // Noise spectrum from previous frame. - // Magnitude spectrum of previous analyze frame. - float magnPrevAnalyze[HALF_ANAL_BLOCKL]; - // Magnitude spectrum of previous process frame. - float magnPrevProcess[HALF_ANAL_BLOCKL]; - float logLrtTimeAvg[HALF_ANAL_BLOCKL]; // Log LRT factor with time-smoothing. - float priorSpeechProb; // Prior speech/noise probability. - float featureData[7]; - // Conservative noise spectrum estimate. - float magnAvgPause[HALF_ANAL_BLOCKL]; - float signalEnergy; // Energy of |magn|. - float sumMagn; - float whiteNoiseLevel; // Initial noise estimate. - float initMagnEst[HALF_ANAL_BLOCKL]; // Initial magnitude spectrum estimate. - float pinkNoiseNumerator; // Pink noise parameter: numerator. - float pinkNoiseExp; // Pink noise parameter: power of frequencies. - float parametricNoise[HALF_ANAL_BLOCKL]; - // Parameters for feature extraction. - NSParaExtract featureExtractionParams; - // Histograms for parameter estimation. - int histLrt[HIST_PAR_EST]; - int histSpecFlat[HIST_PAR_EST]; - int histSpecDiff[HIST_PAR_EST]; - // Quantities for high band estimate. - float speechProb[HALF_ANAL_BLOCKL]; // Final speech/noise prob: prior + LRT. - // Buffering data for HB. - float dataBufHB[NUM_HIGH_BANDS_MAX][ANAL_BLOCKL_MAX]; - -} NoiseSuppressionC; - -#ifdef __cplusplus -extern "C" { -#endif - -/**************************************************************************** - * WebRtcNs_InitCore(...) - * - * This function initializes a noise suppression instance - * - * Input: - * - self : Instance that should be initialized - * - fs : Sampling frequency - * - * Output: - * - self : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNs_InitCore(NoiseSuppressionC* self, uint32_t fs); - -/**************************************************************************** - * WebRtcNs_set_policy_core(...) - * - * This changes the aggressiveness of the noise suppression method. - * - * Input: - * - self : Instance that should be initialized - * - mode : 0: Mild (6dB), 1: Medium (10dB), 2: Aggressive (15dB) - * - * Output: - * - self : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNs_set_policy_core(NoiseSuppressionC* self, int mode); - -/**************************************************************************** - * WebRtcNs_AnalyzeCore - * - * Estimate the background noise. - * - * Input: - * - self : Instance that should be initialized - * - speechFrame : Input speech frame for lower band - * - * Output: - * - self : Updated instance - */ -void WebRtcNs_AnalyzeCore(NoiseSuppressionC* self, const float* speechFrame); - -/**************************************************************************** - * WebRtcNs_ProcessCore - * - * Do noise suppression. - * - * Input: - * - self : Instance that should be initialized - * - inFrame : Input speech frame for each band - * - num_bands : Number of bands - * - * Output: - * - self : Updated instance - * - outFrame : Output speech frame for each band - */ -void WebRtcNs_ProcessCore(NoiseSuppressionC* self, - const float* const* inFrame, - size_t num_bands, - float* const* outFrame); - -#ifdef __cplusplus -} -#endif -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_NS_CORE_H_ diff --git a/webrtc/modules/audio_processing/ns/ns_fft.cc b/webrtc/modules/audio_processing/ns/ns_fft.cc new file mode 100644 index 0000000..264c469 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/ns_fft.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/ns_fft.h" + +#include "common_audio/third_party/ooura/fft_size_256/fft4g.h" + +namespace webrtc { + +NrFft::NrFft() : bit_reversal_state_(kFftSize / 2), tables_(kFftSize / 2) { + // Initialize WebRtc_rdt (setting (bit_reversal_state_[0] to 0 triggers + // initialization) + bit_reversal_state_[0] = 0.f; + std::array tmp_buffer; + tmp_buffer.fill(0.f); + WebRtc_rdft(kFftSize, 1, tmp_buffer.data(), bit_reversal_state_.data(), + tables_.data()); +} + +void NrFft::Fft(rtc::ArrayView time_data, + rtc::ArrayView real, + rtc::ArrayView imag) { + WebRtc_rdft(kFftSize, 1, time_data.data(), bit_reversal_state_.data(), + tables_.data()); + + imag[0] = 0; + real[0] = time_data[0]; + + imag[kFftSizeBy2Plus1 - 1] = 0; + real[kFftSizeBy2Plus1 - 1] = time_data[1]; + + for (size_t i = 1; i < kFftSizeBy2Plus1 - 1; ++i) { + real[i] = time_data[2 * i]; + imag[i] = time_data[2 * i + 1]; + } +} + +void NrFft::Ifft(rtc::ArrayView real, + rtc::ArrayView imag, + rtc::ArrayView time_data) { + time_data[0] = real[0]; + time_data[1] = real[kFftSizeBy2Plus1 - 1]; + for (size_t i = 1; i < kFftSizeBy2Plus1 - 1; ++i) { + time_data[2 * i] = real[i]; + time_data[2 * i + 1] = imag[i]; + } + WebRtc_rdft(kFftSize, -1, time_data.data(), bit_reversal_state_.data(), + tables_.data()); + + // Scale the output + constexpr float kScaling = 2.f / kFftSize; + for (float& d : time_data) { + d *= kScaling; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/ns_fft.h b/webrtc/modules/audio_processing/ns/ns_fft.h new file mode 100644 index 0000000..539251e --- /dev/null +++ b/webrtc/modules/audio_processing/ns/ns_fft.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_NS_FFT_H_ +#define MODULES_AUDIO_PROCESSING_NS_NS_FFT_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" + +namespace webrtc { + +// Wrapper class providing 256 point FFT functionality. +class NrFft { + public: + NrFft(); + NrFft(const NrFft&) = delete; + NrFft& operator=(const NrFft&) = delete; + + // Transforms the signal from time to frequency domain. + void Fft(rtc::ArrayView time_data, + rtc::ArrayView real, + rtc::ArrayView imag); + + // Transforms the signal from frequency to time domain. + void Ifft(rtc::ArrayView real, + rtc::ArrayView imag, + rtc::ArrayView time_data); + + private: + std::vector bit_reversal_state_; + std::vector tables_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_NS_FFT_H_ diff --git a/webrtc/modules/audio_processing/ns/nsx_core.c b/webrtc/modules/audio_processing/ns/nsx_core.c deleted file mode 100644 index 7144579..0000000 --- a/webrtc/modules/audio_processing/ns/nsx_core.c +++ /dev/null @@ -1,2112 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_processing/ns/include/noise_suppression_x.h" - -#include -#include -#include -#include - -#include "webrtc/common_audio/signal_processing/include/real_fft.h" -#include "webrtc/modules/audio_processing/ns/nsx_core.h" -#include "webrtc/system_wrappers/include/cpu_features_wrapper.h" - -#if (defined WEBRTC_DETECT_NEON || defined WEBRTC_HAS_NEON) -/* Tables are defined in ARM assembly files. */ -extern const int16_t WebRtcNsx_kLogTable[9]; -extern const int16_t WebRtcNsx_kCounterDiv[201]; -extern const int16_t WebRtcNsx_kLogTableFrac[256]; -#else -static const int16_t WebRtcNsx_kLogTable[9] = { - 0, 177, 355, 532, 710, 887, 1065, 1242, 1420 -}; - -static const int16_t WebRtcNsx_kCounterDiv[201] = { - 32767, 16384, 10923, 8192, 6554, 5461, 4681, 4096, 3641, 3277, 2979, 2731, - 2521, 2341, 2185, 2048, 1928, 1820, 1725, 1638, 1560, 1489, 1425, 1365, 1311, - 1260, 1214, 1170, 1130, 1092, 1057, 1024, 993, 964, 936, 910, 886, 862, 840, - 819, 799, 780, 762, 745, 728, 712, 697, 683, 669, 655, 643, 630, 618, 607, - 596, 585, 575, 565, 555, 546, 537, 529, 520, 512, 504, 496, 489, 482, 475, - 468, 462, 455, 449, 443, 437, 431, 426, 420, 415, 410, 405, 400, 395, 390, - 386, 381, 377, 372, 368, 364, 360, 356, 352, 349, 345, 341, 338, 334, 331, - 328, 324, 321, 318, 315, 312, 309, 306, 303, 301, 298, 295, 293, 290, 287, - 285, 282, 280, 278, 275, 273, 271, 269, 266, 264, 262, 260, 258, 256, 254, - 252, 250, 248, 246, 245, 243, 241, 239, 237, 236, 234, 232, 231, 229, 228, - 226, 224, 223, 221, 220, 218, 217, 216, 214, 213, 211, 210, 209, 207, 206, - 205, 204, 202, 201, 200, 199, 197, 196, 195, 194, 193, 192, 191, 189, 188, - 187, 186, 185, 184, 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, 173, - 172, 172, 171, 170, 169, 168, 167, 166, 165, 165, 164, 163 -}; - -static const int16_t WebRtcNsx_kLogTableFrac[256] = { - 0, 1, 3, 4, 6, 7, 9, 10, 11, 13, 14, 16, 17, 18, 20, 21, - 22, 24, 25, 26, 28, 29, 30, 32, 33, 34, 36, 37, 38, 40, 41, 42, - 44, 45, 46, 47, 49, 50, 51, 52, 54, 55, 56, 57, 59, 60, 61, 62, - 63, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 77, 78, 79, 80, 81, - 82, 84, 85, 86, 87, 88, 89, 90, 92, 93, 94, 95, 96, 97, 98, 99, - 100, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 116, - 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, - 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, - 147, 148, 149, 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 160, - 161, 162, 163, 164, 165, 166, 167, 168, 169, 169, 170, 171, 172, 173, 174, - 175, 176, 177, 178, 178, 179, 180, 181, 182, 183, 184, 185, 185, 186, 187, - 188, 189, 190, 191, 192, 192, 193, 194, 195, 196, 197, 198, 198, 199, 200, - 201, 202, 203, 203, 204, 205, 206, 207, 208, 208, 209, 210, 211, 212, 212, - 213, 214, 215, 216, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 224, - 225, 226, 227, 228, 228, 229, 230, 231, 231, 232, 233, 234, 234, 235, 236, - 237, 238, 238, 239, 240, 241, 241, 242, 243, 244, 244, 245, 246, 247, 247, - 248, 249, 249, 250, 251, 252, 252, 253, 254, 255, 255 -}; -#endif // WEBRTC_DETECT_NEON || WEBRTC_HAS_NEON - -// Skip first frequency bins during estimation. (0 <= value < 64) -static const size_t kStartBand = 5; - -// hybrib Hanning & flat window -static const int16_t kBlocks80w128x[128] = { - 0, 536, 1072, 1606, 2139, 2669, 3196, 3720, 4240, 4756, 5266, - 5771, 6270, 6762, 7246, 7723, 8192, 8652, 9102, 9543, 9974, 10394, - 10803, 11200, 11585, 11958, 12318, 12665, 12998, 13318, 13623, 13913, 14189, - 14449, 14694, 14924, 15137, 15334, 15515, 15679, 15826, 15956, 16069, 16165, - 16244, 16305, 16349, 16375, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16375, 16349, 16305, 16244, 16165, 16069, 15956, - 15826, 15679, 15515, 15334, 15137, 14924, 14694, 14449, 14189, 13913, 13623, - 13318, 12998, 12665, 12318, 11958, 11585, 11200, 10803, 10394, 9974, 9543, - 9102, 8652, 8192, 7723, 7246, 6762, 6270, 5771, 5266, 4756, 4240, - 3720, 3196, 2669, 2139, 1606, 1072, 536 -}; - -// hybrib Hanning & flat window -static const int16_t kBlocks160w256x[256] = { - 0, 268, 536, 804, 1072, 1339, 1606, 1872, - 2139, 2404, 2669, 2933, 3196, 3459, 3720, 3981, - 4240, 4499, 4756, 5012, 5266, 5520, 5771, 6021, - 6270, 6517, 6762, 7005, 7246, 7486, 7723, 7959, - 8192, 8423, 8652, 8878, 9102, 9324, 9543, 9760, - 9974, 10185, 10394, 10600, 10803, 11003, 11200, 11394, - 11585, 11773, 11958, 12140, 12318, 12493, 12665, 12833, - 12998, 13160, 13318, 13472, 13623, 13770, 13913, 14053, - 14189, 14321, 14449, 14574, 14694, 14811, 14924, 15032, - 15137, 15237, 15334, 15426, 15515, 15599, 15679, 15754, - 15826, 15893, 15956, 16015, 16069, 16119, 16165, 16207, - 16244, 16277, 16305, 16329, 16349, 16364, 16375, 16382, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16382, 16375, 16364, 16349, 16329, 16305, 16277, - 16244, 16207, 16165, 16119, 16069, 16015, 15956, 15893, - 15826, 15754, 15679, 15599, 15515, 15426, 15334, 15237, - 15137, 15032, 14924, 14811, 14694, 14574, 14449, 14321, - 14189, 14053, 13913, 13770, 13623, 13472, 13318, 13160, - 12998, 12833, 12665, 12493, 12318, 12140, 11958, 11773, - 11585, 11394, 11200, 11003, 10803, 10600, 10394, 10185, - 9974, 9760, 9543, 9324, 9102, 8878, 8652, 8423, - 8192, 7959, 7723, 7486, 7246, 7005, 6762, 6517, - 6270, 6021, 5771, 5520, 5266, 5012, 4756, 4499, - 4240, 3981, 3720, 3459, 3196, 2933, 2669, 2404, - 2139, 1872, 1606, 1339, 1072, 804, 536, 268 -}; - -// Gain factor1 table: Input value in Q8 and output value in Q13 -// original floating point code -// if (gain > blim) { -// factor1 = 1.0 + 1.3 * (gain - blim); -// if (gain * factor1 > 1.0) { -// factor1 = 1.0 / gain; -// } -// } else { -// factor1 = 1.0; -// } -static const int16_t kFactor1Table[257] = { - 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8233, 8274, 8315, 8355, 8396, 8436, 8475, 8515, 8554, 8592, 8631, 8669, - 8707, 8745, 8783, 8820, 8857, 8894, 8931, 8967, 9003, 9039, 9075, 9111, 9146, 9181, - 9216, 9251, 9286, 9320, 9354, 9388, 9422, 9456, 9489, 9523, 9556, 9589, 9622, 9655, - 9687, 9719, 9752, 9784, 9816, 9848, 9879, 9911, 9942, 9973, 10004, 10035, 10066, - 10097, 10128, 10158, 10188, 10218, 10249, 10279, 10308, 10338, 10368, 10397, 10426, - 10456, 10485, 10514, 10543, 10572, 10600, 10629, 10657, 10686, 10714, 10742, 10770, - 10798, 10826, 10854, 10882, 10847, 10810, 10774, 10737, 10701, 10666, 10631, 10596, - 10562, 10527, 10494, 10460, 10427, 10394, 10362, 10329, 10297, 10266, 10235, 10203, - 10173, 10142, 10112, 10082, 10052, 10023, 9994, 9965, 9936, 9908, 9879, 9851, 9824, - 9796, 9769, 9742, 9715, 9689, 9662, 9636, 9610, 9584, 9559, 9534, 9508, 9484, 9459, - 9434, 9410, 9386, 9362, 9338, 9314, 9291, 9268, 9245, 9222, 9199, 9176, 9154, 9132, - 9110, 9088, 9066, 9044, 9023, 9002, 8980, 8959, 8939, 8918, 8897, 8877, 8857, 8836, - 8816, 8796, 8777, 8757, 8738, 8718, 8699, 8680, 8661, 8642, 8623, 8605, 8586, 8568, - 8550, 8532, 8514, 8496, 8478, 8460, 8443, 8425, 8408, 8391, 8373, 8356, 8339, 8323, - 8306, 8289, 8273, 8256, 8240, 8224, 8208, 8192 -}; - -// For Factor2 tables -// original floating point code -// if (gain > blim) { -// factor2 = 1.0; -// } else { -// factor2 = 1.0 - 0.3 * (blim - gain); -// if (gain <= inst->denoiseBound) { -// factor2 = 1.0 - 0.3 * (blim - inst->denoiseBound); -// } -// } -// -// Gain factor table: Input value in Q8 and output value in Q13 -static const int16_t kFactor2Aggressiveness1[257] = { - 7577, 7577, 7577, 7577, 7577, 7577, - 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7596, 7614, 7632, - 7650, 7667, 7683, 7699, 7715, 7731, 7746, 7761, 7775, 7790, 7804, 7818, 7832, 7845, - 7858, 7871, 7884, 7897, 7910, 7922, 7934, 7946, 7958, 7970, 7982, 7993, 8004, 8016, - 8027, 8038, 8049, 8060, 8070, 8081, 8091, 8102, 8112, 8122, 8132, 8143, 8152, 8162, - 8172, 8182, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192 -}; - -// Gain factor table: Input value in Q8 and output value in Q13 -static const int16_t kFactor2Aggressiveness2[257] = { - 7270, 7270, 7270, 7270, 7270, 7306, - 7339, 7369, 7397, 7424, 7448, 7472, 7495, 7517, 7537, 7558, 7577, 7596, 7614, 7632, - 7650, 7667, 7683, 7699, 7715, 7731, 7746, 7761, 7775, 7790, 7804, 7818, 7832, 7845, - 7858, 7871, 7884, 7897, 7910, 7922, 7934, 7946, 7958, 7970, 7982, 7993, 8004, 8016, - 8027, 8038, 8049, 8060, 8070, 8081, 8091, 8102, 8112, 8122, 8132, 8143, 8152, 8162, - 8172, 8182, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192 -}; - -// Gain factor table: Input value in Q8 and output value in Q13 -static const int16_t kFactor2Aggressiveness3[257] = { - 7184, 7184, 7184, 7229, 7270, 7306, - 7339, 7369, 7397, 7424, 7448, 7472, 7495, 7517, 7537, 7558, 7577, 7596, 7614, 7632, - 7650, 7667, 7683, 7699, 7715, 7731, 7746, 7761, 7775, 7790, 7804, 7818, 7832, 7845, - 7858, 7871, 7884, 7897, 7910, 7922, 7934, 7946, 7958, 7970, 7982, 7993, 8004, 8016, - 8027, 8038, 8049, 8060, 8070, 8081, 8091, 8102, 8112, 8122, 8132, 8143, 8152, 8162, - 8172, 8182, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192 -}; - -// sum of log2(i) from table index to inst->anaLen2 in Q5 -// Note that the first table value is invalid, since log2(0) = -infinity -static const int16_t kSumLogIndex[66] = { - 0, 22917, 22917, 22885, 22834, 22770, 22696, 22613, - 22524, 22428, 22326, 22220, 22109, 21994, 21876, 21754, - 21629, 21501, 21370, 21237, 21101, 20963, 20822, 20679, - 20535, 20388, 20239, 20089, 19937, 19783, 19628, 19470, - 19312, 19152, 18991, 18828, 18664, 18498, 18331, 18164, - 17994, 17824, 17653, 17480, 17306, 17132, 16956, 16779, - 16602, 16423, 16243, 16063, 15881, 15699, 15515, 15331, - 15146, 14960, 14774, 14586, 14398, 14209, 14019, 13829, - 13637, 13445 -}; - -// sum of log2(i)^2 from table index to inst->anaLen2 in Q2 -// Note that the first table value is invalid, since log2(0) = -infinity -static const int16_t kSumSquareLogIndex[66] = { - 0, 16959, 16959, 16955, 16945, 16929, 16908, 16881, - 16850, 16814, 16773, 16729, 16681, 16630, 16575, 16517, - 16456, 16392, 16325, 16256, 16184, 16109, 16032, 15952, - 15870, 15786, 15700, 15612, 15521, 15429, 15334, 15238, - 15140, 15040, 14938, 14834, 14729, 14622, 14514, 14404, - 14292, 14179, 14064, 13947, 13830, 13710, 13590, 13468, - 13344, 13220, 13094, 12966, 12837, 12707, 12576, 12444, - 12310, 12175, 12039, 11902, 11763, 11624, 11483, 11341, - 11198, 11054 -}; - -// log2(table index) in Q12 -// Note that the first table value is invalid, since log2(0) = -infinity -static const int16_t kLogIndex[129] = { - 0, 0, 4096, 6492, 8192, 9511, 10588, 11499, - 12288, 12984, 13607, 14170, 14684, 15157, 15595, 16003, - 16384, 16742, 17080, 17400, 17703, 17991, 18266, 18529, - 18780, 19021, 19253, 19476, 19691, 19898, 20099, 20292, - 20480, 20662, 20838, 21010, 21176, 21338, 21496, 21649, - 21799, 21945, 22087, 22226, 22362, 22495, 22625, 22752, - 22876, 22998, 23117, 23234, 23349, 23462, 23572, 23680, - 23787, 23892, 23994, 24095, 24195, 24292, 24388, 24483, - 24576, 24668, 24758, 24847, 24934, 25021, 25106, 25189, - 25272, 25354, 25434, 25513, 25592, 25669, 25745, 25820, - 25895, 25968, 26041, 26112, 26183, 26253, 26322, 26390, - 26458, 26525, 26591, 26656, 26721, 26784, 26848, 26910, - 26972, 27033, 27094, 27154, 27213, 27272, 27330, 27388, - 27445, 27502, 27558, 27613, 27668, 27722, 27776, 27830, - 27883, 27935, 27988, 28039, 28090, 28141, 28191, 28241, - 28291, 28340, 28388, 28437, 28484, 28532, 28579, 28626, - 28672 -}; - -// determinant of estimation matrix in Q0 corresponding to the log2 tables above -// Note that the first table value is invalid, since log2(0) = -infinity -static const int16_t kDeterminantEstMatrix[66] = { - 0, 29814, 25574, 22640, 20351, 18469, 16873, 15491, - 14277, 13199, 12233, 11362, 10571, 9851, 9192, 8587, - 8030, 7515, 7038, 6596, 6186, 5804, 5448, 5115, - 4805, 4514, 4242, 3988, 3749, 3524, 3314, 3116, - 2930, 2755, 2590, 2435, 2289, 2152, 2022, 1900, - 1785, 1677, 1575, 1478, 1388, 1302, 1221, 1145, - 1073, 1005, 942, 881, 825, 771, 721, 674, - 629, 587, 547, 510, 475, 442, 411, 382, - 355, 330 -}; - -// Update the noise estimation information. -static void UpdateNoiseEstimate(NoiseSuppressionFixedC* inst, int offset) { - int32_t tmp32no1 = 0; - int32_t tmp32no2 = 0; - int16_t tmp16 = 0; - const int16_t kExp2Const = 11819; // Q13 - - size_t i = 0; - - tmp16 = WebRtcSpl_MaxValueW16(inst->noiseEstLogQuantile + offset, - inst->magnLen); - // Guarantee a Q-domain as high as possible and still fit in int16 - inst->qNoise = 14 - (int) WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - kExp2Const, tmp16, 21); - for (i = 0; i < inst->magnLen; i++) { - // inst->quantile[i]=exp(inst->lquantile[offset+i]); - // in Q21 - tmp32no2 = kExp2Const * inst->noiseEstLogQuantile[offset + i]; - tmp32no1 = (0x00200000 | (tmp32no2 & 0x001FFFFF)); // 2^21 + frac - tmp16 = (int16_t)(tmp32no2 >> 21); - tmp16 -= 21;// shift 21 to get result in Q0 - tmp16 += (int16_t) inst->qNoise; //shift to get result in Q(qNoise) - if (tmp16 < 0) { - tmp32no1 >>= -tmp16; - } else { - tmp32no1 <<= tmp16; - } - inst->noiseEstQuantile[i] = WebRtcSpl_SatW32ToW16(tmp32no1); - } -} - -// Noise Estimation -static void NoiseEstimationC(NoiseSuppressionFixedC* inst, - uint16_t* magn, - uint32_t* noise, - int16_t* q_noise) { - int16_t lmagn[HALF_ANAL_BLOCKL], counter, countDiv; - int16_t countProd, delta, zeros, frac; - int16_t log2, tabind, logval, tmp16, tmp16no1, tmp16no2; - const int16_t log2_const = 22713; // Q15 - const int16_t width_factor = 21845; - - size_t i, s, offset; - - tabind = inst->stages - inst->normData; - assert(tabind < 9); - assert(tabind > -9); - if (tabind < 0) { - logval = -WebRtcNsx_kLogTable[-tabind]; - } else { - logval = WebRtcNsx_kLogTable[tabind]; - } - - // lmagn(i)=log(magn(i))=log(2)*log2(magn(i)) - // magn is in Q(-stages), and the real lmagn values are: - // real_lmagn(i)=log(magn(i)*2^stages)=log(magn(i))+log(2^stages) - // lmagn in Q8 - for (i = 0; i < inst->magnLen; i++) { - if (magn[i]) { - zeros = WebRtcSpl_NormU32((uint32_t)magn[i]); - frac = (int16_t)((((uint32_t)magn[i] << zeros) - & 0x7FFFFFFF) >> 23); - // log2(magn(i)) - assert(frac < 256); - log2 = (int16_t)(((31 - zeros) << 8) - + WebRtcNsx_kLogTableFrac[frac]); - // log2(magn(i))*log(2) - lmagn[i] = (int16_t)((log2 * log2_const) >> 15); - // + log(2^stages) - lmagn[i] += logval; - } else { - lmagn[i] = logval;//0; - } - } - - // loop over simultaneous estimates - for (s = 0; s < SIMULT; s++) { - offset = s * inst->magnLen; - - // Get counter values from state - counter = inst->noiseEstCounter[s]; - assert(counter < 201); - countDiv = WebRtcNsx_kCounterDiv[counter]; - countProd = (int16_t)(counter * countDiv); - - // quant_est(...) - for (i = 0; i < inst->magnLen; i++) { - // compute delta - if (inst->noiseEstDensity[offset + i] > 512) { - // Get the value for delta by shifting intead of dividing. - int factor = WebRtcSpl_NormW16(inst->noiseEstDensity[offset + i]); - delta = (int16_t)(FACTOR_Q16 >> (14 - factor)); - } else { - delta = FACTOR_Q7; - if (inst->blockIndex < END_STARTUP_LONG) { - // Smaller step size during startup. This prevents from using - // unrealistic values causing overflow. - delta = FACTOR_Q7_STARTUP; - } - } - - // update log quantile estimate - tmp16 = (int16_t)((delta * countDiv) >> 14); - if (lmagn[i] > inst->noiseEstLogQuantile[offset + i]) { - // +=QUANTILE*delta/(inst->counter[s]+1) QUANTILE=0.25, =1 in Q2 - // CounterDiv=1/(inst->counter[s]+1) in Q15 - tmp16 += 2; - inst->noiseEstLogQuantile[offset + i] += tmp16 / 4; - } else { - tmp16 += 1; - // *(1-QUANTILE), in Q2 QUANTILE=0.25, 1-0.25=0.75=3 in Q2 - // TODO(bjornv): investigate why we need to truncate twice. - tmp16no2 = (int16_t)((tmp16 / 2) * 3 / 2); - inst->noiseEstLogQuantile[offset + i] -= tmp16no2; - if (inst->noiseEstLogQuantile[offset + i] < logval) { - // This is the smallest fixed point representation we can - // have, hence we limit the output. - inst->noiseEstLogQuantile[offset + i] = logval; - } - } - - // update density estimate - if (WEBRTC_SPL_ABS_W16(lmagn[i] - inst->noiseEstLogQuantile[offset + i]) - < WIDTH_Q8) { - tmp16no1 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - inst->noiseEstDensity[offset + i], countProd, 15); - tmp16no2 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - width_factor, countDiv, 15); - inst->noiseEstDensity[offset + i] = tmp16no1 + tmp16no2; - } - } // end loop over magnitude spectrum - - if (counter >= END_STARTUP_LONG) { - inst->noiseEstCounter[s] = 0; - if (inst->blockIndex >= END_STARTUP_LONG) { - UpdateNoiseEstimate(inst, offset); - } - } - inst->noiseEstCounter[s]++; - - } // end loop over simultaneous estimates - - // Sequentially update the noise during startup - if (inst->blockIndex < END_STARTUP_LONG) { - UpdateNoiseEstimate(inst, offset); - } - - for (i = 0; i < inst->magnLen; i++) { - noise[i] = (uint32_t)(inst->noiseEstQuantile[i]); // Q(qNoise) - } - (*q_noise) = (int16_t)inst->qNoise; -} - -// Filter the data in the frequency domain, and create spectrum. -static void PrepareSpectrumC(NoiseSuppressionFixedC* inst, int16_t* freq_buf) { - size_t i = 0, j = 0; - - for (i = 0; i < inst->magnLen; i++) { - inst->real[i] = (int16_t)((inst->real[i] * - (int16_t)(inst->noiseSupFilter[i])) >> 14); // Q(normData-stages) - inst->imag[i] = (int16_t)((inst->imag[i] * - (int16_t)(inst->noiseSupFilter[i])) >> 14); // Q(normData-stages) - } - - freq_buf[0] = inst->real[0]; - freq_buf[1] = -inst->imag[0]; - for (i = 1, j = 2; i < inst->anaLen2; i += 1, j += 2) { - freq_buf[j] = inst->real[i]; - freq_buf[j + 1] = -inst->imag[i]; - } - freq_buf[inst->anaLen] = inst->real[inst->anaLen2]; - freq_buf[inst->anaLen + 1] = -inst->imag[inst->anaLen2]; -} - -// Denormalize the real-valued signal |in|, the output from inverse FFT. -static void DenormalizeC(NoiseSuppressionFixedC* inst, - int16_t* in, - int factor) { - size_t i = 0; - int32_t tmp32 = 0; - for (i = 0; i < inst->anaLen; i += 1) { - tmp32 = WEBRTC_SPL_SHIFT_W32((int32_t)in[i], - factor - inst->normData); - inst->real[i] = WebRtcSpl_SatW32ToW16(tmp32); // Q0 - } -} - -// For the noise supression process, synthesis, read out fully processed -// segment, and update synthesis buffer. -static void SynthesisUpdateC(NoiseSuppressionFixedC* inst, - int16_t* out_frame, - int16_t gain_factor) { - size_t i = 0; - int16_t tmp16a = 0; - int16_t tmp16b = 0; - int32_t tmp32 = 0; - - // synthesis - for (i = 0; i < inst->anaLen; i++) { - tmp16a = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - inst->window[i], inst->real[i], 14); // Q0, window in Q14 - tmp32 = WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(tmp16a, gain_factor, 13); // Q0 - // Down shift with rounding - tmp16b = WebRtcSpl_SatW32ToW16(tmp32); // Q0 - inst->synthesisBuffer[i] = WebRtcSpl_AddSatW16(inst->synthesisBuffer[i], - tmp16b); // Q0 - } - - // read out fully processed segment - for (i = 0; i < inst->blockLen10ms; i++) { - out_frame[i] = inst->synthesisBuffer[i]; // Q0 - } - - // update synthesis buffer - memcpy(inst->synthesisBuffer, inst->synthesisBuffer + inst->blockLen10ms, - (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->synthesisBuffer)); - WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer - + inst->anaLen - inst->blockLen10ms, inst->blockLen10ms); -} - -// Update analysis buffer for lower band, and window data before FFT. -static void AnalysisUpdateC(NoiseSuppressionFixedC* inst, - int16_t* out, - int16_t* new_speech) { - size_t i = 0; - - // For lower band update analysis buffer. - memcpy(inst->analysisBuffer, inst->analysisBuffer + inst->blockLen10ms, - (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->analysisBuffer)); - memcpy(inst->analysisBuffer + inst->anaLen - inst->blockLen10ms, new_speech, - inst->blockLen10ms * sizeof(*inst->analysisBuffer)); - - // Window data before FFT. - for (i = 0; i < inst->anaLen; i++) { - out[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - inst->window[i], inst->analysisBuffer[i], 14); // Q0 - } -} - -// Normalize the real-valued signal |in|, the input to forward FFT. -static void NormalizeRealBufferC(NoiseSuppressionFixedC* inst, - const int16_t* in, - int16_t* out) { - size_t i = 0; - assert(inst->normData >= 0); - for (i = 0; i < inst->anaLen; ++i) { - out[i] = in[i] << inst->normData; // Q(normData) - } -} - -// Declare function pointers. -NoiseEstimation WebRtcNsx_NoiseEstimation; -PrepareSpectrum WebRtcNsx_PrepareSpectrum; -SynthesisUpdate WebRtcNsx_SynthesisUpdate; -AnalysisUpdate WebRtcNsx_AnalysisUpdate; -Denormalize WebRtcNsx_Denormalize; -NormalizeRealBuffer WebRtcNsx_NormalizeRealBuffer; - -#if (defined WEBRTC_DETECT_NEON || defined WEBRTC_HAS_NEON) -// Initialize function pointers for ARM Neon platform. -static void WebRtcNsx_InitNeon(void) { - WebRtcNsx_NoiseEstimation = WebRtcNsx_NoiseEstimationNeon; - WebRtcNsx_PrepareSpectrum = WebRtcNsx_PrepareSpectrumNeon; - WebRtcNsx_SynthesisUpdate = WebRtcNsx_SynthesisUpdateNeon; - WebRtcNsx_AnalysisUpdate = WebRtcNsx_AnalysisUpdateNeon; -} -#endif - -#if defined(MIPS32_LE) -// Initialize function pointers for MIPS platform. -static void WebRtcNsx_InitMips(void) { - WebRtcNsx_PrepareSpectrum = WebRtcNsx_PrepareSpectrum_mips; - WebRtcNsx_SynthesisUpdate = WebRtcNsx_SynthesisUpdate_mips; - WebRtcNsx_AnalysisUpdate = WebRtcNsx_AnalysisUpdate_mips; - WebRtcNsx_NormalizeRealBuffer = WebRtcNsx_NormalizeRealBuffer_mips; -#if defined(MIPS_DSP_R1_LE) - WebRtcNsx_Denormalize = WebRtcNsx_Denormalize_mips; -#endif -} -#endif - -void WebRtcNsx_CalcParametricNoiseEstimate(NoiseSuppressionFixedC* inst, - int16_t pink_noise_exp_avg, - int32_t pink_noise_num_avg, - int freq_index, - uint32_t* noise_estimate, - uint32_t* noise_estimate_avg) { - int32_t tmp32no1 = 0; - int32_t tmp32no2 = 0; - - int16_t int_part = 0; - int16_t frac_part = 0; - - // Use pink noise estimate - // noise_estimate = 2^(pinkNoiseNumerator + pinkNoiseExp * log2(j)) - assert(freq_index >= 0); - assert(freq_index < 129); - tmp32no2 = (pink_noise_exp_avg * kLogIndex[freq_index]) >> 15; // Q11 - tmp32no1 = pink_noise_num_avg - tmp32no2; // Q11 - - // Calculate output: 2^tmp32no1 - // Output in Q(minNorm-stages) - tmp32no1 += (inst->minNorm - inst->stages) << 11; - if (tmp32no1 > 0) { - int_part = (int16_t)(tmp32no1 >> 11); - frac_part = (int16_t)(tmp32no1 & 0x000007ff); // Q11 - // Piecewise linear approximation of 'b' in - // 2^(int_part+frac_part) = 2^int_part * (1 + b) - // 'b' is given in Q11 and below stored in frac_part. - if (frac_part >> 10) { - // Upper fractional part - tmp32no2 = (2048 - frac_part) * 1244; // Q21 - tmp32no2 = 2048 - (tmp32no2 >> 10); - } else { - // Lower fractional part - tmp32no2 = (frac_part * 804) >> 10; - } - // Shift fractional part to Q(minNorm-stages) - tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, int_part - 11); - *noise_estimate_avg = (1 << int_part) + (uint32_t)tmp32no2; - // Scale up to initMagnEst, which is not block averaged - *noise_estimate = (*noise_estimate_avg) * (uint32_t)(inst->blockIndex + 1); - } -} - -// Initialize state -int32_t WebRtcNsx_InitCore(NoiseSuppressionFixedC* inst, uint32_t fs) { - int i; - - //check for valid pointer - if (inst == NULL) { - return -1; - } - // - - // Initialization of struct - if (fs == 8000 || fs == 16000 || fs == 32000 || fs == 48000) { - inst->fs = fs; - } else { - return -1; - } - - if (fs == 8000) { - inst->blockLen10ms = 80; - inst->anaLen = 128; - inst->stages = 7; - inst->window = kBlocks80w128x; - inst->thresholdLogLrt = 131072; //default threshold for LRT feature - inst->maxLrt = 0x0040000; - inst->minLrt = 52429; - } else { - inst->blockLen10ms = 160; - inst->anaLen = 256; - inst->stages = 8; - inst->window = kBlocks160w256x; - inst->thresholdLogLrt = 212644; //default threshold for LRT feature - inst->maxLrt = 0x0080000; - inst->minLrt = 104858; - } - inst->anaLen2 = inst->anaLen / 2; - inst->magnLen = inst->anaLen2 + 1; - - if (inst->real_fft != NULL) { - WebRtcSpl_FreeRealFFT(inst->real_fft); - } - inst->real_fft = WebRtcSpl_CreateRealFFT(inst->stages); - if (inst->real_fft == NULL) { - return -1; - } - - WebRtcSpl_ZerosArrayW16(inst->analysisBuffer, ANAL_BLOCKL_MAX); - WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer, ANAL_BLOCKL_MAX); - - // for HB processing - WebRtcSpl_ZerosArrayW16(inst->dataBufHBFX[0], - NUM_HIGH_BANDS_MAX * ANAL_BLOCKL_MAX); - // for quantile noise estimation - WebRtcSpl_ZerosArrayW16(inst->noiseEstQuantile, HALF_ANAL_BLOCKL); - for (i = 0; i < SIMULT * HALF_ANAL_BLOCKL; i++) { - inst->noiseEstLogQuantile[i] = 2048; // Q8 - inst->noiseEstDensity[i] = 153; // Q9 - } - for (i = 0; i < SIMULT; i++) { - inst->noiseEstCounter[i] = (int16_t)(END_STARTUP_LONG * (i + 1)) / SIMULT; - } - - // Initialize suppression filter with ones - WebRtcSpl_MemSetW16((int16_t*)inst->noiseSupFilter, 16384, HALF_ANAL_BLOCKL); - - // Set the aggressiveness: default - inst->aggrMode = 0; - - //initialize variables for new method - inst->priorNonSpeechProb = 8192; // Q14(0.5) prior probability for speech/noise - for (i = 0; i < HALF_ANAL_BLOCKL; i++) { - inst->prevMagnU16[i] = 0; - inst->prevNoiseU32[i] = 0; //previous noise-spectrum - inst->logLrtTimeAvgW32[i] = 0; //smooth LR ratio - inst->avgMagnPause[i] = 0; //conservative noise spectrum estimate - inst->initMagnEst[i] = 0; //initial average magnitude spectrum - } - - //feature quantities - inst->thresholdSpecDiff = 50; //threshold for difference feature: determined on-line - inst->thresholdSpecFlat = 20480; //threshold for flatness: determined on-line - inst->featureLogLrt = inst->thresholdLogLrt; //average LRT factor (= threshold) - inst->featureSpecFlat = inst->thresholdSpecFlat; //spectral flatness (= threshold) - inst->featureSpecDiff = inst->thresholdSpecDiff; //spectral difference (= threshold) - inst->weightLogLrt = 6; //default weighting par for LRT feature - inst->weightSpecFlat = 0; //default weighting par for spectral flatness feature - inst->weightSpecDiff = 0; //default weighting par for spectral difference feature - - inst->curAvgMagnEnergy = 0; //window time-average of input magnitude spectrum - inst->timeAvgMagnEnergy = 0; //normalization for spectral difference - inst->timeAvgMagnEnergyTmp = 0; //normalization for spectral difference - - //histogram quantities: used to estimate/update thresholds for features - WebRtcSpl_ZerosArrayW16(inst->histLrt, HIST_PAR_EST); - WebRtcSpl_ZerosArrayW16(inst->histSpecDiff, HIST_PAR_EST); - WebRtcSpl_ZerosArrayW16(inst->histSpecFlat, HIST_PAR_EST); - - inst->blockIndex = -1; //frame counter - - //inst->modelUpdate = 500; //window for update - inst->modelUpdate = (1 << STAT_UPDATES); //window for update - inst->cntThresUpdate = 0; //counter feature thresholds updates - - inst->sumMagn = 0; - inst->magnEnergy = 0; - inst->prevQMagn = 0; - inst->qNoise = 0; - inst->prevQNoise = 0; - - inst->energyIn = 0; - inst->scaleEnergyIn = 0; - - inst->whiteNoiseLevel = 0; - inst->pinkNoiseNumerator = 0; - inst->pinkNoiseExp = 0; - inst->minNorm = 15; // Start with full scale - inst->zeroInputSignal = 0; - - //default mode - WebRtcNsx_set_policy_core(inst, 0); - -#ifdef NS_FILEDEBUG - inst->infile = fopen("indebug.pcm", "wb"); - inst->outfile = fopen("outdebug.pcm", "wb"); - inst->file1 = fopen("file1.pcm", "wb"); - inst->file2 = fopen("file2.pcm", "wb"); - inst->file3 = fopen("file3.pcm", "wb"); - inst->file4 = fopen("file4.pcm", "wb"); - inst->file5 = fopen("file5.pcm", "wb"); -#endif - - // Initialize function pointers. - WebRtcNsx_NoiseEstimation = NoiseEstimationC; - WebRtcNsx_PrepareSpectrum = PrepareSpectrumC; - WebRtcNsx_SynthesisUpdate = SynthesisUpdateC; - WebRtcNsx_AnalysisUpdate = AnalysisUpdateC; - WebRtcNsx_Denormalize = DenormalizeC; - WebRtcNsx_NormalizeRealBuffer = NormalizeRealBufferC; - -#ifdef WEBRTC_DETECT_NEON - uint64_t features = WebRtc_GetCPUFeaturesARM(); - if ((features & kCPUFeatureNEON) != 0) { - WebRtcNsx_InitNeon(); - } -#elif defined(WEBRTC_HAS_NEON) - WebRtcNsx_InitNeon(); -#endif - -#if defined(MIPS32_LE) - WebRtcNsx_InitMips(); -#endif - - inst->initFlag = 1; - - return 0; -} - -int WebRtcNsx_set_policy_core(NoiseSuppressionFixedC* inst, int mode) { - // allow for modes:0,1,2,3 - if (mode < 0 || mode > 3) { - return -1; - } - - inst->aggrMode = mode; - if (mode == 0) { - inst->overdrive = 256; // Q8(1.0) - inst->denoiseBound = 8192; // Q14(0.5) - inst->gainMap = 0; // No gain compensation - } else if (mode == 1) { - inst->overdrive = 256; // Q8(1.0) - inst->denoiseBound = 4096; // Q14(0.25) - inst->factor2Table = kFactor2Aggressiveness1; - inst->gainMap = 1; - } else if (mode == 2) { - inst->overdrive = 282; // ~= Q8(1.1) - inst->denoiseBound = 2048; // Q14(0.125) - inst->factor2Table = kFactor2Aggressiveness2; - inst->gainMap = 1; - } else if (mode == 3) { - inst->overdrive = 320; // Q8(1.25) - inst->denoiseBound = 1475; // ~= Q14(0.09) - inst->factor2Table = kFactor2Aggressiveness3; - inst->gainMap = 1; - } - return 0; -} - -// Extract thresholds for feature parameters -// histograms are computed over some window_size (given by window_pars) -// thresholds and weights are extracted every window -// flag 0 means update histogram only, flag 1 means compute the thresholds/weights -// threshold and weights are returned in: inst->priorModelPars -void WebRtcNsx_FeatureParameterExtraction(NoiseSuppressionFixedC* inst, - int flag) { - uint32_t tmpU32; - uint32_t histIndex; - uint32_t posPeak1SpecFlatFX, posPeak2SpecFlatFX; - uint32_t posPeak1SpecDiffFX, posPeak2SpecDiffFX; - - int32_t tmp32; - int32_t fluctLrtFX, thresFluctLrtFX; - int32_t avgHistLrtFX, avgSquareHistLrtFX, avgHistLrtComplFX; - - int16_t j; - int16_t numHistLrt; - - int i; - int useFeatureSpecFlat, useFeatureSpecDiff, featureSum; - int maxPeak1, maxPeak2; - int weightPeak1SpecFlat, weightPeak2SpecFlat; - int weightPeak1SpecDiff, weightPeak2SpecDiff; - - //update histograms - if (!flag) { - // LRT - // Type casting to UWord32 is safe since negative values will not be wrapped to larger - // values than HIST_PAR_EST - histIndex = (uint32_t)(inst->featureLogLrt); - if (histIndex < HIST_PAR_EST) { - inst->histLrt[histIndex]++; - } - // Spectral flatness - // (inst->featureSpecFlat*20)>>10 = (inst->featureSpecFlat*5)>>8 - histIndex = (inst->featureSpecFlat * 5) >> 8; - if (histIndex < HIST_PAR_EST) { - inst->histSpecFlat[histIndex]++; - } - // Spectral difference - histIndex = HIST_PAR_EST; - if (inst->timeAvgMagnEnergy > 0) { - // Guard against division by zero - // If timeAvgMagnEnergy == 0 we have no normalizing statistics and - // therefore can't update the histogram - histIndex = ((inst->featureSpecDiff * 5) >> inst->stages) / - inst->timeAvgMagnEnergy; - } - if (histIndex < HIST_PAR_EST) { - inst->histSpecDiff[histIndex]++; - } - } - - // extract parameters for speech/noise probability - if (flag) { - useFeatureSpecDiff = 1; - //for LRT feature: - // compute the average over inst->featureExtractionParams.rangeAvgHistLrt - avgHistLrtFX = 0; - avgSquareHistLrtFX = 0; - numHistLrt = 0; - for (i = 0; i < BIN_SIZE_LRT; i++) { - j = (2 * i + 1); - tmp32 = inst->histLrt[i] * j; - avgHistLrtFX += tmp32; - numHistLrt += inst->histLrt[i]; - avgSquareHistLrtFX += tmp32 * j; - } - avgHistLrtComplFX = avgHistLrtFX; - for (; i < HIST_PAR_EST; i++) { - j = (2 * i + 1); - tmp32 = inst->histLrt[i] * j; - avgHistLrtComplFX += tmp32; - avgSquareHistLrtFX += tmp32 * j; - } - fluctLrtFX = avgSquareHistLrtFX * numHistLrt - - avgHistLrtFX * avgHistLrtComplFX; - thresFluctLrtFX = THRES_FLUCT_LRT * numHistLrt; - // get threshold for LRT feature: - tmpU32 = (FACTOR_1_LRT_DIFF * (uint32_t)avgHistLrtFX); - if ((fluctLrtFX < thresFluctLrtFX) || (numHistLrt == 0) || - (tmpU32 > (uint32_t)(100 * numHistLrt))) { - //very low fluctuation, so likely noise - inst->thresholdLogLrt = inst->maxLrt; - } else { - tmp32 = (int32_t)((tmpU32 << (9 + inst->stages)) / numHistLrt / - 25); - // check if value is within min/max range - inst->thresholdLogLrt = WEBRTC_SPL_SAT(inst->maxLrt, - tmp32, - inst->minLrt); - } - if (fluctLrtFX < thresFluctLrtFX) { - // Do not use difference feature if fluctuation of LRT feature is very low: - // most likely just noise state - useFeatureSpecDiff = 0; - } - - // for spectral flatness and spectral difference: compute the main peaks of histogram - maxPeak1 = 0; - maxPeak2 = 0; - posPeak1SpecFlatFX = 0; - posPeak2SpecFlatFX = 0; - weightPeak1SpecFlat = 0; - weightPeak2SpecFlat = 0; - - // peaks for flatness - for (i = 0; i < HIST_PAR_EST; i++) { - if (inst->histSpecFlat[i] > maxPeak1) { - // Found new "first" peak - maxPeak2 = maxPeak1; - weightPeak2SpecFlat = weightPeak1SpecFlat; - posPeak2SpecFlatFX = posPeak1SpecFlatFX; - - maxPeak1 = inst->histSpecFlat[i]; - weightPeak1SpecFlat = inst->histSpecFlat[i]; - posPeak1SpecFlatFX = (uint32_t)(2 * i + 1); - } else if (inst->histSpecFlat[i] > maxPeak2) { - // Found new "second" peak - maxPeak2 = inst->histSpecFlat[i]; - weightPeak2SpecFlat = inst->histSpecFlat[i]; - posPeak2SpecFlatFX = (uint32_t)(2 * i + 1); - } - } - - // for spectral flatness feature - useFeatureSpecFlat = 1; - // merge the two peaks if they are close - if ((posPeak1SpecFlatFX - posPeak2SpecFlatFX < LIM_PEAK_SPACE_FLAT_DIFF) - && (weightPeak2SpecFlat * LIM_PEAK_WEIGHT_FLAT_DIFF > weightPeak1SpecFlat)) { - weightPeak1SpecFlat += weightPeak2SpecFlat; - posPeak1SpecFlatFX = (posPeak1SpecFlatFX + posPeak2SpecFlatFX) >> 1; - } - //reject if weight of peaks is not large enough, or peak value too small - if (weightPeak1SpecFlat < THRES_WEIGHT_FLAT_DIFF || posPeak1SpecFlatFX - < THRES_PEAK_FLAT) { - useFeatureSpecFlat = 0; - } else { // if selected, get the threshold - // compute the threshold and check if value is within min/max range - inst->thresholdSpecFlat = WEBRTC_SPL_SAT(MAX_FLAT_Q10, FACTOR_2_FLAT_Q10 - * posPeak1SpecFlatFX, MIN_FLAT_Q10); //Q10 - } - // done with flatness feature - - if (useFeatureSpecDiff) { - //compute two peaks for spectral difference - maxPeak1 = 0; - maxPeak2 = 0; - posPeak1SpecDiffFX = 0; - posPeak2SpecDiffFX = 0; - weightPeak1SpecDiff = 0; - weightPeak2SpecDiff = 0; - // peaks for spectral difference - for (i = 0; i < HIST_PAR_EST; i++) { - if (inst->histSpecDiff[i] > maxPeak1) { - // Found new "first" peak - maxPeak2 = maxPeak1; - weightPeak2SpecDiff = weightPeak1SpecDiff; - posPeak2SpecDiffFX = posPeak1SpecDiffFX; - - maxPeak1 = inst->histSpecDiff[i]; - weightPeak1SpecDiff = inst->histSpecDiff[i]; - posPeak1SpecDiffFX = (uint32_t)(2 * i + 1); - } else if (inst->histSpecDiff[i] > maxPeak2) { - // Found new "second" peak - maxPeak2 = inst->histSpecDiff[i]; - weightPeak2SpecDiff = inst->histSpecDiff[i]; - posPeak2SpecDiffFX = (uint32_t)(2 * i + 1); - } - } - - // merge the two peaks if they are close - if ((posPeak1SpecDiffFX - posPeak2SpecDiffFX < LIM_PEAK_SPACE_FLAT_DIFF) - && (weightPeak2SpecDiff * LIM_PEAK_WEIGHT_FLAT_DIFF > weightPeak1SpecDiff)) { - weightPeak1SpecDiff += weightPeak2SpecDiff; - posPeak1SpecDiffFX = (posPeak1SpecDiffFX + posPeak2SpecDiffFX) >> 1; - } - // get the threshold value and check if value is within min/max range - inst->thresholdSpecDiff = WEBRTC_SPL_SAT(MAX_DIFF, FACTOR_1_LRT_DIFF - * posPeak1SpecDiffFX, MIN_DIFF); //5x bigger - //reject if weight of peaks is not large enough - if (weightPeak1SpecDiff < THRES_WEIGHT_FLAT_DIFF) { - useFeatureSpecDiff = 0; - } - // done with spectral difference feature - } - - // select the weights between the features - // inst->priorModelPars[4] is weight for LRT: always selected - featureSum = 6 / (1 + useFeatureSpecFlat + useFeatureSpecDiff); - inst->weightLogLrt = featureSum; - inst->weightSpecFlat = useFeatureSpecFlat * featureSum; - inst->weightSpecDiff = useFeatureSpecDiff * featureSum; - - // set histograms to zero for next update - WebRtcSpl_ZerosArrayW16(inst->histLrt, HIST_PAR_EST); - WebRtcSpl_ZerosArrayW16(inst->histSpecDiff, HIST_PAR_EST); - WebRtcSpl_ZerosArrayW16(inst->histSpecFlat, HIST_PAR_EST); - } // end of flag == 1 -} - - -// Compute spectral flatness on input spectrum -// magn is the magnitude spectrum -// spectral flatness is returned in inst->featureSpecFlat -void WebRtcNsx_ComputeSpectralFlatness(NoiseSuppressionFixedC* inst, - uint16_t* magn) { - uint32_t tmpU32; - uint32_t avgSpectralFlatnessNum, avgSpectralFlatnessDen; - - int32_t tmp32; - int32_t currentSpectralFlatness, logCurSpectralFlatness; - - int16_t zeros, frac, intPart; - - size_t i; - - // for flatness - avgSpectralFlatnessNum = 0; - avgSpectralFlatnessDen = inst->sumMagn - (uint32_t)magn[0]; // Q(normData-stages) - - // compute log of ratio of the geometric to arithmetic mean: check for log(0) case - // flatness = exp( sum(log(magn[i]))/N - log(sum(magn[i])/N) ) - // = exp( sum(log(magn[i]))/N ) * N / sum(magn[i]) - // = 2^( sum(log2(magn[i]))/N - (log2(sum(magn[i])) - log2(N)) ) [This is used] - for (i = 1; i < inst->magnLen; i++) { - // First bin is excluded from spectrum measures. Number of bins is now a power of 2 - if (magn[i]) { - zeros = WebRtcSpl_NormU32((uint32_t)magn[i]); - frac = (int16_t)(((uint32_t)((uint32_t)(magn[i]) << zeros) - & 0x7FFFFFFF) >> 23); - // log2(magn(i)) - assert(frac < 256); - tmpU32 = (uint32_t)(((31 - zeros) << 8) - + WebRtcNsx_kLogTableFrac[frac]); // Q8 - avgSpectralFlatnessNum += tmpU32; // Q8 - } else { - //if at least one frequency component is zero, treat separately - tmpU32 = WEBRTC_SPL_UMUL_32_16(inst->featureSpecFlat, SPECT_FLAT_TAVG_Q14); // Q24 - inst->featureSpecFlat -= tmpU32 >> 14; // Q10 - return; - } - } - //ratio and inverse log: check for case of log(0) - zeros = WebRtcSpl_NormU32(avgSpectralFlatnessDen); - frac = (int16_t)(((avgSpectralFlatnessDen << zeros) & 0x7FFFFFFF) >> 23); - // log2(avgSpectralFlatnessDen) - assert(frac < 256); - tmp32 = (int32_t)(((31 - zeros) << 8) + WebRtcNsx_kLogTableFrac[frac]); // Q8 - logCurSpectralFlatness = (int32_t)avgSpectralFlatnessNum; - logCurSpectralFlatness += ((int32_t)(inst->stages - 1) << (inst->stages + 7)); // Q(8+stages-1) - logCurSpectralFlatness -= (tmp32 << (inst->stages - 1)); - logCurSpectralFlatness <<= (10 - inst->stages); // Q17 - tmp32 = (int32_t)(0x00020000 | (WEBRTC_SPL_ABS_W32(logCurSpectralFlatness) - & 0x0001FFFF)); //Q17 - intPart = 7 - (logCurSpectralFlatness >> 17); // Add 7 for output in Q10. - if (intPart > 0) { - currentSpectralFlatness = tmp32 >> intPart; - } else { - currentSpectralFlatness = tmp32 << -intPart; - } - - //time average update of spectral flatness feature - tmp32 = currentSpectralFlatness - (int32_t)inst->featureSpecFlat; // Q10 - tmp32 *= SPECT_FLAT_TAVG_Q14; // Q24 - inst->featureSpecFlat += tmp32 >> 14; // Q10 - // done with flatness feature -} - - -// Compute the difference measure between input spectrum and a template/learned noise spectrum -// magn_tmp is the input spectrum -// the reference/template spectrum is inst->magn_avg_pause[i] -// returns (normalized) spectral difference in inst->featureSpecDiff -void WebRtcNsx_ComputeSpectralDifference(NoiseSuppressionFixedC* inst, - uint16_t* magnIn) { - // This is to be calculated: - // avgDiffNormMagn = var(magnIn) - cov(magnIn, magnAvgPause)^2 / var(magnAvgPause) - - uint32_t tmpU32no1, tmpU32no2; - uint32_t varMagnUFX, varPauseUFX, avgDiffNormMagnUFX; - - int32_t tmp32no1, tmp32no2; - int32_t avgPauseFX, avgMagnFX, covMagnPauseFX; - int32_t maxPause, minPause; - - int16_t tmp16no1; - - size_t i; - int norm32, nShifts; - - avgPauseFX = 0; - maxPause = 0; - minPause = inst->avgMagnPause[0]; // Q(prevQMagn) - // compute average quantities - for (i = 0; i < inst->magnLen; i++) { - // Compute mean of magn_pause - avgPauseFX += inst->avgMagnPause[i]; // in Q(prevQMagn) - maxPause = WEBRTC_SPL_MAX(maxPause, inst->avgMagnPause[i]); - minPause = WEBRTC_SPL_MIN(minPause, inst->avgMagnPause[i]); - } - // normalize by replacing div of "inst->magnLen" with "inst->stages-1" shifts - avgPauseFX >>= inst->stages - 1; - avgMagnFX = inst->sumMagn >> (inst->stages - 1); - // Largest possible deviation in magnPause for (co)var calculations - tmp32no1 = WEBRTC_SPL_MAX(maxPause - avgPauseFX, avgPauseFX - minPause); - // Get number of shifts to make sure we don't get wrap around in varPause - nShifts = WEBRTC_SPL_MAX(0, 10 + inst->stages - WebRtcSpl_NormW32(tmp32no1)); - - varMagnUFX = 0; - varPauseUFX = 0; - covMagnPauseFX = 0; - for (i = 0; i < inst->magnLen; i++) { - // Compute var and cov of magn and magn_pause - tmp16no1 = (int16_t)((int32_t)magnIn[i] - avgMagnFX); - tmp32no2 = inst->avgMagnPause[i] - avgPauseFX; - varMagnUFX += (uint32_t)(tmp16no1 * tmp16no1); // Q(2*qMagn) - tmp32no1 = tmp32no2 * tmp16no1; // Q(prevQMagn+qMagn) - covMagnPauseFX += tmp32no1; // Q(prevQMagn+qMagn) - tmp32no1 = tmp32no2 >> nShifts; // Q(prevQMagn-minPause). - varPauseUFX += tmp32no1 * tmp32no1; // Q(2*(prevQMagn-minPause)) - } - //update of average magnitude spectrum: Q(-2*stages) and averaging replaced by shifts - inst->curAvgMagnEnergy += - inst->magnEnergy >> (2 * inst->normData + inst->stages - 1); - - avgDiffNormMagnUFX = varMagnUFX; // Q(2*qMagn) - if ((varPauseUFX) && (covMagnPauseFX)) { - tmpU32no1 = (uint32_t)WEBRTC_SPL_ABS_W32(covMagnPauseFX); // Q(prevQMagn+qMagn) - norm32 = WebRtcSpl_NormU32(tmpU32no1) - 16; - if (norm32 > 0) { - tmpU32no1 <<= norm32; // Q(prevQMagn+qMagn+norm32) - } else { - tmpU32no1 >>= -norm32; // Q(prevQMagn+qMagn+norm32) - } - tmpU32no2 = WEBRTC_SPL_UMUL(tmpU32no1, tmpU32no1); // Q(2*(prevQMagn+qMagn-norm32)) - - nShifts += norm32; - nShifts <<= 1; - if (nShifts < 0) { - varPauseUFX >>= (-nShifts); // Q(2*(qMagn+norm32+minPause)) - nShifts = 0; - } - if (varPauseUFX > 0) { - // Q(2*(qMagn+norm32-16+minPause)) - tmpU32no1 = tmpU32no2 / varPauseUFX; - tmpU32no1 >>= nShifts; - - // Q(2*qMagn) - avgDiffNormMagnUFX -= WEBRTC_SPL_MIN(avgDiffNormMagnUFX, tmpU32no1); - } else { - avgDiffNormMagnUFX = 0; - } - } - //normalize and compute time average update of difference feature - tmpU32no1 = avgDiffNormMagnUFX >> (2 * inst->normData); - if (inst->featureSpecDiff > tmpU32no1) { - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(inst->featureSpecDiff - tmpU32no1, - SPECT_DIFF_TAVG_Q8); // Q(8-2*stages) - inst->featureSpecDiff -= tmpU32no2 >> 8; // Q(-2*stages) - } else { - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no1 - inst->featureSpecDiff, - SPECT_DIFF_TAVG_Q8); // Q(8-2*stages) - inst->featureSpecDiff += tmpU32no2 >> 8; // Q(-2*stages) - } -} - -// Transform input (speechFrame) to frequency domain magnitude (magnU16) -void WebRtcNsx_DataAnalysis(NoiseSuppressionFixedC* inst, - short* speechFrame, - uint16_t* magnU16) { - uint32_t tmpU32no1; - - int32_t tmp_1_w32 = 0; - int32_t tmp_2_w32 = 0; - int32_t sum_log_magn = 0; - int32_t sum_log_i_log_magn = 0; - - uint16_t sum_log_magn_u16 = 0; - uint16_t tmp_u16 = 0; - - int16_t sum_log_i = 0; - int16_t sum_log_i_square = 0; - int16_t frac = 0; - int16_t log2 = 0; - int16_t matrix_determinant = 0; - int16_t maxWinData; - - size_t i, j; - int zeros; - int net_norm = 0; - int right_shifts_in_magnU16 = 0; - int right_shifts_in_initMagnEst = 0; - - int16_t winData_buff[ANAL_BLOCKL_MAX * 2 + 16]; - int16_t realImag_buff[ANAL_BLOCKL_MAX * 2 + 16]; - - // Align the structures to 32-byte boundary for the FFT function. - int16_t* winData = (int16_t*) (((uintptr_t)winData_buff + 31) & ~31); - int16_t* realImag = (int16_t*) (((uintptr_t) realImag_buff + 31) & ~31); - - // Update analysis buffer for lower band, and window data before FFT. - WebRtcNsx_AnalysisUpdate(inst, winData, speechFrame); - - // Get input energy - inst->energyIn = - WebRtcSpl_Energy(winData, inst->anaLen, &inst->scaleEnergyIn); - - // Reset zero input flag - inst->zeroInputSignal = 0; - // Acquire norm for winData - maxWinData = WebRtcSpl_MaxAbsValueW16(winData, inst->anaLen); - inst->normData = WebRtcSpl_NormW16(maxWinData); - if (maxWinData == 0) { - // Treat zero input separately. - inst->zeroInputSignal = 1; - return; - } - - // Determine the net normalization in the frequency domain - net_norm = inst->stages - inst->normData; - // Track lowest normalization factor and use it to prevent wrap around in shifting - right_shifts_in_magnU16 = inst->normData - inst->minNorm; - right_shifts_in_initMagnEst = WEBRTC_SPL_MAX(-right_shifts_in_magnU16, 0); - inst->minNorm -= right_shifts_in_initMagnEst; - right_shifts_in_magnU16 = WEBRTC_SPL_MAX(right_shifts_in_magnU16, 0); - - // create realImag as winData interleaved with zeros (= imag. part), normalize it - WebRtcNsx_NormalizeRealBuffer(inst, winData, realImag); - - // FFT output will be in winData[]. - WebRtcSpl_RealForwardFFT(inst->real_fft, realImag, winData); - - inst->imag[0] = 0; // Q(normData-stages) - inst->imag[inst->anaLen2] = 0; - inst->real[0] = winData[0]; // Q(normData-stages) - inst->real[inst->anaLen2] = winData[inst->anaLen]; - // Q(2*(normData-stages)) - inst->magnEnergy = (uint32_t)(inst->real[0] * inst->real[0]); - inst->magnEnergy += (uint32_t)(inst->real[inst->anaLen2] * - inst->real[inst->anaLen2]); - magnU16[0] = (uint16_t)WEBRTC_SPL_ABS_W16(inst->real[0]); // Q(normData-stages) - magnU16[inst->anaLen2] = (uint16_t)WEBRTC_SPL_ABS_W16(inst->real[inst->anaLen2]); - inst->sumMagn = (uint32_t)magnU16[0]; // Q(normData-stages) - inst->sumMagn += (uint32_t)magnU16[inst->anaLen2]; - - if (inst->blockIndex >= END_STARTUP_SHORT) { - for (i = 1, j = 2; i < inst->anaLen2; i += 1, j += 2) { - inst->real[i] = winData[j]; - inst->imag[i] = -winData[j + 1]; - // magnitude spectrum - // energy in Q(2*(normData-stages)) - tmpU32no1 = (uint32_t)(winData[j] * winData[j]); - tmpU32no1 += (uint32_t)(winData[j + 1] * winData[j + 1]); - inst->magnEnergy += tmpU32no1; // Q(2*(normData-stages)) - - magnU16[i] = (uint16_t)WebRtcSpl_SqrtFloor(tmpU32no1); // Q(normData-stages) - inst->sumMagn += (uint32_t)magnU16[i]; // Q(normData-stages) - } - } else { - // - // Gather information during startup for noise parameter estimation - // - - // Switch initMagnEst to Q(minNorm-stages) - inst->initMagnEst[0] >>= right_shifts_in_initMagnEst; - inst->initMagnEst[inst->anaLen2] >>= right_shifts_in_initMagnEst; - - // Update initMagnEst with magnU16 in Q(minNorm-stages). - inst->initMagnEst[0] += magnU16[0] >> right_shifts_in_magnU16; - inst->initMagnEst[inst->anaLen2] += - magnU16[inst->anaLen2] >> right_shifts_in_magnU16; - - log2 = 0; - if (magnU16[inst->anaLen2]) { - // Calculate log2(magnU16[inst->anaLen2]) - zeros = WebRtcSpl_NormU32((uint32_t)magnU16[inst->anaLen2]); - frac = (int16_t)((((uint32_t)magnU16[inst->anaLen2] << zeros) & - 0x7FFFFFFF) >> 23); // Q8 - // log2(magnU16(i)) in Q8 - assert(frac < 256); - log2 = (int16_t)(((31 - zeros) << 8) + WebRtcNsx_kLogTableFrac[frac]); - } - - sum_log_magn = (int32_t)log2; // Q8 - // sum_log_i_log_magn in Q17 - sum_log_i_log_magn = (kLogIndex[inst->anaLen2] * log2) >> 3; - - for (i = 1, j = 2; i < inst->anaLen2; i += 1, j += 2) { - inst->real[i] = winData[j]; - inst->imag[i] = -winData[j + 1]; - // magnitude spectrum - // energy in Q(2*(normData-stages)) - tmpU32no1 = (uint32_t)(winData[j] * winData[j]); - tmpU32no1 += (uint32_t)(winData[j + 1] * winData[j + 1]); - inst->magnEnergy += tmpU32no1; // Q(2*(normData-stages)) - - magnU16[i] = (uint16_t)WebRtcSpl_SqrtFloor(tmpU32no1); // Q(normData-stages) - inst->sumMagn += (uint32_t)magnU16[i]; // Q(normData-stages) - - // Switch initMagnEst to Q(minNorm-stages) - inst->initMagnEst[i] >>= right_shifts_in_initMagnEst; - - // Update initMagnEst with magnU16 in Q(minNorm-stages). - inst->initMagnEst[i] += magnU16[i] >> right_shifts_in_magnU16; - - if (i >= kStartBand) { - // For pink noise estimation. Collect data neglecting lower frequency band - log2 = 0; - if (magnU16[i]) { - zeros = WebRtcSpl_NormU32((uint32_t)magnU16[i]); - frac = (int16_t)((((uint32_t)magnU16[i] << zeros) & - 0x7FFFFFFF) >> 23); - // log2(magnU16(i)) in Q8 - assert(frac < 256); - log2 = (int16_t)(((31 - zeros) << 8) - + WebRtcNsx_kLogTableFrac[frac]); - } - sum_log_magn += (int32_t)log2; // Q8 - // sum_log_i_log_magn in Q17 - sum_log_i_log_magn += (kLogIndex[i] * log2) >> 3; - } - } - - // - //compute simplified noise model during startup - // - - // Estimate White noise - - // Switch whiteNoiseLevel to Q(minNorm-stages) - inst->whiteNoiseLevel >>= right_shifts_in_initMagnEst; - - // Update the average magnitude spectrum, used as noise estimate. - tmpU32no1 = WEBRTC_SPL_UMUL_32_16(inst->sumMagn, inst->overdrive); - tmpU32no1 >>= inst->stages + 8; - - // Replacing division above with 'stages' shifts - // Shift to same Q-domain as whiteNoiseLevel - tmpU32no1 >>= right_shifts_in_magnU16; - // This operation is safe from wrap around as long as END_STARTUP_SHORT < 128 - assert(END_STARTUP_SHORT < 128); - inst->whiteNoiseLevel += tmpU32no1; // Q(minNorm-stages) - - // Estimate Pink noise parameters - // Denominator used in both parameter estimates. - // The value is only dependent on the size of the frequency band (kStartBand) - // and to reduce computational complexity stored in a table (kDeterminantEstMatrix[]) - assert(kStartBand < 66); - matrix_determinant = kDeterminantEstMatrix[kStartBand]; // Q0 - sum_log_i = kSumLogIndex[kStartBand]; // Q5 - sum_log_i_square = kSumSquareLogIndex[kStartBand]; // Q2 - if (inst->fs == 8000) { - // Adjust values to shorter blocks in narrow band. - tmp_1_w32 = (int32_t)matrix_determinant; - tmp_1_w32 += (kSumLogIndex[65] * sum_log_i) >> 9; - tmp_1_w32 -= (kSumLogIndex[65] * kSumLogIndex[65]) >> 10; - tmp_1_w32 -= (int32_t)sum_log_i_square << 4; - tmp_1_w32 -= ((inst->magnLen - kStartBand) * kSumSquareLogIndex[65]) >> 2; - matrix_determinant = (int16_t)tmp_1_w32; - sum_log_i -= kSumLogIndex[65]; // Q5 - sum_log_i_square -= kSumSquareLogIndex[65]; // Q2 - } - - // Necessary number of shifts to fit sum_log_magn in a word16 - zeros = 16 - WebRtcSpl_NormW32(sum_log_magn); - if (zeros < 0) { - zeros = 0; - } - tmp_1_w32 = sum_log_magn << 1; // Q9 - sum_log_magn_u16 = (uint16_t)(tmp_1_w32 >> zeros); // Q(9-zeros). - - // Calculate and update pinkNoiseNumerator. Result in Q11. - tmp_2_w32 = WEBRTC_SPL_MUL_16_U16(sum_log_i_square, sum_log_magn_u16); // Q(11-zeros) - tmpU32no1 = sum_log_i_log_magn >> 12; // Q5 - - // Shift the largest value of sum_log_i and tmp32no3 before multiplication - tmp_u16 = ((uint16_t)sum_log_i << 1); // Q6 - if ((uint32_t)sum_log_i > tmpU32no1) { - tmp_u16 >>= zeros; - } else { - tmpU32no1 >>= zeros; - } - tmp_2_w32 -= (int32_t)WEBRTC_SPL_UMUL_32_16(tmpU32no1, tmp_u16); // Q(11-zeros) - matrix_determinant >>= zeros; // Q(-zeros) - tmp_2_w32 = WebRtcSpl_DivW32W16(tmp_2_w32, matrix_determinant); // Q11 - tmp_2_w32 += (int32_t)net_norm << 11; // Q11 - if (tmp_2_w32 < 0) { - tmp_2_w32 = 0; - } - inst->pinkNoiseNumerator += tmp_2_w32; // Q11 - - // Calculate and update pinkNoiseExp. Result in Q14. - tmp_2_w32 = WEBRTC_SPL_MUL_16_U16(sum_log_i, sum_log_magn_u16); // Q(14-zeros) - tmp_1_w32 = sum_log_i_log_magn >> (3 + zeros); - tmp_1_w32 *= inst->magnLen - kStartBand; - tmp_2_w32 -= tmp_1_w32; // Q(14-zeros) - if (tmp_2_w32 > 0) { - // If the exponential parameter is negative force it to zero, which means a - // flat spectrum. - tmp_1_w32 = WebRtcSpl_DivW32W16(tmp_2_w32, matrix_determinant); // Q14 - inst->pinkNoiseExp += WEBRTC_SPL_SAT(16384, tmp_1_w32, 0); // Q14 - } - } -} - -void WebRtcNsx_DataSynthesis(NoiseSuppressionFixedC* inst, short* outFrame) { - int32_t energyOut; - - int16_t realImag_buff[ANAL_BLOCKL_MAX * 2 + 16]; - int16_t rfft_out_buff[ANAL_BLOCKL_MAX * 2 + 16]; - - // Align the structures to 32-byte boundary for the FFT function. - int16_t* realImag = (int16_t*) (((uintptr_t)realImag_buff + 31) & ~31); - int16_t* rfft_out = (int16_t*) (((uintptr_t) rfft_out_buff + 31) & ~31); - - int16_t tmp16no1, tmp16no2; - int16_t energyRatio; - int16_t gainFactor, gainFactor1, gainFactor2; - - size_t i; - int outCIFFT; - int scaleEnergyOut = 0; - - if (inst->zeroInputSignal) { - // synthesize the special case of zero input - // read out fully processed segment - for (i = 0; i < inst->blockLen10ms; i++) { - outFrame[i] = inst->synthesisBuffer[i]; // Q0 - } - // update synthesis buffer - memcpy(inst->synthesisBuffer, inst->synthesisBuffer + inst->blockLen10ms, - (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->synthesisBuffer)); - WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer + inst->anaLen - inst->blockLen10ms, - inst->blockLen10ms); - return; - } - - // Filter the data in the frequency domain, and create spectrum. - WebRtcNsx_PrepareSpectrum(inst, realImag); - - // Inverse FFT output will be in rfft_out[]. - outCIFFT = WebRtcSpl_RealInverseFFT(inst->real_fft, realImag, rfft_out); - - WebRtcNsx_Denormalize(inst, rfft_out, outCIFFT); - - //scale factor: only do it after END_STARTUP_LONG time - gainFactor = 8192; // 8192 = Q13(1.0) - if (inst->gainMap == 1 && - inst->blockIndex > END_STARTUP_LONG && - inst->energyIn > 0) { - // Q(-scaleEnergyOut) - energyOut = WebRtcSpl_Energy(inst->real, inst->anaLen, &scaleEnergyOut); - if (scaleEnergyOut == 0 && !(energyOut & 0x7f800000)) { - energyOut = WEBRTC_SPL_SHIFT_W32(energyOut, 8 + scaleEnergyOut - - inst->scaleEnergyIn); - } else { - // |energyIn| is currently in Q(|scaleEnergyIn|), but to later on end up - // with an |energyRatio| in Q8 we need to change the Q-domain to - // Q(-8-scaleEnergyOut). - inst->energyIn >>= 8 + scaleEnergyOut - inst->scaleEnergyIn; - } - - assert(inst->energyIn > 0); - energyRatio = (energyOut + inst->energyIn / 2) / inst->energyIn; // Q8 - // Limit the ratio to [0, 1] in Q8, i.e., [0, 256] - energyRatio = WEBRTC_SPL_SAT(256, energyRatio, 0); - - // all done in lookup tables now - assert(energyRatio < 257); - gainFactor1 = kFactor1Table[energyRatio]; // Q8 - gainFactor2 = inst->factor2Table[energyRatio]; // Q8 - - //combine both scales with speech/noise prob: note prior (priorSpeechProb) is not frequency dependent - - // factor = inst->priorSpeechProb*factor1 + (1.0-inst->priorSpeechProb)*factor2; // original code - tmp16no1 = (int16_t)(((16384 - inst->priorNonSpeechProb) * gainFactor1) >> - 14); // in Q13, where 16384 = Q14(1.0) - tmp16no2 = (int16_t)((inst->priorNonSpeechProb * gainFactor2) >> 14); - gainFactor = tmp16no1 + tmp16no2; // Q13 - } // out of flag_gain_map==1 - - // Synthesis, read out fully processed segment, and update synthesis buffer. - WebRtcNsx_SynthesisUpdate(inst, outFrame, gainFactor); -} - -void WebRtcNsx_ProcessCore(NoiseSuppressionFixedC* inst, - const short* const* speechFrame, - int num_bands, - short* const* outFrame) { - // main routine for noise suppression - - uint32_t tmpU32no1, tmpU32no2, tmpU32no3; - uint32_t satMax, maxNoiseU32; - uint32_t tmpMagnU32, tmpNoiseU32; - uint32_t nearMagnEst; - uint32_t noiseUpdateU32; - uint32_t noiseU32[HALF_ANAL_BLOCKL]; - uint32_t postLocSnr[HALF_ANAL_BLOCKL]; - uint32_t priorLocSnr[HALF_ANAL_BLOCKL]; - uint32_t prevNearSnr[HALF_ANAL_BLOCKL]; - uint32_t curNearSnr; - uint32_t priorSnr; - uint32_t noise_estimate = 0; - uint32_t noise_estimate_avg = 0; - uint32_t numerator = 0; - - int32_t tmp32no1, tmp32no2; - int32_t pink_noise_num_avg = 0; - - uint16_t tmpU16no1; - uint16_t magnU16[HALF_ANAL_BLOCKL]; - uint16_t prevNoiseU16[HALF_ANAL_BLOCKL]; - uint16_t nonSpeechProbFinal[HALF_ANAL_BLOCKL]; - uint16_t gammaNoise, prevGammaNoise; - uint16_t noiseSupFilterTmp[HALF_ANAL_BLOCKL]; - - int16_t qMagn, qNoise; - int16_t avgProbSpeechHB, gainModHB, avgFilterGainHB, gainTimeDomainHB; - int16_t pink_noise_exp_avg = 0; - - size_t i, j; - int nShifts, postShifts; - int norm32no1, norm32no2; - int flag, sign; - int q_domain_to_use = 0; - - // Code for ARMv7-Neon platform assumes the following: - assert(inst->anaLen > 0); - assert(inst->anaLen2 > 0); - assert(inst->anaLen % 16 == 0); - assert(inst->anaLen2 % 8 == 0); - assert(inst->blockLen10ms > 0); - assert(inst->blockLen10ms % 16 == 0); - assert(inst->magnLen == inst->anaLen2 + 1); - -#ifdef NS_FILEDEBUG - if (fwrite(spframe, sizeof(short), - inst->blockLen10ms, inst->infile) != inst->blockLen10ms) { - assert(false); - } -#endif - - // Check that initialization has been done - assert(inst->initFlag == 1); - assert((num_bands - 1) <= NUM_HIGH_BANDS_MAX); - - const short* const* speechFrameHB = NULL; - short* const* outFrameHB = NULL; - size_t num_high_bands = 0; - if (num_bands > 1) { - speechFrameHB = &speechFrame[1]; - outFrameHB = &outFrame[1]; - num_high_bands = (size_t)(num_bands - 1); - } - - // Store speechFrame and transform to frequency domain - WebRtcNsx_DataAnalysis(inst, (short*)speechFrame[0], magnU16); - - if (inst->zeroInputSignal) { - WebRtcNsx_DataSynthesis(inst, outFrame[0]); - - if (num_bands > 1) { - // update analysis buffer for H band - // append new data to buffer FX - for (i = 0; i < num_high_bands; ++i) { - int block_shift = inst->anaLen - inst->blockLen10ms; - memcpy(inst->dataBufHBFX[i], inst->dataBufHBFX[i] + inst->blockLen10ms, - block_shift * sizeof(*inst->dataBufHBFX[i])); - memcpy(inst->dataBufHBFX[i] + block_shift, speechFrameHB[i], - inst->blockLen10ms * sizeof(*inst->dataBufHBFX[i])); - for (j = 0; j < inst->blockLen10ms; j++) { - outFrameHB[i][j] = inst->dataBufHBFX[i][j]; // Q0 - } - } - } // end of H band gain computation - return; - } - - // Update block index when we have something to process - inst->blockIndex++; - // - - // Norm of magn - qMagn = inst->normData - inst->stages; - - // Compute spectral flatness on input spectrum - WebRtcNsx_ComputeSpectralFlatness(inst, magnU16); - - // quantile noise estimate - WebRtcNsx_NoiseEstimation(inst, magnU16, noiseU32, &qNoise); - - //noise estimate from previous frame - for (i = 0; i < inst->magnLen; i++) { - prevNoiseU16[i] = (uint16_t)(inst->prevNoiseU32[i] >> 11); // Q(prevQNoise) - } - - if (inst->blockIndex < END_STARTUP_SHORT) { - // Noise Q-domain to be used later; see description at end of section. - q_domain_to_use = WEBRTC_SPL_MIN((int)qNoise, inst->minNorm - inst->stages); - - // Calculate frequency independent parts in parametric noise estimate and calculate - // the estimate for the lower frequency band (same values for all frequency bins) - if (inst->pinkNoiseExp) { - pink_noise_exp_avg = (int16_t)WebRtcSpl_DivW32W16(inst->pinkNoiseExp, - (int16_t)(inst->blockIndex + 1)); // Q14 - pink_noise_num_avg = WebRtcSpl_DivW32W16(inst->pinkNoiseNumerator, - (int16_t)(inst->blockIndex + 1)); // Q11 - WebRtcNsx_CalcParametricNoiseEstimate(inst, - pink_noise_exp_avg, - pink_noise_num_avg, - kStartBand, - &noise_estimate, - &noise_estimate_avg); - } else { - // Use white noise estimate if we have poor pink noise parameter estimates - noise_estimate = inst->whiteNoiseLevel; // Q(minNorm-stages) - noise_estimate_avg = noise_estimate / (inst->blockIndex + 1); // Q(minNorm-stages) - } - for (i = 0; i < inst->magnLen; i++) { - // Estimate the background noise using the pink noise parameters if permitted - if ((inst->pinkNoiseExp) && (i >= kStartBand)) { - // Reset noise_estimate - noise_estimate = 0; - noise_estimate_avg = 0; - // Calculate the parametric noise estimate for current frequency bin - WebRtcNsx_CalcParametricNoiseEstimate(inst, - pink_noise_exp_avg, - pink_noise_num_avg, - i, - &noise_estimate, - &noise_estimate_avg); - } - // Calculate parametric Wiener filter - noiseSupFilterTmp[i] = inst->denoiseBound; - if (inst->initMagnEst[i]) { - // numerator = (initMagnEst - noise_estimate * overdrive) - // Result in Q(8+minNorm-stages) - tmpU32no1 = WEBRTC_SPL_UMUL_32_16(noise_estimate, inst->overdrive); - numerator = inst->initMagnEst[i] << 8; - if (numerator > tmpU32no1) { - // Suppression filter coefficient larger than zero, so calculate. - numerator -= tmpU32no1; - - // Determine number of left shifts in numerator for best accuracy after - // division - nShifts = WebRtcSpl_NormU32(numerator); - nShifts = WEBRTC_SPL_SAT(6, nShifts, 0); - - // Shift numerator to Q(nShifts+8+minNorm-stages) - numerator <<= nShifts; - - // Shift denominator to Q(nShifts-6+minNorm-stages) - tmpU32no1 = inst->initMagnEst[i] >> (6 - nShifts); - if (tmpU32no1 == 0) { - // This is only possible if numerator = 0, in which case - // we don't need any division. - tmpU32no1 = 1; - } - tmpU32no2 = numerator / tmpU32no1; // Q14 - noiseSupFilterTmp[i] = (uint16_t)WEBRTC_SPL_SAT(16384, tmpU32no2, - (uint32_t)(inst->denoiseBound)); // Q14 - } - } - // Weight quantile noise 'noiseU32' with modeled noise 'noise_estimate_avg' - // 'noiseU32 is in Q(qNoise) and 'noise_estimate' in Q(minNorm-stages) - // To guarantee that we do not get wrap around when shifting to the same domain - // we use the lowest one. Furthermore, we need to save 6 bits for the weighting. - // 'noise_estimate_avg' can handle this operation by construction, but 'noiseU32' - // may not. - - // Shift 'noiseU32' to 'q_domain_to_use' - tmpU32no1 = noiseU32[i] >> (qNoise - q_domain_to_use); - // Shift 'noise_estimate_avg' to 'q_domain_to_use' - tmpU32no2 = noise_estimate_avg >> - (inst->minNorm - inst->stages - q_domain_to_use); - // Make a simple check to see if we have enough room for weighting 'tmpU32no1' - // without wrap around - nShifts = 0; - if (tmpU32no1 & 0xfc000000) { - tmpU32no1 >>= 6; - tmpU32no2 >>= 6; - nShifts = 6; - } - tmpU32no1 *= inst->blockIndex; - tmpU32no2 *= (END_STARTUP_SHORT - inst->blockIndex); - // Add them together and divide by startup length - noiseU32[i] = WebRtcSpl_DivU32U16(tmpU32no1 + tmpU32no2, END_STARTUP_SHORT); - // Shift back if necessary - noiseU32[i] <<= nShifts; - } - // Update new Q-domain for 'noiseU32' - qNoise = q_domain_to_use; - } - // compute average signal during END_STARTUP_LONG time: - // used to normalize spectral difference measure - if (inst->blockIndex < END_STARTUP_LONG) { - // substituting division with shift ending up in Q(-2*stages) - inst->timeAvgMagnEnergyTmp += - inst->magnEnergy >> (2 * inst->normData + inst->stages - 1); - inst->timeAvgMagnEnergy = WebRtcSpl_DivU32U16(inst->timeAvgMagnEnergyTmp, - inst->blockIndex + 1); - } - - //start processing at frames == converged+1 - // STEP 1: compute prior and post SNR based on quantile noise estimates - - // compute direct decision (DD) estimate of prior SNR: needed for new method - satMax = (uint32_t)1048575;// Largest possible value without getting overflow despite shifting 12 steps - postShifts = 6 + qMagn - qNoise; - nShifts = 5 - inst->prevQMagn + inst->prevQNoise; - for (i = 0; i < inst->magnLen; i++) { - // FLOAT: - // post SNR - // postLocSnr[i] = 0.0; - // if (magn[i] > noise[i]) - // { - // postLocSnr[i] = magn[i] / (noise[i] + 0.0001); - // } - // // previous post SNR - // // previous estimate: based on previous frame with gain filter (smooth is previous filter) - // - // prevNearSnr[i] = inst->prevMagnU16[i] / (inst->noisePrev[i] + 0.0001) * (inst->smooth[i]); - // - // // DD estimate is sum of two terms: current estimate and previous estimate - // // directed decision update of priorSnr (or we actually store [2*priorSnr+1]) - // - // priorLocSnr[i] = DD_PR_SNR * prevNearSnr[i] + (1.0 - DD_PR_SNR) * (postLocSnr[i] - 1.0); - - // calculate post SNR: output in Q11 - postLocSnr[i] = 2048; // 1.0 in Q11 - tmpU32no1 = (uint32_t)magnU16[i] << 6; // Q(6+qMagn) - if (postShifts < 0) { - tmpU32no2 = noiseU32[i] >> -postShifts; // Q(6+qMagn) - } else { - tmpU32no2 = noiseU32[i] << postShifts; // Q(6+qMagn) - } - if (tmpU32no1 > tmpU32no2) { - // Current magnitude larger than noise - tmpU32no1 <<= 11; // Q(17+qMagn) - if (tmpU32no2 > 0) { - tmpU32no1 /= tmpU32no2; // Q11 - postLocSnr[i] = WEBRTC_SPL_MIN(satMax, tmpU32no1); // Q11 - } else { - postLocSnr[i] = satMax; - } - } - - // calculate prevNearSnr[i] and save for later instead of recalculating it later - // |nearMagnEst| in Q(prevQMagn + 14) - nearMagnEst = inst->prevMagnU16[i] * inst->noiseSupFilter[i]; - tmpU32no1 = nearMagnEst << 3; // Q(prevQMagn+17) - tmpU32no2 = inst->prevNoiseU32[i] >> nShifts; // Q(prevQMagn+6) - - if (tmpU32no2 > 0) { - tmpU32no1 /= tmpU32no2; // Q11 - tmpU32no1 = WEBRTC_SPL_MIN(satMax, tmpU32no1); // Q11 - } else { - tmpU32no1 = satMax; // Q11 - } - prevNearSnr[i] = tmpU32no1; // Q11 - - //directed decision update of priorSnr - tmpU32no1 = WEBRTC_SPL_UMUL_32_16(prevNearSnr[i], DD_PR_SNR_Q11); // Q22 - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(postLocSnr[i] - 2048, ONE_MINUS_DD_PR_SNR_Q11); // Q22 - priorSnr = tmpU32no1 + tmpU32no2 + 512; // Q22 (added 512 for rounding) - // priorLocSnr = 1 + 2*priorSnr - priorLocSnr[i] = 2048 + (priorSnr >> 10); // Q11 - } // end of loop over frequencies - // done with step 1: DD computation of prior and post SNR - - // STEP 2: compute speech/noise likelihood - - //compute difference of input spectrum with learned/estimated noise spectrum - WebRtcNsx_ComputeSpectralDifference(inst, magnU16); - //compute histograms for determination of parameters (thresholds and weights for features) - //parameters are extracted once every window time (=inst->modelUpdate) - //counter update - inst->cntThresUpdate++; - flag = (int)(inst->cntThresUpdate == inst->modelUpdate); - //update histogram - WebRtcNsx_FeatureParameterExtraction(inst, flag); - //compute model parameters - if (flag) { - inst->cntThresUpdate = 0; // Reset counter - //update every window: - // get normalization for spectral difference for next window estimate - - // Shift to Q(-2*stages) - inst->curAvgMagnEnergy >>= STAT_UPDATES; - - tmpU32no1 = (inst->curAvgMagnEnergy + inst->timeAvgMagnEnergy + 1) >> 1; //Q(-2*stages) - // Update featureSpecDiff - if ((tmpU32no1 != inst->timeAvgMagnEnergy) && (inst->featureSpecDiff) && - (inst->timeAvgMagnEnergy > 0)) { - norm32no1 = 0; - tmpU32no3 = tmpU32no1; - while (0xFFFF0000 & tmpU32no3) { - tmpU32no3 >>= 1; - norm32no1++; - } - tmpU32no2 = inst->featureSpecDiff; - while (0xFFFF0000 & tmpU32no2) { - tmpU32no2 >>= 1; - norm32no1++; - } - tmpU32no3 = WEBRTC_SPL_UMUL(tmpU32no3, tmpU32no2); - tmpU32no3 /= inst->timeAvgMagnEnergy; - if (WebRtcSpl_NormU32(tmpU32no3) < norm32no1) { - inst->featureSpecDiff = 0x007FFFFF; - } else { - inst->featureSpecDiff = WEBRTC_SPL_MIN(0x007FFFFF, - tmpU32no3 << norm32no1); - } - } - - inst->timeAvgMagnEnergy = tmpU32no1; // Q(-2*stages) - inst->curAvgMagnEnergy = 0; - } - - //compute speech/noise probability - WebRtcNsx_SpeechNoiseProb(inst, nonSpeechProbFinal, priorLocSnr, postLocSnr); - - //time-avg parameter for noise update - gammaNoise = NOISE_UPDATE_Q8; // Q8 - - maxNoiseU32 = 0; - postShifts = inst->prevQNoise - qMagn; - nShifts = inst->prevQMagn - qMagn; - for (i = 0; i < inst->magnLen; i++) { - // temporary noise update: use it for speech frames if update value is less than previous - // the formula has been rewritten into: - // noiseUpdate = noisePrev[i] + (1 - gammaNoise) * nonSpeechProb * (magn[i] - noisePrev[i]) - - if (postShifts < 0) { - tmpU32no2 = magnU16[i] >> -postShifts; // Q(prevQNoise) - } else { - tmpU32no2 = (uint32_t)magnU16[i] << postShifts; // Q(prevQNoise) - } - if (prevNoiseU16[i] > tmpU32no2) { - sign = -1; - tmpU32no1 = prevNoiseU16[i] - tmpU32no2; - } else { - sign = 1; - tmpU32no1 = tmpU32no2 - prevNoiseU16[i]; - } - noiseUpdateU32 = inst->prevNoiseU32[i]; // Q(prevQNoise+11) - tmpU32no3 = 0; - if ((tmpU32no1) && (nonSpeechProbFinal[i])) { - // This value will be used later, if gammaNoise changes - tmpU32no3 = WEBRTC_SPL_UMUL_32_16(tmpU32no1, nonSpeechProbFinal[i]); // Q(prevQNoise+8) - if (0x7c000000 & tmpU32no3) { - // Shifting required before multiplication - tmpU32no2 = (tmpU32no3 >> 5) * gammaNoise; // Q(prevQNoise+11) - } else { - // We can do shifting after multiplication - tmpU32no2 = (tmpU32no3 * gammaNoise) >> 5; // Q(prevQNoise+11) - } - if (sign > 0) { - noiseUpdateU32 += tmpU32no2; // Q(prevQNoise+11) - } else { - // This operation is safe. We can never get wrap around, since worst - // case scenario means magnU16 = 0 - noiseUpdateU32 -= tmpU32no2; // Q(prevQNoise+11) - } - } - - //increase gamma (i.e., less noise update) for frame likely to be speech - prevGammaNoise = gammaNoise; - gammaNoise = NOISE_UPDATE_Q8; - //time-constant based on speech/noise state - //increase gamma (i.e., less noise update) for frames likely to be speech - if (nonSpeechProbFinal[i] < ONE_MINUS_PROB_RANGE_Q8) { - gammaNoise = GAMMA_NOISE_TRANS_AND_SPEECH_Q8; - } - - if (prevGammaNoise != gammaNoise) { - // new noise update - // this line is the same as above, only that the result is stored in a different variable and the gammaNoise - // has changed - // - // noiseUpdate = noisePrev[i] + (1 - gammaNoise) * nonSpeechProb * (magn[i] - noisePrev[i]) - - if (0x7c000000 & tmpU32no3) { - // Shifting required before multiplication - tmpU32no2 = (tmpU32no3 >> 5) * gammaNoise; // Q(prevQNoise+11) - } else { - // We can do shifting after multiplication - tmpU32no2 = (tmpU32no3 * gammaNoise) >> 5; // Q(prevQNoise+11) - } - if (sign > 0) { - tmpU32no1 = inst->prevNoiseU32[i] + tmpU32no2; // Q(prevQNoise+11) - } else { - tmpU32no1 = inst->prevNoiseU32[i] - tmpU32no2; // Q(prevQNoise+11) - } - if (noiseUpdateU32 > tmpU32no1) { - noiseUpdateU32 = tmpU32no1; // Q(prevQNoise+11) - } - } - noiseU32[i] = noiseUpdateU32; // Q(prevQNoise+11) - if (noiseUpdateU32 > maxNoiseU32) { - maxNoiseU32 = noiseUpdateU32; - } - - // conservative noise update - // // original FLOAT code - // if (prob_speech < PROB_RANGE) { - // inst->avgMagnPause[i] = inst->avgMagnPause[i] + (1.0 - gamma_pause)*(magn[i] - inst->avgMagnPause[i]); - // } - - tmp32no2 = WEBRTC_SPL_SHIFT_W32(inst->avgMagnPause[i], -nShifts); - if (nonSpeechProbFinal[i] > ONE_MINUS_PROB_RANGE_Q8) { - if (nShifts < 0) { - tmp32no1 = (int32_t)magnU16[i] - tmp32no2; // Q(qMagn) - tmp32no1 *= ONE_MINUS_GAMMA_PAUSE_Q8; // Q(8+prevQMagn+nShifts) - tmp32no1 = (tmp32no1 + 128) >> 8; // Q(qMagn). - } else { - // In Q(qMagn+nShifts) - tmp32no1 = ((int32_t)magnU16[i] << nShifts) - inst->avgMagnPause[i]; - tmp32no1 *= ONE_MINUS_GAMMA_PAUSE_Q8; // Q(8+prevQMagn+nShifts) - tmp32no1 = (tmp32no1 + (128 << nShifts)) >> (8 + nShifts); // Q(qMagn). - } - tmp32no2 += tmp32no1; // Q(qMagn) - } - inst->avgMagnPause[i] = tmp32no2; - } // end of frequency loop - - norm32no1 = WebRtcSpl_NormU32(maxNoiseU32); - qNoise = inst->prevQNoise + norm32no1 - 5; - // done with step 2: noise update - - // STEP 3: compute dd update of prior snr and post snr based on new noise estimate - nShifts = inst->prevQNoise + 11 - qMagn; - for (i = 0; i < inst->magnLen; i++) { - // FLOAT code - // // post and prior SNR - // curNearSnr = 0.0; - // if (magn[i] > noise[i]) - // { - // curNearSnr = magn[i] / (noise[i] + 0.0001) - 1.0; - // } - // // DD estimate is sum of two terms: current estimate and previous estimate - // // directed decision update of snrPrior - // snrPrior = DD_PR_SNR * prevNearSnr[i] + (1.0 - DD_PR_SNR) * curNearSnr; - // // gain filter - // tmpFloat1 = inst->overdrive + snrPrior; - // tmpFloat2 = snrPrior / tmpFloat1; - // theFilter[i] = tmpFloat2; - - // calculate curNearSnr again, this is necessary because a new noise estimate has been made since then. for the original - curNearSnr = 0; // Q11 - if (nShifts < 0) { - // This case is equivalent with magn < noise which implies curNearSnr = 0; - tmpMagnU32 = (uint32_t)magnU16[i]; // Q(qMagn) - tmpNoiseU32 = noiseU32[i] << -nShifts; // Q(qMagn) - } else if (nShifts > 17) { - tmpMagnU32 = (uint32_t)magnU16[i] << 17; // Q(qMagn+17) - tmpNoiseU32 = noiseU32[i] >> (nShifts - 17); // Q(qMagn+17) - } else { - tmpMagnU32 = (uint32_t)magnU16[i] << nShifts; // Q(qNoise_prev+11) - tmpNoiseU32 = noiseU32[i]; // Q(qNoise_prev+11) - } - if (tmpMagnU32 > tmpNoiseU32) { - tmpU32no1 = tmpMagnU32 - tmpNoiseU32; // Q(qCur) - norm32no2 = WEBRTC_SPL_MIN(11, WebRtcSpl_NormU32(tmpU32no1)); - tmpU32no1 <<= norm32no2; // Q(qCur+norm32no2) - tmpU32no2 = tmpNoiseU32 >> (11 - norm32no2); // Q(qCur+norm32no2-11) - if (tmpU32no2 > 0) { - tmpU32no1 /= tmpU32no2; // Q11 - } - curNearSnr = WEBRTC_SPL_MIN(satMax, tmpU32no1); // Q11 - } - - //directed decision update of priorSnr - // FLOAT - // priorSnr = DD_PR_SNR * prevNearSnr + (1.0-DD_PR_SNR) * curNearSnr; - - tmpU32no1 = WEBRTC_SPL_UMUL_32_16(prevNearSnr[i], DD_PR_SNR_Q11); // Q22 - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(curNearSnr, ONE_MINUS_DD_PR_SNR_Q11); // Q22 - priorSnr = tmpU32no1 + tmpU32no2; // Q22 - - //gain filter - tmpU32no1 = inst->overdrive + ((priorSnr + 8192) >> 14); // Q8 - assert(inst->overdrive > 0); - tmpU16no1 = (priorSnr + tmpU32no1 / 2) / tmpU32no1; // Q14 - inst->noiseSupFilter[i] = WEBRTC_SPL_SAT(16384, tmpU16no1, inst->denoiseBound); // 16384 = Q14(1.0) // Q14 - - // Weight in the parametric Wiener filter during startup - if (inst->blockIndex < END_STARTUP_SHORT) { - // Weight the two suppression filters - tmpU32no1 = inst->noiseSupFilter[i] * inst->blockIndex; - tmpU32no2 = noiseSupFilterTmp[i] * - (END_STARTUP_SHORT - inst->blockIndex); - tmpU32no1 += tmpU32no2; - inst->noiseSupFilter[i] = (uint16_t)WebRtcSpl_DivU32U16(tmpU32no1, - END_STARTUP_SHORT); - } - } // end of loop over frequencies - //done with step3 - - // save noise and magnitude spectrum for next frame - inst->prevQNoise = qNoise; - inst->prevQMagn = qMagn; - if (norm32no1 > 5) { - for (i = 0; i < inst->magnLen; i++) { - inst->prevNoiseU32[i] = noiseU32[i] << (norm32no1 - 5); // Q(qNoise+11) - inst->prevMagnU16[i] = magnU16[i]; // Q(qMagn) - } - } else { - for (i = 0; i < inst->magnLen; i++) { - inst->prevNoiseU32[i] = noiseU32[i] >> (5 - norm32no1); // Q(qNoise+11) - inst->prevMagnU16[i] = magnU16[i]; // Q(qMagn) - } - } - - WebRtcNsx_DataSynthesis(inst, outFrame[0]); -#ifdef NS_FILEDEBUG - if (fwrite(outframe, sizeof(short), - inst->blockLen10ms, inst->outfile) != inst->blockLen10ms) { - assert(false); - } -#endif - - //for H band: - // only update data buffer, then apply time-domain gain is applied derived from L band - if (num_bands > 1) { - // update analysis buffer for H band - // append new data to buffer FX - for (i = 0; i < num_high_bands; ++i) { - memcpy(inst->dataBufHBFX[i], inst->dataBufHBFX[i] + inst->blockLen10ms, - (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->dataBufHBFX[i])); - memcpy(inst->dataBufHBFX[i] + inst->anaLen - inst->blockLen10ms, - speechFrameHB[i], inst->blockLen10ms * sizeof(*inst->dataBufHBFX[i])); - } - // range for averaging low band quantities for H band gain - - gainTimeDomainHB = 16384; // 16384 = Q14(1.0) - //average speech prob from low band - //average filter gain from low band - //avg over second half (i.e., 4->8kHz) of freq. spectrum - tmpU32no1 = 0; // Q12 - tmpU16no1 = 0; // Q8 - for (i = inst->anaLen2 - (inst->anaLen2 >> 2); i < inst->anaLen2; i++) { - tmpU16no1 += nonSpeechProbFinal[i]; // Q8 - tmpU32no1 += (uint32_t)(inst->noiseSupFilter[i]); // Q14 - } - assert(inst->stages >= 7); - avgProbSpeechHB = (4096 - (tmpU16no1 >> (inst->stages - 7))); // Q12 - avgFilterGainHB = (int16_t)(tmpU32no1 >> (inst->stages - 3)); // Q14 - - // // original FLOAT code - // // gain based on speech probability: - // avg_prob_speech_tt=(float)2.0*avg_prob_speech-(float)1.0; - // gain_mod=(float)0.5*((float)1.0+(float)tanh(avg_prob_speech_tt)); // between 0 and 1 - - // gain based on speech probability: - // original expression: "0.5 * (1 + tanh(2x-1))" - // avgProbSpeechHB has been anyway saturated to a value between 0 and 1 so the other cases don't have to be dealt with - // avgProbSpeechHB and gainModHB are in Q12, 3607 = Q12(0.880615234375) which is a zero point of - // |0.5 * (1 + tanh(2x-1)) - x| - |0.5 * (1 + tanh(2x-1)) - 0.880615234375| meaning that from that point the error of approximating - // the expression with f(x) = x would be greater than the error of approximating the expression with f(x) = 0.880615234375 - // error: "|0.5 * (1 + tanh(2x-1)) - x| from x=0 to 0.880615234375" -> http://www.wolframalpha.com/input/?i=|0.5+*+(1+%2B+tanh(2x-1))+-+x|+from+x%3D0+to+0.880615234375 - // and: "|0.5 * (1 + tanh(2x-1)) - 0.880615234375| from x=0.880615234375 to 1" -> http://www.wolframalpha.com/input/?i=+|0.5+*+(1+%2B+tanh(2x-1))+-+0.880615234375|+from+x%3D0.880615234375+to+1 - gainModHB = WEBRTC_SPL_MIN(avgProbSpeechHB, 3607); - - // // original FLOAT code - // //combine gain with low band gain - // if (avg_prob_speech < (float)0.5) { - // gain_time_domain_HB=(float)0.5*gain_mod+(float)0.5*avg_filter_gain; - // } - // else { - // gain_time_domain_HB=(float)0.25*gain_mod+(float)0.75*avg_filter_gain; - // } - - - //combine gain with low band gain - if (avgProbSpeechHB < 2048) { - // 2048 = Q12(0.5) - // the next two lines in float are "gain_time_domain = 0.5 * gain_mod + 0.5 * avg_filter_gain"; Q2(0.5) = 2 equals one left shift - gainTimeDomainHB = (gainModHB << 1) + (avgFilterGainHB >> 1); // Q14 - } else { - // "gain_time_domain = 0.25 * gain_mod + 0.75 * agv_filter_gain;" - gainTimeDomainHB = (int16_t)((3 * avgFilterGainHB) >> 2); // 3 = Q2(0.75) - gainTimeDomainHB += gainModHB; // Q14 - } - //make sure gain is within flooring range - gainTimeDomainHB - = WEBRTC_SPL_SAT(16384, gainTimeDomainHB, (int16_t)(inst->denoiseBound)); // 16384 = Q14(1.0) - - - //apply gain - for (i = 0; i < num_high_bands; ++i) { - for (j = 0; j < inst->blockLen10ms; j++) { - outFrameHB[i][j] = (int16_t)((gainTimeDomainHB * - inst->dataBufHBFX[i][j]) >> 14); // Q0 - } - } - } // end of H band gain computation -} diff --git a/webrtc/modules/audio_processing/ns/nsx_core.h b/webrtc/modules/audio_processing/ns/nsx_core.h deleted file mode 100644 index f463dbb..0000000 --- a/webrtc/modules/audio_processing/ns/nsx_core.h +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_ - -#ifdef NS_FILEDEBUG -#include -#endif - -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/ns/nsx_defines.h" -#include "webrtc/typedefs.h" - -typedef struct NoiseSuppressionFixedC_ { - uint32_t fs; - - const int16_t* window; - int16_t analysisBuffer[ANAL_BLOCKL_MAX]; - int16_t synthesisBuffer[ANAL_BLOCKL_MAX]; - uint16_t noiseSupFilter[HALF_ANAL_BLOCKL]; - uint16_t overdrive; /* Q8 */ - uint16_t denoiseBound; /* Q14 */ - const int16_t* factor2Table; - int16_t noiseEstLogQuantile[SIMULT* HALF_ANAL_BLOCKL]; - int16_t noiseEstDensity[SIMULT* HALF_ANAL_BLOCKL]; - int16_t noiseEstCounter[SIMULT]; - int16_t noiseEstQuantile[HALF_ANAL_BLOCKL]; - - size_t anaLen; - size_t anaLen2; - size_t magnLen; - int aggrMode; - int stages; - int initFlag; - int gainMap; - - int32_t maxLrt; - int32_t minLrt; - // Log LRT factor with time-smoothing in Q8. - int32_t logLrtTimeAvgW32[HALF_ANAL_BLOCKL]; - int32_t featureLogLrt; - int32_t thresholdLogLrt; - int16_t weightLogLrt; - - uint32_t featureSpecDiff; - uint32_t thresholdSpecDiff; - int16_t weightSpecDiff; - - uint32_t featureSpecFlat; - uint32_t thresholdSpecFlat; - int16_t weightSpecFlat; - - // Conservative estimate of noise spectrum. - int32_t avgMagnPause[HALF_ANAL_BLOCKL]; - uint32_t magnEnergy; - uint32_t sumMagn; - uint32_t curAvgMagnEnergy; - uint32_t timeAvgMagnEnergy; - uint32_t timeAvgMagnEnergyTmp; - - uint32_t whiteNoiseLevel; // Initial noise estimate. - // Initial magnitude spectrum estimate. - uint32_t initMagnEst[HALF_ANAL_BLOCKL]; - // Pink noise parameters: - int32_t pinkNoiseNumerator; // Numerator. - int32_t pinkNoiseExp; // Power of freq. - int minNorm; // Smallest normalization factor. - int zeroInputSignal; // Zero input signal flag. - - // Noise spectrum from previous frame. - uint32_t prevNoiseU32[HALF_ANAL_BLOCKL]; - // Magnitude spectrum from previous frame. - uint16_t prevMagnU16[HALF_ANAL_BLOCKL]; - // Prior speech/noise probability in Q14. - int16_t priorNonSpeechProb; - - int blockIndex; // Frame index counter. - // Parameter for updating or estimating thresholds/weights for prior model. - int modelUpdate; - int cntThresUpdate; - - // Histograms for parameter estimation. - int16_t histLrt[HIST_PAR_EST]; - int16_t histSpecFlat[HIST_PAR_EST]; - int16_t histSpecDiff[HIST_PAR_EST]; - - // Quantities for high band estimate. - int16_t dataBufHBFX[NUM_HIGH_BANDS_MAX][ANAL_BLOCKL_MAX]; - - int qNoise; - int prevQNoise; - int prevQMagn; - size_t blockLen10ms; - - int16_t real[ANAL_BLOCKL_MAX]; - int16_t imag[ANAL_BLOCKL_MAX]; - int32_t energyIn; - int scaleEnergyIn; - int normData; - - struct RealFFT* real_fft; -} NoiseSuppressionFixedC; - -#ifdef __cplusplus -extern "C" -{ -#endif - -/**************************************************************************** - * WebRtcNsx_InitCore(...) - * - * This function initializes a noise suppression instance - * - * Input: - * - inst : Instance that should be initialized - * - fs : Sampling frequency - * - * Output: - * - inst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int32_t WebRtcNsx_InitCore(NoiseSuppressionFixedC* inst, uint32_t fs); - -/**************************************************************************** - * WebRtcNsx_set_policy_core(...) - * - * This changes the aggressiveness of the noise suppression method. - * - * Input: - * - inst : Instance that should be initialized - * - mode : 0: Mild (6 dB), 1: Medium (10 dB), 2: Aggressive (15 dB) - * - * Output: - * - inst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNsx_set_policy_core(NoiseSuppressionFixedC* inst, int mode); - -/**************************************************************************** - * WebRtcNsx_ProcessCore - * - * Do noise suppression. - * - * Input: - * - inst : Instance that should be initialized - * - inFrame : Input speech frame for each band - * - num_bands : Number of bands - * - * Output: - * - inst : Updated instance - * - outFrame : Output speech frame for each band - */ -void WebRtcNsx_ProcessCore(NoiseSuppressionFixedC* inst, - const short* const* inFrame, - int num_bands, - short* const* outFrame); - -/**************************************************************************** - * Some function pointers, for internal functions shared by ARM NEON and - * generic C code. - */ -// Noise Estimation. -typedef void (*NoiseEstimation)(NoiseSuppressionFixedC* inst, - uint16_t* magn, - uint32_t* noise, - int16_t* q_noise); -extern NoiseEstimation WebRtcNsx_NoiseEstimation; - -// Filter the data in the frequency domain, and create spectrum. -typedef void (*PrepareSpectrum)(NoiseSuppressionFixedC* inst, - int16_t* freq_buff); -extern PrepareSpectrum WebRtcNsx_PrepareSpectrum; - -// For the noise supression process, synthesis, read out fully processed -// segment, and update synthesis buffer. -typedef void (*SynthesisUpdate)(NoiseSuppressionFixedC* inst, - int16_t* out_frame, - int16_t gain_factor); -extern SynthesisUpdate WebRtcNsx_SynthesisUpdate; - -// Update analysis buffer for lower band, and window data before FFT. -typedef void (*AnalysisUpdate)(NoiseSuppressionFixedC* inst, - int16_t* out, - int16_t* new_speech); -extern AnalysisUpdate WebRtcNsx_AnalysisUpdate; - -// Denormalize the real-valued signal |in|, the output from inverse FFT. -typedef void (*Denormalize)(NoiseSuppressionFixedC* inst, - int16_t* in, - int factor); -extern Denormalize WebRtcNsx_Denormalize; - -// Normalize the real-valued signal |in|, the input to forward FFT. -typedef void (*NormalizeRealBuffer)(NoiseSuppressionFixedC* inst, - const int16_t* in, - int16_t* out); -extern NormalizeRealBuffer WebRtcNsx_NormalizeRealBuffer; - -// Compute speech/noise probability. -// Intended to be private. -void WebRtcNsx_SpeechNoiseProb(NoiseSuppressionFixedC* inst, - uint16_t* nonSpeechProbFinal, - uint32_t* priorLocSnr, - uint32_t* postLocSnr); - -#if (defined WEBRTC_DETECT_NEON || defined WEBRTC_HAS_NEON) -// For the above function pointers, functions for generic platforms are declared -// and defined as static in file nsx_core.c, while those for ARM Neon platforms -// are declared below and defined in file nsx_core_neon.c. -void WebRtcNsx_NoiseEstimationNeon(NoiseSuppressionFixedC* inst, - uint16_t* magn, - uint32_t* noise, - int16_t* q_noise); -void WebRtcNsx_SynthesisUpdateNeon(NoiseSuppressionFixedC* inst, - int16_t* out_frame, - int16_t gain_factor); -void WebRtcNsx_AnalysisUpdateNeon(NoiseSuppressionFixedC* inst, - int16_t* out, - int16_t* new_speech); -void WebRtcNsx_PrepareSpectrumNeon(NoiseSuppressionFixedC* inst, - int16_t* freq_buff); -#endif - -#if defined(MIPS32_LE) -// For the above function pointers, functions for generic platforms are declared -// and defined as static in file nsx_core.c, while those for MIPS platforms -// are declared below and defined in file nsx_core_mips.c. -void WebRtcNsx_SynthesisUpdate_mips(NoiseSuppressionFixedC* inst, - int16_t* out_frame, - int16_t gain_factor); -void WebRtcNsx_AnalysisUpdate_mips(NoiseSuppressionFixedC* inst, - int16_t* out, - int16_t* new_speech); -void WebRtcNsx_PrepareSpectrum_mips(NoiseSuppressionFixedC* inst, - int16_t* freq_buff); -void WebRtcNsx_NormalizeRealBuffer_mips(NoiseSuppressionFixedC* inst, - const int16_t* in, - int16_t* out); -#if defined(MIPS_DSP_R1_LE) -void WebRtcNsx_Denormalize_mips(NoiseSuppressionFixedC* inst, - int16_t* in, - int factor); -#endif - -#endif - -#ifdef __cplusplus -} -#endif - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_ diff --git a/webrtc/modules/audio_processing/ns/nsx_core_c.c b/webrtc/modules/audio_processing/ns/nsx_core_c.c deleted file mode 100644 index 14322d3..0000000 --- a/webrtc/modules/audio_processing/ns/nsx_core_c.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (c) 2013 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 - -#include "webrtc/modules/audio_processing/ns/include/noise_suppression_x.h" -#include "webrtc/modules/audio_processing/ns/nsx_core.h" -#include "webrtc/modules/audio_processing/ns/nsx_defines.h" - -static const int16_t kIndicatorTable[17] = { - 0, 2017, 3809, 5227, 6258, 6963, 7424, 7718, - 7901, 8014, 8084, 8126, 8152, 8168, 8177, 8183, 8187 -}; - -// Compute speech/noise probability -// speech/noise probability is returned in: probSpeechFinal -//snrLocPrior is the prior SNR for each frequency (in Q11) -//snrLocPost is the post SNR for each frequency (in Q11) -void WebRtcNsx_SpeechNoiseProb(NoiseSuppressionFixedC* inst, - uint16_t* nonSpeechProbFinal, - uint32_t* priorLocSnr, - uint32_t* postLocSnr) { - uint32_t zeros, num, den, tmpU32no1, tmpU32no2, tmpU32no3; - int32_t invLrtFX, indPriorFX, tmp32, tmp32no1, tmp32no2, besselTmpFX32; - int32_t frac32, logTmp; - int32_t logLrtTimeAvgKsumFX; - int16_t indPriorFX16; - int16_t tmp16, tmp16no1, tmp16no2, tmpIndFX, tableIndex, frac, intPart; - size_t i; - int normTmp, normTmp2, nShifts; - - // compute feature based on average LR factor - // this is the average over all frequencies of the smooth log LRT - logLrtTimeAvgKsumFX = 0; - for (i = 0; i < inst->magnLen; i++) { - besselTmpFX32 = (int32_t)postLocSnr[i]; // Q11 - normTmp = WebRtcSpl_NormU32(postLocSnr[i]); - num = postLocSnr[i] << normTmp; // Q(11+normTmp) - if (normTmp > 10) { - den = priorLocSnr[i] << (normTmp - 11); // Q(normTmp) - } else { - den = priorLocSnr[i] >> (11 - normTmp); // Q(normTmp) - } - if (den > 0) { - besselTmpFX32 -= num / den; // Q11 - } else { - besselTmpFX32 = 0; - } - - // inst->logLrtTimeAvg[i] += LRT_TAVG * (besselTmp - log(snrLocPrior) - // - inst->logLrtTimeAvg[i]); - // Here, LRT_TAVG = 0.5 - zeros = WebRtcSpl_NormU32(priorLocSnr[i]); - frac32 = (int32_t)(((priorLocSnr[i] << zeros) & 0x7FFFFFFF) >> 19); - tmp32 = (frac32 * frac32 * -43) >> 19; - tmp32 += ((int16_t)frac32 * 5412) >> 12; - frac32 = tmp32 + 37; - // tmp32 = log2(priorLocSnr[i]) - tmp32 = (int32_t)(((31 - zeros) << 12) + frac32) - (11 << 12); // Q12 - logTmp = (tmp32 * 178) >> 8; // log2(priorLocSnr[i])*log(2) - // tmp32no1 = LRT_TAVG * (log(snrLocPrior) + inst->logLrtTimeAvg[i]) in Q12. - tmp32no1 = (logTmp + inst->logLrtTimeAvgW32[i]) / 2; - inst->logLrtTimeAvgW32[i] += (besselTmpFX32 - tmp32no1); // Q12 - - logLrtTimeAvgKsumFX += inst->logLrtTimeAvgW32[i]; // Q12 - } - inst->featureLogLrt = (logLrtTimeAvgKsumFX * BIN_SIZE_LRT) >> - (inst->stages + 11); - - // done with computation of LR factor - - // - //compute the indicator functions - // - - // average LRT feature - // FLOAT code - // indicator0 = 0.5 * (tanh(widthPrior * - // (logLrtTimeAvgKsum - threshPrior0)) + 1.0); - tmpIndFX = 16384; // Q14(1.0) - tmp32no1 = logLrtTimeAvgKsumFX - inst->thresholdLogLrt; // Q12 - nShifts = 7 - inst->stages; // WIDTH_PR_MAP_SHIFT - inst->stages + 5; - //use larger width in tanh map for pause regions - if (tmp32no1 < 0) { - tmpIndFX = 0; - tmp32no1 = -tmp32no1; - //widthPrior = widthPrior * 2.0; - nShifts++; - } - tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, nShifts); // Q14 - // compute indicator function: sigmoid map - tableIndex = (int16_t)(tmp32no1 >> 14); - if ((tableIndex < 16) && (tableIndex >= 0)) { - tmp16no2 = kIndicatorTable[tableIndex]; - tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; - frac = (int16_t)(tmp32no1 & 0x00003fff); // Q14 - tmp16no2 += (int16_t)((tmp16no1 * frac) >> 14); - if (tmpIndFX == 0) { - tmpIndFX = 8192 - tmp16no2; // Q14 - } else { - tmpIndFX = 8192 + tmp16no2; // Q14 - } - } - indPriorFX = inst->weightLogLrt * tmpIndFX; // 6*Q14 - - //spectral flatness feature - if (inst->weightSpecFlat) { - tmpU32no1 = WEBRTC_SPL_UMUL(inst->featureSpecFlat, 400); // Q10 - tmpIndFX = 16384; // Q14(1.0) - //use larger width in tanh map for pause regions - tmpU32no2 = inst->thresholdSpecFlat - tmpU32no1; //Q10 - nShifts = 4; - if (inst->thresholdSpecFlat < tmpU32no1) { - tmpIndFX = 0; - tmpU32no2 = tmpU32no1 - inst->thresholdSpecFlat; - //widthPrior = widthPrior * 2.0; - nShifts++; - } - tmpU32no1 = WebRtcSpl_DivU32U16(tmpU32no2 << nShifts, 25); // Q14 - // compute indicator function: sigmoid map - // FLOAT code - // indicator1 = 0.5 * (tanh(sgnMap * widthPrior * - // (threshPrior1 - tmpFloat1)) + 1.0); - tableIndex = (int16_t)(tmpU32no1 >> 14); - if (tableIndex < 16) { - tmp16no2 = kIndicatorTable[tableIndex]; - tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; - frac = (int16_t)(tmpU32no1 & 0x00003fff); // Q14 - tmp16no2 += (int16_t)((tmp16no1 * frac) >> 14); - if (tmpIndFX) { - tmpIndFX = 8192 + tmp16no2; // Q14 - } else { - tmpIndFX = 8192 - tmp16no2; // Q14 - } - } - indPriorFX += inst->weightSpecFlat * tmpIndFX; // 6*Q14 - } - - //for template spectral-difference - if (inst->weightSpecDiff) { - tmpU32no1 = 0; - if (inst->featureSpecDiff) { - normTmp = WEBRTC_SPL_MIN(20 - inst->stages, - WebRtcSpl_NormU32(inst->featureSpecDiff)); - assert(normTmp >= 0); - tmpU32no1 = inst->featureSpecDiff << normTmp; // Q(normTmp-2*stages) - tmpU32no2 = inst->timeAvgMagnEnergy >> (20 - inst->stages - normTmp); - if (tmpU32no2 > 0) { - // Q(20 - inst->stages) - tmpU32no1 /= tmpU32no2; - } else { - tmpU32no1 = (uint32_t)(0x7fffffff); - } - } - tmpU32no3 = (inst->thresholdSpecDiff << 17) / 25; - tmpU32no2 = tmpU32no1 - tmpU32no3; - nShifts = 1; - tmpIndFX = 16384; // Q14(1.0) - //use larger width in tanh map for pause regions - if (tmpU32no2 & 0x80000000) { - tmpIndFX = 0; - tmpU32no2 = tmpU32no3 - tmpU32no1; - //widthPrior = widthPrior * 2.0; - nShifts--; - } - tmpU32no1 = tmpU32no2 >> nShifts; - // compute indicator function: sigmoid map - /* FLOAT code - indicator2 = 0.5 * (tanh(widthPrior * (tmpFloat1 - threshPrior2)) + 1.0); - */ - tableIndex = (int16_t)(tmpU32no1 >> 14); - if (tableIndex < 16) { - tmp16no2 = kIndicatorTable[tableIndex]; - tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; - frac = (int16_t)(tmpU32no1 & 0x00003fff); // Q14 - tmp16no2 += (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - tmp16no1, frac, 14); - if (tmpIndFX) { - tmpIndFX = 8192 + tmp16no2; - } else { - tmpIndFX = 8192 - tmp16no2; - } - } - indPriorFX += inst->weightSpecDiff * tmpIndFX; // 6*Q14 - } - - //combine the indicator function with the feature weights - // FLOAT code - // indPrior = 1 - (weightIndPrior0 * indicator0 + weightIndPrior1 * - // indicator1 + weightIndPrior2 * indicator2); - indPriorFX16 = WebRtcSpl_DivW32W16ResW16(98307 - indPriorFX, 6); // Q14 - // done with computing indicator function - - //compute the prior probability - // FLOAT code - // inst->priorNonSpeechProb += PRIOR_UPDATE * - // (indPriorNonSpeech - inst->priorNonSpeechProb); - tmp16 = indPriorFX16 - inst->priorNonSpeechProb; // Q14 - inst->priorNonSpeechProb += (int16_t)((PRIOR_UPDATE_Q14 * tmp16) >> 14); - - //final speech probability: combine prior model with LR factor: - - memset(nonSpeechProbFinal, 0, sizeof(uint16_t) * inst->magnLen); - - if (inst->priorNonSpeechProb > 0) { - for (i = 0; i < inst->magnLen; i++) { - // FLOAT code - // invLrt = exp(inst->logLrtTimeAvg[i]); - // invLrt = inst->priorSpeechProb * invLrt; - // nonSpeechProbFinal[i] = (1.0 - inst->priorSpeechProb) / - // (1.0 - inst->priorSpeechProb + invLrt); - // invLrt = (1.0 - inst->priorNonSpeechProb) * invLrt; - // nonSpeechProbFinal[i] = inst->priorNonSpeechProb / - // (inst->priorNonSpeechProb + invLrt); - if (inst->logLrtTimeAvgW32[i] < 65300) { - tmp32no1 = (inst->logLrtTimeAvgW32[i] * 23637) >> 14; // Q12 - intPart = (int16_t)(tmp32no1 >> 12); - if (intPart < -8) { - intPart = -8; - } - frac = (int16_t)(tmp32no1 & 0x00000fff); // Q12 - - // Quadratic approximation of 2^frac - tmp32no2 = (frac * frac * 44) >> 19; // Q12. - tmp32no2 += (frac * 84) >> 7; // Q12 - invLrtFX = (1 << (8 + intPart)) + - WEBRTC_SPL_SHIFT_W32(tmp32no2, intPart - 4); // Q8 - - normTmp = WebRtcSpl_NormW32(invLrtFX); - normTmp2 = WebRtcSpl_NormW16((16384 - inst->priorNonSpeechProb)); - if (normTmp + normTmp2 >= 7) { - if (normTmp + normTmp2 < 15) { - invLrtFX >>= 15 - normTmp2 - normTmp; - // Q(normTmp+normTmp2-7) - tmp32no1 = invLrtFX * (16384 - inst->priorNonSpeechProb); - // Q(normTmp+normTmp2+7) - invLrtFX = WEBRTC_SPL_SHIFT_W32(tmp32no1, 7 - normTmp - normTmp2); - // Q14 - } else { - tmp32no1 = invLrtFX * (16384 - inst->priorNonSpeechProb); - // Q22 - invLrtFX = tmp32no1 >> 8; // Q14. - } - - tmp32no1 = (int32_t)inst->priorNonSpeechProb << 8; // Q22 - - nonSpeechProbFinal[i] = tmp32no1 / - (inst->priorNonSpeechProb + invLrtFX); // Q8 - } - } - } - } -} - diff --git a/webrtc/modules/audio_processing/ns/nsx_core_mips.c b/webrtc/modules/audio_processing/ns/nsx_core_mips.c deleted file mode 100644 index d99be87..0000000 --- a/webrtc/modules/audio_processing/ns/nsx_core_mips.c +++ /dev/null @@ -1,1002 +0,0 @@ -/* - * Copyright (c) 2013 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 -#include - -#include "webrtc/modules/audio_processing/ns/include/noise_suppression_x.h" -#include "webrtc/modules/audio_processing/ns/nsx_core.h" - -static const int16_t kIndicatorTable[17] = { - 0, 2017, 3809, 5227, 6258, 6963, 7424, 7718, - 7901, 8014, 8084, 8126, 8152, 8168, 8177, 8183, 8187 -}; - -// Compute speech/noise probability -// speech/noise probability is returned in: probSpeechFinal -//snrLocPrior is the prior SNR for each frequency (in Q11) -//snrLocPost is the post SNR for each frequency (in Q11) -void WebRtcNsx_SpeechNoiseProb(NoiseSuppressionFixedC* inst, - uint16_t* nonSpeechProbFinal, - uint32_t* priorLocSnr, - uint32_t* postLocSnr) { - uint32_t tmpU32no1, tmpU32no2, tmpU32no3; - int32_t indPriorFX, tmp32no1; - int32_t logLrtTimeAvgKsumFX; - int16_t indPriorFX16; - int16_t tmp16, tmp16no1, tmp16no2, tmpIndFX, tableIndex, frac; - size_t i; - int normTmp, nShifts; - - int32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9; - int32_t const_max = 0x7fffffff; - int32_t const_neg43 = -43; - int32_t const_5412 = 5412; - int32_t const_11rsh12 = (11 << 12); - int32_t const_178 = 178; - - - // compute feature based on average LR factor - // this is the average over all frequencies of the smooth log LRT - logLrtTimeAvgKsumFX = 0; - for (i = 0; i < inst->magnLen; i++) { - r0 = postLocSnr[i]; // Q11 - r1 = priorLocSnr[i]; - r2 = inst->logLrtTimeAvgW32[i]; - - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "clz %[r3], %[r0] \n\t" - "clz %[r5], %[r1] \n\t" - "slti %[r4], %[r3], 32 \n\t" - "slti %[r6], %[r5], 32 \n\t" - "movz %[r3], $0, %[r4] \n\t" - "movz %[r5], $0, %[r6] \n\t" - "slti %[r4], %[r3], 11 \n\t" - "addiu %[r6], %[r3], -11 \n\t" - "neg %[r7], %[r6] \n\t" - "sllv %[r6], %[r1], %[r6] \n\t" - "srav %[r7], %[r1], %[r7] \n\t" - "movn %[r6], %[r7], %[r4] \n\t" - "sllv %[r1], %[r1], %[r5] \n\t" - "and %[r1], %[r1], %[const_max] \n\t" - "sra %[r1], %[r1], 19 \n\t" - "mul %[r7], %[r1], %[r1] \n\t" - "sllv %[r3], %[r0], %[r3] \n\t" - "divu %[r8], %[r3], %[r6] \n\t" - "slti %[r6], %[r6], 1 \n\t" - "mul %[r7], %[r7], %[const_neg43] \n\t" - "sra %[r7], %[r7], 19 \n\t" - "movz %[r3], %[r8], %[r6] \n\t" - "subu %[r0], %[r0], %[r3] \n\t" - "movn %[r0], $0, %[r6] \n\t" - "mul %[r1], %[r1], %[const_5412] \n\t" - "sra %[r1], %[r1], 12 \n\t" - "addu %[r7], %[r7], %[r1] \n\t" - "addiu %[r1], %[r7], 37 \n\t" - "addiu %[r5], %[r5], -31 \n\t" - "neg %[r5], %[r5] \n\t" - "sll %[r5], %[r5], 12 \n\t" - "addu %[r5], %[r5], %[r1] \n\t" - "subu %[r7], %[r5], %[const_11rsh12] \n\t" - "mul %[r7], %[r7], %[const_178] \n\t" - "sra %[r7], %[r7], 8 \n\t" - "addu %[r7], %[r7], %[r2] \n\t" - "sra %[r7], %[r7], 1 \n\t" - "subu %[r2], %[r2], %[r7] \n\t" - "addu %[r2], %[r2], %[r0] \n\t" - ".set pop \n\t" - : [r0] "+r" (r0), [r1] "+r" (r1), [r2] "+r" (r2), - [r3] "=&r" (r3), [r4] "=&r" (r4), [r5] "=&r" (r5), - [r6] "=&r" (r6), [r7] "=&r" (r7), [r8] "=&r" (r8) - : [const_max] "r" (const_max), [const_neg43] "r" (const_neg43), - [const_5412] "r" (const_5412), [const_11rsh12] "r" (const_11rsh12), - [const_178] "r" (const_178) - : "hi", "lo" - ); - inst->logLrtTimeAvgW32[i] = r2; - logLrtTimeAvgKsumFX += r2; - } - - inst->featureLogLrt = (logLrtTimeAvgKsumFX * BIN_SIZE_LRT) >> - (inst->stages + 11); - - // done with computation of LR factor - - // - // compute the indicator functions - // - - // average LRT feature - // FLOAT code - // indicator0 = 0.5 * (tanh(widthPrior * - // (logLrtTimeAvgKsum - threshPrior0)) + 1.0); - tmpIndFX = 16384; // Q14(1.0) - tmp32no1 = logLrtTimeAvgKsumFX - inst->thresholdLogLrt; // Q12 - nShifts = 7 - inst->stages; // WIDTH_PR_MAP_SHIFT - inst->stages + 5; - //use larger width in tanh map for pause regions - if (tmp32no1 < 0) { - tmpIndFX = 0; - tmp32no1 = -tmp32no1; - //widthPrior = widthPrior * 2.0; - nShifts++; - } - tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, nShifts); // Q14 - // compute indicator function: sigmoid map - tableIndex = (int16_t)(tmp32no1 >> 14); - if ((tableIndex < 16) && (tableIndex >= 0)) { - tmp16no2 = kIndicatorTable[tableIndex]; - tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; - frac = (int16_t)(tmp32no1 & 0x00003fff); // Q14 - tmp16no2 += (int16_t)((tmp16no1 * frac) >> 14); - if (tmpIndFX == 0) { - tmpIndFX = 8192 - tmp16no2; // Q14 - } else { - tmpIndFX = 8192 + tmp16no2; // Q14 - } - } - indPriorFX = inst->weightLogLrt * tmpIndFX; // 6*Q14 - - //spectral flatness feature - if (inst->weightSpecFlat) { - tmpU32no1 = WEBRTC_SPL_UMUL(inst->featureSpecFlat, 400); // Q10 - tmpIndFX = 16384; // Q14(1.0) - //use larger width in tanh map for pause regions - tmpU32no2 = inst->thresholdSpecFlat - tmpU32no1; //Q10 - nShifts = 4; - if (inst->thresholdSpecFlat < tmpU32no1) { - tmpIndFX = 0; - tmpU32no2 = tmpU32no1 - inst->thresholdSpecFlat; - //widthPrior = widthPrior * 2.0; - nShifts++; - } - tmpU32no1 = WebRtcSpl_DivU32U16(tmpU32no2 << nShifts, 25); //Q14 - // compute indicator function: sigmoid map - // FLOAT code - // indicator1 = 0.5 * (tanh(sgnMap * widthPrior * - // (threshPrior1 - tmpFloat1)) + 1.0); - tableIndex = (int16_t)(tmpU32no1 >> 14); - if (tableIndex < 16) { - tmp16no2 = kIndicatorTable[tableIndex]; - tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; - frac = (int16_t)(tmpU32no1 & 0x00003fff); // Q14 - tmp16no2 += (int16_t)((tmp16no1 * frac) >> 14); - if (tmpIndFX) { - tmpIndFX = 8192 + tmp16no2; // Q14 - } else { - tmpIndFX = 8192 - tmp16no2; // Q14 - } - } - indPriorFX += inst->weightSpecFlat * tmpIndFX; // 6*Q14 - } - - //for template spectral-difference - if (inst->weightSpecDiff) { - tmpU32no1 = 0; - if (inst->featureSpecDiff) { - normTmp = WEBRTC_SPL_MIN(20 - inst->stages, - WebRtcSpl_NormU32(inst->featureSpecDiff)); - assert(normTmp >= 0); - tmpU32no1 = inst->featureSpecDiff << normTmp; // Q(normTmp-2*stages) - tmpU32no2 = inst->timeAvgMagnEnergy >> (20 - inst->stages - normTmp); - if (tmpU32no2 > 0) { - // Q(20 - inst->stages) - tmpU32no1 /= tmpU32no2; - } else { - tmpU32no1 = (uint32_t)(0x7fffffff); - } - } - tmpU32no3 = (inst->thresholdSpecDiff << 17) / 25; - tmpU32no2 = tmpU32no1 - tmpU32no3; - nShifts = 1; - tmpIndFX = 16384; // Q14(1.0) - //use larger width in tanh map for pause regions - if (tmpU32no2 & 0x80000000) { - tmpIndFX = 0; - tmpU32no2 = tmpU32no3 - tmpU32no1; - //widthPrior = widthPrior * 2.0; - nShifts--; - } - tmpU32no1 = tmpU32no2 >> nShifts; - // compute indicator function: sigmoid map - /* FLOAT code - indicator2 = 0.5 * (tanh(widthPrior * (tmpFloat1 - threshPrior2)) + 1.0); - */ - tableIndex = (int16_t)(tmpU32no1 >> 14); - if (tableIndex < 16) { - tmp16no2 = kIndicatorTable[tableIndex]; - tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; - frac = (int16_t)(tmpU32no1 & 0x00003fff); // Q14 - tmp16no2 += (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - tmp16no1, frac, 14); - if (tmpIndFX) { - tmpIndFX = 8192 + tmp16no2; - } else { - tmpIndFX = 8192 - tmp16no2; - } - } - indPriorFX += inst->weightSpecDiff * tmpIndFX; // 6*Q14 - } - - //combine the indicator function with the feature weights - // FLOAT code - // indPrior = 1 - (weightIndPrior0 * indicator0 + weightIndPrior1 * - // indicator1 + weightIndPrior2 * indicator2); - indPriorFX16 = WebRtcSpl_DivW32W16ResW16(98307 - indPriorFX, 6); // Q14 - // done with computing indicator function - - //compute the prior probability - // FLOAT code - // inst->priorNonSpeechProb += PRIOR_UPDATE * - // (indPriorNonSpeech - inst->priorNonSpeechProb); - tmp16 = indPriorFX16 - inst->priorNonSpeechProb; // Q14 - inst->priorNonSpeechProb += (int16_t)((PRIOR_UPDATE_Q14 * tmp16) >> 14); - - //final speech probability: combine prior model with LR factor: - - memset(nonSpeechProbFinal, 0, sizeof(uint16_t) * inst->magnLen); - - if (inst->priorNonSpeechProb > 0) { - r0 = inst->priorNonSpeechProb; - r1 = 16384 - r0; - int32_t const_23637 = 23637; - int32_t const_44 = 44; - int32_t const_84 = 84; - int32_t const_1 = 1; - int32_t const_neg8 = -8; - for (i = 0; i < inst->magnLen; i++) { - r2 = inst->logLrtTimeAvgW32[i]; - if (r2 < 65300) { - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "mul %[r2], %[r2], %[const_23637] \n\t" - "sll %[r6], %[r1], 16 \n\t" - "clz %[r7], %[r6] \n\t" - "clo %[r8], %[r6] \n\t" - "slt %[r9], %[r6], $0 \n\t" - "movn %[r7], %[r8], %[r9] \n\t" - "sra %[r2], %[r2], 14 \n\t" - "andi %[r3], %[r2], 0xfff \n\t" - "mul %[r4], %[r3], %[r3] \n\t" - "mul %[r3], %[r3], %[const_84] \n\t" - "sra %[r2], %[r2], 12 \n\t" - "slt %[r5], %[r2], %[const_neg8] \n\t" - "movn %[r2], %[const_neg8], %[r5] \n\t" - "mul %[r4], %[r4], %[const_44] \n\t" - "sra %[r3], %[r3], 7 \n\t" - "addiu %[r7], %[r7], -1 \n\t" - "slti %[r9], %[r7], 31 \n\t" - "movz %[r7], $0, %[r9] \n\t" - "sra %[r4], %[r4], 19 \n\t" - "addu %[r4], %[r4], %[r3] \n\t" - "addiu %[r3], %[r2], 8 \n\t" - "addiu %[r2], %[r2], -4 \n\t" - "neg %[r5], %[r2] \n\t" - "sllv %[r6], %[r4], %[r2] \n\t" - "srav %[r5], %[r4], %[r5] \n\t" - "slt %[r2], %[r2], $0 \n\t" - "movn %[r6], %[r5], %[r2] \n\t" - "sllv %[r3], %[const_1], %[r3] \n\t" - "addu %[r2], %[r3], %[r6] \n\t" - "clz %[r4], %[r2] \n\t" - "clo %[r5], %[r2] \n\t" - "slt %[r8], %[r2], $0 \n\t" - "movn %[r4], %[r5], %[r8] \n\t" - "addiu %[r4], %[r4], -1 \n\t" - "slt %[r5], $0, %[r2] \n\t" - "or %[r5], %[r5], %[r7] \n\t" - "movz %[r4], $0, %[r5] \n\t" - "addiu %[r6], %[r7], -7 \n\t" - "addu %[r6], %[r6], %[r4] \n\t" - "bltz %[r6], 1f \n\t" - " nop \n\t" - "addiu %[r4], %[r6], -8 \n\t" - "neg %[r3], %[r4] \n\t" - "srav %[r5], %[r2], %[r3] \n\t" - "mul %[r5], %[r5], %[r1] \n\t" - "mul %[r2], %[r2], %[r1] \n\t" - "slt %[r4], %[r4], $0 \n\t" - "srav %[r5], %[r5], %[r6] \n\t" - "sra %[r2], %[r2], 8 \n\t" - "movn %[r2], %[r5], %[r4] \n\t" - "sll %[r3], %[r0], 8 \n\t" - "addu %[r2], %[r0], %[r2] \n\t" - "divu %[r3], %[r3], %[r2] \n\t" - "1: \n\t" - ".set pop \n\t" - : [r2] "+r" (r2), [r3] "=&r" (r3), [r4] "=&r" (r4), - [r5] "=&r" (r5), [r6] "=&r" (r6), [r7] "=&r" (r7), - [r8] "=&r" (r8), [r9] "=&r" (r9) - : [r0] "r" (r0), [r1] "r" (r1), [const_23637] "r" (const_23637), - [const_neg8] "r" (const_neg8), [const_84] "r" (const_84), - [const_1] "r" (const_1), [const_44] "r" (const_44) - : "hi", "lo" - ); - nonSpeechProbFinal[i] = r3; - } - } - } -} - -// Update analysis buffer for lower band, and window data before FFT. -void WebRtcNsx_AnalysisUpdate_mips(NoiseSuppressionFixedC* inst, - int16_t* out, - int16_t* new_speech) { - int iters, after; - int anaLen = (int)inst->anaLen; - int *window = (int*)inst->window; - int *anaBuf = (int*)inst->analysisBuffer; - int *outBuf = (int*)out; - int r0, r1, r2, r3, r4, r5, r6, r7; -#if defined(MIPS_DSP_R1_LE) - int r8; -#endif - - // For lower band update analysis buffer. - memcpy(inst->analysisBuffer, inst->analysisBuffer + inst->blockLen10ms, - (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->analysisBuffer)); - memcpy(inst->analysisBuffer + inst->anaLen - inst->blockLen10ms, new_speech, - inst->blockLen10ms * sizeof(*inst->analysisBuffer)); - - // Window data before FFT. -#if defined(MIPS_DSP_R1_LE) - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "sra %[iters], %[anaLen], 3 \n\t" - "1: \n\t" - "blez %[iters], 2f \n\t" - " nop \n\t" - "lw %[r0], 0(%[window]) \n\t" - "lw %[r1], 0(%[anaBuf]) \n\t" - "lw %[r2], 4(%[window]) \n\t" - "lw %[r3], 4(%[anaBuf]) \n\t" - "lw %[r4], 8(%[window]) \n\t" - "lw %[r5], 8(%[anaBuf]) \n\t" - "lw %[r6], 12(%[window]) \n\t" - "lw %[r7], 12(%[anaBuf]) \n\t" - "muleq_s.w.phl %[r8], %[r0], %[r1] \n\t" - "muleq_s.w.phr %[r0], %[r0], %[r1] \n\t" - "muleq_s.w.phl %[r1], %[r2], %[r3] \n\t" - "muleq_s.w.phr %[r2], %[r2], %[r3] \n\t" - "muleq_s.w.phl %[r3], %[r4], %[r5] \n\t" - "muleq_s.w.phr %[r4], %[r4], %[r5] \n\t" - "muleq_s.w.phl %[r5], %[r6], %[r7] \n\t" - "muleq_s.w.phr %[r6], %[r6], %[r7] \n\t" -#if defined(MIPS_DSP_R2_LE) - "precr_sra_r.ph.w %[r8], %[r0], 15 \n\t" - "precr_sra_r.ph.w %[r1], %[r2], 15 \n\t" - "precr_sra_r.ph.w %[r3], %[r4], 15 \n\t" - "precr_sra_r.ph.w %[r5], %[r6], 15 \n\t" - "sw %[r8], 0(%[outBuf]) \n\t" - "sw %[r1], 4(%[outBuf]) \n\t" - "sw %[r3], 8(%[outBuf]) \n\t" - "sw %[r5], 12(%[outBuf]) \n\t" -#else - "shra_r.w %[r8], %[r8], 15 \n\t" - "shra_r.w %[r0], %[r0], 15 \n\t" - "shra_r.w %[r1], %[r1], 15 \n\t" - "shra_r.w %[r2], %[r2], 15 \n\t" - "shra_r.w %[r3], %[r3], 15 \n\t" - "shra_r.w %[r4], %[r4], 15 \n\t" - "shra_r.w %[r5], %[r5], 15 \n\t" - "shra_r.w %[r6], %[r6], 15 \n\t" - "sll %[r0], %[r0], 16 \n\t" - "sll %[r2], %[r2], 16 \n\t" - "sll %[r4], %[r4], 16 \n\t" - "sll %[r6], %[r6], 16 \n\t" - "packrl.ph %[r0], %[r8], %[r0] \n\t" - "packrl.ph %[r2], %[r1], %[r2] \n\t" - "packrl.ph %[r4], %[r3], %[r4] \n\t" - "packrl.ph %[r6], %[r5], %[r6] \n\t" - "sw %[r0], 0(%[outBuf]) \n\t" - "sw %[r2], 4(%[outBuf]) \n\t" - "sw %[r4], 8(%[outBuf]) \n\t" - "sw %[r6], 12(%[outBuf]) \n\t" -#endif - "addiu %[window], %[window], 16 \n\t" - "addiu %[anaBuf], %[anaBuf], 16 \n\t" - "addiu %[outBuf], %[outBuf], 16 \n\t" - "b 1b \n\t" - " addiu %[iters], %[iters], -1 \n\t" - "2: \n\t" - "andi %[after], %[anaLen], 7 \n\t" - "3: \n\t" - "blez %[after], 4f \n\t" - " nop \n\t" - "lh %[r0], 0(%[window]) \n\t" - "lh %[r1], 0(%[anaBuf]) \n\t" - "mul %[r0], %[r0], %[r1] \n\t" - "addiu %[window], %[window], 2 \n\t" - "addiu %[anaBuf], %[anaBuf], 2 \n\t" - "addiu %[outBuf], %[outBuf], 2 \n\t" - "shra_r.w %[r0], %[r0], 14 \n\t" - "sh %[r0], -2(%[outBuf]) \n\t" - "b 3b \n\t" - " addiu %[after], %[after], -1 \n\t" - "4: \n\t" - ".set pop \n\t" - : [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), - [r3] "=&r" (r3), [r4] "=&r" (r4), [r5] "=&r" (r5), - [r6] "=&r" (r6), [r7] "=&r" (r7), [r8] "=&r" (r8), - [iters] "=&r" (iters), [after] "=&r" (after), - [window] "+r" (window),[anaBuf] "+r" (anaBuf), - [outBuf] "+r" (outBuf) - : [anaLen] "r" (anaLen) - : "memory", "hi", "lo" - ); -#else - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "sra %[iters], %[anaLen], 2 \n\t" - "1: \n\t" - "blez %[iters], 2f \n\t" - " nop \n\t" - "lh %[r0], 0(%[window]) \n\t" - "lh %[r1], 0(%[anaBuf]) \n\t" - "lh %[r2], 2(%[window]) \n\t" - "lh %[r3], 2(%[anaBuf]) \n\t" - "lh %[r4], 4(%[window]) \n\t" - "lh %[r5], 4(%[anaBuf]) \n\t" - "lh %[r6], 6(%[window]) \n\t" - "lh %[r7], 6(%[anaBuf]) \n\t" - "mul %[r0], %[r0], %[r1] \n\t" - "mul %[r2], %[r2], %[r3] \n\t" - "mul %[r4], %[r4], %[r5] \n\t" - "mul %[r6], %[r6], %[r7] \n\t" - "addiu %[window], %[window], 8 \n\t" - "addiu %[anaBuf], %[anaBuf], 8 \n\t" - "addiu %[r0], %[r0], 0x2000 \n\t" - "addiu %[r2], %[r2], 0x2000 \n\t" - "addiu %[r4], %[r4], 0x2000 \n\t" - "addiu %[r6], %[r6], 0x2000 \n\t" - "sra %[r0], %[r0], 14 \n\t" - "sra %[r2], %[r2], 14 \n\t" - "sra %[r4], %[r4], 14 \n\t" - "sra %[r6], %[r6], 14 \n\t" - "sh %[r0], 0(%[outBuf]) \n\t" - "sh %[r2], 2(%[outBuf]) \n\t" - "sh %[r4], 4(%[outBuf]) \n\t" - "sh %[r6], 6(%[outBuf]) \n\t" - "addiu %[outBuf], %[outBuf], 8 \n\t" - "b 1b \n\t" - " addiu %[iters], %[iters], -1 \n\t" - "2: \n\t" - "andi %[after], %[anaLen], 3 \n\t" - "3: \n\t" - "blez %[after], 4f \n\t" - " nop \n\t" - "lh %[r0], 0(%[window]) \n\t" - "lh %[r1], 0(%[anaBuf]) \n\t" - "mul %[r0], %[r0], %[r1] \n\t" - "addiu %[window], %[window], 2 \n\t" - "addiu %[anaBuf], %[anaBuf], 2 \n\t" - "addiu %[outBuf], %[outBuf], 2 \n\t" - "addiu %[r0], %[r0], 0x2000 \n\t" - "sra %[r0], %[r0], 14 \n\t" - "sh %[r0], -2(%[outBuf]) \n\t" - "b 3b \n\t" - " addiu %[after], %[after], -1 \n\t" - "4: \n\t" - ".set pop \n\t" - : [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), - [r3] "=&r" (r3), [r4] "=&r" (r4), [r5] "=&r" (r5), - [r6] "=&r" (r6), [r7] "=&r" (r7), [iters] "=&r" (iters), - [after] "=&r" (after), [window] "+r" (window), - [anaBuf] "+r" (anaBuf), [outBuf] "+r" (outBuf) - : [anaLen] "r" (anaLen) - : "memory", "hi", "lo" - ); -#endif -} - -// For the noise supression process, synthesis, read out fully processed -// segment, and update synthesis buffer. -void WebRtcNsx_SynthesisUpdate_mips(NoiseSuppressionFixedC* inst, - int16_t* out_frame, - int16_t gain_factor) { - int iters = (int)inst->blockLen10ms >> 2; - int after = inst->blockLen10ms & 3; - int r0, r1, r2, r3, r4, r5, r6, r7; - int16_t *window = (int16_t*)inst->window; - int16_t *real = inst->real; - int16_t *synthBuf = inst->synthesisBuffer; - int16_t *out = out_frame; - int sat_pos = 0x7fff; - int sat_neg = 0xffff8000; - int block10 = (int)inst->blockLen10ms; - int anaLen = (int)inst->anaLen; - - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "1: \n\t" - "blez %[iters], 2f \n\t" - " nop \n\t" - "lh %[r0], 0(%[window]) \n\t" - "lh %[r1], 0(%[real]) \n\t" - "lh %[r2], 2(%[window]) \n\t" - "lh %[r3], 2(%[real]) \n\t" - "lh %[r4], 4(%[window]) \n\t" - "lh %[r5], 4(%[real]) \n\t" - "lh %[r6], 6(%[window]) \n\t" - "lh %[r7], 6(%[real]) \n\t" - "mul %[r0], %[r0], %[r1] \n\t" - "mul %[r2], %[r2], %[r3] \n\t" - "mul %[r4], %[r4], %[r5] \n\t" - "mul %[r6], %[r6], %[r7] \n\t" - "addiu %[r0], %[r0], 0x2000 \n\t" - "addiu %[r2], %[r2], 0x2000 \n\t" - "addiu %[r4], %[r4], 0x2000 \n\t" - "addiu %[r6], %[r6], 0x2000 \n\t" - "sra %[r0], %[r0], 14 \n\t" - "sra %[r2], %[r2], 14 \n\t" - "sra %[r4], %[r4], 14 \n\t" - "sra %[r6], %[r6], 14 \n\t" - "mul %[r0], %[r0], %[gain_factor] \n\t" - "mul %[r2], %[r2], %[gain_factor] \n\t" - "mul %[r4], %[r4], %[gain_factor] \n\t" - "mul %[r6], %[r6], %[gain_factor] \n\t" - "addiu %[r0], %[r0], 0x1000 \n\t" - "addiu %[r2], %[r2], 0x1000 \n\t" - "addiu %[r4], %[r4], 0x1000 \n\t" - "addiu %[r6], %[r6], 0x1000 \n\t" - "sra %[r0], %[r0], 13 \n\t" - "sra %[r2], %[r2], 13 \n\t" - "sra %[r4], %[r4], 13 \n\t" - "sra %[r6], %[r6], 13 \n\t" - "slt %[r1], %[r0], %[sat_pos] \n\t" - "slt %[r3], %[r2], %[sat_pos] \n\t" - "slt %[r5], %[r4], %[sat_pos] \n\t" - "slt %[r7], %[r6], %[sat_pos] \n\t" - "movz %[r0], %[sat_pos], %[r1] \n\t" - "movz %[r2], %[sat_pos], %[r3] \n\t" - "movz %[r4], %[sat_pos], %[r5] \n\t" - "movz %[r6], %[sat_pos], %[r7] \n\t" - "lh %[r1], 0(%[synthBuf]) \n\t" - "lh %[r3], 2(%[synthBuf]) \n\t" - "lh %[r5], 4(%[synthBuf]) \n\t" - "lh %[r7], 6(%[synthBuf]) \n\t" - "addu %[r0], %[r0], %[r1] \n\t" - "addu %[r2], %[r2], %[r3] \n\t" - "addu %[r4], %[r4], %[r5] \n\t" - "addu %[r6], %[r6], %[r7] \n\t" - "slt %[r1], %[r0], %[sat_pos] \n\t" - "slt %[r3], %[r2], %[sat_pos] \n\t" - "slt %[r5], %[r4], %[sat_pos] \n\t" - "slt %[r7], %[r6], %[sat_pos] \n\t" - "movz %[r0], %[sat_pos], %[r1] \n\t" - "movz %[r2], %[sat_pos], %[r3] \n\t" - "movz %[r4], %[sat_pos], %[r5] \n\t" - "movz %[r6], %[sat_pos], %[r7] \n\t" - "slt %[r1], %[r0], %[sat_neg] \n\t" - "slt %[r3], %[r2], %[sat_neg] \n\t" - "slt %[r5], %[r4], %[sat_neg] \n\t" - "slt %[r7], %[r6], %[sat_neg] \n\t" - "movn %[r0], %[sat_neg], %[r1] \n\t" - "movn %[r2], %[sat_neg], %[r3] \n\t" - "movn %[r4], %[sat_neg], %[r5] \n\t" - "movn %[r6], %[sat_neg], %[r7] \n\t" - "sh %[r0], 0(%[synthBuf]) \n\t" - "sh %[r2], 2(%[synthBuf]) \n\t" - "sh %[r4], 4(%[synthBuf]) \n\t" - "sh %[r6], 6(%[synthBuf]) \n\t" - "sh %[r0], 0(%[out]) \n\t" - "sh %[r2], 2(%[out]) \n\t" - "sh %[r4], 4(%[out]) \n\t" - "sh %[r6], 6(%[out]) \n\t" - "addiu %[window], %[window], 8 \n\t" - "addiu %[real], %[real], 8 \n\t" - "addiu %[synthBuf],%[synthBuf], 8 \n\t" - "addiu %[out], %[out], 8 \n\t" - "b 1b \n\t" - " addiu %[iters], %[iters], -1 \n\t" - "2: \n\t" - "blez %[after], 3f \n\t" - " subu %[block10], %[anaLen], %[block10] \n\t" - "lh %[r0], 0(%[window]) \n\t" - "lh %[r1], 0(%[real]) \n\t" - "mul %[r0], %[r0], %[r1] \n\t" - "addiu %[window], %[window], 2 \n\t" - "addiu %[real], %[real], 2 \n\t" - "addiu %[r0], %[r0], 0x2000 \n\t" - "sra %[r0], %[r0], 14 \n\t" - "mul %[r0], %[r0], %[gain_factor] \n\t" - "addiu %[r0], %[r0], 0x1000 \n\t" - "sra %[r0], %[r0], 13 \n\t" - "slt %[r1], %[r0], %[sat_pos] \n\t" - "movz %[r0], %[sat_pos], %[r1] \n\t" - "lh %[r1], 0(%[synthBuf]) \n\t" - "addu %[r0], %[r0], %[r1] \n\t" - "slt %[r1], %[r0], %[sat_pos] \n\t" - "movz %[r0], %[sat_pos], %[r1] \n\t" - "slt %[r1], %[r0], %[sat_neg] \n\t" - "movn %[r0], %[sat_neg], %[r1] \n\t" - "sh %[r0], 0(%[synthBuf]) \n\t" - "sh %[r0], 0(%[out]) \n\t" - "addiu %[synthBuf],%[synthBuf], 2 \n\t" - "addiu %[out], %[out], 2 \n\t" - "b 2b \n\t" - " addiu %[after], %[after], -1 \n\t" - "3: \n\t" - "sra %[iters], %[block10], 2 \n\t" - "4: \n\t" - "blez %[iters], 5f \n\t" - " andi %[after], %[block10], 3 \n\t" - "lh %[r0], 0(%[window]) \n\t" - "lh %[r1], 0(%[real]) \n\t" - "lh %[r2], 2(%[window]) \n\t" - "lh %[r3], 2(%[real]) \n\t" - "lh %[r4], 4(%[window]) \n\t" - "lh %[r5], 4(%[real]) \n\t" - "lh %[r6], 6(%[window]) \n\t" - "lh %[r7], 6(%[real]) \n\t" - "mul %[r0], %[r0], %[r1] \n\t" - "mul %[r2], %[r2], %[r3] \n\t" - "mul %[r4], %[r4], %[r5] \n\t" - "mul %[r6], %[r6], %[r7] \n\t" - "addiu %[r0], %[r0], 0x2000 \n\t" - "addiu %[r2], %[r2], 0x2000 \n\t" - "addiu %[r4], %[r4], 0x2000 \n\t" - "addiu %[r6], %[r6], 0x2000 \n\t" - "sra %[r0], %[r0], 14 \n\t" - "sra %[r2], %[r2], 14 \n\t" - "sra %[r4], %[r4], 14 \n\t" - "sra %[r6], %[r6], 14 \n\t" - "mul %[r0], %[r0], %[gain_factor] \n\t" - "mul %[r2], %[r2], %[gain_factor] \n\t" - "mul %[r4], %[r4], %[gain_factor] \n\t" - "mul %[r6], %[r6], %[gain_factor] \n\t" - "addiu %[r0], %[r0], 0x1000 \n\t" - "addiu %[r2], %[r2], 0x1000 \n\t" - "addiu %[r4], %[r4], 0x1000 \n\t" - "addiu %[r6], %[r6], 0x1000 \n\t" - "sra %[r0], %[r0], 13 \n\t" - "sra %[r2], %[r2], 13 \n\t" - "sra %[r4], %[r4], 13 \n\t" - "sra %[r6], %[r6], 13 \n\t" - "slt %[r1], %[r0], %[sat_pos] \n\t" - "slt %[r3], %[r2], %[sat_pos] \n\t" - "slt %[r5], %[r4], %[sat_pos] \n\t" - "slt %[r7], %[r6], %[sat_pos] \n\t" - "movz %[r0], %[sat_pos], %[r1] \n\t" - "movz %[r2], %[sat_pos], %[r3] \n\t" - "movz %[r4], %[sat_pos], %[r5] \n\t" - "movz %[r6], %[sat_pos], %[r7] \n\t" - "lh %[r1], 0(%[synthBuf]) \n\t" - "lh %[r3], 2(%[synthBuf]) \n\t" - "lh %[r5], 4(%[synthBuf]) \n\t" - "lh %[r7], 6(%[synthBuf]) \n\t" - "addu %[r0], %[r0], %[r1] \n\t" - "addu %[r2], %[r2], %[r3] \n\t" - "addu %[r4], %[r4], %[r5] \n\t" - "addu %[r6], %[r6], %[r7] \n\t" - "slt %[r1], %[r0], %[sat_pos] \n\t" - "slt %[r3], %[r2], %[sat_pos] \n\t" - "slt %[r5], %[r4], %[sat_pos] \n\t" - "slt %[r7], %[r6], %[sat_pos] \n\t" - "movz %[r0], %[sat_pos], %[r1] \n\t" - "movz %[r2], %[sat_pos], %[r3] \n\t" - "movz %[r4], %[sat_pos], %[r5] \n\t" - "movz %[r6], %[sat_pos], %[r7] \n\t" - "slt %[r1], %[r0], %[sat_neg] \n\t" - "slt %[r3], %[r2], %[sat_neg] \n\t" - "slt %[r5], %[r4], %[sat_neg] \n\t" - "slt %[r7], %[r6], %[sat_neg] \n\t" - "movn %[r0], %[sat_neg], %[r1] \n\t" - "movn %[r2], %[sat_neg], %[r3] \n\t" - "movn %[r4], %[sat_neg], %[r5] \n\t" - "movn %[r6], %[sat_neg], %[r7] \n\t" - "sh %[r0], 0(%[synthBuf]) \n\t" - "sh %[r2], 2(%[synthBuf]) \n\t" - "sh %[r4], 4(%[synthBuf]) \n\t" - "sh %[r6], 6(%[synthBuf]) \n\t" - "addiu %[window], %[window], 8 \n\t" - "addiu %[real], %[real], 8 \n\t" - "addiu %[synthBuf],%[synthBuf], 8 \n\t" - "b 4b \n\t" - " addiu %[iters], %[iters], -1 \n\t" - "5: \n\t" - "blez %[after], 6f \n\t" - " nop \n\t" - "lh %[r0], 0(%[window]) \n\t" - "lh %[r1], 0(%[real]) \n\t" - "mul %[r0], %[r0], %[r1] \n\t" - "addiu %[window], %[window], 2 \n\t" - "addiu %[real], %[real], 2 \n\t" - "addiu %[r0], %[r0], 0x2000 \n\t" - "sra %[r0], %[r0], 14 \n\t" - "mul %[r0], %[r0], %[gain_factor] \n\t" - "addiu %[r0], %[r0], 0x1000 \n\t" - "sra %[r0], %[r0], 13 \n\t" - "slt %[r1], %[r0], %[sat_pos] \n\t" - "movz %[r0], %[sat_pos], %[r1] \n\t" - "lh %[r1], 0(%[synthBuf]) \n\t" - "addu %[r0], %[r0], %[r1] \n\t" - "slt %[r1], %[r0], %[sat_pos] \n\t" - "movz %[r0], %[sat_pos], %[r1] \n\t" - "slt %[r1], %[r0], %[sat_neg] \n\t" - "movn %[r0], %[sat_neg], %[r1] \n\t" - "sh %[r0], 0(%[synthBuf]) \n\t" - "addiu %[synthBuf],%[synthBuf], 2 \n\t" - "b 2b \n\t" - " addiu %[after], %[after], -1 \n\t" - "6: \n\t" - ".set pop \n\t" - : [r0] "=&r" (r0), [r1] "=&r" (r1), [r2] "=&r" (r2), - [r3] "=&r" (r3), [r4] "=&r" (r4), [r5] "=&r" (r5), - [r6] "=&r" (r6), [r7] "=&r" (r7), [iters] "+r" (iters), - [after] "+r" (after), [block10] "+r" (block10), - [window] "+r" (window), [real] "+r" (real), - [synthBuf] "+r" (synthBuf), [out] "+r" (out) - : [gain_factor] "r" (gain_factor), [sat_pos] "r" (sat_pos), - [sat_neg] "r" (sat_neg), [anaLen] "r" (anaLen) - : "memory", "hi", "lo" - ); - - // update synthesis buffer - memcpy(inst->synthesisBuffer, inst->synthesisBuffer + inst->blockLen10ms, - (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->synthesisBuffer)); - WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer - + inst->anaLen - inst->blockLen10ms, inst->blockLen10ms); -} - -// Filter the data in the frequency domain, and create spectrum. -void WebRtcNsx_PrepareSpectrum_mips(NoiseSuppressionFixedC* inst, - int16_t* freq_buf) { - uint16_t *noiseSupFilter = inst->noiseSupFilter; - int16_t *real = inst->real; - int16_t *imag = inst->imag; - int32_t loop_count = 2; - int16_t tmp_1, tmp_2, tmp_3, tmp_4, tmp_5, tmp_6; - int16_t tmp16 = (int16_t)(inst->anaLen << 1) - 4; - int16_t* freq_buf_f = freq_buf; - int16_t* freq_buf_s = &freq_buf[tmp16]; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - //first sample - "lh %[tmp_1], 0(%[noiseSupFilter]) \n\t" - "lh %[tmp_2], 0(%[real]) \n\t" - "lh %[tmp_3], 0(%[imag]) \n\t" - "mul %[tmp_2], %[tmp_2], %[tmp_1] \n\t" - "mul %[tmp_3], %[tmp_3], %[tmp_1] \n\t" - "sra %[tmp_2], %[tmp_2], 14 \n\t" - "sra %[tmp_3], %[tmp_3], 14 \n\t" - "sh %[tmp_2], 0(%[real]) \n\t" - "sh %[tmp_3], 0(%[imag]) \n\t" - "negu %[tmp_3], %[tmp_3] \n\t" - "sh %[tmp_2], 0(%[freq_buf_f]) \n\t" - "sh %[tmp_3], 2(%[freq_buf_f]) \n\t" - "addiu %[real], %[real], 2 \n\t" - "addiu %[imag], %[imag], 2 \n\t" - "addiu %[noiseSupFilter], %[noiseSupFilter], 2 \n\t" - "addiu %[freq_buf_f], %[freq_buf_f], 4 \n\t" - "1: \n\t" - "lh %[tmp_1], 0(%[noiseSupFilter]) \n\t" - "lh %[tmp_2], 0(%[real]) \n\t" - "lh %[tmp_3], 0(%[imag]) \n\t" - "lh %[tmp_4], 2(%[noiseSupFilter]) \n\t" - "lh %[tmp_5], 2(%[real]) \n\t" - "lh %[tmp_6], 2(%[imag]) \n\t" - "mul %[tmp_2], %[tmp_2], %[tmp_1] \n\t" - "mul %[tmp_3], %[tmp_3], %[tmp_1] \n\t" - "mul %[tmp_5], %[tmp_5], %[tmp_4] \n\t" - "mul %[tmp_6], %[tmp_6], %[tmp_4] \n\t" - "addiu %[loop_count], %[loop_count], 2 \n\t" - "sra %[tmp_2], %[tmp_2], 14 \n\t" - "sra %[tmp_3], %[tmp_3], 14 \n\t" - "sra %[tmp_5], %[tmp_5], 14 \n\t" - "sra %[tmp_6], %[tmp_6], 14 \n\t" - "addiu %[noiseSupFilter], %[noiseSupFilter], 4 \n\t" - "sh %[tmp_2], 0(%[real]) \n\t" - "sh %[tmp_2], 4(%[freq_buf_s]) \n\t" - "sh %[tmp_3], 0(%[imag]) \n\t" - "sh %[tmp_3], 6(%[freq_buf_s]) \n\t" - "negu %[tmp_3], %[tmp_3] \n\t" - "sh %[tmp_5], 2(%[real]) \n\t" - "sh %[tmp_5], 0(%[freq_buf_s]) \n\t" - "sh %[tmp_6], 2(%[imag]) \n\t" - "sh %[tmp_6], 2(%[freq_buf_s]) \n\t" - "negu %[tmp_6], %[tmp_6] \n\t" - "addiu %[freq_buf_s], %[freq_buf_s], -8 \n\t" - "addiu %[real], %[real], 4 \n\t" - "addiu %[imag], %[imag], 4 \n\t" - "sh %[tmp_2], 0(%[freq_buf_f]) \n\t" - "sh %[tmp_3], 2(%[freq_buf_f]) \n\t" - "sh %[tmp_5], 4(%[freq_buf_f]) \n\t" - "sh %[tmp_6], 6(%[freq_buf_f]) \n\t" - "blt %[loop_count], %[loop_size], 1b \n\t" - " addiu %[freq_buf_f], %[freq_buf_f], 8 \n\t" - //last two samples: - "lh %[tmp_1], 0(%[noiseSupFilter]) \n\t" - "lh %[tmp_2], 0(%[real]) \n\t" - "lh %[tmp_3], 0(%[imag]) \n\t" - "lh %[tmp_4], 2(%[noiseSupFilter]) \n\t" - "lh %[tmp_5], 2(%[real]) \n\t" - "lh %[tmp_6], 2(%[imag]) \n\t" - "mul %[tmp_2], %[tmp_2], %[tmp_1] \n\t" - "mul %[tmp_3], %[tmp_3], %[tmp_1] \n\t" - "mul %[tmp_5], %[tmp_5], %[tmp_4] \n\t" - "mul %[tmp_6], %[tmp_6], %[tmp_4] \n\t" - "sra %[tmp_2], %[tmp_2], 14 \n\t" - "sra %[tmp_3], %[tmp_3], 14 \n\t" - "sra %[tmp_5], %[tmp_5], 14 \n\t" - "sra %[tmp_6], %[tmp_6], 14 \n\t" - "sh %[tmp_2], 0(%[real]) \n\t" - "sh %[tmp_2], 4(%[freq_buf_s]) \n\t" - "sh %[tmp_3], 0(%[imag]) \n\t" - "sh %[tmp_3], 6(%[freq_buf_s]) \n\t" - "negu %[tmp_3], %[tmp_3] \n\t" - "sh %[tmp_2], 0(%[freq_buf_f]) \n\t" - "sh %[tmp_3], 2(%[freq_buf_f]) \n\t" - "sh %[tmp_5], 4(%[freq_buf_f]) \n\t" - "sh %[tmp_6], 6(%[freq_buf_f]) \n\t" - "sh %[tmp_5], 2(%[real]) \n\t" - "sh %[tmp_6], 2(%[imag]) \n\t" - ".set pop \n\t" - : [real] "+r" (real), [imag] "+r" (imag), - [freq_buf_f] "+r" (freq_buf_f), [freq_buf_s] "+r" (freq_buf_s), - [loop_count] "+r" (loop_count), [noiseSupFilter] "+r" (noiseSupFilter), - [tmp_1] "=&r" (tmp_1), [tmp_2] "=&r" (tmp_2), [tmp_3] "=&r" (tmp_3), - [tmp_4] "=&r" (tmp_4), [tmp_5] "=&r" (tmp_5), [tmp_6] "=&r" (tmp_6) - : [loop_size] "r" (inst->anaLen2) - : "memory", "hi", "lo" - ); -} - -#if defined(MIPS_DSP_R1_LE) -// Denormalize the real-valued signal |in|, the output from inverse FFT. -void WebRtcNsx_Denormalize_mips(NoiseSuppressionFixedC* inst, - int16_t* in, - int factor) { - int32_t r0, r1, r2, r3, t0; - int len = (int)inst->anaLen; - int16_t *out = &inst->real[0]; - int shift = factor - inst->normData; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "beqz %[len], 8f \n\t" - " nop \n\t" - "bltz %[shift], 4f \n\t" - " sra %[t0], %[len], 2 \n\t" - "beqz %[t0], 2f \n\t" - " andi %[len], %[len], 3 \n\t" - "1: \n\t" - "lh %[r0], 0(%[in]) \n\t" - "lh %[r1], 2(%[in]) \n\t" - "lh %[r2], 4(%[in]) \n\t" - "lh %[r3], 6(%[in]) \n\t" - "shllv_s.ph %[r0], %[r0], %[shift] \n\t" - "shllv_s.ph %[r1], %[r1], %[shift] \n\t" - "shllv_s.ph %[r2], %[r2], %[shift] \n\t" - "shllv_s.ph %[r3], %[r3], %[shift] \n\t" - "addiu %[in], %[in], 8 \n\t" - "addiu %[t0], %[t0], -1 \n\t" - "sh %[r0], 0(%[out]) \n\t" - "sh %[r1], 2(%[out]) \n\t" - "sh %[r2], 4(%[out]) \n\t" - "sh %[r3], 6(%[out]) \n\t" - "bgtz %[t0], 1b \n\t" - " addiu %[out], %[out], 8 \n\t" - "2: \n\t" - "beqz %[len], 8f \n\t" - " nop \n\t" - "3: \n\t" - "lh %[r0], 0(%[in]) \n\t" - "addiu %[in], %[in], 2 \n\t" - "addiu %[len], %[len], -1 \n\t" - "shllv_s.ph %[r0], %[r0], %[shift] \n\t" - "addiu %[out], %[out], 2 \n\t" - "bgtz %[len], 3b \n\t" - " sh %[r0], -2(%[out]) \n\t" - "b 8f \n\t" - "4: \n\t" - "negu %[shift], %[shift] \n\t" - "beqz %[t0], 6f \n\t" - " andi %[len], %[len], 3 \n\t" - "5: \n\t" - "lh %[r0], 0(%[in]) \n\t" - "lh %[r1], 2(%[in]) \n\t" - "lh %[r2], 4(%[in]) \n\t" - "lh %[r3], 6(%[in]) \n\t" - "srav %[r0], %[r0], %[shift] \n\t" - "srav %[r1], %[r1], %[shift] \n\t" - "srav %[r2], %[r2], %[shift] \n\t" - "srav %[r3], %[r3], %[shift] \n\t" - "addiu %[in], %[in], 8 \n\t" - "addiu %[t0], %[t0], -1 \n\t" - "sh %[r0], 0(%[out]) \n\t" - "sh %[r1], 2(%[out]) \n\t" - "sh %[r2], 4(%[out]) \n\t" - "sh %[r3], 6(%[out]) \n\t" - "bgtz %[t0], 5b \n\t" - " addiu %[out], %[out], 8 \n\t" - "6: \n\t" - "beqz %[len], 8f \n\t" - " nop \n\t" - "7: \n\t" - "lh %[r0], 0(%[in]) \n\t" - "addiu %[in], %[in], 2 \n\t" - "addiu %[len], %[len], -1 \n\t" - "srav %[r0], %[r0], %[shift] \n\t" - "addiu %[out], %[out], 2 \n\t" - "bgtz %[len], 7b \n\t" - " sh %[r0], -2(%[out]) \n\t" - "8: \n\t" - ".set pop \n\t" - : [t0] "=&r" (t0), [r0] "=&r" (r0), [r1] "=&r" (r1), - [r2] "=&r" (r2), [r3] "=&r" (r3) - : [len] "r" (len), [shift] "r" (shift), [in] "r" (in), - [out] "r" (out) - : "memory" - ); -} -#endif - -// Normalize the real-valued signal |in|, the input to forward FFT. -void WebRtcNsx_NormalizeRealBuffer_mips(NoiseSuppressionFixedC* inst, - const int16_t* in, - int16_t* out) { - int32_t r0, r1, r2, r3, t0; - int len = (int)inst->anaLen; - int shift = inst->normData; - - __asm __volatile ( - ".set push \n\t" - ".set noreorder \n\t" - "beqz %[len], 4f \n\t" - " sra %[t0], %[len], 2 \n\t" - "beqz %[t0], 2f \n\t" - " andi %[len], %[len], 3 \n\t" - "1: \n\t" - "lh %[r0], 0(%[in]) \n\t" - "lh %[r1], 2(%[in]) \n\t" - "lh %[r2], 4(%[in]) \n\t" - "lh %[r3], 6(%[in]) \n\t" - "sllv %[r0], %[r0], %[shift] \n\t" - "sllv %[r1], %[r1], %[shift] \n\t" - "sllv %[r2], %[r2], %[shift] \n\t" - "sllv %[r3], %[r3], %[shift] \n\t" - "addiu %[in], %[in], 8 \n\t" - "addiu %[t0], %[t0], -1 \n\t" - "sh %[r0], 0(%[out]) \n\t" - "sh %[r1], 2(%[out]) \n\t" - "sh %[r2], 4(%[out]) \n\t" - "sh %[r3], 6(%[out]) \n\t" - "bgtz %[t0], 1b \n\t" - " addiu %[out], %[out], 8 \n\t" - "2: \n\t" - "beqz %[len], 4f \n\t" - " nop \n\t" - "3: \n\t" - "lh %[r0], 0(%[in]) \n\t" - "addiu %[in], %[in], 2 \n\t" - "addiu %[len], %[len], -1 \n\t" - "sllv %[r0], %[r0], %[shift] \n\t" - "addiu %[out], %[out], 2 \n\t" - "bgtz %[len], 3b \n\t" - " sh %[r0], -2(%[out]) \n\t" - "4: \n\t" - ".set pop \n\t" - : [t0] "=&r" (t0), [r0] "=&r" (r0), [r1] "=&r" (r1), - [r2] "=&r" (r2), [r3] "=&r" (r3) - : [len] "r" (len), [shift] "r" (shift), [in] "r" (in), - [out] "r" (out) - : "memory" - ); -} - diff --git a/webrtc/modules/audio_processing/ns/nsx_core_neon.c b/webrtc/modules/audio_processing/ns/nsx_core_neon.c deleted file mode 100644 index 65788ae..0000000 --- a/webrtc/modules/audio_processing/ns/nsx_core_neon.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_processing/ns/nsx_core.h" - -#include -#include - -// Constants to compensate for shifting signal log(2^shifts). -const int16_t WebRtcNsx_kLogTable[9] = { - 0, 177, 355, 532, 710, 887, 1065, 1242, 1420 -}; - -const int16_t WebRtcNsx_kCounterDiv[201] = { - 32767, 16384, 10923, 8192, 6554, 5461, 4681, 4096, 3641, 3277, 2979, 2731, - 2521, 2341, 2185, 2048, 1928, 1820, 1725, 1638, 1560, 1489, 1425, 1365, 1311, - 1260, 1214, 1170, 1130, 1092, 1057, 1024, 993, 964, 936, 910, 886, 862, 840, - 819, 799, 780, 762, 745, 728, 712, 697, 683, 669, 655, 643, 630, 618, 607, - 596, 585, 575, 565, 555, 546, 537, 529, 520, 512, 504, 496, 489, 482, 475, - 468, 462, 455, 449, 443, 437, 431, 426, 420, 415, 410, 405, 400, 395, 390, - 386, 381, 377, 372, 368, 364, 360, 356, 352, 349, 345, 341, 338, 334, 331, - 328, 324, 321, 318, 315, 312, 309, 306, 303, 301, 298, 295, 293, 290, 287, - 285, 282, 280, 278, 275, 273, 271, 269, 266, 264, 262, 260, 258, 256, 254, - 252, 250, 248, 246, 245, 243, 241, 239, 237, 236, 234, 232, 231, 229, 228, - 226, 224, 223, 221, 220, 218, 217, 216, 214, 213, 211, 210, 209, 207, 206, - 205, 204, 202, 201, 200, 199, 197, 196, 195, 194, 193, 192, 191, 189, 188, - 187, 186, 185, 184, 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, 173, - 172, 172, 171, 170, 169, 168, 167, 166, 165, 165, 164, 163 -}; - -const int16_t WebRtcNsx_kLogTableFrac[256] = { - 0, 1, 3, 4, 6, 7, 9, 10, 11, 13, 14, 16, 17, 18, 20, 21, - 22, 24, 25, 26, 28, 29, 30, 32, 33, 34, 36, 37, 38, 40, 41, 42, - 44, 45, 46, 47, 49, 50, 51, 52, 54, 55, 56, 57, 59, 60, 61, 62, - 63, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 77, 78, 79, 80, 81, - 82, 84, 85, 86, 87, 88, 89, 90, 92, 93, 94, 95, 96, 97, 98, 99, - 100, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 116, - 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, - 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, - 147, 148, 149, 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 160, - 161, 162, 163, 164, 165, 166, 167, 168, 169, 169, 170, 171, 172, 173, 174, - 175, 176, 177, 178, 178, 179, 180, 181, 182, 183, 184, 185, 185, 186, 187, - 188, 189, 190, 191, 192, 192, 193, 194, 195, 196, 197, 198, 198, 199, 200, - 201, 202, 203, 203, 204, 205, 206, 207, 208, 208, 209, 210, 211, 212, 212, - 213, 214, 215, 216, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 224, - 225, 226, 227, 228, 228, 229, 230, 231, 231, 232, 233, 234, 234, 235, 236, - 237, 238, 238, 239, 240, 241, 241, 242, 243, 244, 244, 245, 246, 247, 247, - 248, 249, 249, 250, 251, 252, 252, 253, 254, 255, 255 -}; - -// Update the noise estimation information. -static void UpdateNoiseEstimateNeon(NoiseSuppressionFixedC* inst, int offset) { - const int16_t kExp2Const = 11819; // Q13 - int16_t* ptr_noiseEstLogQuantile = NULL; - int16_t* ptr_noiseEstQuantile = NULL; - int16x4_t kExp2Const16x4 = vdup_n_s16(kExp2Const); - int32x4_t twentyOne32x4 = vdupq_n_s32(21); - int32x4_t constA32x4 = vdupq_n_s32(0x1fffff); - int32x4_t constB32x4 = vdupq_n_s32(0x200000); - - int16_t tmp16 = WebRtcSpl_MaxValueW16(inst->noiseEstLogQuantile + offset, - inst->magnLen); - - // Guarantee a Q-domain as high as possible and still fit in int16 - inst->qNoise = 14 - (int) WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(kExp2Const, - tmp16, - 21); - - int32x4_t qNoise32x4 = vdupq_n_s32(inst->qNoise); - - for (ptr_noiseEstLogQuantile = &inst->noiseEstLogQuantile[offset], - ptr_noiseEstQuantile = &inst->noiseEstQuantile[0]; - ptr_noiseEstQuantile < &inst->noiseEstQuantile[inst->magnLen - 3]; - ptr_noiseEstQuantile += 4, ptr_noiseEstLogQuantile += 4) { - - // tmp32no2 = kExp2Const * inst->noiseEstLogQuantile[offset + i]; - int16x4_t v16x4 = vld1_s16(ptr_noiseEstLogQuantile); - int32x4_t v32x4B = vmull_s16(v16x4, kExp2Const16x4); - - // tmp32no1 = (0x00200000 | (tmp32no2 & 0x001FFFFF)); // 2^21 + frac - int32x4_t v32x4A = vandq_s32(v32x4B, constA32x4); - v32x4A = vorrq_s32(v32x4A, constB32x4); - - // tmp16 = (int16_t)(tmp32no2 >> 21); - v32x4B = vshrq_n_s32(v32x4B, 21); - - // tmp16 -= 21;// shift 21 to get result in Q0 - v32x4B = vsubq_s32(v32x4B, twentyOne32x4); - - // tmp16 += (int16_t) inst->qNoise; - // shift to get result in Q(qNoise) - v32x4B = vaddq_s32(v32x4B, qNoise32x4); - - // if (tmp16 < 0) { - // tmp32no1 >>= -tmp16; - // } else { - // tmp32no1 <<= tmp16; - // } - v32x4B = vshlq_s32(v32x4A, v32x4B); - - // tmp16 = WebRtcSpl_SatW32ToW16(tmp32no1); - v16x4 = vqmovn_s32(v32x4B); - - //inst->noiseEstQuantile[i] = tmp16; - vst1_s16(ptr_noiseEstQuantile, v16x4); - } - - // Last iteration: - - // inst->quantile[i]=exp(inst->lquantile[offset+i]); - // in Q21 - int32_t tmp32no2 = kExp2Const * *ptr_noiseEstLogQuantile; - int32_t tmp32no1 = (0x00200000 | (tmp32no2 & 0x001FFFFF)); // 2^21 + frac - - tmp16 = (int16_t)(tmp32no2 >> 21); - tmp16 -= 21;// shift 21 to get result in Q0 - tmp16 += (int16_t) inst->qNoise; //shift to get result in Q(qNoise) - if (tmp16 < 0) { - tmp32no1 >>= -tmp16; - } else { - tmp32no1 <<= tmp16; - } - *ptr_noiseEstQuantile = WebRtcSpl_SatW32ToW16(tmp32no1); -} - -// Noise Estimation -void WebRtcNsx_NoiseEstimationNeon(NoiseSuppressionFixedC* inst, - uint16_t* magn, - uint32_t* noise, - int16_t* q_noise) { - int16_t lmagn[HALF_ANAL_BLOCKL], counter, countDiv; - int16_t countProd, delta, zeros, frac; - int16_t log2, tabind, logval, tmp16, tmp16no1, tmp16no2; - const int16_t log2_const = 22713; - const int16_t width_factor = 21845; - - size_t i, s, offset; - - tabind = inst->stages - inst->normData; - assert(tabind < 9); - assert(tabind > -9); - if (tabind < 0) { - logval = -WebRtcNsx_kLogTable[-tabind]; - } else { - logval = WebRtcNsx_kLogTable[tabind]; - } - - int16x8_t logval_16x8 = vdupq_n_s16(logval); - - // lmagn(i)=log(magn(i))=log(2)*log2(magn(i)) - // magn is in Q(-stages), and the real lmagn values are: - // real_lmagn(i)=log(magn(i)*2^stages)=log(magn(i))+log(2^stages) - // lmagn in Q8 - for (i = 0; i < inst->magnLen; i++) { - if (magn[i]) { - zeros = WebRtcSpl_NormU32((uint32_t)magn[i]); - frac = (int16_t)((((uint32_t)magn[i] << zeros) - & 0x7FFFFFFF) >> 23); - assert(frac < 256); - // log2(magn(i)) - log2 = (int16_t)(((31 - zeros) << 8) - + WebRtcNsx_kLogTableFrac[frac]); - // log2(magn(i))*log(2) - lmagn[i] = (int16_t)((log2 * log2_const) >> 15); - // + log(2^stages) - lmagn[i] += logval; - } else { - lmagn[i] = logval; - } - } - - int16x4_t Q3_16x4 = vdup_n_s16(3); - int16x8_t WIDTHQ8_16x8 = vdupq_n_s16(WIDTH_Q8); - int16x8_t WIDTHFACTOR_16x8 = vdupq_n_s16(width_factor); - - int16_t factor = FACTOR_Q7; - if (inst->blockIndex < END_STARTUP_LONG) - factor = FACTOR_Q7_STARTUP; - - // Loop over simultaneous estimates - for (s = 0; s < SIMULT; s++) { - offset = s * inst->magnLen; - - // Get counter values from state - counter = inst->noiseEstCounter[s]; - assert(counter < 201); - countDiv = WebRtcNsx_kCounterDiv[counter]; - countProd = (int16_t)(counter * countDiv); - - // quant_est(...) - int16_t deltaBuff[8]; - int16x4_t tmp16x4_0; - int16x4_t tmp16x4_1; - int16x4_t countDiv_16x4 = vdup_n_s16(countDiv); - int16x8_t countProd_16x8 = vdupq_n_s16(countProd); - int16x8_t tmp16x8_0 = vdupq_n_s16(countDiv); - int16x8_t prod16x8 = vqrdmulhq_s16(WIDTHFACTOR_16x8, tmp16x8_0); - int16x8_t tmp16x8_1; - int16x8_t tmp16x8_2; - int16x8_t tmp16x8_3; - uint16x8_t tmp16x8_4; - int32x4_t tmp32x4; - - for (i = 0; i + 7 < inst->magnLen; i += 8) { - // Compute delta. - // Smaller step size during startup. This prevents from using - // unrealistic values causing overflow. - tmp16x8_0 = vdupq_n_s16(factor); - vst1q_s16(deltaBuff, tmp16x8_0); - - int j; - for (j = 0; j < 8; j++) { - if (inst->noiseEstDensity[offset + i + j] > 512) { - // Get values for deltaBuff by shifting intead of dividing. - int factor = WebRtcSpl_NormW16(inst->noiseEstDensity[offset + i + j]); - deltaBuff[j] = (int16_t)(FACTOR_Q16 >> (14 - factor)); - } - } - - // Update log quantile estimate - - // tmp16 = (int16_t)((delta * countDiv) >> 14); - tmp32x4 = vmull_s16(vld1_s16(&deltaBuff[0]), countDiv_16x4); - tmp16x4_1 = vshrn_n_s32(tmp32x4, 14); - tmp32x4 = vmull_s16(vld1_s16(&deltaBuff[4]), countDiv_16x4); - tmp16x4_0 = vshrn_n_s32(tmp32x4, 14); - tmp16x8_0 = vcombine_s16(tmp16x4_1, tmp16x4_0); // Keep for several lines. - - // prepare for the "if" branch - // tmp16 += 2; - // tmp16_1 = (Word16)(tmp16>>2); - tmp16x8_1 = vrshrq_n_s16(tmp16x8_0, 2); - - // inst->noiseEstLogQuantile[offset+i] + tmp16_1; - tmp16x8_2 = vld1q_s16(&inst->noiseEstLogQuantile[offset + i]); // Keep - tmp16x8_1 = vaddq_s16(tmp16x8_2, tmp16x8_1); // Keep for several lines - - // Prepare for the "else" branch - // tmp16 += 1; - // tmp16_1 = (Word16)(tmp16>>1); - tmp16x8_0 = vrshrq_n_s16(tmp16x8_0, 1); - - // tmp16_2 = (int16_t)((tmp16_1 * 3) >> 1); - tmp32x4 = vmull_s16(vget_low_s16(tmp16x8_0), Q3_16x4); - tmp16x4_1 = vshrn_n_s32(tmp32x4, 1); - - // tmp16_2 = (int16_t)((tmp16_1 * 3) >> 1); - tmp32x4 = vmull_s16(vget_high_s16(tmp16x8_0), Q3_16x4); - tmp16x4_0 = vshrn_n_s32(tmp32x4, 1); - - // inst->noiseEstLogQuantile[offset + i] - tmp16_2; - tmp16x8_0 = vcombine_s16(tmp16x4_1, tmp16x4_0); // keep - tmp16x8_0 = vsubq_s16(tmp16x8_2, tmp16x8_0); - - // logval is the smallest fixed point representation we can have. Values - // below that will correspond to values in the interval [0, 1], which - // can't possibly occur. - tmp16x8_0 = vmaxq_s16(tmp16x8_0, logval_16x8); - - // Do the if-else branches: - tmp16x8_3 = vld1q_s16(&lmagn[i]); // keep for several lines - tmp16x8_4 = vcgtq_s16(tmp16x8_3, tmp16x8_2); - tmp16x8_2 = vbslq_s16(tmp16x8_4, tmp16x8_1, tmp16x8_0); - vst1q_s16(&inst->noiseEstLogQuantile[offset + i], tmp16x8_2); - - // Update density estimate - // tmp16_1 + tmp16_2 - tmp16x8_1 = vld1q_s16(&inst->noiseEstDensity[offset + i]); - tmp16x8_0 = vqrdmulhq_s16(tmp16x8_1, countProd_16x8); - tmp16x8_0 = vaddq_s16(tmp16x8_0, prod16x8); - - // lmagn[i] - inst->noiseEstLogQuantile[offset + i] - tmp16x8_3 = vsubq_s16(tmp16x8_3, tmp16x8_2); - tmp16x8_3 = vabsq_s16(tmp16x8_3); - tmp16x8_4 = vcgtq_s16(WIDTHQ8_16x8, tmp16x8_3); - tmp16x8_1 = vbslq_s16(tmp16x8_4, tmp16x8_0, tmp16x8_1); - vst1q_s16(&inst->noiseEstDensity[offset + i], tmp16x8_1); - } // End loop over magnitude spectrum - - // Last iteration over magnitude spectrum: - // compute delta - if (inst->noiseEstDensity[offset + i] > 512) { - // Get values for deltaBuff by shifting intead of dividing. - int factor = WebRtcSpl_NormW16(inst->noiseEstDensity[offset + i]); - delta = (int16_t)(FACTOR_Q16 >> (14 - factor)); - } else { - delta = FACTOR_Q7; - if (inst->blockIndex < END_STARTUP_LONG) { - // Smaller step size during startup. This prevents from using - // unrealistic values causing overflow. - delta = FACTOR_Q7_STARTUP; - } - } - // update log quantile estimate - tmp16 = (int16_t)((delta * countDiv) >> 14); - if (lmagn[i] > inst->noiseEstLogQuantile[offset + i]) { - // +=QUANTILE*delta/(inst->counter[s]+1) QUANTILE=0.25, =1 in Q2 - // CounterDiv=1/(inst->counter[s]+1) in Q15 - tmp16 += 2; - inst->noiseEstLogQuantile[offset + i] += tmp16 / 4; - } else { - tmp16 += 1; - // *(1-QUANTILE), in Q2 QUANTILE=0.25, 1-0.25=0.75=3 in Q2 - // TODO(bjornv): investigate why we need to truncate twice. - tmp16no2 = (int16_t)((tmp16 / 2) * 3 / 2); - inst->noiseEstLogQuantile[offset + i] -= tmp16no2; - if (inst->noiseEstLogQuantile[offset + i] < logval) { - // logval is the smallest fixed point representation we can have. - // Values below that will correspond to values in the interval - // [0, 1], which can't possibly occur. - inst->noiseEstLogQuantile[offset + i] = logval; - } - } - - // update density estimate - if (WEBRTC_SPL_ABS_W16(lmagn[i] - inst->noiseEstLogQuantile[offset + i]) - < WIDTH_Q8) { - tmp16no1 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - inst->noiseEstDensity[offset + i], countProd, 15); - tmp16no2 = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - width_factor, countDiv, 15); - inst->noiseEstDensity[offset + i] = tmp16no1 + tmp16no2; - } - - - if (counter >= END_STARTUP_LONG) { - inst->noiseEstCounter[s] = 0; - if (inst->blockIndex >= END_STARTUP_LONG) { - UpdateNoiseEstimateNeon(inst, offset); - } - } - inst->noiseEstCounter[s]++; - - } // end loop over simultaneous estimates - - // Sequentially update the noise during startup - if (inst->blockIndex < END_STARTUP_LONG) { - UpdateNoiseEstimateNeon(inst, offset); - } - - for (i = 0; i < inst->magnLen; i++) { - noise[i] = (uint32_t)(inst->noiseEstQuantile[i]); // Q(qNoise) - } - (*q_noise) = (int16_t)inst->qNoise; -} - -// Filter the data in the frequency domain, and create spectrum. -void WebRtcNsx_PrepareSpectrumNeon(NoiseSuppressionFixedC* inst, - int16_t* freq_buf) { - assert(inst->magnLen % 8 == 1); - assert(inst->anaLen2 % 16 == 0); - - // (1) Filtering. - - // Fixed point C code for the next block is as follows: - // for (i = 0; i < inst->magnLen; i++) { - // inst->real[i] = (int16_t)((inst->real[i] * - // (int16_t)(inst->noiseSupFilter[i])) >> 14); // Q(normData-stages) - // inst->imag[i] = (int16_t)((inst->imag[i] * - // (int16_t)(inst->noiseSupFilter[i])) >> 14); // Q(normData-stages) - // } - - int16_t* preal = &inst->real[0]; - int16_t* pimag = &inst->imag[0]; - int16_t* pns_filter = (int16_t*)&inst->noiseSupFilter[0]; - int16_t* pimag_end = pimag + inst->magnLen - 4; - - while (pimag < pimag_end) { - int16x8_t real = vld1q_s16(preal); - int16x8_t imag = vld1q_s16(pimag); - int16x8_t ns_filter = vld1q_s16(pns_filter); - - int32x4_t tmp_r_0 = vmull_s16(vget_low_s16(real), vget_low_s16(ns_filter)); - int32x4_t tmp_i_0 = vmull_s16(vget_low_s16(imag), vget_low_s16(ns_filter)); - int32x4_t tmp_r_1 = vmull_s16(vget_high_s16(real), - vget_high_s16(ns_filter)); - int32x4_t tmp_i_1 = vmull_s16(vget_high_s16(imag), - vget_high_s16(ns_filter)); - - int16x4_t result_r_0 = vshrn_n_s32(tmp_r_0, 14); - int16x4_t result_i_0 = vshrn_n_s32(tmp_i_0, 14); - int16x4_t result_r_1 = vshrn_n_s32(tmp_r_1, 14); - int16x4_t result_i_1 = vshrn_n_s32(tmp_i_1, 14); - - vst1q_s16(preal, vcombine_s16(result_r_0, result_r_1)); - vst1q_s16(pimag, vcombine_s16(result_i_0, result_i_1)); - preal += 8; - pimag += 8; - pns_filter += 8; - } - - // Filter the last element - *preal = (int16_t)((*preal * *pns_filter) >> 14); - *pimag = (int16_t)((*pimag * *pns_filter) >> 14); - - // (2) Create spectrum. - - // Fixed point C code for the rest of the function is as follows: - // freq_buf[0] = inst->real[0]; - // freq_buf[1] = -inst->imag[0]; - // for (i = 1, j = 2; i < inst->anaLen2; i += 1, j += 2) { - // freq_buf[j] = inst->real[i]; - // freq_buf[j + 1] = -inst->imag[i]; - // } - // freq_buf[inst->anaLen] = inst->real[inst->anaLen2]; - // freq_buf[inst->anaLen + 1] = -inst->imag[inst->anaLen2]; - - preal = &inst->real[0]; - pimag = &inst->imag[0]; - pimag_end = pimag + inst->anaLen2; - int16_t * freq_buf_start = freq_buf; - while (pimag < pimag_end) { - // loop unroll - int16x8x2_t real_imag_0; - int16x8x2_t real_imag_1; - real_imag_0.val[1] = vld1q_s16(pimag); - real_imag_0.val[0] = vld1q_s16(preal); - preal += 8; - pimag += 8; - real_imag_1.val[1] = vld1q_s16(pimag); - real_imag_1.val[0] = vld1q_s16(preal); - preal += 8; - pimag += 8; - - real_imag_0.val[1] = vnegq_s16(real_imag_0.val[1]); - real_imag_1.val[1] = vnegq_s16(real_imag_1.val[1]); - vst2q_s16(freq_buf_start, real_imag_0); - freq_buf_start += 16; - vst2q_s16(freq_buf_start, real_imag_1); - freq_buf_start += 16; - } - freq_buf[inst->anaLen] = inst->real[inst->anaLen2]; - freq_buf[inst->anaLen + 1] = -inst->imag[inst->anaLen2]; -} - -// For the noise supress process, synthesis, read out fully processed segment, -// and update synthesis buffer. -void WebRtcNsx_SynthesisUpdateNeon(NoiseSuppressionFixedC* inst, - int16_t* out_frame, - int16_t gain_factor) { - assert(inst->anaLen % 16 == 0); - assert(inst->blockLen10ms % 16 == 0); - - int16_t* preal_start = inst->real; - const int16_t* pwindow = inst->window; - int16_t* preal_end = preal_start + inst->anaLen; - int16_t* psynthesis_buffer = inst->synthesisBuffer; - - while (preal_start < preal_end) { - // Loop unroll. - int16x8_t window_0 = vld1q_s16(pwindow); - int16x8_t real_0 = vld1q_s16(preal_start); - int16x8_t synthesis_buffer_0 = vld1q_s16(psynthesis_buffer); - - int16x8_t window_1 = vld1q_s16(pwindow + 8); - int16x8_t real_1 = vld1q_s16(preal_start + 8); - int16x8_t synthesis_buffer_1 = vld1q_s16(psynthesis_buffer + 8); - - int32x4_t tmp32a_0_low = vmull_s16(vget_low_s16(real_0), - vget_low_s16(window_0)); - int32x4_t tmp32a_0_high = vmull_s16(vget_high_s16(real_0), - vget_high_s16(window_0)); - - int32x4_t tmp32a_1_low = vmull_s16(vget_low_s16(real_1), - vget_low_s16(window_1)); - int32x4_t tmp32a_1_high = vmull_s16(vget_high_s16(real_1), - vget_high_s16(window_1)); - - int16x4_t tmp16a_0_low = vqrshrn_n_s32(tmp32a_0_low, 14); - int16x4_t tmp16a_0_high = vqrshrn_n_s32(tmp32a_0_high, 14); - - int16x4_t tmp16a_1_low = vqrshrn_n_s32(tmp32a_1_low, 14); - int16x4_t tmp16a_1_high = vqrshrn_n_s32(tmp32a_1_high, 14); - - int32x4_t tmp32b_0_low = vmull_n_s16(tmp16a_0_low, gain_factor); - int32x4_t tmp32b_0_high = vmull_n_s16(tmp16a_0_high, gain_factor); - - int32x4_t tmp32b_1_low = vmull_n_s16(tmp16a_1_low, gain_factor); - int32x4_t tmp32b_1_high = vmull_n_s16(tmp16a_1_high, gain_factor); - - int16x4_t tmp16b_0_low = vqrshrn_n_s32(tmp32b_0_low, 13); - int16x4_t tmp16b_0_high = vqrshrn_n_s32(tmp32b_0_high, 13); - - int16x4_t tmp16b_1_low = vqrshrn_n_s32(tmp32b_1_low, 13); - int16x4_t tmp16b_1_high = vqrshrn_n_s32(tmp32b_1_high, 13); - - synthesis_buffer_0 = vqaddq_s16(vcombine_s16(tmp16b_0_low, tmp16b_0_high), - synthesis_buffer_0); - synthesis_buffer_1 = vqaddq_s16(vcombine_s16(tmp16b_1_low, tmp16b_1_high), - synthesis_buffer_1); - vst1q_s16(psynthesis_buffer, synthesis_buffer_0); - vst1q_s16(psynthesis_buffer + 8, synthesis_buffer_1); - - pwindow += 16; - preal_start += 16; - psynthesis_buffer += 16; - } - - // Read out fully processed segment. - int16_t * p_start = inst->synthesisBuffer; - int16_t * p_end = inst->synthesisBuffer + inst->blockLen10ms; - int16_t * p_frame = out_frame; - while (p_start < p_end) { - int16x8_t frame_0 = vld1q_s16(p_start); - vst1q_s16(p_frame, frame_0); - p_start += 8; - p_frame += 8; - } - - // Update synthesis buffer. - int16_t* p_start_src = inst->synthesisBuffer + inst->blockLen10ms; - int16_t* p_end_src = inst->synthesisBuffer + inst->anaLen; - int16_t* p_start_dst = inst->synthesisBuffer; - while (p_start_src < p_end_src) { - int16x8_t frame = vld1q_s16(p_start_src); - vst1q_s16(p_start_dst, frame); - p_start_src += 8; - p_start_dst += 8; - } - - p_start = inst->synthesisBuffer + inst->anaLen - inst->blockLen10ms; - p_end = p_start + inst->blockLen10ms; - int16x8_t zero = vdupq_n_s16(0); - for (;p_start < p_end; p_start += 8) { - vst1q_s16(p_start, zero); - } -} - -// Update analysis buffer for lower band, and window data before FFT. -void WebRtcNsx_AnalysisUpdateNeon(NoiseSuppressionFixedC* inst, - int16_t* out, - int16_t* new_speech) { - assert(inst->blockLen10ms % 16 == 0); - assert(inst->anaLen % 16 == 0); - - // For lower band update analysis buffer. - // memcpy(inst->analysisBuffer, inst->analysisBuffer + inst->blockLen10ms, - // (inst->anaLen - inst->blockLen10ms) * sizeof(*inst->analysisBuffer)); - int16_t* p_start_src = inst->analysisBuffer + inst->blockLen10ms; - int16_t* p_end_src = inst->analysisBuffer + inst->anaLen; - int16_t* p_start_dst = inst->analysisBuffer; - while (p_start_src < p_end_src) { - int16x8_t frame = vld1q_s16(p_start_src); - vst1q_s16(p_start_dst, frame); - - p_start_src += 8; - p_start_dst += 8; - } - - // memcpy(inst->analysisBuffer + inst->anaLen - inst->blockLen10ms, - // new_speech, inst->blockLen10ms * sizeof(*inst->analysisBuffer)); - p_start_src = new_speech; - p_end_src = new_speech + inst->blockLen10ms; - p_start_dst = inst->analysisBuffer + inst->anaLen - inst->blockLen10ms; - while (p_start_src < p_end_src) { - int16x8_t frame = vld1q_s16(p_start_src); - vst1q_s16(p_start_dst, frame); - - p_start_src += 8; - p_start_dst += 8; - } - - // Window data before FFT. - int16_t* p_start_window = (int16_t*) inst->window; - int16_t* p_start_buffer = inst->analysisBuffer; - int16_t* p_start_out = out; - const int16_t* p_end_out = out + inst->anaLen; - - // Load the first element to reduce pipeline bubble. - int16x8_t window = vld1q_s16(p_start_window); - int16x8_t buffer = vld1q_s16(p_start_buffer); - p_start_window += 8; - p_start_buffer += 8; - - while (p_start_out < p_end_out) { - // Unroll loop. - int32x4_t tmp32_low = vmull_s16(vget_low_s16(window), vget_low_s16(buffer)); - int32x4_t tmp32_high = vmull_s16(vget_high_s16(window), - vget_high_s16(buffer)); - window = vld1q_s16(p_start_window); - buffer = vld1q_s16(p_start_buffer); - - int16x4_t result_low = vrshrn_n_s32(tmp32_low, 14); - int16x4_t result_high = vrshrn_n_s32(tmp32_high, 14); - vst1q_s16(p_start_out, vcombine_s16(result_low, result_high)); - - p_start_buffer += 8; - p_start_window += 8; - p_start_out += 8; - } -} diff --git a/webrtc/modules/audio_processing/ns/nsx_defines.h b/webrtc/modules/audio_processing/ns/nsx_defines.h deleted file mode 100644 index 862dc3c..0000000 --- a/webrtc/modules/audio_processing/ns/nsx_defines.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_DEFINES_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_DEFINES_H_ - -#define ANAL_BLOCKL_MAX 256 /* Max analysis block length */ -#define HALF_ANAL_BLOCKL 129 /* Half max analysis block length + 1 */ -#define NUM_HIGH_BANDS_MAX 2 /* Max number of high bands */ -#define SIMULT 3 -#define END_STARTUP_LONG 200 -#define END_STARTUP_SHORT 50 -#define FACTOR_Q16 2621440 /* 40 in Q16 */ -#define FACTOR_Q7 5120 /* 40 in Q7 */ -#define FACTOR_Q7_STARTUP 1024 /* 8 in Q7 */ -#define WIDTH_Q8 3 /* 0.01 in Q8 (or 25 ) */ - -/* PARAMETERS FOR NEW METHOD */ -#define DD_PR_SNR_Q11 2007 /* ~= Q11(0.98) DD update of prior SNR */ -#define ONE_MINUS_DD_PR_SNR_Q11 41 /* DD update of prior SNR */ -#define SPECT_FLAT_TAVG_Q14 4915 /* (0.30) tavg parameter for spectral flatness measure */ -#define SPECT_DIFF_TAVG_Q8 77 /* (0.30) tavg parameter for spectral flatness measure */ -#define PRIOR_UPDATE_Q14 1638 /* Q14(0.1) Update parameter of prior model */ -#define NOISE_UPDATE_Q8 26 /* 26 ~= Q8(0.1) Update parameter for noise */ - -/* Probability threshold for noise state in speech/noise likelihood. */ -#define ONE_MINUS_PROB_RANGE_Q8 205 /* 205 ~= Q8(0.8) */ -#define HIST_PAR_EST 1000 /* Histogram size for estimation of parameters */ - -/* FEATURE EXTRACTION CONFIG */ -/* Bin size of histogram */ -#define BIN_SIZE_LRT 10 -/* Scale parameters: multiply dominant peaks of the histograms by scale factor to obtain. */ -/* Thresholds for prior model */ -#define FACTOR_1_LRT_DIFF 6 /* For LRT and spectral difference (5 times bigger) */ -/* For spectral_flatness: used when noise is flatter than speech (10 times bigger). */ -#define FACTOR_2_FLAT_Q10 922 -/* Peak limit for spectral flatness (varies between 0 and 1) */ -#define THRES_PEAK_FLAT 24 /* * 2 * BIN_SIZE_FLAT_FX */ -/* Limit on spacing of two highest peaks in histogram: spacing determined by bin size. */ -#define LIM_PEAK_SPACE_FLAT_DIFF 4 /* * 2 * BIN_SIZE_DIFF_FX */ -/* Limit on relevance of second peak */ -#define LIM_PEAK_WEIGHT_FLAT_DIFF 2 -#define THRES_FLUCT_LRT 10240 /* = 20 * inst->modelUpdate; fluctuation limit of LRT feat. */ -/* Limit on the max and min values for the feature thresholds */ -#define MAX_FLAT_Q10 38912 /* * 2 * BIN_SIZE_FLAT_FX */ -#define MIN_FLAT_Q10 4096 /* * 2 * BIN_SIZE_FLAT_FX */ -#define MAX_DIFF 100 /* * 2 * BIN_SIZE_DIFF_FX */ -#define MIN_DIFF 16 /* * 2 * BIN_SIZE_DIFF_FX */ -/* Criteria of weight of histogram peak to accept/reject feature */ -#define THRES_WEIGHT_FLAT_DIFF 154 /*(int)(0.3*(inst->modelUpdate)) for flatness and difference */ - -#define STAT_UPDATES 9 /* Update every 512 = 1 << 9 block */ -#define ONE_MINUS_GAMMA_PAUSE_Q8 13 /* ~= Q8(0.05) Update for conservative noise estimate */ -#define GAMMA_NOISE_TRANS_AND_SPEECH_Q8 3 /* ~= Q8(0.01) Update for transition and noise region */ - -#endif /* WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_DEFINES_H_ */ diff --git a/webrtc/modules/audio_processing/ns/prior_signal_model.cc b/webrtc/modules/audio_processing/ns/prior_signal_model.cc new file mode 100644 index 0000000..f25a1e2 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/prior_signal_model.cc @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/prior_signal_model.h" + +namespace webrtc { + +PriorSignalModel::PriorSignalModel(float lrt_initial_value) + : lrt(lrt_initial_value) {} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/prior_signal_model.h b/webrtc/modules/audio_processing/ns/prior_signal_model.h new file mode 100644 index 0000000..dcfa7ea --- /dev/null +++ b/webrtc/modules/audio_processing/ns/prior_signal_model.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_H_ +#define MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_H_ + +namespace webrtc { + +// Struct for storing the prior signal model parameters. +struct PriorSignalModel { + explicit PriorSignalModel(float lrt_initial_value); + PriorSignalModel(const PriorSignalModel&) = delete; + PriorSignalModel& operator=(const PriorSignalModel&) = delete; + + float lrt; + float flatness_threshold = .5f; + float template_diff_threshold = .5f; + float lrt_weighting = 1.f; + float flatness_weighting = 0.f; + float difference_weighting = 0.f; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_H_ diff --git a/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.cc b/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.cc new file mode 100644 index 0000000..c814658 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.cc @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/prior_signal_model_estimator.h" + +#include +#include + +#include "modules/audio_processing/ns/fast_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +// Identifies the first of the two largest peaks in the histogram. +void FindFirstOfTwoLargestPeaks( + float bin_size, + rtc::ArrayView spectral_flatness, + float* peak_position, + int* peak_weight) { + RTC_DCHECK(peak_position); + RTC_DCHECK(peak_weight); + + int peak_value = 0; + int secondary_peak_value = 0; + *peak_position = 0.f; + float secondary_peak_position = 0.f; + *peak_weight = 0; + int secondary_peak_weight = 0; + + // Identify the two largest peaks. + for (int i = 0; i < kHistogramSize; ++i) { + const float bin_mid = (i + 0.5f) * bin_size; + if (spectral_flatness[i] > peak_value) { + // Found new "first" peak candidate. + secondary_peak_value = peak_value; + secondary_peak_weight = *peak_weight; + secondary_peak_position = *peak_position; + + peak_value = spectral_flatness[i]; + *peak_weight = spectral_flatness[i]; + *peak_position = bin_mid; + } else if (spectral_flatness[i] > secondary_peak_value) { + // Found new "second" peak candidate. + secondary_peak_value = spectral_flatness[i]; + secondary_peak_weight = spectral_flatness[i]; + secondary_peak_position = bin_mid; + } + } + + // Merge the peaks if they are close. + if ((fabs(secondary_peak_position - *peak_position) < 2 * bin_size) && + (secondary_peak_weight > 0.5f * (*peak_weight))) { + *peak_weight += secondary_peak_weight; + *peak_position = 0.5f * (*peak_position + secondary_peak_position); + } +} + +void UpdateLrt(rtc::ArrayView lrt_histogram, + float* prior_model_lrt, + bool* low_lrt_fluctuations) { + RTC_DCHECK(prior_model_lrt); + RTC_DCHECK(low_lrt_fluctuations); + + float average = 0.f; + float average_compl = 0.f; + float average_squared = 0.f; + int count = 0; + + for (int i = 0; i < 10; ++i) { + float bin_mid = (i + 0.5f) * kBinSizeLrt; + average += lrt_histogram[i] * bin_mid; + count += lrt_histogram[i]; + } + if (count > 0) { + average = average / count; + } + + for (int i = 0; i < kHistogramSize; ++i) { + float bin_mid = (i + 0.5f) * kBinSizeLrt; + average_squared += lrt_histogram[i] * bin_mid * bin_mid; + average_compl += lrt_histogram[i] * bin_mid; + } + constexpr float kOneFeatureUpdateWindowSize = 1.f / kFeatureUpdateWindowSize; + average_squared = average_squared * kOneFeatureUpdateWindowSize; + average_compl = average_compl * kOneFeatureUpdateWindowSize; + + // Fluctuation limit of LRT feature. + *low_lrt_fluctuations = average_squared - average * average_compl < 0.05f; + + // Get threshold for LRT feature. + constexpr float kMaxLrt = 1.f; + constexpr float kMinLrt = .2f; + if (*low_lrt_fluctuations) { + // Very low fluctuation, so likely noise. + *prior_model_lrt = kMaxLrt; + } else { + *prior_model_lrt = std::min(kMaxLrt, std::max(kMinLrt, 1.2f * average)); + } +} + +} // namespace + +PriorSignalModelEstimator::PriorSignalModelEstimator(float lrt_initial_value) + : prior_model_(lrt_initial_value) {} + +// Extract thresholds for feature parameters and computes the threshold/weights. +void PriorSignalModelEstimator::Update(const Histograms& histograms) { + bool low_lrt_fluctuations; + UpdateLrt(histograms.get_lrt(), &prior_model_.lrt, &low_lrt_fluctuations); + + // For spectral flatness and spectral difference: compute the main peaks of + // the histograms. + float spectral_flatness_peak_position; + int spectral_flatness_peak_weight; + FindFirstOfTwoLargestPeaks( + kBinSizeSpecFlat, histograms.get_spectral_flatness(), + &spectral_flatness_peak_position, &spectral_flatness_peak_weight); + + float spectral_diff_peak_position = 0.f; + int spectral_diff_peak_weight = 0; + FindFirstOfTwoLargestPeaks(kBinSizeSpecDiff, histograms.get_spectral_diff(), + &spectral_diff_peak_position, + &spectral_diff_peak_weight); + + // Reject if weight of peaks is not large enough, or peak value too small. + // Peak limit for spectral flatness (varies between 0 and 1). + const int use_spec_flat = spectral_flatness_peak_weight < 0.3f * 500 || + spectral_flatness_peak_position < 0.6f + ? 0 + : 1; + + // Reject if weight of peaks is not large enough or if fluctuation of the LRT + // feature are very low, indicating a noise state. + const int use_spec_diff = + spectral_diff_peak_weight < 0.3f * 500 || low_lrt_fluctuations ? 0 : 1; + + // Update the model. + prior_model_.template_diff_threshold = 1.2f * spectral_diff_peak_position; + prior_model_.template_diff_threshold = + std::min(1.f, std::max(0.16f, prior_model_.template_diff_threshold)); + + float one_by_feature_sum = 1.f / (1.f + use_spec_flat + use_spec_diff); + prior_model_.lrt_weighting = one_by_feature_sum; + + if (use_spec_flat == 1) { + prior_model_.flatness_threshold = 0.9f * spectral_flatness_peak_position; + prior_model_.flatness_threshold = + std::min(.95f, std::max(0.1f, prior_model_.flatness_threshold)); + prior_model_.flatness_weighting = one_by_feature_sum; + } else { + prior_model_.flatness_weighting = 0.f; + } + + if (use_spec_diff == 1) { + prior_model_.difference_weighting = one_by_feature_sum; + } else { + prior_model_.difference_weighting = 0.f; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.h b/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.h new file mode 100644 index 0000000..d178323 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/prior_signal_model_estimator.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_ESTIMATOR_H_ + +#include "modules/audio_processing/ns/histograms.h" +#include "modules/audio_processing/ns/prior_signal_model.h" + +namespace webrtc { + +// Estimator of the prior signal model parameters. +class PriorSignalModelEstimator { + public: + explicit PriorSignalModelEstimator(float lrt_initial_value); + PriorSignalModelEstimator(const PriorSignalModelEstimator&) = delete; + PriorSignalModelEstimator& operator=(const PriorSignalModelEstimator&) = + delete; + + // Updates the model estimate. + void Update(const Histograms& h); + + // Returns the estimated model. + const PriorSignalModel& get_prior_model() const { return prior_model_; } + + private: + PriorSignalModel prior_model_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_PRIOR_SIGNAL_MODEL_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/ns/quantile_noise_estimator.cc b/webrtc/modules/audio_processing/ns/quantile_noise_estimator.cc new file mode 100644 index 0000000..bab494f --- /dev/null +++ b/webrtc/modules/audio_processing/ns/quantile_noise_estimator.cc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/quantile_noise_estimator.h" + +#include + +#include "modules/audio_processing/ns/fast_math.h" + +namespace webrtc { + +QuantileNoiseEstimator::QuantileNoiseEstimator() { + quantile_.fill(0.f); + density_.fill(0.3f); + log_quantile_.fill(8.f); + + constexpr float kOneBySimult = 1.f / kSimult; + for (size_t i = 0; i < kSimult; ++i) { + counter_[i] = floor(kLongStartupPhaseBlocks * (i + 1.f) * kOneBySimult); + } +} + +void QuantileNoiseEstimator::Estimate( + rtc::ArrayView signal_spectrum, + rtc::ArrayView noise_spectrum) { + std::array log_spectrum; + LogApproximation(signal_spectrum, log_spectrum); + + int quantile_index_to_return = -1; + // Loop over simultaneous estimates. + for (int s = 0, k = 0; s < kSimult; + ++s, k += static_cast(kFftSizeBy2Plus1)) { + const float one_by_counter_plus_1 = 1.f / (counter_[s] + 1.f); + for (int i = 0, j = k; i < static_cast(kFftSizeBy2Plus1); ++i, ++j) { + // Update log quantile estimate. + const float delta = density_[j] > 1.f ? 40.f / density_[j] : 40.f; + + const float multiplier = delta * one_by_counter_plus_1; + if (log_spectrum[i] > log_quantile_[j]) { + log_quantile_[j] += 0.25f * multiplier; + } else { + log_quantile_[j] -= 0.75f * multiplier; + } + + // Update density estimate. + constexpr float kWidth = 0.01f; + constexpr float kOneByWidthPlus2 = 1.f / (2.f * kWidth); + if (fabs(log_spectrum[i] - log_quantile_[j]) < kWidth) { + density_[j] = (counter_[s] * density_[j] + kOneByWidthPlus2) * + one_by_counter_plus_1; + } + } + + if (counter_[s] >= kLongStartupPhaseBlocks) { + counter_[s] = 0; + if (num_updates_ >= kLongStartupPhaseBlocks) { + quantile_index_to_return = k; + } + } + + ++counter_[s]; + } + + // Sequentially update the noise during startup. + if (num_updates_ < kLongStartupPhaseBlocks) { + // Use the last "s" to get noise during startup that differ from zero. + quantile_index_to_return = kFftSizeBy2Plus1 * (kSimult - 1); + ++num_updates_; + } + + if (quantile_index_to_return >= 0) { + ExpApproximation( + rtc::ArrayView(&log_quantile_[quantile_index_to_return], + kFftSizeBy2Plus1), + quantile_); + } + + std::copy(quantile_.begin(), quantile_.end(), noise_spectrum.begin()); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/quantile_noise_estimator.h b/webrtc/modules/audio_processing/ns/quantile_noise_estimator.h new file mode 100644 index 0000000..67d1512 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/quantile_noise_estimator.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_QUANTILE_NOISE_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_QUANTILE_NOISE_ESTIMATOR_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" + +namespace webrtc { + +constexpr int kSimult = 3; + +// For quantile noise estimation. +class QuantileNoiseEstimator { + public: + QuantileNoiseEstimator(); + QuantileNoiseEstimator(const QuantileNoiseEstimator&) = delete; + QuantileNoiseEstimator& operator=(const QuantileNoiseEstimator&) = delete; + + // Estimate noise. + void Estimate(rtc::ArrayView signal_spectrum, + rtc::ArrayView noise_spectrum); + + private: + std::array density_; + std::array log_quantile_; + std::array quantile_; + std::array counter_; + int num_updates_ = 1; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_QUANTILE_NOISE_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/ns/signal_model.cc b/webrtc/modules/audio_processing/ns/signal_model.cc new file mode 100644 index 0000000..364bfd0 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/signal_model.cc @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/signal_model.h" + +namespace webrtc { + +SignalModel::SignalModel() { + constexpr float kSfFeatureThr = 0.5f; + + lrt = kLtrFeatureThr; + spectral_flatness = kSfFeatureThr; + spectral_diff = kSfFeatureThr; + avg_log_lrt.fill(kLtrFeatureThr); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/signal_model.h b/webrtc/modules/audio_processing/ns/signal_model.h new file mode 100644 index 0000000..6614d38 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/signal_model.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_H_ +#define MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_H_ + +#include + +#include "modules/audio_processing/ns/ns_common.h" + +namespace webrtc { + +struct SignalModel { + SignalModel(); + SignalModel(const SignalModel&) = delete; + SignalModel& operator=(const SignalModel&) = delete; + + float lrt; + float spectral_diff; + float spectral_flatness; + // Log LRT factor with time-smoothing. + std::array avg_log_lrt; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_H_ diff --git a/webrtc/modules/audio_processing/ns/signal_model_estimator.cc b/webrtc/modules/audio_processing/ns/signal_model_estimator.cc new file mode 100644 index 0000000..67dd3bb --- /dev/null +++ b/webrtc/modules/audio_processing/ns/signal_model_estimator.cc @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/signal_model_estimator.h" + +#include "modules/audio_processing/ns/fast_math.h" + +namespace webrtc { + +namespace { + +constexpr float kOneByFftSizeBy2Plus1 = 1.f / kFftSizeBy2Plus1; + +// Computes the difference measure between input spectrum and a template/learned +// noise spectrum. +float ComputeSpectralDiff( + rtc::ArrayView conservative_noise_spectrum, + rtc::ArrayView signal_spectrum, + float signal_spectral_sum, + float diff_normalization) { + // spectral_diff = var(signal_spectrum) - cov(signal_spectrum, magnAvgPause)^2 + // / var(magnAvgPause) + + // Compute average quantities. + float noise_average = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + // Conservative smooth noise spectrum from pause frames. + noise_average += conservative_noise_spectrum[i]; + } + noise_average = noise_average * kOneByFftSizeBy2Plus1; + float signal_average = signal_spectral_sum * kOneByFftSizeBy2Plus1; + + // Compute variance and covariance quantities. + float covariance = 0.f; + float noise_variance = 0.f; + float signal_variance = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + float signal_diff = signal_spectrum[i] - signal_average; + float noise_diff = conservative_noise_spectrum[i] - noise_average; + covariance += signal_diff * noise_diff; + noise_variance += noise_diff * noise_diff; + signal_variance += signal_diff * signal_diff; + } + covariance *= kOneByFftSizeBy2Plus1; + noise_variance *= kOneByFftSizeBy2Plus1; + signal_variance *= kOneByFftSizeBy2Plus1; + + // Update of average magnitude spectrum. + float spectral_diff = + signal_variance - (covariance * covariance) / (noise_variance + 0.0001f); + // Normalize. + return spectral_diff / (diff_normalization + 0.0001f); +} + +// Updates the spectral flatness based on the input spectrum. +void UpdateSpectralFlatness( + rtc::ArrayView signal_spectrum, + float signal_spectral_sum, + float* spectral_flatness) { + RTC_DCHECK(spectral_flatness); + + // Compute log of ratio of the geometric to arithmetic mean (handle the log(0) + // separately). + constexpr float kAveraging = 0.3f; + float avg_spect_flatness_num = 0.f; + for (size_t i = 1; i < kFftSizeBy2Plus1; ++i) { + if (signal_spectrum[i] == 0.f) { + *spectral_flatness -= kAveraging * (*spectral_flatness); + return; + } + } + + for (size_t i = 1; i < kFftSizeBy2Plus1; ++i) { + avg_spect_flatness_num += LogApproximation(signal_spectrum[i]); + } + + float avg_spect_flatness_denom = signal_spectral_sum - signal_spectrum[0]; + + avg_spect_flatness_denom = avg_spect_flatness_denom * kOneByFftSizeBy2Plus1; + avg_spect_flatness_num = avg_spect_flatness_num * kOneByFftSizeBy2Plus1; + + float spectral_tmp = + ExpApproximation(avg_spect_flatness_num) / avg_spect_flatness_denom; + + // Time-avg update of spectral flatness feature. + *spectral_flatness += kAveraging * (spectral_tmp - *spectral_flatness); +} + +// Updates the log LRT measures. +void UpdateSpectralLrt(rtc::ArrayView prior_snr, + rtc::ArrayView post_snr, + rtc::ArrayView avg_log_lrt, + float* lrt) { + RTC_DCHECK(lrt); + + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + float tmp1 = 1.f + 2.f * prior_snr[i]; + float tmp2 = 2.f * prior_snr[i] / (tmp1 + 0.0001f); + float bessel_tmp = (post_snr[i] + 1.f) * tmp2; + avg_log_lrt[i] += + .5f * (bessel_tmp - LogApproximation(tmp1) - avg_log_lrt[i]); + } + + float log_lrt_time_avg_k_sum = 0.f; + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + log_lrt_time_avg_k_sum += avg_log_lrt[i]; + } + *lrt = log_lrt_time_avg_k_sum * kOneByFftSizeBy2Plus1; +} + +} // namespace + +SignalModelEstimator::SignalModelEstimator() + : prior_model_estimator_(kLtrFeatureThr) {} + +void SignalModelEstimator::AdjustNormalization(int32_t num_analyzed_frames, + float signal_energy) { + diff_normalization_ *= num_analyzed_frames; + diff_normalization_ += signal_energy; + diff_normalization_ /= (num_analyzed_frames + 1); +} + +// Update the noise features. +void SignalModelEstimator::Update( + rtc::ArrayView prior_snr, + rtc::ArrayView post_snr, + rtc::ArrayView conservative_noise_spectrum, + rtc::ArrayView signal_spectrum, + float signal_spectral_sum, + float signal_energy) { + // Compute spectral flatness on input spectrum. + UpdateSpectralFlatness(signal_spectrum, signal_spectral_sum, + &features_.spectral_flatness); + + // Compute difference of input spectrum with learned/estimated noise spectrum. + float spectral_diff = + ComputeSpectralDiff(conservative_noise_spectrum, signal_spectrum, + signal_spectral_sum, diff_normalization_); + // Compute time-avg update of difference feature. + features_.spectral_diff += 0.3f * (spectral_diff - features_.spectral_diff); + + signal_energy_sum_ += signal_energy; + + // Compute histograms for parameter decisions (thresholds and weights for + // features). Parameters are extracted periodically. + if (--histogram_analysis_counter_ > 0) { + histograms_.Update(features_); + } else { + // Compute model parameters. + prior_model_estimator_.Update(histograms_); + + // Clear histograms for next update. + histograms_.Clear(); + + histogram_analysis_counter_ = kFeatureUpdateWindowSize; + + // Update every window: + // Compute normalization for the spectral difference for next estimation. + signal_energy_sum_ = signal_energy_sum_ / kFeatureUpdateWindowSize; + diff_normalization_ = 0.5f * (signal_energy_sum_ + diff_normalization_); + signal_energy_sum_ = 0.f; + } + + // Compute the LRT. + UpdateSpectralLrt(prior_snr, post_snr, features_.avg_log_lrt, &features_.lrt); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/signal_model_estimator.h b/webrtc/modules/audio_processing/ns/signal_model_estimator.h new file mode 100644 index 0000000..58ce00a --- /dev/null +++ b/webrtc/modules/audio_processing/ns/signal_model_estimator.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_ESTIMATOR_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/histograms.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/prior_signal_model.h" +#include "modules/audio_processing/ns/prior_signal_model_estimator.h" +#include "modules/audio_processing/ns/signal_model.h" + +namespace webrtc { + +class SignalModelEstimator { + public: + SignalModelEstimator(); + SignalModelEstimator(const SignalModelEstimator&) = delete; + SignalModelEstimator& operator=(const SignalModelEstimator&) = delete; + + // Compute signal normalization during the initial startup phase. + void AdjustNormalization(int32_t num_analyzed_frames, float signal_energy); + + void Update( + rtc::ArrayView prior_snr, + rtc::ArrayView post_snr, + rtc::ArrayView conservative_noise_spectrum, + rtc::ArrayView signal_spectrum, + float signal_spectral_sum, + float signal_energy); + + const PriorSignalModel& get_prior_model() const { + return prior_model_estimator_.get_prior_model(); + } + const SignalModel& get_model() { return features_; } + + private: + float diff_normalization_ = 0.f; + float signal_energy_sum_ = 0.f; + Histograms histograms_; + int histogram_analysis_counter_ = 500; + PriorSignalModelEstimator prior_model_estimator_; + SignalModel features_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/ns/speech_probability_estimator.cc b/webrtc/modules/audio_processing/ns/speech_probability_estimator.cc new file mode 100644 index 0000000..fce9bc8 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/speech_probability_estimator.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/speech_probability_estimator.h" + +#include +#include + +#include "modules/audio_processing/ns/fast_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +SpeechProbabilityEstimator::SpeechProbabilityEstimator() { + speech_probability_.fill(0.f); +} + +void SpeechProbabilityEstimator::Update( + int32_t num_analyzed_frames, + rtc::ArrayView prior_snr, + rtc::ArrayView post_snr, + rtc::ArrayView conservative_noise_spectrum, + rtc::ArrayView signal_spectrum, + float signal_spectral_sum, + float signal_energy) { + // Update models. + if (num_analyzed_frames < kLongStartupPhaseBlocks) { + signal_model_estimator_.AdjustNormalization(num_analyzed_frames, + signal_energy); + } + signal_model_estimator_.Update(prior_snr, post_snr, + conservative_noise_spectrum, signal_spectrum, + signal_spectral_sum, signal_energy); + + const SignalModel& model = signal_model_estimator_.get_model(); + const PriorSignalModel& prior_model = + signal_model_estimator_.get_prior_model(); + + // Width parameter in sigmoid map for prior model. + constexpr float kWidthPrior0 = 4.f; + // Width for pause region: lower range, so increase width in tanh map. + constexpr float kWidthPrior1 = 2.f * kWidthPrior0; + + // Average LRT feature: use larger width in tanh map for pause regions. + float width_prior = model.lrt < prior_model.lrt ? kWidthPrior1 : kWidthPrior0; + + // Compute indicator function: sigmoid map. + float indicator0 = + 0.5f * (tanh(width_prior * (model.lrt - prior_model.lrt)) + 1.f); + + // Spectral flatness feature: use larger width in tanh map for pause regions. + width_prior = model.spectral_flatness > prior_model.flatness_threshold + ? kWidthPrior1 + : kWidthPrior0; + + // Compute indicator function: sigmoid map. + float indicator1 = + 0.5f * (tanh(1.f * width_prior * + (prior_model.flatness_threshold - model.spectral_flatness)) + + 1.f); + + // For template spectrum-difference : use larger width in tanh map for pause + // regions. + width_prior = model.spectral_diff < prior_model.template_diff_threshold + ? kWidthPrior1 + : kWidthPrior0; + + // Compute indicator function: sigmoid map. + float indicator2 = + 0.5f * (tanh(width_prior * (model.spectral_diff - + prior_model.template_diff_threshold)) + + 1.f); + + // Combine the indicator function with the feature weights. + float ind_prior = prior_model.lrt_weighting * indicator0 + + prior_model.flatness_weighting * indicator1 + + prior_model.difference_weighting * indicator2; + + // Compute the prior probability. + prior_speech_prob_ += 0.1f * (ind_prior - prior_speech_prob_); + + // Make sure probabilities are within range: keep floor to 0.01. + prior_speech_prob_ = std::max(std::min(prior_speech_prob_, 1.f), 0.01f); + + // Final speech probability: combine prior model with LR factor:. + float gain_prior = + (1.f - prior_speech_prob_) / (prior_speech_prob_ + 0.0001f); + + std::array inv_lrt; + ExpApproximationSignFlip(model.avg_log_lrt, inv_lrt); + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + speech_probability_[i] = 1.f / (1.f + gain_prior * inv_lrt[i]); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/speech_probability_estimator.h b/webrtc/modules/audio_processing/ns/speech_probability_estimator.h new file mode 100644 index 0000000..259c3b6 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/speech_probability_estimator.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_SPEECH_PROBABILITY_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_NS_SPEECH_PROBABILITY_ESTIMATOR_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/signal_model_estimator.h" + +namespace webrtc { + +// Class for estimating the probability of speech. +class SpeechProbabilityEstimator { + public: + SpeechProbabilityEstimator(); + SpeechProbabilityEstimator(const SpeechProbabilityEstimator&) = delete; + SpeechProbabilityEstimator& operator=(const SpeechProbabilityEstimator&) = + delete; + + // Compute speech probability. + void Update( + int32_t num_analyzed_frames, + rtc::ArrayView prior_snr, + rtc::ArrayView post_snr, + rtc::ArrayView conservative_noise_spectrum, + rtc::ArrayView signal_spectrum, + float signal_spectral_sum, + float signal_energy); + + float get_prior_probability() const { return prior_speech_prob_; } + rtc::ArrayView get_probability() { return speech_probability_; } + + private: + SignalModelEstimator signal_model_estimator_; + float prior_speech_prob_ = .5f; + std::array speech_probability_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_SPEECH_PROBABILITY_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/ns/suppression_params.cc b/webrtc/modules/audio_processing/ns/suppression_params.cc new file mode 100644 index 0000000..9a6bd5a --- /dev/null +++ b/webrtc/modules/audio_processing/ns/suppression_params.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/suppression_params.h" + +#include "rtc_base/checks.h" + +namespace webrtc { + +SuppressionParams::SuppressionParams( + NsConfig::SuppressionLevel suppression_level) { + switch (suppression_level) { + case NsConfig::SuppressionLevel::k6dB: + over_subtraction_factor = 1.f; + // 6 dB attenuation. + minimum_attenuating_gain = 0.5f; + use_attenuation_adjustment = false; + break; + case NsConfig::SuppressionLevel::k12dB: + over_subtraction_factor = 1.f; + // 12 dB attenuation. + minimum_attenuating_gain = 0.25f; + use_attenuation_adjustment = true; + break; + case NsConfig::SuppressionLevel::k18dB: + over_subtraction_factor = 1.1f; + // 18 dB attenuation. + minimum_attenuating_gain = 0.125f; + use_attenuation_adjustment = true; + break; + case NsConfig::SuppressionLevel::k21dB: + over_subtraction_factor = 1.25f; + // 20.9 dB attenuation. + minimum_attenuating_gain = 0.09f; + use_attenuation_adjustment = true; + break; + default: + RTC_NOTREACHED(); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/suppression_params.h b/webrtc/modules/audio_processing/ns/suppression_params.h new file mode 100644 index 0000000..ad11977 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/suppression_params.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_SUPPRESSION_PARAMS_H_ +#define MODULES_AUDIO_PROCESSING_NS_SUPPRESSION_PARAMS_H_ + +#include "modules/audio_processing/ns/ns_config.h" + +namespace webrtc { + +struct SuppressionParams { + explicit SuppressionParams(NsConfig::SuppressionLevel suppression_level); + SuppressionParams(const SuppressionParams&) = delete; + SuppressionParams& operator=(const SuppressionParams&) = delete; + + float over_subtraction_factor; + float minimum_attenuating_gain; + bool use_attenuation_adjustment; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_SUPPRESSION_PARAMS_H_ diff --git a/webrtc/modules/audio_processing/ns/wiener_filter.cc b/webrtc/modules/audio_processing/ns/wiener_filter.cc new file mode 100644 index 0000000..e14b797 --- /dev/null +++ b/webrtc/modules/audio_processing/ns/wiener_filter.cc @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/ns/wiener_filter.h" + +#include +#include +#include +#include + +#include "modules/audio_processing/ns/fast_math.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +WienerFilter::WienerFilter(const SuppressionParams& suppression_params) + : suppression_params_(suppression_params) { + filter_.fill(1.f); + initial_spectral_estimate_.fill(0.f); + spectrum_prev_process_.fill(0.f); +} + +void WienerFilter::Update( + int32_t num_analyzed_frames, + rtc::ArrayView noise_spectrum, + rtc::ArrayView prev_noise_spectrum, + rtc::ArrayView parametric_noise_spectrum, + rtc::ArrayView signal_spectrum) { + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + // Previous estimate based on previous frame with gain filter. + float prev_tsa = spectrum_prev_process_[i] / + (prev_noise_spectrum[i] + 0.0001f) * filter_[i]; + + // Current estimate. + float current_tsa; + if (signal_spectrum[i] > noise_spectrum[i]) { + current_tsa = signal_spectrum[i] / (noise_spectrum[i] + 0.0001f) - 1.f; + } else { + current_tsa = 0.f; + } + + // Directed decision estimate is sum of two terms: current estimate and + // previous estimate. + float snr_prior = 0.98f * prev_tsa + (1.f - 0.98f) * current_tsa; + filter_[i] = + snr_prior / (suppression_params_.over_subtraction_factor + snr_prior); + filter_[i] = std::max(std::min(filter_[i], 1.f), + suppression_params_.minimum_attenuating_gain); + } + + if (num_analyzed_frames < kShortStartupPhaseBlocks) { + for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { + initial_spectral_estimate_[i] += signal_spectrum[i]; + float filter_initial = initial_spectral_estimate_[i] - + suppression_params_.over_subtraction_factor * + parametric_noise_spectrum[i]; + filter_initial /= initial_spectral_estimate_[i] + 0.0001f; + + filter_initial = std::max(std::min(filter_initial, 1.f), + suppression_params_.minimum_attenuating_gain); + + // Weight the two suppression filters. + constexpr float kOnyByShortStartupPhaseBlocks = + 1.f / kShortStartupPhaseBlocks; + filter_initial *= kShortStartupPhaseBlocks - num_analyzed_frames; + filter_[i] *= num_analyzed_frames; + filter_[i] += filter_initial; + filter_[i] *= kOnyByShortStartupPhaseBlocks; + } + } + + std::copy(signal_spectrum.begin(), signal_spectrum.end(), + spectrum_prev_process_.begin()); +} + +float WienerFilter::ComputeOverallScalingFactor( + int32_t num_analyzed_frames, + float prior_speech_probability, + float energy_before_filtering, + float energy_after_filtering) const { + if (!suppression_params_.use_attenuation_adjustment || + num_analyzed_frames <= kLongStartupPhaseBlocks) { + return 1.f; + } + + float gain = SqrtFastApproximation(energy_after_filtering / + (energy_before_filtering + 1.f)); + + // Scaling for new version. Threshold in final energy gain factor calculation. + constexpr float kBLim = 0.5f; + float scale_factor1 = 1.f; + if (gain > kBLim) { + scale_factor1 = 1.f + 1.3f * (gain - kBLim); + if (gain * scale_factor1 > 1.f) { + scale_factor1 = 1.f / gain; + } + } + + float scale_factor2 = 1.f; + if (gain < kBLim) { + // Do not reduce scale too much for pause regions: attenuation here should + // be controlled by flooring. + gain = std::max(gain, suppression_params_.minimum_attenuating_gain); + scale_factor2 = 1.f - 0.3f * (kBLim - gain); + } + + // Combine both scales with speech/noise prob: note prior + // (prior_speech_probability) is not frequency dependent. + return prior_speech_probability * scale_factor1 + + (1.f - prior_speech_probability) * scale_factor2; +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/ns/wiener_filter.h b/webrtc/modules/audio_processing/ns/wiener_filter.h new file mode 100644 index 0000000..b55c5dc --- /dev/null +++ b/webrtc/modules/audio_processing/ns/wiener_filter.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_NS_WIENER_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_NS_WIENER_FILTER_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/ns/ns_common.h" +#include "modules/audio_processing/ns/suppression_params.h" + +namespace webrtc { + +// Estimates a Wiener-filter based frequency domain noise reduction filter. +class WienerFilter { + public: + explicit WienerFilter(const SuppressionParams& suppression_params); + WienerFilter(const WienerFilter&) = delete; + WienerFilter& operator=(const WienerFilter&) = delete; + + // Updates the filter estimate. + void Update( + int32_t num_analyzed_frames, + rtc::ArrayView noise_spectrum, + rtc::ArrayView prev_noise_spectrum, + rtc::ArrayView parametric_noise_spectrum, + rtc::ArrayView signal_spectrum); + + // Compute an overall gain scaling factor. + float ComputeOverallScalingFactor(int32_t num_analyzed_frames, + float prior_speech_probability, + float energy_before_filtering, + float energy_after_filtering) const; + + // Returns the filter. + rtc::ArrayView get_filter() const { + return filter_; + } + + private: + const SuppressionParams& suppression_params_; + std::array spectrum_prev_process_; + std::array initial_spectral_estimate_; + std::array filter_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_NS_WIENER_FILTER_H_ diff --git a/webrtc/modules/audio_processing/ns/windows_private.h b/webrtc/modules/audio_processing/ns/windows_private.h deleted file mode 100644 index 44c2e84..0000000 --- a/webrtc/modules/audio_processing/ns/windows_private.h +++ /dev/null @@ -1,574 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_WINDOWS_PRIVATE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_WINDOWS_PRIVATE_H_ - -// Hanning window for 4ms 16kHz -static const float kHanning64w128[128] = { - 0.00000000000000f, 0.02454122852291f, 0.04906767432742f, - 0.07356456359967f, 0.09801714032956f, 0.12241067519922f, - 0.14673047445536f, 0.17096188876030f, 0.19509032201613f, - 0.21910124015687f, 0.24298017990326f, 0.26671275747490f, - 0.29028467725446f, 0.31368174039889f, 0.33688985339222f, - 0.35989503653499f, 0.38268343236509f, 0.40524131400499f, - 0.42755509343028f, 0.44961132965461f, 0.47139673682600f, - 0.49289819222978f, 0.51410274419322f, 0.53499761988710f, - 0.55557023301960f, 0.57580819141785f, 0.59569930449243f, - 0.61523159058063f, 0.63439328416365f, 0.65317284295378f, - 0.67155895484702f, 0.68954054473707f, 0.70710678118655f, - 0.72424708295147f, 0.74095112535496f, 0.75720884650648f, - 0.77301045336274f, 0.78834642762661f, 0.80320753148064f, - 0.81758481315158f, 0.83146961230255f, 0.84485356524971f, - 0.85772861000027f, 0.87008699110871f, 0.88192126434835f, - 0.89322430119552f, 0.90398929312344f, 0.91420975570353f, - 0.92387953251129f, 0.93299279883474f, 0.94154406518302f, - 0.94952818059304f, 0.95694033573221f, 0.96377606579544f, - 0.97003125319454f, 0.97570213003853f, 0.98078528040323f, - 0.98527764238894f, 0.98917650996478f, 0.99247953459871f, - 0.99518472667220f, 0.99729045667869f, 0.99879545620517f, - 0.99969881869620f, 1.00000000000000f, - 0.99969881869620f, 0.99879545620517f, 0.99729045667869f, - 0.99518472667220f, 0.99247953459871f, 0.98917650996478f, - 0.98527764238894f, 0.98078528040323f, 0.97570213003853f, - 0.97003125319454f, 0.96377606579544f, 0.95694033573221f, - 0.94952818059304f, 0.94154406518302f, 0.93299279883474f, - 0.92387953251129f, 0.91420975570353f, 0.90398929312344f, - 0.89322430119552f, 0.88192126434835f, 0.87008699110871f, - 0.85772861000027f, 0.84485356524971f, 0.83146961230255f, - 0.81758481315158f, 0.80320753148064f, 0.78834642762661f, - 0.77301045336274f, 0.75720884650648f, 0.74095112535496f, - 0.72424708295147f, 0.70710678118655f, 0.68954054473707f, - 0.67155895484702f, 0.65317284295378f, 0.63439328416365f, - 0.61523159058063f, 0.59569930449243f, 0.57580819141785f, - 0.55557023301960f, 0.53499761988710f, 0.51410274419322f, - 0.49289819222978f, 0.47139673682600f, 0.44961132965461f, - 0.42755509343028f, 0.40524131400499f, 0.38268343236509f, - 0.35989503653499f, 0.33688985339222f, 0.31368174039889f, - 0.29028467725446f, 0.26671275747490f, 0.24298017990326f, - 0.21910124015687f, 0.19509032201613f, 0.17096188876030f, - 0.14673047445536f, 0.12241067519922f, 0.09801714032956f, - 0.07356456359967f, 0.04906767432742f, 0.02454122852291f -}; - - - -// hybrib Hanning & flat window -static const float kBlocks80w128[128] = { - (float)0.00000000, (float)0.03271908, (float)0.06540313, (float)0.09801714, (float)0.13052619, - (float)0.16289547, (float)0.19509032, (float)0.22707626, (float)0.25881905, (float)0.29028468, - (float)0.32143947, (float)0.35225005, (float)0.38268343, (float)0.41270703, (float)0.44228869, - (float)0.47139674, (float)0.50000000, (float)0.52806785, (float)0.55557023, (float)0.58247770, - (float)0.60876143, (float)0.63439328, (float)0.65934582, (float)0.68359230, (float)0.70710678, - (float)0.72986407, (float)0.75183981, (float)0.77301045, (float)0.79335334, (float)0.81284668, - (float)0.83146961, (float)0.84920218, (float)0.86602540, (float)0.88192126, (float)0.89687274, - (float)0.91086382, (float)0.92387953, (float)0.93590593, (float)0.94693013, (float)0.95694034, - (float)0.96592583, (float)0.97387698, (float)0.98078528, (float)0.98664333, (float)0.99144486, - (float)0.99518473, (float)0.99785892, (float)0.99946459, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)0.99946459, (float)0.99785892, (float)0.99518473, (float)0.99144486, - (float)0.98664333, (float)0.98078528, (float)0.97387698, (float)0.96592583, (float)0.95694034, - (float)0.94693013, (float)0.93590593, (float)0.92387953, (float)0.91086382, (float)0.89687274, - (float)0.88192126, (float)0.86602540, (float)0.84920218, (float)0.83146961, (float)0.81284668, - (float)0.79335334, (float)0.77301045, (float)0.75183981, (float)0.72986407, (float)0.70710678, - (float)0.68359230, (float)0.65934582, (float)0.63439328, (float)0.60876143, (float)0.58247770, - (float)0.55557023, (float)0.52806785, (float)0.50000000, (float)0.47139674, (float)0.44228869, - (float)0.41270703, (float)0.38268343, (float)0.35225005, (float)0.32143947, (float)0.29028468, - (float)0.25881905, (float)0.22707626, (float)0.19509032, (float)0.16289547, (float)0.13052619, - (float)0.09801714, (float)0.06540313, (float)0.03271908 -}; - -// hybrib Hanning & flat window -static const float kBlocks160w256[256] = { - (float)0.00000000, (float)0.01636173, (float)0.03271908, (float)0.04906767, (float)0.06540313, - (float)0.08172107, (float)0.09801714, (float)0.11428696, (float)0.13052619, (float)0.14673047, - (float)0.16289547, (float)0.17901686, (float)0.19509032, (float)0.21111155, (float)0.22707626, - (float)0.24298018, (float)0.25881905, (float)0.27458862, (float)0.29028468, (float)0.30590302, - (float)0.32143947, (float)0.33688985, (float)0.35225005, (float)0.36751594, (float)0.38268343, - (float)0.39774847, (float)0.41270703, (float)0.42755509, (float)0.44228869, (float)0.45690388, - (float)0.47139674, (float)0.48576339, (float)0.50000000, (float)0.51410274, (float)0.52806785, - (float)0.54189158, (float)0.55557023, (float)0.56910015, (float)0.58247770, (float)0.59569930, - (float)0.60876143, (float)0.62166057, (float)0.63439328, (float)0.64695615, (float)0.65934582, - (float)0.67155895, (float)0.68359230, (float)0.69544264, (float)0.70710678, (float)0.71858162, - (float)0.72986407, (float)0.74095113, (float)0.75183981, (float)0.76252720, (float)0.77301045, - (float)0.78328675, (float)0.79335334, (float)0.80320753, (float)0.81284668, (float)0.82226822, - (float)0.83146961, (float)0.84044840, (float)0.84920218, (float)0.85772861, (float)0.86602540, - (float)0.87409034, (float)0.88192126, (float)0.88951608, (float)0.89687274, (float)0.90398929, - (float)0.91086382, (float)0.91749450, (float)0.92387953, (float)0.93001722, (float)0.93590593, - (float)0.94154407, (float)0.94693013, (float)0.95206268, (float)0.95694034, (float)0.96156180, - (float)0.96592583, (float)0.97003125, (float)0.97387698, (float)0.97746197, (float)0.98078528, - (float)0.98384601, (float)0.98664333, (float)0.98917651, (float)0.99144486, (float)0.99344778, - (float)0.99518473, (float)0.99665524, (float)0.99785892, (float)0.99879546, (float)0.99946459, - (float)0.99986614, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)0.99986614, (float)0.99946459, (float)0.99879546, (float)0.99785892, - (float)0.99665524, (float)0.99518473, (float)0.99344778, (float)0.99144486, (float)0.98917651, - (float)0.98664333, (float)0.98384601, (float)0.98078528, (float)0.97746197, (float)0.97387698, - (float)0.97003125, (float)0.96592583, (float)0.96156180, (float)0.95694034, (float)0.95206268, - (float)0.94693013, (float)0.94154407, (float)0.93590593, (float)0.93001722, (float)0.92387953, - (float)0.91749450, (float)0.91086382, (float)0.90398929, (float)0.89687274, (float)0.88951608, - (float)0.88192126, (float)0.87409034, (float)0.86602540, (float)0.85772861, (float)0.84920218, - (float)0.84044840, (float)0.83146961, (float)0.82226822, (float)0.81284668, (float)0.80320753, - (float)0.79335334, (float)0.78328675, (float)0.77301045, (float)0.76252720, (float)0.75183981, - (float)0.74095113, (float)0.72986407, (float)0.71858162, (float)0.70710678, (float)0.69544264, - (float)0.68359230, (float)0.67155895, (float)0.65934582, (float)0.64695615, (float)0.63439328, - (float)0.62166057, (float)0.60876143, (float)0.59569930, (float)0.58247770, (float)0.56910015, - (float)0.55557023, (float)0.54189158, (float)0.52806785, (float)0.51410274, (float)0.50000000, - (float)0.48576339, (float)0.47139674, (float)0.45690388, (float)0.44228869, (float)0.42755509, - (float)0.41270703, (float)0.39774847, (float)0.38268343, (float)0.36751594, (float)0.35225005, - (float)0.33688985, (float)0.32143947, (float)0.30590302, (float)0.29028468, (float)0.27458862, - (float)0.25881905, (float)0.24298018, (float)0.22707626, (float)0.21111155, (float)0.19509032, - (float)0.17901686, (float)0.16289547, (float)0.14673047, (float)0.13052619, (float)0.11428696, - (float)0.09801714, (float)0.08172107, (float)0.06540313, (float)0.04906767, (float)0.03271908, - (float)0.01636173 -}; - -// hybrib Hanning & flat window: for 20ms -static const float kBlocks320w512[512] = { - (float)0.00000000, (float)0.00818114, (float)0.01636173, (float)0.02454123, (float)0.03271908, - (float)0.04089475, (float)0.04906767, (float)0.05723732, (float)0.06540313, (float)0.07356456, - (float)0.08172107, (float)0.08987211, (float)0.09801714, (float)0.10615561, (float)0.11428696, - (float)0.12241068, (float)0.13052619, (float)0.13863297, (float)0.14673047, (float)0.15481816, - (float)0.16289547, (float)0.17096189, (float)0.17901686, (float)0.18705985, (float)0.19509032, - (float)0.20310773, (float)0.21111155, (float)0.21910124, (float)0.22707626, (float)0.23503609, - (float)0.24298018, (float)0.25090801, (float)0.25881905, (float)0.26671276, (float)0.27458862, - (float)0.28244610, (float)0.29028468, (float)0.29810383, (float)0.30590302, (float)0.31368174, - (float)0.32143947, (float)0.32917568, (float)0.33688985, (float)0.34458148, (float)0.35225005, - (float)0.35989504, (float)0.36751594, (float)0.37511224, (float)0.38268343, (float)0.39022901, - (float)0.39774847, (float)0.40524131, (float)0.41270703, (float)0.42014512, (float)0.42755509, - (float)0.43493645, (float)0.44228869, (float)0.44961133, (float)0.45690388, (float)0.46416584, - (float)0.47139674, (float)0.47859608, (float)0.48576339, (float)0.49289819, (float)0.50000000, - (float)0.50706834, (float)0.51410274, (float)0.52110274, (float)0.52806785, (float)0.53499762, - (float)0.54189158, (float)0.54874927, (float)0.55557023, (float)0.56235401, (float)0.56910015, - (float)0.57580819, (float)0.58247770, (float)0.58910822, (float)0.59569930, (float)0.60225052, - (float)0.60876143, (float)0.61523159, (float)0.62166057, (float)0.62804795, (float)0.63439328, - (float)0.64069616, (float)0.64695615, (float)0.65317284, (float)0.65934582, (float)0.66547466, - (float)0.67155895, (float)0.67759830, (float)0.68359230, (float)0.68954054, (float)0.69544264, - (float)0.70129818, (float)0.70710678, (float)0.71286806, (float)0.71858162, (float)0.72424708, - (float)0.72986407, (float)0.73543221, (float)0.74095113, (float)0.74642045, (float)0.75183981, - (float)0.75720885, (float)0.76252720, (float)0.76779452, (float)0.77301045, (float)0.77817464, - (float)0.78328675, (float)0.78834643, (float)0.79335334, (float)0.79830715, (float)0.80320753, - (float)0.80805415, (float)0.81284668, (float)0.81758481, (float)0.82226822, (float)0.82689659, - (float)0.83146961, (float)0.83598698, (float)0.84044840, (float)0.84485357, (float)0.84920218, - (float)0.85349396, (float)0.85772861, (float)0.86190585, (float)0.86602540, (float)0.87008699, - (float)0.87409034, (float)0.87803519, (float)0.88192126, (float)0.88574831, (float)0.88951608, - (float)0.89322430, (float)0.89687274, (float)0.90046115, (float)0.90398929, (float)0.90745693, - (float)0.91086382, (float)0.91420976, (float)0.91749450, (float)0.92071783, (float)0.92387953, - (float)0.92697940, (float)0.93001722, (float)0.93299280, (float)0.93590593, (float)0.93875641, - (float)0.94154407, (float)0.94426870, (float)0.94693013, (float)0.94952818, (float)0.95206268, - (float)0.95453345, (float)0.95694034, (float)0.95928317, (float)0.96156180, (float)0.96377607, - (float)0.96592583, (float)0.96801094, (float)0.97003125, (float)0.97198664, (float)0.97387698, - (float)0.97570213, (float)0.97746197, (float)0.97915640, (float)0.98078528, (float)0.98234852, - (float)0.98384601, (float)0.98527764, (float)0.98664333, (float)0.98794298, (float)0.98917651, - (float)0.99034383, (float)0.99144486, (float)0.99247953, (float)0.99344778, (float)0.99434953, - (float)0.99518473, (float)0.99595331, (float)0.99665524, (float)0.99729046, (float)0.99785892, - (float)0.99836060, (float)0.99879546, (float)0.99916346, (float)0.99946459, (float)0.99969882, - (float)0.99986614, (float)0.99996653, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, - (float)1.00000000, (float)0.99996653, (float)0.99986614, (float)0.99969882, (float)0.99946459, - (float)0.99916346, (float)0.99879546, (float)0.99836060, (float)0.99785892, (float)0.99729046, - (float)0.99665524, (float)0.99595331, (float)0.99518473, (float)0.99434953, (float)0.99344778, - (float)0.99247953, (float)0.99144486, (float)0.99034383, (float)0.98917651, (float)0.98794298, - (float)0.98664333, (float)0.98527764, (float)0.98384601, (float)0.98234852, (float)0.98078528, - (float)0.97915640, (float)0.97746197, (float)0.97570213, (float)0.97387698, (float)0.97198664, - (float)0.97003125, (float)0.96801094, (float)0.96592583, (float)0.96377607, (float)0.96156180, - (float)0.95928317, (float)0.95694034, (float)0.95453345, (float)0.95206268, (float)0.94952818, - (float)0.94693013, (float)0.94426870, (float)0.94154407, (float)0.93875641, (float)0.93590593, - (float)0.93299280, (float)0.93001722, (float)0.92697940, (float)0.92387953, (float)0.92071783, - (float)0.91749450, (float)0.91420976, (float)0.91086382, (float)0.90745693, (float)0.90398929, - (float)0.90046115, (float)0.89687274, (float)0.89322430, (float)0.88951608, (float)0.88574831, - (float)0.88192126, (float)0.87803519, (float)0.87409034, (float)0.87008699, (float)0.86602540, - (float)0.86190585, (float)0.85772861, (float)0.85349396, (float)0.84920218, (float)0.84485357, - (float)0.84044840, (float)0.83598698, (float)0.83146961, (float)0.82689659, (float)0.82226822, - (float)0.81758481, (float)0.81284668, (float)0.80805415, (float)0.80320753, (float)0.79830715, - (float)0.79335334, (float)0.78834643, (float)0.78328675, (float)0.77817464, (float)0.77301045, - (float)0.76779452, (float)0.76252720, (float)0.75720885, (float)0.75183981, (float)0.74642045, - (float)0.74095113, (float)0.73543221, (float)0.72986407, (float)0.72424708, (float)0.71858162, - (float)0.71286806, (float)0.70710678, (float)0.70129818, (float)0.69544264, (float)0.68954054, - (float)0.68359230, (float)0.67759830, (float)0.67155895, (float)0.66547466, (float)0.65934582, - (float)0.65317284, (float)0.64695615, (float)0.64069616, (float)0.63439328, (float)0.62804795, - (float)0.62166057, (float)0.61523159, (float)0.60876143, (float)0.60225052, (float)0.59569930, - (float)0.58910822, (float)0.58247770, (float)0.57580819, (float)0.56910015, (float)0.56235401, - (float)0.55557023, (float)0.54874927, (float)0.54189158, (float)0.53499762, (float)0.52806785, - (float)0.52110274, (float)0.51410274, (float)0.50706834, (float)0.50000000, (float)0.49289819, - (float)0.48576339, (float)0.47859608, (float)0.47139674, (float)0.46416584, (float)0.45690388, - (float)0.44961133, (float)0.44228869, (float)0.43493645, (float)0.42755509, (float)0.42014512, - (float)0.41270703, (float)0.40524131, (float)0.39774847, (float)0.39022901, (float)0.38268343, - (float)0.37511224, (float)0.36751594, (float)0.35989504, (float)0.35225005, (float)0.34458148, - (float)0.33688985, (float)0.32917568, (float)0.32143947, (float)0.31368174, (float)0.30590302, - (float)0.29810383, (float)0.29028468, (float)0.28244610, (float)0.27458862, (float)0.26671276, - (float)0.25881905, (float)0.25090801, (float)0.24298018, (float)0.23503609, (float)0.22707626, - (float)0.21910124, (float)0.21111155, (float)0.20310773, (float)0.19509032, (float)0.18705985, - (float)0.17901686, (float)0.17096189, (float)0.16289547, (float)0.15481816, (float)0.14673047, - (float)0.13863297, (float)0.13052619, (float)0.12241068, (float)0.11428696, (float)0.10615561, - (float)0.09801714, (float)0.08987211, (float)0.08172107, (float)0.07356456, (float)0.06540313, - (float)0.05723732, (float)0.04906767, (float)0.04089475, (float)0.03271908, (float)0.02454123, - (float)0.01636173, (float)0.00818114 -}; - - -// Hanning window: for 15ms at 16kHz with symmetric zeros -static const float kBlocks240w512[512] = { - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00654494, (float)0.01308960, (float)0.01963369, - (float)0.02617695, (float)0.03271908, (float)0.03925982, (float)0.04579887, (float)0.05233596, - (float)0.05887080, (float)0.06540313, (float)0.07193266, (float)0.07845910, (float)0.08498218, - (float)0.09150162, (float)0.09801714, (float)0.10452846, (float)0.11103531, (float)0.11753740, - (float)0.12403446, (float)0.13052620, (float)0.13701233, (float)0.14349262, (float)0.14996676, - (float)0.15643448, (float)0.16289547, (float)0.16934951, (float)0.17579629, (float)0.18223552, - (float)0.18866697, (float)0.19509032, (float)0.20150533, (float)0.20791170, (float)0.21430916, - (float)0.22069745, (float)0.22707628, (float)0.23344538, (float)0.23980446, (float)0.24615330, - (float)0.25249159, (float)0.25881904, (float)0.26513544, (float)0.27144045, (float)0.27773386, - (float)0.28401536, (float)0.29028466, (float)0.29654160, (float)0.30278578, (float)0.30901700, - (float)0.31523499, (float)0.32143945, (float)0.32763019, (float)0.33380687, (float)0.33996925, - (float)0.34611708, (float)0.35225007, (float)0.35836795, (float)0.36447051, (float)0.37055743, - (float)0.37662852, (float)0.38268346, (float)0.38872197, (float)0.39474389, (float)0.40074885, - (float)0.40673664, (float)0.41270703, (float)0.41865975, (float)0.42459452, (float)0.43051112, - (float)0.43640924, (float)0.44228873, (float)0.44814920, (float)0.45399052, (float)0.45981237, - (float)0.46561453, (float)0.47139674, (float)0.47715878, (float)0.48290035, (float)0.48862126, - (float)0.49432120, (float)0.50000000, (float)0.50565743, (float)0.51129311, (float)0.51690692, - (float)0.52249855, (float)0.52806789, (float)0.53361452, (float)0.53913832, (float)0.54463905, - (float)0.55011642, (float)0.55557024, (float)0.56100029, (float)0.56640625, (float)0.57178795, - (float)0.57714522, (float)0.58247769, (float)0.58778524, (float)0.59306765, (float)0.59832460, - (float)0.60355598, (float)0.60876143, (float)0.61394083, (float)0.61909395, (float)0.62422055, - (float)0.62932038, (float)0.63439333, (float)0.63943899, (float)0.64445734, (float)0.64944810, - (float)0.65441096, (float)0.65934587, (float)0.66425246, (float)0.66913062, (float)0.67398012, - (float)0.67880076, (float)0.68359232, (float)0.68835455, (float)0.69308740, (float)0.69779050, - (float)0.70246369, (float)0.70710677, (float)0.71171963, (float)0.71630198, (float)0.72085363, - (float)0.72537440, (float)0.72986406, (float)0.73432255, (float)0.73874950, (float)0.74314487, - (float)0.74750835, (float)0.75183982, (float)0.75613910, (float)0.76040596, (float)0.76464027, - (float)0.76884186, (float)0.77301043, (float)0.77714598, (float)0.78124821, (float)0.78531694, - (float)0.78935206, (float)0.79335338, (float)0.79732066, (float)0.80125386, (float)0.80515265, - (float)0.80901700, (float)0.81284672, (float)0.81664157, (float)0.82040149, (float)0.82412618, - (float)0.82781565, (float)0.83146966, (float)0.83508795, (float)0.83867061, (float)0.84221727, - (float)0.84572780, (float)0.84920216, (float)0.85264021, (float)0.85604161, (float)0.85940641, - (float)0.86273444, (float)0.86602545, (float)0.86927933, (float)0.87249607, (float)0.87567532, - (float)0.87881714, (float)0.88192129, (float)0.88498765, (float)0.88801610, (float)0.89100653, - (float)0.89395881, (float)0.89687276, (float)0.89974827, (float)0.90258533, (float)0.90538365, - (float)0.90814316, (float)0.91086388, (float)0.91354549, (float)0.91618794, (float)0.91879123, - (float)0.92135513, (float)0.92387950, (float)0.92636442, (float)0.92880958, (float)0.93121493, - (float)0.93358046, (float)0.93590593, (float)0.93819135, (float)0.94043654, (float)0.94264150, - (float)0.94480604, (float)0.94693011, (float)0.94901365, (float)0.95105654, (float)0.95305866, - (float)0.95501995, (float)0.95694035, (float)0.95881975, (float)0.96065807, (float)0.96245527, - (float)0.96421117, (float)0.96592581, (float)0.96759909, (float)0.96923089, (float)0.97082120, - (float)0.97236991, (float)0.97387701, (float)0.97534233, (float)0.97676587, (float)0.97814763, - (float)0.97948742, (float)0.98078531, (float)0.98204112, (float)0.98325491, (float)0.98442656, - (float)0.98555607, (float)0.98664331, (float)0.98768836, (float)0.98869103, (float)0.98965138, - (float)0.99056935, (float)0.99144489, (float)0.99227792, (float)0.99306846, (float)0.99381649, - (float)0.99452192, (float)0.99518472, (float)0.99580491, (float)0.99638247, (float)0.99691731, - (float)0.99740952, (float)0.99785894, (float)0.99826562, (float)0.99862951, (float)0.99895066, - (float)0.99922901, (float)0.99946457, (float)0.99965733, (float)0.99980724, (float)0.99991435, - (float)0.99997860, (float)1.00000000, (float)0.99997860, (float)0.99991435, (float)0.99980724, - (float)0.99965733, (float)0.99946457, (float)0.99922901, (float)0.99895066, (float)0.99862951, - (float)0.99826562, (float)0.99785894, (float)0.99740946, (float)0.99691731, (float)0.99638247, - (float)0.99580491, (float)0.99518472, (float)0.99452192, (float)0.99381644, (float)0.99306846, - (float)0.99227792, (float)0.99144489, (float)0.99056935, (float)0.98965138, (float)0.98869103, - (float)0.98768836, (float)0.98664331, (float)0.98555607, (float)0.98442656, (float)0.98325491, - (float)0.98204112, (float)0.98078525, (float)0.97948742, (float)0.97814757, (float)0.97676587, - (float)0.97534227, (float)0.97387695, (float)0.97236991, (float)0.97082120, (float)0.96923089, - (float)0.96759909, (float)0.96592581, (float)0.96421117, (float)0.96245521, (float)0.96065807, - (float)0.95881969, (float)0.95694029, (float)0.95501995, (float)0.95305860, (float)0.95105648, - (float)0.94901365, (float)0.94693011, (float)0.94480604, (float)0.94264150, (float)0.94043654, - (float)0.93819129, (float)0.93590593, (float)0.93358046, (float)0.93121493, (float)0.92880952, - (float)0.92636436, (float)0.92387950, (float)0.92135507, (float)0.91879123, (float)0.91618794, - (float)0.91354543, (float)0.91086382, (float)0.90814310, (float)0.90538365, (float)0.90258527, - (float)0.89974827, (float)0.89687276, (float)0.89395875, (float)0.89100647, (float)0.88801610, - (float)0.88498759, (float)0.88192123, (float)0.87881714, (float)0.87567532, (float)0.87249595, - (float)0.86927933, (float)0.86602539, (float)0.86273432, (float)0.85940641, (float)0.85604161, - (float)0.85264009, (float)0.84920216, (float)0.84572780, (float)0.84221715, (float)0.83867055, - (float)0.83508795, (float)0.83146954, (float)0.82781565, (float)0.82412612, (float)0.82040137, - (float)0.81664157, (float)0.81284660, (float)0.80901700, (float)0.80515265, (float)0.80125374, - (float)0.79732066, (float)0.79335332, (float)0.78935200, (float)0.78531694, (float)0.78124815, - (float)0.77714586, (float)0.77301049, (float)0.76884180, (float)0.76464021, (float)0.76040596, - (float)0.75613904, (float)0.75183970, (float)0.74750835, (float)0.74314481, (float)0.73874938, - (float)0.73432249, (float)0.72986400, (float)0.72537428, (float)0.72085363, (float)0.71630186, - (float)0.71171951, (float)0.70710677, (float)0.70246363, (float)0.69779032, (float)0.69308734, - (float)0.68835449, (float)0.68359220, (float)0.67880070, (float)0.67398006, (float)0.66913044, - (float)0.66425240, (float)0.65934575, (float)0.65441096, (float)0.64944804, (float)0.64445722, - (float)0.63943905, (float)0.63439327, (float)0.62932026, (float)0.62422055, (float)0.61909389, - (float)0.61394072, (float)0.60876143, (float)0.60355592, (float)0.59832448, (float)0.59306765, - (float)0.58778518, (float)0.58247757, (float)0.57714522, (float)0.57178789, (float)0.56640613, - (float)0.56100023, (float)0.55557019, (float)0.55011630, (float)0.54463905, (float)0.53913826, - (float)0.53361434, (float)0.52806783, (float)0.52249849, (float)0.51690674, (float)0.51129305, - (float)0.50565726, (float)0.50000006, (float)0.49432117, (float)0.48862115, (float)0.48290038, - (float)0.47715873, (float)0.47139663, (float)0.46561456, (float)0.45981231, (float)0.45399037, - (float)0.44814920, (float)0.44228864, (float)0.43640912, (float)0.43051112, (float)0.42459446, - (float)0.41865960, (float)0.41270703, (float)0.40673658, (float)0.40074870, (float)0.39474386, - (float)0.38872188, (float)0.38268328, (float)0.37662849, (float)0.37055734, (float)0.36447033, - (float)0.35836792, (float)0.35224995, (float)0.34611690, (float)0.33996922, (float)0.33380675, - (float)0.32763001, (float)0.32143945, (float)0.31523487, (float)0.30901679, (float)0.30278572, - (float)0.29654145, (float)0.29028472, (float)0.28401530, (float)0.27773371, (float)0.27144048, - (float)0.26513538, (float)0.25881892, (float)0.25249159, (float)0.24615324, (float)0.23980433, - (float)0.23344538, (float)0.22707619, (float)0.22069728, (float)0.21430916, (float)0.20791161, - (float)0.20150517, (float)0.19509031, (float)0.18866688, (float)0.18223536, (float)0.17579627, - (float)0.16934940, (float)0.16289529, (float)0.15643445, (float)0.14996666, (float)0.14349243, - (float)0.13701232, (float)0.13052608, (float)0.12403426, (float)0.11753736, (float)0.11103519, - (float)0.10452849, (float)0.09801710, (float)0.09150149, (float)0.08498220, (float)0.07845904, - (float)0.07193252, (float)0.06540315, (float)0.05887074, (float)0.05233581, (float)0.04579888, - (float)0.03925974, (float)0.03271893, (float)0.02617695, (float)0.01963361, (float)0.01308943, - (float)0.00654493, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000 -}; - - -// Hanning window: for 30ms with 1024 fft with symmetric zeros at 16kHz -static const float kBlocks480w1024[1024] = { - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00327249, (float)0.00654494, - (float)0.00981732, (float)0.01308960, (float)0.01636173, (float)0.01963369, (float)0.02290544, - (float)0.02617695, (float)0.02944817, (float)0.03271908, (float)0.03598964, (float)0.03925982, - (float)0.04252957, (float)0.04579887, (float)0.04906768, (float)0.05233596, (float)0.05560368, - (float)0.05887080, (float)0.06213730, (float)0.06540313, (float)0.06866825, (float)0.07193266, - (float)0.07519628, (float)0.07845910, (float)0.08172107, (float)0.08498218, (float)0.08824237, - (float)0.09150162, (float)0.09475989, (float)0.09801714, (float)0.10127335, (float)0.10452846, - (float)0.10778246, (float)0.11103531, (float)0.11428697, (float)0.11753740, (float)0.12078657, - (float)0.12403446, (float)0.12728101, (float)0.13052620, (float)0.13376999, (float)0.13701233, - (float)0.14025325, (float)0.14349262, (float)0.14673047, (float)0.14996676, (float)0.15320145, - (float)0.15643448, (float)0.15966582, (float)0.16289547, (float)0.16612339, (float)0.16934951, - (float)0.17257382, (float)0.17579629, (float)0.17901687, (float)0.18223552, (float)0.18545224, - (float)0.18866697, (float)0.19187967, (float)0.19509032, (float)0.19829889, (float)0.20150533, - (float)0.20470962, (float)0.20791170, (float)0.21111156, (float)0.21430916, (float)0.21750447, - (float)0.22069745, (float)0.22388805, (float)0.22707628, (float)0.23026206, (float)0.23344538, - (float)0.23662618, (float)0.23980446, (float)0.24298020, (float)0.24615330, (float)0.24932377, - (float)0.25249159, (float)0.25565669, (float)0.25881904, (float)0.26197866, (float)0.26513544, - (float)0.26828939, (float)0.27144045, (float)0.27458861, (float)0.27773386, (float)0.28087610, - (float)0.28401536, (float)0.28715158, (float)0.29028466, (float)0.29341471, (float)0.29654160, - (float)0.29966527, (float)0.30278578, (float)0.30590302, (float)0.30901700, (float)0.31212768, - (float)0.31523499, (float)0.31833893, (float)0.32143945, (float)0.32453656, (float)0.32763019, - (float)0.33072028, (float)0.33380687, (float)0.33688986, (float)0.33996925, (float)0.34304500, - (float)0.34611708, (float)0.34918544, (float)0.35225007, (float)0.35531089, (float)0.35836795, - (float)0.36142117, (float)0.36447051, (float)0.36751595, (float)0.37055743, (float)0.37359497, - (float)0.37662852, (float)0.37965801, (float)0.38268346, (float)0.38570479, (float)0.38872197, - (float)0.39173502, (float)0.39474389, (float)0.39774847, (float)0.40074885, (float)0.40374491, - (float)0.40673664, (float)0.40972406, (float)0.41270703, (float)0.41568562, (float)0.41865975, - (float)0.42162940, (float)0.42459452, (float)0.42755508, (float)0.43051112, (float)0.43346250, - (float)0.43640924, (float)0.43935132, (float)0.44228873, (float)0.44522133, (float)0.44814920, - (float)0.45107228, (float)0.45399052, (float)0.45690390, (float)0.45981237, (float)0.46271592, - (float)0.46561453, (float)0.46850815, (float)0.47139674, (float)0.47428030, (float)0.47715878, - (float)0.48003215, (float)0.48290035, (float)0.48576337, (float)0.48862126, (float)0.49147385, - (float)0.49432120, (float)0.49716330, (float)0.50000000, (float)0.50283140, (float)0.50565743, - (float)0.50847799, (float)0.51129311, (float)0.51410276, (float)0.51690692, (float)0.51970553, - (float)0.52249855, (float)0.52528602, (float)0.52806789, (float)0.53084403, (float)0.53361452, - (float)0.53637928, (float)0.53913832, (float)0.54189163, (float)0.54463905, (float)0.54738063, - (float)0.55011642, (float)0.55284631, (float)0.55557024, (float)0.55828828, (float)0.56100029, - (float)0.56370628, (float)0.56640625, (float)0.56910014, (float)0.57178795, (float)0.57446963, - (float)0.57714522, (float)0.57981455, (float)0.58247769, (float)0.58513463, (float)0.58778524, - (float)0.59042960, (float)0.59306765, (float)0.59569931, (float)0.59832460, (float)0.60094351, - (float)0.60355598, (float)0.60616195, (float)0.60876143, (float)0.61135441, (float)0.61394083, - (float)0.61652070, (float)0.61909395, (float)0.62166059, (float)0.62422055, (float)0.62677383, - (float)0.62932038, (float)0.63186020, (float)0.63439333, (float)0.63691956, (float)0.63943899, - (float)0.64195162, (float)0.64445734, (float)0.64695615, (float)0.64944810, (float)0.65193301, - (float)0.65441096, (float)0.65688187, (float)0.65934587, (float)0.66180271, (float)0.66425246, - (float)0.66669512, (float)0.66913062, (float)0.67155898, (float)0.67398012, (float)0.67639405, - (float)0.67880076, (float)0.68120021, (float)0.68359232, (float)0.68597710, (float)0.68835455, - (float)0.69072467, (float)0.69308740, (float)0.69544262, (float)0.69779050, (float)0.70013082, - (float)0.70246369, (float)0.70478904, (float)0.70710677, (float)0.70941699, (float)0.71171963, - (float)0.71401459, (float)0.71630198, (float)0.71858168, (float)0.72085363, (float)0.72311789, - (float)0.72537440, (float)0.72762316, (float)0.72986406, (float)0.73209721, (float)0.73432255, - (float)0.73653996, (float)0.73874950, (float)0.74095118, (float)0.74314487, (float)0.74533057, - (float)0.74750835, (float)0.74967808, (float)0.75183982, (float)0.75399351, (float)0.75613910, - (float)0.75827658, (float)0.76040596, (float)0.76252723, (float)0.76464027, (float)0.76674515, - (float)0.76884186, (float)0.77093029, (float)0.77301043, (float)0.77508241, (float)0.77714598, - (float)0.77920127, (float)0.78124821, (float)0.78328675, (float)0.78531694, (float)0.78733873, - (float)0.78935206, (float)0.79135692, (float)0.79335338, (float)0.79534125, (float)0.79732066, - (float)0.79929149, (float)0.80125386, (float)0.80320752, (float)0.80515265, (float)0.80708915, - (float)0.80901700, (float)0.81093621, (float)0.81284672, (float)0.81474853, (float)0.81664157, - (float)0.81852591, (float)0.82040149, (float)0.82226825, (float)0.82412618, (float)0.82597536, - (float)0.82781565, (float)0.82964706, (float)0.83146966, (float)0.83328325, (float)0.83508795, - (float)0.83688378, (float)0.83867061, (float)0.84044838, (float)0.84221727, (float)0.84397703, - (float)0.84572780, (float)0.84746957, (float)0.84920216, (float)0.85092574, (float)0.85264021, - (float)0.85434544, (float)0.85604161, (float)0.85772866, (float)0.85940641, (float)0.86107504, - (float)0.86273444, (float)0.86438453, (float)0.86602545, (float)0.86765707, (float)0.86927933, - (float)0.87089235, (float)0.87249607, (float)0.87409031, (float)0.87567532, (float)0.87725097, - (float)0.87881714, (float)0.88037390, (float)0.88192129, (float)0.88345921, (float)0.88498765, - (float)0.88650668, (float)0.88801610, (float)0.88951612, (float)0.89100653, (float)0.89248741, - (float)0.89395881, (float)0.89542055, (float)0.89687276, (float)0.89831537, (float)0.89974827, - (float)0.90117162, (float)0.90258533, (float)0.90398932, (float)0.90538365, (float)0.90676826, - (float)0.90814316, (float)0.90950841, (float)0.91086388, (float)0.91220951, (float)0.91354549, - (float)0.91487163, (float)0.91618794, (float)0.91749454, (float)0.91879123, (float)0.92007810, - (float)0.92135513, (float)0.92262226, (float)0.92387950, (float)0.92512691, (float)0.92636442, - (float)0.92759192, (float)0.92880958, (float)0.93001723, (float)0.93121493, (float)0.93240267, - (float)0.93358046, (float)0.93474817, (float)0.93590593, (float)0.93705362, (float)0.93819135, - (float)0.93931901, (float)0.94043654, (float)0.94154406, (float)0.94264150, (float)0.94372880, - (float)0.94480604, (float)0.94587320, (float)0.94693011, (float)0.94797695, (float)0.94901365, - (float)0.95004016, (float)0.95105654, (float)0.95206273, (float)0.95305866, (float)0.95404440, - (float)0.95501995, (float)0.95598525, (float)0.95694035, (float)0.95788521, (float)0.95881975, - (float)0.95974404, (float)0.96065807, (float)0.96156180, (float)0.96245527, (float)0.96333838, - (float)0.96421117, (float)0.96507370, (float)0.96592581, (float)0.96676767, (float)0.96759909, - (float)0.96842021, (float)0.96923089, (float)0.97003126, (float)0.97082120, (float)0.97160077, - (float)0.97236991, (float)0.97312868, (float)0.97387701, (float)0.97461486, (float)0.97534233, - (float)0.97605932, (float)0.97676587, (float)0.97746199, (float)0.97814763, (float)0.97882277, - (float)0.97948742, (float)0.98014158, (float)0.98078531, (float)0.98141843, (float)0.98204112, - (float)0.98265332, (float)0.98325491, (float)0.98384601, (float)0.98442656, (float)0.98499662, - (float)0.98555607, (float)0.98610497, (float)0.98664331, (float)0.98717111, (float)0.98768836, - (float)0.98819500, (float)0.98869103, (float)0.98917651, (float)0.98965138, (float)0.99011570, - (float)0.99056935, (float)0.99101239, (float)0.99144489, (float)0.99186671, (float)0.99227792, - (float)0.99267852, (float)0.99306846, (float)0.99344778, (float)0.99381649, (float)0.99417448, - (float)0.99452192, (float)0.99485862, (float)0.99518472, (float)0.99550015, (float)0.99580491, - (float)0.99609905, (float)0.99638247, (float)0.99665523, (float)0.99691731, (float)0.99716878, - (float)0.99740952, (float)0.99763954, (float)0.99785894, (float)0.99806762, (float)0.99826562, - (float)0.99845290, (float)0.99862951, (float)0.99879545, (float)0.99895066, (float)0.99909520, - (float)0.99922901, (float)0.99935216, (float)0.99946457, (float)0.99956632, (float)0.99965733, - (float)0.99973762, (float)0.99980724, (float)0.99986613, (float)0.99991435, (float)0.99995178, - (float)0.99997860, (float)0.99999464, (float)1.00000000, (float)0.99999464, (float)0.99997860, - (float)0.99995178, (float)0.99991435, (float)0.99986613, (float)0.99980724, (float)0.99973762, - (float)0.99965733, (float)0.99956632, (float)0.99946457, (float)0.99935216, (float)0.99922901, - (float)0.99909520, (float)0.99895066, (float)0.99879545, (float)0.99862951, (float)0.99845290, - (float)0.99826562, (float)0.99806762, (float)0.99785894, (float)0.99763954, (float)0.99740946, - (float)0.99716872, (float)0.99691731, (float)0.99665523, (float)0.99638247, (float)0.99609905, - (float)0.99580491, (float)0.99550015, (float)0.99518472, (float)0.99485862, (float)0.99452192, - (float)0.99417448, (float)0.99381644, (float)0.99344778, (float)0.99306846, (float)0.99267852, - (float)0.99227792, (float)0.99186671, (float)0.99144489, (float)0.99101239, (float)0.99056935, - (float)0.99011564, (float)0.98965138, (float)0.98917651, (float)0.98869103, (float)0.98819494, - (float)0.98768836, (float)0.98717111, (float)0.98664331, (float)0.98610497, (float)0.98555607, - (float)0.98499656, (float)0.98442656, (float)0.98384601, (float)0.98325491, (float)0.98265326, - (float)0.98204112, (float)0.98141843, (float)0.98078525, (float)0.98014158, (float)0.97948742, - (float)0.97882277, (float)0.97814757, (float)0.97746193, (float)0.97676587, (float)0.97605932, - (float)0.97534227, (float)0.97461486, (float)0.97387695, (float)0.97312862, (float)0.97236991, - (float)0.97160077, (float)0.97082120, (float)0.97003126, (float)0.96923089, (float)0.96842015, - (float)0.96759909, (float)0.96676761, (float)0.96592581, (float)0.96507365, (float)0.96421117, - (float)0.96333838, (float)0.96245521, (float)0.96156180, (float)0.96065807, (float)0.95974404, - (float)0.95881969, (float)0.95788515, (float)0.95694029, (float)0.95598525, (float)0.95501995, - (float)0.95404440, (float)0.95305860, (float)0.95206267, (float)0.95105648, (float)0.95004016, - (float)0.94901365, (float)0.94797695, (float)0.94693011, (float)0.94587314, (float)0.94480604, - (float)0.94372880, (float)0.94264150, (float)0.94154406, (float)0.94043654, (float)0.93931895, - (float)0.93819129, (float)0.93705362, (float)0.93590593, (float)0.93474817, (float)0.93358046, - (float)0.93240267, (float)0.93121493, (float)0.93001723, (float)0.92880952, (float)0.92759192, - (float)0.92636436, (float)0.92512691, (float)0.92387950, (float)0.92262226, (float)0.92135507, - (float)0.92007804, (float)0.91879123, (float)0.91749448, (float)0.91618794, (float)0.91487157, - (float)0.91354543, (float)0.91220951, (float)0.91086382, (float)0.90950835, (float)0.90814310, - (float)0.90676820, (float)0.90538365, (float)0.90398932, (float)0.90258527, (float)0.90117157, - (float)0.89974827, (float)0.89831525, (float)0.89687276, (float)0.89542055, (float)0.89395875, - (float)0.89248741, (float)0.89100647, (float)0.88951600, (float)0.88801610, (float)0.88650662, - (float)0.88498759, (float)0.88345915, (float)0.88192123, (float)0.88037384, (float)0.87881714, - (float)0.87725091, (float)0.87567532, (float)0.87409031, (float)0.87249595, (float)0.87089223, - (float)0.86927933, (float)0.86765701, (float)0.86602539, (float)0.86438447, (float)0.86273432, - (float)0.86107504, (float)0.85940641, (float)0.85772860, (float)0.85604161, (float)0.85434544, - (float)0.85264009, (float)0.85092574, (float)0.84920216, (float)0.84746951, (float)0.84572780, - (float)0.84397697, (float)0.84221715, (float)0.84044844, (float)0.83867055, (float)0.83688372, - (float)0.83508795, (float)0.83328319, (float)0.83146954, (float)0.82964706, (float)0.82781565, - (float)0.82597530, (float)0.82412612, (float)0.82226813, (float)0.82040137, (float)0.81852591, - (float)0.81664157, (float)0.81474847, (float)0.81284660, (float)0.81093609, (float)0.80901700, - (float)0.80708915, (float)0.80515265, (float)0.80320752, (float)0.80125374, (float)0.79929143, - (float)0.79732066, (float)0.79534125, (float)0.79335332, (float)0.79135686, (float)0.78935200, - (float)0.78733861, (float)0.78531694, (float)0.78328675, (float)0.78124815, (float)0.77920121, - (float)0.77714586, (float)0.77508223, (float)0.77301049, (float)0.77093029, (float)0.76884180, - (float)0.76674509, (float)0.76464021, (float)0.76252711, (float)0.76040596, (float)0.75827658, - (float)0.75613904, (float)0.75399339, (float)0.75183970, (float)0.74967796, (float)0.74750835, - (float)0.74533057, (float)0.74314481, (float)0.74095106, (float)0.73874938, (float)0.73653996, - (float)0.73432249, (float)0.73209721, (float)0.72986400, (float)0.72762305, (float)0.72537428, - (float)0.72311789, (float)0.72085363, (float)0.71858162, (float)0.71630186, (float)0.71401453, - (float)0.71171951, (float)0.70941705, (float)0.70710677, (float)0.70478898, (float)0.70246363, - (float)0.70013070, (float)0.69779032, (float)0.69544268, (float)0.69308734, (float)0.69072461, - (float)0.68835449, (float)0.68597704, (float)0.68359220, (float)0.68120021, (float)0.67880070, - (float)0.67639399, (float)0.67398006, (float)0.67155886, (float)0.66913044, (float)0.66669512, - (float)0.66425240, (float)0.66180259, (float)0.65934575, (float)0.65688181, (float)0.65441096, - (float)0.65193301, (float)0.64944804, (float)0.64695609, (float)0.64445722, (float)0.64195150, - (float)0.63943905, (float)0.63691956, (float)0.63439327, (float)0.63186014, (float)0.62932026, - (float)0.62677372, (float)0.62422055, (float)0.62166059, (float)0.61909389, (float)0.61652064, - (float)0.61394072, (float)0.61135429, (float)0.60876143, (float)0.60616189, (float)0.60355592, - (float)0.60094339, (float)0.59832448, (float)0.59569913, (float)0.59306765, (float)0.59042960, - (float)0.58778518, (float)0.58513451, (float)0.58247757, (float)0.57981461, (float)0.57714522, - (float)0.57446963, (float)0.57178789, (float)0.56910002, (float)0.56640613, (float)0.56370628, - (float)0.56100023, (float)0.55828822, (float)0.55557019, (float)0.55284619, (float)0.55011630, - (float)0.54738069, (float)0.54463905, (float)0.54189152, (float)0.53913826, (float)0.53637916, - (float)0.53361434, (float)0.53084403, (float)0.52806783, (float)0.52528596, (float)0.52249849, - (float)0.51970541, (float)0.51690674, (float)0.51410276, (float)0.51129305, (float)0.50847787, - (float)0.50565726, (float)0.50283122, (float)0.50000006, (float)0.49716327, (float)0.49432117, - (float)0.49147379, (float)0.48862115, (float)0.48576325, (float)0.48290038, (float)0.48003212, - (float)0.47715873, (float)0.47428021, (float)0.47139663, (float)0.46850798, (float)0.46561456, - (float)0.46271589, (float)0.45981231, (float)0.45690379, (float)0.45399037, (float)0.45107210, - (float)0.44814920, (float)0.44522130, (float)0.44228864, (float)0.43935123, (float)0.43640912, - (float)0.43346232, (float)0.43051112, (float)0.42755505, (float)0.42459446, (float)0.42162928, - (float)0.41865960, (float)0.41568545, (float)0.41270703, (float)0.40972400, (float)0.40673658, - (float)0.40374479, (float)0.40074870, (float)0.39774850, (float)0.39474386, (float)0.39173496, - (float)0.38872188, (float)0.38570464, (float)0.38268328, (float)0.37965804, (float)0.37662849, - (float)0.37359491, (float)0.37055734, (float)0.36751580, (float)0.36447033, (float)0.36142117, - (float)0.35836792, (float)0.35531086, (float)0.35224995, (float)0.34918529, (float)0.34611690, - (float)0.34304500, (float)0.33996922, (float)0.33688980, (float)0.33380675, (float)0.33072016, - (float)0.32763001, (float)0.32453656, (float)0.32143945, (float)0.31833887, (float)0.31523487, - (float)0.31212750, (float)0.30901679, (float)0.30590302, (float)0.30278572, (float)0.29966521, - (float)0.29654145, (float)0.29341453, (float)0.29028472, (float)0.28715155, (float)0.28401530, - (float)0.28087601, (float)0.27773371, (float)0.27458847, (float)0.27144048, (float)0.26828936, - (float)0.26513538, (float)0.26197854, (float)0.25881892, (float)0.25565651, (float)0.25249159, - (float)0.24932374, (float)0.24615324, (float)0.24298008, (float)0.23980433, (float)0.23662600, - (float)0.23344538, (float)0.23026201, (float)0.22707619, (float)0.22388794, (float)0.22069728, - (float)0.21750426, (float)0.21430916, (float)0.21111152, (float)0.20791161, (float)0.20470949, - (float)0.20150517, (float)0.19829892, (float)0.19509031, (float)0.19187963, (float)0.18866688, - (float)0.18545210, (float)0.18223536, (float)0.17901689, (float)0.17579627, (float)0.17257376, - (float)0.16934940, (float)0.16612324, (float)0.16289529, (float)0.15966584, (float)0.15643445, - (float)0.15320137, (float)0.14996666, (float)0.14673033, (float)0.14349243, (float)0.14025325, - (float)0.13701232, (float)0.13376991, (float)0.13052608, (float)0.12728085, (float)0.12403426, - (float)0.12078657, (float)0.11753736, (float)0.11428688, (float)0.11103519, (float)0.10778230, - (float)0.10452849, (float)0.10127334, (float)0.09801710, (float)0.09475980, (float)0.09150149, - (float)0.08824220, (float)0.08498220, (float)0.08172106, (float)0.07845904, (float)0.07519618, - (float)0.07193252, (float)0.06866808, (float)0.06540315, (float)0.06213728, (float)0.05887074, - (float)0.05560357, (float)0.05233581, (float)0.04906749, (float)0.04579888, (float)0.04252954, - (float)0.03925974, (float)0.03598953, (float)0.03271893, (float)0.02944798, (float)0.02617695, - (float)0.02290541, (float)0.01963361, (float)0.01636161, (float)0.01308943, (float)0.00981712, - (float)0.00654493, (float)0.00327244, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, - (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000 -}; - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_WINDOWS_PRIVATE_H_ diff --git a/webrtc/modules/audio_processing/optionally_built_submodule_creators.cc b/webrtc/modules/audio_processing/optionally_built_submodule_creators.cc new file mode 100644 index 0000000..62a1632 --- /dev/null +++ b/webrtc/modules/audio_processing/optionally_built_submodule_creators.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 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 "modules/audio_processing/optionally_built_submodule_creators.h" + +#include + +#include "modules/audio_processing/transient/transient_suppressor_impl.h" + +namespace webrtc { + +std::unique_ptr CreateTransientSuppressor( + const ApmSubmoduleCreationOverrides& overrides) { +#ifdef WEBRTC_EXCLUDE_TRANSIENT_SUPPRESSOR + return nullptr; +#else + if (overrides.transient_suppression) { + return nullptr; + } + return std::make_unique(); +#endif +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/optionally_built_submodule_creators.h b/webrtc/modules/audio_processing/optionally_built_submodule_creators.h new file mode 100644 index 0000000..c96e66f --- /dev/null +++ b/webrtc/modules/audio_processing/optionally_built_submodule_creators.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_OPTIONALLY_BUILT_SUBMODULE_CREATORS_H_ +#define MODULES_AUDIO_PROCESSING_OPTIONALLY_BUILT_SUBMODULE_CREATORS_H_ + +#include + +#include "modules/audio_processing/transient/transient_suppressor.h" + +namespace webrtc { + +// These overrides are only to be used for testing purposes. +// Each flag emulates a preprocessor macro to exclude a submodule of APM from +// the build, e.g. WEBRTC_EXCLUDE_TRANSIENT_SUPPRESSOR. If the corresponding +// flag |transient_suppression| is enabled, then the creators will return +// nullptr instead of a submodule instance, as if the macro had been defined. +struct ApmSubmoduleCreationOverrides { + bool transient_suppression = false; +}; + +// Creates a transient suppressor. +// Will instead return nullptr if one of the following is true: +// * WEBRTC_EXCLUDE_TRANSIENT_SUPPRESSOR is defined +// * The corresponding override in |overrides| is enabled. +std::unique_ptr CreateTransientSuppressor( + const ApmSubmoduleCreationOverrides& overrides); + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_OPTIONALLY_BUILT_SUBMODULE_CREATORS_H_ diff --git a/webrtc/modules/audio_processing/processing_component.cc b/webrtc/modules/audio_processing/processing_component.cc deleted file mode 100644 index 9e16d7c..0000000 --- a/webrtc/modules/audio_processing/processing_component.cc +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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 "webrtc/modules/audio_processing/processing_component.h" - -#include - -#include "webrtc/modules/audio_processing/include/audio_processing.h" - -namespace webrtc { - -ProcessingComponent::ProcessingComponent() - : initialized_(false), - enabled_(false), - num_handles_(0) {} - -ProcessingComponent::~ProcessingComponent() { - assert(initialized_ == false); -} - -int ProcessingComponent::Destroy() { - while (!handles_.empty()) { - DestroyHandle(handles_.back()); - handles_.pop_back(); - } - initialized_ = false; - - return AudioProcessing::kNoError; -} - -int ProcessingComponent::EnableComponent(bool enable) { - if (enable && !enabled_) { - enabled_ = enable; // Must be set before Initialize() is called. - - int err = Initialize(); - if (err != AudioProcessing::kNoError) { - enabled_ = false; - return err; - } - } else { - enabled_ = enable; - } - - return AudioProcessing::kNoError; -} - -bool ProcessingComponent::is_component_enabled() const { - return enabled_; -} - -void* ProcessingComponent::handle(int index) const { - assert(index < num_handles_); - return handles_[index]; -} - -int ProcessingComponent::num_handles() const { - return num_handles_; -} - -int ProcessingComponent::Initialize() { - if (!enabled_) { - return AudioProcessing::kNoError; - } - - num_handles_ = num_handles_required(); - if (num_handles_ > static_cast(handles_.size())) { - handles_.resize(num_handles_, NULL); - } - - assert(static_cast(handles_.size()) >= num_handles_); - for (int i = 0; i < num_handles_; i++) { - if (handles_[i] == NULL) { - handles_[i] = CreateHandle(); - if (handles_[i] == NULL) { - return AudioProcessing::kCreationFailedError; - } - } - - int err = InitializeHandle(handles_[i]); - if (err != AudioProcessing::kNoError) { - return GetHandleError(handles_[i]); - } - } - - initialized_ = true; - return Configure(); -} - -int ProcessingComponent::Configure() { - if (!initialized_) { - return AudioProcessing::kNoError; - } - - assert(static_cast(handles_.size()) >= num_handles_); - for (int i = 0; i < num_handles_; i++) { - int err = ConfigureHandle(handles_[i]); - if (err != AudioProcessing::kNoError) { - return GetHandleError(handles_[i]); - } - } - - return AudioProcessing::kNoError; -} -} // namespace webrtc diff --git a/webrtc/modules/audio_processing/processing_component.h b/webrtc/modules/audio_processing/processing_component.h deleted file mode 100644 index 8ee3ac6..0000000 --- a/webrtc/modules/audio_processing/processing_component.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_PROCESSING_COMPONENT_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_PROCESSING_COMPONENT_H_ - -#include - -#include "webrtc/common.h" - -namespace webrtc { - -class ProcessingComponent { - public: - ProcessingComponent(); - virtual ~ProcessingComponent(); - - virtual int Initialize(); - virtual void SetExtraOptions(const Config& config) {} - virtual int Destroy(); - - bool is_component_enabled() const; - - protected: - virtual int Configure(); - int EnableComponent(bool enable); - void* handle(int index) const; - int num_handles() const; - - private: - virtual void* CreateHandle() const = 0; - virtual int InitializeHandle(void* handle) const = 0; - virtual int ConfigureHandle(void* handle) const = 0; - virtual void DestroyHandle(void* handle) const = 0; - virtual int num_handles_required() const = 0; - virtual int GetHandleError(void* handle) const = 0; - - std::vector handles_; - bool initialized_; - bool enabled_; - int num_handles_; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_PROCESSING_COMPONENT_H__ diff --git a/webrtc/modules/audio_processing/render_queue_item_verifier.h b/webrtc/modules/audio_processing/render_queue_item_verifier.h new file mode 100644 index 0000000..b8aff4a --- /dev/null +++ b/webrtc/modules/audio_processing/render_queue_item_verifier.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_RENDER_QUEUE_ITEM_VERIFIER_H_ +#define MODULES_AUDIO_PROCESSING_RENDER_QUEUE_ITEM_VERIFIER_H_ + +#include + +namespace webrtc { + +// Functor to use when supplying a verifier function for the queue item +// verifcation. +template +class RenderQueueItemVerifier { + public: + explicit RenderQueueItemVerifier(size_t minimum_capacity) + : minimum_capacity_(minimum_capacity) {} + + bool operator()(const std::vector& v) const { + return v.capacity() >= minimum_capacity_; + } + + private: + size_t minimum_capacity_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_RENDER_QUEUE_ITEM_VERIFIER_H__ diff --git a/webrtc/modules/audio_processing/residual_echo_detector.cc b/webrtc/modules/audio_processing/residual_echo_detector.cc new file mode 100644 index 0000000..6188883 --- /dev/null +++ b/webrtc/modules/audio_processing/residual_echo_detector.cc @@ -0,0 +1,215 @@ +/* + * 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 "modules/audio_processing/residual_echo_detector.h" + +#include +#include + +#include "absl/types/optional.h" +#include "modules/audio_processing/audio_buffer.h" +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/metrics.h" + +namespace { + +float Power(rtc::ArrayView input) { + if (input.empty()) { + return 0.f; + } + return std::inner_product(input.begin(), input.end(), input.begin(), 0.f) / + input.size(); +} + +constexpr size_t kLookbackFrames = 650; +// TODO(ivoc): Verify the size of this buffer. +constexpr size_t kRenderBufferSize = 30; +constexpr float kAlpha = 0.001f; +// 10 seconds of data, updated every 10 ms. +constexpr size_t kAggregationBufferSize = 10 * 100; + +} // namespace + +namespace webrtc { + +int ResidualEchoDetector::instance_count_ = 0; + +ResidualEchoDetector::ResidualEchoDetector() + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + render_buffer_(kRenderBufferSize), + render_power_(kLookbackFrames), + render_power_mean_(kLookbackFrames), + render_power_std_dev_(kLookbackFrames), + covariances_(kLookbackFrames), + recent_likelihood_max_(kAggregationBufferSize) {} + +ResidualEchoDetector::~ResidualEchoDetector() = default; + +void ResidualEchoDetector::AnalyzeRenderAudio( + rtc::ArrayView render_audio) { + // Dump debug data assuming 48 kHz sample rate (if this assumption is not + // valid the dumped audio will need to be converted offline accordingly). + data_dumper_->DumpWav("ed_render", render_audio.size(), render_audio.data(), + 48000, 1); + + if (render_buffer_.Size() == 0) { + frames_since_zero_buffer_size_ = 0; + } else if (frames_since_zero_buffer_size_ >= kRenderBufferSize) { + // This can happen in a few cases: at the start of a call, due to a glitch + // or due to clock drift. The excess capture value will be ignored. + // TODO(ivoc): Include how often this happens in APM stats. + render_buffer_.Pop(); + frames_since_zero_buffer_size_ = 0; + } + ++frames_since_zero_buffer_size_; + float power = Power(render_audio); + render_buffer_.Push(power); +} + +void ResidualEchoDetector::AnalyzeCaptureAudio( + rtc::ArrayView capture_audio) { + // Dump debug data assuming 48 kHz sample rate (if this assumption is not + // valid the dumped audio will need to be converted offline accordingly). + data_dumper_->DumpWav("ed_capture", capture_audio.size(), + capture_audio.data(), 48000, 1); + + if (first_process_call_) { + // On the first process call (so the start of a call), we must flush the + // render buffer, otherwise the render data will be delayed. + render_buffer_.Clear(); + first_process_call_ = false; + } + + // Get the next render value. + const absl::optional buffered_render_power = render_buffer_.Pop(); + if (!buffered_render_power) { + // This can happen in a few cases: at the start of a call, due to a glitch + // or due to clock drift. The excess capture value will be ignored. + // TODO(ivoc): Include how often this happens in APM stats. + return; + } + // Update the render statistics, and store the statistics in circular buffers. + render_statistics_.Update(*buffered_render_power); + RTC_DCHECK_LT(next_insertion_index_, kLookbackFrames); + render_power_[next_insertion_index_] = *buffered_render_power; + render_power_mean_[next_insertion_index_] = render_statistics_.mean(); + render_power_std_dev_[next_insertion_index_] = + render_statistics_.std_deviation(); + + // Get the next capture value, update capture statistics and add the relevant + // values to the buffers. + const float capture_power = Power(capture_audio); + capture_statistics_.Update(capture_power); + const float capture_mean = capture_statistics_.mean(); + const float capture_std_deviation = capture_statistics_.std_deviation(); + + // Update the covariance values and determine the new echo likelihood. + echo_likelihood_ = 0.f; + size_t read_index = next_insertion_index_; + + int best_delay = -1; + for (size_t delay = 0; delay < covariances_.size(); ++delay) { + RTC_DCHECK_LT(read_index, render_power_.size()); + covariances_[delay].Update(capture_power, capture_mean, + capture_std_deviation, render_power_[read_index], + render_power_mean_[read_index], + render_power_std_dev_[read_index]); + read_index = read_index > 0 ? read_index - 1 : kLookbackFrames - 1; + + if (covariances_[delay].normalized_cross_correlation() > echo_likelihood_) { + echo_likelihood_ = covariances_[delay].normalized_cross_correlation(); + best_delay = static_cast(delay); + } + } + // This is a temporary log message to help find the underlying cause for echo + // likelihoods > 1.0. + // TODO(ivoc): Remove once the issue is resolved. + if (echo_likelihood_ > 1.1f) { + // Make sure we don't spam the log. + if (log_counter_ < 5 && best_delay != -1) { + size_t read_index = kLookbackFrames + next_insertion_index_ - best_delay; + if (read_index >= kLookbackFrames) { + read_index -= kLookbackFrames; + } + RTC_DCHECK_LT(read_index, render_power_.size()); + RTC_LOG_F(LS_ERROR) << "Echo detector internal state: {" + "Echo likelihood: " + << echo_likelihood_ << ", Best Delay: " << best_delay + << ", Covariance: " + << covariances_[best_delay].covariance() + << ", Last capture power: " << capture_power + << ", Capture mean: " << capture_mean + << ", Capture_standard deviation: " + << capture_std_deviation << ", Last render power: " + << render_power_[read_index] + << ", Render mean: " << render_power_mean_[read_index] + << ", Render standard deviation: " + << render_power_std_dev_[read_index] + << ", Reliability: " << reliability_ << "}"; + log_counter_++; + } + } + RTC_DCHECK_LT(echo_likelihood_, 1.1f); + + reliability_ = (1.0f - kAlpha) * reliability_ + kAlpha * 1.0f; + echo_likelihood_ *= reliability_; + // This is a temporary fix to prevent echo likelihood values > 1.0. + // TODO(ivoc): Find the root cause of this issue and fix it. + echo_likelihood_ = std::min(echo_likelihood_, 1.0f); + int echo_percentage = static_cast(echo_likelihood_ * 100); + RTC_HISTOGRAM_COUNTS("WebRTC.Audio.ResidualEchoDetector.EchoLikelihood", + echo_percentage, 0, 100, 100 /* number of bins */); + + // Update the buffer of recent likelihood values. + recent_likelihood_max_.Update(echo_likelihood_); + + // Update the next insertion index. + next_insertion_index_ = next_insertion_index_ < (kLookbackFrames - 1) + ? next_insertion_index_ + 1 + : 0; +} + +void ResidualEchoDetector::Initialize(int /*capture_sample_rate_hz*/, + int /*num_capture_channels*/, + int /*render_sample_rate_hz*/, + int /*num_render_channels*/) { + render_buffer_.Clear(); + std::fill(render_power_.begin(), render_power_.end(), 0.f); + std::fill(render_power_mean_.begin(), render_power_mean_.end(), 0.f); + std::fill(render_power_std_dev_.begin(), render_power_std_dev_.end(), 0.f); + render_statistics_.Clear(); + capture_statistics_.Clear(); + recent_likelihood_max_.Clear(); + for (auto& cov : covariances_) { + cov.Clear(); + } + echo_likelihood_ = 0.f; + next_insertion_index_ = 0; + reliability_ = 0.f; +} + +void EchoDetector::PackRenderAudioBuffer(AudioBuffer* audio, + std::vector* packed_buffer) { + packed_buffer->clear(); + packed_buffer->insert(packed_buffer->end(), audio->channels()[0], + audio->channels()[0] + audio->num_frames()); +} + +EchoDetector::Metrics ResidualEchoDetector::GetMetrics() const { + EchoDetector::Metrics metrics; + metrics.echo_likelihood = echo_likelihood_; + metrics.echo_likelihood_recent_max = recent_likelihood_max_.max(); + return metrics; +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/residual_echo_detector.h b/webrtc/modules/audio_processing/residual_echo_detector.h new file mode 100644 index 0000000..5d18ecb --- /dev/null +++ b/webrtc/modules/audio_processing/residual_echo_detector.h @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_RESIDUAL_ECHO_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_RESIDUAL_ECHO_DETECTOR_H_ + +#include + +#include "api/array_view.h" +#include "modules/audio_processing/echo_detector/circular_buffer.h" +#include "modules/audio_processing/echo_detector/mean_variance_estimator.h" +#include "modules/audio_processing/echo_detector/moving_max.h" +#include "modules/audio_processing/echo_detector/normalized_covariance_estimator.h" +#include "modules/audio_processing/include/audio_processing.h" + +namespace webrtc { + +class ApmDataDumper; +class AudioBuffer; + +class ResidualEchoDetector : public EchoDetector { + public: + ResidualEchoDetector(); + ~ResidualEchoDetector() override; + + // This function should be called while holding the render lock. + void AnalyzeRenderAudio(rtc::ArrayView render_audio) override; + + // This function should be called while holding the capture lock. + void AnalyzeCaptureAudio(rtc::ArrayView capture_audio) override; + + // This function should be called while holding the capture lock. + void Initialize(int capture_sample_rate_hz, + int num_capture_channels, + int render_sample_rate_hz, + int num_render_channels) override; + + // This function is for testing purposes only. + void SetReliabilityForTest(float value) { reliability_ = value; } + + // This function should be called while holding the capture lock. + EchoDetector::Metrics GetMetrics() const override; + + private: + static int instance_count_; + std::unique_ptr data_dumper_; + // Keep track if the |Process| function has been previously called. + bool first_process_call_ = true; + // Buffer for storing the power of incoming farend buffers. This is needed for + // cases where calls to BufferFarend and Process are jittery. + CircularBuffer render_buffer_; + // Count how long ago it was that the size of |render_buffer_| was zero. This + // value is also reset to zero when clock drift is detected and a value from + // the renderbuffer is discarded, even though the buffer is not actually zero + // at that point. This is done to avoid repeatedly removing elements in this + // situation. + size_t frames_since_zero_buffer_size_ = 0; + + // Circular buffers containing delayed versions of the power, mean and + // standard deviation, for calculating the delayed covariance values. + std::vector render_power_; + std::vector render_power_mean_; + std::vector render_power_std_dev_; + // Covariance estimates for different delay values. + std::vector covariances_; + // Index where next element should be inserted in all of the above circular + // buffers. + size_t next_insertion_index_ = 0; + + MeanVarianceEstimator render_statistics_; + MeanVarianceEstimator capture_statistics_; + // Current echo likelihood. + float echo_likelihood_ = 0.f; + // Reliability of the current likelihood. + float reliability_ = 0.f; + MovingMax recent_likelihood_max_; + + int log_counter_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_RESIDUAL_ECHO_DETECTOR_H_ diff --git a/webrtc/modules/audio_processing/rms_level.cc b/webrtc/modules/audio_processing/rms_level.cc index 70c4422..6992a15 100644 --- a/webrtc/modules/audio_processing/rms_level.cc +++ b/webrtc/modules/audio_processing/rms_level.cc @@ -8,54 +8,121 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/rms_level.h" +#include "modules/audio_processing/rms_level.h" -#include -#include +#include +#include +#include + +#include "rtc_base/checks.h" namespace webrtc { +namespace { +static constexpr float kMaxSquaredLevel = 32768 * 32768; +// kMinLevel is the level corresponding to kMinLevelDb, that is 10^(-127/10). +static constexpr float kMinLevel = 1.995262314968883e-13f; -static const float kMaxSquaredLevel = 32768 * 32768; - -RMSLevel::RMSLevel() - : sum_square_(0), - sample_count_(0) {} - -RMSLevel::~RMSLevel() {} - -void RMSLevel::Reset() { - sum_square_ = 0; - sample_count_ = 0; -} - -void RMSLevel::Process(const int16_t* data, size_t length) { - for (size_t i = 0; i < length; ++i) { - sum_square_ += data[i] * data[i]; +// Calculates the normalized RMS value from a mean square value. The input +// should be the sum of squared samples divided by the number of samples. The +// value will be normalized to full range before computing the RMS, wich is +// returned as a negated dBfs. That is, 0 is full amplitude while 127 is very +// faint. +int ComputeRms(float mean_square) { + if (mean_square <= kMinLevel * kMaxSquaredLevel) { + // Very faint; simply return the minimum value. + return RmsLevel::kMinLevelDb; } - sample_count_ += length; -} - -void RMSLevel::ProcessMuted(size_t length) { - sample_count_ += length; -} - -int RMSLevel::RMS() { - if (sample_count_ == 0 || sum_square_ == 0) { - Reset(); - return kMinLevel; - } - // Normalize by the max level. - float rms = sum_square_ / (sample_count_ * kMaxSquaredLevel); + const float mean_square_norm = mean_square / kMaxSquaredLevel; + RTC_DCHECK_GT(mean_square_norm, kMinLevel); // 20log_10(x^0.5) = 10log_10(x) - rms = 10 * log10(rms); - assert(rms <= 0); - if (rms < -kMinLevel) - rms = -kMinLevel; + const float rms = 10.f * std::log10(mean_square_norm); + RTC_DCHECK_LE(rms, 0.f); + RTC_DCHECK_GT(rms, -RmsLevel::kMinLevelDb); + // Return the negated value. + return static_cast(-rms + 0.5f); +} +} // namespace - rms = -rms; +RmsLevel::RmsLevel() { Reset(); - return static_cast(rms + 0.5); } +RmsLevel::~RmsLevel() = default; + +void RmsLevel::Reset() { + sum_square_ = 0.f; + sample_count_ = 0; + max_sum_square_ = 0.f; + block_size_ = absl::nullopt; +} + +void RmsLevel::Analyze(rtc::ArrayView data) { + if (data.empty()) { + return; + } + + CheckBlockSize(data.size()); + + const float sum_square = + std::accumulate(data.begin(), data.end(), 0.f, + [](float a, int16_t b) { return a + b * b; }); + RTC_DCHECK_GE(sum_square, 0.f); + sum_square_ += sum_square; + sample_count_ += data.size(); + + max_sum_square_ = std::max(max_sum_square_, sum_square); +} + +void RmsLevel::Analyze(rtc::ArrayView data) { + if (data.empty()) { + return; + } + + CheckBlockSize(data.size()); + + float sum_square = 0.f; + + for (float data_k : data) { + int16_t tmp = + static_cast(std::min(std::max(data_k, -32768.f), 32767.f)); + sum_square += tmp * tmp; + } + RTC_DCHECK_GE(sum_square, 0.f); + sum_square_ += sum_square; + sample_count_ += data.size(); + + max_sum_square_ = std::max(max_sum_square_, sum_square); +} + +void RmsLevel::AnalyzeMuted(size_t length) { + CheckBlockSize(length); + sample_count_ += length; +} + +int RmsLevel::Average() { + int rms = (sample_count_ == 0) ? RmsLevel::kMinLevelDb + : ComputeRms(sum_square_ / sample_count_); + Reset(); + return rms; +} + +RmsLevel::Levels RmsLevel::AverageAndPeak() { + // Note that block_size_ should by design always be non-empty when + // sample_count_ != 0. Also, the * operator of absl::optional enforces this + // with a DCHECK. + Levels levels = (sample_count_ == 0) + ? Levels{RmsLevel::kMinLevelDb, RmsLevel::kMinLevelDb} + : Levels{ComputeRms(sum_square_ / sample_count_), + ComputeRms(max_sum_square_ / *block_size_)}; + Reset(); + return levels; +} + +void RmsLevel::CheckBlockSize(size_t block_size) { + if (block_size_ != block_size) { + Reset(); + block_size_ = block_size; + } +} } // namespace webrtc diff --git a/webrtc/modules/audio_processing/rms_level.h b/webrtc/modules/audio_processing/rms_level.h index 12fa212..e1a6d56 100644 --- a/webrtc/modules/audio_processing/rms_level.h +++ b/webrtc/modules/audio_processing/rms_level.h @@ -8,12 +8,14 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ +#ifndef MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ +#define MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ -#include +#include +#include -#include "webrtc/typedefs.h" +#include "absl/types/optional.h" +#include "api/array_view.h" namespace webrtc { @@ -23,37 +25,53 @@ namespace webrtc { // with the intent that it can provide the RTP audio level indication. // // The expected approach is to provide constant-sized chunks of audio to -// Process(). When enough chunks have been accumulated to form a packet, call -// RMS() to get the audio level indicator for the RTP header. -class RMSLevel { +// Analyze(). When enough chunks have been accumulated to form a packet, call +// Average() to get the audio level indicator for the RTP header. +class RmsLevel { public: - static const int kMinLevel = 127; + struct Levels { + int average; + int peak; + }; - RMSLevel(); - ~RMSLevel(); + enum : int { kMinLevelDb = 127 }; + + RmsLevel(); + ~RmsLevel(); // Can be called to reset internal states, but is not required during normal // operation. void Reset(); - // Pass each chunk of audio to Process() to accumulate the level. - void Process(const int16_t* data, size_t length); + // Pass each chunk of audio to Analyze() to accumulate the level. + void Analyze(rtc::ArrayView data); + void Analyze(rtc::ArrayView data); // If all samples with the given |length| have a magnitude of zero, this is // a shortcut to avoid some computation. - void ProcessMuted(size_t length); + void AnalyzeMuted(size_t length); - // Computes the RMS level over all data passed to Process() since the last - // call to RMS(). The returned value is positive but should be interpreted as - // negative as per the RFC. It is constrained to [0, 127]. - int RMS(); + // Computes the RMS level over all data passed to Analyze() since the last + // call to Average(). The returned value is positive but should be interpreted + // as negative as per the RFC. It is constrained to [0, 127]. Resets the + // internal state to start a new measurement period. + int Average(); + + // Like Average() above, but also returns the RMS peak value. Resets the + // internal state to start a new measurement period. + Levels AverageAndPeak(); private: + // Compares |block_size| with |block_size_|. If they are different, calls + // Reset() and stores the new size. + void CheckBlockSize(size_t block_size); + float sum_square_; size_t sample_count_; + float max_sum_square_; + absl::optional block_size_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ - +#endif // MODULES_AUDIO_PROCESSING_RMS_LEVEL_H_ diff --git a/webrtc/modules/audio_processing/splitting_filter.cc b/webrtc/modules/audio_processing/splitting_filter.cc index 60427e2..d47090b 100644 --- a/webrtc/modules/audio_processing/splitting_filter.cc +++ b/webrtc/modules/audio_processing/splitting_filter.cc @@ -8,30 +8,36 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/splitting_filter.h" +#include "modules/audio_processing/splitting_filter.h" -#include "webrtc/base/checks.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/common_audio/channel_buffer.h" +#include + +#include "api/array_view.h" +#include "common_audio/channel_buffer.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "rtc_base/checks.h" namespace webrtc { +namespace { -SplittingFilter::SplittingFilter(int num_channels, +constexpr size_t kSamplesPerBand = 160; +constexpr size_t kTwoBandFilterSamplesPerFrame = 320; + +} // namespace + +SplittingFilter::SplittingFilter(size_t num_channels, size_t num_bands, size_t num_frames) - : num_bands_(num_bands) { + : num_bands_(num_bands), + two_bands_states_(num_bands_ == 2 ? num_channels : 0), + three_band_filter_banks_(num_bands_ == 3 ? num_channels : 0) { RTC_CHECK(num_bands_ == 2 || num_bands_ == 3); - if (num_bands_ == 2) { - two_bands_states_.resize(num_channels); - } else if (num_bands_ == 3) { - for (int i = 0; i < num_channels; ++i) { - three_band_filter_banks_.push_back(new ThreeBandFilterBank(num_frames)); - } - } } -void SplittingFilter::Analysis(const IFChannelBuffer* data, - IFChannelBuffer* bands) { +SplittingFilter::~SplittingFilter() = default; + +void SplittingFilter::Analysis(const ChannelBuffer* data, + ChannelBuffer* bands) { RTC_DCHECK_EQ(num_bands_, bands->num_bands()); RTC_DCHECK_EQ(data->num_channels(), bands->num_channels()); RTC_DCHECK_EQ(data->num_frames(), @@ -43,8 +49,8 @@ void SplittingFilter::Analysis(const IFChannelBuffer* data, } } -void SplittingFilter::Synthesis(const IFChannelBuffer* bands, - IFChannelBuffer* data) { +void SplittingFilter::Synthesis(const ChannelBuffer* bands, + ChannelBuffer* data) { RTC_DCHECK_EQ(num_bands_, bands->num_bands()); RTC_DCHECK_EQ(data->num_channels(), bands->num_channels()); RTC_DCHECK_EQ(data->num_frames(), @@ -56,53 +62,82 @@ void SplittingFilter::Synthesis(const IFChannelBuffer* bands, } } -void SplittingFilter::TwoBandsAnalysis(const IFChannelBuffer* data, - IFChannelBuffer* bands) { - RTC_DCHECK_EQ(static_cast(two_bands_states_.size()), - data->num_channels()); +void SplittingFilter::TwoBandsAnalysis(const ChannelBuffer* data, + ChannelBuffer* bands) { + RTC_DCHECK_EQ(two_bands_states_.size(), data->num_channels()); + RTC_DCHECK_EQ(data->num_frames(), kTwoBandFilterSamplesPerFrame); + for (size_t i = 0; i < two_bands_states_.size(); ++i) { - WebRtcSpl_AnalysisQMF(data->ibuf_const()->channels()[i], - data->num_frames(), - bands->ibuf()->channels(0)[i], - bands->ibuf()->channels(1)[i], + std::array, 2> bands16; + std::array full_band16; + FloatS16ToS16(data->channels(0)[i], full_band16.size(), full_band16.data()); + WebRtcSpl_AnalysisQMF(full_band16.data(), data->num_frames(), + bands16[0].data(), bands16[1].data(), two_bands_states_[i].analysis_state1, two_bands_states_[i].analysis_state2); + S16ToFloatS16(bands16[0].data(), bands16[0].size(), bands->channels(0)[i]); + S16ToFloatS16(bands16[1].data(), bands16[1].size(), bands->channels(1)[i]); } } -void SplittingFilter::TwoBandsSynthesis(const IFChannelBuffer* bands, - IFChannelBuffer* data) { - RTC_DCHECK_EQ(static_cast(two_bands_states_.size()), - data->num_channels()); - for (size_t i = 0; i < two_bands_states_.size(); ++i) { - WebRtcSpl_SynthesisQMF(bands->ibuf_const()->channels(0)[i], - bands->ibuf_const()->channels(1)[i], - bands->num_frames_per_band(), - data->ibuf()->channels()[i], +void SplittingFilter::TwoBandsSynthesis(const ChannelBuffer* bands, + ChannelBuffer* data) { + RTC_DCHECK_LE(data->num_channels(), two_bands_states_.size()); + RTC_DCHECK_EQ(data->num_frames(), kTwoBandFilterSamplesPerFrame); + for (size_t i = 0; i < data->num_channels(); ++i) { + std::array, 2> bands16; + std::array full_band16; + FloatS16ToS16(bands->channels(0)[i], bands16[0].size(), bands16[0].data()); + FloatS16ToS16(bands->channels(1)[i], bands16[1].size(), bands16[1].data()); + WebRtcSpl_SynthesisQMF(bands16[0].data(), bands16[1].data(), + bands->num_frames_per_band(), full_band16.data(), two_bands_states_[i].synthesis_state1, two_bands_states_[i].synthesis_state2); + S16ToFloatS16(full_band16.data(), full_band16.size(), data->channels(0)[i]); } } -void SplittingFilter::ThreeBandsAnalysis(const IFChannelBuffer* data, - IFChannelBuffer* bands) { - RTC_DCHECK_EQ(static_cast(three_band_filter_banks_.size()), - data->num_channels()); +void SplittingFilter::ThreeBandsAnalysis(const ChannelBuffer* data, + ChannelBuffer* bands) { + RTC_DCHECK_EQ(three_band_filter_banks_.size(), data->num_channels()); + RTC_DCHECK_LE(data->num_channels(), three_band_filter_banks_.size()); + RTC_DCHECK_LE(data->num_channels(), bands->num_channels()); + RTC_DCHECK_EQ(data->num_frames(), ThreeBandFilterBank::kFullBandSize); + RTC_DCHECK_EQ(bands->num_frames(), ThreeBandFilterBank::kFullBandSize); + RTC_DCHECK_EQ(bands->num_bands(), ThreeBandFilterBank::kNumBands); + RTC_DCHECK_EQ(bands->num_frames_per_band(), + ThreeBandFilterBank::kSplitBandSize); + for (size_t i = 0; i < three_band_filter_banks_.size(); ++i) { - three_band_filter_banks_[i]->Analysis(data->fbuf_const()->channels()[i], - data->num_frames(), - bands->fbuf()->bands(i)); + three_band_filter_banks_[i].Analysis( + rtc::ArrayView( + data->channels_view()[i].data(), + ThreeBandFilterBank::kFullBandSize), + rtc::ArrayView, + ThreeBandFilterBank::kNumBands>( + bands->bands_view(i).data(), ThreeBandFilterBank::kNumBands)); } } -void SplittingFilter::ThreeBandsSynthesis(const IFChannelBuffer* bands, - IFChannelBuffer* data) { - RTC_DCHECK_EQ(static_cast(three_band_filter_banks_.size()), - data->num_channels()); - for (size_t i = 0; i < three_band_filter_banks_.size(); ++i) { - three_band_filter_banks_[i]->Synthesis(bands->fbuf_const()->bands(i), - bands->num_frames_per_band(), - data->fbuf()->channels()[i]); +void SplittingFilter::ThreeBandsSynthesis(const ChannelBuffer* bands, + ChannelBuffer* data) { + RTC_DCHECK_LE(data->num_channels(), three_band_filter_banks_.size()); + RTC_DCHECK_LE(data->num_channels(), bands->num_channels()); + RTC_DCHECK_LE(data->num_channels(), three_band_filter_banks_.size()); + RTC_DCHECK_EQ(data->num_frames(), ThreeBandFilterBank::kFullBandSize); + RTC_DCHECK_EQ(bands->num_frames(), ThreeBandFilterBank::kFullBandSize); + RTC_DCHECK_EQ(bands->num_bands(), ThreeBandFilterBank::kNumBands); + RTC_DCHECK_EQ(bands->num_frames_per_band(), + ThreeBandFilterBank::kSplitBandSize); + + for (size_t i = 0; i < data->num_channels(); ++i) { + three_band_filter_banks_[i].Synthesis( + rtc::ArrayView, + ThreeBandFilterBank::kNumBands>( + bands->bands_view(i).data(), ThreeBandFilterBank::kNumBands), + rtc::ArrayView( + data->channels_view()[i].data(), + ThreeBandFilterBank::kFullBandSize)); } } diff --git a/webrtc/modules/audio_processing/splitting_filter.h b/webrtc/modules/audio_processing/splitting_filter.h index 4698d3f..e578dd0 100644 --- a/webrtc/modules/audio_processing/splitting_filter.h +++ b/webrtc/modules/audio_processing/splitting_filter.h @@ -8,19 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_SPLITTING_FILTER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_SPLITTING_FILTER_H_ +#ifndef MODULES_AUDIO_PROCESSING_SPLITTING_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_SPLITTING_FILTER_H_ #include +#include #include -#include "webrtc/modules/audio_processing/three_band_filter_bank.h" -#include "webrtc/system_wrappers/include/scoped_vector.h" +#include "common_audio/channel_buffer.h" +#include "modules/audio_processing/three_band_filter_bank.h" namespace webrtc { -class IFChannelBuffer; - struct TwoBandsStates { TwoBandsStates() { memset(analysis_state1, 0, sizeof(analysis_state1)); @@ -41,28 +40,33 @@ struct TwoBandsStates { // // For each block, Analysis() is called to split into bands and then Synthesis() // to merge these bands again. The input and output signals are contained in -// IFChannelBuffers and for the different bands an array of IFChannelBuffers is +// ChannelBuffers and for the different bands an array of ChannelBuffers is // used. class SplittingFilter { public: - SplittingFilter(int num_channels, size_t num_bands, size_t num_frames); + SplittingFilter(size_t num_channels, size_t num_bands, size_t num_frames); + ~SplittingFilter(); - void Analysis(const IFChannelBuffer* data, IFChannelBuffer* bands); - void Synthesis(const IFChannelBuffer* bands, IFChannelBuffer* data); + void Analysis(const ChannelBuffer* data, ChannelBuffer* bands); + void Synthesis(const ChannelBuffer* bands, ChannelBuffer* data); private: // Two-band analysis and synthesis work for 640 samples or less. - void TwoBandsAnalysis(const IFChannelBuffer* data, IFChannelBuffer* bands); - void TwoBandsSynthesis(const IFChannelBuffer* bands, IFChannelBuffer* data); - void ThreeBandsAnalysis(const IFChannelBuffer* data, IFChannelBuffer* bands); - void ThreeBandsSynthesis(const IFChannelBuffer* bands, IFChannelBuffer* data); + void TwoBandsAnalysis(const ChannelBuffer* data, + ChannelBuffer* bands); + void TwoBandsSynthesis(const ChannelBuffer* bands, + ChannelBuffer* data); + void ThreeBandsAnalysis(const ChannelBuffer* data, + ChannelBuffer* bands); + void ThreeBandsSynthesis(const ChannelBuffer* bands, + ChannelBuffer* data); void InitBuffers(); const size_t num_bands_; std::vector two_bands_states_; - ScopedVector three_band_filter_banks_; + std::vector three_band_filter_banks_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_SPLITTING_FILTER_H_ +#endif // MODULES_AUDIO_PROCESSING_SPLITTING_FILTER_H_ diff --git a/webrtc/modules/audio_processing/three_band_filter_bank.cc b/webrtc/modules/audio_processing/three_band_filter_bank.cc index 91e58df..2a7d272 100644 --- a/webrtc/modules/audio_processing/three_band_filter_bank.cc +++ b/webrtc/modules/audio_processing/three_band_filter_bank.cc @@ -30,37 +30,33 @@ // // A similar logic can be applied to the synthesis stage. -// MSVC++ requires this to be set before any other includes to get M_PI. -#define _USE_MATH_DEFINES +#include "modules/audio_processing/three_band_filter_bank.h" -#include "webrtc/modules/audio_processing/three_band_filter_bank.h" +#include -#include - -#include "webrtc/base/checks.h" +#include "rtc_base/checks.h" namespace webrtc { namespace { -const size_t kNumBands = 3; -const size_t kSparsity = 4; - -// Factors to take into account when choosing |kNumCoeffs|: -// 1. Higher |kNumCoeffs|, means faster transition, which ensures less +// Factors to take into account when choosing |kFilterSize|: +// 1. Higher |kFilterSize|, means faster transition, which ensures less // aliasing. This is especially important when there is non-linear // processing between the splitting and merging. // 2. The delay that this filter bank introduces is -// |kNumBands| * |kSparsity| * |kNumCoeffs| / 2, so it increases linearly -// with |kNumCoeffs|. -// 3. The computation complexity also increases linearly with |kNumCoeffs|. -const size_t kNumCoeffs = 4; +// |kNumBands| * |kSparsity| * |kFilterSize| / 2, so it increases linearly +// with |kFilterSize|. +// 3. The computation complexity also increases linearly with |kFilterSize|. -// The Matlab code to generate these |kLowpassCoeffs| is: +// The Matlab code to generate these |kFilterCoeffs| is: // -// N = kNumBands * kSparsity * kNumCoeffs - 1; +// N = kNumBands * kSparsity * kFilterSize - 1; // h = fir1(N, 1 / (2 * kNumBands), kaiser(N + 1, 3.5)); -// reshape(h, kNumBands * kSparsity, kNumCoeffs); +// reshape(h, kNumBands * kSparsity, kFilterSize); // +// The code below uses the values of kFilterSize, kNumBands and kSparsity +// specified in the header. + // Because the total bandwidth of the lower and higher band is double the middle // one (because of the spectrum parity), the low-pass prototype is half the // bandwidth of 1 / (2 * |kNumBands|) and is then shifted with cosine modulation @@ -68,39 +64,84 @@ const size_t kNumCoeffs = 4; // A Kaiser window is used because of its flexibility and the alpha is set to // 3.5, since that sets a stop band attenuation of 40dB ensuring a fast // transition. -const float kLowpassCoeffs[kNumBands * kSparsity][kNumCoeffs] = - {{-0.00047749f, -0.00496888f, +0.16547118f, +0.00425496f}, - {-0.00173287f, -0.01585778f, +0.14989004f, +0.00994113f}, - {-0.00304815f, -0.02536082f, +0.12154542f, +0.01157993f}, - {-0.00383509f, -0.02982767f, +0.08543175f, +0.00983212f}, - {-0.00346946f, -0.02587886f, +0.04760441f, +0.00607594f}, - {-0.00154717f, -0.01136076f, +0.01387458f, +0.00186353f}, - {+0.00186353f, +0.01387458f, -0.01136076f, -0.00154717f}, - {+0.00607594f, +0.04760441f, -0.02587886f, -0.00346946f}, - {+0.00983212f, +0.08543175f, -0.02982767f, -0.00383509f}, - {+0.01157993f, +0.12154542f, -0.02536082f, -0.00304815f}, - {+0.00994113f, +0.14989004f, -0.01585778f, -0.00173287f}, - {+0.00425496f, +0.16547118f, -0.00496888f, -0.00047749f}}; -// Downsamples |in| into |out|, taking one every |kNumbands| starting from -// |offset|. |split_length| is the |out| length. |in| has to be at least -// |kNumBands| * |split_length| long. -void Downsample(const float* in, - size_t split_length, - size_t offset, - float* out) { - for (size_t i = 0; i < split_length; ++i) { - out[i] = in[kNumBands * i + offset]; - } -} +constexpr int kSubSampling = ThreeBandFilterBank::kNumBands; +constexpr int kDctSize = ThreeBandFilterBank::kNumBands; +static_assert(ThreeBandFilterBank::kNumBands * + ThreeBandFilterBank::kSplitBandSize == + ThreeBandFilterBank::kFullBandSize, + "The full band must be split in equally sized subbands"); -// Upsamples |in| into |out|, scaling by |kNumBands| and accumulating it every -// |kNumBands| starting from |offset|. |split_length| is the |in| length. |out| -// has to be at least |kNumBands| * |split_length| long. -void Upsample(const float* in, size_t split_length, size_t offset, float* out) { - for (size_t i = 0; i < split_length; ++i) { - out[kNumBands * i + offset] += kNumBands * in[i]; +const float + kFilterCoeffs[ThreeBandFilterBank::kNumNonZeroFilters][kFilterSize] = { + {-0.00047749f, -0.00496888f, +0.16547118f, +0.00425496f}, + {-0.00173287f, -0.01585778f, +0.14989004f, +0.00994113f}, + {-0.00304815f, -0.02536082f, +0.12154542f, +0.01157993f}, + {-0.00346946f, -0.02587886f, +0.04760441f, +0.00607594f}, + {-0.00154717f, -0.01136076f, +0.01387458f, +0.00186353f}, + {+0.00186353f, +0.01387458f, -0.01136076f, -0.00154717f}, + {+0.00607594f, +0.04760441f, -0.02587886f, -0.00346946f}, + {+0.00983212f, +0.08543175f, -0.02982767f, -0.00383509f}, + {+0.00994113f, +0.14989004f, -0.01585778f, -0.00173287f}, + {+0.00425496f, +0.16547118f, -0.00496888f, -0.00047749f}}; + +constexpr int kZeroFilterIndex1 = 3; +constexpr int kZeroFilterIndex2 = 9; + +const float kDctModulation[ThreeBandFilterBank::kNumNonZeroFilters][kDctSize] = + {{2.f, 2.f, 2.f}, + {1.73205077f, 0.f, -1.73205077f}, + {1.f, -2.f, 1.f}, + {-1.f, 2.f, -1.f}, + {-1.73205077f, 0.f, 1.73205077f}, + {-2.f, -2.f, -2.f}, + {-1.73205077f, 0.f, 1.73205077f}, + {-1.f, 2.f, -1.f}, + {1.f, -2.f, 1.f}, + {1.73205077f, 0.f, -1.73205077f}}; + +// Filters the input signal |in| with the filter |filter| using a shift by +// |in_shift|, taking into account the previous state. +void FilterCore( + rtc::ArrayView filter, + rtc::ArrayView in, + const int in_shift, + rtc::ArrayView out, + rtc::ArrayView state) { + constexpr int kMaxInShift = (kStride - 1); + RTC_DCHECK_GE(in_shift, 0); + RTC_DCHECK_LE(in_shift, kMaxInShift); + std::fill(out.begin(), out.end(), 0.f); + + for (int k = 0; k < in_shift; ++k) { + for (int i = 0, j = kMemorySize + k - in_shift; i < kFilterSize; + ++i, j -= kStride) { + out[k] += state[j] * filter[i]; + } } + + for (int k = in_shift, shift = 0; k < kFilterSize * kStride; ++k, ++shift) { + RTC_DCHECK_GE(shift, 0); + const int loop_limit = std::min(kFilterSize, 1 + (shift >> kStrideLog2)); + for (int i = 0, j = shift; i < loop_limit; ++i, j -= kStride) { + out[k] += in[j] * filter[i]; + } + for (int i = loop_limit, j = kMemorySize + shift - loop_limit * kStride; + i < kFilterSize; ++i, j -= kStride) { + out[k] += state[j] * filter[i]; + } + } + + for (int k = kFilterSize * kStride, shift = kFilterSize * kStride - in_shift; + k < ThreeBandFilterBank::kSplitBandSize; ++k, ++shift) { + for (int i = 0, j = shift; i < kFilterSize; ++i, j -= kStride) { + out[k] += in[j] * filter[i]; + } + } + + // Update current state. + std::copy(in.begin() + ThreeBandFilterBank::kSplitBandSize - kMemorySize, + in.end(), state.begin()); } } // namespace @@ -108,48 +149,72 @@ void Upsample(const float* in, size_t split_length, size_t offset, float* out) { // Because the low-pass filter prototype has half bandwidth it is possible to // use a DCT to shift it in both directions at the same time, to the center // frequencies [1 / 12, 3 / 12, 5 / 12]. -ThreeBandFilterBank::ThreeBandFilterBank(size_t length) - : in_buffer_(rtc::CheckedDivExact(length, kNumBands)), - out_buffer_(in_buffer_.size()) { - for (size_t i = 0; i < kSparsity; ++i) { - for (size_t j = 0; j < kNumBands; ++j) { - analysis_filters_.push_back(new SparseFIRFilter( - kLowpassCoeffs[i * kNumBands + j], kNumCoeffs, kSparsity, i)); - synthesis_filters_.push_back(new SparseFIRFilter( - kLowpassCoeffs[i * kNumBands + j], kNumCoeffs, kSparsity, i)); - } - } - dct_modulation_.resize(kNumBands * kSparsity); - for (size_t i = 0; i < dct_modulation_.size(); ++i) { - dct_modulation_[i].resize(kNumBands); - for (size_t j = 0; j < kNumBands; ++j) { - dct_modulation_[i][j] = - 2.f * cos(2.f * M_PI * i * (2.f * j + 1.f) / dct_modulation_.size()); - } +ThreeBandFilterBank::ThreeBandFilterBank() { + RTC_DCHECK_EQ(state_analysis_.size(), kNumNonZeroFilters); + RTC_DCHECK_EQ(state_synthesis_.size(), kNumNonZeroFilters); + for (int k = 0; k < kNumNonZeroFilters; ++k) { + RTC_DCHECK_EQ(state_analysis_[k].size(), kMemorySize); + RTC_DCHECK_EQ(state_synthesis_[k].size(), kMemorySize); + + state_analysis_[k].fill(0.f); + state_synthesis_[k].fill(0.f); } } +ThreeBandFilterBank::~ThreeBandFilterBank() = default; + // The analysis can be separated in these steps: // 1. Serial to parallel downsampling by a factor of |kNumBands|. // 2. Filtering of |kSparsity| different delayed signals with polyphase // decomposition of the low-pass prototype filter and upsampled by a factor // of |kSparsity|. // 3. Modulating with cosines and accumulating to get the desired band. -void ThreeBandFilterBank::Analysis(const float* in, - size_t length, - float* const* out) { - RTC_CHECK_EQ(in_buffer_.size(), rtc::CheckedDivExact(length, kNumBands)); - for (size_t i = 0; i < kNumBands; ++i) { - memset(out[i], 0, in_buffer_.size() * sizeof(*out[i])); +void ThreeBandFilterBank::Analysis( + rtc::ArrayView in, + rtc::ArrayView, ThreeBandFilterBank::kNumBands> + out) { + // Initialize the output to zero. + for (int band = 0; band < ThreeBandFilterBank::kNumBands; ++band) { + RTC_DCHECK_EQ(out[band].size(), kSplitBandSize); + std::fill(out[band].begin(), out[band].end(), 0); } - for (size_t i = 0; i < kNumBands; ++i) { - Downsample(in, in_buffer_.size(), kNumBands - i - 1, &in_buffer_[0]); - for (size_t j = 0; j < kSparsity; ++j) { - const size_t offset = i + j * kNumBands; - analysis_filters_[offset]->Filter(&in_buffer_[0], - in_buffer_.size(), - &out_buffer_[0]); - DownModulate(&out_buffer_[0], out_buffer_.size(), offset, out); + + for (int downsampling_index = 0; downsampling_index < kSubSampling; + ++downsampling_index) { + // Downsample to form the filter input. + std::array in_subsampled; + for (int k = 0; k < kSplitBandSize; ++k) { + in_subsampled[k] = + in[(kSubSampling - 1) - downsampling_index + kSubSampling * k]; + } + + for (int in_shift = 0; in_shift < kStride; ++in_shift) { + // Choose filter, skip zero filters. + const int index = downsampling_index + in_shift * kSubSampling; + if (index == kZeroFilterIndex1 || index == kZeroFilterIndex2) { + continue; + } + const int filter_index = + index < kZeroFilterIndex1 + ? index + : (index < kZeroFilterIndex2 ? index - 1 : index - 2); + + rtc::ArrayView filter( + kFilterCoeffs[filter_index]); + rtc::ArrayView dct_modulation( + kDctModulation[filter_index]); + rtc::ArrayView state(state_analysis_[filter_index]); + + // Filter. + std::array out_subsampled; + FilterCore(filter, in_subsampled, in_shift, out_subsampled, state); + + // Band and modulate the output. + for (int band = 0; band < ThreeBandFilterBank::kNumBands; ++band) { + for (int n = 0; n < kSplitBandSize; ++n) { + out[band][n] += dct_modulation[band] * out_subsampled[n]; + } + } } } } @@ -160,51 +225,50 @@ void ThreeBandFilterBank::Analysis(const float* in, // prototype filter upsampled by a factor of |kSparsity| and accumulating // |kSparsity| signals with different delays. // 3. Parallel to serial upsampling by a factor of |kNumBands|. -void ThreeBandFilterBank::Synthesis(const float* const* in, - size_t split_length, - float* out) { - RTC_CHECK_EQ(in_buffer_.size(), split_length); - memset(out, 0, kNumBands * in_buffer_.size() * sizeof(*out)); - for (size_t i = 0; i < kNumBands; ++i) { - for (size_t j = 0; j < kSparsity; ++j) { - const size_t offset = i + j * kNumBands; - UpModulate(in, in_buffer_.size(), offset, &in_buffer_[0]); - synthesis_filters_[offset]->Filter(&in_buffer_[0], - in_buffer_.size(), - &out_buffer_[0]); - Upsample(&out_buffer_[0], out_buffer_.size(), i, out); - } - } -} +void ThreeBandFilterBank::Synthesis( + rtc::ArrayView, ThreeBandFilterBank::kNumBands> + in, + rtc::ArrayView out) { + std::fill(out.begin(), out.end(), 0); + for (int upsampling_index = 0; upsampling_index < kSubSampling; + ++upsampling_index) { + for (int in_shift = 0; in_shift < kStride; ++in_shift) { + // Choose filter, skip zero filters. + const int index = upsampling_index + in_shift * kSubSampling; + if (index == kZeroFilterIndex1 || index == kZeroFilterIndex2) { + continue; + } + const int filter_index = + index < kZeroFilterIndex1 + ? index + : (index < kZeroFilterIndex2 ? index - 1 : index - 2); + rtc::ArrayView filter( + kFilterCoeffs[filter_index]); + rtc::ArrayView dct_modulation( + kDctModulation[filter_index]); + rtc::ArrayView state(state_synthesis_[filter_index]); -// Modulates |in| by |dct_modulation_| and accumulates it in each of the -// |kNumBands| bands of |out|. |offset| is the index in the period of the -// cosines used for modulation. |split_length| is the length of |in| and each -// band of |out|. -void ThreeBandFilterBank::DownModulate(const float* in, - size_t split_length, - size_t offset, - float* const* out) { - for (size_t i = 0; i < kNumBands; ++i) { - for (size_t j = 0; j < split_length; ++j) { - out[i][j] += dct_modulation_[offset][i] * in[j]; - } - } -} + // Prepare filter input by modulating the banded input. + std::array in_subsampled; + std::fill(in_subsampled.begin(), in_subsampled.end(), 0.f); + for (int band = 0; band < ThreeBandFilterBank::kNumBands; ++band) { + RTC_DCHECK_EQ(in[band].size(), kSplitBandSize); + for (int n = 0; n < kSplitBandSize; ++n) { + in_subsampled[n] += dct_modulation[band] * in[band][n]; + } + } -// Modulates each of the |kNumBands| bands of |in| by |dct_modulation_| and -// accumulates them in |out|. |out| is cleared before starting to accumulate. -// |offset| is the index in the period of the cosines used for modulation. -// |split_length| is the length of each band of |in| and |out|. -void ThreeBandFilterBank::UpModulate(const float* const* in, - size_t split_length, - size_t offset, - float* out) { - memset(out, 0, split_length * sizeof(*out)); - for (size_t i = 0; i < kNumBands; ++i) { - for (size_t j = 0; j < split_length; ++j) { - out[j] += dct_modulation_[offset][i] * in[i][j]; + // Filter. + std::array out_subsampled; + FilterCore(filter, in_subsampled, in_shift, out_subsampled, state); + + // Upsample. + constexpr float kUpsamplingScaling = kSubSampling; + for (int k = 0; k < kSplitBandSize; ++k) { + out[upsampling_index + kSubSampling * k] += + kUpsamplingScaling * out_subsampled[k]; + } } } } diff --git a/webrtc/modules/audio_processing/three_band_filter_bank.h b/webrtc/modules/audio_processing/three_band_filter_bank.h index cb9cfbe..e6346de 100644 --- a/webrtc/modules/audio_processing/three_band_filter_bank.h +++ b/webrtc/modules/audio_processing/three_band_filter_bank.h @@ -8,17 +8,28 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_THREE_BAND_FILTER_BANK_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_THREE_BAND_FILTER_BANK_H_ +#ifndef MODULES_AUDIO_PROCESSING_THREE_BAND_FILTER_BANK_H_ +#define MODULES_AUDIO_PROCESSING_THREE_BAND_FILTER_BANK_H_ +#include #include +#include #include -#include "webrtc/common_audio/sparse_fir_filter.h" -#include "webrtc/system_wrappers/include/scoped_vector.h" +#include "api/array_view.h" namespace webrtc { +constexpr int kSparsity = 4; +constexpr int kStrideLog2 = 2; +constexpr int kStride = 1 << kStrideLog2; +constexpr int kNumZeroFilters = 2; +constexpr int kFilterSize = 4; +constexpr int kMemorySize = kFilterSize * kStride - 1; +static_assert(kMemorySize == 15, + "The memory size must be sufficient to provide memory for the " + "shifted filters"); + // An implementation of a 3-band FIR filter-bank with DCT modulation, similar to // the proposed in "Multirate Signal Processing for Communication Systems" by // Fredric J Harris. @@ -34,35 +45,33 @@ namespace webrtc { // depending on the input signal after compensating for the delay. class ThreeBandFilterBank final { public: - explicit ThreeBandFilterBank(size_t length); + static const int kNumBands = 3; + static const int kFullBandSize = 480; + static const int kSplitBandSize = + ThreeBandFilterBank::kFullBandSize / ThreeBandFilterBank::kNumBands; + static const int kNumNonZeroFilters = + kSparsity * ThreeBandFilterBank::kNumBands - kNumZeroFilters; - // Splits |in| into 3 downsampled frequency bands in |out|. - // |length| is the |in| length. Each of the 3 bands of |out| has to have a - // length of |length| / 3. - void Analysis(const float* in, size_t length, float* const* out); + ThreeBandFilterBank(); + ~ThreeBandFilterBank(); - // Merges the 3 downsampled frequency bands in |in| into |out|. - // |split_length| is the length of each band of |in|. |out| has to have at - // least a length of 3 * |split_length|. - void Synthesis(const float* const* in, size_t split_length, float* out); + // Splits |in| of size kFullBandSize into 3 downsampled frequency bands in + // |out|, each of size 160. + void Analysis(rtc::ArrayView in, + rtc::ArrayView, kNumBands> out); + + // Merges the 3 downsampled frequency bands in |in|, each of size 160, into + // |out|, which is of size kFullBandSize. + void Synthesis(rtc::ArrayView, kNumBands> in, + rtc::ArrayView out); private: - void DownModulate(const float* in, - size_t split_length, - size_t offset, - float* const* out); - void UpModulate(const float* const* in, - size_t split_length, - size_t offset, - float* out); - - std::vector in_buffer_; - std::vector out_buffer_; - ScopedVector analysis_filters_; - ScopedVector synthesis_filters_; - std::vector> dct_modulation_; + std::array, kNumNonZeroFilters> + state_analysis_; + std::array, kNumNonZeroFilters> + state_synthesis_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_THREE_BAND_FILTER_BANK_H_ +#endif // MODULES_AUDIO_PROCESSING_THREE_BAND_FILTER_BANK_H_ diff --git a/webrtc/modules/audio_processing/transient/BUILD.gn b/webrtc/modules/audio_processing/transient/BUILD.gn new file mode 100644 index 0000000..13e319f --- /dev/null +++ b/webrtc/modules/audio_processing/transient/BUILD.gn @@ -0,0 +1,112 @@ +# Copyright (c) 2020 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. + +import("../../../webrtc.gni") + +rtc_source_set("transient_suppressor_api") { + sources = [ "transient_suppressor.h" ] +} + +rtc_library("transient_suppressor_impl") { + visibility = [ + "..:optionally_built_submodule_creators", + ":transient_suppression_test", + ":transient_suppression_unittests", + ":click_annotate", + ] + sources = [ + "common.h", + "daubechies_8_wavelet_coeffs.h", + "dyadic_decimator.h", + "moving_moments.cc", + "moving_moments.h", + "transient_detector.cc", + "transient_detector.h", + "transient_suppressor_impl.cc", + "transient_suppressor_impl.h", + "windows_private.h", + "wpd_node.cc", + "wpd_node.h", + "wpd_tree.cc", + "wpd_tree.h", + ] + deps = [ + ":transient_suppressor_api", + "../../../common_audio:common_audio", + "../../../common_audio:common_audio_c", + "../../../common_audio:fir_filter", + "../../../common_audio:fir_filter_factory", + "../../../common_audio/third_party/ooura:fft_size_256", + "../../../rtc_base:checks", + "../../../rtc_base:gtest_prod", + "../../../rtc_base:logging", + ] +} + +if (rtc_include_tests) { + rtc_executable("click_annotate") { + testonly = true + sources = [ + "click_annotate.cc", + "file_utils.cc", + "file_utils.h", + ] + deps = [ + ":transient_suppressor_impl", + "..:audio_processing", + "../../../rtc_base/system:file_wrapper", + "../../../system_wrappers", + ] + } + + rtc_executable("transient_suppression_test") { + testonly = true + sources = [ + "file_utils.cc", + "file_utils.h", + "transient_suppression_test.cc", + ] + deps = [ + ":transient_suppressor_impl", + "..:audio_processing", + "../../../common_audio", + "../../../rtc_base:rtc_base_approved", + "../../../rtc_base/system:file_wrapper", + "../../../system_wrappers", + "../../../test:fileutils", + "../../../test:test_support", + "../agc:level_estimation", + "//testing/gtest", + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/flags:parse", + ] + } + + rtc_library("transient_suppression_unittests") { + testonly = true + sources = [ + "dyadic_decimator_unittest.cc", + "file_utils.cc", + "file_utils.h", + "file_utils_unittest.cc", + "moving_moments_unittest.cc", + "transient_detector_unittest.cc", + "transient_suppressor_unittest.cc", + "wpd_node_unittest.cc", + "wpd_tree_unittest.cc", + ] + deps = [ + ":transient_suppressor_impl", + "../../../rtc_base:stringutils", + "../../../rtc_base/system:file_wrapper", + "../../../test:fileutils", + "../../../test:test_support", + "//testing/gtest", + ] + } +} diff --git a/webrtc/modules/audio_processing/transient/click_annotate.cc b/webrtc/modules/audio_processing/transient/click_annotate.cc index 38f7a8e..21641f8 100644 --- a/webrtc/modules/audio_processing/transient/click_annotate.cc +++ b/webrtc/modules/audio_processing/transient/click_annotate.cc @@ -11,14 +11,13 @@ #include #include #include +#include #include -#include "webrtc/modules/audio_processing/transient/transient_detector.h" -#include "webrtc/modules/audio_processing/transient/file_utils.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/system_wrappers/include/file_wrapper.h" +#include "modules/audio_processing/transient/file_utils.h" +#include "modules/audio_processing/transient/transient_detector.h" +#include "rtc_base/system/file_wrapper.h" -using rtc::scoped_ptr; using webrtc::FileWrapper; using webrtc::TransientDetector; @@ -40,16 +39,14 @@ int main(int argc, char* argv[]) { return 0; } - scoped_ptr pcm_file(FileWrapper::Create()); - pcm_file->OpenFile(argv[1], true, false, false); - if (!pcm_file->Open()) { + FileWrapper pcm_file = FileWrapper::OpenReadOnly(argv[1]); + if (!pcm_file.is_open()) { printf("\nThe %s could not be opened.\n\n", argv[1]); return -1; } - scoped_ptr dat_file(FileWrapper::Create()); - dat_file->OpenFile(argv[2], false, false, false); - if (!dat_file->Open()) { + FileWrapper dat_file = FileWrapper::OpenWriteOnly(argv[2]); + if (!dat_file.is_open()) { printf("\nThe %s could not be opened.\n\n", argv[2]); return -1; } @@ -69,14 +66,12 @@ int main(int argc, char* argv[]) { TransientDetector detector(sample_rate_hz); int lost_packets = 0; size_t audio_buffer_length = chunk_size_ms * sample_rate_hz / 1000; - scoped_ptr audio_buffer(new float[audio_buffer_length]); + std::unique_ptr audio_buffer(new float[audio_buffer_length]); std::vector send_times; // Read first buffer from the PCM test file. size_t file_samples_read = ReadInt16FromFileToFloatBuffer( - pcm_file.get(), - audio_buffer_length, - audio_buffer.get()); + &pcm_file, audio_buffer_length, audio_buffer.get()); for (int time = 0; file_samples_read > 0; time += chunk_size_ms) { // Pad the rest of the buffer with zeros. for (size_t i = file_samples_read; i < audio_buffer_length; ++i) { @@ -93,22 +88,20 @@ int main(int argc, char* argv[]) { send_times.push_back(value); // Read next buffer from the PCM test file. - file_samples_read = ReadInt16FromFileToFloatBuffer(pcm_file.get(), - audio_buffer_length, - audio_buffer.get()); + file_samples_read = ReadInt16FromFileToFloatBuffer( + &pcm_file, audio_buffer_length, audio_buffer.get()); } - size_t floats_written = WriteFloatBufferToFile(dat_file.get(), - send_times.size(), - &send_times[0]); + size_t floats_written = + WriteFloatBufferToFile(&dat_file, send_times.size(), &send_times[0]); if (floats_written == 0) { printf("\nThe send times could not be written to DAT file\n\n"); return -1; } - pcm_file->CloseFile(); - dat_file->CloseFile(); + pcm_file.Close(); + dat_file.Close(); return lost_packets; } diff --git a/webrtc/modules/audio_processing/transient/common.h b/webrtc/modules/audio_processing/transient/common.h index 92194e9..63c9a7b 100644 --- a/webrtc/modules/audio_processing/transient/common.h +++ b/webrtc/modules/audio_processing/transient/common.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_COMMON_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_COMMON_H_ +#ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_COMMON_H_ namespace webrtc { namespace ts { @@ -22,6 +22,6 @@ enum { kSampleRate48kHz = 48000 }; -} // namespace ts -} // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_COMMON_H_ +} // namespace ts +} // namespace webrtc +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_COMMON_H_ diff --git a/webrtc/modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h b/webrtc/modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h index b1236ac..92233bf 100644 --- a/webrtc/modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h +++ b/webrtc/modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h @@ -10,8 +10,8 @@ // This header file defines the coefficients of the FIR based approximation of // the Meyer Wavelet -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_DAUBECHIES_8_WAVELET_COEFFS_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_DAUBECHIES_8_WAVELET_COEFFS_H_ +#ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_DAUBECHIES_8_WAVELET_COEFFS_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_DAUBECHIES_8_WAVELET_COEFFS_H_ // Decomposition coefficients Daubechies 8. @@ -19,45 +19,26 @@ namespace webrtc { const int kDaubechies8CoefficientsLength = 16; -const float kDaubechies8HighPassCoefficients[kDaubechies8CoefficientsLength] - = { - -5.44158422430816093862e-02f, - 3.12871590914465924627e-01f, - -6.75630736298012846142e-01f, - 5.85354683654869090148e-01f, - 1.58291052560238926228e-02f, - -2.84015542962428091389e-01f, - -4.72484573997972536787e-04f, - 1.28747426620186011803e-01f, - 1.73693010020221083600e-02f, - -4.40882539310647192377e-02f, - -1.39810279170155156436e-02f, - 8.74609404701565465445e-03f, - 4.87035299301066034600e-03f, - -3.91740372995977108837e-04f, - -6.75449405998556772109e-04f, - -1.17476784002281916305e-04f -}; +const float kDaubechies8HighPassCoefficients[kDaubechies8CoefficientsLength] = { + -5.44158422430816093862e-02f, 3.12871590914465924627e-01f, + -6.75630736298012846142e-01f, 5.85354683654869090148e-01f, + 1.58291052560238926228e-02f, -2.84015542962428091389e-01f, + -4.72484573997972536787e-04f, 1.28747426620186011803e-01f, + 1.73693010020221083600e-02f, -4.40882539310647192377e-02f, + -1.39810279170155156436e-02f, 8.74609404701565465445e-03f, + 4.87035299301066034600e-03f, -3.91740372995977108837e-04f, + -6.75449405998556772109e-04f, -1.17476784002281916305e-04f}; const float kDaubechies8LowPassCoefficients[kDaubechies8CoefficientsLength] = { - -1.17476784002281916305e-04f, - 6.75449405998556772109e-04f, - -3.91740372995977108837e-04f, - -4.87035299301066034600e-03f, - 8.74609404701565465445e-03f, - 1.39810279170155156436e-02f, - -4.40882539310647192377e-02f, - -1.73693010020221083600e-02f, - 1.28747426620186011803e-01f, - 4.72484573997972536787e-04f, - -2.84015542962428091389e-01f, - -1.58291052560238926228e-02f, - 5.85354683654869090148e-01f, - 6.75630736298012846142e-01f, - 3.12871590914465924627e-01f, - 5.44158422430816093862e-02f -}; + -1.17476784002281916305e-04f, 6.75449405998556772109e-04f, + -3.91740372995977108837e-04f, -4.87035299301066034600e-03f, + 8.74609404701565465445e-03f, 1.39810279170155156436e-02f, + -4.40882539310647192377e-02f, -1.73693010020221083600e-02f, + 1.28747426620186011803e-01f, 4.72484573997972536787e-04f, + -2.84015542962428091389e-01f, -1.58291052560238926228e-02f, + 5.85354683654869090148e-01f, 6.75630736298012846142e-01f, + 3.12871590914465924627e-01f, 5.44158422430816093862e-02f}; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_DAUBECHIES_8_WAVELET_COEFFS_H_ +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_DAUBECHIES_8_WAVELET_COEFFS_H_ diff --git a/webrtc/modules/audio_processing/transient/dyadic_decimator.h b/webrtc/modules/audio_processing/transient/dyadic_decimator.h index c1046f2..fcb56b7 100644 --- a/webrtc/modules/audio_processing/transient/dyadic_decimator.h +++ b/webrtc/modules/audio_processing/transient/dyadic_decimator.h @@ -8,13 +8,11 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_DYADIC_DECIMATOR_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_DYADIC_DECIMATOR_H_ +#ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_DYADIC_DECIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_DYADIC_DECIMATOR_H_ #include -#include "webrtc/typedefs.h" - // Provides a set of static methods to perform dyadic decimations. namespace webrtc { @@ -44,7 +42,7 @@ inline size_t GetOutLengthToDyadicDecimate(size_t in_length, // GetOutLengthToDyadicDecimate(). // Must be previously allocated. // Returns the number of output samples, -1 on error. -template +template static size_t DyadicDecimate(const T* in, size_t in_length, bool odd_sequence, @@ -67,4 +65,4 @@ static size_t DyadicDecimate(const T* in, } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_DYADIC_DECIMATOR_H_ +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_DYADIC_DECIMATOR_H_ diff --git a/webrtc/modules/audio_processing/transient/file_utils.cc b/webrtc/modules/audio_processing/transient/file_utils.cc index e043286..58f9932 100644 --- a/webrtc/modules/audio_processing/transient/file_utils.cc +++ b/webrtc/modules/audio_processing/transient/file_utils.cc @@ -8,11 +8,11 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/transient/file_utils.h" +#include "modules/audio_processing/transient/file_utils.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/system_wrappers/include/file_wrapper.h" -#include "webrtc/typedefs.h" +#include + +#include "rtc_base/system/file_wrapper.h" namespace webrtc { @@ -79,11 +79,11 @@ int ConvertDoubleToByteArray(double value, uint8_t out_bytes[8]) { size_t ReadInt16BufferFromFile(FileWrapper* file, size_t length, int16_t* buffer) { - if (!file || !file->Open() || !buffer || length <= 0) { + if (!file || !file->is_open() || !buffer || length <= 0) { return 0; } - rtc::scoped_ptr byte_array(new uint8_t[2]); + std::unique_ptr byte_array(new uint8_t[2]); size_t int16s_read = 0; @@ -105,11 +105,11 @@ size_t ReadInt16BufferFromFile(FileWrapper* file, size_t ReadInt16FromFileToFloatBuffer(FileWrapper* file, size_t length, float* buffer) { - if (!file || !file->Open() || !buffer || length <= 0) { + if (!file || !file->is_open() || !buffer || length <= 0) { return 0; } - rtc::scoped_ptr buffer16(new int16_t[length]); + std::unique_ptr buffer16(new int16_t[length]); size_t int16s_read = ReadInt16BufferFromFile(file, length, buffer16.get()); @@ -123,11 +123,11 @@ size_t ReadInt16FromFileToFloatBuffer(FileWrapper* file, size_t ReadInt16FromFileToDoubleBuffer(FileWrapper* file, size_t length, double* buffer) { - if (!file || !file->Open() || !buffer || length <= 0) { + if (!file || !file->is_open() || !buffer || length <= 0) { return 0; } - rtc::scoped_ptr buffer16(new int16_t[length]); + std::unique_ptr buffer16(new int16_t[length]); size_t int16s_read = ReadInt16BufferFromFile(file, length, buffer16.get()); @@ -141,11 +141,11 @@ size_t ReadInt16FromFileToDoubleBuffer(FileWrapper* file, size_t ReadFloatBufferFromFile(FileWrapper* file, size_t length, float* buffer) { - if (!file || !file->Open() || !buffer || length <= 0) { + if (!file || !file->is_open() || !buffer || length <= 0) { return 0; } - rtc::scoped_ptr byte_array(new uint8_t[4]); + std::unique_ptr byte_array(new uint8_t[4]); size_t floats_read = 0; @@ -164,11 +164,11 @@ size_t ReadFloatBufferFromFile(FileWrapper* file, size_t ReadDoubleBufferFromFile(FileWrapper* file, size_t length, double* buffer) { - if (!file || !file->Open() || !buffer || length <= 0) { + if (!file || !file->is_open() || !buffer || length <= 0) { return 0; } - rtc::scoped_ptr byte_array(new uint8_t[8]); + std::unique_ptr byte_array(new uint8_t[8]); size_t doubles_read = 0; @@ -187,11 +187,11 @@ size_t ReadDoubleBufferFromFile(FileWrapper* file, size_t WriteInt16BufferToFile(FileWrapper* file, size_t length, const int16_t* buffer) { - if (!file || !file->Open() || !buffer || length <= 0) { + if (!file || !file->is_open() || !buffer || length <= 0) { return 0; } - rtc::scoped_ptr byte_array(new uint8_t[2]); + std::unique_ptr byte_array(new uint8_t[2]); size_t int16s_written = 0; @@ -211,11 +211,11 @@ size_t WriteInt16BufferToFile(FileWrapper* file, size_t WriteFloatBufferToFile(FileWrapper* file, size_t length, const float* buffer) { - if (!file || !file->Open() || !buffer || length <= 0) { + if (!file || !file->is_open() || !buffer || length <= 0) { return 0; } - rtc::scoped_ptr byte_array(new uint8_t[4]); + std::unique_ptr byte_array(new uint8_t[4]); size_t floats_written = 0; @@ -234,11 +234,11 @@ size_t WriteFloatBufferToFile(FileWrapper* file, size_t WriteDoubleBufferToFile(FileWrapper* file, size_t length, const double* buffer) { - if (!file || !file->Open() || !buffer || length <= 0) { + if (!file || !file->is_open() || !buffer || length <= 0) { return 0; } - rtc::scoped_ptr byte_array(new uint8_t[8]); + std::unique_ptr byte_array(new uint8_t[8]); size_t doubles_written = 0; diff --git a/webrtc/modules/audio_processing/transient/file_utils.h b/webrtc/modules/audio_processing/transient/file_utils.h index cc76953..6184017 100644 --- a/webrtc/modules/audio_processing/transient/file_utils.h +++ b/webrtc/modules/audio_processing/transient/file_utils.h @@ -8,13 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_FILE_UTILS_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_FILE_UTILS_H_ +#ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_FILE_UTILS_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_FILE_UTILS_H_ #include -#include "webrtc/system_wrappers/include/file_wrapper.h" -#include "webrtc/typedefs.h" +#include "rtc_base/system/file_wrapper.h" namespace webrtc { @@ -115,4 +114,4 @@ size_t WriteDoubleBufferToFile(FileWrapper* file, } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_FILE_UTILS_H_ +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_FILE_UTILS_H_ diff --git a/webrtc/modules/audio_processing/transient/moving_moments.cc b/webrtc/modules/audio_processing/transient/moving_moments.cc index aa47522..83810bf 100644 --- a/webrtc/modules/audio_processing/transient/moving_moments.cc +++ b/webrtc/modules/audio_processing/transient/moving_moments.cc @@ -8,21 +8,17 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/transient/moving_moments.h" +#include "modules/audio_processing/transient/moving_moments.h" -#include -#include +#include -#include "webrtc/base/scoped_ptr.h" +#include "rtc_base/checks.h" namespace webrtc { MovingMoments::MovingMoments(size_t length) - : length_(length), - queue_(), - sum_(0.0), - sum_of_squares_(0.0) { - assert(length > 0); + : length_(length), queue_(), sum_(0.0), sum_of_squares_(0.0) { + RTC_DCHECK_GT(length, 0); for (size_t i = 0; i < length; ++i) { queue_.push(0.0); } @@ -30,9 +26,14 @@ MovingMoments::MovingMoments(size_t length) MovingMoments::~MovingMoments() {} -void MovingMoments::CalculateMoments(const float* in, size_t in_length, - float* first, float* second) { - assert(in && in_length > 0 && first && second); +void MovingMoments::CalculateMoments(const float* in, + size_t in_length, + float* first, + float* second) { + RTC_DCHECK(in); + RTC_DCHECK_GT(in_length, 0); + RTC_DCHECK(first); + RTC_DCHECK(second); for (size_t i = 0; i < in_length; ++i) { const float old_value = queue_.front(); @@ -42,7 +43,7 @@ void MovingMoments::CalculateMoments(const float* in, size_t in_length, sum_ += in[i] - old_value; sum_of_squares_ += in[i] * in[i] - old_value * old_value; first[i] = sum_ / length_; - second[i] = sum_of_squares_ / length_; + second[i] = std::max(0.f, sum_of_squares_ / length_); } } diff --git a/webrtc/modules/audio_processing/transient/moving_moments.h b/webrtc/modules/audio_processing/transient/moving_moments.h index 6e3ad5b..6dc0520 100644 --- a/webrtc/modules/audio_processing/transient/moving_moments.h +++ b/webrtc/modules/audio_processing/transient/moving_moments.h @@ -8,13 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_MOVING_MOMENTS_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_MOVING_MOMENTS_H_ +#ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_MOVING_MOMENTS_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_MOVING_MOMENTS_H_ + +#include #include -#include "webrtc/base/scoped_ptr.h" - namespace webrtc { // Calculates the first and second moments for each value of a buffer taking @@ -33,8 +33,10 @@ class MovingMoments { // Calculates the new values using |in|. Results will be in the out buffers. // |first| and |second| must be allocated with at least |in_length|. - void CalculateMoments(const float* in, size_t in_length, - float* first, float* second); + void CalculateMoments(const float* in, + size_t in_length, + float* first, + float* second); private: size_t length_; @@ -48,5 +50,4 @@ class MovingMoments { } // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_MOVING_MOMENTS_H_ +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_MOVING_MOMENTS_H_ diff --git a/webrtc/modules/audio_processing/transient/transient_detector.cc b/webrtc/modules/audio_processing/transient/transient_detector.cc index 7f021ac..f03a2ea 100644 --- a/webrtc/modules/audio_processing/transient/transient_detector.cc +++ b/webrtc/modules/audio_processing/transient/transient_detector.cc @@ -8,17 +8,20 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/transient/transient_detector.h" +#include "modules/audio_processing/transient/transient_detector.h" -#include #include -#include #include -#include "webrtc/modules/audio_processing/transient/common.h" -#include "webrtc/modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h" -#include "webrtc/modules/audio_processing/transient/moving_moments.h" -#include "webrtc/modules/audio_processing/transient/wpd_tree.h" +#include +#include + +#include "modules/audio_processing/transient/common.h" +#include "modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h" +#include "modules/audio_processing/transient/moving_moments.h" +#include "modules/audio_processing/transient/wpd_node.h" +#include "modules/audio_processing/transient/wpd_tree.h" +#include "rtc_base/checks.h" namespace webrtc { @@ -34,10 +37,10 @@ TransientDetector::TransientDetector(int sample_rate_hz) chunks_at_startup_left_to_delete_(kChunksAtStartupLeftToDelete), reference_energy_(1.f), using_reference_(false) { - assert(sample_rate_hz == ts::kSampleRate8kHz || - sample_rate_hz == ts::kSampleRate16kHz || - sample_rate_hz == ts::kSampleRate32kHz || - sample_rate_hz == ts::kSampleRate48kHz); + RTC_DCHECK(sample_rate_hz == ts::kSampleRate8kHz || + sample_rate_hz == ts::kSampleRate16kHz || + sample_rate_hz == ts::kSampleRate32kHz || + sample_rate_hz == ts::kSampleRate48kHz); int samples_per_transient = sample_rate_hz * kTransientLengthMs / 1000; // Adjustment to avoid data loss while downsampling, making // |samples_per_chunk_| and |samples_per_transient| always divisible by @@ -49,8 +52,7 @@ TransientDetector::TransientDetector(int sample_rate_hz) wpd_tree_.reset(new WPDTree(samples_per_chunk_, kDaubechies8HighPassCoefficients, kDaubechies8LowPassCoefficients, - kDaubechies8CoefficientsLength, - kLevels)); + kDaubechies8CoefficientsLength, kLevels)); for (size_t i = 0; i < kLeaves; ++i) { moving_moments_[i].reset( new MovingMoments(samples_per_transient / kLeaves)); @@ -70,7 +72,8 @@ float TransientDetector::Detect(const float* data, size_t data_length, const float* reference_data, size_t reference_length) { - assert(data && data_length == samples_per_chunk_); + RTC_DCHECK(data); + RTC_DCHECK_EQ(samples_per_chunk_, data_length); // TODO(aluebs): Check if these errors can logically happen and if not assert // on them. @@ -83,8 +86,7 @@ float TransientDetector::Detect(const float* data, for (size_t i = 0; i < kLeaves; ++i) { WPDNode* leaf = wpd_tree_->NodeAt(kLevels, i); - moving_moments_[i]->CalculateMoments(leaf->data(), - tree_leaves_data_length_, + moving_moments_[i]->CalculateMoments(leaf->data(), tree_leaves_data_length_, first_moments_.get(), second_moments_.get()); @@ -124,8 +126,9 @@ float TransientDetector::Detect(const float* data, const float kVerticalScaling = 0.5f; const float kVerticalShift = 1.f; - result = (cos(result * horizontal_scaling + kHorizontalShift) - + kVerticalShift) * kVerticalScaling; + result = (std::cos(result * horizontal_scaling + kHorizontalShift) + + kVerticalShift) * + kVerticalScaling; result *= result; } @@ -158,10 +161,10 @@ float TransientDetector::ReferenceDetectionValue(const float* data, using_reference_ = false; return 1.f; } - assert(reference_energy_ != 0); - float result = 1.f / (1.f + exp(kReferenceNonLinearity * - (kEnergyRatioThreshold - - reference_energy / reference_energy_))); + RTC_DCHECK_NE(0, reference_energy_); + float result = 1.f / (1.f + std::exp(kReferenceNonLinearity * + (kEnergyRatioThreshold - + reference_energy / reference_energy_))); reference_energy_ = kMemory * reference_energy_ + (1.f - kMemory) * reference_energy; diff --git a/webrtc/modules/audio_processing/transient/transient_detector.h b/webrtc/modules/audio_processing/transient/transient_detector.h index 3f96582..5ede2e8 100644 --- a/webrtc/modules/audio_processing/transient/transient_detector.h +++ b/webrtc/modules/audio_processing/transient/transient_detector.h @@ -8,14 +8,16 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_DETECTOR_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_DETECTOR_H_ +#ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_DETECTOR_H_ + +#include #include +#include -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/modules/audio_processing/transient/moving_moments.h" -#include "webrtc/modules/audio_processing/transient/wpd_tree.h" +#include "modules/audio_processing/transient/moving_moments.h" +#include "modules/audio_processing/transient/wpd_tree.h" namespace webrtc { @@ -55,14 +57,14 @@ class TransientDetector { size_t samples_per_chunk_; - rtc::scoped_ptr wpd_tree_; + std::unique_ptr wpd_tree_; size_t tree_leaves_data_length_; // A MovingMoments object is needed for each leaf in the WPD tree. - rtc::scoped_ptr moving_moments_[kLeaves]; + std::unique_ptr moving_moments_[kLeaves]; - rtc::scoped_ptr first_moments_; - rtc::scoped_ptr second_moments_; + std::unique_ptr first_moments_; + std::unique_ptr second_moments_; // Stores the last calculated moments from the previous detection. float last_first_moment_[kLeaves]; @@ -84,4 +86,4 @@ class TransientDetector { } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_DETECTOR_H_ +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_DETECTOR_H_ diff --git a/webrtc/modules/audio_processing/transient/transient_suppressor.h b/webrtc/modules/audio_processing/transient/transient_suppressor.h index b62dcde..bb262b0 100644 --- a/webrtc/modules/audio_processing/transient/transient_suppressor.h +++ b/webrtc/modules/audio_processing/transient/transient_suppressor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * Copyright (c) 2020 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 @@ -8,30 +8,24 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_SUPPRESSOR_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_SUPPRESSOR_H_ +#ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_SUPPRESSOR_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_SUPPRESSOR_H_ -#include -#include - -#include "webrtc/base/scoped_ptr.h" -#ifndef WEBRTC_AUDIO_PROCESSING_ONLY_BUILD -#include "webrtc/test/testsupport/gtest_prod_util.h" -#endif -#include "webrtc/typedefs.h" +#include +#include +#include namespace webrtc { -class TransientDetector; - // Detects transients in an audio stream and suppress them using a simple // restoration algorithm that attenuates unexpected spikes in the spectrum. class TransientSuppressor { public: - TransientSuppressor(); - ~TransientSuppressor(); + virtual ~TransientSuppressor() {} - int Initialize(int sample_rate_hz, int detector_rate_hz, int num_channels); + virtual int Initialize(int sample_rate_hz, + int detector_rate_hz, + int num_channels) = 0; // Processes a |data| chunk, and returns it with keystrokes suppressed from // it. The float format is assumed to be int16 ranged. If there are more than @@ -50,75 +44,17 @@ class TransientSuppressor { // always be set to 1. // |key_pressed| determines if a key was pressed on this audio chunk. // Returns 0 on success and -1 otherwise. - int Suppress(float* data, - size_t data_length, - int num_channels, - const float* detection_data, - size_t detection_length, - const float* reference_data, - size_t reference_length, - float voice_probability, - bool key_pressed); - - private: -#ifndef WEBRTC_AUDIO_PROCESSING_ONLY_BUILD - FRIEND_TEST_ALL_PREFIXES(TransientSuppressorTest, - TypingDetectionLogicWorksAsExpectedForMono); -#endif - void Suppress(float* in_ptr, float* spectral_mean, float* out_ptr); - - void UpdateKeypress(bool key_pressed); - void UpdateRestoration(float voice_probability); - - void UpdateBuffers(float* data); - - void HardRestoration(float* spectral_mean); - void SoftRestoration(float* spectral_mean); - - rtc::scoped_ptr detector_; - - size_t data_length_; - size_t detection_length_; - size_t analysis_length_; - size_t buffer_delay_; - size_t complex_analysis_length_; - int num_channels_; - // Input buffer where the original samples are stored. - rtc::scoped_ptr in_buffer_; - rtc::scoped_ptr detection_buffer_; - // Output buffer where the restored samples are stored. - rtc::scoped_ptr out_buffer_; - - // Arrays for fft. - rtc::scoped_ptr ip_; - rtc::scoped_ptr wfft_; - - rtc::scoped_ptr spectral_mean_; - - // Stores the data for the fft. - rtc::scoped_ptr fft_buffer_; - - rtc::scoped_ptr magnitudes_; - - const float* window_; - - rtc::scoped_ptr mean_factor_; - - float detector_smoothed_; - - int keypress_counter_; - int chunks_since_keypress_; - bool detection_enabled_; - bool suppression_enabled_; - - bool use_hard_restoration_; - int chunks_since_voice_change_; - - uint32_t seed_; - - bool using_reference_; + virtual int Suppress(float* data, + size_t data_length, + int num_channels, + const float* detection_data, + size_t detection_length, + const float* reference_data, + size_t reference_length, + float voice_probability, + bool key_pressed) = 0; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_SUPPRESSOR_H_ +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_SUPPRESSOR_H_ diff --git a/webrtc/modules/audio_processing/transient/transient_suppressor.cc b/webrtc/modules/audio_processing/transient/transient_suppressor_impl.cc similarity index 78% rename from webrtc/modules/audio_processing/transient/transient_suppressor.cc rename to webrtc/modules/audio_processing/transient/transient_suppressor_impl.cc index c8d9e65..d515d30 100644 --- a/webrtc/modules/audio_processing/transient/transient_suppressor.cc +++ b/webrtc/modules/audio_processing/transient/transient_suppressor_impl.cc @@ -8,24 +8,26 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/transient/transient_suppressor.h" +#include "modules/audio_processing/transient/transient_suppressor_impl.h" -#include #include + +#include #include #include #include +#include #include -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/fft4g.h" -#include "webrtc/common_audio/include/audio_util.h" -#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" -#include "webrtc/modules/audio_processing/transient/common.h" -#include "webrtc/modules/audio_processing/transient/transient_detector.h" -#include "webrtc/modules/audio_processing/ns/windows_private.h" -#include "webrtc/system_wrappers/include/logging.h" -#include "webrtc/typedefs.h" +#include "common_audio/include/audio_util.h" +#include "common_audio/signal_processing/include/signal_processing_library.h" +#include "common_audio/third_party/ooura/fft_size_256/fft4g.h" +#include "modules/audio_processing/transient/common.h" +#include "modules/audio_processing/transient/transient_detector.h" +#include "modules/audio_processing/transient/transient_suppressor.h" +#include "modules/audio_processing/transient/windows_private.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" namespace webrtc { @@ -44,7 +46,7 @@ float ComplexMagnitude(float a, float b) { } // namespace -TransientSuppressor::TransientSuppressor() +TransientSuppressorImpl::TransientSuppressorImpl() : data_length_(0), detection_length_(0), analysis_length_(0), @@ -60,14 +62,13 @@ TransientSuppressor::TransientSuppressor() use_hard_restoration_(false), chunks_since_voice_change_(0), seed_(182), - using_reference_(false) { -} + using_reference_(false) {} -TransientSuppressor::~TransientSuppressor() {} +TransientSuppressorImpl::~TransientSuppressorImpl() {} -int TransientSuppressor::Initialize(int sample_rate_hz, - int detection_rate_hz, - int num_channels) { +int TransientSuppressorImpl::Initialize(int sample_rate_hz, + int detection_rate_hz, + int num_channels) { switch (sample_rate_hz) { case ts::kSampleRate8kHz: analysis_length_ = 128u; @@ -101,26 +102,23 @@ int TransientSuppressor::Initialize(int sample_rate_hz, detector_.reset(new TransientDetector(detection_rate_hz)); data_length_ = sample_rate_hz * ts::kChunkSizeMs / 1000; if (data_length_ > analysis_length_) { - assert(false); + RTC_NOTREACHED(); return -1; } buffer_delay_ = analysis_length_ - data_length_; complex_analysis_length_ = analysis_length_ / 2 + 1; - assert(complex_analysis_length_ >= kMaxVoiceBin); + RTC_DCHECK_GE(complex_analysis_length_, kMaxVoiceBin); num_channels_ = num_channels; in_buffer_.reset(new float[analysis_length_ * num_channels_]); - memset(in_buffer_.get(), - 0, + memset(in_buffer_.get(), 0, analysis_length_ * num_channels_ * sizeof(in_buffer_[0])); detection_length_ = detection_rate_hz * ts::kChunkSizeMs / 1000; detection_buffer_.reset(new float[detection_length_]); - memset(detection_buffer_.get(), - 0, + memset(detection_buffer_.get(), 0, detection_length_ * sizeof(detection_buffer_[0])); out_buffer_.reset(new float[analysis_length_ * num_channels_]); - memset(out_buffer_.get(), - 0, + memset(out_buffer_.get(), 0, analysis_length_ * num_channels_ * sizeof(out_buffer_[0])); // ip[0] must be zero to trigger initialization using rdft(). size_t ip_length = 2 + sqrtf(analysis_length_); @@ -129,14 +127,12 @@ int TransientSuppressor::Initialize(int sample_rate_hz, wfft_.reset(new float[complex_analysis_length_ - 1]); memset(wfft_.get(), 0, (complex_analysis_length_ - 1) * sizeof(wfft_[0])); spectral_mean_.reset(new float[complex_analysis_length_ * num_channels_]); - memset(spectral_mean_.get(), - 0, + memset(spectral_mean_.get(), 0, complex_analysis_length_ * num_channels_ * sizeof(spectral_mean_[0])); fft_buffer_.reset(new float[analysis_length_ + 2]); memset(fft_buffer_.get(), 0, (analysis_length_ + 2) * sizeof(fft_buffer_[0])); magnitudes_.reset(new float[complex_analysis_length_]); - memset(magnitudes_.get(), - 0, + memset(magnitudes_.get(), 0, complex_analysis_length_ * sizeof(magnitudes_[0])); mean_factor_.reset(new float[complex_analysis_length_]); @@ -146,9 +142,9 @@ int TransientSuppressor::Initialize(int sample_rate_hz, for (size_t i = 0; i < complex_analysis_length_; ++i) { mean_factor_[i] = kFactorHeight / - (1.f + exp(kLowSlope * static_cast(i - kMinVoiceBin))) + + (1.f + std::exp(kLowSlope * static_cast(i - kMinVoiceBin))) + kFactorHeight / - (1.f + exp(kHighSlope * static_cast(kMaxVoiceBin - i))); + (1.f + std::exp(kHighSlope * static_cast(kMaxVoiceBin - i))); } detector_smoothed_ = 0.f; keypress_counter_ = 0; @@ -162,15 +158,15 @@ int TransientSuppressor::Initialize(int sample_rate_hz, return 0; } -int TransientSuppressor::Suppress(float* data, - size_t data_length, - int num_channels, - const float* detection_data, - size_t detection_length, - const float* reference_data, - size_t reference_length, - float voice_probability, - bool key_pressed) { +int TransientSuppressorImpl::Suppress(float* data, + size_t data_length, + int num_channels, + const float* detection_data, + size_t detection_length, + const float* reference_data, + size_t reference_length, + float voice_probability, + bool key_pressed) { if (!data || data_length != data_length_ || num_channels != num_channels_ || detection_length != detection_length_ || voice_probability < 0 || voice_probability > 1) { @@ -190,8 +186,8 @@ int TransientSuppressor::Suppress(float* data, detection_data = &in_buffer_[buffer_delay_]; } - float detector_result = detector_->Detect( - detection_data, detection_length, reference_data, reference_length); + float detector_result = detector_->Detect(detection_data, detection_length, + reference_data, reference_length); if (detector_result < 0) { return -1; } @@ -229,9 +225,9 @@ int TransientSuppressor::Suppress(float* data, // This should only be called when detection is enabled. UpdateBuffers() must // have been called. At return, |out_buffer_| will be filled with the // processed output. -void TransientSuppressor::Suppress(float* in_ptr, - float* spectral_mean, - float* out_ptr) { +void TransientSuppressorImpl::Suppress(float* in_ptr, + float* spectral_mean, + float* out_ptr) { // Go to frequency domain. for (size_t i = 0; i < analysis_length_; ++i) { // TODO(aluebs): Rename windows @@ -247,8 +243,8 @@ void TransientSuppressor::Suppress(float* in_ptr, fft_buffer_[1] = 0.f; for (size_t i = 0; i < complex_analysis_length_; ++i) { - magnitudes_[i] = ComplexMagnitude(fft_buffer_[i * 2], - fft_buffer_[i * 2 + 1]); + magnitudes_[i] = + ComplexMagnitude(fft_buffer_[i * 2], fft_buffer_[i * 2 + 1]); } // Restore audio if necessary. if (suppression_enabled_) { @@ -269,11 +265,7 @@ void TransientSuppressor::Suppress(float* in_ptr, // Put R[n/2] back in fft_buffer_[1]. fft_buffer_[1] = fft_buffer_[analysis_length_]; - WebRtc_rdft(analysis_length_, - -1, - fft_buffer_.get(), - ip_.get(), - wfft_.get()); + WebRtc_rdft(analysis_length_, -1, fft_buffer_.get(), ip_.get(), wfft_.get()); const float fft_scaling = 2.f / analysis_length_; for (size_t i = 0; i < analysis_length_; ++i) { @@ -281,7 +273,7 @@ void TransientSuppressor::Suppress(float* in_ptr, } } -void TransientSuppressor::UpdateKeypress(bool key_pressed) { +void TransientSuppressorImpl::UpdateKeypress(bool key_pressed) { const int kKeypressPenalty = 1000 / ts::kChunkSizeMs; const int kIsTypingThreshold = 1000 / ts::kChunkSizeMs; const int kChunksUntilNotTyping = 4000 / ts::kChunkSizeMs; // 4 seconds. @@ -295,16 +287,15 @@ void TransientSuppressor::UpdateKeypress(bool key_pressed) { if (keypress_counter_ > kIsTypingThreshold) { if (!suppression_enabled_) { - LOG(LS_INFO) << "[ts] Transient suppression is now enabled."; + RTC_LOG(LS_INFO) << "[ts] Transient suppression is now enabled."; } suppression_enabled_ = true; keypress_counter_ = 0; } - if (detection_enabled_ && - ++chunks_since_keypress_ > kChunksUntilNotTyping) { + if (detection_enabled_ && ++chunks_since_keypress_ > kChunksUntilNotTyping) { if (suppression_enabled_) { - LOG(LS_INFO) << "[ts] Transient suppression is now disabled."; + RTC_LOG(LS_INFO) << "[ts] Transient suppression is now disabled."; } detection_enabled_ = false; suppression_enabled_ = false; @@ -312,7 +303,7 @@ void TransientSuppressor::UpdateKeypress(bool key_pressed) { } } -void TransientSuppressor::UpdateRestoration(float voice_probability) { +void TransientSuppressorImpl::UpdateRestoration(float voice_probability) { const int kHardRestorationOffsetDelay = 3; const int kHardRestorationOnsetDelay = 80; @@ -335,28 +326,24 @@ void TransientSuppressor::UpdateRestoration(float voice_probability) { // Shift buffers to make way for new data. Must be called after // |detection_enabled_| is updated by UpdateKeypress(). -void TransientSuppressor::UpdateBuffers(float* data) { +void TransientSuppressorImpl::UpdateBuffers(float* data) { // TODO(aluebs): Change to ring buffer. - memmove(in_buffer_.get(), - &in_buffer_[data_length_], + memmove(in_buffer_.get(), &in_buffer_[data_length_], (buffer_delay_ + (num_channels_ - 1) * analysis_length_) * sizeof(in_buffer_[0])); // Copy new chunk to buffer. for (int i = 0; i < num_channels_; ++i) { memcpy(&in_buffer_[buffer_delay_ + i * analysis_length_], - &data[i * data_length_], - data_length_ * sizeof(*data)); + &data[i * data_length_], data_length_ * sizeof(*data)); } if (detection_enabled_) { // Shift previous chunk in out buffer. - memmove(out_buffer_.get(), - &out_buffer_[data_length_], + memmove(out_buffer_.get(), &out_buffer_[data_length_], (buffer_delay_ + (num_channels_ - 1) * analysis_length_) * sizeof(out_buffer_[0])); // Initialize new chunk in out buffer. for (int i = 0; i < num_channels_; ++i) { - memset(&out_buffer_[buffer_delay_ + i * analysis_length_], - 0, + memset(&out_buffer_[buffer_delay_ + i * analysis_length_], 0, data_length_ * sizeof(out_buffer_[0])); } } @@ -366,16 +353,16 @@ void TransientSuppressor::UpdateBuffers(float* data) { // Attenuates by a certain factor every peak in the |fft_buffer_| that exceeds // the spectral mean. The attenuation depends on |detector_smoothed_|. // If a restoration takes place, the |magnitudes_| are updated to the new value. -void TransientSuppressor::HardRestoration(float* spectral_mean) { +void TransientSuppressorImpl::HardRestoration(float* spectral_mean) { const float detector_result = - 1.f - pow(1.f - detector_smoothed_, using_reference_ ? 200.f : 50.f); + 1.f - std::pow(1.f - detector_smoothed_, using_reference_ ? 200.f : 50.f); // To restore, we get the peaks in the spectrum. If higher than the previous // spectral mean we adjust them. for (size_t i = 0; i < complex_analysis_length_; ++i) { if (magnitudes_[i] > spectral_mean[i] && magnitudes_[i] > 0) { // RandU() generates values on [0, int16::max()] const float phase = 2 * ts::kPi * WebRtcSpl_RandU(&seed_) / - std::numeric_limits::max(); + std::numeric_limits::max(); const float scaled_mean = detector_result * spectral_mean[i]; fft_buffer_[i * 2] = (1 - detector_result) * fft_buffer_[i * 2] + @@ -393,7 +380,7 @@ void TransientSuppressor::HardRestoration(float* spectral_mean) { // the spectral mean and that is lower than some function of the current block // frequency mean. The attenuation depends on |detector_smoothed_|. // If a restoration takes place, the |magnitudes_| are updated to the new value. -void TransientSuppressor::SoftRestoration(float* spectral_mean) { +void TransientSuppressorImpl::SoftRestoration(float* spectral_mean) { // Get the spectral magnitude mean of the current block. float block_frequency_mean = 0; for (size_t i = kMinVoiceBin; i < kMaxVoiceBin; ++i) { diff --git a/webrtc/modules/audio_processing/transient/transient_suppressor_impl.h b/webrtc/modules/audio_processing/transient/transient_suppressor_impl.h new file mode 100644 index 0000000..4737af5 --- /dev/null +++ b/webrtc/modules/audio_processing/transient/transient_suppressor_impl.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2013 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_SUPPRESSOR_IMPL_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_SUPPRESSOR_IMPL_H_ + +#include +#include + +#include + +#include "modules/audio_processing/transient/transient_suppressor.h" +#include "rtc_base/gtest_prod_util.h" + +namespace webrtc { + +class TransientDetector; + +// Detects transients in an audio stream and suppress them using a simple +// restoration algorithm that attenuates unexpected spikes in the spectrum. +class TransientSuppressorImpl : public TransientSuppressor { + public: + TransientSuppressorImpl(); + ~TransientSuppressorImpl() override; + + int Initialize(int sample_rate_hz, + int detector_rate_hz, + int num_channels) override; + + // Processes a |data| chunk, and returns it with keystrokes suppressed from + // it. The float format is assumed to be int16 ranged. If there are more than + // one channel, the chunks are concatenated one after the other in |data|. + // |data_length| must be equal to |data_length_|. + // |num_channels| must be equal to |num_channels_|. + // A sub-band, ideally the higher, can be used as |detection_data|. If it is + // NULL, |data| is used for the detection too. The |detection_data| is always + // assumed mono. + // If a reference signal (e.g. keyboard microphone) is available, it can be + // passed in as |reference_data|. It is assumed mono and must have the same + // length as |data|. NULL is accepted if unavailable. + // This suppressor performs better if voice information is available. + // |voice_probability| is the probability of voice being present in this chunk + // of audio. If voice information is not available, |voice_probability| must + // always be set to 1. + // |key_pressed| determines if a key was pressed on this audio chunk. + // Returns 0 on success and -1 otherwise. + int Suppress(float* data, + size_t data_length, + int num_channels, + const float* detection_data, + size_t detection_length, + const float* reference_data, + size_t reference_length, + float voice_probability, + bool key_pressed) override; + + private: + FRIEND_TEST_ALL_PREFIXES(TransientSuppressorImplTest, + TypingDetectionLogicWorksAsExpectedForMono); + void Suppress(float* in_ptr, float* spectral_mean, float* out_ptr); + + void UpdateKeypress(bool key_pressed); + void UpdateRestoration(float voice_probability); + + void UpdateBuffers(float* data); + + void HardRestoration(float* spectral_mean); + void SoftRestoration(float* spectral_mean); + + std::unique_ptr detector_; + + size_t data_length_; + size_t detection_length_; + size_t analysis_length_; + size_t buffer_delay_; + size_t complex_analysis_length_; + int num_channels_; + // Input buffer where the original samples are stored. + std::unique_ptr in_buffer_; + std::unique_ptr detection_buffer_; + // Output buffer where the restored samples are stored. + std::unique_ptr out_buffer_; + + // Arrays for fft. + std::unique_ptr ip_; + std::unique_ptr wfft_; + + std::unique_ptr spectral_mean_; + + // Stores the data for the fft. + std::unique_ptr fft_buffer_; + + std::unique_ptr magnitudes_; + + const float* window_; + + std::unique_ptr mean_factor_; + + float detector_smoothed_; + + int keypress_counter_; + int chunks_since_keypress_; + bool detection_enabled_; + bool suppression_enabled_; + + bool use_hard_restoration_; + int chunks_since_voice_change_; + + uint32_t seed_; + + bool using_reference_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_SUPPRESSOR_IMPL_H_ diff --git a/webrtc/modules/audio_processing/transient/windows_private.h b/webrtc/modules/audio_processing/transient/windows_private.h new file mode 100644 index 0000000..54e3c25 --- /dev/null +++ b/webrtc/modules/audio_processing/transient/windows_private.h @@ -0,0 +1,557 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_WINDOWS_PRIVATE_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_WINDOWS_PRIVATE_H_ + +namespace webrtc { + +// Hanning window for 4ms 16kHz +static const float kHanning64w128[128] = { + 0.00000000000000f, 0.02454122852291f, 0.04906767432742f, 0.07356456359967f, + 0.09801714032956f, 0.12241067519922f, 0.14673047445536f, 0.17096188876030f, + 0.19509032201613f, 0.21910124015687f, 0.24298017990326f, 0.26671275747490f, + 0.29028467725446f, 0.31368174039889f, 0.33688985339222f, 0.35989503653499f, + 0.38268343236509f, 0.40524131400499f, 0.42755509343028f, 0.44961132965461f, + 0.47139673682600f, 0.49289819222978f, 0.51410274419322f, 0.53499761988710f, + 0.55557023301960f, 0.57580819141785f, 0.59569930449243f, 0.61523159058063f, + 0.63439328416365f, 0.65317284295378f, 0.67155895484702f, 0.68954054473707f, + 0.70710678118655f, 0.72424708295147f, 0.74095112535496f, 0.75720884650648f, + 0.77301045336274f, 0.78834642762661f, 0.80320753148064f, 0.81758481315158f, + 0.83146961230255f, 0.84485356524971f, 0.85772861000027f, 0.87008699110871f, + 0.88192126434835f, 0.89322430119552f, 0.90398929312344f, 0.91420975570353f, + 0.92387953251129f, 0.93299279883474f, 0.94154406518302f, 0.94952818059304f, + 0.95694033573221f, 0.96377606579544f, 0.97003125319454f, 0.97570213003853f, + 0.98078528040323f, 0.98527764238894f, 0.98917650996478f, 0.99247953459871f, + 0.99518472667220f, 0.99729045667869f, 0.99879545620517f, 0.99969881869620f, + 1.00000000000000f, 0.99969881869620f, 0.99879545620517f, 0.99729045667869f, + 0.99518472667220f, 0.99247953459871f, 0.98917650996478f, 0.98527764238894f, + 0.98078528040323f, 0.97570213003853f, 0.97003125319454f, 0.96377606579544f, + 0.95694033573221f, 0.94952818059304f, 0.94154406518302f, 0.93299279883474f, + 0.92387953251129f, 0.91420975570353f, 0.90398929312344f, 0.89322430119552f, + 0.88192126434835f, 0.87008699110871f, 0.85772861000027f, 0.84485356524971f, + 0.83146961230255f, 0.81758481315158f, 0.80320753148064f, 0.78834642762661f, + 0.77301045336274f, 0.75720884650648f, 0.74095112535496f, 0.72424708295147f, + 0.70710678118655f, 0.68954054473707f, 0.67155895484702f, 0.65317284295378f, + 0.63439328416365f, 0.61523159058063f, 0.59569930449243f, 0.57580819141785f, + 0.55557023301960f, 0.53499761988710f, 0.51410274419322f, 0.49289819222978f, + 0.47139673682600f, 0.44961132965461f, 0.42755509343028f, 0.40524131400499f, + 0.38268343236509f, 0.35989503653499f, 0.33688985339222f, 0.31368174039889f, + 0.29028467725446f, 0.26671275747490f, 0.24298017990326f, 0.21910124015687f, + 0.19509032201613f, 0.17096188876030f, 0.14673047445536f, 0.12241067519922f, + 0.09801714032956f, 0.07356456359967f, 0.04906767432742f, 0.02454122852291f}; + +// hybrib Hanning & flat window +static const float kBlocks80w128[128] = { + 0.00000000f, 0.03271908f, 0.06540313f, 0.09801714f, 0.13052619f, + 0.16289547f, 0.19509032f, 0.22707626f, 0.25881905f, 0.29028468f, + 0.32143947f, 0.35225005f, 0.38268343f, 0.41270703f, 0.44228869f, + 0.47139674f, 0.50000000f, 0.52806785f, 0.55557023f, 0.58247770f, + 0.60876143f, 0.63439328f, 0.65934582f, 0.68359230f, 0.70710678f, + 0.72986407f, 0.75183981f, 0.77301045f, 0.79335334f, 0.81284668f, + 0.83146961f, 0.84920218f, 0.86602540f, 0.88192126f, 0.89687274f, + 0.91086382f, 0.92387953f, 0.93590593f, 0.94693013f, 0.95694034f, + 0.96592583f, 0.97387698f, 0.98078528f, 0.98664333f, 0.99144486f, + 0.99518473f, 0.99785892f, 0.99946459f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 0.99946459f, 0.99785892f, 0.99518473f, 0.99144486f, + 0.98664333f, 0.98078528f, 0.97387698f, 0.96592583f, 0.95694034f, + 0.94693013f, 0.93590593f, 0.92387953f, 0.91086382f, 0.89687274f, + 0.88192126f, 0.86602540f, 0.84920218f, 0.83146961f, 0.81284668f, + 0.79335334f, 0.77301045f, 0.75183981f, 0.72986407f, 0.70710678f, + 0.68359230f, 0.65934582f, 0.63439328f, 0.60876143f, 0.58247770f, + 0.55557023f, 0.52806785f, 0.50000000f, 0.47139674f, 0.44228869f, + 0.41270703f, 0.38268343f, 0.35225005f, 0.32143947f, 0.29028468f, + 0.25881905f, 0.22707626f, 0.19509032f, 0.16289547f, 0.13052619f, + 0.09801714f, 0.06540313f, 0.03271908f}; + +// hybrib Hanning & flat window +static const float kBlocks160w256[256] = { + 0.00000000f, 0.01636173f, 0.03271908f, 0.04906767f, 0.06540313f, + 0.08172107f, 0.09801714f, 0.11428696f, 0.13052619f, 0.14673047f, + 0.16289547f, 0.17901686f, 0.19509032f, 0.21111155f, 0.22707626f, + 0.24298018f, 0.25881905f, 0.27458862f, 0.29028468f, 0.30590302f, + 0.32143947f, 0.33688985f, 0.35225005f, 0.36751594f, 0.38268343f, + 0.39774847f, 0.41270703f, 0.42755509f, 0.44228869f, 0.45690388f, + 0.47139674f, 0.48576339f, 0.50000000f, 0.51410274f, 0.52806785f, + 0.54189158f, 0.55557023f, 0.56910015f, 0.58247770f, 0.59569930f, + 0.60876143f, 0.62166057f, 0.63439328f, 0.64695615f, 0.65934582f, + 0.67155895f, 0.68359230f, 0.69544264f, 0.70710678f, 0.71858162f, + 0.72986407f, 0.74095113f, 0.75183981f, 0.76252720f, 0.77301045f, + 0.78328675f, 0.79335334f, 0.80320753f, 0.81284668f, 0.82226822f, + 0.83146961f, 0.84044840f, 0.84920218f, 0.85772861f, 0.86602540f, + 0.87409034f, 0.88192126f, 0.88951608f, 0.89687274f, 0.90398929f, + 0.91086382f, 0.91749450f, 0.92387953f, 0.93001722f, 0.93590593f, + 0.94154407f, 0.94693013f, 0.95206268f, 0.95694034f, 0.96156180f, + 0.96592583f, 0.97003125f, 0.97387698f, 0.97746197f, 0.98078528f, + 0.98384601f, 0.98664333f, 0.98917651f, 0.99144486f, 0.99344778f, + 0.99518473f, 0.99665524f, 0.99785892f, 0.99879546f, 0.99946459f, + 0.99986614f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 0.99986614f, 0.99946459f, 0.99879546f, 0.99785892f, + 0.99665524f, 0.99518473f, 0.99344778f, 0.99144486f, 0.98917651f, + 0.98664333f, 0.98384601f, 0.98078528f, 0.97746197f, 0.97387698f, + 0.97003125f, 0.96592583f, 0.96156180f, 0.95694034f, 0.95206268f, + 0.94693013f, 0.94154407f, 0.93590593f, 0.93001722f, 0.92387953f, + 0.91749450f, 0.91086382f, 0.90398929f, 0.89687274f, 0.88951608f, + 0.88192126f, 0.87409034f, 0.86602540f, 0.85772861f, 0.84920218f, + 0.84044840f, 0.83146961f, 0.82226822f, 0.81284668f, 0.80320753f, + 0.79335334f, 0.78328675f, 0.77301045f, 0.76252720f, 0.75183981f, + 0.74095113f, 0.72986407f, 0.71858162f, 0.70710678f, 0.69544264f, + 0.68359230f, 0.67155895f, 0.65934582f, 0.64695615f, 0.63439328f, + 0.62166057f, 0.60876143f, 0.59569930f, 0.58247770f, 0.56910015f, + 0.55557023f, 0.54189158f, 0.52806785f, 0.51410274f, 0.50000000f, + 0.48576339f, 0.47139674f, 0.45690388f, 0.44228869f, 0.42755509f, + 0.41270703f, 0.39774847f, 0.38268343f, 0.36751594f, 0.35225005f, + 0.33688985f, 0.32143947f, 0.30590302f, 0.29028468f, 0.27458862f, + 0.25881905f, 0.24298018f, 0.22707626f, 0.21111155f, 0.19509032f, + 0.17901686f, 0.16289547f, 0.14673047f, 0.13052619f, 0.11428696f, + 0.09801714f, 0.08172107f, 0.06540313f, 0.04906767f, 0.03271908f, + 0.01636173f}; + +// hybrib Hanning & flat window: for 20ms +static const float kBlocks320w512[512] = { + 0.00000000f, 0.00818114f, 0.01636173f, 0.02454123f, 0.03271908f, + 0.04089475f, 0.04906767f, 0.05723732f, 0.06540313f, 0.07356456f, + 0.08172107f, 0.08987211f, 0.09801714f, 0.10615561f, 0.11428696f, + 0.12241068f, 0.13052619f, 0.13863297f, 0.14673047f, 0.15481816f, + 0.16289547f, 0.17096189f, 0.17901686f, 0.18705985f, 0.19509032f, + 0.20310773f, 0.21111155f, 0.21910124f, 0.22707626f, 0.23503609f, + 0.24298018f, 0.25090801f, 0.25881905f, 0.26671276f, 0.27458862f, + 0.28244610f, 0.29028468f, 0.29810383f, 0.30590302f, 0.31368174f, + 0.32143947f, 0.32917568f, 0.33688985f, 0.34458148f, 0.35225005f, + 0.35989504f, 0.36751594f, 0.37511224f, 0.38268343f, 0.39022901f, + 0.39774847f, 0.40524131f, 0.41270703f, 0.42014512f, 0.42755509f, + 0.43493645f, 0.44228869f, 0.44961133f, 0.45690388f, 0.46416584f, + 0.47139674f, 0.47859608f, 0.48576339f, 0.49289819f, 0.50000000f, + 0.50706834f, 0.51410274f, 0.52110274f, 0.52806785f, 0.53499762f, + 0.54189158f, 0.54874927f, 0.55557023f, 0.56235401f, 0.56910015f, + 0.57580819f, 0.58247770f, 0.58910822f, 0.59569930f, 0.60225052f, + 0.60876143f, 0.61523159f, 0.62166057f, 0.62804795f, 0.63439328f, + 0.64069616f, 0.64695615f, 0.65317284f, 0.65934582f, 0.66547466f, + 0.67155895f, 0.67759830f, 0.68359230f, 0.68954054f, 0.69544264f, + 0.70129818f, 0.70710678f, 0.71286806f, 0.71858162f, 0.72424708f, + 0.72986407f, 0.73543221f, 0.74095113f, 0.74642045f, 0.75183981f, + 0.75720885f, 0.76252720f, 0.76779452f, 0.77301045f, 0.77817464f, + 0.78328675f, 0.78834643f, 0.79335334f, 0.79830715f, 0.80320753f, + 0.80805415f, 0.81284668f, 0.81758481f, 0.82226822f, 0.82689659f, + 0.83146961f, 0.83598698f, 0.84044840f, 0.84485357f, 0.84920218f, + 0.85349396f, 0.85772861f, 0.86190585f, 0.86602540f, 0.87008699f, + 0.87409034f, 0.87803519f, 0.88192126f, 0.88574831f, 0.88951608f, + 0.89322430f, 0.89687274f, 0.90046115f, 0.90398929f, 0.90745693f, + 0.91086382f, 0.91420976f, 0.91749450f, 0.92071783f, 0.92387953f, + 0.92697940f, 0.93001722f, 0.93299280f, 0.93590593f, 0.93875641f, + 0.94154407f, 0.94426870f, 0.94693013f, 0.94952818f, 0.95206268f, + 0.95453345f, 0.95694034f, 0.95928317f, 0.96156180f, 0.96377607f, + 0.96592583f, 0.96801094f, 0.97003125f, 0.97198664f, 0.97387698f, + 0.97570213f, 0.97746197f, 0.97915640f, 0.98078528f, 0.98234852f, + 0.98384601f, 0.98527764f, 0.98664333f, 0.98794298f, 0.98917651f, + 0.99034383f, 0.99144486f, 0.99247953f, 0.99344778f, 0.99434953f, + 0.99518473f, 0.99595331f, 0.99665524f, 0.99729046f, 0.99785892f, + 0.99836060f, 0.99879546f, 0.99916346f, 0.99946459f, 0.99969882f, + 0.99986614f, 0.99996653f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f, + 1.00000000f, 0.99996653f, 0.99986614f, 0.99969882f, 0.99946459f, + 0.99916346f, 0.99879546f, 0.99836060f, 0.99785892f, 0.99729046f, + 0.99665524f, 0.99595331f, 0.99518473f, 0.99434953f, 0.99344778f, + 0.99247953f, 0.99144486f, 0.99034383f, 0.98917651f, 0.98794298f, + 0.98664333f, 0.98527764f, 0.98384601f, 0.98234852f, 0.98078528f, + 0.97915640f, 0.97746197f, 0.97570213f, 0.97387698f, 0.97198664f, + 0.97003125f, 0.96801094f, 0.96592583f, 0.96377607f, 0.96156180f, + 0.95928317f, 0.95694034f, 0.95453345f, 0.95206268f, 0.94952818f, + 0.94693013f, 0.94426870f, 0.94154407f, 0.93875641f, 0.93590593f, + 0.93299280f, 0.93001722f, 0.92697940f, 0.92387953f, 0.92071783f, + 0.91749450f, 0.91420976f, 0.91086382f, 0.90745693f, 0.90398929f, + 0.90046115f, 0.89687274f, 0.89322430f, 0.88951608f, 0.88574831f, + 0.88192126f, 0.87803519f, 0.87409034f, 0.87008699f, 0.86602540f, + 0.86190585f, 0.85772861f, 0.85349396f, 0.84920218f, 0.84485357f, + 0.84044840f, 0.83598698f, 0.83146961f, 0.82689659f, 0.82226822f, + 0.81758481f, 0.81284668f, 0.80805415f, 0.80320753f, 0.79830715f, + 0.79335334f, 0.78834643f, 0.78328675f, 0.77817464f, 0.77301045f, + 0.76779452f, 0.76252720f, 0.75720885f, 0.75183981f, 0.74642045f, + 0.74095113f, 0.73543221f, 0.72986407f, 0.72424708f, 0.71858162f, + 0.71286806f, 0.70710678f, 0.70129818f, 0.69544264f, 0.68954054f, + 0.68359230f, 0.67759830f, 0.67155895f, 0.66547466f, 0.65934582f, + 0.65317284f, 0.64695615f, 0.64069616f, 0.63439328f, 0.62804795f, + 0.62166057f, 0.61523159f, 0.60876143f, 0.60225052f, 0.59569930f, + 0.58910822f, 0.58247770f, 0.57580819f, 0.56910015f, 0.56235401f, + 0.55557023f, 0.54874927f, 0.54189158f, 0.53499762f, 0.52806785f, + 0.52110274f, 0.51410274f, 0.50706834f, 0.50000000f, 0.49289819f, + 0.48576339f, 0.47859608f, 0.47139674f, 0.46416584f, 0.45690388f, + 0.44961133f, 0.44228869f, 0.43493645f, 0.42755509f, 0.42014512f, + 0.41270703f, 0.40524131f, 0.39774847f, 0.39022901f, 0.38268343f, + 0.37511224f, 0.36751594f, 0.35989504f, 0.35225005f, 0.34458148f, + 0.33688985f, 0.32917568f, 0.32143947f, 0.31368174f, 0.30590302f, + 0.29810383f, 0.29028468f, 0.28244610f, 0.27458862f, 0.26671276f, + 0.25881905f, 0.25090801f, 0.24298018f, 0.23503609f, 0.22707626f, + 0.21910124f, 0.21111155f, 0.20310773f, 0.19509032f, 0.18705985f, + 0.17901686f, 0.17096189f, 0.16289547f, 0.15481816f, 0.14673047f, + 0.13863297f, 0.13052619f, 0.12241068f, 0.11428696f, 0.10615561f, + 0.09801714f, 0.08987211f, 0.08172107f, 0.07356456f, 0.06540313f, + 0.05723732f, 0.04906767f, 0.04089475f, 0.03271908f, 0.02454123f, + 0.01636173f, 0.00818114f}; + +// Hanning window: for 15ms at 16kHz with symmetric zeros +static const float kBlocks240w512[512] = { + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00654494f, 0.01308960f, 0.01963369f, + 0.02617695f, 0.03271908f, 0.03925982f, 0.04579887f, 0.05233596f, + 0.05887080f, 0.06540313f, 0.07193266f, 0.07845910f, 0.08498218f, + 0.09150162f, 0.09801714f, 0.10452846f, 0.11103531f, 0.11753740f, + 0.12403446f, 0.13052620f, 0.13701233f, 0.14349262f, 0.14996676f, + 0.15643448f, 0.16289547f, 0.16934951f, 0.17579629f, 0.18223552f, + 0.18866697f, 0.19509032f, 0.20150533f, 0.20791170f, 0.21430916f, + 0.22069745f, 0.22707628f, 0.23344538f, 0.23980446f, 0.24615330f, + 0.25249159f, 0.25881904f, 0.26513544f, 0.27144045f, 0.27773386f, + 0.28401536f, 0.29028466f, 0.29654160f, 0.30278578f, 0.30901700f, + 0.31523499f, 0.32143945f, 0.32763019f, 0.33380687f, 0.33996925f, + 0.34611708f, 0.35225007f, 0.35836795f, 0.36447051f, 0.37055743f, + 0.37662852f, 0.38268346f, 0.38872197f, 0.39474389f, 0.40074885f, + 0.40673664f, 0.41270703f, 0.41865975f, 0.42459452f, 0.43051112f, + 0.43640924f, 0.44228873f, 0.44814920f, 0.45399052f, 0.45981237f, + 0.46561453f, 0.47139674f, 0.47715878f, 0.48290035f, 0.48862126f, + 0.49432120f, 0.50000000f, 0.50565743f, 0.51129311f, 0.51690692f, + 0.52249855f, 0.52806789f, 0.53361452f, 0.53913832f, 0.54463905f, + 0.55011642f, 0.55557024f, 0.56100029f, 0.56640625f, 0.57178795f, + 0.57714522f, 0.58247769f, 0.58778524f, 0.59306765f, 0.59832460f, + 0.60355598f, 0.60876143f, 0.61394083f, 0.61909395f, 0.62422055f, + 0.62932038f, 0.63439333f, 0.63943899f, 0.64445734f, 0.64944810f, + 0.65441096f, 0.65934587f, 0.66425246f, 0.66913062f, 0.67398012f, + 0.67880076f, 0.68359232f, 0.68835455f, 0.69308740f, 0.69779050f, + 0.70246369f, 0.70710677f, 0.71171963f, 0.71630198f, 0.72085363f, + 0.72537440f, 0.72986406f, 0.73432255f, 0.73874950f, 0.74314487f, + 0.74750835f, 0.75183982f, 0.75613910f, 0.76040596f, 0.76464027f, + 0.76884186f, 0.77301043f, 0.77714598f, 0.78124821f, 0.78531694f, + 0.78935206f, 0.79335338f, 0.79732066f, 0.80125386f, 0.80515265f, + 0.80901700f, 0.81284672f, 0.81664157f, 0.82040149f, 0.82412618f, + 0.82781565f, 0.83146966f, 0.83508795f, 0.83867061f, 0.84221727f, + 0.84572780f, 0.84920216f, 0.85264021f, 0.85604161f, 0.85940641f, + 0.86273444f, 0.86602545f, 0.86927933f, 0.87249607f, 0.87567532f, + 0.87881714f, 0.88192129f, 0.88498765f, 0.88801610f, 0.89100653f, + 0.89395881f, 0.89687276f, 0.89974827f, 0.90258533f, 0.90538365f, + 0.90814316f, 0.91086388f, 0.91354549f, 0.91618794f, 0.91879123f, + 0.92135513f, 0.92387950f, 0.92636442f, 0.92880958f, 0.93121493f, + 0.93358046f, 0.93590593f, 0.93819135f, 0.94043654f, 0.94264150f, + 0.94480604f, 0.94693011f, 0.94901365f, 0.95105654f, 0.95305866f, + 0.95501995f, 0.95694035f, 0.95881975f, 0.96065807f, 0.96245527f, + 0.96421117f, 0.96592581f, 0.96759909f, 0.96923089f, 0.97082120f, + 0.97236991f, 0.97387701f, 0.97534233f, 0.97676587f, 0.97814763f, + 0.97948742f, 0.98078531f, 0.98204112f, 0.98325491f, 0.98442656f, + 0.98555607f, 0.98664331f, 0.98768836f, 0.98869103f, 0.98965138f, + 0.99056935f, 0.99144489f, 0.99227792f, 0.99306846f, 0.99381649f, + 0.99452192f, 0.99518472f, 0.99580491f, 0.99638247f, 0.99691731f, + 0.99740952f, 0.99785894f, 0.99826562f, 0.99862951f, 0.99895066f, + 0.99922901f, 0.99946457f, 0.99965733f, 0.99980724f, 0.99991435f, + 0.99997860f, 1.00000000f, 0.99997860f, 0.99991435f, 0.99980724f, + 0.99965733f, 0.99946457f, 0.99922901f, 0.99895066f, 0.99862951f, + 0.99826562f, 0.99785894f, 0.99740946f, 0.99691731f, 0.99638247f, + 0.99580491f, 0.99518472f, 0.99452192f, 0.99381644f, 0.99306846f, + 0.99227792f, 0.99144489f, 0.99056935f, 0.98965138f, 0.98869103f, + 0.98768836f, 0.98664331f, 0.98555607f, 0.98442656f, 0.98325491f, + 0.98204112f, 0.98078525f, 0.97948742f, 0.97814757f, 0.97676587f, + 0.97534227f, 0.97387695f, 0.97236991f, 0.97082120f, 0.96923089f, + 0.96759909f, 0.96592581f, 0.96421117f, 0.96245521f, 0.96065807f, + 0.95881969f, 0.95694029f, 0.95501995f, 0.95305860f, 0.95105648f, + 0.94901365f, 0.94693011f, 0.94480604f, 0.94264150f, 0.94043654f, + 0.93819129f, 0.93590593f, 0.93358046f, 0.93121493f, 0.92880952f, + 0.92636436f, 0.92387950f, 0.92135507f, 0.91879123f, 0.91618794f, + 0.91354543f, 0.91086382f, 0.90814310f, 0.90538365f, 0.90258527f, + 0.89974827f, 0.89687276f, 0.89395875f, 0.89100647f, 0.88801610f, + 0.88498759f, 0.88192123f, 0.87881714f, 0.87567532f, 0.87249595f, + 0.86927933f, 0.86602539f, 0.86273432f, 0.85940641f, 0.85604161f, + 0.85264009f, 0.84920216f, 0.84572780f, 0.84221715f, 0.83867055f, + 0.83508795f, 0.83146954f, 0.82781565f, 0.82412612f, 0.82040137f, + 0.81664157f, 0.81284660f, 0.80901700f, 0.80515265f, 0.80125374f, + 0.79732066f, 0.79335332f, 0.78935200f, 0.78531694f, 0.78124815f, + 0.77714586f, 0.77301049f, 0.76884180f, 0.76464021f, 0.76040596f, + 0.75613904f, 0.75183970f, 0.74750835f, 0.74314481f, 0.73874938f, + 0.73432249f, 0.72986400f, 0.72537428f, 0.72085363f, 0.71630186f, + 0.71171951f, 0.70710677f, 0.70246363f, 0.69779032f, 0.69308734f, + 0.68835449f, 0.68359220f, 0.67880070f, 0.67398006f, 0.66913044f, + 0.66425240f, 0.65934575f, 0.65441096f, 0.64944804f, 0.64445722f, + 0.63943905f, 0.63439327f, 0.62932026f, 0.62422055f, 0.61909389f, + 0.61394072f, 0.60876143f, 0.60355592f, 0.59832448f, 0.59306765f, + 0.58778518f, 0.58247757f, 0.57714522f, 0.57178789f, 0.56640613f, + 0.56100023f, 0.55557019f, 0.55011630f, 0.54463905f, 0.53913826f, + 0.53361434f, 0.52806783f, 0.52249849f, 0.51690674f, 0.51129305f, + 0.50565726f, 0.50000006f, 0.49432117f, 0.48862115f, 0.48290038f, + 0.47715873f, 0.47139663f, 0.46561456f, 0.45981231f, 0.45399037f, + 0.44814920f, 0.44228864f, 0.43640912f, 0.43051112f, 0.42459446f, + 0.41865960f, 0.41270703f, 0.40673658f, 0.40074870f, 0.39474386f, + 0.38872188f, 0.38268328f, 0.37662849f, 0.37055734f, 0.36447033f, + 0.35836792f, 0.35224995f, 0.34611690f, 0.33996922f, 0.33380675f, + 0.32763001f, 0.32143945f, 0.31523487f, 0.30901679f, 0.30278572f, + 0.29654145f, 0.29028472f, 0.28401530f, 0.27773371f, 0.27144048f, + 0.26513538f, 0.25881892f, 0.25249159f, 0.24615324f, 0.23980433f, + 0.23344538f, 0.22707619f, 0.22069728f, 0.21430916f, 0.20791161f, + 0.20150517f, 0.19509031f, 0.18866688f, 0.18223536f, 0.17579627f, + 0.16934940f, 0.16289529f, 0.15643445f, 0.14996666f, 0.14349243f, + 0.13701232f, 0.13052608f, 0.12403426f, 0.11753736f, 0.11103519f, + 0.10452849f, 0.09801710f, 0.09150149f, 0.08498220f, 0.07845904f, + 0.07193252f, 0.06540315f, 0.05887074f, 0.05233581f, 0.04579888f, + 0.03925974f, 0.03271893f, 0.02617695f, 0.01963361f, 0.01308943f, + 0.00654493f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f}; + +// Hanning window: for 30ms with 1024 fft with symmetric zeros at 16kHz +static const float kBlocks480w1024[1024] = { + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00327249f, 0.00654494f, + 0.00981732f, 0.01308960f, 0.01636173f, 0.01963369f, 0.02290544f, + 0.02617695f, 0.02944817f, 0.03271908f, 0.03598964f, 0.03925982f, + 0.04252957f, 0.04579887f, 0.04906768f, 0.05233596f, 0.05560368f, + 0.05887080f, 0.06213730f, 0.06540313f, 0.06866825f, 0.07193266f, + 0.07519628f, 0.07845910f, 0.08172107f, 0.08498218f, 0.08824237f, + 0.09150162f, 0.09475989f, 0.09801714f, 0.10127335f, 0.10452846f, + 0.10778246f, 0.11103531f, 0.11428697f, 0.11753740f, 0.12078657f, + 0.12403446f, 0.12728101f, 0.13052620f, 0.13376999f, 0.13701233f, + 0.14025325f, 0.14349262f, 0.14673047f, 0.14996676f, 0.15320145f, + 0.15643448f, 0.15966582f, 0.16289547f, 0.16612339f, 0.16934951f, + 0.17257382f, 0.17579629f, 0.17901687f, 0.18223552f, 0.18545224f, + 0.18866697f, 0.19187967f, 0.19509032f, 0.19829889f, 0.20150533f, + 0.20470962f, 0.20791170f, 0.21111156f, 0.21430916f, 0.21750447f, + 0.22069745f, 0.22388805f, 0.22707628f, 0.23026206f, 0.23344538f, + 0.23662618f, 0.23980446f, 0.24298020f, 0.24615330f, 0.24932377f, + 0.25249159f, 0.25565669f, 0.25881904f, 0.26197866f, 0.26513544f, + 0.26828939f, 0.27144045f, 0.27458861f, 0.27773386f, 0.28087610f, + 0.28401536f, 0.28715158f, 0.29028466f, 0.29341471f, 0.29654160f, + 0.29966527f, 0.30278578f, 0.30590302f, 0.30901700f, 0.31212768f, + 0.31523499f, 0.31833893f, 0.32143945f, 0.32453656f, 0.32763019f, + 0.33072028f, 0.33380687f, 0.33688986f, 0.33996925f, 0.34304500f, + 0.34611708f, 0.34918544f, 0.35225007f, 0.35531089f, 0.35836795f, + 0.36142117f, 0.36447051f, 0.36751595f, 0.37055743f, 0.37359497f, + 0.37662852f, 0.37965801f, 0.38268346f, 0.38570479f, 0.38872197f, + 0.39173502f, 0.39474389f, 0.39774847f, 0.40074885f, 0.40374491f, + 0.40673664f, 0.40972406f, 0.41270703f, 0.41568562f, 0.41865975f, + 0.42162940f, 0.42459452f, 0.42755508f, 0.43051112f, 0.43346250f, + 0.43640924f, 0.43935132f, 0.44228873f, 0.44522133f, 0.44814920f, + 0.45107228f, 0.45399052f, 0.45690390f, 0.45981237f, 0.46271592f, + 0.46561453f, 0.46850815f, 0.47139674f, 0.47428030f, 0.47715878f, + 0.48003215f, 0.48290035f, 0.48576337f, 0.48862126f, 0.49147385f, + 0.49432120f, 0.49716330f, 0.50000000f, 0.50283140f, 0.50565743f, + 0.50847799f, 0.51129311f, 0.51410276f, 0.51690692f, 0.51970553f, + 0.52249855f, 0.52528602f, 0.52806789f, 0.53084403f, 0.53361452f, + 0.53637928f, 0.53913832f, 0.54189163f, 0.54463905f, 0.54738063f, + 0.55011642f, 0.55284631f, 0.55557024f, 0.55828828f, 0.56100029f, + 0.56370628f, 0.56640625f, 0.56910014f, 0.57178795f, 0.57446963f, + 0.57714522f, 0.57981455f, 0.58247769f, 0.58513463f, 0.58778524f, + 0.59042960f, 0.59306765f, 0.59569931f, 0.59832460f, 0.60094351f, + 0.60355598f, 0.60616195f, 0.60876143f, 0.61135441f, 0.61394083f, + 0.61652070f, 0.61909395f, 0.62166059f, 0.62422055f, 0.62677383f, + 0.62932038f, 0.63186020f, 0.63439333f, 0.63691956f, 0.63943899f, + 0.64195162f, 0.64445734f, 0.64695615f, 0.64944810f, 0.65193301f, + 0.65441096f, 0.65688187f, 0.65934587f, 0.66180271f, 0.66425246f, + 0.66669512f, 0.66913062f, 0.67155898f, 0.67398012f, 0.67639405f, + 0.67880076f, 0.68120021f, 0.68359232f, 0.68597710f, 0.68835455f, + 0.69072467f, 0.69308740f, 0.69544262f, 0.69779050f, 0.70013082f, + 0.70246369f, 0.70478904f, 0.70710677f, 0.70941699f, 0.71171963f, + 0.71401459f, 0.71630198f, 0.71858168f, 0.72085363f, 0.72311789f, + 0.72537440f, 0.72762316f, 0.72986406f, 0.73209721f, 0.73432255f, + 0.73653996f, 0.73874950f, 0.74095118f, 0.74314487f, 0.74533057f, + 0.74750835f, 0.74967808f, 0.75183982f, 0.75399351f, 0.75613910f, + 0.75827658f, 0.76040596f, 0.76252723f, 0.76464027f, 0.76674515f, + 0.76884186f, 0.77093029f, 0.77301043f, 0.77508241f, 0.77714598f, + 0.77920127f, 0.78124821f, 0.78328675f, 0.78531694f, 0.78733873f, + 0.78935206f, 0.79135692f, 0.79335338f, 0.79534125f, 0.79732066f, + 0.79929149f, 0.80125386f, 0.80320752f, 0.80515265f, 0.80708915f, + 0.80901700f, 0.81093621f, 0.81284672f, 0.81474853f, 0.81664157f, + 0.81852591f, 0.82040149f, 0.82226825f, 0.82412618f, 0.82597536f, + 0.82781565f, 0.82964706f, 0.83146966f, 0.83328325f, 0.83508795f, + 0.83688378f, 0.83867061f, 0.84044838f, 0.84221727f, 0.84397703f, + 0.84572780f, 0.84746957f, 0.84920216f, 0.85092574f, 0.85264021f, + 0.85434544f, 0.85604161f, 0.85772866f, 0.85940641f, 0.86107504f, + 0.86273444f, 0.86438453f, 0.86602545f, 0.86765707f, 0.86927933f, + 0.87089235f, 0.87249607f, 0.87409031f, 0.87567532f, 0.87725097f, + 0.87881714f, 0.88037390f, 0.88192129f, 0.88345921f, 0.88498765f, + 0.88650668f, 0.88801610f, 0.88951612f, 0.89100653f, 0.89248741f, + 0.89395881f, 0.89542055f, 0.89687276f, 0.89831537f, 0.89974827f, + 0.90117162f, 0.90258533f, 0.90398932f, 0.90538365f, 0.90676826f, + 0.90814316f, 0.90950841f, 0.91086388f, 0.91220951f, 0.91354549f, + 0.91487163f, 0.91618794f, 0.91749454f, 0.91879123f, 0.92007810f, + 0.92135513f, 0.92262226f, 0.92387950f, 0.92512691f, 0.92636442f, + 0.92759192f, 0.92880958f, 0.93001723f, 0.93121493f, 0.93240267f, + 0.93358046f, 0.93474817f, 0.93590593f, 0.93705362f, 0.93819135f, + 0.93931901f, 0.94043654f, 0.94154406f, 0.94264150f, 0.94372880f, + 0.94480604f, 0.94587320f, 0.94693011f, 0.94797695f, 0.94901365f, + 0.95004016f, 0.95105654f, 0.95206273f, 0.95305866f, 0.95404440f, + 0.95501995f, 0.95598525f, 0.95694035f, 0.95788521f, 0.95881975f, + 0.95974404f, 0.96065807f, 0.96156180f, 0.96245527f, 0.96333838f, + 0.96421117f, 0.96507370f, 0.96592581f, 0.96676767f, 0.96759909f, + 0.96842021f, 0.96923089f, 0.97003126f, 0.97082120f, 0.97160077f, + 0.97236991f, 0.97312868f, 0.97387701f, 0.97461486f, 0.97534233f, + 0.97605932f, 0.97676587f, 0.97746199f, 0.97814763f, 0.97882277f, + 0.97948742f, 0.98014158f, 0.98078531f, 0.98141843f, 0.98204112f, + 0.98265332f, 0.98325491f, 0.98384601f, 0.98442656f, 0.98499662f, + 0.98555607f, 0.98610497f, 0.98664331f, 0.98717111f, 0.98768836f, + 0.98819500f, 0.98869103f, 0.98917651f, 0.98965138f, 0.99011570f, + 0.99056935f, 0.99101239f, 0.99144489f, 0.99186671f, 0.99227792f, + 0.99267852f, 0.99306846f, 0.99344778f, 0.99381649f, 0.99417448f, + 0.99452192f, 0.99485862f, 0.99518472f, 0.99550015f, 0.99580491f, + 0.99609905f, 0.99638247f, 0.99665523f, 0.99691731f, 0.99716878f, + 0.99740952f, 0.99763954f, 0.99785894f, 0.99806762f, 0.99826562f, + 0.99845290f, 0.99862951f, 0.99879545f, 0.99895066f, 0.99909520f, + 0.99922901f, 0.99935216f, 0.99946457f, 0.99956632f, 0.99965733f, + 0.99973762f, 0.99980724f, 0.99986613f, 0.99991435f, 0.99995178f, + 0.99997860f, 0.99999464f, 1.00000000f, 0.99999464f, 0.99997860f, + 0.99995178f, 0.99991435f, 0.99986613f, 0.99980724f, 0.99973762f, + 0.99965733f, 0.99956632f, 0.99946457f, 0.99935216f, 0.99922901f, + 0.99909520f, 0.99895066f, 0.99879545f, 0.99862951f, 0.99845290f, + 0.99826562f, 0.99806762f, 0.99785894f, 0.99763954f, 0.99740946f, + 0.99716872f, 0.99691731f, 0.99665523f, 0.99638247f, 0.99609905f, + 0.99580491f, 0.99550015f, 0.99518472f, 0.99485862f, 0.99452192f, + 0.99417448f, 0.99381644f, 0.99344778f, 0.99306846f, 0.99267852f, + 0.99227792f, 0.99186671f, 0.99144489f, 0.99101239f, 0.99056935f, + 0.99011564f, 0.98965138f, 0.98917651f, 0.98869103f, 0.98819494f, + 0.98768836f, 0.98717111f, 0.98664331f, 0.98610497f, 0.98555607f, + 0.98499656f, 0.98442656f, 0.98384601f, 0.98325491f, 0.98265326f, + 0.98204112f, 0.98141843f, 0.98078525f, 0.98014158f, 0.97948742f, + 0.97882277f, 0.97814757f, 0.97746193f, 0.97676587f, 0.97605932f, + 0.97534227f, 0.97461486f, 0.97387695f, 0.97312862f, 0.97236991f, + 0.97160077f, 0.97082120f, 0.97003126f, 0.96923089f, 0.96842015f, + 0.96759909f, 0.96676761f, 0.96592581f, 0.96507365f, 0.96421117f, + 0.96333838f, 0.96245521f, 0.96156180f, 0.96065807f, 0.95974404f, + 0.95881969f, 0.95788515f, 0.95694029f, 0.95598525f, 0.95501995f, + 0.95404440f, 0.95305860f, 0.95206267f, 0.95105648f, 0.95004016f, + 0.94901365f, 0.94797695f, 0.94693011f, 0.94587314f, 0.94480604f, + 0.94372880f, 0.94264150f, 0.94154406f, 0.94043654f, 0.93931895f, + 0.93819129f, 0.93705362f, 0.93590593f, 0.93474817f, 0.93358046f, + 0.93240267f, 0.93121493f, 0.93001723f, 0.92880952f, 0.92759192f, + 0.92636436f, 0.92512691f, 0.92387950f, 0.92262226f, 0.92135507f, + 0.92007804f, 0.91879123f, 0.91749448f, 0.91618794f, 0.91487157f, + 0.91354543f, 0.91220951f, 0.91086382f, 0.90950835f, 0.90814310f, + 0.90676820f, 0.90538365f, 0.90398932f, 0.90258527f, 0.90117157f, + 0.89974827f, 0.89831525f, 0.89687276f, 0.89542055f, 0.89395875f, + 0.89248741f, 0.89100647f, 0.88951600f, 0.88801610f, 0.88650662f, + 0.88498759f, 0.88345915f, 0.88192123f, 0.88037384f, 0.87881714f, + 0.87725091f, 0.87567532f, 0.87409031f, 0.87249595f, 0.87089223f, + 0.86927933f, 0.86765701f, 0.86602539f, 0.86438447f, 0.86273432f, + 0.86107504f, 0.85940641f, 0.85772860f, 0.85604161f, 0.85434544f, + 0.85264009f, 0.85092574f, 0.84920216f, 0.84746951f, 0.84572780f, + 0.84397697f, 0.84221715f, 0.84044844f, 0.83867055f, 0.83688372f, + 0.83508795f, 0.83328319f, 0.83146954f, 0.82964706f, 0.82781565f, + 0.82597530f, 0.82412612f, 0.82226813f, 0.82040137f, 0.81852591f, + 0.81664157f, 0.81474847f, 0.81284660f, 0.81093609f, 0.80901700f, + 0.80708915f, 0.80515265f, 0.80320752f, 0.80125374f, 0.79929143f, + 0.79732066f, 0.79534125f, 0.79335332f, 0.79135686f, 0.78935200f, + 0.78733861f, 0.78531694f, 0.78328675f, 0.78124815f, 0.77920121f, + 0.77714586f, 0.77508223f, 0.77301049f, 0.77093029f, 0.76884180f, + 0.76674509f, 0.76464021f, 0.76252711f, 0.76040596f, 0.75827658f, + 0.75613904f, 0.75399339f, 0.75183970f, 0.74967796f, 0.74750835f, + 0.74533057f, 0.74314481f, 0.74095106f, 0.73874938f, 0.73653996f, + 0.73432249f, 0.73209721f, 0.72986400f, 0.72762305f, 0.72537428f, + 0.72311789f, 0.72085363f, 0.71858162f, 0.71630186f, 0.71401453f, + 0.71171951f, 0.70941705f, 0.70710677f, 0.70478898f, 0.70246363f, + 0.70013070f, 0.69779032f, 0.69544268f, 0.69308734f, 0.69072461f, + 0.68835449f, 0.68597704f, 0.68359220f, 0.68120021f, 0.67880070f, + 0.67639399f, 0.67398006f, 0.67155886f, 0.66913044f, 0.66669512f, + 0.66425240f, 0.66180259f, 0.65934575f, 0.65688181f, 0.65441096f, + 0.65193301f, 0.64944804f, 0.64695609f, 0.64445722f, 0.64195150f, + 0.63943905f, 0.63691956f, 0.63439327f, 0.63186014f, 0.62932026f, + 0.62677372f, 0.62422055f, 0.62166059f, 0.61909389f, 0.61652064f, + 0.61394072f, 0.61135429f, 0.60876143f, 0.60616189f, 0.60355592f, + 0.60094339f, 0.59832448f, 0.59569913f, 0.59306765f, 0.59042960f, + 0.58778518f, 0.58513451f, 0.58247757f, 0.57981461f, 0.57714522f, + 0.57446963f, 0.57178789f, 0.56910002f, 0.56640613f, 0.56370628f, + 0.56100023f, 0.55828822f, 0.55557019f, 0.55284619f, 0.55011630f, + 0.54738069f, 0.54463905f, 0.54189152f, 0.53913826f, 0.53637916f, + 0.53361434f, 0.53084403f, 0.52806783f, 0.52528596f, 0.52249849f, + 0.51970541f, 0.51690674f, 0.51410276f, 0.51129305f, 0.50847787f, + 0.50565726f, 0.50283122f, 0.50000006f, 0.49716327f, 0.49432117f, + 0.49147379f, 0.48862115f, 0.48576325f, 0.48290038f, 0.48003212f, + 0.47715873f, 0.47428021f, 0.47139663f, 0.46850798f, 0.46561456f, + 0.46271589f, 0.45981231f, 0.45690379f, 0.45399037f, 0.45107210f, + 0.44814920f, 0.44522130f, 0.44228864f, 0.43935123f, 0.43640912f, + 0.43346232f, 0.43051112f, 0.42755505f, 0.42459446f, 0.42162928f, + 0.41865960f, 0.41568545f, 0.41270703f, 0.40972400f, 0.40673658f, + 0.40374479f, 0.40074870f, 0.39774850f, 0.39474386f, 0.39173496f, + 0.38872188f, 0.38570464f, 0.38268328f, 0.37965804f, 0.37662849f, + 0.37359491f, 0.37055734f, 0.36751580f, 0.36447033f, 0.36142117f, + 0.35836792f, 0.35531086f, 0.35224995f, 0.34918529f, 0.34611690f, + 0.34304500f, 0.33996922f, 0.33688980f, 0.33380675f, 0.33072016f, + 0.32763001f, 0.32453656f, 0.32143945f, 0.31833887f, 0.31523487f, + 0.31212750f, 0.30901679f, 0.30590302f, 0.30278572f, 0.29966521f, + 0.29654145f, 0.29341453f, 0.29028472f, 0.28715155f, 0.28401530f, + 0.28087601f, 0.27773371f, 0.27458847f, 0.27144048f, 0.26828936f, + 0.26513538f, 0.26197854f, 0.25881892f, 0.25565651f, 0.25249159f, + 0.24932374f, 0.24615324f, 0.24298008f, 0.23980433f, 0.23662600f, + 0.23344538f, 0.23026201f, 0.22707619f, 0.22388794f, 0.22069728f, + 0.21750426f, 0.21430916f, 0.21111152f, 0.20791161f, 0.20470949f, + 0.20150517f, 0.19829892f, 0.19509031f, 0.19187963f, 0.18866688f, + 0.18545210f, 0.18223536f, 0.17901689f, 0.17579627f, 0.17257376f, + 0.16934940f, 0.16612324f, 0.16289529f, 0.15966584f, 0.15643445f, + 0.15320137f, 0.14996666f, 0.14673033f, 0.14349243f, 0.14025325f, + 0.13701232f, 0.13376991f, 0.13052608f, 0.12728085f, 0.12403426f, + 0.12078657f, 0.11753736f, 0.11428688f, 0.11103519f, 0.10778230f, + 0.10452849f, 0.10127334f, 0.09801710f, 0.09475980f, 0.09150149f, + 0.08824220f, 0.08498220f, 0.08172106f, 0.07845904f, 0.07519618f, + 0.07193252f, 0.06866808f, 0.06540315f, 0.06213728f, 0.05887074f, + 0.05560357f, 0.05233581f, 0.04906749f, 0.04579888f, 0.04252954f, + 0.03925974f, 0.03598953f, 0.03271893f, 0.02944798f, 0.02617695f, + 0.02290541f, 0.01963361f, 0.01636161f, 0.01308943f, 0.00981712f, + 0.00654493f, 0.00327244f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_WINDOWS_PRIVATE_H_ diff --git a/webrtc/modules/audio_processing/transient/wpd_node.cc b/webrtc/modules/audio_processing/transient/wpd_node.cc index 8114a70..2e0ee7e 100644 --- a/webrtc/modules/audio_processing/transient/wpd_node.cc +++ b/webrtc/modules/audio_processing/transient/wpd_node.cc @@ -8,29 +8,30 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/transient/wpd_node.h" +#include "modules/audio_processing/transient/wpd_node.h" -#include #include #include -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/fir_filter.h" -#include "webrtc/modules/audio_processing/transient/dyadic_decimator.h" +#include "common_audio/fir_filter.h" +#include "common_audio/fir_filter_factory.h" +#include "modules/audio_processing/transient/dyadic_decimator.h" +#include "rtc_base/checks.h" namespace webrtc { WPDNode::WPDNode(size_t length, const float* coefficients, size_t coefficients_length) - : // The data buffer has parent data length to be able to contain and filter - // it. + : // The data buffer has parent data length to be able to contain and + // filter it. data_(new float[2 * length + 1]), length_(length), - filter_(FIRFilter::Create(coefficients, - coefficients_length, - 2 * length + 1)) { - assert(length > 0 && coefficients && coefficients_length > 0); + filter_( + CreateFirFilter(coefficients, coefficients_length, 2 * length + 1)) { + RTC_DCHECK_GT(length, 0); + RTC_DCHECK(coefficients); + RTC_DCHECK_GT(coefficients_length, 0); memset(data_.get(), 0.f, (2 * length + 1) * sizeof(data_[0])); } @@ -46,8 +47,8 @@ int WPDNode::Update(const float* parent_data, size_t parent_data_length) { // Decimate data. const bool kOddSequence = true; - size_t output_samples = DyadicDecimate( - data_.get(), parent_data_length, kOddSequence, data_.get(), length_); + size_t output_samples = DyadicDecimate(data_.get(), parent_data_length, + kOddSequence, data_.get(), length_); if (output_samples != length_) { return -1; } diff --git a/webrtc/modules/audio_processing/transient/wpd_node.h b/webrtc/modules/audio_processing/transient/wpd_node.h index f66cad9..6a52fb7 100644 --- a/webrtc/modules/audio_processing/transient/wpd_node.h +++ b/webrtc/modules/audio_processing/transient/wpd_node.h @@ -8,11 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_NODE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_NODE_H_ +#ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_NODE_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_NODE_H_ -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/typedefs.h" +#include namespace webrtc { @@ -36,11 +35,11 @@ class WPDNode { size_t length() const { return length_; } private: - rtc::scoped_ptr data_; + std::unique_ptr data_; size_t length_; - rtc::scoped_ptr filter_; + std::unique_ptr filter_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_NODE_H_ +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_NODE_H_ diff --git a/webrtc/modules/audio_processing/transient/wpd_tree.cc b/webrtc/modules/audio_processing/transient/wpd_tree.cc index 40a37a0..c8aa615 100644 --- a/webrtc/modules/audio_processing/transient/wpd_tree.cc +++ b/webrtc/modules/audio_processing/transient/wpd_tree.cc @@ -8,31 +8,30 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/transient/wpd_tree.h" +#include "modules/audio_processing/transient/wpd_tree.h" -#include -#include #include -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/modules/audio_processing/transient/dyadic_decimator.h" -#include "webrtc/modules/audio_processing/transient/wpd_node.h" +#include "modules/audio_processing/transient/wpd_node.h" +#include "rtc_base/checks.h" namespace webrtc { -WPDTree::WPDTree(size_t data_length, const float* high_pass_coefficients, - const float* low_pass_coefficients, size_t coefficients_length, +WPDTree::WPDTree(size_t data_length, + const float* high_pass_coefficients, + const float* low_pass_coefficients, + size_t coefficients_length, int levels) : data_length_(data_length), levels_(levels), num_nodes_((1 << (levels + 1)) - 1) { - assert(data_length > (static_cast(1) << levels) && - high_pass_coefficients && - low_pass_coefficients && - levels > 0); + RTC_DCHECK_GT(data_length, (static_cast(1) << levels)); + RTC_DCHECK(high_pass_coefficients); + RTC_DCHECK(low_pass_coefficients); + RTC_DCHECK_GT(levels, 0); // Size is 1 more, so we can use the array as 1-based. nodes_[0] is never // allocated. - nodes_.reset(new rtc::scoped_ptr[num_nodes_ + 1]); + nodes_.reset(new std::unique_ptr[num_nodes_ + 1]); // Create the first node const float kRootCoefficient = 1.f; // Identity Coefficient. @@ -66,10 +65,10 @@ WPDTree::WPDTree(size_t data_length, const float* high_pass_coefficients, WPDTree::~WPDTree() {} WPDNode* WPDTree::NodeAt(int level, int index) { - const int kNumNodesAtLevel = 1 << level; - if (level < 0 || level > levels_ || index < 0 || index >= kNumNodesAtLevel) { + if (level < 0 || level > levels_ || index < 0 || index >= 1 << level) { return NULL; } + return nodes_[(1 << level) + index].get(); } @@ -99,8 +98,8 @@ int WPDTree::Update(const float* data, size_t data_length) { index_left_child = index * 2; index_right_child = index_left_child + 1; - update_result = nodes_[index_left_child]->Update( - nodes_[index]->data(), nodes_[index]->length()); + update_result = nodes_[index_left_child]->Update(nodes_[index]->data(), + nodes_[index]->length()); if (update_result != 0) { return -1; } diff --git a/webrtc/modules/audio_processing/transient/wpd_tree.h b/webrtc/modules/audio_processing/transient/wpd_tree.h index 7f0fc79..c54220f 100644 --- a/webrtc/modules/audio_processing/transient/wpd_tree.h +++ b/webrtc/modules/audio_processing/transient/wpd_tree.h @@ -8,11 +8,14 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_TREE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_TREE_H_ +#ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_TREE_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_TREE_H_ -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/modules/audio_processing/transient/wpd_node.h" +#include + +#include + +#include "modules/audio_processing/transient/wpd_node.h" namespace webrtc { @@ -46,9 +49,7 @@ class WPDTree { ~WPDTree(); // Returns the number of nodes at any given level. - static int NumberOfNodesAtLevel(int level) { - return 1 << level; - } + static int NumberOfNodesAtLevel(int level) { return 1 << level; } // Returns a pointer to the node at the given level and index(of that level). // Level goes from 0 to levels(). @@ -83,9 +84,9 @@ class WPDTree { size_t data_length_; int levels_; int num_nodes_; - rtc::scoped_ptr[]> nodes_; + std::unique_ptr[]> nodes_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_TREE_H_ +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_WPD_TREE_H_ diff --git a/webrtc/modules/audio_processing/typing_detection.cc b/webrtc/modules/audio_processing/typing_detection.cc index 5f5ce0a..e725b26 100644 --- a/webrtc/modules/audio_processing/typing_detection.cc +++ b/webrtc/modules/audio_processing/typing_detection.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/typing_detection.h" +#include "modules/audio_processing/typing_detection.h" namespace webrtc { @@ -24,8 +24,7 @@ TypingDetection::TypingDetection() reporting_threshold_(300), penalty_decay_(1), type_event_delay_(2), - report_detection_update_period_(1) { -} + report_detection_update_period_(1) {} TypingDetection::~TypingDetection() {} @@ -41,8 +40,7 @@ bool TypingDetection::Process(bool key_pressed, bool vad_activity) { else ++time_since_last_typing_; - if (time_since_last_typing_ < type_event_delay_ && - vad_activity && + if (time_since_last_typing_ < type_event_delay_ && vad_activity && time_active_ < time_window_) { penalty_counter_ += cost_per_typing_; if (penalty_counter_ > reporting_threshold_) @@ -73,15 +71,20 @@ void TypingDetection::SetParameters(int time_window, int penalty_decay, int type_event_delay, int report_detection_update_period) { - if (time_window) time_window_ = time_window; + if (time_window) + time_window_ = time_window; - if (cost_per_typing) cost_per_typing_ = cost_per_typing; + if (cost_per_typing) + cost_per_typing_ = cost_per_typing; - if (reporting_threshold) reporting_threshold_ = reporting_threshold; + if (reporting_threshold) + reporting_threshold_ = reporting_threshold; - if (penalty_decay) penalty_decay_ = penalty_decay; + if (penalty_decay) + penalty_decay_ = penalty_decay; - if (type_event_delay) type_event_delay_ = type_event_delay; + if (type_event_delay) + type_event_delay_ = type_event_delay; if (report_detection_update_period) report_detection_update_period_ = report_detection_update_period; diff --git a/webrtc/modules/audio_processing/typing_detection.h b/webrtc/modules/audio_processing/typing_detection.h index 5fa6456..d8fb359 100644 --- a/webrtc/modules/audio_processing/typing_detection.h +++ b/webrtc/modules/audio_processing/typing_detection.h @@ -8,15 +8,14 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TYPING_DETECTION_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_TYPING_DETECTION_H_ +#ifndef MODULES_AUDIO_PROCESSING_TYPING_DETECTION_H_ +#define MODULES_AUDIO_PROCESSING_TYPING_DETECTION_H_ -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/typedefs.h" +#include "rtc_base/system/rtc_export.h" namespace webrtc { -class TypingDetection { +class RTC_EXPORT TypingDetection { public: TypingDetection(); virtual ~TypingDetection(); @@ -90,4 +89,4 @@ class TypingDetection { } // namespace webrtc -#endif // #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TYPING_DETECTION_H_ +#endif // #ifndef MODULES_AUDIO_PROCESSING_TYPING_DETECTION_H_ diff --git a/webrtc/modules/audio_processing/utility/BUILD.gn b/webrtc/modules/audio_processing/utility/BUILD.gn new file mode 100644 index 0000000..437b544 --- /dev/null +++ b/webrtc/modules/audio_processing/utility/BUILD.gn @@ -0,0 +1,81 @@ +# Copyright (c) 2014 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. + +import("../../../webrtc.gni") + +rtc_library("cascaded_biquad_filter") { + sources = [ + "cascaded_biquad_filter.cc", + "cascaded_biquad_filter.h", + ] + deps = [ + "../../../api:array_view", + "../../../rtc_base:checks", + ] +} + +rtc_library("legacy_delay_estimator") { + sources = [ + "delay_estimator.cc", + "delay_estimator.h", + "delay_estimator_internal.h", + "delay_estimator_wrapper.cc", + "delay_estimator_wrapper.h", + ] + deps = [ "../../../rtc_base:checks" ] +} + +rtc_library("pffft_wrapper") { + visibility = [ "../*" ] + sources = [ + "pffft_wrapper.cc", + "pffft_wrapper.h", + ] + deps = [ + "../../../api:array_view", + "../../../rtc_base:checks", + "//third_party/pffft", + ] +} + +if (rtc_include_tests) { + rtc_library("cascaded_biquad_filter_unittest") { + testonly = true + + sources = [ "cascaded_biquad_filter_unittest.cc" ] + deps = [ + ":cascaded_biquad_filter", + "../../../rtc_base:rtc_base_approved", + "../../../test:test_support", + "//testing/gtest", + ] + } + + rtc_library("legacy_delay_estimator_unittest") { + testonly = true + + sources = [ "delay_estimator_unittest.cc" ] + deps = [ + ":legacy_delay_estimator", + "../../../rtc_base:rtc_base_approved", + "../../../test:test_support", + "//testing/gtest", + ] + } + + rtc_library("pffft_wrapper_unittest") { + testonly = true + sources = [ "pffft_wrapper_unittest.cc" ] + deps = [ + ":pffft_wrapper", + "../../../test:test_support", + "//testing/gtest", + "//third_party/pffft", + ] + } +} diff --git a/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc b/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc new file mode 100644 index 0000000..08b9464 --- /dev/null +++ b/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.cc @@ -0,0 +1,117 @@ +/* + * 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 "modules/audio_processing/utility/cascaded_biquad_filter.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +CascadedBiQuadFilter::BiQuadParam::BiQuadParam(std::complex zero, + std::complex pole, + float gain, + bool mirror_zero_along_i_axis) + : zero(zero), + pole(pole), + gain(gain), + mirror_zero_along_i_axis(mirror_zero_along_i_axis) {} + +CascadedBiQuadFilter::BiQuadParam::BiQuadParam(const BiQuadParam&) = default; + +CascadedBiQuadFilter::BiQuad::BiQuad( + const CascadedBiQuadFilter::BiQuadParam& param) + : x(), y() { + float z_r = std::real(param.zero); + float z_i = std::imag(param.zero); + float p_r = std::real(param.pole); + float p_i = std::imag(param.pole); + float gain = param.gain; + + if (param.mirror_zero_along_i_axis) { + // Assuming zeroes at z_r and -z_r. + RTC_DCHECK(z_i == 0.f); + coefficients.b[0] = gain * 1.f; + coefficients.b[1] = 0.f; + coefficients.b[2] = gain * -(z_r * z_r); + } else { + // Assuming zeros at (z_r + z_i*i) and (z_r - z_i*i). + coefficients.b[0] = gain * 1.f; + coefficients.b[1] = gain * -2.f * z_r; + coefficients.b[2] = gain * (z_r * z_r + z_i * z_i); + } + + // Assuming poles at (p_r + p_i*i) and (p_r - p_i*i). + coefficients.a[0] = -2.f * p_r; + coefficients.a[1] = p_r * p_r + p_i * p_i; +} + +void CascadedBiQuadFilter::BiQuad::BiQuad::Reset() { + x[0] = x[1] = y[0] = y[1] = 0.f; +} + +CascadedBiQuadFilter::CascadedBiQuadFilter( + const CascadedBiQuadFilter::BiQuadCoefficients& coefficients, + size_t num_biquads) + : biquads_(num_biquads, BiQuad(coefficients)) {} + +CascadedBiQuadFilter::CascadedBiQuadFilter( + const std::vector& biquad_params) { + for (const auto& param : biquad_params) { + biquads_.push_back(BiQuad(param)); + } +} + +CascadedBiQuadFilter::~CascadedBiQuadFilter() = default; + +void CascadedBiQuadFilter::Process(rtc::ArrayView x, + rtc::ArrayView y) { + if (biquads_.size() > 0) { + ApplyBiQuad(x, y, &biquads_[0]); + for (size_t k = 1; k < biquads_.size(); ++k) { + ApplyBiQuad(y, y, &biquads_[k]); + } + } else { + std::copy(x.begin(), x.end(), y.begin()); + } +} + +void CascadedBiQuadFilter::Process(rtc::ArrayView y) { + for (auto& biquad : biquads_) { + ApplyBiQuad(y, y, &biquad); + } +} + +void CascadedBiQuadFilter::Reset() { + for (auto& biquad : biquads_) { + biquad.Reset(); + } +} + +void CascadedBiQuadFilter::ApplyBiQuad(rtc::ArrayView x, + rtc::ArrayView y, + CascadedBiQuadFilter::BiQuad* biquad) { + RTC_DCHECK_EQ(x.size(), y.size()); + const auto* c_b = biquad->coefficients.b; + const auto* c_a = biquad->coefficients.a; + auto* m_x = biquad->x; + auto* m_y = biquad->y; + for (size_t k = 0; k < x.size(); ++k) { + const float tmp = x[k]; + y[k] = c_b[0] * tmp + c_b[1] * m_x[0] + c_b[2] * m_x[1] - c_a[0] * m_y[0] - + c_a[1] * m_y[1]; + m_x[1] = m_x[0]; + m_x[0] = tmp; + m_y[1] = m_y[0]; + m_y[0] = y[k]; + } +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.h b/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.h new file mode 100644 index 0000000..120b52a --- /dev/null +++ b/webrtc/modules/audio_processing/utility/cascaded_biquad_filter.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_CASCADED_BIQUAD_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_CASCADED_BIQUAD_FILTER_H_ + +#include + +#include +#include + +#include "api/array_view.h" + +namespace webrtc { + +// Applies a number of biquads in a cascaded manner. The filter implementation +// is direct form 1. +class CascadedBiQuadFilter { + public: + struct BiQuadParam { + BiQuadParam(std::complex zero, + std::complex pole, + float gain, + bool mirror_zero_along_i_axis = false); + explicit BiQuadParam(const BiQuadParam&); + std::complex zero; + std::complex pole; + float gain; + bool mirror_zero_along_i_axis; + }; + + struct BiQuadCoefficients { + float b[3]; + float a[2]; + }; + + struct BiQuad { + explicit BiQuad(const BiQuadCoefficients& coefficients) + : coefficients(coefficients), x(), y() {} + explicit BiQuad(const CascadedBiQuadFilter::BiQuadParam& param); + void Reset(); + BiQuadCoefficients coefficients; + float x[2]; + float y[2]; + }; + + CascadedBiQuadFilter( + const CascadedBiQuadFilter::BiQuadCoefficients& coefficients, + size_t num_biquads); + explicit CascadedBiQuadFilter( + const std::vector& biquad_params); + ~CascadedBiQuadFilter(); + CascadedBiQuadFilter(const CascadedBiQuadFilter&) = delete; + CascadedBiQuadFilter& operator=(const CascadedBiQuadFilter&) = delete; + + // Applies the biquads on the values in x in order to form the output in y. + void Process(rtc::ArrayView x, rtc::ArrayView y); + // Applies the biquads on the values in y in an in-place manner. + void Process(rtc::ArrayView y); + // Resets the filter to its initial state. + void Reset(); + + private: + void ApplyBiQuad(rtc::ArrayView x, + rtc::ArrayView y, + CascadedBiQuadFilter::BiQuad* biquad); + + std::vector biquads_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_CASCADED_BIQUAD_FILTER_H_ diff --git a/webrtc/modules/audio_processing/utility/delay_estimator.c b/webrtc/modules/audio_processing/utility/delay_estimator.cc similarity index 84% rename from webrtc/modules/audio_processing/utility/delay_estimator.c rename to webrtc/modules/audio_processing/utility/delay_estimator.cc index f9f3dc2..73c70b0 100644 --- a/webrtc/modules/audio_processing/utility/delay_estimator.c +++ b/webrtc/modules/audio_processing/utility/delay_estimator.cc @@ -8,20 +8,27 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/utility/delay_estimator.h" +#include "modules/audio_processing/utility/delay_estimator.h" -#include #include #include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + // Number of right shifts for scaling is linearly depending on number of bits in // the far-end binary spectrum. static const int kShiftsAtZero = 13; // Right shifts at zero binary spectrum. static const int kShiftsLinearSlope = 3; -static const int32_t kProbabilityOffset = 1024; // 2 in Q9. +static const int32_t kProbabilityOffset = 1024; // 2 in Q9. static const int32_t kProbabilityLowerLimit = 8704; // 17 in Q9. -static const int32_t kProbabilityMinSpread = 2816; // 5.5 in Q9. +static const int32_t kProbabilityMinSpread = 2816; // 5.5 in Q9. // Robust validation settings static const float kHistogramMax = 3000.f; @@ -35,15 +42,17 @@ static const float kFractionSlope = 0.05f; static const float kMinFractionWhenPossiblyCausal = 0.5f; static const float kMinFractionWhenPossiblyNonCausal = 0.25f; +} // namespace + // Counts and returns number of bits of a 32-bit word. static int BitCount(uint32_t u32) { - uint32_t tmp = u32 - ((u32 >> 1) & 033333333333) - - ((u32 >> 2) & 011111111111); + uint32_t tmp = + u32 - ((u32 >> 1) & 033333333333) - ((u32 >> 2) & 011111111111); tmp = ((tmp + (tmp >> 3)) & 030707070707); tmp = (tmp + (tmp >> 6)); tmp = (tmp + (tmp >> 12) + (tmp >> 24)) & 077; - return ((int) tmp); + return ((int)tmp); } // Compares the |binary_vector| with all rows of the |binary_matrix| and counts @@ -67,7 +76,7 @@ static void BitCountComparison(uint32_t binary_vector, // Compare |binary_vector| with all rows of the |binary_matrix| for (; n < matrix_size; n++) { - bit_counts[n] = (int32_t) BitCount(binary_vector ^ binary_matrix[n]); + bit_counts[n] = (int32_t)BitCount(binary_vector ^ binary_matrix[n]); } } @@ -94,11 +103,12 @@ static void UpdateRobustValidationStatistics(BinaryDelayEstimator* self, int32_t valley_level_q14) { const float valley_depth = valley_depth_q14 * kQ14Scaling; float decrease_in_last_set = valley_depth; - const int max_hits_for_slow_change = (candidate_delay < self->last_delay) ? - kMaxHitsWhenPossiblyNonCausal : kMaxHitsWhenPossiblyCausal; + const int max_hits_for_slow_change = (candidate_delay < self->last_delay) + ? kMaxHitsWhenPossiblyNonCausal + : kMaxHitsWhenPossiblyCausal; int i = 0; - assert(self->history_size == self->farend->history_size); + RTC_DCHECK_EQ(self->history_size, self->farend->history_size); // Reset |candidate_hits| if we have a new candidate. if (candidate_delay != self->last_candidate_delay) { self->candidate_hits = 0; @@ -125,18 +135,20 @@ static void UpdateRobustValidationStatistics(BinaryDelayEstimator* self, // |candidate_delay| is a "potential" candidate and we start decreasing // these histogram bins more rapidly with |valley_depth|. if (self->candidate_hits < max_hits_for_slow_change) { - decrease_in_last_set = (self->mean_bit_counts[self->compare_delay] - - valley_level_q14) * kQ14Scaling; + decrease_in_last_set = + (self->mean_bit_counts[self->compare_delay] - valley_level_q14) * + kQ14Scaling; } // 4. All other bins are decreased with |valley_depth|. // TODO(bjornv): Investigate how to make this loop more efficient. Split up // the loop? Remove parts that doesn't add too much. for (i = 0; i < self->history_size; ++i) { int is_in_last_set = (i >= self->last_delay - 2) && - (i <= self->last_delay + 1) && (i != candidate_delay); - int is_in_candidate_set = (i >= candidate_delay - 2) && - (i <= candidate_delay + 1); - self->histogram[i] -= decrease_in_last_set * is_in_last_set + + (i <= self->last_delay + 1) && (i != candidate_delay); + int is_in_candidate_set = + (i >= candidate_delay - 2) && (i <= candidate_delay + 1); + self->histogram[i] -= + decrease_in_last_set * is_in_last_set + valley_depth * (!is_in_last_set && !is_in_candidate_set); // 5. No histogram bin can go below 0. if (self->histogram[i] < 0) { @@ -194,16 +206,18 @@ static int HistogramBasedValidation(const BinaryDelayEstimator* self, // into tables? if (delay_difference > self->allowed_offset) { fraction = 1.f - kFractionSlope * (delay_difference - self->allowed_offset); - fraction = (fraction > kMinFractionWhenPossiblyCausal ? fraction : - kMinFractionWhenPossiblyCausal); + fraction = (fraction > kMinFractionWhenPossiblyCausal + ? fraction + : kMinFractionWhenPossiblyCausal); } else if (delay_difference < 0) { - fraction = kMinFractionWhenPossiblyNonCausal - - kFractionSlope * delay_difference; + fraction = + kMinFractionWhenPossiblyNonCausal - kFractionSlope * delay_difference; fraction = (fraction > 1.f ? 1.f : fraction); } histogram_threshold *= fraction; - histogram_threshold = (histogram_threshold > kMinHistogramThreshold ? - histogram_threshold : kMinHistogramThreshold); + histogram_threshold = + (histogram_threshold > kMinHistogramThreshold ? histogram_threshold + : kMinHistogramThreshold); is_histogram_valid = (self->histogram[candidate_delay] >= histogram_threshold) && @@ -241,8 +255,8 @@ static int RobustValidation(const BinaryDelayEstimator* self, // i) Before we actually have a valid estimate (|last_delay| == -2), we say // a candidate is valid if either algorithm states so // (|is_instantaneous_valid| OR |is_histogram_valid|). - is_robust = (self->last_delay < 0) && - (is_instantaneous_valid || is_histogram_valid); + is_robust = + (self->last_delay < 0) && (is_instantaneous_valid || is_histogram_valid); // ii) Otherwise, we need both algorithms to be certain // (|is_instantaneous_valid| AND |is_histogram_valid|) is_robust |= is_instantaneous_valid && is_histogram_valid; @@ -250,13 +264,12 @@ static int RobustValidation(const BinaryDelayEstimator* self, // the instantaneous one if |is_histogram_valid| = 1 and the histogram // is significantly strong. is_robust |= is_histogram_valid && - (self->histogram[candidate_delay] > self->last_delay_histogram); + (self->histogram[candidate_delay] > self->last_delay_histogram); return is_robust; } void WebRtc_FreeBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self) { - if (self == NULL) { return; } @@ -276,7 +289,8 @@ BinaryDelayEstimatorFarend* WebRtc_CreateBinaryDelayEstimatorFarend( if (history_size > 1) { // Sanity conditions fulfilled. - self = malloc(sizeof(BinaryDelayEstimatorFarend)); + self = static_cast( + malloc(sizeof(BinaryDelayEstimatorFarend))); } if (self == NULL) { return NULL; @@ -294,24 +308,22 @@ BinaryDelayEstimatorFarend* WebRtc_CreateBinaryDelayEstimatorFarend( int WebRtc_AllocateFarendBufferMemory(BinaryDelayEstimatorFarend* self, int history_size) { - assert(self != NULL); + RTC_DCHECK(self); // (Re-)Allocate memory for history buffers. - self->binary_far_history = + self->binary_far_history = static_cast( realloc(self->binary_far_history, - history_size * sizeof(*self->binary_far_history)); - self->far_bit_counts = realloc(self->far_bit_counts, - history_size * sizeof(*self->far_bit_counts)); + history_size * sizeof(*self->binary_far_history))); + self->far_bit_counts = static_cast(realloc( + self->far_bit_counts, history_size * sizeof(*self->far_bit_counts))); if ((self->binary_far_history == NULL) || (self->far_bit_counts == NULL)) { history_size = 0; } // Fill with zeros if we have expanded the buffers. if (history_size > self->history_size) { int size_diff = history_size - self->history_size; - memset(&self->binary_far_history[self->history_size], - 0, + memset(&self->binary_far_history[self->history_size], 0, sizeof(*self->binary_far_history) * size_diff); - memset(&self->far_bit_counts[self->history_size], - 0, + memset(&self->far_bit_counts[self->history_size], 0, sizeof(*self->far_bit_counts) * size_diff); } self->history_size = history_size; @@ -320,22 +332,23 @@ int WebRtc_AllocateFarendBufferMemory(BinaryDelayEstimatorFarend* self, } void WebRtc_InitBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self) { - assert(self != NULL); + RTC_DCHECK(self); memset(self->binary_far_history, 0, sizeof(uint32_t) * self->history_size); memset(self->far_bit_counts, 0, sizeof(int) * self->history_size); } void WebRtc_SoftResetBinaryDelayEstimatorFarend( - BinaryDelayEstimatorFarend* self, int delay_shift) { + BinaryDelayEstimatorFarend* self, + int delay_shift) { int abs_shift = abs(delay_shift); int shift_size = 0; int dest_index = 0; int src_index = 0; int padding_index = 0; - assert(self != NULL); + RTC_DCHECK(self); shift_size = self->history_size - abs_shift; - assert(shift_size > 0); + RTC_DCHECK_GT(shift_size, 0); if (delay_shift == 0) { return; } else if (delay_shift > 0) { @@ -351,8 +364,7 @@ void WebRtc_SoftResetBinaryDelayEstimatorFarend( sizeof(*self->binary_far_history) * shift_size); memset(&self->binary_far_history[padding_index], 0, sizeof(*self->binary_far_history) * abs_shift); - memmove(&self->far_bit_counts[dest_index], - &self->far_bit_counts[src_index], + memmove(&self->far_bit_counts[dest_index], &self->far_bit_counts[src_index], sizeof(*self->far_bit_counts) * shift_size); memset(&self->far_bit_counts[padding_index], 0, sizeof(*self->far_bit_counts) * abs_shift); @@ -360,7 +372,7 @@ void WebRtc_SoftResetBinaryDelayEstimatorFarend( void WebRtc_AddBinaryFarSpectrum(BinaryDelayEstimatorFarend* handle, uint32_t binary_far_spectrum) { - assert(handle != NULL); + RTC_DCHECK(handle); // Shift binary spectrum history and insert current |binary_far_spectrum|. memmove(&(handle->binary_far_history[1]), &(handle->binary_far_history[0]), (handle->history_size - 1) * sizeof(uint32_t)); @@ -374,7 +386,6 @@ void WebRtc_AddBinaryFarSpectrum(BinaryDelayEstimatorFarend* handle, } void WebRtc_FreeBinaryDelayEstimator(BinaryDelayEstimator* self) { - if (self == NULL) { return; } @@ -399,12 +410,14 @@ void WebRtc_FreeBinaryDelayEstimator(BinaryDelayEstimator* self) { } BinaryDelayEstimator* WebRtc_CreateBinaryDelayEstimator( - BinaryDelayEstimatorFarend* farend, int max_lookahead) { + BinaryDelayEstimatorFarend* farend, + int max_lookahead) { BinaryDelayEstimator* self = NULL; if ((farend != NULL) && (max_lookahead >= 0)) { // Sanity conditions fulfilled. - self = malloc(sizeof(BinaryDelayEstimator)); + self = static_cast( + malloc(sizeof(BinaryDelayEstimator))); } if (self == NULL) { return NULL; @@ -422,8 +435,8 @@ BinaryDelayEstimator* WebRtc_CreateBinaryDelayEstimator( self->mean_bit_counts = NULL; self->bit_counts = NULL; self->histogram = NULL; - self->binary_near_history = - malloc((max_lookahead + 1) * sizeof(*self->binary_near_history)); + self->binary_near_history = static_cast( + malloc((max_lookahead + 1) * sizeof(*self->binary_near_history))); if (self->binary_near_history == NULL || WebRtc_AllocateHistoryBufferMemory(self, farend->history_size) == 0) { WebRtc_FreeBinaryDelayEstimator(self); @@ -444,30 +457,26 @@ int WebRtc_AllocateHistoryBufferMemory(BinaryDelayEstimator* self, // The extra array element in |mean_bit_counts| and |histogram| is a dummy // element only used while |last_delay| == -2, i.e., before we have a valid // estimate. - self->mean_bit_counts = + self->mean_bit_counts = static_cast( realloc(self->mean_bit_counts, - (history_size + 1) * sizeof(*self->mean_bit_counts)); - self->bit_counts = - realloc(self->bit_counts, history_size * sizeof(*self->bit_counts)); - self->histogram = - realloc(self->histogram, (history_size + 1) * sizeof(*self->histogram)); + (history_size + 1) * sizeof(*self->mean_bit_counts))); + self->bit_counts = static_cast( + realloc(self->bit_counts, history_size * sizeof(*self->bit_counts))); + self->histogram = static_cast( + realloc(self->histogram, (history_size + 1) * sizeof(*self->histogram))); - if ((self->mean_bit_counts == NULL) || - (self->bit_counts == NULL) || + if ((self->mean_bit_counts == NULL) || (self->bit_counts == NULL) || (self->histogram == NULL)) { history_size = 0; } // Fill with zeros if we have expanded the buffers. if (history_size > self->history_size) { int size_diff = history_size - self->history_size; - memset(&self->mean_bit_counts[self->history_size], - 0, + memset(&self->mean_bit_counts[self->history_size], 0, sizeof(*self->mean_bit_counts) * size_diff); - memset(&self->bit_counts[self->history_size], - 0, + memset(&self->bit_counts[self->history_size], 0, sizeof(*self->bit_counts) * size_diff); - memset(&self->histogram[self->history_size], - 0, + memset(&self->histogram[self->history_size], 0, sizeof(*self->histogram) * size_diff); } self->history_size = history_size; @@ -477,18 +486,17 @@ int WebRtc_AllocateHistoryBufferMemory(BinaryDelayEstimator* self, void WebRtc_InitBinaryDelayEstimator(BinaryDelayEstimator* self) { int i = 0; - assert(self != NULL); + RTC_DCHECK(self); memset(self->bit_counts, 0, sizeof(int32_t) * self->history_size); - memset(self->binary_near_history, - 0, + memset(self->binary_near_history, 0, sizeof(uint32_t) * self->near_history_size); for (i = 0; i <= self->history_size; ++i) { self->mean_bit_counts[i] = (20 << 9); // 20 in Q9. self->histogram[i] = 0.f; } - self->minimum_probability = kMaxBitCountsQ9; // 32 in Q9. - self->last_delay_probability = (int) kMaxBitCountsQ9; // 32 in Q9. + self->minimum_probability = kMaxBitCountsQ9; // 32 in Q9. + self->last_delay_probability = (int)kMaxBitCountsQ9; // 32 in Q9. // Default return value if we're unable to estimate. -1 is used for errors. self->last_delay = -2; @@ -502,7 +510,7 @@ void WebRtc_InitBinaryDelayEstimator(BinaryDelayEstimator* self) { int WebRtc_SoftResetBinaryDelayEstimator(BinaryDelayEstimator* self, int delay_shift) { int lookahead = 0; - assert(self != NULL); + RTC_DCHECK(self); lookahead = self->lookahead; self->lookahead -= delay_shift; if (self->lookahead < 0) { @@ -524,7 +532,7 @@ int WebRtc_ProcessBinarySpectrum(BinaryDelayEstimator* self, int32_t value_worst_candidate = 0; int32_t valley_depth = 0; - assert(self != NULL); + RTC_DCHECK(self); if (self->farend->history_size != self->history_size) { // Non matching history sizes. return -1; @@ -612,22 +620,36 @@ int WebRtc_ProcessBinarySpectrum(BinaryDelayEstimator* self, // and deeper than the best estimate so far // (|value_best_candidate| < |last_delay_probability|) valid_candidate = ((valley_depth > kProbabilityOffset) && - ((value_best_candidate < self->minimum_probability) || - (value_best_candidate < self->last_delay_probability))); + ((value_best_candidate < self->minimum_probability) || + (value_best_candidate < self->last_delay_probability))); + + // Check for nonstationary farend signal. + const bool non_stationary_farend = + std::any_of(self->farend->far_bit_counts, + self->farend->far_bit_counts + self->history_size, + [](int a) { return a > 0; }); + + if (non_stationary_farend) { + // Only update the validation statistics when the farend is nonstationary + // as the underlying estimates are otherwise frozen. + UpdateRobustValidationStatistics(self, candidate_delay, valley_depth, + value_best_candidate); + } - UpdateRobustValidationStatistics(self, candidate_delay, valley_depth, - value_best_candidate); if (self->robust_validation_enabled) { int is_histogram_valid = HistogramBasedValidation(self, candidate_delay); valid_candidate = RobustValidation(self, candidate_delay, valid_candidate, is_histogram_valid); - } - if (valid_candidate) { + + // Only update the delay estimate when the farend is nonstationary and when + // a valid delay candidate is available. + if (non_stationary_farend && valid_candidate) { if (candidate_delay != self->last_delay) { self->last_delay_histogram = - (self->histogram[candidate_delay] > kLastHistogramMax ? - kLastHistogramMax : self->histogram[candidate_delay]); + (self->histogram[candidate_delay] > kLastHistogramMax + ? kLastHistogramMax + : self->histogram[candidate_delay]); // Adjust the histogram if we made a change to |last_delay|, though it was // not the most likely one according to the histogram. if (self->histogram[candidate_delay] < @@ -646,13 +668,13 @@ int WebRtc_ProcessBinarySpectrum(BinaryDelayEstimator* self, } int WebRtc_binary_last_delay(BinaryDelayEstimator* self) { - assert(self != NULL); + RTC_DCHECK(self); return self->last_delay; } float WebRtc_binary_last_delay_quality(BinaryDelayEstimator* self) { float quality = 0; - assert(self != NULL); + RTC_DCHECK(self); if (self->robust_validation_enabled) { // Simply a linear function of the histogram height at delay estimate. @@ -660,8 +682,8 @@ float WebRtc_binary_last_delay_quality(BinaryDelayEstimator* self) { } else { // Note that |last_delay_probability| states how deep the minimum of the // cost function is, so it is rather an error probability. - quality = (float) (kMaxBitCountsQ9 - self->last_delay_probability) / - kMaxBitCountsQ9; + quality = (float)(kMaxBitCountsQ9 - self->last_delay_probability) / + kMaxBitCountsQ9; if (quality < 0) { quality = 0; } @@ -682,3 +704,5 @@ void WebRtc_MeanEstimatorFix(int32_t new_value, } *mean_value += diff; } + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/utility/delay_estimator.h b/webrtc/modules/audio_processing/utility/delay_estimator.h index 65c3f03..df281bc 100644 --- a/webrtc/modules/audio_processing/utility/delay_estimator.h +++ b/webrtc/modules/audio_processing/utility/delay_estimator.h @@ -11,10 +11,12 @@ // Performs delay estimation on binary converted spectra. // The return value is 0 - OK and -1 - Error, unless otherwise stated. -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_ +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_ -#include "webrtc/typedefs.h" +#include + +namespace webrtc { static const int32_t kMaxBitCountsQ9 = (32 << 9); // 32 matching bits in Q9. @@ -117,7 +119,8 @@ void WebRtc_InitBinaryDelayEstimatorFarend(BinaryDelayEstimatorFarend* self); // - delay_shift : The amount of blocks to shift history buffers. // void WebRtc_SoftResetBinaryDelayEstimatorFarend( - BinaryDelayEstimatorFarend* self, int delay_shift); + BinaryDelayEstimatorFarend* self, + int delay_shift); // Adds the binary far-end spectrum to the internal far-end history buffer. This // spectrum is used as reference when calculating the delay using @@ -153,7 +156,8 @@ void WebRtc_FreeBinaryDelayEstimator(BinaryDelayEstimator* self); // See WebRtc_CreateDelayEstimator(..) in delay_estimator_wrapper.c for detailed // description. BinaryDelayEstimator* WebRtc_CreateBinaryDelayEstimator( - BinaryDelayEstimatorFarend* farend, int max_lookahead); + BinaryDelayEstimatorFarend* farend, + int max_lookahead); // Re-allocates |history_size| dependent buffers. The far-end buffers will be // updated at the same time if needed. @@ -248,4 +252,6 @@ void WebRtc_MeanEstimatorFix(int32_t new_value, int factor, int32_t* mean_value); -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_ +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_H_ diff --git a/webrtc/modules/audio_processing/utility/delay_estimator_internal.h b/webrtc/modules/audio_processing/utility/delay_estimator_internal.h index fd11028..fce95d8 100644 --- a/webrtc/modules/audio_processing/utility/delay_estimator_internal.h +++ b/webrtc/modules/audio_processing/utility/delay_estimator_internal.h @@ -10,11 +10,12 @@ // Header file including the delay estimator handle used for testing. -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_ +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_ -#include "webrtc/modules/audio_processing/utility/delay_estimator.h" -#include "webrtc/typedefs.h" +#include "modules/audio_processing/utility/delay_estimator.h" + +namespace webrtc { typedef union { float float_; @@ -45,4 +46,6 @@ typedef struct { BinaryDelayEstimator* binary_handle; } DelayEstimator; -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_ +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_INTERNAL_H_ diff --git a/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.c b/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc similarity index 82% rename from webrtc/modules/audio_processing/utility/delay_estimator_wrapper.c rename to webrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc index b5448bc..8eac2f6 100644 --- a/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.c +++ b/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc @@ -8,15 +8,16 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h" +#include "modules/audio_processing/utility/delay_estimator_wrapper.h" -#include #include #include -#include "webrtc/modules/audio_processing/utility/delay_estimator.h" -#include "webrtc/modules/audio_processing/utility/delay_estimator_internal.h" -#include "webrtc/system_wrappers/include/compile_assert_c.h" +#include "modules/audio_processing/utility/delay_estimator.h" +#include "modules/audio_processing/utility/delay_estimator_internal.h" +#include "rtc_base/checks.h" + +namespace webrtc { // Only bit |kBandFirst| through bit |kBandLast| are processed and // |kBandFirst| - |kBandLast| must be < 32. @@ -43,7 +44,7 @@ static __inline uint32_t SetBit(uint32_t in, int pos) { static void MeanEstimatorFloat(float new_value, float scale, float* mean_value) { - assert(scale < 1.0f); + RTC_DCHECK_LT(scale, 1.0f); *mean_value += (new_value - *mean_value) * scale; } @@ -65,7 +66,7 @@ static uint32_t BinarySpectrumFix(const uint16_t* spectrum, int i = kBandFirst; uint32_t out = 0; - assert(q_domain < 16); + RTC_DCHECK_LT(q_domain, 16); if (!(*threshold_initialized)) { // Set the |threshold_spectrum| to half the input |spectrum| as starting @@ -73,7 +74,7 @@ static uint32_t BinarySpectrumFix(const uint16_t* spectrum, for (i = kBandFirst; i <= kBandLast; i++) { if (spectrum[i] > 0) { // Convert input spectrum from Q(|q_domain|) to Q15. - int32_t spectrum_q15 = ((int32_t) spectrum[i]) << (15 - q_domain); + int32_t spectrum_q15 = ((int32_t)spectrum[i]) << (15 - q_domain); threshold_spectrum[i].int32_ = (spectrum_q15 >> 1); *threshold_initialized = 1; } @@ -81,7 +82,7 @@ static uint32_t BinarySpectrumFix(const uint16_t* spectrum, } for (i = kBandFirst; i <= kBandLast; i++) { // Convert input spectrum from Q(|q_domain|) to Q15. - int32_t spectrum_q15 = ((int32_t) spectrum[i]) << (15 - q_domain); + int32_t spectrum_q15 = ((int32_t)spectrum[i]) << (15 - q_domain); // Update the |threshold_spectrum|. WebRtc_MeanEstimatorFix(spectrum_q15, 6, &(threshold_spectrum[i].int32_)); // Convert |spectrum| at current frequency bin to a binary value. @@ -124,7 +125,7 @@ static uint32_t BinarySpectrumFloat(const float* spectrum, } void WebRtc_FreeDelayEstimatorFarend(void* handle) { - DelayEstimatorFarend* self = (DelayEstimatorFarend*) handle; + DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle; if (handle == NULL) { return; @@ -144,10 +145,11 @@ void* WebRtc_CreateDelayEstimatorFarend(int spectrum_size, int history_size) { // Check if the sub band used in the delay estimation is small enough to fit // the binary spectra in a uint32_t. - COMPILE_ASSERT(kBandLast - kBandFirst < 32); + static_assert(kBandLast - kBandFirst < 32, ""); if (spectrum_size >= kBandLast) { - self = malloc(sizeof(DelayEstimatorFarend)); + self = static_cast( + malloc(sizeof(DelayEstimatorFarend))); } if (self != NULL) { @@ -158,7 +160,8 @@ void* WebRtc_CreateDelayEstimatorFarend(int spectrum_size, int history_size) { memory_fail |= (self->binary_farend == NULL); // Allocate memory for spectrum buffers. - self->mean_far_spectrum = malloc(spectrum_size * sizeof(SpectrumType)); + self->mean_far_spectrum = static_cast( + malloc(spectrum_size * sizeof(SpectrumType))); memory_fail |= (self->mean_far_spectrum == NULL); self->spectrum_size = spectrum_size; @@ -173,7 +176,7 @@ void* WebRtc_CreateDelayEstimatorFarend(int spectrum_size, int history_size) { } int WebRtc_InitDelayEstimatorFarend(void* handle) { - DelayEstimatorFarend* self = (DelayEstimatorFarend*) handle; + DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle; if (self == NULL) { return -1; @@ -192,8 +195,8 @@ int WebRtc_InitDelayEstimatorFarend(void* handle) { } void WebRtc_SoftResetDelayEstimatorFarend(void* handle, int delay_shift) { - DelayEstimatorFarend* self = (DelayEstimatorFarend*) handle; - assert(self != NULL); + DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle; + RTC_DCHECK(self); WebRtc_SoftResetBinaryDelayEstimatorFarend(self->binary_farend, delay_shift); } @@ -201,7 +204,7 @@ int WebRtc_AddFarSpectrumFix(void* handle, const uint16_t* far_spectrum, int spectrum_size, int far_q) { - DelayEstimatorFarend* self = (DelayEstimatorFarend*) handle; + DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle; uint32_t binary_spectrum = 0; if (self == NULL) { @@ -231,7 +234,7 @@ int WebRtc_AddFarSpectrumFix(void* handle, int WebRtc_AddFarSpectrumFloat(void* handle, const float* far_spectrum, int spectrum_size) { - DelayEstimatorFarend* self = (DelayEstimatorFarend*) handle; + DelayEstimatorFarend* self = (DelayEstimatorFarend*)handle; uint32_t binary_spectrum = 0; if (self == NULL) { @@ -255,7 +258,7 @@ int WebRtc_AddFarSpectrumFloat(void* handle, } void WebRtc_FreeDelayEstimator(void* handle) { - DelayEstimator* self = (DelayEstimator*) handle; + DelayEstimator* self = (DelayEstimator*)handle; if (handle == NULL) { return; @@ -272,10 +275,10 @@ void WebRtc_FreeDelayEstimator(void* handle) { void* WebRtc_CreateDelayEstimator(void* farend_handle, int max_lookahead) { DelayEstimator* self = NULL; - DelayEstimatorFarend* farend = (DelayEstimatorFarend*) farend_handle; + DelayEstimatorFarend* farend = (DelayEstimatorFarend*)farend_handle; if (farend_handle != NULL) { - self = malloc(sizeof(DelayEstimator)); + self = static_cast(malloc(sizeof(DelayEstimator))); } if (self != NULL) { @@ -287,8 +290,8 @@ void* WebRtc_CreateDelayEstimator(void* farend_handle, int max_lookahead) { memory_fail |= (self->binary_handle == NULL); // Allocate memory for spectrum buffers. - self->mean_near_spectrum = malloc(farend->spectrum_size * - sizeof(SpectrumType)); + self->mean_near_spectrum = static_cast( + malloc(farend->spectrum_size * sizeof(SpectrumType))); memory_fail |= (self->mean_near_spectrum == NULL); self->spectrum_size = farend->spectrum_size; @@ -303,7 +306,7 @@ void* WebRtc_CreateDelayEstimator(void* farend_handle, int max_lookahead) { } int WebRtc_InitDelayEstimator(void* handle) { - DelayEstimator* self = (DelayEstimator*) handle; + DelayEstimator* self = (DelayEstimator*)handle; if (self == NULL) { return -1; @@ -322,13 +325,13 @@ int WebRtc_InitDelayEstimator(void* handle) { } int WebRtc_SoftResetDelayEstimator(void* handle, int delay_shift) { - DelayEstimator* self = (DelayEstimator*) handle; - assert(self != NULL); + DelayEstimator* self = (DelayEstimator*)handle; + RTC_DCHECK(self); return WebRtc_SoftResetBinaryDelayEstimator(self->binary_handle, delay_shift); } int WebRtc_set_history_size(void* handle, int history_size) { - DelayEstimator* self = handle; + DelayEstimator* self = static_cast(handle); if ((self == NULL) || (history_size <= 1)) { return -1; @@ -337,7 +340,7 @@ int WebRtc_set_history_size(void* handle, int history_size) { } int WebRtc_history_size(const void* handle) { - const DelayEstimator* self = handle; + const DelayEstimator* self = static_cast(handle); if (self == NULL) { return -1; @@ -351,9 +354,9 @@ int WebRtc_history_size(const void* handle) { } int WebRtc_set_lookahead(void* handle, int lookahead) { - DelayEstimator* self = (DelayEstimator*) handle; - assert(self != NULL); - assert(self->binary_handle != NULL); + DelayEstimator* self = (DelayEstimator*)handle; + RTC_DCHECK(self); + RTC_DCHECK(self->binary_handle); if ((lookahead > self->binary_handle->near_history_size - 1) || (lookahead < 0)) { return -1; @@ -363,14 +366,14 @@ int WebRtc_set_lookahead(void* handle, int lookahead) { } int WebRtc_lookahead(void* handle) { - DelayEstimator* self = (DelayEstimator*) handle; - assert(self != NULL); - assert(self->binary_handle != NULL); + DelayEstimator* self = (DelayEstimator*)handle; + RTC_DCHECK(self); + RTC_DCHECK(self->binary_handle); return self->binary_handle->lookahead; } int WebRtc_set_allowed_offset(void* handle, int allowed_offset) { - DelayEstimator* self = (DelayEstimator*) handle; + DelayEstimator* self = (DelayEstimator*)handle; if ((self == NULL) || (allowed_offset < 0)) { return -1; @@ -380,7 +383,7 @@ int WebRtc_set_allowed_offset(void* handle, int allowed_offset) { } int WebRtc_get_allowed_offset(const void* handle) { - const DelayEstimator* self = (const DelayEstimator*) handle; + const DelayEstimator* self = (const DelayEstimator*)handle; if (self == NULL) { return -1; @@ -389,7 +392,7 @@ int WebRtc_get_allowed_offset(const void* handle) { } int WebRtc_enable_robust_validation(void* handle, int enable) { - DelayEstimator* self = (DelayEstimator*) handle; + DelayEstimator* self = (DelayEstimator*)handle; if (self == NULL) { return -1; @@ -397,13 +400,13 @@ int WebRtc_enable_robust_validation(void* handle, int enable) { if ((enable < 0) || (enable > 1)) { return -1; } - assert(self->binary_handle != NULL); + RTC_DCHECK(self->binary_handle); self->binary_handle->robust_validation_enabled = enable; return 0; } int WebRtc_is_robust_validation_enabled(const void* handle) { - const DelayEstimator* self = (const DelayEstimator*) handle; + const DelayEstimator* self = (const DelayEstimator*)handle; if (self == NULL) { return -1; @@ -415,7 +418,7 @@ int WebRtc_DelayEstimatorProcessFix(void* handle, const uint16_t* near_spectrum, int spectrum_size, int near_q) { - DelayEstimator* self = (DelayEstimator*) handle; + DelayEstimator* self = (DelayEstimator*)handle; uint32_t binary_spectrum = 0; if (self == NULL) { @@ -435,10 +438,9 @@ int WebRtc_DelayEstimatorProcessFix(void* handle, } // Get binary spectra. - binary_spectrum = BinarySpectrumFix(near_spectrum, - self->mean_near_spectrum, - near_q, - &(self->near_spectrum_initialized)); + binary_spectrum = + BinarySpectrumFix(near_spectrum, self->mean_near_spectrum, near_q, + &(self->near_spectrum_initialized)); return WebRtc_ProcessBinarySpectrum(self->binary_handle, binary_spectrum); } @@ -446,7 +448,7 @@ int WebRtc_DelayEstimatorProcessFix(void* handle, int WebRtc_DelayEstimatorProcessFloat(void* handle, const float* near_spectrum, int spectrum_size) { - DelayEstimator* self = (DelayEstimator*) handle; + DelayEstimator* self = (DelayEstimator*)handle; uint32_t binary_spectrum = 0; if (self == NULL) { @@ -469,7 +471,7 @@ int WebRtc_DelayEstimatorProcessFloat(void* handle, } int WebRtc_last_delay(void* handle) { - DelayEstimator* self = (DelayEstimator*) handle; + DelayEstimator* self = (DelayEstimator*)handle; if (self == NULL) { return -1; @@ -479,7 +481,9 @@ int WebRtc_last_delay(void* handle) { } float WebRtc_last_delay_quality(void* handle) { - DelayEstimator* self = (DelayEstimator*) handle; - assert(self != NULL); + DelayEstimator* self = (DelayEstimator*)handle; + RTC_DCHECK(self); return WebRtc_binary_last_delay_quality(self->binary_handle); } + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h b/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h index fdadebe..dbcafaf 100644 --- a/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h +++ b/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h @@ -11,10 +11,12 @@ // Performs delay estimation on block by block basis. // The return value is 0 - OK and -1 - Error, unless otherwise stated. -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_ +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_ -#include "webrtc/typedefs.h" +#include + +namespace webrtc { // Releases the memory allocated by WebRtc_CreateDelayEstimatorFarend(...) void WebRtc_FreeDelayEstimatorFarend(void* handle); @@ -241,4 +243,6 @@ int WebRtc_last_delay(void* handle); // - delay_quality : >= 0 - Estimation quality of last calculated delay. float WebRtc_last_delay_quality(void* handle); -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_ +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_DELAY_ESTIMATOR_WRAPPER_H_ diff --git a/webrtc/modules/audio_processing/utility/pffft_wrapper.cc b/webrtc/modules/audio_processing/utility/pffft_wrapper.cc new file mode 100644 index 0000000..88642fb --- /dev/null +++ b/webrtc/modules/audio_processing/utility/pffft_wrapper.cc @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/utility/pffft_wrapper.h" + +#include "rtc_base/checks.h" +#include "third_party/pffft/src/pffft.h" + +namespace webrtc { +namespace { + +size_t GetBufferSize(size_t fft_size, Pffft::FftType fft_type) { + return fft_size * (fft_type == Pffft::FftType::kReal ? 1 : 2); +} + +float* AllocatePffftBuffer(size_t size) { + return static_cast(pffft_aligned_malloc(size * sizeof(float))); +} + +} // namespace + +Pffft::FloatBuffer::FloatBuffer(size_t fft_size, FftType fft_type) + : size_(GetBufferSize(fft_size, fft_type)), + data_(AllocatePffftBuffer(size_)) {} + +Pffft::FloatBuffer::~FloatBuffer() { + pffft_aligned_free(data_); +} + +rtc::ArrayView Pffft::FloatBuffer::GetConstView() const { + return {data_, size_}; +} + +rtc::ArrayView Pffft::FloatBuffer::GetView() { + return {data_, size_}; +} + +Pffft::Pffft(size_t fft_size, FftType fft_type) + : fft_size_(fft_size), + fft_type_(fft_type), + pffft_status_(pffft_new_setup( + fft_size_, + fft_type == Pffft::FftType::kReal ? PFFFT_REAL : PFFFT_COMPLEX)), + scratch_buffer_( + AllocatePffftBuffer(GetBufferSize(fft_size_, fft_type_))) { + RTC_DCHECK(pffft_status_); + RTC_DCHECK(scratch_buffer_); +} + +Pffft::~Pffft() { + pffft_destroy_setup(pffft_status_); + pffft_aligned_free(scratch_buffer_); +} + +bool Pffft::IsValidFftSize(size_t fft_size, FftType fft_type) { + if (fft_size == 0) { + return false; + } + // PFFFT only supports transforms for inputs of length N of the form + // N = (2^a)*(3^b)*(5^c) where b >=0 and c >= 0 and a >= 5 for the real FFT + // and a >= 4 for the complex FFT. + constexpr int kFactors[] = {2, 3, 5}; + int factorization[] = {0, 0, 0}; + int n = static_cast(fft_size); + for (int i = 0; i < 3; ++i) { + while (n % kFactors[i] == 0) { + n = n / kFactors[i]; + factorization[i]++; + } + } + int a_min = (fft_type == Pffft::FftType::kReal) ? 5 : 4; + return factorization[0] >= a_min && n == 1; +} + +bool Pffft::IsSimdEnabled() { + return pffft_simd_size() > 1; +} + +std::unique_ptr Pffft::CreateBuffer() const { + // Cannot use make_unique from absl because Pffft is the only friend of + // Pffft::FloatBuffer. + std::unique_ptr buffer( + new Pffft::FloatBuffer(fft_size_, fft_type_)); + return buffer; +} + +void Pffft::ForwardTransform(const FloatBuffer& in, + FloatBuffer* out, + bool ordered) { + RTC_DCHECK_EQ(in.size(), GetBufferSize(fft_size_, fft_type_)); + RTC_DCHECK_EQ(in.size(), out->size()); + RTC_DCHECK(scratch_buffer_); + if (ordered) { + pffft_transform_ordered(pffft_status_, in.const_data(), out->data(), + scratch_buffer_, PFFFT_FORWARD); + } else { + pffft_transform(pffft_status_, in.const_data(), out->data(), + scratch_buffer_, PFFFT_FORWARD); + } +} + +void Pffft::BackwardTransform(const FloatBuffer& in, + FloatBuffer* out, + bool ordered) { + RTC_DCHECK_EQ(in.size(), GetBufferSize(fft_size_, fft_type_)); + RTC_DCHECK_EQ(in.size(), out->size()); + RTC_DCHECK(scratch_buffer_); + if (ordered) { + pffft_transform_ordered(pffft_status_, in.const_data(), out->data(), + scratch_buffer_, PFFFT_BACKWARD); + } else { + pffft_transform(pffft_status_, in.const_data(), out->data(), + scratch_buffer_, PFFFT_BACKWARD); + } +} + +void Pffft::FrequencyDomainConvolve(const FloatBuffer& fft_x, + const FloatBuffer& fft_y, + FloatBuffer* out, + float scaling) { + RTC_DCHECK_EQ(fft_x.size(), GetBufferSize(fft_size_, fft_type_)); + RTC_DCHECK_EQ(fft_x.size(), fft_y.size()); + RTC_DCHECK_EQ(fft_x.size(), out->size()); + pffft_zconvolve_accumulate(pffft_status_, fft_x.const_data(), + fft_y.const_data(), out->data(), scaling); +} + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/utility/pffft_wrapper.h b/webrtc/modules/audio_processing/utility/pffft_wrapper.h new file mode 100644 index 0000000..160f0da --- /dev/null +++ b/webrtc/modules/audio_processing/utility/pffft_wrapper.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_UTILITY_PFFFT_WRAPPER_H_ +#define MODULES_AUDIO_PROCESSING_UTILITY_PFFFT_WRAPPER_H_ + +#include + +#include "api/array_view.h" + +// Forward declaration. +struct PFFFT_Setup; + +namespace webrtc { + +// Pretty-Fast Fast Fourier Transform (PFFFT) wrapper class. +// Not thread safe. +class Pffft { + public: + enum class FftType { kReal, kComplex }; + + // 1D floating point buffer used as input/output data type for the FFT ops. + // It must be constructed using Pffft::CreateBuffer(). + class FloatBuffer { + public: + FloatBuffer(const FloatBuffer&) = delete; + FloatBuffer& operator=(const FloatBuffer&) = delete; + ~FloatBuffer(); + + rtc::ArrayView GetConstView() const; + rtc::ArrayView GetView(); + + private: + friend class Pffft; + FloatBuffer(size_t fft_size, FftType fft_type); + const float* const_data() const { return data_; } + float* data() { return data_; } + size_t size() const { return size_; } + + const size_t size_; + float* const data_; + }; + + // TODO(https://crbug.com/webrtc/9577): Consider adding a factory and making + // the ctor private. + // static std::unique_ptr Create(size_t fft_size, + // FftType fft_type); Ctor. |fft_size| must be a supported size (see + // Pffft::IsValidFftSize()). If not supported, the code will crash. + Pffft(size_t fft_size, FftType fft_type); + Pffft(const Pffft&) = delete; + Pffft& operator=(const Pffft&) = delete; + ~Pffft(); + + // Returns true if the FFT size is supported. + static bool IsValidFftSize(size_t fft_size, FftType fft_type); + + // Returns true if SIMD code optimizations are being used. + static bool IsSimdEnabled(); + + // Creates a buffer of the right size. + std::unique_ptr CreateBuffer() const; + + // TODO(https://crbug.com/webrtc/9577): Overload with rtc::ArrayView args. + // Computes the forward fast Fourier transform. + void ForwardTransform(const FloatBuffer& in, FloatBuffer* out, bool ordered); + // Computes the backward fast Fourier transform. + void BackwardTransform(const FloatBuffer& in, FloatBuffer* out, bool ordered); + + // Multiplies the frequency components of |fft_x| and |fft_y| and accumulates + // them into |out|. The arrays must have been obtained with + // ForwardTransform(..., /*ordered=*/false) - i.e., |fft_x| and |fft_y| must + // not be ordered. + void FrequencyDomainConvolve(const FloatBuffer& fft_x, + const FloatBuffer& fft_y, + FloatBuffer* out, + float scaling = 1.f); + + private: + const size_t fft_size_; + const FftType fft_type_; + PFFFT_Setup* pffft_status_; + float* const scratch_buffer_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_UTILITY_PFFFT_WRAPPER_H_ diff --git a/webrtc/modules/audio_processing/vad/BUILD.gn b/webrtc/modules/audio_processing/vad/BUILD.gn new file mode 100644 index 0000000..71e079d --- /dev/null +++ b/webrtc/modules/audio_processing/vad/BUILD.gn @@ -0,0 +1,69 @@ +# Copyright (c) 2018 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. + +import("../../../webrtc.gni") +rtc_library("vad") { + visibility = [ + "../*", + "../../../rtc_tools:*", + ] + sources = [ + "common.h", + "gmm.cc", + "gmm.h", + "noise_gmm_tables.h", + "pitch_based_vad.cc", + "pitch_based_vad.h", + "pitch_internal.cc", + "pitch_internal.h", + "pole_zero_filter.cc", + "pole_zero_filter.h", + "standalone_vad.cc", + "standalone_vad.h", + "vad_audio_proc.cc", + "vad_audio_proc.h", + "vad_audio_proc_internal.h", + "vad_circular_buffer.cc", + "vad_circular_buffer.h", + "voice_activity_detector.cc", + "voice_activity_detector.h", + "voice_gmm_tables.h", + ] + deps = [ + "../../../audio/utility:audio_frame_operations", + "../../../common_audio", + "../../../common_audio:common_audio_c", + "../../../common_audio/third_party/ooura:fft_size_256", + "../../../rtc_base:checks", + "../../audio_coding:isac_vad", + ] +} + +if (rtc_include_tests) { + rtc_library("vad_unittests") { + testonly = true + sources = [ + "gmm_unittest.cc", + "pitch_based_vad_unittest.cc", + "pitch_internal_unittest.cc", + "pole_zero_filter_unittest.cc", + "standalone_vad_unittest.cc", + "vad_audio_proc_unittest.cc", + "vad_circular_buffer_unittest.cc", + "voice_activity_detector_unittest.cc", + ] + deps = [ + ":vad", + "../../../common_audio", + "../../../test:fileutils", + "../../../test:test_support", + "//testing/gmock", + "//testing/gtest", + ] + } +} diff --git a/webrtc/modules/audio_processing/vad/common.h b/webrtc/modules/audio_processing/vad/common.h index be99c1c..b5a5fb3 100644 --- a/webrtc/modules/audio_processing/vad/common.h +++ b/webrtc/modules/audio_processing/vad/common.h @@ -8,8 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VAD_COMMON_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VAD_COMMON_H_ +#ifndef MODULES_AUDIO_PROCESSING_VAD_COMMON_H_ +#define MODULES_AUDIO_PROCESSING_VAD_COMMON_H_ + +#include static const int kSampleRateHz = 16000; static const size_t kLength10Ms = kSampleRateHz / 100; @@ -24,4 +26,4 @@ struct AudioFeatures { bool silence; }; -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VAD_COMMON_H_ +#endif // MODULES_AUDIO_PROCESSING_VAD_COMMON_H_ diff --git a/webrtc/modules/audio_processing/vad/gmm.cc b/webrtc/modules/audio_processing/vad/gmm.cc index 9651975..3b8764c 100644 --- a/webrtc/modules/audio_processing/vad/gmm.cc +++ b/webrtc/modules/audio_processing/vad/gmm.cc @@ -8,12 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/vad/gmm.h" +#include "modules/audio_processing/vad/gmm.h" #include -#include - -#include "webrtc/typedefs.h" namespace webrtc { diff --git a/webrtc/modules/audio_processing/vad/gmm.h b/webrtc/modules/audio_processing/vad/gmm.h index 9f3e578..93eb675 100644 --- a/webrtc/modules/audio_processing/vad/gmm.h +++ b/webrtc/modules/audio_processing/vad/gmm.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VAD_GMM_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VAD_GMM_H_ +#ifndef MODULES_AUDIO_PROCESSING_VAD_GMM_H_ +#define MODULES_AUDIO_PROCESSING_VAD_GMM_H_ namespace webrtc { @@ -42,4 +42,4 @@ struct GmmParameters { double EvaluateGmm(const double* x, const GmmParameters& gmm_parameters); } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VAD_GMM_H_ +#endif // MODULES_AUDIO_PROCESSING_VAD_GMM_H_ diff --git a/webrtc/modules/audio_processing/vad/noise_gmm_tables.h b/webrtc/modules/audio_processing/vad/noise_gmm_tables.h index 293af57..944a540 100644 --- a/webrtc/modules/audio_processing/vad/noise_gmm_tables.h +++ b/webrtc/modules/audio_processing/vad/noise_gmm_tables.h @@ -10,8 +10,10 @@ // GMM tables for inactive segments. Generated by MakeGmmTables.m. -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VAD_NOISE_GMM_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VAD_NOISE_GMM_TABLES_H_ +#ifndef MODULES_AUDIO_PROCESSING_VAD_NOISE_GMM_TABLES_H_ +#define MODULES_AUDIO_PROCESSING_VAD_NOISE_GMM_TABLES_H_ + +namespace webrtc { static const int kNoiseGmmNumMixtures = 12; static const int kNoiseGmmDim = 3; @@ -70,16 +72,11 @@ static const double kNoiseGmmMean[kNoiseGmmNumMixtures][kNoiseGmmDim] = { {-2.30193040814533e+00, 1.43953696546439e+03, 7.04085275122649e+01}}; static const double kNoiseGmmWeights[kNoiseGmmNumMixtures] = { - -1.09422832086193e+01, - -1.10847897513425e+01, - -1.36767587732187e+01, - -1.79789356118641e+01, - -1.42830169160894e+01, - -1.56500228061379e+01, - -1.83124990950113e+01, - -1.69979436177477e+01, - -1.12329424387828e+01, - -1.41311785780639e+01, - -1.47171861448585e+01, - -1.35963362781839e+01}; -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VAD_NOISE_GMM_TABLES_H_ + -1.09422832086193e+01, -1.10847897513425e+01, -1.36767587732187e+01, + -1.79789356118641e+01, -1.42830169160894e+01, -1.56500228061379e+01, + -1.83124990950113e+01, -1.69979436177477e+01, -1.12329424387828e+01, + -1.41311785780639e+01, -1.47171861448585e+01, -1.35963362781839e+01}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_VAD_NOISE_GMM_TABLES_H_ diff --git a/webrtc/modules/audio_processing/vad/pitch_based_vad.cc b/webrtc/modules/audio_processing/vad/pitch_based_vad.cc index 39ec37e..68e60dc 100644 --- a/webrtc/modules/audio_processing/vad/pitch_based_vad.cc +++ b/webrtc/modules/audio_processing/vad/pitch_based_vad.cc @@ -8,17 +8,14 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/vad/pitch_based_vad.h" +#include "modules/audio_processing/vad/pitch_based_vad.h" -#include -#include #include -#include "webrtc/modules/audio_processing/vad/vad_circular_buffer.h" -#include "webrtc/modules/audio_processing/vad/common.h" -#include "webrtc/modules/audio_processing/vad/noise_gmm_tables.h" -#include "webrtc/modules/audio_processing/vad/voice_gmm_tables.h" -#include "webrtc/modules/interface/module_common_types.h" +#include "modules/audio_processing/vad/common.h" +#include "modules/audio_processing/vad/noise_gmm_tables.h" +#include "modules/audio_processing/vad/vad_circular_buffer.h" +#include "modules/audio_processing/vad/voice_gmm_tables.h" namespace webrtc { @@ -60,8 +57,7 @@ PitchBasedVad::PitchBasedVad() voice_gmm_.covar_inverse = &kVoiceGmmCovarInverse[0][0][0]; } -PitchBasedVad::~PitchBasedVad() { -} +PitchBasedVad::~PitchBasedVad() {} int PitchBasedVad::VoicingProbability(const AudioFeatures& features, double* p_combined) { diff --git a/webrtc/modules/audio_processing/vad/pitch_based_vad.h b/webrtc/modules/audio_processing/vad/pitch_based_vad.h index c502184..e005e23 100644 --- a/webrtc/modules/audio_processing/vad/pitch_based_vad.h +++ b/webrtc/modules/audio_processing/vad/pitch_based_vad.h @@ -8,17 +8,16 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VAD_PITCH_BASED_VAD_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VAD_PITCH_BASED_VAD_H_ +#ifndef MODULES_AUDIO_PROCESSING_VAD_PITCH_BASED_VAD_H_ +#define MODULES_AUDIO_PROCESSING_VAD_PITCH_BASED_VAD_H_ -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/modules/audio_processing/vad/common.h" -#include "webrtc/modules/audio_processing/vad/gmm.h" -#include "webrtc/typedefs.h" +#include + +#include "modules/audio_processing/vad/common.h" +#include "modules/audio_processing/vad/gmm.h" namespace webrtc { -class AudioFrame; class VadCircularBuffer; // Computes the probability of the input audio frame to be active given @@ -50,8 +49,9 @@ class PitchBasedVad { double p_prior_; - rtc::scoped_ptr circular_buffer_; + std::unique_ptr circular_buffer_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VAD_PITCH_BASED_VAD_H_ + +#endif // MODULES_AUDIO_PROCESSING_VAD_PITCH_BASED_VAD_H_ diff --git a/webrtc/modules/audio_processing/vad/pitch_internal.cc b/webrtc/modules/audio_processing/vad/pitch_internal.cc index 309b45a..8f86918 100644 --- a/webrtc/modules/audio_processing/vad/pitch_internal.cc +++ b/webrtc/modules/audio_processing/vad/pitch_internal.cc @@ -8,10 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/vad/pitch_internal.h" +#include "modules/audio_processing/vad/pitch_internal.h" #include +namespace webrtc { + // A 4-to-3 linear interpolation. // The interpolation constants are derived as following: // Input pitch parameters are updated every 7.5 ms. Within a 30-ms interval @@ -49,3 +51,5 @@ void GetSubframesPitchParameters(int sampling_rate_hz, pitch_lag_hz[n] = (sampling_rate_hz) / (pitch_lag_hz[n]); } } + +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/vad/pitch_internal.h b/webrtc/modules/audio_processing/vad/pitch_internal.h index b25b1a8..938745d 100644 --- a/webrtc/modules/audio_processing/vad/pitch_internal.h +++ b/webrtc/modules/audio_processing/vad/pitch_internal.h @@ -8,8 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VAD_PITCH_INTERNAL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VAD_PITCH_INTERNAL_H_ +#ifndef MODULES_AUDIO_PROCESSING_VAD_PITCH_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_VAD_PITCH_INTERNAL_H_ + +namespace webrtc { // TODO(turajs): Write a description of this function. Also be consistent with // usage of |sampling_rate_hz| vs |kSamplingFreqHz|. @@ -23,4 +25,6 @@ void GetSubframesPitchParameters(int sampling_rate_hz, double* log_pitch_gain, double* pitch_lag_hz); -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VAD_PITCH_INTERNAL_H_ +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_VAD_PITCH_INTERNAL_H_ diff --git a/webrtc/modules/audio_processing/vad/pole_zero_filter.cc b/webrtc/modules/audio_processing/vad/pole_zero_filter.cc index 9769515..e7a6113 100644 --- a/webrtc/modules/audio_processing/vad/pole_zero_filter.cc +++ b/webrtc/modules/audio_processing/vad/pole_zero_filter.cc @@ -8,10 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/vad/pole_zero_filter.h" +#include "modules/audio_processing/vad/pole_zero_filter.h" -#include #include + #include namespace webrtc { @@ -53,7 +53,8 @@ PoleZeroFilter::PoleZeroFilter(const float* numerator_coefficients, } template -static float FilterArPast(const T* past, size_t order, +static float FilterArPast(const T* past, + size_t order, const float* coefficients) { float sum = 0.0f; size_t past_index = order - 1; diff --git a/webrtc/modules/audio_processing/vad/pole_zero_filter.h b/webrtc/modules/audio_processing/vad/pole_zero_filter.h index bd13050..11a0511 100644 --- a/webrtc/modules/audio_processing/vad/pole_zero_filter.h +++ b/webrtc/modules/audio_processing/vad/pole_zero_filter.h @@ -8,12 +8,11 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VAD_POLE_ZERO_FILTER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VAD_POLE_ZERO_FILTER_H_ +#ifndef MODULES_AUDIO_PROCESSING_VAD_POLE_ZERO_FILTER_H_ +#define MODULES_AUDIO_PROCESSING_VAD_POLE_ZERO_FILTER_H_ -#include - -#include "webrtc/typedefs.h" +#include +#include namespace webrtc { @@ -49,4 +48,4 @@ class PoleZeroFilter { } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VAD_POLE_ZERO_FILTER_H_ +#endif // MODULES_AUDIO_PROCESSING_VAD_POLE_ZERO_FILTER_H_ diff --git a/webrtc/modules/audio_processing/vad/standalone_vad.cc b/webrtc/modules/audio_processing/vad/standalone_vad.cc index 468b8ff..1397668 100644 --- a/webrtc/modules/audio_processing/vad/standalone_vad.cc +++ b/webrtc/modules/audio_processing/vad/standalone_vad.cc @@ -8,21 +8,19 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/vad/standalone_vad.h" +#include "modules/audio_processing/vad/standalone_vad.h" -#include +#include -#include "webrtc/modules/interface/module_common_types.h" -#include "webrtc/modules/utility/interface/audio_frame_operations.h" -#include "webrtc/typedefs.h" +#include "common_audio/vad/include/webrtc_vad.h" +#include "rtc_base/checks.h" namespace webrtc { static const int kDefaultStandaloneVadMode = 3; StandaloneVad::StandaloneVad(VadInst* vad) - : vad_(vad), buffer_(), index_(0), mode_(kDefaultStandaloneVadMode) { -} + : vad_(vad), buffer_(), index_(0), mode_(kDefaultStandaloneVadMode) {} StandaloneVad::~StandaloneVad() { WebRtcVad_Free(vad_); @@ -64,7 +62,7 @@ int StandaloneVad::GetActivity(double* p, size_t length_p) { const size_t num_frames = index_ / kLength10Ms; if (num_frames > length_p) return -1; - assert(WebRtcVad_ValidRateAndFrameLength(kSampleRateHz, index_) == 0); + RTC_DCHECK_EQ(0, WebRtcVad_ValidRateAndFrameLength(kSampleRateHz, index_)); int activity = WebRtcVad_Process(vad_, kSampleRateHz, buffer_, index_); if (activity < 0) diff --git a/webrtc/modules/audio_processing/vad/standalone_vad.h b/webrtc/modules/audio_processing/vad/standalone_vad.h index 6a25424..3dff416 100644 --- a/webrtc/modules/audio_processing/vad/standalone_vad.h +++ b/webrtc/modules/audio_processing/vad/standalone_vad.h @@ -8,18 +8,17 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_STANDALONE_VAD_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_STANDALONE_VAD_H_ +#ifndef MODULES_AUDIO_PROCESSING_AGC_STANDALONE_VAD_H_ +#define MODULES_AUDIO_PROCESSING_AGC_STANDALONE_VAD_H_ -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/modules/audio_processing/vad/common.h" -#include "webrtc/common_audio/vad/include/webrtc_vad.h" -#include "webrtc/typedefs.h" +#include +#include + +#include "common_audio/vad/include/webrtc_vad.h" +#include "modules/audio_processing/vad/common.h" namespace webrtc { -class AudioFrame; - class StandaloneVad { public: static StandaloneVad* Create(); @@ -67,4 +66,4 @@ class StandaloneVad { } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_STANDALONE_VAD_H_ +#endif // MODULES_AUDIO_PROCESSING_AGC_STANDALONE_VAD_H_ diff --git a/webrtc/modules/audio_processing/vad/vad_audio_proc.cc b/webrtc/modules/audio_processing/vad/vad_audio_proc.cc index 8535d1f..97cf651 100644 --- a/webrtc/modules/audio_processing/vad/vad_audio_proc.cc +++ b/webrtc/modules/audio_processing/vad/vad_audio_proc.cc @@ -8,22 +8,23 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/vad/vad_audio_proc.h" +#include "modules/audio_processing/vad/vad_audio_proc.h" #include #include +#include -#include "webrtc/common_audio/fft4g.h" -#include "webrtc/modules/audio_processing/vad/vad_audio_proc_internal.h" -#include "webrtc/modules/audio_processing/vad/pitch_internal.h" -#include "webrtc/modules/audio_processing/vad/pole_zero_filter.h" +#include "common_audio/third_party/ooura/fft_size_256/fft4g.h" +#include "modules/audio_processing/vad/pitch_internal.h" +#include "modules/audio_processing/vad/pole_zero_filter.h" +#include "modules/audio_processing/vad/vad_audio_proc_internal.h" +#include "rtc_base/checks.h" extern "C" { -#include "webrtc/modules/audio_coding/codecs/isac/main/source/codec.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/lpc_analysis.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" -#include "webrtc/modules/audio_coding/codecs/isac/main/source/structs.h" +#include "modules/audio_coding/codecs/isac/main/source/filter_functions.h" +#include "modules/audio_coding/codecs/isac/main/source/isac_vad.h" +#include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" +#include "modules/audio_coding/codecs/isac/main/source/structs.h" } -#include "webrtc/modules/interface/module_common_types.h" namespace webrtc { @@ -32,9 +33,9 @@ namespace webrtc { struct VadAudioProc::PitchAnalysisStruct : public ::PitchAnalysisStruct {}; struct VadAudioProc::PreFiltBankstr : public ::PreFiltBankstr {}; -static const float kFrequencyResolution = +static constexpr float kFrequencyResolution = kSampleRateHz / static_cast(VadAudioProc::kDftSize); -static const int kSilenceRms = 5; +static constexpr int kSilenceRms = 5; // TODO(turajs): Make a Create or Init for VadAudioProc. VadAudioProc::VadAudioProc() @@ -66,8 +67,7 @@ VadAudioProc::VadAudioProc() WebRtcIsac_InitPitchAnalysis(pitch_analysis_handle_.get()); } -VadAudioProc::~VadAudioProc() { -} +VadAudioProc::~VadAudioProc() {} void VadAudioProc::ResetBuffer() { memcpy(audio_buffer_, &audio_buffer_[kNumSamplesToProcess], @@ -95,7 +95,7 @@ int VadAudioProc::ExtractFeatures(const int16_t* frame, if (num_buffer_samples_ < kBufferLength) { return 0; } - assert(num_buffer_samples_ == kBufferLength); + RTC_DCHECK_EQ(num_buffer_samples_, kBufferLength); features->num_frames = kNum10msSubframes; features->silence = false; @@ -121,7 +121,7 @@ int VadAudioProc::ExtractFeatures(const int16_t* frame, void VadAudioProc::SubframeCorrelation(double* corr, size_t length_corr, size_t subframe_index) { - assert(length_corr >= kLpcOrder + 1); + RTC_DCHECK_GE(length_corr, kLpcOrder + 1); double windowed_audio[kNumSubframeSamples + kNumPastSignalSamples]; size_t buffer_index = subframe_index * kNumSubframeSamples; @@ -137,7 +137,7 @@ void VadAudioProc::SubframeCorrelation(double* corr, // each 10ms sub-frame. This is equivalent to computing LPC coefficients for the // first half of each 10 ms subframe. void VadAudioProc::GetLpcPolynomials(double* lpc, size_t length_lpc) { - assert(length_lpc >= kNum10msSubframes * (kLpcOrder + 1)); + RTC_DCHECK_GE(length_lpc, kNum10msSubframes * (kLpcOrder + 1)); double corr[kLpcOrder + 1]; double reflec_coeff[kLpcOrder]; for (size_t i = 0, offset_lpc = 0; i < kNum10msSubframes; @@ -165,7 +165,7 @@ static float QuadraticInterpolation(float prev_val, fractional_index = -(next_val - prev_val) * 0.5f / (next_val + prev_val - 2.f * curr_val); - assert(fabs(fractional_index) < 1); + RTC_DCHECK_LT(fabs(fractional_index), 1); return fractional_index; } @@ -176,7 +176,7 @@ static float QuadraticInterpolation(float prev_val, // to save on one square root. void VadAudioProc::FindFirstSpectralPeaks(double* f_peak, size_t length_f_peak) { - assert(length_f_peak >= kNum10msSubframes); + RTC_DCHECK_GE(length_f_peak, kNum10msSubframes); double lpc[kNum10msSubframes * (kLpcOrder + 1)]; // For all sub-frames. GetLpcPolynomials(lpc, kNum10msSubframes * (kLpcOrder + 1)); @@ -232,7 +232,7 @@ void VadAudioProc::PitchAnalysis(double* log_pitch_gains, size_t length) { // TODO(turajs): This can be "imported" from iSAC & and the next two // constants. - assert(length >= kNum10msSubframes); + RTC_DCHECK_GE(length, kNum10msSubframes); const int kNumPitchSubframes = 4; double gains[kNumPitchSubframes]; double lags[kNumPitchSubframes]; @@ -262,7 +262,7 @@ void VadAudioProc::PitchAnalysis(double* log_pitch_gains, } void VadAudioProc::Rms(double* rms, size_t length_rms) { - assert(length_rms >= kNum10msSubframes); + RTC_DCHECK_GE(length_rms, kNum10msSubframes); size_t offset = kNumPastSignalSamples; for (size_t i = 0; i < kNum10msSubframes; i++) { rms[i] = 0; diff --git a/webrtc/modules/audio_processing/vad/vad_audio_proc.h b/webrtc/modules/audio_processing/vad/vad_audio_proc.h index 85500ae..4a71ce3 100644 --- a/webrtc/modules/audio_processing/vad/vad_audio_proc.h +++ b/webrtc/modules/audio_processing/vad/vad_audio_proc.h @@ -8,16 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_H_ +#ifndef MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_H_ +#define MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_H_ -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/modules/audio_processing/vad/common.h" -#include "webrtc/typedefs.h" +#include +#include + +#include + +#include "modules/audio_processing/vad/common.h" // AudioFeatures, kSampleR... namespace webrtc { -class AudioFrame; class PoleZeroFilter; class VadAudioProc { @@ -49,25 +51,28 @@ class VadAudioProc { // For every 30 ms we compute 3 spectral peak there for 3 LPC analysis. // LPC is computed over 15 ms of windowed audio. For every 10 ms sub-frame // we need 5 ms of past signal to create the input of LPC analysis. - static const size_t kNumPastSignalSamples = - static_cast(kSampleRateHz / 200); + enum : size_t { + kNumPastSignalSamples = static_cast(kSampleRateHz / 200) + }; // TODO(turajs): maybe defining this at a higher level (maybe enum) so that // all the code recognize it as "no-error." - static const int kNoError = 0; + enum : int { kNoError = 0 }; - static const size_t kNum10msSubframes = 3; - static const size_t kNumSubframeSamples = - static_cast(kSampleRateHz / 100); - static const size_t kNumSamplesToProcess = - kNum10msSubframes * - kNumSubframeSamples; // Samples in 30 ms @ given sampling rate. - static const size_t kBufferLength = - kNumPastSignalSamples + kNumSamplesToProcess; - static const size_t kIpLength = kDftSize >> 1; - static const size_t kWLength = kDftSize >> 1; - - static const size_t kLpcOrder = 16; + enum : size_t { kNum10msSubframes = 3 }; + enum : size_t { + kNumSubframeSamples = static_cast(kSampleRateHz / 100) + }; + enum : size_t { + // Samples in 30 ms @ given sampling rate. + kNumSamplesToProcess = kNum10msSubframes * kNumSubframeSamples + }; + enum : size_t { + kBufferLength = kNumPastSignalSamples + kNumSamplesToProcess + }; + enum : size_t { kIpLength = kDftSize >> 1 }; + enum : size_t { kWLength = kDftSize >> 1 }; + enum : size_t { kLpcOrder = 16 }; size_t ip_[kIpLength]; float w_fft_[kWLength]; @@ -79,11 +84,11 @@ class VadAudioProc { double log_old_gain_; double old_lag_; - rtc::scoped_ptr pitch_analysis_handle_; - rtc::scoped_ptr pre_filter_handle_; - rtc::scoped_ptr high_pass_filter_; + std::unique_ptr pitch_analysis_handle_; + std::unique_ptr pre_filter_handle_; + std::unique_ptr high_pass_filter_; }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_H_ +#endif // MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_H_ diff --git a/webrtc/modules/audio_processing/vad/vad_audio_proc_internal.h b/webrtc/modules/audio_processing/vad/vad_audio_proc_internal.h index 45586b9..915524f 100644 --- a/webrtc/modules/audio_processing/vad/vad_audio_proc_internal.h +++ b/webrtc/modules/audio_processing/vad/vad_audio_proc_internal.h @@ -8,29 +8,16 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_INTERNAL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_INTERNAL_H_ +#ifndef MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_INTERNAL_H_ +#define MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROC_INTERNAL_H_ namespace webrtc { // These values should match MATLAB counterparts for unit-tests to pass. -static const double kCorrWeight[] = {1.000000, - 0.985000, - 0.970225, - 0.955672, - 0.941337, - 0.927217, - 0.913308, - 0.899609, - 0.886115, - 0.872823, - 0.859730, - 0.846834, - 0.834132, - 0.821620, - 0.809296, - 0.797156, - 0.785199}; +static const double kCorrWeight[] = { + 1.000000, 0.985000, 0.970225, 0.955672, 0.941337, 0.927217, + 0.913308, 0.899609, 0.886115, 0.872823, 0.859730, 0.846834, + 0.834132, 0.821620, 0.809296, 0.797156, 0.785199}; static const double kLpcAnalWin[] = { 0.00000000, 0.01314436, 0.02628645, 0.03942400, 0.05255473, 0.06567639, @@ -75,11 +62,9 @@ static const double kLpcAnalWin[] = { 0.06567639, 0.05255473, 0.03942400, 0.02628645, 0.01314436, 0.00000000}; static const size_t kFilterOrder = 2; -static const float kCoeffNumerator[kFilterOrder + 1] = {0.974827f, - -1.949650f, +static const float kCoeffNumerator[kFilterOrder + 1] = {0.974827f, -1.949650f, 0.974827f}; -static const float kCoeffDenominator[kFilterOrder + 1] = {1.0f, - -1.971999f, +static const float kCoeffDenominator[kFilterOrder + 1] = {1.0f, -1.971999f, 0.972457f}; static_assert(kFilterOrder + 1 == @@ -91,4 +76,4 @@ static_assert(kFilterOrder + 1 == } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROCESSING_H_ +#endif // MODULES_AUDIO_PROCESSING_VAD_VAD_AUDIO_PROCESSING_H_ diff --git a/webrtc/modules/audio_processing/vad/vad_circular_buffer.cc b/webrtc/modules/audio_processing/vad/vad_circular_buffer.cc index d337893..31f14d7 100644 --- a/webrtc/modules/audio_processing/vad/vad_circular_buffer.cc +++ b/webrtc/modules/audio_processing/vad/vad_circular_buffer.cc @@ -8,9 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/vad/vad_circular_buffer.h" +#include "modules/audio_processing/vad/vad_circular_buffer.h" -#include #include namespace webrtc { @@ -20,11 +19,9 @@ VadCircularBuffer::VadCircularBuffer(int buffer_size) is_full_(false), index_(0), buffer_size_(buffer_size), - sum_(0) { -} + sum_(0) {} -VadCircularBuffer::~VadCircularBuffer() { -} +VadCircularBuffer::~VadCircularBuffer() {} void VadCircularBuffer::Reset() { is_full_ = false; diff --git a/webrtc/modules/audio_processing/vad/vad_circular_buffer.h b/webrtc/modules/audio_processing/vad/vad_circular_buffer.h index 5238f77..46b03d4 100644 --- a/webrtc/modules/audio_processing/vad/vad_circular_buffer.h +++ b/webrtc/modules/audio_processing/vad/vad_circular_buffer.h @@ -8,10 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VAD_CIRCULAR_BUFFER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VAD_CIRCULAR_BUFFER_H_ +#ifndef MODULES_AUDIO_PROCESSING_VAD_VAD_CIRCULAR_BUFFER_H_ +#define MODULES_AUDIO_PROCESSING_VAD_VAD_CIRCULAR_BUFFER_H_ -#include "webrtc/base/scoped_ptr.h" +#include namespace webrtc { @@ -58,7 +58,7 @@ class VadCircularBuffer { // corresponding linear index. int ConvertToLinearIndex(int* index) const; - rtc::scoped_ptr buffer_; + std::unique_ptr buffer_; bool is_full_; int index_; int buffer_size_; @@ -66,4 +66,4 @@ class VadCircularBuffer { }; } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VAD_CIRCULAR_BUFFER_H_ +#endif // MODULES_AUDIO_PROCESSING_VAD_VAD_CIRCULAR_BUFFER_H_ diff --git a/webrtc/modules/audio_processing/vad/voice_activity_detector.cc b/webrtc/modules/audio_processing/vad/voice_activity_detector.cc index ef56a35..f0d34c6 100644 --- a/webrtc/modules/audio_processing/vad/voice_activity_detector.cc +++ b/webrtc/modules/audio_processing/vad/voice_activity_detector.cc @@ -8,17 +8,16 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/modules/audio_processing/vad/voice_activity_detector.h" +#include "modules/audio_processing/vad/voice_activity_detector.h" #include -#include "webrtc/base/checks.h" +#include "rtc_base/checks.h" namespace webrtc { namespace { -const size_t kMaxLength = 320; -const int kNumChannels = 1; +const size_t kNumChannels = 1; const double kDefaultVoiceValue = 1.0; const double kNeutralProbability = 0.5; @@ -28,8 +27,9 @@ const double kLowProbability = 0.01; VoiceActivityDetector::VoiceActivityDetector() : last_voice_probability_(kDefaultVoiceValue), - standalone_vad_(StandaloneVad::Create()) { -} + standalone_vad_(StandaloneVad::Create()) {} + +VoiceActivityDetector::~VoiceActivityDetector() = default; // Because ISAC has a different chunk length, it updates // |chunkwise_voice_probabilities_| and |chunkwise_rms_| when there is new data. @@ -37,8 +37,7 @@ VoiceActivityDetector::VoiceActivityDetector() void VoiceActivityDetector::ProcessChunk(const int16_t* audio, size_t length, int sample_rate_hz) { - RTC_DCHECK_EQ(static_cast(length), sample_rate_hz / 100); - RTC_DCHECK_LE(length, kMaxLength); + RTC_DCHECK_EQ(length, sample_rate_hz / 100); // Resample to the required rate. const int16_t* resampled_ptr = audio; if (sample_rate_hz != kSampleRateHz) { diff --git a/webrtc/modules/audio_processing/vad/voice_activity_detector.h b/webrtc/modules/audio_processing/vad/voice_activity_detector.h index e2dcf02..a19883d 100644 --- a/webrtc/modules/audio_processing/vad/voice_activity_detector.h +++ b/webrtc/modules/audio_processing/vad/voice_activity_detector.h @@ -8,17 +8,20 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VOICE_ACTIVITY_DETECTOR_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VOICE_ACTIVITY_DETECTOR_H_ +#ifndef MODULES_AUDIO_PROCESSING_VAD_VOICE_ACTIVITY_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_VAD_VOICE_ACTIVITY_DETECTOR_H_ +#include +#include + +#include #include -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_audio/resampler/include/resampler.h" -#include "webrtc/modules/audio_processing/vad/vad_audio_proc.h" -#include "webrtc/modules/audio_processing/vad/common.h" -#include "webrtc/modules/audio_processing/vad/pitch_based_vad.h" -#include "webrtc/modules/audio_processing/vad/standalone_vad.h" +#include "common_audio/resampler/include/resampler.h" +#include "modules/audio_processing/vad/common.h" +#include "modules/audio_processing/vad/pitch_based_vad.h" +#include "modules/audio_processing/vad/standalone_vad.h" +#include "modules/audio_processing/vad/vad_audio_proc.h" namespace webrtc { @@ -27,10 +30,9 @@ namespace webrtc { class VoiceActivityDetector { public: VoiceActivityDetector(); + ~VoiceActivityDetector(); - // Processes each audio chunk and estimates the voice probability. The maximum - // supported sample rate is 32kHz. - // TODO(aluebs): Change |length| to size_t. + // Processes each audio chunk and estimates the voice probability. void ProcessChunk(const int16_t* audio, size_t length, int sample_rate_hz); // Returns a vector of voice probabilities for each chunk. It can be empty for @@ -58,7 +60,7 @@ class VoiceActivityDetector { Resampler resampler_; VadAudioProc audio_processing_; - rtc::scoped_ptr standalone_vad_; + std::unique_ptr standalone_vad_; PitchBasedVad pitch_based_vad_; int16_t resampled_[kLength10Ms]; @@ -67,4 +69,4 @@ class VoiceActivityDetector { } // namespace webrtc -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VOICE_ACTIVITY_DETECTOR_H_ +#endif // MODULES_AUDIO_PROCESSING_VAD_VOICE_ACTIVITY_DETECTOR_H_ diff --git a/webrtc/modules/audio_processing/vad/voice_gmm_tables.h b/webrtc/modules/audio_processing/vad/voice_gmm_tables.h index 2f247c3..ef4ad7e 100644 --- a/webrtc/modules/audio_processing/vad/voice_gmm_tables.h +++ b/webrtc/modules/audio_processing/vad/voice_gmm_tables.h @@ -10,8 +10,8 @@ // GMM tables for active segments. Generated by MakeGmmTables.m. -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VOICE_GMM_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VOICE_GMM_TABLES_H_ +#ifndef MODULES_AUDIO_PROCESSING_VAD_VOICE_GMM_TABLES_H_ +#define MODULES_AUDIO_PROCESSING_VAD_VOICE_GMM_TABLES_H_ static const int kVoiceGmmNumMixtures = 12; static const int kVoiceGmmDim = 3; @@ -70,16 +70,8 @@ static const double kVoiceGmmMean[kVoiceGmmNumMixtures][kVoiceGmmDim] = { {-7.29187507662854e-01, 5.22717685022855e+02, 1.16377942283991e+02}}; static const double kVoiceGmmWeights[kVoiceGmmNumMixtures] = { - -1.39789694361035e+01, - -1.19527720202104e+01, - -1.32396317929055e+01, - -1.09436815209238e+01, - -1.13440027478149e+01, - -1.12200721834504e+01, - -1.02537324043693e+01, - -1.60789861938302e+01, - -1.03394494048344e+01, - -1.83207938586818e+01, - -1.31186044948288e+01, - -9.52479998673554e+00}; -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VAD_VOICE_GMM_TABLES_H_ + -1.39789694361035e+01, -1.19527720202104e+01, -1.32396317929055e+01, + -1.09436815209238e+01, -1.13440027478149e+01, -1.12200721834504e+01, + -1.02537324043693e+01, -1.60789861938302e+01, -1.03394494048344e+01, + -1.83207938586818e+01, -1.31186044948288e+01, -9.52479998673554e+00}; +#endif // MODULES_AUDIO_PROCESSING_VAD_VOICE_GMM_TABLES_H_ diff --git a/webrtc/modules/audio_processing/voice_detection.cc b/webrtc/modules/audio_processing/voice_detection.cc new file mode 100644 index 0000000..e6c92ae --- /dev/null +++ b/webrtc/modules/audio_processing/voice_detection.cc @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019 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 "modules/audio_processing/voice_detection.h" + +#include "common_audio/vad/include/webrtc_vad.h" +#include "modules/audio_processing/audio_buffer.h" +#include "rtc_base/checks.h" + +namespace webrtc { +class VoiceDetection::Vad { + public: + Vad() { + state_ = WebRtcVad_Create(); + RTC_CHECK(state_); + int error = WebRtcVad_Init(state_); + RTC_DCHECK_EQ(0, error); + } + ~Vad() { WebRtcVad_Free(state_); } + + Vad(Vad&) = delete; + Vad& operator=(Vad&) = delete; + + VadInst* state() { return state_; } + + private: + VadInst* state_ = nullptr; +}; + +VoiceDetection::VoiceDetection(int sample_rate_hz, Likelihood likelihood) + : sample_rate_hz_(sample_rate_hz), + frame_size_samples_(static_cast(sample_rate_hz_ / 100)), + likelihood_(likelihood), + vad_(new Vad()) { + int mode = 2; + switch (likelihood) { + case VoiceDetection::kVeryLowLikelihood: + mode = 3; + break; + case VoiceDetection::kLowLikelihood: + mode = 2; + break; + case VoiceDetection::kModerateLikelihood: + mode = 1; + break; + case VoiceDetection::kHighLikelihood: + mode = 0; + break; + default: + RTC_NOTREACHED(); + break; + } + int error = WebRtcVad_set_mode(vad_->state(), mode); + RTC_DCHECK_EQ(0, error); +} + +VoiceDetection::~VoiceDetection() {} + +bool VoiceDetection::ProcessCaptureAudio(AudioBuffer* audio) { + RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, + audio->num_frames_per_band()); + std::array mixed_low_pass_data; + rtc::ArrayView mixed_low_pass(mixed_low_pass_data.data(), + audio->num_frames_per_band()); + if (audio->num_channels() == 1) { + FloatS16ToS16(audio->split_bands_const(0)[kBand0To8kHz], + audio->num_frames_per_band(), mixed_low_pass_data.data()); + } else { + const int num_channels = static_cast(audio->num_channels()); + for (size_t i = 0; i < audio->num_frames_per_band(); ++i) { + int32_t value = + FloatS16ToS16(audio->split_channels_const(kBand0To8kHz)[0][i]); + for (int j = 1; j < num_channels; ++j) { + value += FloatS16ToS16(audio->split_channels_const(kBand0To8kHz)[j][i]); + } + mixed_low_pass_data[i] = value / num_channels; + } + } + + int vad_ret = WebRtcVad_Process(vad_->state(), sample_rate_hz_, + mixed_low_pass.data(), frame_size_samples_); + RTC_DCHECK(vad_ret == 0 || vad_ret == 1); + return vad_ret == 0 ? false : true; +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/voice_detection.h b/webrtc/modules/audio_processing/voice_detection.h new file mode 100644 index 0000000..79d44e6 --- /dev/null +++ b/webrtc/modules/audio_processing/voice_detection.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_VOICE_DETECTION_H_ +#define MODULES_AUDIO_PROCESSING_VOICE_DETECTION_H_ + +#include + +#include + +#include "modules/audio_processing/include/audio_processing.h" + +namespace webrtc { + +class AudioBuffer; + +// The voice activity detection (VAD) component analyzes the stream to +// determine if voice is present. +class VoiceDetection { + public: + // Specifies the likelihood that a frame will be declared to contain voice. + // A higher value makes it more likely that speech will not be clipped, at + // the expense of more noise being detected as voice. + enum Likelihood { + kVeryLowLikelihood, + kLowLikelihood, + kModerateLikelihood, + kHighLikelihood + }; + + VoiceDetection(int sample_rate_hz, Likelihood likelihood); + ~VoiceDetection(); + + VoiceDetection(VoiceDetection&) = delete; + VoiceDetection& operator=(VoiceDetection&) = delete; + + // Returns true if voice is detected in the current frame. + bool ProcessCaptureAudio(AudioBuffer* audio); + + Likelihood likelihood() const { return likelihood_; } + + private: + class Vad; + + int sample_rate_hz_; + size_t frame_size_samples_; + Likelihood likelihood_; + std::unique_ptr vad_; +}; +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_VOICE_DETECTION_H_ diff --git a/webrtc/modules/audio_processing/voice_detection_impl.cc b/webrtc/modules/audio_processing/voice_detection_impl.cc deleted file mode 100644 index 374189e..0000000 --- a/webrtc/modules/audio_processing/voice_detection_impl.cc +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/modules/audio_processing/voice_detection_impl.h" - -#include - -#include "webrtc/common_audio/vad/include/webrtc_vad.h" -#include "webrtc/modules/audio_processing/audio_buffer.h" -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" - -namespace webrtc { - -typedef VadInst Handle; - -namespace { -int MapSetting(VoiceDetection::Likelihood likelihood) { - switch (likelihood) { - case VoiceDetection::kVeryLowLikelihood: - return 3; - case VoiceDetection::kLowLikelihood: - return 2; - case VoiceDetection::kModerateLikelihood: - return 1; - case VoiceDetection::kHighLikelihood: - return 0; - } - assert(false); - return -1; -} -} // namespace - -VoiceDetectionImpl::VoiceDetectionImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit) - : ProcessingComponent(), - apm_(apm), - crit_(crit), - stream_has_voice_(false), - using_external_vad_(false), - likelihood_(kLowLikelihood), - frame_size_ms_(10), - frame_size_samples_(0) {} - -VoiceDetectionImpl::~VoiceDetectionImpl() {} - -int VoiceDetectionImpl::ProcessCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - if (using_external_vad_) { - using_external_vad_ = false; - return apm_->kNoError; - } - assert(audio->num_frames_per_band() <= 160); - - // TODO(ajm): concatenate data in frame buffer here. - - int vad_ret = WebRtcVad_Process(static_cast(handle(0)), - apm_->proc_split_sample_rate_hz(), - audio->mixed_low_pass_data(), - frame_size_samples_); - if (vad_ret == 0) { - stream_has_voice_ = false; - audio->set_activity(AudioFrame::kVadPassive); - } else if (vad_ret == 1) { - stream_has_voice_ = true; - audio->set_activity(AudioFrame::kVadActive); - } else { - return apm_->kUnspecifiedError; - } - - return apm_->kNoError; -} - -int VoiceDetectionImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(crit_); - return EnableComponent(enable); -} - -bool VoiceDetectionImpl::is_enabled() const { - return is_component_enabled(); -} - -int VoiceDetectionImpl::set_stream_has_voice(bool has_voice) { - using_external_vad_ = true; - stream_has_voice_ = has_voice; - return apm_->kNoError; -} - -bool VoiceDetectionImpl::stream_has_voice() const { - // TODO(ajm): enable this assertion? - //assert(using_external_vad_ || is_component_enabled()); - return stream_has_voice_; -} - -int VoiceDetectionImpl::set_likelihood(VoiceDetection::Likelihood likelihood) { - CriticalSectionScoped crit_scoped(crit_); - if (MapSetting(likelihood) == -1) { - return apm_->kBadParameterError; - } - - likelihood_ = likelihood; - return Configure(); -} - -VoiceDetection::Likelihood VoiceDetectionImpl::likelihood() const { - return likelihood_; -} - -int VoiceDetectionImpl::set_frame_size_ms(int size) { - CriticalSectionScoped crit_scoped(crit_); - assert(size == 10); // TODO(ajm): remove when supported. - if (size != 10 && - size != 20 && - size != 30) { - return apm_->kBadParameterError; - } - - frame_size_ms_ = size; - - return Initialize(); -} - -int VoiceDetectionImpl::frame_size_ms() const { - return frame_size_ms_; -} - -int VoiceDetectionImpl::Initialize() { - int err = ProcessingComponent::Initialize(); - if (err != apm_->kNoError || !is_component_enabled()) { - return err; - } - - using_external_vad_ = false; - frame_size_samples_ = static_cast( - frame_size_ms_ * apm_->proc_split_sample_rate_hz() / 1000); - // TODO(ajm): intialize frame buffer here. - - return apm_->kNoError; -} - -void* VoiceDetectionImpl::CreateHandle() const { - return WebRtcVad_Create(); -} - -void VoiceDetectionImpl::DestroyHandle(void* handle) const { - WebRtcVad_Free(static_cast(handle)); -} - -int VoiceDetectionImpl::InitializeHandle(void* handle) const { - return WebRtcVad_Init(static_cast(handle)); -} - -int VoiceDetectionImpl::ConfigureHandle(void* handle) const { - return WebRtcVad_set_mode(static_cast(handle), - MapSetting(likelihood_)); -} - -int VoiceDetectionImpl::num_handles_required() const { - return 1; -} - -int VoiceDetectionImpl::GetHandleError(void* handle) const { - // The VAD has no get_error() function. - assert(handle != NULL); - return apm_->kUnspecifiedError; -} -} // namespace webrtc diff --git a/webrtc/modules/audio_processing/voice_detection_impl.h b/webrtc/modules/audio_processing/voice_detection_impl.h deleted file mode 100644 index b188083..0000000 --- a/webrtc/modules/audio_processing/voice_detection_impl.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VOICE_DETECTION_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_VOICE_DETECTION_IMPL_H_ - -#include "webrtc/modules/audio_processing/include/audio_processing.h" -#include "webrtc/modules/audio_processing/processing_component.h" - -namespace webrtc { - -class AudioBuffer; -class CriticalSectionWrapper; - -class VoiceDetectionImpl : public VoiceDetection, - public ProcessingComponent { - public: - VoiceDetectionImpl(const AudioProcessing* apm, CriticalSectionWrapper* crit); - virtual ~VoiceDetectionImpl(); - - int ProcessCaptureAudio(AudioBuffer* audio); - - // VoiceDetection implementation. - bool is_enabled() const override; - - // ProcessingComponent implementation. - int Initialize() override; - - private: - // VoiceDetection implementation. - int Enable(bool enable) override; - int set_stream_has_voice(bool has_voice) override; - bool stream_has_voice() const override; - int set_likelihood(Likelihood likelihood) override; - Likelihood likelihood() const override; - int set_frame_size_ms(int size) override; - int frame_size_ms() const override; - - // ProcessingComponent implementation. - void* CreateHandle() const override; - int InitializeHandle(void* handle) const override; - int ConfigureHandle(void* handle) const override; - void DestroyHandle(void* handle) const override; - int num_handles_required() const override; - int GetHandleError(void* handle) const override; - - const AudioProcessing* apm_; - CriticalSectionWrapper* crit_; - bool stream_has_voice_; - bool using_external_vad_; - Likelihood likelihood_; - int frame_size_ms_; - size_t frame_size_samples_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_VOICE_DETECTION_IMPL_H_ diff --git a/webrtc/modules/interface/meson.build b/webrtc/modules/interface/meson.build deleted file mode 100644 index a74b3f0..0000000 --- a/webrtc/modules/interface/meson.build +++ /dev/null @@ -1,7 +0,0 @@ -interface_headers = [ - 'module_common_types.h', -] - -install_headers(interface_headers, - subdir: 'webrtc_audio_processing/webrtc/modules/interface' -) diff --git a/webrtc/modules/interface/module_common_types.h b/webrtc/modules/interface/module_common_types.h deleted file mode 100644 index cc4993a..0000000 --- a/webrtc/modules/interface/module_common_types.h +++ /dev/null @@ -1,816 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef MODULE_COMMON_TYPES_H -#define MODULE_COMMON_TYPES_H - -#include -#include // memcpy - -#include -#include - -#include "webrtc/base/constructormagic.h" -#include "webrtc/common_types.h" -#ifndef WEBRTC_AUDIO_PROCESSING_ONLY_BUILD -#include "webrtc/common_video/rotation.h" -#endif -#include "webrtc/typedefs.h" - -namespace webrtc { - -struct RTPAudioHeader { - uint8_t numEnergy; // number of valid entries in arrOfEnergy - uint8_t arrOfEnergy[kRtpCsrcSize]; // one energy byte (0-9) per channel - bool isCNG; // is this CNG - uint8_t channel; // number of channels 2 = stereo -}; - -const int16_t kNoPictureId = -1; -const int16_t kMaxOneBytePictureId = 0x7F; // 7 bits -const int16_t kMaxTwoBytePictureId = 0x7FFF; // 15 bits -const int16_t kNoTl0PicIdx = -1; -const uint8_t kNoTemporalIdx = 0xFF; -const uint8_t kNoSpatialIdx = 0xFF; -const uint8_t kNoGofIdx = 0xFF; -const size_t kMaxVp9RefPics = 3; -const size_t kMaxVp9FramesInGof = 0xFF; // 8 bits -const size_t kMaxVp9NumberOfSpatialLayers = 8; -const int kNoKeyIdx = -1; - -struct RTPVideoHeaderVP8 { - void InitRTPVideoHeaderVP8() { - nonReference = false; - pictureId = kNoPictureId; - tl0PicIdx = kNoTl0PicIdx; - temporalIdx = kNoTemporalIdx; - layerSync = false; - keyIdx = kNoKeyIdx; - partitionId = 0; - beginningOfPartition = false; - } - - bool nonReference; // Frame is discardable. - int16_t pictureId; // Picture ID index, 15 bits; - // kNoPictureId if PictureID does not exist. - int16_t tl0PicIdx; // TL0PIC_IDX, 8 bits; - // kNoTl0PicIdx means no value provided. - uint8_t temporalIdx; // Temporal layer index, or kNoTemporalIdx. - bool layerSync; // This frame is a layer sync frame. - // Disabled if temporalIdx == kNoTemporalIdx. - int keyIdx; // 5 bits; kNoKeyIdx means not used. - int partitionId; // VP8 partition ID - bool beginningOfPartition; // True if this packet is the first - // in a VP8 partition. Otherwise false -}; - -enum TemporalStructureMode { - kTemporalStructureMode1, // 1 temporal layer structure - i.e., IPPP... - kTemporalStructureMode2, // 2 temporal layers 0-1-0-1... - kTemporalStructureMode3 // 3 temporal layers 0-2-1-2-0-2-1-2... -}; - -struct GofInfoVP9 { - void SetGofInfoVP9(TemporalStructureMode tm) { - switch (tm) { - case kTemporalStructureMode1: - num_frames_in_gof = 1; - temporal_idx[0] = 0; - temporal_up_switch[0] = false; - num_ref_pics[0] = 1; - pid_diff[0][0] = 1; - break; - case kTemporalStructureMode2: - num_frames_in_gof = 2; - temporal_idx[0] = 0; - temporal_up_switch[0] = false; - num_ref_pics[0] = 1; - pid_diff[0][0] = 2; - - temporal_idx[1] = 1; - temporal_up_switch[1] = true; - num_ref_pics[1] = 1; - pid_diff[1][0] = 1; - break; - case kTemporalStructureMode3: - num_frames_in_gof = 4; - temporal_idx[0] = 0; - temporal_up_switch[0] = false; - num_ref_pics[0] = 1; - pid_diff[0][0] = 4; - - temporal_idx[1] = 2; - temporal_up_switch[1] = true; - num_ref_pics[1] = 1; - pid_diff[1][0] = 1; - - temporal_idx[2] = 1; - temporal_up_switch[2] = true; - num_ref_pics[2] = 1; - pid_diff[2][0] = 2; - - temporal_idx[3] = 2; - temporal_up_switch[3] = false; - num_ref_pics[3] = 2; - pid_diff[3][0] = 1; - pid_diff[3][1] = 2; - break; - default: - assert(false); - } - } - - void CopyGofInfoVP9(const GofInfoVP9& src) { - num_frames_in_gof = src.num_frames_in_gof; - for (size_t i = 0; i < num_frames_in_gof; ++i) { - temporal_idx[i] = src.temporal_idx[i]; - temporal_up_switch[i] = src.temporal_up_switch[i]; - num_ref_pics[i] = src.num_ref_pics[i]; - for (size_t r = 0; r < num_ref_pics[i]; ++r) { - pid_diff[i][r] = src.pid_diff[i][r]; - } - } - } - - size_t num_frames_in_gof; - uint8_t temporal_idx[kMaxVp9FramesInGof]; - bool temporal_up_switch[kMaxVp9FramesInGof]; - size_t num_ref_pics[kMaxVp9FramesInGof]; - int16_t pid_diff[kMaxVp9FramesInGof][kMaxVp9RefPics]; -}; - -struct RTPVideoHeaderVP9 { - void InitRTPVideoHeaderVP9() { - inter_pic_predicted = false; - flexible_mode = false; - beginning_of_frame = false; - end_of_frame = false; - ss_data_available = false; - picture_id = kNoPictureId; - max_picture_id = kMaxTwoBytePictureId; - tl0_pic_idx = kNoTl0PicIdx; - temporal_idx = kNoTemporalIdx; - spatial_idx = kNoSpatialIdx; - temporal_up_switch = false; - inter_layer_predicted = false; - gof_idx = kNoGofIdx; - num_ref_pics = 0; - num_spatial_layers = 1; - } - - bool inter_pic_predicted; // This layer frame is dependent on previously - // coded frame(s). - bool flexible_mode; // This frame is in flexible mode. - bool beginning_of_frame; // True if this packet is the first in a VP9 layer - // frame. - bool end_of_frame; // True if this packet is the last in a VP9 layer frame. - bool ss_data_available; // True if SS data is available in this payload - // descriptor. - int16_t picture_id; // PictureID index, 15 bits; - // kNoPictureId if PictureID does not exist. - int16_t max_picture_id; // Maximum picture ID index; either 0x7F or 0x7FFF; - int16_t tl0_pic_idx; // TL0PIC_IDX, 8 bits; - // kNoTl0PicIdx means no value provided. - uint8_t temporal_idx; // Temporal layer index, or kNoTemporalIdx. - uint8_t spatial_idx; // Spatial layer index, or kNoSpatialIdx. - bool temporal_up_switch; // True if upswitch to higher frame rate is possible - // starting from this frame. - bool inter_layer_predicted; // Frame is dependent on directly lower spatial - // layer frame. - - uint8_t gof_idx; // Index to predefined temporal frame info in SS data. - - size_t num_ref_pics; // Number of reference pictures used by this layer - // frame. - int16_t pid_diff[kMaxVp9RefPics]; // P_DIFF signaled to derive the PictureID - // of the reference pictures. - int16_t ref_picture_id[kMaxVp9RefPics]; // PictureID of reference pictures. - - // SS data. - size_t num_spatial_layers; // Always populated. - bool spatial_layer_resolution_present; - uint16_t width[kMaxVp9NumberOfSpatialLayers]; - uint16_t height[kMaxVp9NumberOfSpatialLayers]; - GofInfoVP9 gof; -}; - -// The packetization types that we support: single, aggregated, and fragmented. -enum H264PacketizationTypes { - kH264SingleNalu, // This packet contains a single NAL unit. - kH264StapA, // This packet contains STAP-A (single time - // aggregation) packets. If this packet has an - // associated NAL unit type, it'll be for the - // first such aggregated packet. - kH264FuA, // This packet contains a FU-A (fragmentation - // unit) packet, meaning it is a part of a frame - // that was too large to fit into a single packet. -}; - -struct RTPVideoHeaderH264 { - uint8_t nalu_type; // The NAL unit type. If this is a header for a - // fragmented packet, it's the NAL unit type of - // the original data. If this is the header for an - // aggregated packet, it's the NAL unit type of - // the first NAL unit in the packet. - H264PacketizationTypes packetization_type; -}; - -union RTPVideoTypeHeader { - RTPVideoHeaderVP8 VP8; - RTPVideoHeaderVP9 VP9; - RTPVideoHeaderH264 H264; -}; - -enum RtpVideoCodecTypes { - kRtpVideoNone, - kRtpVideoGeneric, - kRtpVideoVp8, - kRtpVideoVp9, - kRtpVideoH264 -}; -#ifndef WEBRTC_AUDIO_PROCESSING_ONLY_BUILD -// Since RTPVideoHeader is used as a member of a union, it can't have a -// non-trivial default constructor. -struct RTPVideoHeader { - uint16_t width; // size - uint16_t height; - VideoRotation rotation; - - bool isFirstPacket; // first packet in frame - uint8_t simulcastIdx; // Index if the simulcast encoder creating - // this frame, 0 if not using simulcast. - RtpVideoCodecTypes codec; - RTPVideoTypeHeader codecHeader; -}; -#endif -union RTPTypeHeader { - RTPAudioHeader Audio; -#ifndef WEBRTC_AUDIO_PROCESSING_ONLY_BUILD - RTPVideoHeader Video; -#endif -}; - -struct WebRtcRTPHeader { - RTPHeader header; - FrameType frameType; - RTPTypeHeader type; - // NTP time of the capture time in local timebase in milliseconds. - int64_t ntp_time_ms; -}; - -class RTPFragmentationHeader { - public: - RTPFragmentationHeader() - : fragmentationVectorSize(0), - fragmentationOffset(NULL), - fragmentationLength(NULL), - fragmentationTimeDiff(NULL), - fragmentationPlType(NULL) {}; - - ~RTPFragmentationHeader() { - delete[] fragmentationOffset; - delete[] fragmentationLength; - delete[] fragmentationTimeDiff; - delete[] fragmentationPlType; - } - - void CopyFrom(const RTPFragmentationHeader& src) { - if (this == &src) { - return; - } - - if (src.fragmentationVectorSize != fragmentationVectorSize) { - // new size of vectors - - // delete old - delete[] fragmentationOffset; - fragmentationOffset = NULL; - delete[] fragmentationLength; - fragmentationLength = NULL; - delete[] fragmentationTimeDiff; - fragmentationTimeDiff = NULL; - delete[] fragmentationPlType; - fragmentationPlType = NULL; - - if (src.fragmentationVectorSize > 0) { - // allocate new - if (src.fragmentationOffset) { - fragmentationOffset = new size_t[src.fragmentationVectorSize]; - } - if (src.fragmentationLength) { - fragmentationLength = new size_t[src.fragmentationVectorSize]; - } - if (src.fragmentationTimeDiff) { - fragmentationTimeDiff = new uint16_t[src.fragmentationVectorSize]; - } - if (src.fragmentationPlType) { - fragmentationPlType = new uint8_t[src.fragmentationVectorSize]; - } - } - // set new size - fragmentationVectorSize = src.fragmentationVectorSize; - } - - if (src.fragmentationVectorSize > 0) { - // copy values - if (src.fragmentationOffset) { - memcpy(fragmentationOffset, src.fragmentationOffset, - src.fragmentationVectorSize * sizeof(size_t)); - } - if (src.fragmentationLength) { - memcpy(fragmentationLength, src.fragmentationLength, - src.fragmentationVectorSize * sizeof(size_t)); - } - if (src.fragmentationTimeDiff) { - memcpy(fragmentationTimeDiff, src.fragmentationTimeDiff, - src.fragmentationVectorSize * sizeof(uint16_t)); - } - if (src.fragmentationPlType) { - memcpy(fragmentationPlType, src.fragmentationPlType, - src.fragmentationVectorSize * sizeof(uint8_t)); - } - } - } - - void VerifyAndAllocateFragmentationHeader(const size_t size) { - assert(size <= std::numeric_limits::max()); - const uint16_t size16 = static_cast(size); - if (fragmentationVectorSize < size16) { - uint16_t oldVectorSize = fragmentationVectorSize; - { - // offset - size_t* oldOffsets = fragmentationOffset; - fragmentationOffset = new size_t[size16]; - memset(fragmentationOffset + oldVectorSize, 0, - sizeof(size_t) * (size16 - oldVectorSize)); - // copy old values - memcpy(fragmentationOffset, oldOffsets, - sizeof(size_t) * oldVectorSize); - delete[] oldOffsets; - } - // length - { - size_t* oldLengths = fragmentationLength; - fragmentationLength = new size_t[size16]; - memset(fragmentationLength + oldVectorSize, 0, - sizeof(size_t) * (size16 - oldVectorSize)); - memcpy(fragmentationLength, oldLengths, - sizeof(size_t) * oldVectorSize); - delete[] oldLengths; - } - // time diff - { - uint16_t* oldTimeDiffs = fragmentationTimeDiff; - fragmentationTimeDiff = new uint16_t[size16]; - memset(fragmentationTimeDiff + oldVectorSize, 0, - sizeof(uint16_t) * (size16 - oldVectorSize)); - memcpy(fragmentationTimeDiff, oldTimeDiffs, - sizeof(uint16_t) * oldVectorSize); - delete[] oldTimeDiffs; - } - // payload type - { - uint8_t* oldTimePlTypes = fragmentationPlType; - fragmentationPlType = new uint8_t[size16]; - memset(fragmentationPlType + oldVectorSize, 0, - sizeof(uint8_t) * (size16 - oldVectorSize)); - memcpy(fragmentationPlType, oldTimePlTypes, - sizeof(uint8_t) * oldVectorSize); - delete[] oldTimePlTypes; - } - fragmentationVectorSize = size16; - } - } - - uint16_t fragmentationVectorSize; // Number of fragmentations - size_t* fragmentationOffset; // Offset of pointer to data for each - // fragmentation - size_t* fragmentationLength; // Data size for each fragmentation - uint16_t* fragmentationTimeDiff; // Timestamp difference relative "now" for - // each fragmentation - uint8_t* fragmentationPlType; // Payload type of each fragmentation - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(RTPFragmentationHeader); -}; - -struct RTCPVoIPMetric { - // RFC 3611 4.7 - uint8_t lossRate; - uint8_t discardRate; - uint8_t burstDensity; - uint8_t gapDensity; - uint16_t burstDuration; - uint16_t gapDuration; - uint16_t roundTripDelay; - uint16_t endSystemDelay; - uint8_t signalLevel; - uint8_t noiseLevel; - uint8_t RERL; - uint8_t Gmin; - uint8_t Rfactor; - uint8_t extRfactor; - uint8_t MOSLQ; - uint8_t MOSCQ; - uint8_t RXconfig; - uint16_t JBnominal; - uint16_t JBmax; - uint16_t JBabsMax; -}; - -// Types for the FEC packet masks. The type |kFecMaskRandom| is based on a -// random loss model. The type |kFecMaskBursty| is based on a bursty/consecutive -// loss model. The packet masks are defined in -// modules/rtp_rtcp/fec_private_tables_random(bursty).h -enum FecMaskType { - kFecMaskRandom, - kFecMaskBursty, -}; - -// Struct containing forward error correction settings. -struct FecProtectionParams { - int fec_rate; - bool use_uep_protection; - int max_fec_frames; - FecMaskType fec_mask_type; -}; - -// Interface used by the CallStats class to distribute call statistics. -// Callbacks will be triggered as soon as the class has been registered to a -// CallStats object using RegisterStatsObserver. -class CallStatsObserver { - public: - virtual void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) = 0; - - virtual ~CallStatsObserver() {} -}; - -struct VideoContentMetrics { - VideoContentMetrics() - : motion_magnitude(0.0f), - spatial_pred_err(0.0f), - spatial_pred_err_h(0.0f), - spatial_pred_err_v(0.0f) {} - - void Reset() { - motion_magnitude = 0.0f; - spatial_pred_err = 0.0f; - spatial_pred_err_h = 0.0f; - spatial_pred_err_v = 0.0f; - } - float motion_magnitude; - float spatial_pred_err; - float spatial_pred_err_h; - float spatial_pred_err_v; -}; - -/* This class holds up to 60 ms of super-wideband (32 kHz) stereo audio. It - * allows for adding and subtracting frames while keeping track of the resulting - * states. - * - * Notes - * - The total number of samples in |data_| is - * samples_per_channel_ * num_channels_ - * - * - Stereo data is interleaved starting with the left channel. - * - * - The +operator assume that you would never add exactly opposite frames when - * deciding the resulting state. To do this use the -operator. - */ -class AudioFrame { - public: - // Stereo, 32 kHz, 60 ms (2 * 32 * 60) - static const size_t kMaxDataSizeSamples = 3840; - - enum VADActivity { - kVadActive = 0, - kVadPassive = 1, - kVadUnknown = 2 - }; - enum SpeechType { - kNormalSpeech = 0, - kPLC = 1, - kCNG = 2, - kPLCCNG = 3, - kUndefined = 4 - }; - - AudioFrame(); - virtual ~AudioFrame() {} - - // Resets all members to their default state (except does not modify the - // contents of |data_|). - void Reset(); - - // |interleaved_| is not changed by this method. - void UpdateFrame(int id, uint32_t timestamp, const int16_t* data, - size_t samples_per_channel, int sample_rate_hz, - SpeechType speech_type, VADActivity vad_activity, - int num_channels = 1, uint32_t energy = -1); - - AudioFrame& Append(const AudioFrame& rhs); - - void CopyFrom(const AudioFrame& src); - - void Mute(); - - AudioFrame& operator>>=(const int rhs); - AudioFrame& operator+=(const AudioFrame& rhs); - AudioFrame& operator-=(const AudioFrame& rhs); - - int id_; - // RTP timestamp of the first sample in the AudioFrame. - uint32_t timestamp_; - // Time since the first frame in milliseconds. - // -1 represents an uninitialized value. - int64_t elapsed_time_ms_; - // NTP time of the estimated capture time in local timebase in milliseconds. - // -1 represents an uninitialized value. - int64_t ntp_time_ms_; - int16_t data_[kMaxDataSizeSamples]; - size_t samples_per_channel_; - int sample_rate_hz_; - int num_channels_; - SpeechType speech_type_; - VADActivity vad_activity_; - // Note that there is no guarantee that |energy_| is correct. Any user of this - // member must verify that the value is correct. - // TODO(henrike) Remove |energy_|. - // See https://code.google.com/p/webrtc/issues/detail?id=3315. - uint32_t energy_; - bool interleaved_; - - private: - RTC_DISALLOW_COPY_AND_ASSIGN(AudioFrame); -}; - -inline AudioFrame::AudioFrame() - : data_() { - Reset(); -} - -inline void AudioFrame::Reset() { - id_ = -1; - // TODO(wu): Zero is a valid value for |timestamp_|. We should initialize - // to an invalid value, or add a new member to indicate invalidity. - timestamp_ = 0; - elapsed_time_ms_ = -1; - ntp_time_ms_ = -1; - samples_per_channel_ = 0; - sample_rate_hz_ = 0; - num_channels_ = 0; - speech_type_ = kUndefined; - vad_activity_ = kVadUnknown; - energy_ = 0xffffffff; - interleaved_ = true; -} - -inline void AudioFrame::UpdateFrame(int id, - uint32_t timestamp, - const int16_t* data, - size_t samples_per_channel, - int sample_rate_hz, - SpeechType speech_type, - VADActivity vad_activity, - int num_channels, - uint32_t energy) { - id_ = id; - timestamp_ = timestamp; - samples_per_channel_ = samples_per_channel; - sample_rate_hz_ = sample_rate_hz; - speech_type_ = speech_type; - vad_activity_ = vad_activity; - num_channels_ = num_channels; - energy_ = energy; - - assert(num_channels >= 0); - const size_t length = samples_per_channel * num_channels; - assert(length <= kMaxDataSizeSamples); - if (data != NULL) { - memcpy(data_, data, sizeof(int16_t) * length); - } else { - memset(data_, 0, sizeof(int16_t) * length); - } -} - -inline void AudioFrame::CopyFrom(const AudioFrame& src) { - if (this == &src) return; - - id_ = src.id_; - timestamp_ = src.timestamp_; - elapsed_time_ms_ = src.elapsed_time_ms_; - ntp_time_ms_ = src.ntp_time_ms_; - samples_per_channel_ = src.samples_per_channel_; - sample_rate_hz_ = src.sample_rate_hz_; - speech_type_ = src.speech_type_; - vad_activity_ = src.vad_activity_; - num_channels_ = src.num_channels_; - energy_ = src.energy_; - interleaved_ = src.interleaved_; - - assert(num_channels_ >= 0); - const size_t length = samples_per_channel_ * num_channels_; - assert(length <= kMaxDataSizeSamples); - memcpy(data_, src.data_, sizeof(int16_t) * length); -} - -inline void AudioFrame::Mute() { - memset(data_, 0, samples_per_channel_ * num_channels_ * sizeof(int16_t)); -} - -inline AudioFrame& AudioFrame::operator>>=(const int rhs) { - assert((num_channels_ > 0) && (num_channels_ < 3)); - if ((num_channels_ > 2) || (num_channels_ < 1)) return *this; - - for (size_t i = 0; i < samples_per_channel_ * num_channels_; i++) { - data_[i] = static_cast(data_[i] >> rhs); - } - return *this; -} - -inline AudioFrame& AudioFrame::Append(const AudioFrame& rhs) { - // Sanity check - assert((num_channels_ > 0) && (num_channels_ < 3)); - assert(interleaved_ == rhs.interleaved_); - if ((num_channels_ > 2) || (num_channels_ < 1)) return *this; - if (num_channels_ != rhs.num_channels_) return *this; - - if ((vad_activity_ == kVadActive) || rhs.vad_activity_ == kVadActive) { - vad_activity_ = kVadActive; - } else if (vad_activity_ == kVadUnknown || rhs.vad_activity_ == kVadUnknown) { - vad_activity_ = kVadUnknown; - } - if (speech_type_ != rhs.speech_type_) { - speech_type_ = kUndefined; - } - - size_t offset = samples_per_channel_ * num_channels_; - for (size_t i = 0; i < rhs.samples_per_channel_ * rhs.num_channels_; i++) { - data_[offset + i] = rhs.data_[i]; - } - samples_per_channel_ += rhs.samples_per_channel_; - return *this; -} - -namespace { -inline int16_t ClampToInt16(int32_t input) { - if (input < -0x00008000) { - return -0x8000; - } else if (input > 0x00007FFF) { - return 0x7FFF; - } else { - return static_cast(input); - } -} -} - -inline AudioFrame& AudioFrame::operator+=(const AudioFrame& rhs) { - // Sanity check - assert((num_channels_ > 0) && (num_channels_ < 3)); - assert(interleaved_ == rhs.interleaved_); - if ((num_channels_ > 2) || (num_channels_ < 1)) return *this; - if (num_channels_ != rhs.num_channels_) return *this; - - bool noPrevData = false; - if (samples_per_channel_ != rhs.samples_per_channel_) { - if (samples_per_channel_ == 0) { - // special case we have no data to start with - samples_per_channel_ = rhs.samples_per_channel_; - noPrevData = true; - } else { - return *this; - } - } - - if ((vad_activity_ == kVadActive) || rhs.vad_activity_ == kVadActive) { - vad_activity_ = kVadActive; - } else if (vad_activity_ == kVadUnknown || rhs.vad_activity_ == kVadUnknown) { - vad_activity_ = kVadUnknown; - } - - if (speech_type_ != rhs.speech_type_) speech_type_ = kUndefined; - - if (noPrevData) { - memcpy(data_, rhs.data_, - sizeof(int16_t) * rhs.samples_per_channel_ * num_channels_); - } else { - // IMPROVEMENT this can be done very fast in assembly - for (size_t i = 0; i < samples_per_channel_ * num_channels_; i++) { - int32_t wrap_guard = - static_cast(data_[i]) + static_cast(rhs.data_[i]); - data_[i] = ClampToInt16(wrap_guard); - } - } - energy_ = 0xffffffff; - return *this; -} - -inline AudioFrame& AudioFrame::operator-=(const AudioFrame& rhs) { - // Sanity check - assert((num_channels_ > 0) && (num_channels_ < 3)); - assert(interleaved_ == rhs.interleaved_); - if ((num_channels_ > 2) || (num_channels_ < 1)) return *this; - - if ((samples_per_channel_ != rhs.samples_per_channel_) || - (num_channels_ != rhs.num_channels_)) { - return *this; - } - if ((vad_activity_ != kVadPassive) || rhs.vad_activity_ != kVadPassive) { - vad_activity_ = kVadUnknown; - } - speech_type_ = kUndefined; - - for (size_t i = 0; i < samples_per_channel_ * num_channels_; i++) { - int32_t wrap_guard = - static_cast(data_[i]) - static_cast(rhs.data_[i]); - data_[i] = ClampToInt16(wrap_guard); - } - energy_ = 0xffffffff; - return *this; -} - -inline bool IsNewerSequenceNumber(uint16_t sequence_number, - uint16_t prev_sequence_number) { - // Distinguish between elements that are exactly 0x8000 apart. - // If s1>s2 and |s1-s2| = 0x8000: IsNewer(s1,s2)=true, IsNewer(s2,s1)=false - // rather than having IsNewer(s1,s2) = IsNewer(s2,s1) = false. - if (static_cast(sequence_number - prev_sequence_number) == 0x8000) { - return sequence_number > prev_sequence_number; - } - return sequence_number != prev_sequence_number && - static_cast(sequence_number - prev_sequence_number) < 0x8000; -} - -inline bool IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) { - // Distinguish between elements that are exactly 0x80000000 apart. - // If t1>t2 and |t1-t2| = 0x80000000: IsNewer(t1,t2)=true, - // IsNewer(t2,t1)=false - // rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false. - if (static_cast(timestamp - prev_timestamp) == 0x80000000) { - return timestamp > prev_timestamp; - } - return timestamp != prev_timestamp && - static_cast(timestamp - prev_timestamp) < 0x80000000; -} - -inline uint16_t LatestSequenceNumber(uint16_t sequence_number1, - uint16_t sequence_number2) { - return IsNewerSequenceNumber(sequence_number1, sequence_number2) - ? sequence_number1 - : sequence_number2; -} - -inline uint32_t LatestTimestamp(uint32_t timestamp1, uint32_t timestamp2) { - return IsNewerTimestamp(timestamp1, timestamp2) ? timestamp1 : timestamp2; -} - -// Utility class to unwrap a sequence number to a larger type, for easier -// handling large ranges. Note that sequence numbers will never be unwrapped -// to a negative value. -class SequenceNumberUnwrapper { - public: - SequenceNumberUnwrapper() : last_seq_(-1) {} - - // Get the unwrapped sequence, but don't update the internal state. - int64_t UnwrapWithoutUpdate(uint16_t sequence_number) { - if (last_seq_ == -1) - return sequence_number; - - uint16_t cropped_last = static_cast(last_seq_); - int64_t delta = sequence_number - cropped_last; - if (IsNewerSequenceNumber(sequence_number, cropped_last)) { - if (delta < 0) - delta += (1 << 16); // Wrap forwards. - } else if (delta > 0 && (last_seq_ + delta - (1 << 16)) >= 0) { - // If sequence_number is older but delta is positive, this is a backwards - // wrap-around. However, don't wrap backwards past 0 (unwrapped). - delta -= (1 << 16); - } - - return last_seq_ + delta; - } - - // Only update the internal state to the specified last (unwrapped) sequence. - void UpdateLast(int64_t last_sequence) { last_seq_ = last_sequence; } - - // Unwrap the sequence number and update the internal state. - int64_t Unwrap(uint16_t sequence_number) { - int64_t unwrapped = UnwrapWithoutUpdate(sequence_number); - UpdateLast(unwrapped); - return unwrapped; - } - - private: - int64_t last_seq_; -}; - -} // namespace webrtc - -#endif // MODULE_COMMON_TYPES_H diff --git a/webrtc/modules/meson.build b/webrtc/modules/meson.build index 86800cd..0d73dd8 100644 --- a/webrtc/modules/meson.build +++ b/webrtc/modules/meson.build @@ -1,3 +1,3 @@ +subdir('third_party/fft') subdir('audio_coding') subdir('audio_processing') -subdir('interface') diff --git a/webrtc/modules/third_party/fft/BUILD.gn b/webrtc/modules/third_party/fft/BUILD.gn new file mode 100644 index 0000000..49dbd6f --- /dev/null +++ b/webrtc/modules/third_party/fft/BUILD.gn @@ -0,0 +1,16 @@ +# Copyright (c) 2018 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. + +import("../../../webrtc.gni") + +rtc_library("fft") { + sources = [ + "fft.c", + "fft.h", + ] +} diff --git a/webrtc/modules/third_party/fft/LICENSE b/webrtc/modules/third_party/fft/LICENSE new file mode 100644 index 0000000..c0a7805 --- /dev/null +++ b/webrtc/modules/third_party/fft/LICENSE @@ -0,0 +1,25 @@ +/* + * Copyright(c)1995,97 Mark Olesen + * Queen's Univ at Kingston (Canada) + * + * Permission to use, copy, modify, and distribute this software for + * any purpose without fee is hereby granted, provided that this + * entire notice is included in all copies of any software which is + * or includes a copy or modification of this software and in all + * copies of the supporting documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR QUEEN'S + * UNIVERSITY AT KINGSTON MAKES ANY REPRESENTATION OR WARRANTY OF ANY + * KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS + * FITNESS FOR ANY PARTICULAR PURPOSE. + * + * All of which is to say that you can do what you like with this + * source code provided you don't try to sell it as your own and you + * include an unaltered copy of this message (including the + * copyright). + * + * It is also implicitly understood that bug fixes and improvements + * should make their way back to the general Internet community so + * that everyone benefits. + */ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/fft.c b/webrtc/modules/third_party/fft/fft.c similarity index 99% rename from webrtc/modules/audio_coding/codecs/isac/main/source/fft.c rename to webrtc/modules/third_party/fft/fft.c index c854d8c..7260462 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/fft.c +++ b/webrtc/modules/third_party/fft/fft.c @@ -123,12 +123,11 @@ * following two constants should agree with the array dimensions. * *----------------------------------------------------------------------*/ -#include "fft.h" #include #include - +#include "modules/third_party/fft/fft.h" /* double precision routine */ static int @@ -212,7 +211,7 @@ int WebRtcIsac_Fftns(unsigned int ndim, const int dims[], { max_factors = (int)nSpan; } - if ((int)nSpan > max_perm) + if ((int)nSpan > max_perm) { max_perm = (int)nSpan; } @@ -416,7 +415,7 @@ static int FFTRADIX (REAL Re[], } /* test that mfactors is in range */ - if (mfactor > NFACTOR) + if (mfactor > FFT_NFACTOR) { return -1; } diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/fft.h b/webrtc/modules/third_party/fft/fft.h similarity index 61% rename from webrtc/modules/audio_coding/codecs/isac/main/source/fft.h rename to webrtc/modules/third_party/fft/fft.h index a42f57b..f8f8b6f 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/fft.h +++ b/webrtc/modules/third_party/fft/fft.h @@ -2,7 +2,7 @@ * 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 + * 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. @@ -27,19 +27,32 @@ * See the comments in the code for correct usage! */ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_FFT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_FFT_H_ +#ifndef MODULES_THIRD_PARTY_FFT_FFT_H_ +#define MODULES_THIRD_PARTY_FFT_FFT_H_ +#define FFT_MAXFFTSIZE 2048 +#define FFT_NFACTOR 11 -#include "structs.h" +typedef struct { + unsigned int SpaceAlloced; + unsigned int MaxPermAlloced; + double Tmp0[FFT_MAXFFTSIZE]; + double Tmp1[FFT_MAXFFTSIZE]; + double Tmp2[FFT_MAXFFTSIZE]; + double Tmp3[FFT_MAXFFTSIZE]; + int Perm[FFT_MAXFFTSIZE]; + int factor[FFT_NFACTOR]; +} FFTstr; /* double precision routine */ +int WebRtcIsac_Fftns(unsigned int ndim, + const int dims[], + double Re[], + double Im[], + int isign, + double scaling, + FFTstr* fftstate); -int WebRtcIsac_Fftns (unsigned int ndim, const int dims[], double Re[], double Im[], - int isign, double scaling, FFTstr *fftstate); - - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_FFT_H_ */ +#endif /* MODULES_THIRD_PARTY_FFT_FFT_H_ */ diff --git a/webrtc/modules/third_party/fft/meson.build b/webrtc/modules/third_party/fft/meson.build new file mode 100644 index 0000000..1868796 --- /dev/null +++ b/webrtc/modules/third_party/fft/meson.build @@ -0,0 +1,13 @@ +fft_sources = ['fft.c'] + +libfft = static_library('libfft', + fft_sources, + dependencies: common_deps, + include_directories: webrtc_inc, + cpp_args : common_cxxflags +) + +fft_dep = declare_dependency( + link_with: libfft +) + diff --git a/webrtc/modules/utility/interface/audio_frame_operations.h b/webrtc/modules/utility/interface/audio_frame_operations.h deleted file mode 100644 index c2af68a..0000000 --- a/webrtc/modules/utility/interface/audio_frame_operations.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_VOICE_ENGINE_AUDIO_FRAME_OPERATIONS_H_ -#define WEBRTC_VOICE_ENGINE_AUDIO_FRAME_OPERATIONS_H_ - -#include "webrtc/typedefs.h" - -namespace webrtc { - -class AudioFrame; - -// TODO(andrew): consolidate this with utility.h and audio_frame_manipulator.h. -// Change reference parameters to pointers. Consider using a namespace rather -// than a class. -class AudioFrameOperations { - public: - // Upmixes mono |src_audio| to stereo |dst_audio|. This is an out-of-place - // operation, meaning src_audio and dst_audio must point to different - // buffers. It is the caller's responsibility to ensure that |dst_audio| is - // sufficiently large. - static void MonoToStereo(const int16_t* src_audio, size_t samples_per_channel, - int16_t* dst_audio); - // |frame.num_channels_| will be updated. This version checks for sufficient - // buffer size and that |num_channels_| is mono. - static int MonoToStereo(AudioFrame* frame); - - // Downmixes stereo |src_audio| to mono |dst_audio|. This is an in-place - // operation, meaning |src_audio| and |dst_audio| may point to the same - // buffer. - static void StereoToMono(const int16_t* src_audio, size_t samples_per_channel, - int16_t* dst_audio); - // |frame.num_channels_| will be updated. This version checks that - // |num_channels_| is stereo. - static int StereoToMono(AudioFrame* frame); - - // Swap the left and right channels of |frame|. Fails silently if |frame| is - // not stereo. - static void SwapStereoChannels(AudioFrame* frame); - - // Zeros out the audio and sets |frame.energy| to zero. - static void Mute(AudioFrame& frame); - - static int Scale(float left, float right, AudioFrame& frame); - - static int ScaleWithSat(float scale, AudioFrame& frame); -}; - -} // namespace webrtc - -#endif // #ifndef WEBRTC_VOICE_ENGINE_AUDIO_FRAME_OPERATIONS_H_ diff --git a/webrtc/rtc_base/BUILD.gn b/webrtc/rtc_base/BUILD.gn new file mode 100644 index 0000000..3383580 --- /dev/null +++ b/webrtc/rtc_base/BUILD.gn @@ -0,0 +1,1482 @@ +# Copyright (c) 2014 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. + +import("//build/config/crypto.gni") +import("//build/config/ui.gni") +import("../webrtc.gni") + +if (is_android) { + import("//build/config/android/config.gni") + import("//build/config/android/rules.gni") +} + +config("rtc_base_chromium_config") { + defines = [ "NO_MAIN_THREAD_WRAPPING" ] +} + +if (!rtc_build_ssl) { + config("external_ssl_library") { + assert(rtc_ssl_root != "", + "You must specify rtc_ssl_root when rtc_build_ssl==0.") + include_dirs = [ rtc_ssl_root ] + } +} + +rtc_source_set("protobuf_utils") { + sources = [ "protobuf_utils.h" ] + if (rtc_enable_protobuf) { + public_configs = [ "//third_party/protobuf:protobuf_config" ] + deps = [ "//third_party/protobuf:protobuf_lite" ] + } +} + +rtc_source_set("compile_assert_c") { + sources = [ "compile_assert_c.h" ] +} + +rtc_source_set("ignore_wundef") { + sources = [ "ignore_wundef.h" ] +} + +rtc_source_set("untyped_function") { + sources = [ "untyped_function.h" ] + deps = [ "system:assume" ] +} + +rtc_source_set("robo_caller") { + sources = [ + "robo_caller.cc", + "robo_caller.h", + ] + deps = [ + ":untyped_function", + "../api:function_view", + "system:assume", + "system:inline", + ] +} + +# The subset of rtc_base approved for use outside of libjingle. +# TODO(bugs.webrtc.org/9838): Create small and focused build targets and remove +# the old concept of rtc_base and rtc_base_approved. +rtc_library("rtc_base_approved") { + visibility = [ "*" ] + deps = [ + ":checks", + ":rtc_task_queue", + ":safe_compare", + ":type_traits", + "../api:array_view", + "../api:scoped_refptr", + "synchronization:mutex", + "system:arch", + "system:rtc_export", + "system:unused", + "third_party/base64", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + public_deps = [] # no-presubmit-check TODO(webrtc:8603) + + sources = [ + "bind.h", + "bit_buffer.cc", + "bit_buffer.h", + "buffer.h", + "buffer_queue.cc", + "buffer_queue.h", + "byte_buffer.cc", + "byte_buffer.h", + "byte_order.h", + "copy_on_write_buffer.cc", + "copy_on_write_buffer.h", + "event_tracer.cc", + "event_tracer.h", + "location.cc", + "location.h", + "message_buffer_reader.h", + "numerics/histogram_percentile_counter.cc", + "numerics/histogram_percentile_counter.h", + "numerics/mod_ops.h", + "numerics/moving_max_counter.h", + "numerics/sample_counter.cc", + "numerics/sample_counter.h", + "one_time_event.h", + "race_checker.cc", + "race_checker.h", + "random.cc", + "random.h", + "rate_statistics.cc", + "rate_statistics.h", + "rate_tracker.cc", + "rate_tracker.h", + "swap_queue.h", + "timestamp_aligner.cc", + "timestamp_aligner.h", + "trace_event.h", + "zero_memory.cc", + "zero_memory.h", + ] + + if (is_win) { + sources += [ + "win/windows_version.cc", + "win/windows_version.h", + ] + data_deps = [ "//build/win:runtime_libs" ] + } + + if (is_nacl) { + public_deps += # no-presubmit-check TODO(webrtc:8603) + [ "//native_client_sdk/src/libraries/nacl_io" ] + } + + if (is_android) { + libs = [ "log" ] + } + + public_deps += [ # no-presubmit-check TODO(webrtc:8603) + ":atomicops", + ":criticalsection", + ":logging", + ":macromagic", + ":platform_thread", + ":platform_thread_types", + ":refcount", + ":rtc_event", + ":safe_conversions", + ":stringutils", + ":thread_checker", + ":timeutils", + "synchronization:sequence_checker", + ] +} + +rtc_source_set("macromagic") { + # TODO(bugs.webrtc.org/9606): This should not be public. + visibility = [ "*" ] + sources = [ + "arraysize.h", + "constructor_magic.h", + "format_macros.h", + "thread_annotations.h", + ] + deps = [ "system:arch" ] +} + +rtc_library("platform_thread_types") { + sources = [ + "platform_thread_types.cc", + "platform_thread_types.h", + ] + deps = [ ":macromagic" ] +} + +rtc_source_set("refcount") { + visibility = [ "*" ] + sources = [ + "ref_count.h", + "ref_counted_object.h", + "ref_counter.h", + ] + deps = [ ":macromagic" ] +} + +rtc_library("criticalsection") { + sources = [ + "deprecated/recursive_critical_section.cc", + "deprecated/recursive_critical_section.h", + ] + deps = [ + ":atomicops", + ":checks", + ":macromagic", + ":platform_thread_types", + "synchronization:yield", + "system:unused", + ] +} + +rtc_library("platform_thread") { + visibility = [ + ":rtc_base_approved", + ":rtc_task_queue_libevent", + ":rtc_task_queue_stdlib", + ":rtc_task_queue_win", + "synchronization:mutex", + "synchronization:sequence_checker", + ] + sources = [ + "platform_thread.cc", + "platform_thread.h", + ] + deps = [ + ":atomicops", + ":checks", + ":macromagic", + ":platform_thread_types", + ":rtc_event", + ":thread_checker", + ":timeutils", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_library("rtc_event") { + if (build_with_chromium) { + sources = [ + "../../webrtc_overrides/rtc_base/event.cc", + "../../webrtc_overrides/rtc_base/event.h", + ] + deps = [ + ":checks", + "system:rtc_export", # Only Chromium's rtc::Event use RTC_EXPORT. + "//base", # Dependency on chromium's waitable_event. + ] + } else { + sources = [ + "event.cc", + "event.h", + ] + deps = [ + ":checks", + "synchronization:yield_policy", + "system:warn_current_thread_is_deadlocked", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + } +} + +rtc_library("logging") { + visibility = [ "*" ] + libs = [] + deps = [ + ":checks", + ":criticalsection", + ":macromagic", + ":platform_thread_types", + ":stringutils", + ":timeutils", + "synchronization:mutex", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/meta:type_traits", + "//third_party/abseil-cpp/absl/strings", + ] + + if (build_with_chromium) { + # Dependency on chromium's logging (in //base). + deps += [ "//base" ] + sources = [ + "../../webrtc_overrides/rtc_base/logging.cc", + "../../webrtc_overrides/rtc_base/logging.h", + ] + } else { + configs += [ + "..:no_exit_time_destructors", + "..:no_global_constructors", + ] + sources = [ + "logging.cc", + "logging.h", + ] + deps += [ "system:inline" ] + + if (is_mac) { + frameworks = [ "Foundation.framework" ] + } + + # logging.h needs the deprecation header while downstream projects are + # removing code that depends on logging implementation details. + deps += [ ":deprecation" ] + + if (is_android) { + libs += [ "log" ] + } + } +} + +rtc_source_set("thread_checker") { + sources = [ "thread_checker.h" ] + deps = [ + ":deprecation", + "synchronization:sequence_checker", + ] +} + +rtc_source_set("atomicops") { + sources = [ "atomic_ops.h" ] +} + +rtc_library("checks") { + # TODO(bugs.webrtc.org/9607): This should not be public. + visibility = [ "*" ] + libs = [] + sources = [ + "checks.cc", + "checks.h", + ] + deps = [ + ":safe_compare", + "system:inline", + "system:rtc_export", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/meta:type_traits", + "//third_party/abseil-cpp/absl/strings", + ] + if (is_android) { + libs += [ "log" ] + } +} + +rtc_library("rate_limiter") { + sources = [ + "rate_limiter.cc", + "rate_limiter.h", + ] + deps = [ + ":rtc_base_approved", + "../system_wrappers", + "synchronization:mutex", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_source_set("sanitizer") { + sources = [ "sanitizer.h" ] + absl_deps = [ "//third_party/abseil-cpp/absl/meta:type_traits" ] +} + +rtc_source_set("bounded_inline_vector") { + public = [ "bounded_inline_vector.h" ] + sources = [ "bounded_inline_vector_impl.h" ] + deps = [ ":checks" ] +} + +rtc_source_set("divide_round") { + sources = [ "numerics/divide_round.h" ] + deps = [ + ":checks", + ":safe_compare", + ] +} + +rtc_source_set("safe_compare") { + sources = [ "numerics/safe_compare.h" ] + deps = [ ":type_traits" ] +} + +rtc_source_set("safe_minmax") { + sources = [ "numerics/safe_minmax.h" ] + deps = [ + ":checks", + ":safe_compare", + ":type_traits", + ] +} + +rtc_source_set("safe_conversions") { + sources = [ + "numerics/safe_conversions.h", + "numerics/safe_conversions_impl.h", + ] + deps = [ ":checks" ] +} + +rtc_library("timeutils") { + visibility = [ "*" ] + sources = [ + "time_utils.cc", + "time_utils.h", + ] + deps = [ + ":checks", + ":safe_conversions", + ":stringutils", + "system:rtc_export", + ] + libs = [] + if (is_win) { + libs += [ "winmm.lib" ] + } +} + +rtc_library("stringutils") { + sources = [ + "string_encode.cc", + "string_encode.h", + "string_to_number.cc", + "string_to_number.h", + "string_utils.cc", + "string_utils.h", + "strings/string_builder.cc", + "strings/string_builder.h", + "strings/string_format.cc", + "strings/string_format.h", + ] + deps = [ + ":checks", + ":macromagic", + ":safe_minmax", + "../api:array_view", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_library("audio_format_to_string") { + sources = [ + "strings/audio_format_to_string.cc", + "strings/audio_format_to_string.h", + ] + deps = [ + ":stringutils", + "../api/audio_codecs:audio_codecs_api", + ] +} + +rtc_source_set("type_traits") { + sources = [ "type_traits.h" ] +} + +rtc_source_set("deprecation") { + sources = [ "deprecation.h" ] +} + +rtc_library("rtc_task_queue") { + visibility = [ "*" ] + sources = [ + "task_queue.cc", + "task_queue.h", + ] + deps = [ + ":macromagic", + "../api/task_queue", + "system:rtc_export", + "task_utils:to_queued_task", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] +} + +rtc_source_set("rtc_operations_chain") { + visibility = [ "*" ] + sources = [ + "operations_chain.cc", + "operations_chain.h", + ] + deps = [ + ":checks", + ":macromagic", + ":refcount", + "../api:scoped_refptr", + "synchronization:sequence_checker", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +if (rtc_enable_libevent) { + rtc_library("rtc_task_queue_libevent") { + visibility = [ "../api/task_queue:default_task_queue_factory" ] + sources = [ + "task_queue_libevent.cc", + "task_queue_libevent.h", + ] + deps = [ + ":checks", + ":criticalsection", + ":logging", + ":macromagic", + ":platform_thread", + ":platform_thread_types", + ":safe_conversions", + ":timeutils", + "../api/task_queue", + "synchronization:mutex", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/container:inlined_vector", + "//third_party/abseil-cpp/absl/strings", + ] + if (rtc_build_libevent) { + deps += [ "//base/third_party/libevent" ] + } + } +} + +if (is_mac || is_ios) { + rtc_library("rtc_task_queue_gcd") { + visibility = [ "../api/task_queue:default_task_queue_factory" ] + sources = [ + "task_queue_gcd.cc", + "task_queue_gcd.h", + ] + deps = [ + ":checks", + ":logging", + "../api/task_queue", + "synchronization:mutex", + "system:gcd_helpers", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] + } +} + +if (is_win) { + rtc_library("rtc_task_queue_win") { + visibility = [ "../api/task_queue:default_task_queue_factory" ] + sources = [ + "task_queue_win.cc", + "task_queue_win.h", + ] + deps = [ + ":checks", + ":criticalsection", + ":logging", + ":macromagic", + ":platform_thread", + ":rtc_event", + ":safe_conversions", + ":timeutils", + "../api/task_queue", + "synchronization:mutex", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] + } +} + +rtc_library("rtc_task_queue_stdlib") { + sources = [ + "task_queue_stdlib.cc", + "task_queue_stdlib.h", + ] + deps = [ + ":checks", + ":criticalsection", + ":logging", + ":macromagic", + ":platform_thread", + ":rtc_event", + ":safe_conversions", + ":timeutils", + "../api/task_queue", + "synchronization:mutex", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_library("weak_ptr") { + sources = [ + "weak_ptr.cc", + "weak_ptr.h", + ] + deps = [ + ":refcount", + "../api:scoped_refptr", + "synchronization:sequence_checker", + ] +} + +rtc_library("rtc_numerics") { + sources = [ + "numerics/event_based_exponential_moving_average.cc", + "numerics/event_based_exponential_moving_average.h", + "numerics/exp_filter.cc", + "numerics/exp_filter.h", + "numerics/math_utils.h", + "numerics/moving_average.cc", + "numerics/moving_average.h", + "numerics/moving_median_filter.h", + "numerics/percentile_filter.h", + "numerics/running_statistics.h", + "numerics/sequence_number_util.h", + ] + deps = [ + ":checks", + ":rtc_base_approved", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("rtc_stats_counters") { + sources = [ + "numerics/event_rate_counter.cc", + "numerics/event_rate_counter.h", + "numerics/sample_stats.cc", + "numerics/sample_stats.h", + ] + deps = [ + "../api/numerics", + "../api/units:data_rate", + "../api/units:time_delta", + "../api/units:timestamp", + ] + absl_deps = [] +} + +config("rtc_json_suppressions") { + if (!is_win || is_clang) { + cflags_cc = [ + # TODO(bugs.webrtc.org/10770): Update jsoncpp API usage and remove + # -Wno-deprecated-declarations. + "-Wno-deprecated-declarations", + + # TODO(bugs.webrtc.org/10814): Remove -Wno-undef as soon as it get + # removed upstream. + "-Wno-undef", + ] + } +} + +rtc_library("rtc_json") { + public_configs = [ ":rtc_json_suppressions" ] + poisonous = [ "rtc_json" ] + defines = [] + sources = [ + "strings/json.cc", + "strings/json.h", + ] + deps = [ ":stringutils" ] + all_dependent_configs = [ "//third_party/jsoncpp:jsoncpp_config" ] + if (rtc_build_json) { + public_deps = # no-presubmit-check TODO(webrtc:8603) + [ "//third_party/jsoncpp" ] + } else { + include_dirs = [ "$rtc_jsoncpp_root" ] + + # When defined changes the include path for json.h to where it is + # expected to be when building json outside of the standalone build. + defines += [ "WEBRTC_EXTERNAL_JSON" ] + } +} + +rtc_source_set("net_helpers") { + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "net_helpers.cc", + # "net_helpers.h", + # ] +} + +rtc_source_set("async_resolver_interface") { + visibility = [ "*" ] + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "async_resolver_interface.cc", + # "async_resolver_interface.h", + # ] +} + +rtc_source_set("ip_address") { + visibility = [ "*" ] + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "ip_address.cc", + # "ip_address.h", + # ] +} + +rtc_source_set("socket_address") { + visibility = [ "*" ] + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "socket_address.cc", + # "socket_address.h", + # ] +} + +rtc_source_set("null_socket_server") { + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "null_socket_server.cc", + # "null_socket_server.h", + # ] +} + +rtc_source_set("socket_server") { + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "socket_server.h", + # ] +} + +rtc_source_set("threading") { + visibility = [ "*" ] + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "asyncresolver.cc", + # "asyncresolver.h", + # "defaultsocketserver.cc", + # "defaultsocketserver.h", + # "message_handler.cc", + # "message_handler.h", + # "network_monitor.cc", + # "network_monitor.h", + # "network_monitor_factory.cc", + # "network_monitor_factory.h", + # "physical_socket_server.cc", + # "physical_socket_server.h", + # "signal_thread.cc", + # "signal_thread.h", + # "thread.cc", + # "thread.h", + # ] +} + +rtc_source_set("socket_factory") { + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "socket_factory.h", + # ] +} + +rtc_source_set("async_socket") { + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "async_socket.cc", + # "async_socket.h", + # ] +} + +rtc_source_set("socket") { + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "socket.cc", + # "socket.h", + # ] +} + +rtc_source_set("network_constants") { + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "network_constants.h", + # ] +} + +if (is_android) { + rtc_source_set("ifaddrs_android") { + # TODO(bugs.webrtc.org/9987): This build target will soon contain + # the following files: + # sources = [ + # "ifaddrs_android.cc", + # "ifaddrs_android.h", + # ] + } +} + +if (is_win) { + rtc_source_set("win32") { + sources = [ + "win32.cc", + "win32.h", + "win32_window.cc", + "win32_window.h", + ] + + deps = [ + ":checks", + ":macromagic", + ":rtc_base_approved", + ] + + libs = [ + "crypt32.lib", + "iphlpapi.lib", + "secur32.lib", + ] + + defines = [ "_CRT_NONSTDC_NO_DEPRECATE" ] + } +} + +rtc_library("rtc_base") { + visibility = [ "*" ] + cflags = [] + cflags_cc = [] + libs = [] + defines = [] + deps = [ + ":checks", + ":deprecation", + ":rtc_task_queue", + ":stringutils", + "../api:array_view", + "../api:function_view", + "../api:scoped_refptr", + "../api/numerics", + "../api/task_queue", + "../system_wrappers:field_trial", + "network:sent_packet", + "synchronization:mutex", + "synchronization:sequence_checker", + "system:file_wrapper", + "system:inline", + "system:rtc_export", + "task_utils:pending_task_safety_flag", + "task_utils:repeating_task", + "task_utils:to_queued_task", + "third_party/base64", + "third_party/sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/container:flat_hash_map", + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] + public_deps = [ ":rtc_base_approved" ] # no-presubmit-check TODO(webrtc:8603) + public_configs = [] + + sources = [ + "async_invoker.cc", + "async_invoker.h", + "async_invoker_inl.h", + "async_packet_socket.cc", + "async_packet_socket.h", + "async_resolver_interface.cc", + "async_resolver_interface.h", + "async_socket.cc", + "async_socket.h", + "async_tcp_socket.cc", + "async_tcp_socket.h", + "async_udp_socket.cc", + "async_udp_socket.h", + "crc32.cc", + "crc32.h", + "crypt_string.cc", + "crypt_string.h", + "data_rate_limiter.cc", + "data_rate_limiter.h", + "deprecated/signal_thread.cc", + "deprecated/signal_thread.h", + "dscp.h", + "file_rotating_stream.cc", + "file_rotating_stream.h", + "helpers.cc", + "helpers.h", + "http_common.cc", + "http_common.h", + "ip_address.cc", + "ip_address.h", + "keep_ref_until_done.h", + "mdns_responder_interface.h", + "message_digest.cc", + "message_digest.h", + "message_handler.cc", + "message_handler.h", + "net_helper.cc", + "net_helper.h", + "net_helpers.cc", + "net_helpers.h", + "network.cc", + "network.h", + "network_constants.cc", + "network_constants.h", + "network_monitor.cc", + "network_monitor.h", + "network_monitor_factory.cc", + "network_monitor_factory.h", + "network_route.cc", + "network_route.h", + "null_socket_server.cc", + "null_socket_server.h", + "openssl.h", + "openssl_adapter.cc", + "openssl_adapter.h", + "openssl_certificate.cc", + "openssl_certificate.h", + "openssl_digest.cc", + "openssl_digest.h", + "openssl_identity.cc", + "openssl_identity.h", + "openssl_session_cache.cc", + "openssl_session_cache.h", + "openssl_stream_adapter.cc", + "openssl_stream_adapter.h", + "openssl_utility.cc", + "openssl_utility.h", + "physical_socket_server.cc", + "physical_socket_server.h", + "proxy_info.cc", + "proxy_info.h", + "rtc_certificate.cc", + "rtc_certificate.h", + "rtc_certificate_generator.cc", + "rtc_certificate_generator.h", + "signal_thread.h", + "sigslot_repeater.h", + "socket.cc", + "socket.h", + "socket_adapters.cc", + "socket_adapters.h", + "socket_address.cc", + "socket_address.h", + "socket_address_pair.cc", + "socket_address_pair.h", + "socket_factory.h", + "socket_server.h", + "ssl_adapter.cc", + "ssl_adapter.h", + "ssl_certificate.cc", + "ssl_certificate.h", + "ssl_fingerprint.cc", + "ssl_fingerprint.h", + "ssl_identity.cc", + "ssl_identity.h", + "ssl_stream_adapter.cc", + "ssl_stream_adapter.h", + "stream.cc", + "stream.h", + "thread.cc", + "thread.h", + "thread_message.h", + "unique_id_generator.cc", + "unique_id_generator.h", + ] + + if (build_with_chromium) { + include_dirs = [ "../../boringssl/src/include" ] + public_configs += [ ":rtc_base_chromium_config" ] + } else { + sources += [ + "callback.h", + "log_sinks.cc", + "log_sinks.h", + "rolling_accumulator.h", + "ssl_roots.h", + ] + + deps += [ ":rtc_numerics" ] + + if (is_win) { + sources += [ "win32_socket_init.h" ] + if (current_os != "winuwp") { + sources += [ + "win32_socket_server.cc", + "win32_socket_server.h", + ] + } + } + } # !build_with_chromium + + if (rtc_build_ssl) { + deps += [ "//third_party/boringssl" ] + } else { + configs += [ ":external_ssl_library" ] + } + + if (is_android) { + sources += [ + "ifaddrs_android.cc", + "ifaddrs_android.h", + ] + + libs += [ + "log", + "GLESv2", + ] + } + + if (is_ios || is_mac) { + sources += [ "mac_ifaddrs_converter.cc" ] + deps += [ "system:cocoa_threading" ] + } + + if (is_linux || is_chromeos) { + libs += [ + "dl", + "rt", + ] + } + + if (is_ios) { + frameworks = [ + "CFNetwork.framework", + "Foundation.framework", + "Security.framework", + "SystemConfiguration.framework", + "UIKit.framework", + ] + } + + if (is_win) { + deps += [ ":win32" ] + } + + if (is_posix || is_fuchsia) { + sources += [ + "ifaddrs_converter.cc", + "ifaddrs_converter.h", + ] + } + + if (is_nacl) { + public_deps += # no-presubmit-check TODO(webrtc:8603) + [ "//native_client_sdk/src/libraries/nacl_io" ] + + defines += [ "timezone=_timezone" ] + sources -= [ "ifaddrs_converter.cc" ] + } +} + +rtc_source_set("gtest_prod") { + sources = [ "gtest_prod_util.h" ] +} + +rtc_library("gunit_helpers") { + testonly = true + sources = [ + "gunit.cc", + "gunit.h", + ] + deps = [ + ":logging", + ":rtc_base", + ":rtc_base_tests_utils", + ":stringutils", + "../test:test_support", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_library("testclient") { + testonly = true + sources = [ + "test_client.cc", + "test_client.h", + ] + deps = [ + ":criticalsection", + ":gunit_helpers", + ":rtc_base", + ":rtc_base_tests_utils", + ":timeutils", + "synchronization:mutex", + ] +} + +rtc_library("robo_caller_unittests") { + testonly = true + + sources = [ "robo_caller_unittest.cc" ] + deps = [ + ":gunit_helpers", + ":robo_caller", + ":rtc_base", + "../api:function_view", + "../test:test_support", + ] +} + +rtc_library("rtc_base_tests_utils") { + testonly = true + sources = [ + "cpu_time.cc", + "cpu_time.h", + "fake_clock.cc", + "fake_clock.h", + "fake_mdns_responder.h", + "fake_network.h", + "fake_ssl_identity.cc", + "fake_ssl_identity.h", + "firewall_socket_server.cc", + "firewall_socket_server.h", + "memory_stream.cc", + "memory_stream.h", + "memory_usage.cc", + "memory_usage.h", + "nat_server.cc", + "nat_server.h", + "nat_socket_factory.cc", + "nat_socket_factory.h", + "nat_types.cc", + "nat_types.h", + "proxy_server.cc", + "proxy_server.h", + "server_socket_adapters.cc", + "server_socket_adapters.h", + "sigslot_tester.h", + "socket_stream.cc", + "socket_stream.h", + "test_base64.h", + "test_certificate_verifier.h", + "test_echo_server.cc", + "test_echo_server.h", + "test_utils.cc", + "test_utils.h", + "virtual_socket_server.cc", + "virtual_socket_server.h", + ] + deps = [ + ":checks", + ":rtc_base", + "../api/units:time_delta", + "../api/units:timestamp", + "memory:fifo_buffer", + "synchronization:mutex", + "third_party/sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/memory", + ] +} + +rtc_library("task_queue_for_test") { + testonly = true + + sources = [ + "task_queue_for_test.cc", + "task_queue_for_test.h", + ] + deps = [ + ":checks", + ":rtc_base_approved", + ":rtc_event", + ":rtc_task_queue", + "../api/task_queue", + "../api/task_queue:default_task_queue_factory", + "task_utils:to_queued_task", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +if (rtc_include_tests) { + rtc_library("sigslot_unittest") { + testonly = true + sources = [ "sigslot_unittest.cc" ] + deps = [ + ":gunit_helpers", + ":rtc_base", + ":rtc_base_tests_utils", + "../test:test_support", + "synchronization:mutex", + "third_party/sigslot", + ] + } + + rtc_library("untyped_function_unittest") { + testonly = true + sources = [ "untyped_function_unittest.cc" ] + deps = [ + ":untyped_function", + "../test:test_support", + ] + } + + rtc_library("rtc_base_nonparallel_tests") { + testonly = true + + sources = [ + "cpu_time_unittest.cc", + "file_rotating_stream_unittest.cc", + "null_socket_server_unittest.cc", + "physical_socket_server_unittest.cc", + "socket_address_unittest.cc", + "socket_unittest.cc", + "socket_unittest.h", + ] + deps = [ + ":checks", + ":gunit_helpers", + ":rtc_base", + ":rtc_base_tests_utils", + ":testclient", + "../system_wrappers", + "../test:fileutils", + "../test:test_main", + "../test:test_support", + "third_party/sigslot", + "//testing/gtest", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] + if (is_win) { + sources += [ "win32_socket_server_unittest.cc" ] + } + } + + rtc_library("rtc_base_approved_unittests") { + testonly = true + sources = [ + "atomic_ops_unittest.cc", + "base64_unittest.cc", + "bind_unittest.cc", + "bit_buffer_unittest.cc", + "bounded_inline_vector_unittest.cc", + "buffer_queue_unittest.cc", + "buffer_unittest.cc", + "byte_buffer_unittest.cc", + "byte_order_unittest.cc", + "checks_unittest.cc", + "copy_on_write_buffer_unittest.cc", + "deprecated/recursive_critical_section_unittest.cc", + "event_tracer_unittest.cc", + "event_unittest.cc", + "logging_unittest.cc", + "numerics/divide_round_unittest.cc", + "numerics/histogram_percentile_counter_unittest.cc", + "numerics/mod_ops_unittest.cc", + "numerics/moving_max_counter_unittest.cc", + "numerics/safe_compare_unittest.cc", + "numerics/safe_minmax_unittest.cc", + "numerics/sample_counter_unittest.cc", + "one_time_event_unittest.cc", + "platform_thread_unittest.cc", + "random_unittest.cc", + "rate_limiter_unittest.cc", + "rate_statistics_unittest.cc", + "rate_tracker_unittest.cc", + "ref_counted_object_unittest.cc", + "sanitizer_unittest.cc", + "string_encode_unittest.cc", + "string_to_number_unittest.cc", + "string_utils_unittest.cc", + "strings/string_builder_unittest.cc", + "strings/string_format_unittest.cc", + "swap_queue_unittest.cc", + "thread_annotations_unittest.cc", + "thread_checker_unittest.cc", + "time_utils_unittest.cc", + "timestamp_aligner_unittest.cc", + "virtual_socket_unittest.cc", + "zero_memory_unittest.cc", + ] + if (is_win) { + sources += [ "win/windows_version_unittest.cc" ] + } + deps = [ + ":bounded_inline_vector", + ":checks", + ":divide_round", + ":gunit_helpers", + ":rate_limiter", + ":rtc_base", + ":rtc_base_approved", + ":rtc_base_tests_utils", + ":rtc_numerics", + ":rtc_task_queue", + ":safe_compare", + ":safe_minmax", + ":sanitizer", + ":stringutils", + ":testclient", + "../api:array_view", + "../api:scoped_refptr", + "../api/numerics", + "../api/units:time_delta", + "../system_wrappers", + "../test:fileutils", + "../test:test_main", + "../test:test_support", + "memory:unittests", + "synchronization:mutex", + "task_utils:to_queued_task", + "third_party/base64", + "third_party/sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/memory", + ] + } + + rtc_library("rtc_task_queue_unittests") { + testonly = true + + sources = [ "task_queue_unittest.cc" ] + deps = [ + ":gunit_helpers", + ":rtc_base_approved", + ":rtc_base_tests_utils", + ":rtc_task_queue", + ":task_queue_for_test", + "../test:test_main", + "../test:test_support", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] + } + + rtc_library("rtc_operations_chain_unittests") { + testonly = true + + sources = [ "operations_chain_unittest.cc" ] + deps = [ + ":gunit_helpers", + ":rtc_base", + ":rtc_base_approved", + ":rtc_event", + ":rtc_operations_chain", + "../test:test_support", + ] + } + + rtc_library("weak_ptr_unittests") { + testonly = true + + sources = [ "weak_ptr_unittest.cc" ] + deps = [ + ":gunit_helpers", + ":rtc_base_approved", + ":rtc_base_tests_utils", + ":rtc_event", + ":task_queue_for_test", + ":weak_ptr", + "../test:test_main", + "../test:test_support", + ] + } + + rtc_library("rtc_numerics_unittests") { + testonly = true + + sources = [ + "numerics/event_based_exponential_moving_average_unittest.cc", + "numerics/exp_filter_unittest.cc", + "numerics/moving_average_unittest.cc", + "numerics/moving_median_filter_unittest.cc", + "numerics/percentile_filter_unittest.cc", + "numerics/running_statistics_unittest.cc", + "numerics/sequence_number_util_unittest.cc", + ] + deps = [ + ":rtc_base_approved", + ":rtc_numerics", + "../test:test_main", + "../test:test_support", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ] + } + + rtc_library("rtc_json_unittests") { + testonly = true + + sources = [ "strings/json_unittest.cc" ] + deps = [ + ":gunit_helpers", + ":rtc_base_tests_utils", + ":rtc_json", + "../test:test_main", + "../test:test_support", + ] + } + + rtc_library("rtc_base_unittests") { + testonly = true + defines = [] + + sources = [ + "callback_unittest.cc", + "crc32_unittest.cc", + "data_rate_limiter_unittest.cc", + "deprecated/signal_thread_unittest.cc", + "fake_clock_unittest.cc", + "helpers_unittest.cc", + "ip_address_unittest.cc", + "memory_usage_unittest.cc", + "message_digest_unittest.cc", + "nat_unittest.cc", + "network_route_unittest.cc", + "network_unittest.cc", + "proxy_unittest.cc", + "rolling_accumulator_unittest.cc", + "rtc_certificate_generator_unittest.cc", + "rtc_certificate_unittest.cc", + "sigslot_tester_unittest.cc", + "test_client_unittest.cc", + "thread_unittest.cc", + "unique_id_generator_unittest.cc", + ] + deps = [ + ":checks", + ":gunit_helpers", + ":rtc_base_tests_utils", + ":stringutils", + ":testclient", + "../api:array_view", + "../api/task_queue", + "../api/task_queue:task_queue_test", + "../test:field_trial", + "../test:fileutils", + "../test:rtc_expect_death", + "../test:test_main", + "../test:test_support", + "memory:fifo_buffer", + "synchronization:mutex", + "synchronization:synchronization_unittests", + "task_utils:pending_task_safety_flag", + "task_utils:to_queued_task", + "third_party/sigslot", + ] + if (is_win) { + sources += [ + "win32_unittest.cc", + "win32_window_unittest.cc", + ] + deps += [ ":win32" ] + } + if (is_posix || is_fuchsia) { + sources += [ + "openssl_adapter_unittest.cc", + "openssl_session_cache_unittest.cc", + "openssl_utility_unittest.cc", + "ssl_adapter_unittest.cc", + "ssl_identity_unittest.cc", + "ssl_stream_adapter_unittest.cc", + ] + } + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] + public_deps = [ ":rtc_base" ] # no-presubmit-check TODO(webrtc:8603) + if (build_with_chromium) { + include_dirs = [ "../../boringssl/src/include" ] + } + if (rtc_build_ssl) { + deps += [ "//third_party/boringssl" ] + } else { + configs += [ ":external_ssl_library" ] + } + } +} + +if (is_android) { + rtc_android_library("base_java") { + visibility = [ "*" ] + sources = [ + "java/src/org/webrtc/ContextUtils.java", + "java/src/org/webrtc/Loggable.java", + "java/src/org/webrtc/Logging.java", + "java/src/org/webrtc/Size.java", + "java/src/org/webrtc/ThreadUtils.java", + ] + deps = [ + "//third_party/android_deps:com_android_support_support_annotations_java", + ] + } + java_cpp_enum("network_monitor_enums") { + sources = [ "network_monitor.h" ] + visibility = [ "*" ] + } +} diff --git a/webrtc/base/arraysize.h b/webrtc/rtc_base/arraysize.h similarity index 85% rename from webrtc/base/arraysize.h rename to webrtc/rtc_base/arraysize.h index 56a1039..bf8e6d8 100644 --- a/webrtc/base/arraysize.h +++ b/webrtc/rtc_base/arraysize.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_BASE_ARRAYSIZE_H_ -#define WEBRTC_BASE_ARRAYSIZE_H_ +#ifndef RTC_BASE_ARRAYSIZE_H_ +#define RTC_BASE_ARRAYSIZE_H_ #include @@ -24,8 +24,9 @@ // This template function declaration is used in defining arraysize. // Note that the function doesn't need an implementation, as we only // use its type. -template char (&ArraySizeHelper(T (&array)[N]))[N]; +template +char (&ArraySizeHelper(T (&array)[N]))[N]; #define arraysize(array) (sizeof(ArraySizeHelper(array))) -#endif // WEBRTC_BASE_ARRAYSIZE_H_ +#endif // RTC_BASE_ARRAYSIZE_H_ diff --git a/webrtc/base/atomicops.h b/webrtc/rtc_base/atomic_ops.h similarity index 53% rename from webrtc/base/atomicops.h rename to webrtc/rtc_base/atomic_ops.h index a863566..18a24a8 100644 --- a/webrtc/base/atomicops.h +++ b/webrtc/rtc_base/atomic_ops.h @@ -8,16 +8,19 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_BASE_ATOMICOPS_H_ -#define WEBRTC_BASE_ATOMICOPS_H_ +#ifndef RTC_BASE_ATOMIC_OPS_H_ +#define RTC_BASE_ATOMIC_OPS_H_ #if defined(WEBRTC_WIN) +// clang-format off +// clang formating would change include order. + // Include winsock2.h before including to maintain consistency with -// win32.h. We can't include win32.h directly here since it pulls in -// headers such as basictypes.h which causes problems in Chromium where webrtc -// exists as two separate projects, webrtc and libjingle. +// win32.h. To include win32.h directly, it must be broken out into its own +// build target. #include #include +// clang-format on #endif // defined(WEBRTC_WIN) namespace rtc { @@ -31,24 +34,25 @@ class AtomicOps { static int Decrement(volatile int* i) { return ::InterlockedDecrement(reinterpret_cast(i)); } - static int AcquireLoad(volatile const int* i) { - return *i; - } - static void ReleaseStore(volatile int* i, int value) { - *i = value; - } + static int AcquireLoad(volatile const int* i) { return *i; } + static void ReleaseStore(volatile int* i, int value) { *i = value; } static int CompareAndSwap(volatile int* i, int old_value, int new_value) { return ::InterlockedCompareExchange(reinterpret_cast(i), - new_value, - old_value); + new_value, old_value); + } + // Pointer variants. + template + static T* AcquireLoadPtr(T* volatile* ptr) { + return *ptr; + } + template + static T* CompareAndSwapPtr(T* volatile* ptr, T* old_value, T* new_value) { + return static_cast(::InterlockedCompareExchangePointer( + reinterpret_cast(ptr), new_value, old_value)); } #else - static int Increment(volatile int* i) { - return __sync_add_and_fetch(i, 1); - } - static int Decrement(volatile int* i) { - return __sync_sub_and_fetch(i, 1); - } + static int Increment(volatile int* i) { return __sync_add_and_fetch(i, 1); } + static int Decrement(volatile int* i) { return __sync_sub_and_fetch(i, 1); } static int AcquireLoad(volatile const int* i) { return __atomic_load_n(i, __ATOMIC_ACQUIRE); } @@ -58,11 +62,18 @@ class AtomicOps { static int CompareAndSwap(volatile int* i, int old_value, int new_value) { return __sync_val_compare_and_swap(i, old_value, new_value); } + // Pointer variants. + template + static T* AcquireLoadPtr(T* volatile* ptr) { + return __atomic_load_n(ptr, __ATOMIC_ACQUIRE); + } + template + static T* CompareAndSwapPtr(T* volatile* ptr, T* old_value, T* new_value) { + return __sync_val_compare_and_swap(ptr, old_value, new_value); + } #endif }; +} // namespace rtc - -} - -#endif // WEBRTC_BASE_ATOMICOPS_H_ +#endif // RTC_BASE_ATOMIC_OPS_H_ diff --git a/webrtc/rtc_base/buffer.h b/webrtc/rtc_base/buffer.h new file mode 100644 index 0000000..d1639e2 --- /dev/null +++ b/webrtc/rtc_base/buffer.h @@ -0,0 +1,437 @@ +/* + * Copyright 2004 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. + */ + +#ifndef RTC_BASE_BUFFER_H_ +#define RTC_BASE_BUFFER_H_ + +#include + +#include +#include +#include +#include +#include + +#include "api/array_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/type_traits.h" +#include "rtc_base/zero_memory.h" + +namespace rtc { + +namespace internal { + +// (Internal; please don't use outside this file.) Determines if elements of +// type U are compatible with a BufferT. For most types, we just ignore +// top-level const and forbid top-level volatile and require T and U to be +// otherwise equal, but all byte-sized integers (notably char, int8_t, and +// uint8_t) are compatible with each other. (Note: We aim to get rid of this +// behavior, and treat all types the same.) +template +struct BufferCompat { + static constexpr bool value = + !std::is_volatile::value && + ((std::is_integral::value && sizeof(T) == 1) + ? (std::is_integral::value && sizeof(U) == 1) + : (std::is_same::type>::value)); +}; + +} // namespace internal + +// Basic buffer class, can be grown and shrunk dynamically. +// Unlike std::string/vector, does not initialize data when increasing size. +// If "ZeroOnFree" is true, any memory is explicitly cleared before releasing. +// The type alias "ZeroOnFreeBuffer" below should be used instead of setting +// "ZeroOnFree" in the template manually to "true". +template +class BufferT { + // We want T's destructor and default constructor to be trivial, i.e. perform + // no action, so that we don't have to touch the memory we allocate and + // deallocate. And we want T to be trivially copyable, so that we can copy T + // instances with std::memcpy. This is precisely the definition of a trivial + // type. + static_assert(std::is_trivial::value, "T must be a trivial type."); + + // This class relies heavily on being able to mutate its data. + static_assert(!std::is_const::value, "T may not be const"); + + public: + using value_type = T; + using const_iterator = const T*; + + // An empty BufferT. + BufferT() : size_(0), capacity_(0), data_(nullptr) { + RTC_DCHECK(IsConsistent()); + } + + // Disable copy construction and copy assignment, since copying a buffer is + // expensive enough that we want to force the user to be explicit about it. + BufferT(const BufferT&) = delete; + BufferT& operator=(const BufferT&) = delete; + + BufferT(BufferT&& buf) + : size_(buf.size()), + capacity_(buf.capacity()), + data_(std::move(buf.data_)) { + RTC_DCHECK(IsConsistent()); + buf.OnMovedFrom(); + } + + // Construct a buffer with the specified number of uninitialized elements. + explicit BufferT(size_t size) : BufferT(size, size) {} + + BufferT(size_t size, size_t capacity) + : size_(size), + capacity_(std::max(size, capacity)), + data_(capacity_ > 0 ? new T[capacity_] : nullptr) { + RTC_DCHECK(IsConsistent()); + } + + // Construct a buffer and copy the specified number of elements into it. + template ::value>::type* = nullptr> + BufferT(const U* data, size_t size) : BufferT(data, size, size) {} + + template ::value>::type* = nullptr> + BufferT(U* data, size_t size, size_t capacity) : BufferT(size, capacity) { + static_assert(sizeof(T) == sizeof(U), ""); + std::memcpy(data_.get(), data, size * sizeof(U)); + } + + // Construct a buffer from the contents of an array. + template ::value>::type* = nullptr> + BufferT(U (&array)[N]) : BufferT(array, N) {} + + ~BufferT() { MaybeZeroCompleteBuffer(); } + + // Get a pointer to the data. Just .data() will give you a (const) T*, but if + // T is a byte-sized integer, you may also use .data() for any other + // byte-sized integer U. + template ::value>::type* = nullptr> + const U* data() const { + RTC_DCHECK(IsConsistent()); + return reinterpret_cast(data_.get()); + } + + template ::value>::type* = nullptr> + U* data() { + RTC_DCHECK(IsConsistent()); + return reinterpret_cast(data_.get()); + } + + bool empty() const { + RTC_DCHECK(IsConsistent()); + return size_ == 0; + } + + size_t size() const { + RTC_DCHECK(IsConsistent()); + return size_; + } + + size_t capacity() const { + RTC_DCHECK(IsConsistent()); + return capacity_; + } + + BufferT& operator=(BufferT&& buf) { + RTC_DCHECK(buf.IsConsistent()); + MaybeZeroCompleteBuffer(); + size_ = buf.size_; + capacity_ = buf.capacity_; + using std::swap; + swap(data_, buf.data_); + buf.data_.reset(); + buf.OnMovedFrom(); + return *this; + } + + bool operator==(const BufferT& buf) const { + RTC_DCHECK(IsConsistent()); + if (size_ != buf.size_) { + return false; + } + if (std::is_integral::value) { + // Optimization. + return std::memcmp(data_.get(), buf.data_.get(), size_ * sizeof(T)) == 0; + } + for (size_t i = 0; i < size_; ++i) { + if (data_[i] != buf.data_[i]) { + return false; + } + } + return true; + } + + bool operator!=(const BufferT& buf) const { return !(*this == buf); } + + T& operator[](size_t index) { + RTC_DCHECK_LT(index, size_); + return data()[index]; + } + + T operator[](size_t index) const { + RTC_DCHECK_LT(index, size_); + return data()[index]; + } + + T* begin() { return data(); } + T* end() { return data() + size(); } + const T* begin() const { return data(); } + const T* end() const { return data() + size(); } + const T* cbegin() const { return data(); } + const T* cend() const { return data() + size(); } + + // The SetData functions replace the contents of the buffer. They accept the + // same input types as the constructors. + template ::value>::type* = nullptr> + void SetData(const U* data, size_t size) { + RTC_DCHECK(IsConsistent()); + const size_t old_size = size_; + size_ = 0; + AppendData(data, size); + if (ZeroOnFree && size_ < old_size) { + ZeroTrailingData(old_size - size_); + } + } + + template ::value>::type* = nullptr> + void SetData(const U (&array)[N]) { + SetData(array, N); + } + + template ::value>::type* = nullptr> + void SetData(const W& w) { + SetData(w.data(), w.size()); + } + + // Replaces the data in the buffer with at most |max_elements| of data, using + // the function |setter|, which should have the following signature: + // + // size_t setter(ArrayView view) + // + // |setter| is given an appropriately typed ArrayView of length exactly + // |max_elements| that describes the area where it should write the data; it + // should return the number of elements actually written. (If it doesn't fill + // the whole ArrayView, it should leave the unused space at the end.) + template ::value>::type* = nullptr> + size_t SetData(size_t max_elements, F&& setter) { + RTC_DCHECK(IsConsistent()); + const size_t old_size = size_; + size_ = 0; + const size_t written = AppendData(max_elements, std::forward(setter)); + if (ZeroOnFree && size_ < old_size) { + ZeroTrailingData(old_size - size_); + } + return written; + } + + // The AppendData functions add data to the end of the buffer. They accept + // the same input types as the constructors. + template ::value>::type* = nullptr> + void AppendData(const U* data, size_t size) { + RTC_DCHECK(IsConsistent()); + const size_t new_size = size_ + size; + EnsureCapacityWithHeadroom(new_size, true); + static_assert(sizeof(T) == sizeof(U), ""); + std::memcpy(data_.get() + size_, data, size * sizeof(U)); + size_ = new_size; + RTC_DCHECK(IsConsistent()); + } + + template ::value>::type* = nullptr> + void AppendData(const U (&array)[N]) { + AppendData(array, N); + } + + template ::value>::type* = nullptr> + void AppendData(const W& w) { + AppendData(w.data(), w.size()); + } + + template ::value>::type* = nullptr> + void AppendData(const U& item) { + AppendData(&item, 1); + } + + // Appends at most |max_elements| to the end of the buffer, using the function + // |setter|, which should have the following signature: + // + // size_t setter(ArrayView view) + // + // |setter| is given an appropriately typed ArrayView of length exactly + // |max_elements| that describes the area where it should write the data; it + // should return the number of elements actually written. (If it doesn't fill + // the whole ArrayView, it should leave the unused space at the end.) + template ::value>::type* = nullptr> + size_t AppendData(size_t max_elements, F&& setter) { + RTC_DCHECK(IsConsistent()); + const size_t old_size = size_; + SetSize(old_size + max_elements); + U* base_ptr = data() + old_size; + size_t written_elements = setter(rtc::ArrayView(base_ptr, max_elements)); + + RTC_CHECK_LE(written_elements, max_elements); + size_ = old_size + written_elements; + RTC_DCHECK(IsConsistent()); + return written_elements; + } + + // Sets the size of the buffer. If the new size is smaller than the old, the + // buffer contents will be kept but truncated; if the new size is greater, + // the existing contents will be kept and the new space will be + // uninitialized. + void SetSize(size_t size) { + const size_t old_size = size_; + EnsureCapacityWithHeadroom(size, true); + size_ = size; + if (ZeroOnFree && size_ < old_size) { + ZeroTrailingData(old_size - size_); + } + } + + // Ensure that the buffer size can be increased to at least capacity without + // further reallocation. (Of course, this operation might need to reallocate + // the buffer.) + void EnsureCapacity(size_t capacity) { + // Don't allocate extra headroom, since the user is asking for a specific + // capacity. + EnsureCapacityWithHeadroom(capacity, false); + } + + // Resets the buffer to zero size without altering capacity. Works even if the + // buffer has been moved from. + void Clear() { + MaybeZeroCompleteBuffer(); + size_ = 0; + RTC_DCHECK(IsConsistent()); + } + + // Swaps two buffers. Also works for buffers that have been moved from. + friend void swap(BufferT& a, BufferT& b) { + using std::swap; + swap(a.size_, b.size_); + swap(a.capacity_, b.capacity_); + swap(a.data_, b.data_); + } + + private: + void EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom) { + RTC_DCHECK(IsConsistent()); + if (capacity <= capacity_) + return; + + // If the caller asks for extra headroom, ensure that the new capacity is + // >= 1.5 times the old capacity. Any constant > 1 is sufficient to prevent + // quadratic behavior; as to why we pick 1.5 in particular, see + // https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md and + // http://www.gahcep.com/cpp-internals-stl-vector-part-1/. + const size_t new_capacity = + extra_headroom ? std::max(capacity, capacity_ + capacity_ / 2) + : capacity; + + std::unique_ptr new_data(new T[new_capacity]); + if (data_ != nullptr) { + std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T)); + } + MaybeZeroCompleteBuffer(); + data_ = std::move(new_data); + capacity_ = new_capacity; + RTC_DCHECK(IsConsistent()); + } + + // Zero the complete buffer if template argument "ZeroOnFree" is true. + void MaybeZeroCompleteBuffer() { + if (ZeroOnFree && capacity_ > 0) { + // It would be sufficient to only zero "size_" elements, as all other + // methods already ensure that the unused capacity contains no sensitive + // data---but better safe than sorry. + ExplicitZeroMemory(data_.get(), capacity_ * sizeof(T)); + } + } + + // Zero the first "count" elements of unused capacity. + void ZeroTrailingData(size_t count) { + RTC_DCHECK(IsConsistent()); + RTC_DCHECK_LE(count, capacity_ - size_); + ExplicitZeroMemory(data_.get() + size_, count * sizeof(T)); + } + + // Precondition for all methods except Clear, operator= and the destructor. + // Postcondition for all methods except move construction and move + // assignment, which leave the moved-from object in a possibly inconsistent + // state. + bool IsConsistent() const { + return (data_ || capacity_ == 0) && capacity_ >= size_; + } + + // Called when *this has been moved from. Conceptually it's a no-op, but we + // can mutate the state slightly to help subsequent sanity checks catch bugs. + void OnMovedFrom() { + RTC_DCHECK(!data_); // Our heap block should have been stolen. +#if RTC_DCHECK_IS_ON + // Ensure that *this is always inconsistent, to provoke bugs. + size_ = 1; + capacity_ = 0; +#else + // Make *this consistent and empty. Shouldn't be necessary, but better safe + // than sorry. + size_ = 0; + capacity_ = 0; +#endif + } + + size_t size_; + size_t capacity_; + std::unique_ptr data_; +}; + +// By far the most common sort of buffer. +using Buffer = BufferT; + +// A buffer that zeros memory before releasing it. +template +using ZeroOnFreeBuffer = BufferT; + +} // namespace rtc + +#endif // RTC_BASE_BUFFER_H_ diff --git a/webrtc/rtc_base/checks.cc b/webrtc/rtc_base/checks.cc new file mode 100644 index 0000000..e5fc2ed --- /dev/null +++ b/webrtc/rtc_base/checks.cc @@ -0,0 +1,207 @@ +/* + * Copyright 2006 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. + */ + +// Most of this was borrowed (with minor modifications) from V8's and Chromium's +// src/base/logging.cc. + +#include +#include +#include + +#if defined(WEBRTC_ANDROID) +#define RTC_LOG_TAG_ANDROID "rtc" +#include // NOLINT +#endif + +#if defined(WEBRTC_WIN) +#include +#endif + +#if defined(WEBRTC_WIN) +#define LAST_SYSTEM_ERROR (::GetLastError()) +#elif defined(__native_client__) && __native_client__ +#define LAST_SYSTEM_ERROR (0) +#elif defined(WEBRTC_POSIX) +#include +#define LAST_SYSTEM_ERROR (errno) +#endif // WEBRTC_WIN + +#include "rtc_base/checks.h" + +namespace { +#if defined(__GNUC__) +__attribute__((__format__(__printf__, 2, 3))) +#endif +void AppendFormat(std::string* s, const char* fmt, ...) { + va_list args, copy; + va_start(args, fmt); + va_copy(copy, args); + const int predicted_length = std::vsnprintf(nullptr, 0, fmt, copy); + va_end(copy); + + if (predicted_length > 0) { + const size_t size = s->size(); + s->resize(size + predicted_length); + // Pass "+ 1" to vsnprintf to include space for the '\0'. + std::vsnprintf(&((*s)[size]), predicted_length + 1, fmt, args); + } + va_end(args); +} +} // namespace + +namespace rtc { +namespace webrtc_checks_impl { + +#if RTC_CHECK_MSG_ENABLED +// Reads one argument from args, appends it to s and advances fmt. +// Returns true iff an argument was sucessfully parsed. +bool ParseArg(va_list* args, const CheckArgType** fmt, std::string* s) { + if (**fmt == CheckArgType::kEnd) + return false; + + switch (**fmt) { + case CheckArgType::kInt: + AppendFormat(s, "%d", va_arg(*args, int)); + break; + case CheckArgType::kLong: + AppendFormat(s, "%ld", va_arg(*args, long)); + break; + case CheckArgType::kLongLong: + AppendFormat(s, "%lld", va_arg(*args, long long)); + break; + case CheckArgType::kUInt: + AppendFormat(s, "%u", va_arg(*args, unsigned)); + break; + case CheckArgType::kULong: + AppendFormat(s, "%lu", va_arg(*args, unsigned long)); + break; + case CheckArgType::kULongLong: + AppendFormat(s, "%llu", va_arg(*args, unsigned long long)); + break; + case CheckArgType::kDouble: + AppendFormat(s, "%g", va_arg(*args, double)); + break; + case CheckArgType::kLongDouble: + AppendFormat(s, "%Lg", va_arg(*args, long double)); + break; + case CheckArgType::kCharP: + s->append(va_arg(*args, const char*)); + break; + case CheckArgType::kStdString: + s->append(*va_arg(*args, const std::string*)); + break; + case CheckArgType::kStringView: { + const absl::string_view sv = *va_arg(*args, const absl::string_view*); + s->append(sv.data(), sv.size()); + break; + } + case CheckArgType::kVoidP: + AppendFormat(s, "%p", va_arg(*args, const void*)); + break; + default: + s->append("[Invalid CheckArgType]"); + return false; + } + (*fmt)++; + return true; +} + +RTC_NORETURN void FatalLog(const char* file, + int line, + const char* message, + const CheckArgType* fmt, + ...) { + va_list args; + va_start(args, fmt); + + std::string s; + AppendFormat(&s, + "\n\n" + "#\n" + "# Fatal error in: %s, line %d\n" + "# last system error: %u\n" + "# Check failed: %s", + file, line, LAST_SYSTEM_ERROR, message); + + if (*fmt == CheckArgType::kCheckOp) { + // This log message was generated by RTC_CHECK_OP, so we have to complete + // the error message using the operands that have been passed as the first + // two arguments. + fmt++; + + std::string s1, s2; + if (ParseArg(&args, &fmt, &s1) && ParseArg(&args, &fmt, &s2)) + AppendFormat(&s, " (%s vs. %s)\n# ", s1.c_str(), s2.c_str()); + } else { + s.append("\n# "); + } + + // Append all the user-supplied arguments to the message. + while (ParseArg(&args, &fmt, &s)) + ; + + va_end(args); + + const char* output = s.c_str(); + +#if defined(WEBRTC_ANDROID) + __android_log_print(ANDROID_LOG_ERROR, RTC_LOG_TAG_ANDROID, "%s\n", output); +#endif + + fflush(stdout); + fprintf(stderr, "%s", output); + fflush(stderr); +#if defined(WEBRTC_WIN) + DebugBreak(); +#endif + abort(); +} +#else // RTC_CHECK_MSG_ENABLED +RTC_NORETURN void FatalLog(const char* file, int line) { + std::string s; + AppendFormat(&s, + "\n\n" + "#\n" + "# Fatal error in: %s, line %d\n" + "# last system error: %u\n" + "# Check failed.\n" + "# ", + file, line, LAST_SYSTEM_ERROR); + const char* output = s.c_str(); + +#if defined(WEBRTC_ANDROID) + __android_log_print(ANDROID_LOG_ERROR, RTC_LOG_TAG_ANDROID, "%s\n", output); +#endif + + fflush(stdout); + fprintf(stderr, "%s", output); + fflush(stderr); +#if defined(WEBRTC_WIN) + DebugBreak(); +#endif + abort(); +} +#endif // RTC_CHECK_MSG_ENABLED + +} // namespace webrtc_checks_impl +} // namespace rtc + +// Function to call from the C version of the RTC_CHECK and RTC_DCHECK macros. +RTC_NORETURN void rtc_FatalMessage(const char* file, + int line, + const char* msg) { +#if RTC_CHECK_MSG_ENABLED + static constexpr rtc::webrtc_checks_impl::CheckArgType t[] = { + rtc::webrtc_checks_impl::CheckArgType::kEnd}; + rtc::webrtc_checks_impl::FatalLog(file, line, msg, t); +#else + rtc::webrtc_checks_impl::FatalLog(file, line); +#endif +} diff --git a/webrtc/rtc_base/checks.h b/webrtc/rtc_base/checks.h new file mode 100644 index 0000000..61c074a --- /dev/null +++ b/webrtc/rtc_base/checks.h @@ -0,0 +1,485 @@ +/* + * Copyright 2006 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. + */ + +#ifndef RTC_BASE_CHECKS_H_ +#define RTC_BASE_CHECKS_H_ + +// If you for some reson need to know if DCHECKs are on, test the value of +// RTC_DCHECK_IS_ON. (Test its value, not if it's defined; it'll always be +// defined, to either a true or a false value.) +#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +#define RTC_DCHECK_IS_ON 1 +#else +#define RTC_DCHECK_IS_ON 0 +#endif + +// Annotate a function that will not return control flow to the caller. +#if defined(_MSC_VER) +#define RTC_NORETURN __declspec(noreturn) +#elif defined(__GNUC__) +#define RTC_NORETURN __attribute__((__noreturn__)) +#else +#define RTC_NORETURN +#endif + +#ifdef __cplusplus +extern "C" { +#endif +RTC_NORETURN void rtc_FatalMessage(const char* file, int line, const char* msg); +#ifdef __cplusplus +} // extern "C" +#endif + +#ifdef RTC_DISABLE_CHECK_MSG +#define RTC_CHECK_MSG_ENABLED 0 +#else +#define RTC_CHECK_MSG_ENABLED 1 +#endif + +#if RTC_CHECK_MSG_ENABLED +#define RTC_CHECK_EVAL_MESSAGE(message) message +#else +#define RTC_CHECK_EVAL_MESSAGE(message) "" +#endif + +#ifdef __cplusplus +// C++ version. + +#include + +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" +#include "rtc_base/numerics/safe_compare.h" +#include "rtc_base/system/inline.h" +#include "rtc_base/system/rtc_export.h" + +// The macros here print a message to stderr and abort under various +// conditions. All will accept additional stream messages. For example: +// RTC_DCHECK_EQ(foo, bar) << "I'm printed when foo != bar."; +// +// - RTC_CHECK(x) is an assertion that x is always true, and that if it isn't, +// it's better to terminate the process than to continue. During development, +// the reason that it's better to terminate might simply be that the error +// handling code isn't in place yet; in production, the reason might be that +// the author of the code truly believes that x will always be true, but that +// they recognizes that if they are wrong, abrupt and unpleasant process +// termination is still better than carrying on with the assumption violated. +// +// RTC_CHECK always evaluates its argument, so it's OK for x to have side +// effects. +// +// - RTC_DCHECK(x) is the same as RTC_CHECK(x)---an assertion that x is always +// true---except that x will only be evaluated in debug builds; in production +// builds, x is simply assumed to be true. This is useful if evaluating x is +// expensive and the expected cost of failing to detect the violated +// assumption is acceptable. You should not handle cases where a production +// build fails to spot a violated condition, even those that would result in +// crashes. If the code needs to cope with the error, make it cope, but don't +// call RTC_DCHECK; if the condition really can't occur, but you'd sleep +// better at night knowing that the process will suicide instead of carrying +// on in case you were wrong, use RTC_CHECK instead of RTC_DCHECK. +// +// RTC_DCHECK only evaluates its argument in debug builds, so if x has visible +// side effects, you need to write e.g. +// bool w = x; RTC_DCHECK(w); +// +// - RTC_CHECK_EQ, _NE, _GT, ..., and RTC_DCHECK_EQ, _NE, _GT, ... are +// specialized variants of RTC_CHECK and RTC_DCHECK that print prettier +// messages if the condition doesn't hold. Prefer them to raw RTC_CHECK and +// RTC_DCHECK. +// +// - FATAL() aborts unconditionally. + +namespace rtc { +namespace webrtc_checks_impl { +enum class CheckArgType : int8_t { + kEnd = 0, + kInt, + kLong, + kLongLong, + kUInt, + kULong, + kULongLong, + kDouble, + kLongDouble, + kCharP, + kStdString, + kStringView, + kVoidP, + + // kCheckOp doesn't represent an argument type. Instead, it is sent as the + // first argument from RTC_CHECK_OP to make FatalLog use the next two + // arguments to build the special CHECK_OP error message + // (the "a == b (1 vs. 2)" bit). + kCheckOp, +}; + +#if RTC_CHECK_MSG_ENABLED +RTC_NORETURN RTC_EXPORT void FatalLog(const char* file, + int line, + const char* message, + const CheckArgType* fmt, + ...); +#else +RTC_NORETURN RTC_EXPORT void FatalLog(const char* file, int line); +#endif + +// Wrapper for log arguments. Only ever make values of this type with the +// MakeVal() functions. +template +struct Val { + static constexpr CheckArgType Type() { return N; } + T GetVal() const { return val; } + T val; +}; + +// Case for when we need to construct a temp string and then print that. +// (We can't use Val +// because we need somewhere to store the temp string.) +struct ToStringVal { + static constexpr CheckArgType Type() { return CheckArgType::kStdString; } + const std::string* GetVal() const { return &val; } + std::string val; +}; + +inline Val MakeVal(int x) { + return {x}; +} +inline Val MakeVal(long x) { + return {x}; +} +inline Val MakeVal(long long x) { + return {x}; +} +inline Val MakeVal(unsigned int x) { + return {x}; +} +inline Val MakeVal(unsigned long x) { + return {x}; +} +inline Val MakeVal( + unsigned long long x) { + return {x}; +} + +inline Val MakeVal(double x) { + return {x}; +} +inline Val MakeVal(long double x) { + return {x}; +} + +inline Val MakeVal(const char* x) { + return {x}; +} +inline Val MakeVal( + const std::string& x) { + return {&x}; +} +inline Val MakeVal( + const absl::string_view& x) { + return {&x}; +} + +inline Val MakeVal(const void* x) { + return {x}; +} + +// The enum class types are not implicitly convertible to arithmetic types. +template ::value && + !std::is_arithmetic::value>* = nullptr> +inline decltype(MakeVal(std::declval>())) MakeVal( + T x) { + return {static_cast>(x)}; +} + +template ()))* = nullptr> +ToStringVal MakeVal(const T& x) { + return {ToLogString(x)}; +} + +// Ephemeral type that represents the result of the logging << operator. +template +class LogStreamer; + +// Base case: Before the first << argument. +template <> +class LogStreamer<> final { + public: + template ())), + absl::enable_if_t::value || + std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(U arg) const { + return LogStreamer(MakeVal(arg), this); + } + + template ())), + absl::enable_if_t::value && + !std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(const U& arg) const { + return LogStreamer(MakeVal(arg), this); + } + +#if RTC_CHECK_MSG_ENABLED + template + RTC_NORETURN RTC_FORCE_INLINE static void Call(const char* file, + const int line, + const char* message, + const Us&... args) { + static constexpr CheckArgType t[] = {Us::Type()..., CheckArgType::kEnd}; + FatalLog(file, line, message, t, args.GetVal()...); + } + + template + RTC_NORETURN RTC_FORCE_INLINE static void CallCheckOp(const char* file, + const int line, + const char* message, + const Us&... args) { + static constexpr CheckArgType t[] = {CheckArgType::kCheckOp, Us::Type()..., + CheckArgType::kEnd}; + FatalLog(file, line, message, t, args.GetVal()...); + } +#else + template + RTC_NORETURN RTC_FORCE_INLINE static void Call(const char* file, + const int line) { + FatalLog(file, line); + } +#endif +}; + +// Inductive case: We've already seen at least one << argument. The most recent +// one had type `T`, and the earlier ones had types `Ts`. +template +class LogStreamer final { + public: + RTC_FORCE_INLINE LogStreamer(T arg, const LogStreamer* prior) + : arg_(arg), prior_(prior) {} + + template ())), + absl::enable_if_t::value || + std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(U arg) const { + return LogStreamer(MakeVal(arg), this); + } + + template ())), + absl::enable_if_t::value && + !std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(const U& arg) const { + return LogStreamer(MakeVal(arg), this); + } + +#if RTC_CHECK_MSG_ENABLED + template + RTC_NORETURN RTC_FORCE_INLINE void Call(const char* file, + const int line, + const char* message, + const Us&... args) const { + prior_->Call(file, line, message, arg_, args...); + } + + template + RTC_NORETURN RTC_FORCE_INLINE void CallCheckOp(const char* file, + const int line, + const char* message, + const Us&... args) const { + prior_->CallCheckOp(file, line, message, arg_, args...); + } +#else + template + RTC_NORETURN RTC_FORCE_INLINE void Call(const char* file, + const int line) const { + prior_->Call(file, line); + } +#endif + + private: + // The most recent argument. + T arg_; + + // Earlier arguments. + const LogStreamer* prior_; +}; + +template +class FatalLogCall final { + public: + FatalLogCall(const char* file, int line, const char* message) + : file_(file), line_(line), message_(message) {} + + // This can be any binary operator with precedence lower than <<. + template + RTC_NORETURN RTC_FORCE_INLINE void operator&( + const LogStreamer& streamer) { +#if RTC_CHECK_MSG_ENABLED + isCheckOp ? streamer.CallCheckOp(file_, line_, message_) + : streamer.Call(file_, line_, message_); +#else + streamer.Call(file_, line_); +#endif + } + + private: + const char* file_; + int line_; + const char* message_; +}; + +} // namespace webrtc_checks_impl + +// The actual stream used isn't important. We reference |ignored| in the code +// but don't evaluate it; this is to avoid "unused variable" warnings (we do so +// in a particularly convoluted way with an extra ?: because that appears to be +// the simplest construct that keeps Visual Studio from complaining about +// condition being unused). +#define RTC_EAT_STREAM_PARAMETERS(ignored) \ + (true ? true : ((void)(ignored), true)) \ + ? static_cast(0) \ + : ::rtc::webrtc_checks_impl::FatalLogCall("", 0, "") & \ + ::rtc::webrtc_checks_impl::LogStreamer<>() + +// Call RTC_EAT_STREAM_PARAMETERS with an argument that fails to compile if +// values of the same types as |a| and |b| can't be compared with the given +// operation, and that would evaluate |a| and |b| if evaluated. +#define RTC_EAT_STREAM_PARAMETERS_OP(op, a, b) \ + RTC_EAT_STREAM_PARAMETERS(((void)::rtc::Safe##op(a, b))) + +// RTC_CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by NDEBUG or anything else, so the check will be executed +// regardless of compilation mode. +// +// We make sure RTC_CHECK et al. always evaluates |condition|, as +// doing RTC_CHECK(FunctionWithSideEffect()) is a common idiom. +// +// RTC_CHECK_OP is a helper macro for binary operators. +// Don't use this macro directly in your code, use RTC_CHECK_EQ et al below. +#if RTC_CHECK_MSG_ENABLED +#define RTC_CHECK(condition) \ + (condition) ? static_cast(0) \ + : ::rtc::webrtc_checks_impl::FatalLogCall( \ + __FILE__, __LINE__, #condition) & \ + ::rtc::webrtc_checks_impl::LogStreamer<>() + +#define RTC_CHECK_OP(name, op, val1, val2) \ + ::rtc::Safe##name((val1), (val2)) \ + ? static_cast(0) \ + : ::rtc::webrtc_checks_impl::FatalLogCall( \ + __FILE__, __LINE__, #val1 " " #op " " #val2) & \ + ::rtc::webrtc_checks_impl::LogStreamer<>() << (val1) << (val2) +#else +#define RTC_CHECK(condition) \ + (condition) \ + ? static_cast(0) \ + : true ? ::rtc::webrtc_checks_impl::FatalLogCall(__FILE__, \ + __LINE__, "") & \ + ::rtc::webrtc_checks_impl::LogStreamer<>() \ + : ::rtc::webrtc_checks_impl::FatalLogCall("", 0, "") & \ + ::rtc::webrtc_checks_impl::LogStreamer<>() + +#define RTC_CHECK_OP(name, op, val1, val2) \ + ::rtc::Safe##name((val1), (val2)) \ + ? static_cast(0) \ + : true ? ::rtc::webrtc_checks_impl::FatalLogCall(__FILE__, \ + __LINE__, "") & \ + ::rtc::webrtc_checks_impl::LogStreamer<>() \ + : ::rtc::webrtc_checks_impl::FatalLogCall("", 0, "") & \ + ::rtc::webrtc_checks_impl::LogStreamer<>() +#endif + +#define RTC_CHECK_EQ(val1, val2) RTC_CHECK_OP(Eq, ==, val1, val2) +#define RTC_CHECK_NE(val1, val2) RTC_CHECK_OP(Ne, !=, val1, val2) +#define RTC_CHECK_LE(val1, val2) RTC_CHECK_OP(Le, <=, val1, val2) +#define RTC_CHECK_LT(val1, val2) RTC_CHECK_OP(Lt, <, val1, val2) +#define RTC_CHECK_GE(val1, val2) RTC_CHECK_OP(Ge, >=, val1, val2) +#define RTC_CHECK_GT(val1, val2) RTC_CHECK_OP(Gt, >, val1, val2) + +// The RTC_DCHECK macro is equivalent to RTC_CHECK except that it only generates +// code in debug builds. It does reference the condition parameter in all cases, +// though, so callers won't risk getting warnings about unused variables. +#if RTC_DCHECK_IS_ON +#define RTC_DCHECK(condition) RTC_CHECK(condition) +#define RTC_DCHECK_EQ(v1, v2) RTC_CHECK_EQ(v1, v2) +#define RTC_DCHECK_NE(v1, v2) RTC_CHECK_NE(v1, v2) +#define RTC_DCHECK_LE(v1, v2) RTC_CHECK_LE(v1, v2) +#define RTC_DCHECK_LT(v1, v2) RTC_CHECK_LT(v1, v2) +#define RTC_DCHECK_GE(v1, v2) RTC_CHECK_GE(v1, v2) +#define RTC_DCHECK_GT(v1, v2) RTC_CHECK_GT(v1, v2) +#else +#define RTC_DCHECK(condition) RTC_EAT_STREAM_PARAMETERS(condition) +#define RTC_DCHECK_EQ(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Eq, v1, v2) +#define RTC_DCHECK_NE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Ne, v1, v2) +#define RTC_DCHECK_LE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Le, v1, v2) +#define RTC_DCHECK_LT(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Lt, v1, v2) +#define RTC_DCHECK_GE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Ge, v1, v2) +#define RTC_DCHECK_GT(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Gt, v1, v2) +#endif + +#define RTC_UNREACHABLE_CODE_HIT false +#define RTC_NOTREACHED() RTC_DCHECK(RTC_UNREACHABLE_CODE_HIT) + +// TODO(bugs.webrtc.org/8454): Add an RTC_ prefix or rename differently. +#define FATAL() \ + ::rtc::webrtc_checks_impl::FatalLogCall(__FILE__, __LINE__, \ + "FATAL()") & \ + ::rtc::webrtc_checks_impl::LogStreamer<>() + +// Performs the integer division a/b and returns the result. CHECKs that the +// remainder is zero. +template +inline T CheckedDivExact(T a, T b) { + RTC_CHECK_EQ(a % b, 0) << a << " is not evenly divisible by " << b; + return a / b; +} + +} // namespace rtc + +#else // __cplusplus not defined +// C version. Lacks many features compared to the C++ version, but usage +// guidelines are the same. + +#define RTC_CHECK(condition) \ + do { \ + if (!(condition)) { \ + rtc_FatalMessage(__FILE__, __LINE__, \ + RTC_CHECK_EVAL_MESSAGE("CHECK failed: " #condition)); \ + } \ + } while (0) + +#define RTC_CHECK_EQ(a, b) RTC_CHECK((a) == (b)) +#define RTC_CHECK_NE(a, b) RTC_CHECK((a) != (b)) +#define RTC_CHECK_LE(a, b) RTC_CHECK((a) <= (b)) +#define RTC_CHECK_LT(a, b) RTC_CHECK((a) < (b)) +#define RTC_CHECK_GE(a, b) RTC_CHECK((a) >= (b)) +#define RTC_CHECK_GT(a, b) RTC_CHECK((a) > (b)) + +#define RTC_DCHECK(condition) \ + do { \ + if (RTC_DCHECK_IS_ON && !(condition)) { \ + rtc_FatalMessage(__FILE__, __LINE__, \ + RTC_CHECK_EVAL_MESSAGE("DCHECK failed: " #condition)); \ + } \ + } while (0) + +#define RTC_DCHECK_EQ(a, b) RTC_DCHECK((a) == (b)) +#define RTC_DCHECK_NE(a, b) RTC_DCHECK((a) != (b)) +#define RTC_DCHECK_LE(a, b) RTC_DCHECK((a) <= (b)) +#define RTC_DCHECK_LT(a, b) RTC_DCHECK((a) < (b)) +#define RTC_DCHECK_GE(a, b) RTC_DCHECK((a) >= (b)) +#define RTC_DCHECK_GT(a, b) RTC_DCHECK((a) > (b)) + +#endif // __cplusplus + +#endif // RTC_BASE_CHECKS_H_ diff --git a/webrtc/system_wrappers/include/compile_assert_c.h b/webrtc/rtc_base/compile_assert_c.h similarity index 59% rename from webrtc/system_wrappers/include/compile_assert_c.h rename to webrtc/rtc_base/compile_assert_c.h index b402d71..db2e4a8 100644 --- a/webrtc/system_wrappers/include/compile_assert_c.h +++ b/webrtc/rtc_base/compile_assert_c.h @@ -8,17 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_COMPILE_ASSERT_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_COMPILE_ASSERT_H_ - -#ifdef __cplusplus -#error "Only use this for C files. For C++, use static_assert." -#endif +#ifndef RTC_BASE_COMPILE_ASSERT_C_H_ +#define RTC_BASE_COMPILE_ASSERT_C_H_ // Use this macro to verify at compile time that certain restrictions are met. // The argument is the boolean expression to evaluate. // Example: -// COMPILE_ASSERT(sizeof(foo) < 128); -#define COMPILE_ASSERT(expression) switch (0) {case 0: case expression:;} +// RTC_COMPILE_ASSERT(sizeof(foo) < 128); +// Note: In C++, use static_assert instead! +#define RTC_COMPILE_ASSERT(expression) \ + switch (0) { \ + case 0: \ + case expression:; \ + } -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_COMPILE_ASSERT_H_ +#endif // RTC_BASE_COMPILE_ASSERT_C_H_ diff --git a/webrtc/rtc_base/constructor_magic.h b/webrtc/rtc_base/constructor_magic.h new file mode 100644 index 0000000..8d12a7b --- /dev/null +++ b/webrtc/rtc_base/constructor_magic.h @@ -0,0 +1,20 @@ +/* + * Copyright 2004 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. + */ + +#ifndef RTC_BASE_CONSTRUCTOR_MAGIC_H_ +#define RTC_BASE_CONSTRUCTOR_MAGIC_H_ + +// A macro to disallow the copy constructor and operator= functions. This should +// be used in the declarations for a class. +#define RTC_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + TypeName& operator=(const TypeName&) = delete + +#endif // RTC_BASE_CONSTRUCTOR_MAGIC_H_ diff --git a/webrtc/rtc_base/deprecation.h b/webrtc/rtc_base/deprecation.h new file mode 100644 index 0000000..f285ab0 --- /dev/null +++ b/webrtc/rtc_base/deprecation.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015 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. + */ + +#ifndef RTC_BASE_DEPRECATION_H_ +#define RTC_BASE_DEPRECATION_H_ + +// Annotate the declarations of deprecated functions with this to cause a +// compiler warning when they're used. Like so: +// +// RTC_DEPRECATED std::pony PonyPlz(const std::pony_spec& ps); +// +// NOTE 1: The annotation goes on the declaration in the .h file, not the +// definition in the .cc file! +// +// NOTE 2: In order to keep unit testing the deprecated function without +// getting warnings, do something like this: +// +// std::pony DEPRECATED_PonyPlz(const std::pony_spec& ps); +// RTC_DEPRECATED inline std::pony PonyPlz(const std::pony_spec& ps) { +// return DEPRECATED_PonyPlz(ps); +// } +// +// In other words, rename the existing function, and provide an inline wrapper +// using the original name that calls it. That way, callers who are willing to +// call it using the DEPRECATED_-prefixed name don't get the warning. +// +// TODO(kwiberg): Remove this when we can use [[deprecated]] from C++14. +#if defined(_MSC_VER) +// Note: Deprecation warnings seem to fail to trigger on Windows +// (https://bugs.chromium.org/p/webrtc/issues/detail?id=5368). +#define RTC_DEPRECATED __declspec(deprecated) +#elif defined(__GNUC__) +#define RTC_DEPRECATED __attribute__((__deprecated__)) +#else +#define RTC_DEPRECATED +#endif + +#endif // RTC_BASE_DEPRECATION_H_ diff --git a/webrtc/rtc_base/event.cc b/webrtc/rtc_base/event.cc new file mode 100644 index 0000000..67c8746 --- /dev/null +++ b/webrtc/rtc_base/event.cc @@ -0,0 +1,203 @@ +/* + * Copyright 2004 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 "rtc_base/event.h" + +#if defined(WEBRTC_WIN) +#include +#elif defined(WEBRTC_POSIX) +#include +#include +#include +#include +#else +#error "Must define either WEBRTC_WIN or WEBRTC_POSIX." +#endif + +#include "absl/types/optional.h" +#include "rtc_base/checks.h" +#include "rtc_base/synchronization/yield_policy.h" +#include "rtc_base/system/warn_current_thread_is_deadlocked.h" + +namespace rtc { + +Event::Event() : Event(false, false) {} + +#if defined(WEBRTC_WIN) + +Event::Event(bool manual_reset, bool initially_signaled) { + event_handle_ = ::CreateEvent(nullptr, // Security attributes. + manual_reset, initially_signaled, + nullptr); // Name. + RTC_CHECK(event_handle_); +} + +Event::~Event() { + CloseHandle(event_handle_); +} + +void Event::Set() { + SetEvent(event_handle_); +} + +void Event::Reset() { + ResetEvent(event_handle_); +} + +bool Event::Wait(const int give_up_after_ms, int /*warn_after_ms*/) { + ScopedYieldPolicy::YieldExecution(); + const DWORD ms = give_up_after_ms == kForever ? INFINITE : give_up_after_ms; + return (WaitForSingleObject(event_handle_, ms) == WAIT_OBJECT_0); +} + +#elif defined(WEBRTC_POSIX) + +// On MacOS, clock_gettime is available from version 10.12, and on +// iOS, from version 10.0. So we can't use it yet. +#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) +#define USE_CLOCK_GETTIME 0 +#define USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP 0 +// On Android, pthread_condattr_setclock is available from version 21. By +// default, we target a new enough version for 64-bit platforms but not for +// 32-bit platforms. For older versions, use +// pthread_cond_timedwait_monotonic_np. +#elif defined(WEBRTC_ANDROID) && (__ANDROID_API__ < 21) +#define USE_CLOCK_GETTIME 1 +#define USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP 1 +#else +#define USE_CLOCK_GETTIME 1 +#define USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP 0 +#endif + +Event::Event(bool manual_reset, bool initially_signaled) + : is_manual_reset_(manual_reset), event_status_(initially_signaled) { + RTC_CHECK(pthread_mutex_init(&event_mutex_, nullptr) == 0); + pthread_condattr_t cond_attr; + RTC_CHECK(pthread_condattr_init(&cond_attr) == 0); +#if USE_CLOCK_GETTIME && !USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP + RTC_CHECK(pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC) == 0); +#endif + RTC_CHECK(pthread_cond_init(&event_cond_, &cond_attr) == 0); + pthread_condattr_destroy(&cond_attr); +} + +Event::~Event() { + pthread_mutex_destroy(&event_mutex_); + pthread_cond_destroy(&event_cond_); +} + +void Event::Set() { + pthread_mutex_lock(&event_mutex_); + event_status_ = true; + pthread_cond_broadcast(&event_cond_); + pthread_mutex_unlock(&event_mutex_); +} + +void Event::Reset() { + pthread_mutex_lock(&event_mutex_); + event_status_ = false; + pthread_mutex_unlock(&event_mutex_); +} + +namespace { + +timespec GetTimespec(const int milliseconds_from_now) { + timespec ts; + + // Get the current time. +#if USE_CLOCK_GETTIME + clock_gettime(CLOCK_MONOTONIC, &ts); +#else + timeval tv; + gettimeofday(&tv, nullptr); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; +#endif + + // Add the specified number of milliseconds to it. + ts.tv_sec += (milliseconds_from_now / 1000); + ts.tv_nsec += (milliseconds_from_now % 1000) * 1000000; + + // Normalize. + if (ts.tv_nsec >= 1000000000) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + + return ts; +} + +} // namespace + +bool Event::Wait(const int give_up_after_ms, const int warn_after_ms) { + // Instant when we'll log a warning message (because we've been waiting so + // long it might be a bug), but not yet give up waiting. nullopt if we + // shouldn't log a warning. + const absl::optional warn_ts = + warn_after_ms == kForever || + (give_up_after_ms != kForever && warn_after_ms > give_up_after_ms) + ? absl::nullopt + : absl::make_optional(GetTimespec(warn_after_ms)); + + // Instant when we'll stop waiting and return an error. nullopt if we should + // never give up. + const absl::optional give_up_ts = + give_up_after_ms == kForever + ? absl::nullopt + : absl::make_optional(GetTimespec(give_up_after_ms)); + + ScopedYieldPolicy::YieldExecution(); + pthread_mutex_lock(&event_mutex_); + + // Wait for `event_cond_` to trigger and `event_status_` to be set, with the + // given timeout (or without a timeout if none is given). + const auto wait = [&](const absl::optional timeout_ts) { + int error = 0; + while (!event_status_ && error == 0) { + if (timeout_ts == absl::nullopt) { + error = pthread_cond_wait(&event_cond_, &event_mutex_); + } else { +#if USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP + error = pthread_cond_timedwait_monotonic_np(&event_cond_, &event_mutex_, + &*timeout_ts); +#else + error = + pthread_cond_timedwait(&event_cond_, &event_mutex_, &*timeout_ts); +#endif + } + } + return error; + }; + + int error; + if (warn_ts == absl::nullopt) { + error = wait(give_up_ts); + } else { + error = wait(warn_ts); + if (error == ETIMEDOUT) { + webrtc::WarnThatTheCurrentThreadIsProbablyDeadlocked(); + error = wait(give_up_ts); + } + } + + // NOTE(liulk): Exactly one thread will auto-reset this event. All + // the other threads will think it's unsignaled. This seems to be + // consistent with auto-reset events in WEBRTC_WIN + if (error == 0 && !is_manual_reset_) + event_status_ = false; + + pthread_mutex_unlock(&event_mutex_); + + return (error == 0); +} + +#endif + +} // namespace rtc diff --git a/webrtc/rtc_base/event.h b/webrtc/rtc_base/event.h new file mode 100644 index 0000000..584ad5d --- /dev/null +++ b/webrtc/rtc_base/event.h @@ -0,0 +1,86 @@ +/* + * Copyright 2004 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. + */ + +#ifndef RTC_BASE_EVENT_H_ +#define RTC_BASE_EVENT_H_ + +#if defined(WEBRTC_WIN) +#include +#elif defined(WEBRTC_POSIX) +#include +#else +#error "Must define either WEBRTC_WIN or WEBRTC_POSIX." +#endif + +namespace rtc { + +class Event { + public: + static const int kForever = -1; + + Event(); + Event(bool manual_reset, bool initially_signaled); + Event(const Event&) = delete; + Event& operator=(const Event&) = delete; + ~Event(); + + void Set(); + void Reset(); + + // Waits for the event to become signaled, but logs a warning if it takes more + // than `warn_after_ms` milliseconds, and gives up completely if it takes more + // than `give_up_after_ms` milliseconds. (If `warn_after_ms >= + // give_up_after_ms`, no warning will be logged.) Either or both may be + // `kForever`, which means wait indefinitely. + // + // Returns true if the event was signaled, false if there was a timeout or + // some other error. + bool Wait(int give_up_after_ms, int warn_after_ms); + + // Waits with the given timeout and a reasonable default warning timeout. + bool Wait(int give_up_after_ms) { + return Wait(give_up_after_ms, + give_up_after_ms == kForever ? 3000 : kForever); + } + + private: +#if defined(WEBRTC_WIN) + HANDLE event_handle_; +#elif defined(WEBRTC_POSIX) + pthread_mutex_t event_mutex_; + pthread_cond_t event_cond_; + const bool is_manual_reset_; + bool event_status_; +#endif +}; + +// These classes are provided for compatibility with Chromium. +// The rtc::Event implementation is overriden inside of Chromium for the +// purposes of detecting when threads are blocked that shouldn't be as well as +// to use the more accurate event implementation that's there than is provided +// by default on some platforms (e.g. Windows). +// When building with standalone WebRTC, this class is a noop. +// For further information, please see the +// ScopedAllowBaseSyncPrimitives(ForTesting) classes in Chromium. +class ScopedAllowBaseSyncPrimitives { + public: + ScopedAllowBaseSyncPrimitives() {} + ~ScopedAllowBaseSyncPrimitives() {} +}; + +class ScopedAllowBaseSyncPrimitivesForTesting { + public: + ScopedAllowBaseSyncPrimitivesForTesting() {} + ~ScopedAllowBaseSyncPrimitivesForTesting() {} +}; + +} // namespace rtc + +#endif // RTC_BASE_EVENT_H_ diff --git a/webrtc/rtc_base/event_tracer.cc b/webrtc/rtc_base/event_tracer.cc new file mode 100644 index 0000000..3af8183 --- /dev/null +++ b/webrtc/rtc_base/event_tracer.cc @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2012 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 "rtc_base/event_tracer.h" + +#include +#include +#include +#include + +#include +#include + +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" +#include "rtc_base/event.h" +#include "rtc_base/logging.h" +#include "rtc_base/platform_thread.h" +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" +#include "rtc_base/thread_checker.h" +#include "rtc_base/time_utils.h" +#include "rtc_base/trace_event.h" + +// This is a guesstimate that should be enough in most cases. +static const size_t kEventLoggerArgsStrBufferInitialSize = 256; +static const size_t kTraceArgBufferLength = 32; + +namespace webrtc { + +namespace { + +GetCategoryEnabledPtr g_get_category_enabled_ptr = nullptr; +AddTraceEventPtr g_add_trace_event_ptr = nullptr; + +} // namespace + +void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr, + AddTraceEventPtr add_trace_event_ptr) { + g_get_category_enabled_ptr = get_category_enabled_ptr; + g_add_trace_event_ptr = add_trace_event_ptr; +} + +const unsigned char* EventTracer::GetCategoryEnabled(const char* name) { + if (g_get_category_enabled_ptr) + return g_get_category_enabled_ptr(name); + + // A string with null terminator means category is disabled. + return reinterpret_cast("\0"); +} + +// Arguments to this function (phase, etc.) are as defined in +// webrtc/rtc_base/trace_event.h. +void EventTracer::AddTraceEvent(char phase, + const unsigned char* category_enabled, + const char* name, + unsigned long long id, + int num_args, + const char** arg_names, + const unsigned char* arg_types, + const unsigned long long* arg_values, + unsigned char flags) { + if (g_add_trace_event_ptr) { + g_add_trace_event_ptr(phase, category_enabled, name, id, num_args, + arg_names, arg_types, arg_values, flags); + } +} + +} // namespace webrtc + +namespace rtc { +namespace tracing { +namespace { + +static void EventTracingThreadFunc(void* params); + +// Atomic-int fast path for avoiding logging when disabled. +static volatile int g_event_logging_active = 0; + +// TODO(pbos): Log metadata for all threads, etc. +class EventLogger final { + public: + EventLogger() + : logging_thread_(EventTracingThreadFunc, + this, + "EventTracingThread", + kLowPriority) {} + ~EventLogger() { RTC_DCHECK(thread_checker_.IsCurrent()); } + + void AddTraceEvent(const char* name, + const unsigned char* category_enabled, + char phase, + int num_args, + const char** arg_names, + const unsigned char* arg_types, + const unsigned long long* arg_values, + uint64_t timestamp, + int pid, + rtc::PlatformThreadId thread_id) { + std::vector args(num_args); + for (int i = 0; i < num_args; ++i) { + TraceArg& arg = args[i]; + arg.name = arg_names[i]; + arg.type = arg_types[i]; + arg.value.as_uint = arg_values[i]; + + // Value is a pointer to a temporary string, so we have to make a copy. + if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) { + // Space for the string and for the terminating null character. + size_t str_length = strlen(arg.value.as_string) + 1; + char* str_copy = new char[str_length]; + memcpy(str_copy, arg.value.as_string, str_length); + arg.value.as_string = str_copy; + } + } + webrtc::MutexLock lock(&mutex_); + trace_events_.push_back( + {name, category_enabled, phase, args, timestamp, 1, thread_id}); + } + + // The TraceEvent format is documented here: + // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview + void Log() { + RTC_DCHECK(output_file_); + static const int kLoggingIntervalMs = 100; + fprintf(output_file_, "{ \"traceEvents\": [\n"); + bool has_logged_event = false; + while (true) { + bool shutting_down = shutdown_event_.Wait(kLoggingIntervalMs); + std::vector events; + { + webrtc::MutexLock lock(&mutex_); + trace_events_.swap(events); + } + std::string args_str; + args_str.reserve(kEventLoggerArgsStrBufferInitialSize); + for (TraceEvent& e : events) { + args_str.clear(); + if (!e.args.empty()) { + args_str += ", \"args\": {"; + bool is_first_argument = true; + for (TraceArg& arg : e.args) { + if (!is_first_argument) + args_str += ","; + is_first_argument = false; + args_str += " \""; + args_str += arg.name; + args_str += "\": "; + args_str += TraceArgValueAsString(arg); + + // Delete our copy of the string. + if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) { + delete[] arg.value.as_string; + arg.value.as_string = nullptr; + } + } + args_str += " }"; + } + fprintf(output_file_, + "%s{ \"name\": \"%s\"" + ", \"cat\": \"%s\"" + ", \"ph\": \"%c\"" + ", \"ts\": %" PRIu64 + ", \"pid\": %d" +#if defined(WEBRTC_WIN) + ", \"tid\": %lu" +#else + ", \"tid\": %d" +#endif // defined(WEBRTC_WIN) + "%s" + "}\n", + has_logged_event ? "," : " ", e.name, e.category_enabled, + e.phase, e.timestamp, e.pid, e.tid, args_str.c_str()); + has_logged_event = true; + } + if (shutting_down) + break; + } + fprintf(output_file_, "]}\n"); + if (output_file_owned_) + fclose(output_file_); + output_file_ = nullptr; + } + + void Start(FILE* file, bool owned) { + RTC_DCHECK(thread_checker_.IsCurrent()); + RTC_DCHECK(file); + RTC_DCHECK(!output_file_); + output_file_ = file; + output_file_owned_ = owned; + { + webrtc::MutexLock lock(&mutex_); + // Since the atomic fast-path for adding events to the queue can be + // bypassed while the logging thread is shutting down there may be some + // stale events in the queue, hence the vector needs to be cleared to not + // log events from a previous logging session (which may be days old). + trace_events_.clear(); + } + // Enable event logging (fast-path). This should be disabled since starting + // shouldn't be done twice. + RTC_CHECK_EQ(0, + rtc::AtomicOps::CompareAndSwap(&g_event_logging_active, 0, 1)); + + // Finally start, everything should be set up now. + logging_thread_.Start(); + TRACE_EVENT_INSTANT0("webrtc", "EventLogger::Start"); + } + + void Stop() { + RTC_DCHECK(thread_checker_.IsCurrent()); + TRACE_EVENT_INSTANT0("webrtc", "EventLogger::Stop"); + // Try to stop. Abort if we're not currently logging. + if (rtc::AtomicOps::CompareAndSwap(&g_event_logging_active, 1, 0) == 0) + return; + + // Wake up logging thread to finish writing. + shutdown_event_.Set(); + // Join the logging thread. + logging_thread_.Stop(); + } + + private: + struct TraceArg { + const char* name; + unsigned char type; + // Copied from webrtc/rtc_base/trace_event.h TraceValueUnion. + union TraceArgValue { + bool as_bool; + unsigned long long as_uint; + long long as_int; + double as_double; + const void* as_pointer; + const char* as_string; + } value; + + // Assert that the size of the union is equal to the size of the as_uint + // field since we are assigning to arbitrary types using it. + static_assert(sizeof(TraceArgValue) == sizeof(unsigned long long), + "Size of TraceArg value union is not equal to the size of " + "the uint field of that union."); + }; + + struct TraceEvent { + const char* name; + const unsigned char* category_enabled; + char phase; + std::vector args; + uint64_t timestamp; + int pid; + rtc::PlatformThreadId tid; + }; + + static std::string TraceArgValueAsString(TraceArg arg) { + std::string output; + + if (arg.type == TRACE_VALUE_TYPE_STRING || + arg.type == TRACE_VALUE_TYPE_COPY_STRING) { + // Space for every character to be an espaced character + two for + // quatation marks. + output.reserve(strlen(arg.value.as_string) * 2 + 2); + output += '\"'; + const char* c = arg.value.as_string; + do { + if (*c == '"' || *c == '\\') { + output += '\\'; + output += *c; + } else { + output += *c; + } + } while (*++c); + output += '\"'; + } else { + output.resize(kTraceArgBufferLength); + size_t print_length = 0; + switch (arg.type) { + case TRACE_VALUE_TYPE_BOOL: + if (arg.value.as_bool) { + strcpy(&output[0], "true"); + print_length = 4; + } else { + strcpy(&output[0], "false"); + print_length = 5; + } + break; + case TRACE_VALUE_TYPE_UINT: + print_length = snprintf(&output[0], kTraceArgBufferLength, "%llu", + arg.value.as_uint); + break; + case TRACE_VALUE_TYPE_INT: + print_length = snprintf(&output[0], kTraceArgBufferLength, "%lld", + arg.value.as_int); + break; + case TRACE_VALUE_TYPE_DOUBLE: + print_length = snprintf(&output[0], kTraceArgBufferLength, "%f", + arg.value.as_double); + break; + case TRACE_VALUE_TYPE_POINTER: + print_length = snprintf(&output[0], kTraceArgBufferLength, "\"%p\"", + arg.value.as_pointer); + break; + } + size_t output_length = print_length < kTraceArgBufferLength + ? print_length + : kTraceArgBufferLength - 1; + // This will hopefully be very close to nop. On most implementations, it + // just writes null byte and sets the length field of the string. + output.resize(output_length); + } + + return output; + } + + webrtc::Mutex mutex_; + std::vector trace_events_ RTC_GUARDED_BY(mutex_); + rtc::PlatformThread logging_thread_; + rtc::Event shutdown_event_; + rtc::ThreadChecker thread_checker_; + FILE* output_file_ = nullptr; + bool output_file_owned_ = false; +}; + +static void EventTracingThreadFunc(void* params) { + static_cast(params)->Log(); +} + +static EventLogger* volatile g_event_logger = nullptr; +static const char* const kDisabledTracePrefix = TRACE_DISABLED_BY_DEFAULT(""); +const unsigned char* InternalGetCategoryEnabled(const char* name) { + const char* prefix_ptr = &kDisabledTracePrefix[0]; + const char* name_ptr = name; + // Check whether name contains the default-disabled prefix. + while (*prefix_ptr == *name_ptr && *prefix_ptr != '\0') { + ++prefix_ptr; + ++name_ptr; + } + return reinterpret_cast(*prefix_ptr == '\0' ? "" + : name); +} + +void InternalAddTraceEvent(char phase, + const unsigned char* category_enabled, + const char* name, + unsigned long long id, + int num_args, + const char** arg_names, + const unsigned char* arg_types, + const unsigned long long* arg_values, + unsigned char flags) { + // Fast path for when event tracing is inactive. + if (rtc::AtomicOps::AcquireLoad(&g_event_logging_active) == 0) + return; + + g_event_logger->AddTraceEvent(name, category_enabled, phase, num_args, + arg_names, arg_types, arg_values, + rtc::TimeMicros(), 1, rtc::CurrentThreadId()); +} + +} // namespace + +void SetupInternalTracer() { + RTC_CHECK(rtc::AtomicOps::CompareAndSwapPtr( + &g_event_logger, static_cast(nullptr), + new EventLogger()) == nullptr); + webrtc::SetupEventTracer(InternalGetCategoryEnabled, InternalAddTraceEvent); +} + +void StartInternalCaptureToFile(FILE* file) { + if (g_event_logger) { + g_event_logger->Start(file, false); + } +} + +bool StartInternalCapture(const char* filename) { + if (!g_event_logger) + return false; + + FILE* file = fopen(filename, "w"); + if (!file) { + RTC_LOG(LS_ERROR) << "Failed to open trace file '" << filename + << "' for writing."; + return false; + } + g_event_logger->Start(file, true); + return true; +} + +void StopInternalCapture() { + if (g_event_logger) { + g_event_logger->Stop(); + } +} + +void ShutdownInternalTracer() { + StopInternalCapture(); + EventLogger* old_logger = rtc::AtomicOps::AcquireLoadPtr(&g_event_logger); + RTC_DCHECK(old_logger); + RTC_CHECK(rtc::AtomicOps::CompareAndSwapPtr( + &g_event_logger, old_logger, + static_cast(nullptr)) == old_logger); + delete old_logger; + webrtc::SetupEventTracer(nullptr, nullptr); +} + +} // namespace tracing +} // namespace rtc diff --git a/webrtc/rtc_base/event_tracer.h b/webrtc/rtc_base/event_tracer.h new file mode 100644 index 0000000..4bbda57 --- /dev/null +++ b/webrtc/rtc_base/event_tracer.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2012 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. + */ + +// This file defines the interface for event tracing in WebRTC. +// +// Event log handlers are set through SetupEventTracer(). User of this API will +// provide two function pointers to handle event tracing calls. +// +// * GetCategoryEnabledPtr +// Event tracing system calls this function to determine if a particular +// event category is enabled. +// +// * AddTraceEventPtr +// Adds a tracing event. It is the user's responsibility to log the data +// provided. +// +// Parameters for the above two functions are described in trace_event.h. + +#ifndef RTC_BASE_EVENT_TRACER_H_ +#define RTC_BASE_EVENT_TRACER_H_ + +#include + +namespace webrtc { + +typedef const unsigned char* (*GetCategoryEnabledPtr)(const char* name); +typedef void (*AddTraceEventPtr)(char phase, + const unsigned char* category_enabled, + const char* name, + unsigned long long id, + int num_args, + const char** arg_names, + const unsigned char* arg_types, + const unsigned long long* arg_values, + unsigned char flags); + +// User of WebRTC can call this method to setup event tracing. +// +// This method must be called before any WebRTC methods. Functions +// provided should be thread-safe. +void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr, + AddTraceEventPtr add_trace_event_ptr); + +// This class defines interface for the event tracing system to call +// internally. Do not call these methods directly. +class EventTracer { + public: + static const unsigned char* GetCategoryEnabled(const char* name); + + static void AddTraceEvent(char phase, + const unsigned char* category_enabled, + const char* name, + unsigned long long id, + int num_args, + const char** arg_names, + const unsigned char* arg_types, + const unsigned long long* arg_values, + unsigned char flags); +}; + +} // namespace webrtc + +namespace rtc { +namespace tracing { +// Set up internal event tracer. +void SetupInternalTracer(); +bool StartInternalCapture(const char* filename); +void StartInternalCaptureToFile(FILE* file); +void StopInternalCapture(); +// Make sure we run this, this will tear down the internal tracing. +void ShutdownInternalTracer(); +} // namespace tracing +} // namespace rtc + +#endif // RTC_BASE_EVENT_TRACER_H_ diff --git a/webrtc/rtc_base/experiments/field_trial_parser.cc b/webrtc/rtc_base/experiments/field_trial_parser.cc new file mode 100644 index 0000000..b88d0f9 --- /dev/null +++ b/webrtc/rtc_base/experiments/field_trial_parser.cc @@ -0,0 +1,247 @@ +/* + * Copyright 2019 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 "rtc_base/experiments/field_trial_parser.h" + +#include + +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { +namespace { + +int FindOrEnd(std::string str, size_t start, char delimiter) { + size_t pos = str.find(delimiter, start); + pos = (pos == std::string::npos) ? str.length() : pos; + return static_cast(pos); +} +} // namespace + +FieldTrialParameterInterface::FieldTrialParameterInterface(std::string key) + : key_(key) {} +FieldTrialParameterInterface::~FieldTrialParameterInterface() { + RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_ + << "' never used."; +} + +void ParseFieldTrial( + std::initializer_list fields, + std::string trial_string) { + std::map field_map; + FieldTrialParameterInterface* keyless_field = nullptr; + for (FieldTrialParameterInterface* field : fields) { + field->MarkAsUsed(); + if (!field->sub_parameters_.empty()) { + for (FieldTrialParameterInterface* sub_field : field->sub_parameters_) { + RTC_DCHECK(!sub_field->key_.empty()); + sub_field->MarkAsUsed(); + field_map[sub_field->key_] = sub_field; + } + continue; + } + + if (field->key_.empty()) { + RTC_DCHECK(!keyless_field); + keyless_field = field; + } else { + field_map[field->key_] = field; + } + } + + size_t i = 0; + while (i < trial_string.length()) { + int val_end = FindOrEnd(trial_string, i, ','); + int colon_pos = FindOrEnd(trial_string, i, ':'); + int key_end = std::min(val_end, colon_pos); + int val_begin = key_end + 1; + std::string key = trial_string.substr(i, key_end - i); + absl::optional opt_value; + if (val_end >= val_begin) + opt_value = trial_string.substr(val_begin, val_end - val_begin); + i = val_end + 1; + auto field = field_map.find(key); + if (field != field_map.end()) { + if (!field->second->Parse(std::move(opt_value))) { + RTC_LOG(LS_WARNING) << "Failed to read field with key: '" << key + << "' in trial: \"" << trial_string << "\""; + } + } else if (!opt_value && keyless_field && !key.empty()) { + if (!keyless_field->Parse(key)) { + RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '" + << key << "' in trial: \"" << trial_string << "\""; + } + } else { + RTC_LOG(LS_INFO) << "No field with key: '" << key + << "' (found in trial: \"" << trial_string << "\")"; + std::string valid_keys; + for (const auto& f : field_map) { + valid_keys += f.first; + valid_keys += ", "; + } + RTC_LOG(LS_INFO) << "Valid keys are: " << valid_keys; + } + } + + for (FieldTrialParameterInterface* field : fields) { + field->ParseDone(); + } +} + +template <> +absl::optional ParseTypedParameter(std::string str) { + if (str == "true" || str == "1") { + return true; + } else if (str == "false" || str == "0") { + return false; + } + return absl::nullopt; +} + +template <> +absl::optional ParseTypedParameter(std::string str) { + double value; + char unit[2]{0, 0}; + if (sscanf(str.c_str(), "%lf%1s", &value, unit) >= 1) { + if (unit[0] == '%') + return value / 100; + return value; + } else { + return absl::nullopt; + } +} + +template <> +absl::optional ParseTypedParameter(std::string str) { + int64_t value; + if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) { + if (rtc::IsValueInRangeForNumericType(value)) { + return static_cast(value); + } + } + return absl::nullopt; +} + +template <> +absl::optional ParseTypedParameter(std::string str) { + int64_t value; + if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) { + if (rtc::IsValueInRangeForNumericType(value)) { + return static_cast(value); + } + } + return absl::nullopt; +} + +template <> +absl::optional ParseTypedParameter(std::string str) { + return std::move(str); +} + +template <> +absl::optional> ParseTypedParameter>( + std::string str) { + return ParseOptionalParameter(str); +} +template <> +absl::optional> ParseTypedParameter>( + std::string str) { + return ParseOptionalParameter(str); +} +template <> +absl::optional> +ParseTypedParameter>(std::string str) { + return ParseOptionalParameter(str); +} +template <> +absl::optional> +ParseTypedParameter>(std::string str) { + return ParseOptionalParameter(str); +} + +FieldTrialFlag::FieldTrialFlag(std::string key) : FieldTrialFlag(key, false) {} + +FieldTrialFlag::FieldTrialFlag(std::string key, bool default_value) + : FieldTrialParameterInterface(key), value_(default_value) {} + +bool FieldTrialFlag::Get() const { + return value_; +} + +webrtc::FieldTrialFlag::operator bool() const { + return value_; +} + +bool FieldTrialFlag::Parse(absl::optional str_value) { + // Only set the flag if there is no argument provided. + if (str_value) { + absl::optional opt_value = ParseTypedParameter(*str_value); + if (!opt_value) + return false; + value_ = *opt_value; + } else { + value_ = true; + } + return true; +} + +AbstractFieldTrialEnum::AbstractFieldTrialEnum( + std::string key, + int default_value, + std::map mapping) + : FieldTrialParameterInterface(key), + value_(default_value), + enum_mapping_(mapping) { + for (auto& key_val : enum_mapping_) + valid_values_.insert(key_val.second); +} +AbstractFieldTrialEnum::AbstractFieldTrialEnum(const AbstractFieldTrialEnum&) = + default; +AbstractFieldTrialEnum::~AbstractFieldTrialEnum() = default; + +bool AbstractFieldTrialEnum::Parse(absl::optional str_value) { + if (str_value) { + auto it = enum_mapping_.find(*str_value); + if (it != enum_mapping_.end()) { + value_ = it->second; + return true; + } + absl::optional value = ParseTypedParameter(*str_value); + if (value.has_value() && + (valid_values_.find(*value) != valid_values_.end())) { + value_ = *value; + return true; + } + } + return false; +} + +template class FieldTrialParameter; +template class FieldTrialParameter; +template class FieldTrialParameter; +template class FieldTrialParameter; +template class FieldTrialParameter; + +template class FieldTrialConstrained; +template class FieldTrialConstrained; +template class FieldTrialConstrained; + +template class FieldTrialOptional; +template class FieldTrialOptional; +template class FieldTrialOptional; +template class FieldTrialOptional; +template class FieldTrialOptional; + +} // namespace webrtc diff --git a/webrtc/rtc_base/experiments/field_trial_parser.h b/webrtc/rtc_base/experiments/field_trial_parser.h new file mode 100644 index 0000000..42535ed --- /dev/null +++ b/webrtc/rtc_base/experiments/field_trial_parser.h @@ -0,0 +1,288 @@ +/* + * Copyright 2018 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. + */ +#ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_ +#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_ + +#include + +#include +#include +#include +#include +#include + +#include "absl/types/optional.h" + +// Field trial parser functionality. Provides funcitonality to parse field trial +// argument strings in key:value format. Each parameter is described using +// key:value, parameters are separated with a ,. Values can't include the comma +// character, since there's no quote facility. For most types, white space is +// ignored. Parameters are declared with a given type for which an +// implementation of ParseTypedParameter should be provided. The +// ParseTypedParameter implementation is given whatever is between the : and the +// ,. If the key is provided without : a FieldTrialOptional will use nullopt. + +// Example string: "my_optional,my_int:3,my_string:hello" + +// For further description of usage and behavior, see the examples in the unit +// tests. + +namespace webrtc { +class FieldTrialParameterInterface { + public: + virtual ~FieldTrialParameterInterface(); + std::string key() const { return key_; } + + protected: + // Protected to allow implementations to provide assignment and copy. + FieldTrialParameterInterface(const FieldTrialParameterInterface&) = default; + FieldTrialParameterInterface& operator=(const FieldTrialParameterInterface&) = + default; + explicit FieldTrialParameterInterface(std::string key); + friend void ParseFieldTrial( + std::initializer_list fields, + std::string raw_string); + void MarkAsUsed() { used_ = true; } + virtual bool Parse(absl::optional str_value) = 0; + + virtual void ParseDone() {} + + std::vector sub_parameters_; + + private: + std::string key_; + bool used_ = false; +}; + +// ParseFieldTrial function parses the given string and fills the given fields +// with extracted values if available. +void ParseFieldTrial( + std::initializer_list fields, + std::string raw_string); + +// Specialize this in code file for custom types. Should return absl::nullopt if +// the given string cannot be properly parsed. +template +absl::optional ParseTypedParameter(std::string); + +// This class uses the ParseTypedParameter function to implement a parameter +// implementation with an enforced default value. +template +class FieldTrialParameter : public FieldTrialParameterInterface { + public: + FieldTrialParameter(std::string key, T default_value) + : FieldTrialParameterInterface(key), value_(default_value) {} + T Get() const { return value_; } + operator T() const { return Get(); } + const T* operator->() const { return &value_; } + + void SetForTest(T value) { value_ = value; } + + protected: + bool Parse(absl::optional str_value) override { + if (str_value) { + absl::optional value = ParseTypedParameter(*str_value); + if (value.has_value()) { + value_ = value.value(); + return true; + } + } + return false; + } + + private: + T value_; +}; + +// This class uses the ParseTypedParameter function to implement a parameter +// implementation with an enforced default value and a range constraint. Values +// outside the configured range will be ignored. +template +class FieldTrialConstrained : public FieldTrialParameterInterface { + public: + FieldTrialConstrained(std::string key, + T default_value, + absl::optional lower_limit, + absl::optional upper_limit) + : FieldTrialParameterInterface(key), + value_(default_value), + lower_limit_(lower_limit), + upper_limit_(upper_limit) {} + T Get() const { return value_; } + operator T() const { return Get(); } + const T* operator->() const { return &value_; } + + protected: + bool Parse(absl::optional str_value) override { + if (str_value) { + absl::optional value = ParseTypedParameter(*str_value); + if (value && (!lower_limit_ || *value >= *lower_limit_) && + (!upper_limit_ || *value <= *upper_limit_)) { + value_ = *value; + return true; + } + } + return false; + } + + private: + T value_; + absl::optional lower_limit_; + absl::optional upper_limit_; +}; + +class AbstractFieldTrialEnum : public FieldTrialParameterInterface { + public: + AbstractFieldTrialEnum(std::string key, + int default_value, + std::map mapping); + ~AbstractFieldTrialEnum() override; + AbstractFieldTrialEnum(const AbstractFieldTrialEnum&); + + protected: + bool Parse(absl::optional str_value) override; + + protected: + int value_; + std::map enum_mapping_; + std::set valid_values_; +}; + +// The FieldTrialEnum class can be used to quickly define a parser for a +// specific enum. It handles values provided as integers and as strings if a +// mapping is provided. +template +class FieldTrialEnum : public AbstractFieldTrialEnum { + public: + FieldTrialEnum(std::string key, + T default_value, + std::map mapping) + : AbstractFieldTrialEnum(key, + static_cast(default_value), + ToIntMap(mapping)) {} + T Get() const { return static_cast(value_); } + operator T() const { return Get(); } + + private: + static std::map ToIntMap(std::map mapping) { + std::map res; + for (const auto& it : mapping) + res[it.first] = static_cast(it.second); + return res; + } +}; + +// This class uses the ParseTypedParameter function to implement an optional +// parameter implementation that can default to absl::nullopt. +template +class FieldTrialOptional : public FieldTrialParameterInterface { + public: + explicit FieldTrialOptional(std::string key) + : FieldTrialParameterInterface(key) {} + FieldTrialOptional(std::string key, absl::optional default_value) + : FieldTrialParameterInterface(key), value_(default_value) {} + absl::optional GetOptional() const { return value_; } + const T& Value() const { return value_.value(); } + const T& operator*() const { return value_.value(); } + const T* operator->() const { return &value_.value(); } + explicit operator bool() const { return value_.has_value(); } + + protected: + bool Parse(absl::optional str_value) override { + if (str_value) { + absl::optional value = ParseTypedParameter(*str_value); + if (!value.has_value()) + return false; + value_ = value.value(); + } else { + value_ = absl::nullopt; + } + return true; + } + + private: + absl::optional value_; +}; + +// Equivalent to a FieldTrialParameter in the case that both key and value +// are present. If key is missing, evaluates to false. If key is present, but no +// explicit value is provided, the flag evaluates to true. +class FieldTrialFlag : public FieldTrialParameterInterface { + public: + explicit FieldTrialFlag(std::string key); + FieldTrialFlag(std::string key, bool default_value); + bool Get() const; + operator bool() const; + + protected: + bool Parse(absl::optional str_value) override; + + private: + bool value_; +}; + +template +absl::optional> ParseOptionalParameter(std::string str) { + if (str.empty()) + return absl::optional(); + auto parsed = ParseTypedParameter(str); + if (parsed.has_value()) + return parsed; + return absl::nullopt; +} + +template <> +absl::optional ParseTypedParameter(std::string str); +template <> +absl::optional ParseTypedParameter(std::string str); +template <> +absl::optional ParseTypedParameter(std::string str); +template <> +absl::optional ParseTypedParameter(std::string str); +template <> +absl::optional ParseTypedParameter(std::string str); + +template <> +absl::optional> ParseTypedParameter>( + std::string str); +template <> +absl::optional> ParseTypedParameter>( + std::string str); +template <> +absl::optional> +ParseTypedParameter>(std::string str); +template <> +absl::optional> +ParseTypedParameter>(std::string str); + +// Accepts true, false, else parsed with sscanf %i, true if != 0. +extern template class FieldTrialParameter; +// Interpreted using sscanf %lf. +extern template class FieldTrialParameter; +// Interpreted using sscanf %i. +extern template class FieldTrialParameter; +// Interpreted using sscanf %u. +extern template class FieldTrialParameter; +// Using the given value as is. +extern template class FieldTrialParameter; + +extern template class FieldTrialConstrained; +extern template class FieldTrialConstrained; +extern template class FieldTrialConstrained; + +extern template class FieldTrialOptional; +extern template class FieldTrialOptional; +extern template class FieldTrialOptional; +extern template class FieldTrialOptional; +extern template class FieldTrialOptional; + +} // namespace webrtc + +#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_ diff --git a/webrtc/rtc_base/gtest_prod_util.h b/webrtc/rtc_base/gtest_prod_util.h new file mode 100644 index 0000000..0661cd7 --- /dev/null +++ b/webrtc/rtc_base/gtest_prod_util.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef RTC_BASE_GTEST_PROD_UTIL_H_ +#define RTC_BASE_GTEST_PROD_UTIL_H_ + +// Define our own version of FRIEND_TEST here rather than including +// gtest_prod.h to avoid depending on any part of GTest in production code. +#define FRIEND_TEST_WEBRTC(test_case_name, test_name) \ + friend class test_case_name##_##test_name##_Test + +// This file is a plain copy of Chromium's base/gtest_prod_util.h. +// +// This is a wrapper for gtest's FRIEND_TEST macro that friends +// test with all possible prefixes. This is very helpful when changing the test +// prefix, because the friend declarations don't need to be updated. +// +// Example usage: +// +// class MyClass { +// private: +// void MyMethod(); +// FRIEND_TEST_ALL_PREFIXES(MyClassTest, MyMethod); +// }; +#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \ + FRIEND_TEST_WEBRTC(test_case_name, test_name); \ + FRIEND_TEST_WEBRTC(test_case_name, DISABLED_##test_name); \ + FRIEND_TEST_WEBRTC(test_case_name, FLAKY_##test_name); \ + FRIEND_TEST_WEBRTC(test_case_name, FAILS_##test_name) + +#endif // RTC_BASE_GTEST_PROD_UTIL_H_ diff --git a/webrtc/rtc_base/ignore_wundef.h b/webrtc/rtc_base/ignore_wundef.h new file mode 100644 index 0000000..1564096 --- /dev/null +++ b/webrtc/rtc_base/ignore_wundef.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_IGNORE_WUNDEF_H_ +#define RTC_BASE_IGNORE_WUNDEF_H_ + +// If a header file uses #if on possibly undefined macros (and it's for some +// reason not possible to just fix the header file), include it like this: +// +// RTC_PUSH_IGNORING_WUNDEF() +// #include "misbehaving_header.h" +// RTC_POP_IGNORING_WUNDEF() +// +// This will cause the compiler to not emit -Wundef warnings for that file. + +#ifdef __clang__ +#define RTC_PUSH_IGNORING_WUNDEF() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wundef\"") +#define RTC_POP_IGNORING_WUNDEF() _Pragma("clang diagnostic pop") +#else +#define RTC_PUSH_IGNORING_WUNDEF() +#define RTC_POP_IGNORING_WUNDEF() +#endif // __clang__ + +#endif // RTC_BASE_IGNORE_WUNDEF_H_ diff --git a/webrtc/rtc_base/logging.cc b/webrtc/rtc_base/logging.cc new file mode 100644 index 0000000..13a5f02 --- /dev/null +++ b/webrtc/rtc_base/logging.cc @@ -0,0 +1,562 @@ +/* + * Copyright 2004 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 "rtc_base/logging.h" + +#include + +#if RTC_LOG_ENABLED() + +#if defined(WEBRTC_WIN) +#include +#if _MSC_VER < 1900 +#define snprintf _snprintf +#endif +#undef ERROR // wingdi.h +#endif + +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include +#elif defined(WEBRTC_ANDROID) +#include + +// Android has a 1024 limit on log inputs. We use 60 chars as an +// approx for the header/tag portion. +// See android/system/core/liblog/logd_write.c +static const int kMaxLogLineSize = 1024 - 60; +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) || WEBRTC_ANDROID + +#include +#include +#include + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "rtc_base/checks.h" +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/string_encode.h" +#include "rtc_base/string_utils.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" +#include "rtc_base/time_utils.h" + +namespace rtc { +namespace { +// By default, release builds don't log, debug builds at info level +#if !defined(NDEBUG) +static LoggingSeverity g_min_sev = LS_INFO; +static LoggingSeverity g_dbg_sev = LS_INFO; +#else +static LoggingSeverity g_min_sev = LS_NONE; +static LoggingSeverity g_dbg_sev = LS_NONE; +#endif + +// Return the filename portion of the string (that following the last slash). +const char* FilenameFromPath(const char* file) { + const char* end1 = ::strrchr(file, '/'); + const char* end2 = ::strrchr(file, '\\'); + if (!end1 && !end2) + return file; + else + return (end1 > end2) ? end1 + 1 : end2 + 1; +} + +// Global lock for log subsystem, only needed to serialize access to streams_. +// TODO(bugs.webrtc.org/11665): this is not currently constant initialized and +// trivially destructible. +webrtc::Mutex g_log_mutex_; +} // namespace + +///////////////////////////////////////////////////////////////////////////// +// LogMessage +///////////////////////////////////////////////////////////////////////////// + +bool LogMessage::log_to_stderr_ = true; + +// The list of logging streams currently configured. +// Note: we explicitly do not clean this up, because of the uncertain ordering +// of destructors at program exit. Let the person who sets the stream trigger +// cleanup by setting to null, or let it leak (safe at program exit). +ABSL_CONST_INIT LogSink* LogMessage::streams_ RTC_GUARDED_BY(g_log_mutex_) = + nullptr; +ABSL_CONST_INIT std::atomic LogMessage::streams_empty_ = {true}; + +// Boolean options default to false (0) +bool LogMessage::thread_, LogMessage::timestamp_; + +LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev) + : LogMessage(file, line, sev, ERRCTX_NONE, 0) {} + +LogMessage::LogMessage(const char* file, + int line, + LoggingSeverity sev, + LogErrorContext err_ctx, + int err) + : severity_(sev) { + if (timestamp_) { + // Use SystemTimeMillis so that even if tests use fake clocks, the timestamp + // in log messages represents the real system time. + int64_t time = TimeDiff(SystemTimeMillis(), LogStartTime()); + // Also ensure WallClockStartTime is initialized, so that it matches + // LogStartTime. + WallClockStartTime(); + // TODO(kwiberg): Switch to absl::StrFormat, if binary size is ok. + char timestamp[50]; // Maximum string length of an int64_t is 20. + int len = + snprintf(timestamp, sizeof(timestamp), "[%03" PRId64 ":%03" PRId64 "]", + time / 1000, time % 1000); + RTC_DCHECK_LT(len, sizeof(timestamp)); + print_stream_ << timestamp; + } + + if (thread_) { + PlatformThreadId id = CurrentThreadId(); + print_stream_ << "[" << id << "] "; + } + + if (file != nullptr) { +#if defined(WEBRTC_ANDROID) + tag_ = FilenameFromPath(file); + print_stream_ << "(line " << line << "): "; +#else + print_stream_ << "(" << FilenameFromPath(file) << ":" << line << "): "; +#endif + } + + if (err_ctx != ERRCTX_NONE) { + char tmp_buf[1024]; + SimpleStringBuilder tmp(tmp_buf); + tmp.AppendFormat("[0x%08X]", err); + switch (err_ctx) { + case ERRCTX_ERRNO: + tmp << " " << strerror(err); + break; +#ifdef WEBRTC_WIN + case ERRCTX_HRESULT: { + char msgbuf[256]; + DWORD flags = + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + if (DWORD len = FormatMessageA( + flags, nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), nullptr)) { + while ((len > 0) && + isspace(static_cast(msgbuf[len - 1]))) { + msgbuf[--len] = 0; + } + tmp << " " << msgbuf; + } + break; + } +#endif // WEBRTC_WIN + default: + break; + } + extra_ = tmp.str(); + } +} + +#if defined(WEBRTC_ANDROID) +LogMessage::LogMessage(const char* file, + int line, + LoggingSeverity sev, + const char* tag) + : LogMessage(file, line, sev, ERRCTX_NONE, 0 /* err */) { + tag_ = tag; + print_stream_ << tag << ": "; +} +#endif + +// DEPRECATED. Currently only used by downstream projects that use +// implementation details of logging.h. Work is ongoing to remove those +// dependencies. +LogMessage::LogMessage(const char* file, + int line, + LoggingSeverity sev, + const std::string& tag) + : LogMessage(file, line, sev) { + print_stream_ << tag << ": "; +} + +LogMessage::~LogMessage() { + FinishPrintStream(); + + const std::string str = print_stream_.Release(); + + if (severity_ >= g_dbg_sev) { +#if defined(WEBRTC_ANDROID) + OutputToDebug(str, severity_, tag_); +#else + OutputToDebug(str, severity_); +#endif + } + + webrtc::MutexLock lock(&g_log_mutex_); + for (LogSink* entry = streams_; entry != nullptr; entry = entry->next_) { + if (severity_ >= entry->min_severity_) { +#if defined(WEBRTC_ANDROID) + entry->OnLogMessage(str, severity_, tag_); +#else + entry->OnLogMessage(str, severity_); +#endif + } + } +} + +void LogMessage::AddTag(const char* tag) { +#ifdef WEBRTC_ANDROID + tag_ = tag; +#endif +} + +rtc::StringBuilder& LogMessage::stream() { + return print_stream_; +} + +int LogMessage::GetMinLogSeverity() { + return g_min_sev; +} + +LoggingSeverity LogMessage::GetLogToDebug() { + return g_dbg_sev; +} +int64_t LogMessage::LogStartTime() { + static const int64_t g_start = SystemTimeMillis(); + return g_start; +} + +uint32_t LogMessage::WallClockStartTime() { + static const uint32_t g_start_wallclock = time(nullptr); + return g_start_wallclock; +} + +void LogMessage::LogThreads(bool on) { + thread_ = on; +} + +void LogMessage::LogTimestamps(bool on) { + timestamp_ = on; +} + +void LogMessage::LogToDebug(LoggingSeverity min_sev) { + g_dbg_sev = min_sev; + webrtc::MutexLock lock(&g_log_mutex_); + UpdateMinLogSeverity(); +} + +void LogMessage::SetLogToStderr(bool log_to_stderr) { + log_to_stderr_ = log_to_stderr; +} + +int LogMessage::GetLogToStream(LogSink* stream) { + webrtc::MutexLock lock(&g_log_mutex_); + LoggingSeverity sev = LS_NONE; + for (LogSink* entry = streams_; entry != nullptr; entry = entry->next_) { + if (stream == nullptr || stream == entry) { + sev = std::min(sev, entry->min_severity_); + } + } + return sev; +} + +void LogMessage::AddLogToStream(LogSink* stream, LoggingSeverity min_sev) { + webrtc::MutexLock lock(&g_log_mutex_); + stream->min_severity_ = min_sev; + stream->next_ = streams_; + streams_ = stream; + streams_empty_.store(false, std::memory_order_relaxed); + UpdateMinLogSeverity(); +} + +void LogMessage::RemoveLogToStream(LogSink* stream) { + webrtc::MutexLock lock(&g_log_mutex_); + for (LogSink** entry = &streams_; *entry != nullptr; + entry = &(*entry)->next_) { + if (*entry == stream) { + *entry = (*entry)->next_; + break; + } + } + streams_empty_.store(streams_ == nullptr, std::memory_order_relaxed); + UpdateMinLogSeverity(); +} + +void LogMessage::ConfigureLogging(const char* params) { + LoggingSeverity current_level = LS_VERBOSE; + LoggingSeverity debug_level = GetLogToDebug(); + + std::vector tokens; + tokenize(params, ' ', &tokens); + + for (const std::string& token : tokens) { + if (token.empty()) + continue; + + // Logging features + if (token == "tstamp") { + LogTimestamps(); + } else if (token == "thread") { + LogThreads(); + + // Logging levels + } else if (token == "verbose") { + current_level = LS_VERBOSE; + } else if (token == "info") { + current_level = LS_INFO; + } else if (token == "warning") { + current_level = LS_WARNING; + } else if (token == "error") { + current_level = LS_ERROR; + } else if (token == "none") { + current_level = LS_NONE; + + // Logging targets + } else if (token == "debug") { + debug_level = current_level; + } + } + +#if defined(WEBRTC_WIN) && !defined(WINUWP) + if ((LS_NONE != debug_level) && !::IsDebuggerPresent()) { + // First, attempt to attach to our parent's console... so if you invoke + // from the command line, we'll see the output there. Otherwise, create + // our own console window. + // Note: These methods fail if a console already exists, which is fine. + if (!AttachConsole(ATTACH_PARENT_PROCESS)) + ::AllocConsole(); + } +#endif // defined(WEBRTC_WIN) && !defined(WINUWP) + + LogToDebug(debug_level); +} + +void LogMessage::UpdateMinLogSeverity() + RTC_EXCLUSIVE_LOCKS_REQUIRED(g_log_mutex_) { + LoggingSeverity min_sev = g_dbg_sev; + for (LogSink* entry = streams_; entry != nullptr; entry = entry->next_) { + min_sev = std::min(min_sev, entry->min_severity_); + } + g_min_sev = min_sev; +} + +#if defined(WEBRTC_ANDROID) +void LogMessage::OutputToDebug(const std::string& str, + LoggingSeverity severity, + const char* tag) { +#else +void LogMessage::OutputToDebug(const std::string& str, + LoggingSeverity severity) { +#endif + bool log_to_stderr = log_to_stderr_; +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG) + // On the Mac, all stderr output goes to the Console log and causes clutter. + // So in opt builds, don't log to stderr unless the user specifically sets + // a preference to do so. + CFStringRef key = CFStringCreateWithCString( + kCFAllocatorDefault, "logToStdErr", kCFStringEncodingUTF8); + CFStringRef domain = CFBundleGetIdentifier(CFBundleGetMainBundle()); + if (key != nullptr && domain != nullptr) { + Boolean exists_and_is_valid; + Boolean should_log = + CFPreferencesGetAppBooleanValue(key, domain, &exists_and_is_valid); + // If the key doesn't exist or is invalid or is false, we will not log to + // stderr. + log_to_stderr = exists_and_is_valid && should_log; + } + if (key != nullptr) { + CFRelease(key); + } +#endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG) + +#if defined(WEBRTC_WIN) + // Always log to the debugger. + // Perhaps stderr should be controlled by a preference, as on Mac? + OutputDebugStringA(str.c_str()); + if (log_to_stderr) { + // This handles dynamically allocated consoles, too. + if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) { + log_to_stderr = false; + DWORD written = 0; + ::WriteFile(error_handle, str.data(), static_cast(str.size()), + &written, 0); + } + } +#endif // WEBRTC_WIN + +#if defined(WEBRTC_ANDROID) + // Android's logging facility uses severity to log messages but we + // need to map libjingle's severity levels to Android ones first. + // Also write to stderr which maybe available to executable started + // from the shell. + int prio; + switch (severity) { + case LS_VERBOSE: + prio = ANDROID_LOG_VERBOSE; + break; + case LS_INFO: + prio = ANDROID_LOG_INFO; + break; + case LS_WARNING: + prio = ANDROID_LOG_WARN; + break; + case LS_ERROR: + prio = ANDROID_LOG_ERROR; + break; + default: + prio = ANDROID_LOG_UNKNOWN; + } + + int size = str.size(); + int line = 0; + int idx = 0; + const int max_lines = size / kMaxLogLineSize + 1; + if (max_lines == 1) { + __android_log_print(prio, tag, "%.*s", size, str.c_str()); + } else { + while (size > 0) { + const int len = std::min(size, kMaxLogLineSize); + // Use the size of the string in the format (str may have \0 in the + // middle). + __android_log_print(prio, tag, "[%d/%d] %.*s", line + 1, max_lines, len, + str.c_str() + idx); + idx += len; + size -= len; + ++line; + } + } +#endif // WEBRTC_ANDROID + if (log_to_stderr) { + fprintf(stderr, "%s", str.c_str()); + fflush(stderr); + } +} + +// static +bool LogMessage::IsNoop(LoggingSeverity severity) { + if (severity >= g_dbg_sev || severity >= g_min_sev) + return false; + return streams_empty_.load(std::memory_order_relaxed); +} + +void LogMessage::FinishPrintStream() { + if (!extra_.empty()) + print_stream_ << " : " << extra_; + print_stream_ << "\n"; +} + +namespace webrtc_logging_impl { + +void Log(const LogArgType* fmt, ...) { + va_list args; + va_start(args, fmt); + + LogMetadataErr meta; + const char* tag = nullptr; + switch (*fmt) { + case LogArgType::kLogMetadata: { + meta = {va_arg(args, LogMetadata), ERRCTX_NONE, 0}; + break; + } + case LogArgType::kLogMetadataErr: { + meta = va_arg(args, LogMetadataErr); + break; + } +#ifdef WEBRTC_ANDROID + case LogArgType::kLogMetadataTag: { + const LogMetadataTag tag_meta = va_arg(args, LogMetadataTag); + meta = {{nullptr, 0, tag_meta.severity}, ERRCTX_NONE, 0}; + tag = tag_meta.tag; + break; + } +#endif + default: { + RTC_NOTREACHED(); + va_end(args); + return; + } + } + + LogMessage log_message(meta.meta.File(), meta.meta.Line(), + meta.meta.Severity(), meta.err_ctx, meta.err); + if (tag) { + log_message.AddTag(tag); + } + + for (++fmt; *fmt != LogArgType::kEnd; ++fmt) { + switch (*fmt) { + case LogArgType::kInt: + log_message.stream() << va_arg(args, int); + break; + case LogArgType::kLong: + log_message.stream() << va_arg(args, long); + break; + case LogArgType::kLongLong: + log_message.stream() << va_arg(args, long long); + break; + case LogArgType::kUInt: + log_message.stream() << va_arg(args, unsigned); + break; + case LogArgType::kULong: + log_message.stream() << va_arg(args, unsigned long); + break; + case LogArgType::kULongLong: + log_message.stream() << va_arg(args, unsigned long long); + break; + case LogArgType::kDouble: + log_message.stream() << va_arg(args, double); + break; + case LogArgType::kLongDouble: + log_message.stream() << va_arg(args, long double); + break; + case LogArgType::kCharP: { + const char* s = va_arg(args, const char*); + log_message.stream() << (s ? s : "(null)"); + break; + } + case LogArgType::kStdString: + log_message.stream() << *va_arg(args, const std::string*); + break; + case LogArgType::kStringView: + log_message.stream() << *va_arg(args, const absl::string_view*); + break; + case LogArgType::kVoidP: + log_message.stream() << rtc::ToHex( + reinterpret_cast(va_arg(args, const void*))); + break; + default: + RTC_NOTREACHED(); + va_end(args); + return; + } + } + + va_end(args); +} + +} // namespace webrtc_logging_impl +} // namespace rtc +#endif + +namespace rtc { +// Inefficient default implementation, override is recommended. +void LogSink::OnLogMessage(const std::string& msg, + LoggingSeverity severity, + const char* tag) { + OnLogMessage(tag + (": " + msg), severity); +} + +void LogSink::OnLogMessage(const std::string& msg, + LoggingSeverity /* severity */) { + OnLogMessage(msg); +} +} // namespace rtc diff --git a/webrtc/rtc_base/logging.h b/webrtc/rtc_base/logging.h new file mode 100644 index 0000000..d2607c2 --- /dev/null +++ b/webrtc/rtc_base/logging.h @@ -0,0 +1,712 @@ +/* + * Copyright 2004 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. + */ + +// RTC_LOG(...) an ostream target that can be used to send formatted +// output to a variety of logging targets, such as debugger console, stderr, +// or any LogSink. +// The severity level passed as the first argument to the logging +// functions is used as a filter, to limit the verbosity of the logging. +// Static members of LogMessage documented below are used to control the +// verbosity and target of the output. +// There are several variations on the RTC_LOG macro which facilitate logging +// of common error conditions, detailed below. + +// RTC_LOG(sev) logs the given stream at severity "sev", which must be a +// compile-time constant of the LoggingSeverity type, without the namespace +// prefix. +// RTC_LOG_V(sev) Like RTC_LOG(), but sev is a run-time variable of the +// LoggingSeverity type (basically, it just doesn't prepend the namespace). +// RTC_LOG_F(sev) Like RTC_LOG(), but includes the name of the current function. +// RTC_LOG_T(sev) Like RTC_LOG(), but includes the this pointer. +// RTC_LOG_T_F(sev) Like RTC_LOG_F(), but includes the this pointer. +// RTC_LOG_GLE(sev [, mod]) attempt to add a string description of the +// HRESULT returned by GetLastError. +// RTC_LOG_ERRNO(sev) attempts to add a string description of an errno-derived +// error. errno and associated facilities exist on both Windows and POSIX, +// but on Windows they only apply to the C/C++ runtime. +// RTC_LOG_ERR(sev) is an alias for the platform's normal error system, i.e. +// _GLE on Windows and _ERRNO on POSIX. +// (The above three also all have _EX versions that let you specify the error +// code, rather than using the last one.) +// RTC_LOG_E(sev, ctx, err, ...) logs a detailed error interpreted using the +// specified context. +// RTC_LOG_CHECK_LEVEL(sev) (and RTC_LOG_CHECK_LEVEL_V(sev)) can be used as a +// test before performing expensive or sensitive operations whose sole +// purpose is to output logging data at the desired level. + +#ifndef RTC_BASE_LOGGING_H_ +#define RTC_BASE_LOGGING_H_ + +#include + +#include +#include // no-presubmit-check TODO(webrtc:8982) +#include +#include + +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" +#include "rtc_base/constructor_magic.h" +#include "rtc_base/deprecation.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/system/inline.h" + +#if !defined(NDEBUG) || defined(DLOG_ALWAYS_ON) +#define RTC_DLOG_IS_ON 1 +#else +#define RTC_DLOG_IS_ON 0 +#endif + +#if defined(RTC_DISABLE_LOGGING) +#define RTC_LOG_ENABLED() 0 +#else +#define RTC_LOG_ENABLED() 1 +#endif + +namespace rtc { + +////////////////////////////////////////////////////////////////////// + +// Note that the non-standard LoggingSeverity aliases exist because they are +// still in broad use. The meanings of the levels are: +// LS_VERBOSE: This level is for data which we do not want to appear in the +// normal debug log, but should appear in diagnostic logs. +// LS_INFO: Chatty level used in debugging for all sorts of things, the default +// in debug builds. +// LS_WARNING: Something that may warrant investigation. +// LS_ERROR: Something that should not have occurred. +// LS_NONE: Don't log. +enum LoggingSeverity { + LS_VERBOSE, + LS_INFO, + LS_WARNING, + LS_ERROR, + LS_NONE, + INFO = LS_INFO, + WARNING = LS_WARNING, + LERROR = LS_ERROR +}; + +// LogErrorContext assists in interpreting the meaning of an error value. +enum LogErrorContext { + ERRCTX_NONE, + ERRCTX_ERRNO, // System-local errno + ERRCTX_HRESULT, // Windows HRESULT + + // Abbreviations for LOG_E macro + ERRCTX_EN = ERRCTX_ERRNO, // LOG_E(sev, EN, x) + ERRCTX_HR = ERRCTX_HRESULT, // LOG_E(sev, HR, x) +}; + +class LogMessage; +// Virtual sink interface that can receive log messages. +class LogSink { + public: + LogSink() {} + virtual ~LogSink() {} + virtual void OnLogMessage(const std::string& msg, + LoggingSeverity severity, + const char* tag); + virtual void OnLogMessage(const std::string& message, + LoggingSeverity severity); + virtual void OnLogMessage(const std::string& message) = 0; + + private: + friend class ::rtc::LogMessage; +#if RTC_LOG_ENABLED() + // Members for LogMessage class to keep linked list of the registered sinks. + LogSink* next_ = nullptr; + LoggingSeverity min_severity_; +#endif +}; + +namespace webrtc_logging_impl { + +class LogMetadata { + public: + LogMetadata(const char* file, int line, LoggingSeverity severity) + : file_(file), + line_and_sev_(static_cast(line) << 3 | severity) {} + LogMetadata() = default; + + const char* File() const { return file_; } + int Line() const { return line_and_sev_ >> 3; } + LoggingSeverity Severity() const { + return static_cast(line_and_sev_ & 0x7); + } + + private: + const char* file_; + + // Line number and severity, the former in the most significant 29 bits, the + // latter in the least significant 3 bits. (This is an optimization; since + // both numbers are usually compile-time constants, this way we can load them + // both with a single instruction.) + uint32_t line_and_sev_; +}; +static_assert(std::is_trivial::value, ""); + +struct LogMetadataErr { + LogMetadata meta; + LogErrorContext err_ctx; + int err; +}; + +#ifdef WEBRTC_ANDROID +struct LogMetadataTag { + LoggingSeverity severity; + const char* tag; +}; +#endif + +enum class LogArgType : int8_t { + kEnd = 0, + kInt, + kLong, + kLongLong, + kUInt, + kULong, + kULongLong, + kDouble, + kLongDouble, + kCharP, + kStdString, + kStringView, + kVoidP, + kLogMetadata, + kLogMetadataErr, +#ifdef WEBRTC_ANDROID + kLogMetadataTag, +#endif +}; + +// Wrapper for log arguments. Only ever make values of this type with the +// MakeVal() functions. +template +struct Val { + static constexpr LogArgType Type() { return N; } + T GetVal() const { return val; } + T val; +}; + +// Case for when we need to construct a temp string and then print that. +// (We can't use Val +// because we need somewhere to store the temp string.) +struct ToStringVal { + static constexpr LogArgType Type() { return LogArgType::kStdString; } + const std::string* GetVal() const { return &val; } + std::string val; +}; + +inline Val MakeVal(int x) { + return {x}; +} +inline Val MakeVal(long x) { + return {x}; +} +inline Val MakeVal(long long x) { + return {x}; +} +inline Val MakeVal(unsigned int x) { + return {x}; +} +inline Val MakeVal(unsigned long x) { + return {x}; +} +inline Val MakeVal( + unsigned long long x) { + return {x}; +} + +inline Val MakeVal(double x) { + return {x}; +} +inline Val MakeVal(long double x) { + return {x}; +} + +inline Val MakeVal(const char* x) { + return {x}; +} +inline Val MakeVal( + const std::string& x) { + return {&x}; +} +inline Val MakeVal( + const absl::string_view& x) { + return {&x}; +} + +inline Val MakeVal(const void* x) { + return {x}; +} + +inline Val MakeVal( + const LogMetadata& x) { + return {x}; +} +inline Val MakeVal( + const LogMetadataErr& x) { + return {x}; +} + +// The enum class types are not implicitly convertible to arithmetic types. +template ::value && + !std::is_arithmetic::value>* = nullptr> +inline decltype(MakeVal(std::declval>())) MakeVal( + T x) { + return {static_cast>(x)}; +} + +#ifdef WEBRTC_ANDROID +inline Val MakeVal( + const LogMetadataTag& x) { + return {x}; +} +#endif + +template +struct has_to_log_string : std::false_type {}; +template +struct has_to_log_string()))> + : std::true_type {}; + +// Handle arbitrary types other than the above by falling back to stringstream. +// TODO(bugs.webrtc.org/9278): Get rid of this overload when callers don't need +// it anymore. No in-tree caller does, but some external callers still do. +template < + typename T, + typename T1 = absl::decay_t, + absl::enable_if_t::value && + !std::is_same::value && + !std::is_same::value && + !has_to_log_string::value && +#ifdef WEBRTC_ANDROID + !std::is_same::value && +#endif + !std::is_same::value>* = nullptr> +ToStringVal MakeVal(const T& x) { + std::ostringstream os; // no-presubmit-check TODO(webrtc:8982) + os << x; + return {os.str()}; +} + +template ::value>* = nullptr> +ToStringVal MakeVal(const T& x) { + return {ToLogString(x)}; +} + +#if RTC_LOG_ENABLED() +void Log(const LogArgType* fmt, ...); +#else +inline void Log(const LogArgType* fmt, ...) { + // Do nothing, shouldn't be invoked +} +#endif + +// Ephemeral type that represents the result of the logging << operator. +template +class LogStreamer; + +// Base case: Before the first << argument. +template <> +class LogStreamer<> final { + public: + template ())), + absl::enable_if_t::value || + std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(U arg) const { + return LogStreamer(MakeVal(arg), this); + } + + template ())), + absl::enable_if_t::value && + !std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(const U& arg) const { + return LogStreamer(MakeVal(arg), this); + } + + template + RTC_FORCE_INLINE static void Call(const Us&... args) { + static constexpr LogArgType t[] = {Us::Type()..., LogArgType::kEnd}; + Log(t, args.GetVal()...); + } +}; + +// Inductive case: We've already seen at least one << argument. The most recent +// one had type `T`, and the earlier ones had types `Ts`. +template +class LogStreamer final { + public: + RTC_FORCE_INLINE LogStreamer(T arg, const LogStreamer* prior) + : arg_(arg), prior_(prior) {} + + template ())), + absl::enable_if_t::value || + std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(U arg) const { + return LogStreamer(MakeVal(arg), this); + } + + template ())), + absl::enable_if_t::value && + !std::is_enum::value>* = nullptr> + RTC_FORCE_INLINE LogStreamer operator<<(const U& arg) const { + return LogStreamer(MakeVal(arg), this); + } + + template + RTC_FORCE_INLINE void Call(const Us&... args) const { + prior_->Call(arg_, args...); + } + + private: + // The most recent argument. + T arg_; + + // Earlier arguments. + const LogStreamer* prior_; +}; + +class LogCall final { + public: + // This can be any binary operator with precedence lower than <<. + // We return bool here to be able properly remove logging if + // RTC_DISABLE_LOGGING is defined. + template + RTC_FORCE_INLINE bool operator&(const LogStreamer& streamer) { + streamer.Call(); + return true; + } +}; + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". +class LogMessageVoidify { + public: + LogMessageVoidify() = default; + // This has to be an operator with a precedence lower than << but + // higher than ?: + template + void operator&(LogStreamer&& streamer) {} +}; + +} // namespace webrtc_logging_impl + +// Direct use of this class is deprecated; please use the logging macros +// instead. +// TODO(bugs.webrtc.org/9278): Move this class to an unnamed namespace in the +// .cc file. +class LogMessage { + public: + // Same as the above, but using a compile-time constant for the logging + // severity. This saves space at the call site, since passing an empty struct + // is generally the same as not passing an argument at all. + template + RTC_NO_INLINE LogMessage(const char* file, + int line, + std::integral_constant) + : LogMessage(file, line, S) {} + +#if RTC_LOG_ENABLED() + LogMessage(const char* file, int line, LoggingSeverity sev); + LogMessage(const char* file, + int line, + LoggingSeverity sev, + LogErrorContext err_ctx, + int err); +#if defined(WEBRTC_ANDROID) + LogMessage(const char* file, int line, LoggingSeverity sev, const char* tag); +#endif + // DEPRECATED - DO NOT USE - PLEASE USE THE MACROS INSTEAD OF THE CLASS. + // Android code should use the 'const char*' version since tags are static + // and we want to avoid allocating a std::string copy per log line. + RTC_DEPRECATED + LogMessage(const char* file, + int line, + LoggingSeverity sev, + const std::string& tag); + ~LogMessage(); + + void AddTag(const char* tag); + rtc::StringBuilder& stream(); + // Returns the time at which this function was called for the first time. + // The time will be used as the logging start time. + // If this is not called externally, the LogMessage ctor also calls it, in + // which case the logging start time will be the time of the first LogMessage + // instance is created. + static int64_t LogStartTime(); + // Returns the wall clock equivalent of |LogStartTime|, in seconds from the + // epoch. + static uint32_t WallClockStartTime(); + // LogThreads: Display the thread identifier of the current thread + static void LogThreads(bool on = true); + // LogTimestamps: Display the elapsed time of the program + static void LogTimestamps(bool on = true); + // These are the available logging channels + // Debug: Debug console on Windows, otherwise stderr + static void LogToDebug(LoggingSeverity min_sev); + static LoggingSeverity GetLogToDebug(); + // Sets whether logs will be directed to stderr in debug mode. + static void SetLogToStderr(bool log_to_stderr); + // Stream: Any non-blocking stream interface. + // Installs the |stream| to collect logs with severtiy |min_sev| or higher. + // |stream| must live until deinstalled by RemoveLogToStream. + // If |stream| is the first stream added to the system, we might miss some + // early concurrent log statement happening from another thread happening near + // this instant. + static void AddLogToStream(LogSink* stream, LoggingSeverity min_sev); + // Removes the specified stream, without destroying it. When the method + // has completed, it's guaranteed that |stream| will receive no more logging + // calls. + static void RemoveLogToStream(LogSink* stream); + // Returns the severity for the specified stream, of if none is specified, + // the minimum stream severity. + static int GetLogToStream(LogSink* stream = nullptr); + // Testing against MinLogSeverity allows code to avoid potentially expensive + // logging operations by pre-checking the logging level. + static int GetMinLogSeverity(); + // Parses the provided parameter stream to configure the options above. + // Useful for configuring logging from the command line. + static void ConfigureLogging(const char* params); + // Checks the current global debug severity and if the |streams_| collection + // is empty. If |severity| is smaller than the global severity and if the + // |streams_| collection is empty, the LogMessage will be considered a noop + // LogMessage. + static bool IsNoop(LoggingSeverity severity); + // Version of IsNoop that uses fewer instructions at the call site, since the + // caller doesn't have to pass an argument. + template + RTC_NO_INLINE static bool IsNoop() { + return IsNoop(S); + } +#else + // Next methods do nothing; no one will call these functions. + LogMessage(const char* file, int line, LoggingSeverity sev) {} + LogMessage(const char* file, + int line, + LoggingSeverity sev, + LogErrorContext err_ctx, + int err) {} +#if defined(WEBRTC_ANDROID) + LogMessage(const char* file, int line, LoggingSeverity sev, const char* tag) { + } +#endif + // DEPRECATED - DO NOT USE - PLEASE USE THE MACROS INSTEAD OF THE CLASS. + // Android code should use the 'const char*' version since tags are static + // and we want to avoid allocating a std::string copy per log line. + RTC_DEPRECATED + LogMessage(const char* file, + int line, + LoggingSeverity sev, + const std::string& tag) {} + ~LogMessage() = default; + + inline void AddTag(const char* tag) {} + inline rtc::StringBuilder& stream() { return print_stream_; } + inline static int64_t LogStartTime() { return 0; } + inline static uint32_t WallClockStartTime() { return 0; } + inline static void LogThreads(bool on = true) {} + inline static void LogTimestamps(bool on = true) {} + inline static void LogToDebug(LoggingSeverity min_sev) {} + inline static LoggingSeverity GetLogToDebug() { + return LoggingSeverity::LS_INFO; + } + inline static void SetLogToStderr(bool log_to_stderr) {} + inline static void AddLogToStream(LogSink* stream, LoggingSeverity min_sev) {} + inline static void RemoveLogToStream(LogSink* stream) {} + inline static int GetLogToStream(LogSink* stream = nullptr) { return 0; } + inline static int GetMinLogSeverity() { return 0; } + inline static void ConfigureLogging(const char* params) {} + static constexpr bool IsNoop(LoggingSeverity severity) { return true; } + template + static constexpr bool IsNoop() { + return IsNoop(S); + } +#endif // RTC_LOG_ENABLED() + + private: + friend class LogMessageForTesting; + +#if RTC_LOG_ENABLED() + // Updates min_sev_ appropriately when debug sinks change. + static void UpdateMinLogSeverity(); + +// These write out the actual log messages. +#if defined(WEBRTC_ANDROID) + static void OutputToDebug(const std::string& msg, + LoggingSeverity severity, + const char* tag); +#else + static void OutputToDebug(const std::string& msg, LoggingSeverity severity); +#endif // defined(WEBRTC_ANDROID) + + // Called from the dtor (or from a test) to append optional extra error + // information to the log stream and a newline character. + void FinishPrintStream(); + + // The severity level of this message + LoggingSeverity severity_; + +#if defined(WEBRTC_ANDROID) + // The default Android debug output tag. + const char* tag_ = "libjingle"; +#endif + + // String data generated in the constructor, that should be appended to + // the message before output. + std::string extra_; + + // The output streams and their associated severities + static LogSink* streams_; + + // Holds true with high probability if |streams_| is empty, false with high + // probability otherwise. Operated on with std::memory_order_relaxed because + // it's ok to lose or log some additional statements near the instant streams + // are added/removed. + static std::atomic streams_empty_; + + // Flags for formatting options + static bool thread_, timestamp_; + + // Determines if logs will be directed to stderr in debug mode. + static bool log_to_stderr_; +#else // RTC_LOG_ENABLED() + // Next methods do nothing; no one will call these functions. + inline static void UpdateMinLogSeverity() {} +#if defined(WEBRTC_ANDROID) + inline static void OutputToDebug(const std::string& msg, + LoggingSeverity severity, + const char* tag) {} +#else + inline static void OutputToDebug(const std::string& msg, + LoggingSeverity severity) {} +#endif // defined(WEBRTC_ANDROID) + inline void FinishPrintStream() {} +#endif // RTC_LOG_ENABLED() + + // The stringbuilder that buffers the formatted message before output + rtc::StringBuilder print_stream_; + + RTC_DISALLOW_COPY_AND_ASSIGN(LogMessage); +}; + +////////////////////////////////////////////////////////////////////// +// Logging Helpers +////////////////////////////////////////////////////////////////////// + +#define RTC_LOG_FILE_LINE(sev, file, line) \ + ::rtc::webrtc_logging_impl::LogCall() & \ + ::rtc::webrtc_logging_impl::LogStreamer<>() \ + << ::rtc::webrtc_logging_impl::LogMetadata(file, line, sev) + +#define RTC_LOG(sev) \ + !rtc::LogMessage::IsNoop<::rtc::sev>() && \ + RTC_LOG_FILE_LINE(::rtc::sev, __FILE__, __LINE__) + +// The _V version is for when a variable is passed in. +#define RTC_LOG_V(sev) \ + !rtc::LogMessage::IsNoop(sev) && RTC_LOG_FILE_LINE(sev, __FILE__, __LINE__) + +// The _F version prefixes the message with the current function name. +#if (defined(__GNUC__) && !defined(NDEBUG)) || defined(WANT_PRETTY_LOG_F) +#define RTC_LOG_F(sev) RTC_LOG(sev) << __PRETTY_FUNCTION__ << ": " +#define RTC_LOG_T_F(sev) \ + RTC_LOG(sev) << this << ": " << __PRETTY_FUNCTION__ << ": " +#else +#define RTC_LOG_F(sev) RTC_LOG(sev) << __FUNCTION__ << ": " +#define RTC_LOG_T_F(sev) RTC_LOG(sev) << this << ": " << __FUNCTION__ << ": " +#endif + +#define RTC_LOG_CHECK_LEVEL(sev) ::rtc::LogCheckLevel(::rtc::sev) +#define RTC_LOG_CHECK_LEVEL_V(sev) ::rtc::LogCheckLevel(sev) + +inline bool LogCheckLevel(LoggingSeverity sev) { + return (LogMessage::GetMinLogSeverity() <= sev); +} + +#define RTC_LOG_E(sev, ctx, err) \ + !rtc::LogMessage::IsNoop<::rtc::sev>() && \ + ::rtc::webrtc_logging_impl::LogCall() & \ + ::rtc::webrtc_logging_impl::LogStreamer<>() \ + << ::rtc::webrtc_logging_impl::LogMetadataErr { \ + {__FILE__, __LINE__, ::rtc::sev}, ::rtc::ERRCTX_##ctx, (err) \ + } + +#define RTC_LOG_T(sev) RTC_LOG(sev) << this << ": " + +#define RTC_LOG_ERRNO_EX(sev, err) RTC_LOG_E(sev, ERRNO, err) +#define RTC_LOG_ERRNO(sev) RTC_LOG_ERRNO_EX(sev, errno) + +#if defined(WEBRTC_WIN) +#define RTC_LOG_GLE_EX(sev, err) RTC_LOG_E(sev, HRESULT, err) +#define RTC_LOG_GLE(sev) RTC_LOG_GLE_EX(sev, static_cast(GetLastError())) +#define RTC_LOG_ERR_EX(sev, err) RTC_LOG_GLE_EX(sev, err) +#define RTC_LOG_ERR(sev) RTC_LOG_GLE(sev) +#elif defined(__native_client__) && __native_client__ +#define RTC_LOG_ERR_EX(sev, err) RTC_LOG(sev) +#define RTC_LOG_ERR(sev) RTC_LOG(sev) +#elif defined(WEBRTC_POSIX) +#define RTC_LOG_ERR_EX(sev, err) RTC_LOG_ERRNO_EX(sev, err) +#define RTC_LOG_ERR(sev) RTC_LOG_ERRNO(sev) +#endif // WEBRTC_WIN + +#ifdef WEBRTC_ANDROID + +namespace webrtc_logging_impl { +// TODO(kwiberg): Replace these with absl::string_view. +inline const char* AdaptString(const char* str) { + return str; +} +inline const char* AdaptString(const std::string& str) { + return str.c_str(); +} +} // namespace webrtc_logging_impl + +#define RTC_LOG_TAG(sev, tag) \ + !rtc::LogMessage::IsNoop(sev) && \ + ::rtc::webrtc_logging_impl::LogCall() & \ + ::rtc::webrtc_logging_impl::LogStreamer<>() \ + << ::rtc::webrtc_logging_impl::LogMetadataTag { \ + sev, ::rtc::webrtc_logging_impl::AdaptString(tag) \ + } + +#else + +// DEPRECATED. This macro is only intended for Android. +#define RTC_LOG_TAG(sev, tag) RTC_LOG_V(sev) + +#endif + +// The RTC_DLOG macros are equivalent to their RTC_LOG counterparts except that +// they only generate code in debug builds. +#if RTC_DLOG_IS_ON +#define RTC_DLOG(sev) RTC_LOG(sev) +#define RTC_DLOG_V(sev) RTC_LOG_V(sev) +#define RTC_DLOG_F(sev) RTC_LOG_F(sev) +#else +#define RTC_DLOG_EAT_STREAM_PARAMS() \ + while (false) \ + ::rtc::webrtc_logging_impl::LogMessageVoidify() & \ + (::rtc::webrtc_logging_impl::LogStreamer<>()) +#define RTC_DLOG(sev) RTC_DLOG_EAT_STREAM_PARAMS() +#define RTC_DLOG_V(sev) RTC_DLOG_EAT_STREAM_PARAMS() +#define RTC_DLOG_F(sev) RTC_DLOG_EAT_STREAM_PARAMS() +#endif + +} // namespace rtc + +#endif // RTC_BASE_LOGGING_H_ diff --git a/webrtc/rtc_base/memory/BUILD.gn b/webrtc/rtc_base/memory/BUILD.gn new file mode 100644 index 0000000..838fbc6 --- /dev/null +++ b/webrtc/rtc_base/memory/BUILD.gn @@ -0,0 +1,56 @@ +# Copyright (c) 2018 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. + +import("../../webrtc.gni") +if (is_android) { + import("//build/config/android/config.gni") + import("//build/config/android/rules.gni") +} + +rtc_library("aligned_malloc") { + sources = [ + "aligned_malloc.cc", + "aligned_malloc.h", + ] + deps = [ "..:checks" ] +} + +# Test only utility. +# TODO: Tag with `testonly = true` once all depending targets are correctly +# tagged. +rtc_library("fifo_buffer") { + visibility = [ + ":unittests", + "..:rtc_base_tests_utils", + "..:rtc_base_unittests", + "../../p2p:rtc_p2p", # This needs to be fixed. + ] + sources = [ + "fifo_buffer.cc", + "fifo_buffer.h", + ] + deps = [ + "..:rtc_base", + "../synchronization:mutex", + "../task_utils:pending_task_safety_flag", + "../task_utils:to_queued_task", + ] +} + +rtc_library("unittests") { + testonly = true + sources = [ + "aligned_malloc_unittest.cc", + "fifo_buffer_unittest.cc", + ] + deps = [ + ":aligned_malloc", + ":fifo_buffer", + "../../test:test_support", + ] +} diff --git a/webrtc/system_wrappers/source/aligned_malloc.cc b/webrtc/rtc_base/memory/aligned_malloc.cc similarity index 92% rename from webrtc/system_wrappers/source/aligned_malloc.cc rename to webrtc/rtc_base/memory/aligned_malloc.cc index a654e97..b00fab2 100644 --- a/webrtc/system_wrappers/source/aligned_malloc.cc +++ b/webrtc/rtc_base/memory/aligned_malloc.cc @@ -8,19 +8,19 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/system_wrappers/include/aligned_malloc.h" +#include "rtc_base/memory/aligned_malloc.h" -#include -#include +#include // for free, malloc +#include // for memcpy -#if _WIN32 +#include "rtc_base/checks.h" + +#ifdef _WIN32 #include #else #include #endif -#include "webrtc/typedefs.h" - // Reference on memory alignment: // http://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me namespace webrtc { @@ -63,9 +63,7 @@ void* AlignedMalloc(size_t size, size_t alignment) { // A pointer to the start of the memory must be stored so that it can be // retreived for deletion, ergo the sizeof(uintptr_t). void* memory_pointer = malloc(size + sizeof(uintptr_t) + alignment - 1); - if (memory_pointer == NULL) { - return NULL; - } + RTC_CHECK(memory_pointer) << "Couldn't allocate memory in AlignedMalloc"; // Aligning after the sizeof(uintptr_t) bytes will leave room for the header // in the same memory block. diff --git a/webrtc/system_wrappers/include/aligned_malloc.h b/webrtc/rtc_base/memory/aligned_malloc.h similarity index 75% rename from webrtc/system_wrappers/include/aligned_malloc.h rename to webrtc/rtc_base/memory/aligned_malloc.h index 277abec..42a6daa 100644 --- a/webrtc/system_wrappers/include/aligned_malloc.h +++ b/webrtc/rtc_base/memory/aligned_malloc.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ALIGNED_MALLOC_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ALIGNED_MALLOC_H_ +#ifndef RTC_BASE_MEMORY_ALIGNED_MALLOC_H_ +#define RTC_BASE_MEMORY_ALIGNED_MALLOC_H_ // The functions declared here // 1) Allocates block of aligned memory. @@ -36,24 +36,22 @@ void AlignedFree(void* mem_block); // Templated versions to facilitate usage of aligned malloc without casting // to and from void*. -template +template T* GetRightAlign(const T* ptr, size_t alignment) { - return reinterpret_cast(GetRightAlign(reinterpret_cast(ptr), - alignment)); + return reinterpret_cast( + GetRightAlign(reinterpret_cast(ptr), alignment)); } -template +template T* AlignedMalloc(size_t size, size_t alignment) { return reinterpret_cast(AlignedMalloc(size, alignment)); } -// Deleter for use with scoped_ptr. E.g., use as -// scoped_ptr foo; +// Deleter for use with unique_ptr. E.g., use as +// std::unique_ptr foo; struct AlignedFreeDeleter { - inline void operator()(void* ptr) const { - AlignedFree(ptr); - } + inline void operator()(void* ptr) const { AlignedFree(ptr); } }; } // namespace webrtc -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ALIGNED_MALLOC_H_ +#endif // RTC_BASE_MEMORY_ALIGNED_MALLOC_H_ diff --git a/webrtc/rtc_base/meson.build b/webrtc/rtc_base/meson.build new file mode 100644 index 0000000..5977c7e --- /dev/null +++ b/webrtc/rtc_base/meson.build @@ -0,0 +1,64 @@ +base_sources = [ + 'checks.cc', + 'event.cc', + 'event_tracer.cc', + 'experiments/field_trial_parser.cc', + 'logging.cc', + 'memory/aligned_malloc.cc', + 'platform_thread.cc', + 'platform_thread_types.cc', + 'race_checker.cc', + 'string_encode.cc', + 'string_to_number.cc', + 'string_utils.cc', + 'strings/string_builder.cc', + 'synchronization/mutex.cc', + 'synchronization/rw_lock_wrapper.cc', + 'synchronization/yield.cc', + 'synchronization/yield_policy.cc', + 'system/file_wrapper.cc', + 'time_utils.cc', + 'zero_memory.cc', +] + +base_headers = [ + [ '', 'arraysize.h' ], + [ '', 'checks.h' ], + [ '', 'constructor_magic.h' ], + [ '', 'deprecation.h' ], + [ '', 'ref_count.h' ], + [ '', 'type_traits.h' ], + [ 'numerics', 'safe_compare.h' ], + [ 'system', 'file_wrapper.h' ], + [ 'system', 'inline.h' ], + [ 'system', 'rtc_export.h' ], +] + +if have_posix + base_sources += [ + 'synchronization/rw_lock_posix.cc', + ] +elif have_win + base_sources += [ + 'synchronization/rw_lock_win.cc', + ] +endif + +foreach h : base_headers + install_headers( + join_paths(h[0], h[1]), + subdir: join_paths('webrtc_audio_processing', 'rtc_base', h[0]) + ) +endforeach + +libbase = static_library('libbase', + base_sources, + dependencies: common_deps, + include_directories: webrtc_inc, + cpp_args : common_cxxflags +) + +base_dep = declare_dependency( + link_with: libbase +) + diff --git a/webrtc/rtc_base/numerics/safe_compare.h b/webrtc/rtc_base/numerics/safe_compare.h new file mode 100644 index 0000000..85f0a30 --- /dev/null +++ b/webrtc/rtc_base/numerics/safe_compare.h @@ -0,0 +1,176 @@ +/* + * Copyright 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. + */ + +// This file defines six constexpr functions: +// +// rtc::SafeEq // == +// rtc::SafeNe // != +// rtc::SafeLt // < +// rtc::SafeLe // <= +// rtc::SafeGt // > +// rtc::SafeGe // >= +// +// They each accept two arguments of arbitrary types, and in almost all cases, +// they simply call the appropriate comparison operator. However, if both +// arguments are integers, they don't compare them using C++'s quirky rules, +// but instead adhere to the true mathematical definitions. It is as if the +// arguments were first converted to infinite-range signed integers, and then +// compared, although of course nothing expensive like that actually takes +// place. In practice, for signed/signed and unsigned/unsigned comparisons and +// some mixed-signed comparisons with a compile-time constant, the overhead is +// zero; in the remaining cases, it is just a few machine instructions (no +// branches). + +#ifndef RTC_BASE_NUMERICS_SAFE_COMPARE_H_ +#define RTC_BASE_NUMERICS_SAFE_COMPARE_H_ + +#include +#include + +#include +#include + +#include "rtc_base/type_traits.h" + +namespace rtc { + +namespace safe_cmp_impl { + +template +struct LargerIntImpl : std::false_type {}; +template <> +struct LargerIntImpl : std::true_type { + using type = int16_t; +}; +template <> +struct LargerIntImpl : std::true_type { + using type = int32_t; +}; +template <> +struct LargerIntImpl : std::true_type { + using type = int64_t; +}; + +// LargerInt::value is true iff there's a signed type that's larger +// than T1 (and no larger than the larger of T2 and int*, for performance +// reasons); and if there is such a type, LargerInt::type is an alias +// for it. +template +struct LargerInt + : LargerIntImpl {}; + +template +constexpr typename std::make_unsigned::type MakeUnsigned(T a) { + return static_cast::type>(a); +} + +// Overload for when both T1 and T2 have the same signedness. +template ::value == + std::is_signed::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return Op::Op(a, b); +} + +// Overload for signed - unsigned comparison that can be promoted to a bigger +// signed type. +template ::value && + std::is_unsigned::value && + LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return Op::Op(a, static_cast::type>(b)); +} + +// Overload for unsigned - signed comparison that can be promoted to a bigger +// signed type. +template ::value && + std::is_signed::value && + LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return Op::Op(static_cast::type>(a), b); +} + +// Overload for signed - unsigned comparison that can't be promoted to a bigger +// signed type. +template ::value && + std::is_unsigned::value && + !LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return a < 0 ? Op::Op(-1, 0) : Op::Op(safe_cmp_impl::MakeUnsigned(a), b); +} + +// Overload for unsigned - signed comparison that can't be promoted to a bigger +// signed type. +template ::value && + std::is_signed::value && + !LargerInt::value>::type* = nullptr> +constexpr bool Cmp(T1 a, T2 b) { + return b < 0 ? Op::Op(0, -1) : Op::Op(a, safe_cmp_impl::MakeUnsigned(b)); +} + +#define RTC_SAFECMP_MAKE_OP(name, op) \ + struct name { \ + template \ + static constexpr bool Op(T1 a, T2 b) { \ + return a op b; \ + } \ + }; +RTC_SAFECMP_MAKE_OP(EqOp, ==) +RTC_SAFECMP_MAKE_OP(NeOp, !=) +RTC_SAFECMP_MAKE_OP(LtOp, <) +RTC_SAFECMP_MAKE_OP(LeOp, <=) +RTC_SAFECMP_MAKE_OP(GtOp, >) +RTC_SAFECMP_MAKE_OP(GeOp, >=) +#undef RTC_SAFECMP_MAKE_OP + +} // namespace safe_cmp_impl + +#define RTC_SAFECMP_MAKE_FUN(name) \ + template \ + constexpr \ + typename std::enable_if::value && IsIntlike::value, \ + bool>::type Safe##name(T1 a, T2 b) { \ + /* Unary plus here turns enums into real integral types. */ \ + return safe_cmp_impl::Cmp(+a, +b); \ + } \ + template \ + constexpr \ + typename std::enable_if::value || !IsIntlike::value, \ + bool>::type Safe##name(const T1& a, \ + const T2& b) { \ + return safe_cmp_impl::name##Op::Op(a, b); \ + } +RTC_SAFECMP_MAKE_FUN(Eq) +RTC_SAFECMP_MAKE_FUN(Ne) +RTC_SAFECMP_MAKE_FUN(Lt) +RTC_SAFECMP_MAKE_FUN(Le) +RTC_SAFECMP_MAKE_FUN(Gt) +RTC_SAFECMP_MAKE_FUN(Ge) +#undef RTC_SAFECMP_MAKE_FUN + +} // namespace rtc + +#endif // RTC_BASE_NUMERICS_SAFE_COMPARE_H_ diff --git a/webrtc/base/safe_conversions.h b/webrtc/rtc_base/numerics/safe_conversions.h similarity index 69% rename from webrtc/base/safe_conversions.h rename to webrtc/rtc_base/numerics/safe_conversions.h index 51239bc..5d58672 100644 --- a/webrtc/base/safe_conversions.h +++ b/webrtc/rtc_base/numerics/safe_conversions.h @@ -10,37 +10,43 @@ // Borrowed from Chromium's src/base/numerics/safe_conversions.h. -#ifndef WEBRTC_BASE_SAFE_CONVERSIONS_H_ -#define WEBRTC_BASE_SAFE_CONVERSIONS_H_ +#ifndef RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ +#define RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ #include -#include "webrtc/base/checks.h" -#include "webrtc/base/safe_conversions_impl.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions_impl.h" namespace rtc { // Convenience function that returns true if the supplied value is in range // for the destination type. template -inline bool IsValueInRangeForNumericType(Src value) { +inline constexpr bool IsValueInRangeForNumericType(Src value) { return internal::RangeCheck(value) == internal::TYPE_VALID; } -// checked_cast<> is analogous to static_cast<> for numeric types, -// except that it CHECKs that the specified numeric conversion will not -// overflow or underflow. NaN source will always trigger a CHECK. +// checked_cast<> and dchecked_cast<> are analogous to static_cast<> for +// numeric types, except that they [D]CHECK that the specified numeric +// conversion will not overflow or underflow. NaN source will always trigger +// the [D]CHECK. template -inline Dst checked_cast(Src value) { +inline constexpr Dst checked_cast(Src value) { RTC_CHECK(IsValueInRangeForNumericType(value)); return static_cast(value); } +template +inline constexpr Dst dchecked_cast(Src value) { + RTC_DCHECK(IsValueInRangeForNumericType(value)); + return static_cast(value); +} // saturated_cast<> is analogous to static_cast<> for numeric types, except // that the specified numeric conversion will saturate rather than overflow or // underflow. NaN assignment to an integral will trigger a RTC_CHECK condition. template -inline Dst saturated_cast(Src value) { +inline constexpr Dst saturated_cast(Src value) { // Optimization for floating point values, which already saturate. if (std::numeric_limits::is_iec559) return static_cast(value); @@ -67,4 +73,4 @@ inline Dst saturated_cast(Src value) { } // namespace rtc -#endif // WEBRTC_BASE_SAFE_CONVERSIONS_H_ +#endif // RTC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ diff --git a/webrtc/base/safe_conversions_impl.h b/webrtc/rtc_base/numerics/safe_conversions_impl.h similarity index 51% rename from webrtc/base/safe_conversions_impl.h rename to webrtc/rtc_base/numerics/safe_conversions_impl.h index 52e52ef..e924ce3 100644 --- a/webrtc/base/safe_conversions_impl.h +++ b/webrtc/rtc_base/numerics/safe_conversions_impl.h @@ -10,37 +10,29 @@ // Borrowed from Chromium's src/base/numerics/safe_conversions_impl.h. -#ifndef WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_ -#define WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_ +#ifndef RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ +#define RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ #include namespace rtc { namespace internal { -enum DstSign { - DST_UNSIGNED, - DST_SIGNED -}; +enum DstSign { DST_UNSIGNED, DST_SIGNED }; -enum SrcSign { - SRC_UNSIGNED, - SRC_SIGNED -}; +enum SrcSign { SRC_UNSIGNED, SRC_SIGNED }; -enum DstRange { - OVERLAPS_RANGE, - CONTAINS_RANGE -}; +enum DstRange { OVERLAPS_RANGE, CONTAINS_RANGE }; // Helper templates to statically determine if our destination type can contain // all values represented by the source type. -template ::is_signed ? - DST_SIGNED : DST_UNSIGNED, - SrcSign IsSrcSigned = std::numeric_limits::is_signed ? - SRC_SIGNED : SRC_UNSIGNED> +template ::is_signed ? DST_SIGNED : DST_UNSIGNED, + SrcSign IsSrcSigned = + std::numeric_limits::is_signed ? SRC_SIGNED : SRC_UNSIGNED> struct StaticRangeCheck {}; template @@ -48,20 +40,18 @@ struct StaticRangeCheck { typedef std::numeric_limits DstLimits; typedef std::numeric_limits SrcLimits; // Compare based on max_exponent, which we must compute for integrals. - static const size_t kDstMaxExponent = DstLimits::is_iec559 ? - DstLimits::max_exponent : - (sizeof(Dst) * 8 - 1); - static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ? - SrcLimits::max_exponent : - (sizeof(Src) * 8 - 1); - static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ? - CONTAINS_RANGE : OVERLAPS_RANGE; + static const size_t kDstMaxExponent = + DstLimits::is_iec559 ? DstLimits::max_exponent : (sizeof(Dst) * 8 - 1); + static const size_t kSrcMaxExponent = + SrcLimits::is_iec559 ? SrcLimits::max_exponent : (sizeof(Src) * 8 - 1); + static const DstRange value = + kDstMaxExponent >= kSrcMaxExponent ? CONTAINS_RANGE : OVERLAPS_RANGE; }; template struct StaticRangeCheck { - static const DstRange value = sizeof(Dst) >= sizeof(Src) ? - CONTAINS_RANGE : OVERLAPS_RANGE; + static const DstRange value = + sizeof(Dst) >= sizeof(Src) ? CONTAINS_RANGE : OVERLAPS_RANGE; }; template @@ -69,12 +59,11 @@ struct StaticRangeCheck { typedef std::numeric_limits DstLimits; typedef std::numeric_limits SrcLimits; // Compare based on max_exponent, which we must compute for integrals. - static const size_t kDstMaxExponent = DstLimits::is_iec559 ? - DstLimits::max_exponent : - (sizeof(Dst) * 8 - 1); + static const size_t kDstMaxExponent = + DstLimits::is_iec559 ? DstLimits::max_exponent : (sizeof(Dst) * 8 - 1); static const size_t kSrcMaxExponent = sizeof(Src) * 8; - static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ? - CONTAINS_RANGE : OVERLAPS_RANGE; + static const DstRange value = + kDstMaxExponent >= kSrcMaxExponent ? CONTAINS_RANGE : OVERLAPS_RANGE; }; template @@ -82,7 +71,6 @@ struct StaticRangeCheck { static const DstRange value = OVERLAPS_RANGE; }; - enum RangeCheckResult { TYPE_VALID = 0, // Value can be represented by the destination type. TYPE_UNDERFLOW = 1, // Value would overflow. @@ -94,15 +82,15 @@ enum RangeCheckResult { // check by taking advantage of the fact that only NaN can be out of range in // both directions at once. #define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \ - RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \ - ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW)) + RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \ + ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW)) template ::is_signed ? - DST_SIGNED : DST_UNSIGNED, - SrcSign IsSrcSigned = std::numeric_limits::is_signed ? - SRC_SIGNED : SRC_UNSIGNED, + DstSign IsDstSigned = + std::numeric_limits::is_signed ? DST_SIGNED : DST_UNSIGNED, + SrcSign IsSrcSigned = + std::numeric_limits::is_signed ? SRC_SIGNED : SRC_UNSIGNED, DstRange IsSrcRangeContained = StaticRangeCheck::value> struct RangeCheckImpl {}; @@ -113,68 +101,69 @@ struct RangeCheckImpl {}; // Dst range always contains the result: nothing to check. template struct RangeCheckImpl { - static RangeCheckResult Check(Src value) { - return TYPE_VALID; - } + static constexpr RangeCheckResult Check(Src value) { return TYPE_VALID; } }; // Signed to signed narrowing. template struct RangeCheckImpl { - static RangeCheckResult Check(Src value) { + static constexpr RangeCheckResult Check(Src value) { typedef std::numeric_limits DstLimits; - return DstLimits::is_iec559 ? - BASE_NUMERIC_RANGE_CHECK_RESULT( - value <= static_cast(DstLimits::max()), - value >= static_cast(DstLimits::max() * -1)) : - BASE_NUMERIC_RANGE_CHECK_RESULT( - value <= static_cast(DstLimits::max()), - value >= static_cast(DstLimits::min())); + return DstLimits::is_iec559 + ? BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(DstLimits::max() * -1)) + : BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(DstLimits::min())); } }; // Unsigned to unsigned narrowing. template struct RangeCheckImpl { - static RangeCheckResult Check(Src value) { + static constexpr RangeCheckResult Check(Src value) { typedef std::numeric_limits DstLimits; return BASE_NUMERIC_RANGE_CHECK_RESULT( - value <= static_cast(DstLimits::max()), true); + value <= static_cast(DstLimits::max()), true); } }; // Unsigned to signed. template struct RangeCheckImpl { - static RangeCheckResult Check(Src value) { + static constexpr RangeCheckResult Check(Src value) { typedef std::numeric_limits DstLimits; - return sizeof(Dst) > sizeof(Src) ? TYPE_VALID : - BASE_NUMERIC_RANGE_CHECK_RESULT( - value <= static_cast(DstLimits::max()), true); + return sizeof(Dst) > sizeof(Src) + ? TYPE_VALID + : BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), true); } }; // Signed to unsigned. template struct RangeCheckImpl { - static RangeCheckResult Check(Src value) { - typedef std::numeric_limits DstLimits; - typedef std::numeric_limits SrcLimits; - // Compare based on max_exponent, which we must compute for integrals. - static const size_t kDstMaxExponent = sizeof(Dst) * 8; - static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ? - SrcLimits::max_exponent : - (sizeof(Src) * 8 - 1); - return (kDstMaxExponent >= kSrcMaxExponent) ? - BASE_NUMERIC_RANGE_CHECK_RESULT(true, value >= static_cast(0)) : - BASE_NUMERIC_RANGE_CHECK_RESULT( - value <= static_cast(DstLimits::max()), - value >= static_cast(0)); + typedef std::numeric_limits DstLimits; + typedef std::numeric_limits SrcLimits; + // Compare based on max_exponent, which we must compute for integrals. + static constexpr size_t DstMaxExponent() { return sizeof(Dst) * 8; } + static constexpr size_t SrcMaxExponent() { + return SrcLimits::is_iec559 ? SrcLimits::max_exponent + : (sizeof(Src) * 8 - 1); + } + static constexpr RangeCheckResult Check(Src value) { + return (DstMaxExponent() >= SrcMaxExponent()) + ? BASE_NUMERIC_RANGE_CHECK_RESULT(true, + value >= static_cast(0)) + : BASE_NUMERIC_RANGE_CHECK_RESULT( + value <= static_cast(DstLimits::max()), + value >= static_cast(0)); } }; template -inline RangeCheckResult RangeCheck(Src value) { +inline constexpr RangeCheckResult RangeCheck(Src value) { static_assert(std::numeric_limits::is_specialized, "argument must be numeric"); static_assert(std::numeric_limits::is_specialized, @@ -185,4 +174,4 @@ inline RangeCheckResult RangeCheck(Src value) { } // namespace internal } // namespace rtc -#endif // WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_ +#endif // RTC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ diff --git a/webrtc/rtc_base/numerics/safe_minmax.h b/webrtc/rtc_base/numerics/safe_minmax.h new file mode 100644 index 0000000..6c41dfd --- /dev/null +++ b/webrtc/rtc_base/numerics/safe_minmax.h @@ -0,0 +1,335 @@ +/* + * Copyright 2017 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. + */ + +// Minimum and maximum +// =================== +// +// rtc::SafeMin(x, y) +// rtc::SafeMax(x, y) +// +// (These are both constexpr.) +// +// Accept two arguments of either any two integral or any two floating-point +// types, and return the smaller and larger value, respectively, with no +// truncation or wrap-around. If only one of the input types is statically +// guaranteed to be able to represent the result, the return type is that type; +// if either one would do, the result type is the smaller type. (One of these +// two cases always applies.) +// +// * The case with one floating-point and one integral type is not allowed, +// because the floating-point type will have greater range, but may not +// have sufficient precision to represent the integer value exactly.) +// +// Clamp (a.k.a. constrain to a given interval) +// ============================================ +// +// rtc::SafeClamp(x, a, b) +// +// Accepts three arguments of any mix of integral types or any mix of +// floating-point types, and returns the value in the closed interval [a, b] +// that is closest to x (that is, if x < a it returns a; if x > b it returns b; +// and if a <= x <= b it returns x). As for SafeMin() and SafeMax(), there is +// no truncation or wrap-around. The result type +// +// 1. is statically guaranteed to be able to represent the result; +// +// 2. is no larger than the largest of the three argument types; and +// +// 3. has the same signedness as the type of the first argument, if this is +// possible without violating the First or Second Law. +// +// There is always at least one type that meets criteria 1 and 2. If more than +// one type meets these criteria equally well, the result type is one of the +// types that is smallest. Note that unlike SafeMin() and SafeMax(), +// SafeClamp() will sometimes pick a return type that isn't the type of any of +// its arguments. +// +// * In this context, a type A is smaller than a type B if it has a smaller +// range; that is, if A::max() - A::min() < B::max() - B::min(). For +// example, int8_t < int16_t == uint16_t < int32_t, and all integral types +// are smaller than all floating-point types.) +// +// * As for SafeMin and SafeMax, mixing integer and floating-point arguments +// is not allowed, because floating-point types have greater range than +// integer types, but do not have sufficient precision to represent the +// values of most integer types exactly. +// +// Requesting a specific return type +// ================================= +// +// All three functions allow callers to explicitly specify the return type as a +// template parameter, overriding the default return type. E.g. +// +// rtc::SafeMin(x, y) // returns an int +// +// If the requested type is statically guaranteed to be able to represent the +// result, then everything's fine, and the return type is as requested. But if +// the requested type is too small, a static_assert is triggered. + +#ifndef RTC_BASE_NUMERICS_SAFE_MINMAX_H_ +#define RTC_BASE_NUMERICS_SAFE_MINMAX_H_ + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_compare.h" +#include "rtc_base/type_traits.h" + +namespace rtc { + +namespace safe_minmax_impl { + +// Make the range of a type available via something other than a constexpr +// function, to work around MSVC limitations. See +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +template +struct Limits { + static constexpr T lowest = std::numeric_limits::lowest(); + static constexpr T max = std::numeric_limits::max(); +}; + +template ::value> +struct UnderlyingType; + +template +struct UnderlyingType { + using type = T; +}; + +template +struct UnderlyingType { + using type = typename std::underlying_type::type; +}; + +// Given two types T1 and T2, find types that can hold the smallest (in +// ::min_t) and the largest (in ::max_t) of the two values. +template ::value, + bool int2 = IsIntlike::value> +struct MType { + static_assert(int1 == int2, + "You may not mix integral and floating-point arguments"); +}; + +// Specialization for when neither type is integral (and therefore presumably +// floating-point). +template +struct MType { + using min_t = typename std::common_type::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); + + using max_t = typename std::common_type::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); +}; + +// Specialization for when both types are integral. +template +struct MType { + // The type with the lowest minimum value. In case of a tie, the type with + // the lowest maximum value. In case that too is a tie, the types have the + // same range, and we arbitrarily pick T1. + using min_t = typename std::conditional< + SafeLt(Limits::lowest, Limits::lowest), + T1, + typename std::conditional< + SafeGt(Limits::lowest, Limits::lowest), + T2, + typename std::conditional::max, Limits::max), + T1, + T2>::type>::type>::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); + + // The type with the highest maximum value. In case of a tie, the types have + // the same range (because in C++, integer types with the same maximum also + // have the same minimum). + static_assert(SafeNe(Limits::max, Limits::max) || + SafeEq(Limits::lowest, Limits::lowest), + "integer types with the same max should have the same min"); + using max_t = typename std:: + conditional::max, Limits::max), T1, T2>::type; + static_assert(std::is_same::value || + std::is_same::value, + ""); +}; + +// A dummy type that we pass around at compile time but never actually use. +// Declared but not defined. +struct DefaultType; + +// ::type is A, except we fall back to B if A is DefaultType. We static_assert +// that the chosen type can hold all values that B can hold. +template +struct TypeOr { + using type = typename std:: + conditional::value, B, A>::type; + static_assert(SafeLe(Limits::lowest, Limits::lowest) && + SafeGe(Limits::max, Limits::max), + "The specified type isn't large enough"); + static_assert(IsIntlike::value == IsIntlike::value && + std::is_floating_point::value == + std::is_floating_point::value, + "float<->int conversions not allowed"); +}; + +} // namespace safe_minmax_impl + +template < + typename R = safe_minmax_impl::DefaultType, + typename T1 = safe_minmax_impl::DefaultType, + typename T2 = safe_minmax_impl::DefaultType, + typename R2 = typename safe_minmax_impl::TypeOr< + R, + typename safe_minmax_impl::MType< + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type>::min_t>::type> +constexpr R2 SafeMin(T1 a, T2 b) { + static_assert(IsIntlike::value || std::is_floating_point::value, + "The first argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The second argument must be integral or floating-point"); + return SafeLt(a, b) ? static_cast(a) : static_cast(b); +} + +template < + typename R = safe_minmax_impl::DefaultType, + typename T1 = safe_minmax_impl::DefaultType, + typename T2 = safe_minmax_impl::DefaultType, + typename R2 = typename safe_minmax_impl::TypeOr< + R, + typename safe_minmax_impl::MType< + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type>::max_t>::type> +constexpr R2 SafeMax(T1 a, T2 b) { + static_assert(IsIntlike::value || std::is_floating_point::value, + "The first argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The second argument must be integral or floating-point"); + return SafeGt(a, b) ? static_cast(a) : static_cast(b); +} + +namespace safe_minmax_impl { + +// Given three types T, L, and H, let ::type be a suitable return value for +// SafeClamp(T, L, H). See the docs at the top of this file for details. +template ::value, + bool int2 = IsIntlike::value, + bool int3 = IsIntlike::value> +struct ClampType { + static_assert(int1 == int2 && int1 == int3, + "You may not mix integral and floating-point arguments"); +}; + +// Specialization for when all three types are floating-point. +template +struct ClampType { + using type = typename std::common_type::type; +}; + +// Specialization for when all three types are integral. +template +struct ClampType { + private: + // Range of the return value. The return type must be able to represent this + // full range. + static constexpr auto r_min = + SafeMax(Limits::lowest, SafeMin(Limits::lowest, Limits::lowest)); + static constexpr auto r_max = + SafeMin(Limits::max, SafeMax(Limits::max, Limits::max)); + + // Is the given type an acceptable return type? (That is, can it represent + // all possible return values, and is it no larger than the largest of the + // input types?) + template + struct AcceptableType { + private: + static constexpr bool not_too_large = sizeof(A) <= sizeof(L) || + sizeof(A) <= sizeof(H) || + sizeof(A) <= sizeof(T); + static constexpr bool range_contained = + SafeLe(Limits::lowest, r_min) && SafeLe(r_max, Limits::max); + + public: + static constexpr bool value = not_too_large && range_contained; + }; + + using best_signed_type = typename std::conditional< + AcceptableType::value, + int8_t, + typename std::conditional< + AcceptableType::value, + int16_t, + typename std::conditional::value, + int32_t, + int64_t>::type>::type>::type; + + using best_unsigned_type = typename std::conditional< + AcceptableType::value, + uint8_t, + typename std::conditional< + AcceptableType::value, + uint16_t, + typename std::conditional::value, + uint32_t, + uint64_t>::type>::type>::type; + + public: + // Pick the best type, preferring the same signedness as T but falling back + // to the other one if necessary. + using type = typename std::conditional< + std::is_signed::value, + typename std::conditional::value, + best_signed_type, + best_unsigned_type>::type, + typename std::conditional::value, + best_unsigned_type, + best_signed_type>::type>::type; + static_assert(AcceptableType::value, ""); +}; + +} // namespace safe_minmax_impl + +template < + typename R = safe_minmax_impl::DefaultType, + typename T = safe_minmax_impl::DefaultType, + typename L = safe_minmax_impl::DefaultType, + typename H = safe_minmax_impl::DefaultType, + typename R2 = typename safe_minmax_impl::TypeOr< + R, + typename safe_minmax_impl::ClampType< + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type, + typename safe_minmax_impl::UnderlyingType::type>::type>::type> +R2 SafeClamp(T x, L min, H max) { + static_assert(IsIntlike::value || std::is_floating_point::value, + "The first argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The second argument must be integral or floating-point"); + static_assert(IsIntlike::value || std::is_floating_point::value, + "The third argument must be integral or floating-point"); + RTC_DCHECK_LE(min, max); + return SafeLe(x, min) + ? static_cast(min) + : SafeGe(x, max) ? static_cast(max) : static_cast(x); +} + +} // namespace rtc + +#endif // RTC_BASE_NUMERICS_SAFE_MINMAX_H_ diff --git a/webrtc/rtc_base/platform_thread.cc b/webrtc/rtc_base/platform_thread.cc new file mode 100644 index 0000000..8a5f2c9 --- /dev/null +++ b/webrtc/rtc_base/platform_thread.cc @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2015 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 "rtc_base/platform_thread.h" + +#if !defined(WEBRTC_WIN) +#include +#endif +#include +#include + +#include + +#include "rtc_base/checks.h" + +namespace rtc { +namespace { +#if !defined(WEBRTC_WIN) +struct ThreadAttributes { + ThreadAttributes() { pthread_attr_init(&attr); } + ~ThreadAttributes() { pthread_attr_destroy(&attr); } + pthread_attr_t* operator&() { return &attr; } + pthread_attr_t attr; +}; +#endif // defined(WEBRTC_WIN) +} // namespace + +PlatformThread::PlatformThread(ThreadRunFunction func, + void* obj, + absl::string_view thread_name, + ThreadPriority priority /*= kNormalPriority*/) + : run_function_(func), priority_(priority), obj_(obj), name_(thread_name) { + RTC_DCHECK(func); + RTC_DCHECK(!name_.empty()); + // TODO(tommi): Consider lowering the limit to 15 (limit on Linux). + RTC_DCHECK(name_.length() < 64); + spawned_thread_checker_.Detach(); +} + +PlatformThread::~PlatformThread() { + RTC_DCHECK(thread_checker_.IsCurrent()); +#if defined(WEBRTC_WIN) + RTC_DCHECK(!thread_); + RTC_DCHECK(!thread_id_); +#endif // defined(WEBRTC_WIN) +} + +#if defined(WEBRTC_WIN) +DWORD WINAPI PlatformThread::StartThread(void* param) { + // The GetLastError() function only returns valid results when it is called + // after a Win32 API function that returns a "failed" result. A crash dump + // contains the result from GetLastError() and to make sure it does not + // falsely report a Windows error we call SetLastError here. + ::SetLastError(ERROR_SUCCESS); + static_cast(param)->Run(); + return 0; +} +#else +void* PlatformThread::StartThread(void* param) { + static_cast(param)->Run(); + return 0; +} +#endif // defined(WEBRTC_WIN) + +void PlatformThread::Start() { + RTC_DCHECK(thread_checker_.IsCurrent()); + RTC_DCHECK(!thread_) << "Thread already started?"; +#if defined(WEBRTC_WIN) + // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION. + // Set the reserved stack stack size to 1M, which is the default on Windows + // and Linux. + thread_ = ::CreateThread(nullptr, 1024 * 1024, &StartThread, this, + STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id_); + RTC_CHECK(thread_) << "CreateThread failed"; + RTC_DCHECK(thread_id_); +#else + ThreadAttributes attr; + // Set the stack stack size to 1M. + pthread_attr_setstacksize(&attr, 1024 * 1024); + RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this)); +#endif // defined(WEBRTC_WIN) +} + +bool PlatformThread::IsRunning() const { + RTC_DCHECK(thread_checker_.IsCurrent()); +#if defined(WEBRTC_WIN) + return thread_ != nullptr; +#else + return thread_ != 0; +#endif // defined(WEBRTC_WIN) +} + +PlatformThreadRef PlatformThread::GetThreadRef() const { +#if defined(WEBRTC_WIN) + return thread_id_; +#else + return thread_; +#endif // defined(WEBRTC_WIN) +} + +void PlatformThread::Stop() { + RTC_DCHECK(thread_checker_.IsCurrent()); + if (!IsRunning()) + return; + +#if defined(WEBRTC_WIN) + WaitForSingleObject(thread_, INFINITE); + CloseHandle(thread_); + thread_ = nullptr; + thread_id_ = 0; +#else + RTC_CHECK_EQ(0, pthread_join(thread_, nullptr)); + thread_ = 0; +#endif // defined(WEBRTC_WIN) + spawned_thread_checker_.Detach(); +} + +void PlatformThread::Run() { + // Attach the worker thread checker to this thread. + RTC_DCHECK(spawned_thread_checker_.IsCurrent()); + rtc::SetCurrentThreadName(name_.c_str()); + SetPriority(priority_); + run_function_(obj_); +} + +bool PlatformThread::SetPriority(ThreadPriority priority) { + RTC_DCHECK(spawned_thread_checker_.IsCurrent()); + +#if defined(WEBRTC_WIN) + return SetThreadPriority(thread_, priority) != FALSE; +#elif defined(__native_client__) || defined(WEBRTC_FUCHSIA) + // Setting thread priorities is not supported in NaCl or Fuchsia. + return true; +#elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX) + // TODO(tommi): Switch to the same mechanism as Chromium uses for changing + // thread priorities. + return true; +#else + const int policy = SCHED_FIFO; + const int min_prio = sched_get_priority_min(policy); + const int max_prio = sched_get_priority_max(policy); + if (min_prio == -1 || max_prio == -1) { + return false; + } + + if (max_prio - min_prio <= 2) + return false; + + // Convert webrtc priority to system priorities: + sched_param param; + const int top_prio = max_prio - 1; + const int low_prio = min_prio + 1; + switch (priority) { + case kLowPriority: + param.sched_priority = low_prio; + break; + case kNormalPriority: + // The -1 ensures that the kHighPriority is always greater or equal to + // kNormalPriority. + param.sched_priority = (low_prio + top_prio - 1) / 2; + break; + case kHighPriority: + param.sched_priority = std::max(top_prio - 2, low_prio); + break; + case kHighestPriority: + param.sched_priority = std::max(top_prio - 1, low_prio); + break; + case kRealtimePriority: + param.sched_priority = top_prio; + break; + } + return pthread_setschedparam(thread_, policy, ¶m) == 0; +#endif // defined(WEBRTC_WIN) +} + +#if defined(WEBRTC_WIN) +bool PlatformThread::QueueAPC(PAPCFUNC function, ULONG_PTR data) { + RTC_DCHECK(thread_checker_.IsCurrent()); + RTC_DCHECK(IsRunning()); + + return QueueUserAPC(function, thread_, data) != FALSE; +} +#endif + +} // namespace rtc diff --git a/webrtc/rtc_base/platform_thread.h b/webrtc/rtc_base/platform_thread.h new file mode 100644 index 0000000..4968de9 --- /dev/null +++ b/webrtc/rtc_base/platform_thread.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015 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. + */ + +#ifndef RTC_BASE_PLATFORM_THREAD_H_ +#define RTC_BASE_PLATFORM_THREAD_H_ + +#ifndef WEBRTC_WIN +#include +#endif +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/constructor_magic.h" +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/thread_checker.h" + +namespace rtc { + +// Callback function that the spawned thread will enter once spawned. +typedef void (*ThreadRunFunction)(void*); + +enum ThreadPriority { +#ifdef WEBRTC_WIN + kLowPriority = THREAD_PRIORITY_BELOW_NORMAL, + kNormalPriority = THREAD_PRIORITY_NORMAL, + kHighPriority = THREAD_PRIORITY_ABOVE_NORMAL, + kHighestPriority = THREAD_PRIORITY_HIGHEST, + kRealtimePriority = THREAD_PRIORITY_TIME_CRITICAL +#else + kLowPriority = 1, + kNormalPriority = 2, + kHighPriority = 3, + kHighestPriority = 4, + kRealtimePriority = 5 +#endif +}; + +// Represents a simple worker thread. The implementation must be assumed +// to be single threaded, meaning that all methods of the class, must be +// called from the same thread, including instantiation. +class PlatformThread { + public: + PlatformThread(ThreadRunFunction func, + void* obj, + absl::string_view thread_name, + ThreadPriority priority = kNormalPriority); + virtual ~PlatformThread(); + + const std::string& name() const { return name_; } + + // Spawns a thread and tries to set thread priority according to the priority + // from when CreateThread was called. + void Start(); + + bool IsRunning() const; + + // Returns an identifier for the worker thread that can be used to do + // thread checks. + PlatformThreadRef GetThreadRef() const; + + // Stops (joins) the spawned thread. + void Stop(); + + protected: +#if defined(WEBRTC_WIN) + // Exposed to derived classes to allow for special cases specific to Windows. + bool QueueAPC(PAPCFUNC apc_function, ULONG_PTR data); +#endif + + private: + void Run(); + bool SetPriority(ThreadPriority priority); + + ThreadRunFunction const run_function_ = nullptr; + const ThreadPriority priority_ = kNormalPriority; + void* const obj_; + // TODO(pbos): Make sure call sites use string literals and update to a const + // char* instead of a std::string. + const std::string name_; + rtc::ThreadChecker thread_checker_; + rtc::ThreadChecker spawned_thread_checker_; +#if defined(WEBRTC_WIN) + static DWORD WINAPI StartThread(void* param); + + HANDLE thread_ = nullptr; + DWORD thread_id_ = 0; +#else + static void* StartThread(void* param); + + pthread_t thread_ = 0; +#endif // defined(WEBRTC_WIN) + RTC_DISALLOW_COPY_AND_ASSIGN(PlatformThread); +}; + +} // namespace rtc + +#endif // RTC_BASE_PLATFORM_THREAD_H_ diff --git a/webrtc/rtc_base/platform_thread_types.cc b/webrtc/rtc_base/platform_thread_types.cc new file mode 100644 index 0000000..b0243b4 --- /dev/null +++ b/webrtc/rtc_base/platform_thread_types.cc @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018 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 "rtc_base/platform_thread_types.h" + +#if defined(WEBRTC_LINUX) +#include +#include +#endif + +#if defined(WEBRTC_WIN) +#include "rtc_base/arraysize.h" + +// The SetThreadDescription API was brought in version 1607 of Windows 10. +// For compatibility with various versions of winuser and avoid clashing with +// a potentially defined type, we use the RTC_ prefix. +typedef HRESULT(WINAPI* RTC_SetThreadDescription)(HANDLE hThread, + PCWSTR lpThreadDescription); +#endif + +namespace rtc { + +PlatformThreadId CurrentThreadId() { +#if defined(WEBRTC_WIN) + return GetCurrentThreadId(); +#elif defined(WEBRTC_POSIX) +#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) + return pthread_mach_thread_np(pthread_self()); +#elif defined(WEBRTC_ANDROID) + return gettid(); +#elif defined(WEBRTC_FUCHSIA) + return zx_thread_self(); +#elif defined(WEBRTC_LINUX) + return syscall(__NR_gettid); +#elif defined(__EMSCRIPTEN__) + return static_cast(pthread_self()); +#else + // Default implementation for nacl and solaris. + return reinterpret_cast(pthread_self()); +#endif +#endif // defined(WEBRTC_POSIX) +} + +PlatformThreadRef CurrentThreadRef() { +#if defined(WEBRTC_WIN) + return GetCurrentThreadId(); +#elif defined(WEBRTC_FUCHSIA) + return zx_thread_self(); +#elif defined(WEBRTC_POSIX) + return pthread_self(); +#endif +} + +bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b) { +#if defined(WEBRTC_WIN) || defined(WEBRTC_FUCHSIA) + return a == b; +#elif defined(WEBRTC_POSIX) + return pthread_equal(a, b); +#endif +} + +void SetCurrentThreadName(const char* name) { +#if defined(WEBRTC_WIN) + // The SetThreadDescription API works even if no debugger is attached. + // The names set with this API also show up in ETW traces. Very handy. + static auto set_thread_description_func = + reinterpret_cast(::GetProcAddress( + ::GetModuleHandleA("Kernel32.dll"), "SetThreadDescription")); + if (set_thread_description_func) { + // Convert from ASCII to UTF-16. + wchar_t wide_thread_name[64]; + for (size_t i = 0; i < arraysize(wide_thread_name) - 1; ++i) { + wide_thread_name[i] = name[i]; + if (wide_thread_name[i] == L'\0') + break; + } + // Guarantee null-termination. + wide_thread_name[arraysize(wide_thread_name) - 1] = L'\0'; + set_thread_description_func(::GetCurrentThread(), wide_thread_name); + } + + // For details see: + // https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code +#pragma pack(push, 8) + struct { + DWORD dwType; + LPCSTR szName; + DWORD dwThreadID; + DWORD dwFlags; + } threadname_info = {0x1000, name, static_cast(-1), 0}; +#pragma pack(pop) + +#pragma warning(push) +#pragma warning(disable : 6320 6322) + __try { + ::RaiseException(0x406D1388, 0, sizeof(threadname_info) / sizeof(ULONG_PTR), + reinterpret_cast(&threadname_info)); + } __except (EXCEPTION_EXECUTE_HANDLER) { // NOLINT + } +#pragma warning(pop) +#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID) + prctl(PR_SET_NAME, reinterpret_cast(name)); // NOLINT +#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS) + pthread_setname_np(name); +#endif +} + +} // namespace rtc diff --git a/webrtc/base/platform_thread.h b/webrtc/rtc_base/platform_thread_types.h similarity index 55% rename from webrtc/base/platform_thread.h rename to webrtc/rtc_base/platform_thread_types.h index 50033b3..6b9101e 100644 --- a/webrtc/base/platform_thread.h +++ b/webrtc/rtc_base/platform_thread_types.h @@ -8,28 +8,47 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_BASE_PLATFORM_THREAD_H_ -#define WEBRTC_BASE_PLATFORM_THREAD_H_ +#ifndef RTC_BASE_PLATFORM_THREAD_TYPES_H_ +#define RTC_BASE_PLATFORM_THREAD_TYPES_H_ +// clang-format off +// clang formating would change include order. #if defined(WEBRTC_WIN) +// Include winsock2.h before including to maintain consistency with +// win32.h. To include win32.h directly, it must be broken out into its own +// build target. #include #include +#elif defined(WEBRTC_FUCHSIA) +#include +#include #elif defined(WEBRTC_POSIX) #include #include +#if defined(WEBRTC_MAC) +#include #endif +#endif +// clang-format on namespace rtc { - #if defined(WEBRTC_WIN) typedef DWORD PlatformThreadId; typedef DWORD PlatformThreadRef; +#elif defined(WEBRTC_FUCHSIA) +typedef zx_handle_t PlatformThreadId; +typedef zx_handle_t PlatformThreadRef; #elif defined(WEBRTC_POSIX) typedef pid_t PlatformThreadId; typedef pthread_t PlatformThreadRef; #endif +// Retrieve the ID of the current thread. PlatformThreadId CurrentThreadId(); + +// Retrieves a reference to the current thread. On Windows, this is the same +// as CurrentThreadId. On other platforms it's the pthread_t returned by +// pthread_self(). PlatformThreadRef CurrentThreadRef(); // Compares two thread identifiers for equality. @@ -40,4 +59,4 @@ void SetCurrentThreadName(const char* name); } // namespace rtc -#endif // WEBRTC_BASE_PLATFORM_THREAD_H_ +#endif // RTC_BASE_PLATFORM_THREAD_TYPES_H_ diff --git a/webrtc/rtc_base/race_checker.cc b/webrtc/rtc_base/race_checker.cc new file mode 100644 index 0000000..bf9dfdc --- /dev/null +++ b/webrtc/rtc_base/race_checker.cc @@ -0,0 +1,54 @@ +/* + * 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 "rtc_base/race_checker.h" + +namespace rtc { + +RaceChecker::RaceChecker() {} + +// Note that the implementation here is in itself racy, but we pretend it does +// not matter because we want this useful in release builds without having to +// pay the cost of using atomics. A race hitting the race checker is likely to +// cause access_count_ to diverge from zero and therefore cause the ThreadRef +// comparison to fail, signaling a race, although it may not be in the exact +// spot where a race *first* appeared in the code we're trying to protect. There +// is also a chance that an actual race is missed, however the probability of +// that has been considered small enough to be an acceptable trade off. +bool RaceChecker::Acquire() const { + const PlatformThreadRef current_thread = CurrentThreadRef(); + // Set new accessing thread if this is a new use. + if (access_count_++ == 0) + accessing_thread_ = current_thread; + // If this is being used concurrently this check will fail for the second + // thread entering since it won't set the thread. Recursive use of checked + // methods are OK since the accessing thread remains the same. + const PlatformThreadRef accessing_thread = accessing_thread_; + return IsThreadRefEqual(accessing_thread, current_thread); +} + +void RaceChecker::Release() const { + --access_count_; +} + +namespace internal { +RaceCheckerScope::RaceCheckerScope(const RaceChecker* race_checker) + : race_checker_(race_checker), race_check_ok_(race_checker->Acquire()) {} + +bool RaceCheckerScope::RaceDetected() const { + return !race_check_ok_; +} + +RaceCheckerScope::~RaceCheckerScope() { + race_checker_->Release(); +} + +} // namespace internal +} // namespace rtc diff --git a/webrtc/rtc_base/race_checker.h b/webrtc/rtc_base/race_checker.h new file mode 100644 index 0000000..4d57460 --- /dev/null +++ b/webrtc/rtc_base/race_checker.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_RACE_CHECKER_H_ +#define RTC_BASE_RACE_CHECKER_H_ + +#include "rtc_base/checks.h" +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/thread_annotations.h" + +namespace rtc { + +namespace internal { +class RaceCheckerScope; +} // namespace internal + +// Best-effort race-checking implementation. This primitive uses no +// synchronization at all to be as-fast-as-possible in the non-racy case. +class RTC_LOCKABLE RaceChecker { + public: + friend class internal::RaceCheckerScope; + RaceChecker(); + + private: + bool Acquire() const RTC_EXCLUSIVE_LOCK_FUNCTION(); + void Release() const RTC_UNLOCK_FUNCTION(); + + // Volatile to prevent code being optimized away in Acquire()/Release(). + mutable volatile int access_count_ = 0; + mutable volatile PlatformThreadRef accessing_thread_; +}; + +namespace internal { +class RTC_SCOPED_LOCKABLE RaceCheckerScope { + public: + explicit RaceCheckerScope(const RaceChecker* race_checker) + RTC_EXCLUSIVE_LOCK_FUNCTION(race_checker); + + bool RaceDetected() const; + ~RaceCheckerScope() RTC_UNLOCK_FUNCTION(); + + private: + const RaceChecker* const race_checker_; + const bool race_check_ok_; +}; + +class RTC_SCOPED_LOCKABLE RaceCheckerScopeDoNothing { + public: + explicit RaceCheckerScopeDoNothing(const RaceChecker* race_checker) + RTC_EXCLUSIVE_LOCK_FUNCTION(race_checker) {} + + ~RaceCheckerScopeDoNothing() RTC_UNLOCK_FUNCTION() {} +}; + +} // namespace internal +} // namespace rtc + +#define RTC_CHECK_RUNS_SERIALIZED(x) \ + rtc::internal::RaceCheckerScope race_checker(x); \ + RTC_CHECK(!race_checker.RaceDetected()) + +#if RTC_DCHECK_IS_ON +#define RTC_DCHECK_RUNS_SERIALIZED(x) \ + rtc::internal::RaceCheckerScope race_checker(x); \ + RTC_DCHECK(!race_checker.RaceDetected()) +#else +#define RTC_DCHECK_RUNS_SERIALIZED(x) \ + rtc::internal::RaceCheckerScopeDoNothing race_checker(x) +#endif + +#endif // RTC_BASE_RACE_CHECKER_H_ diff --git a/webrtc/rtc_base/ref_count.h b/webrtc/rtc_base/ref_count.h new file mode 100644 index 0000000..d8d652a --- /dev/null +++ b/webrtc/rtc_base/ref_count.h @@ -0,0 +1,67 @@ +/* + * Copyright 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. + */ +#ifndef RTC_BASE_REF_COUNT_H_ +#define RTC_BASE_REF_COUNT_H_ + +namespace rtc { + +// Refcounted objects should implement the following informal interface: +// +// void AddRef() const ; +// RefCountReleaseStatus Release() const; +// +// You may access members of a reference-counted object, including the AddRef() +// and Release() methods, only if you already own a reference to it, or if +// you're borrowing someone else's reference. (A newly created object is a +// special case: the reference count is zero on construction, and the code that +// creates the object should immediately call AddRef(), bringing the reference +// count from zero to one, e.g., by constructing an rtc::scoped_refptr). +// +// AddRef() creates a new reference to the object. +// +// Release() releases a reference to the object; the caller now has one less +// reference than before the call. Returns kDroppedLastRef if the number of +// references dropped to zero because of this (in which case the object destroys +// itself). Otherwise, returns kOtherRefsRemained, to signal that at the precise +// time the caller's reference was dropped, other references still remained (but +// if other threads own references, this may of course have changed by the time +// Release() returns). +// +// The caller of Release() must treat it in the same way as a delete operation: +// Regardless of the return value from Release(), the caller mustn't access the +// object. The object might still be alive, due to references held by other +// users of the object, but the object can go away at any time, e.g., as the +// result of another thread calling Release(). +// +// Calling AddRef() and Release() manually is discouraged. It's recommended to +// use rtc::scoped_refptr to manage all pointers to reference counted objects. +// Note that rtc::scoped_refptr depends on compile-time duck-typing; formally +// implementing the below RefCountInterface is not required. + +enum class RefCountReleaseStatus { kDroppedLastRef, kOtherRefsRemained }; + +// Interfaces where refcounting is part of the public api should +// inherit this abstract interface. The implementation of these +// methods is usually provided by the RefCountedObject template class, +// applied as a leaf in the inheritance tree. +class RefCountInterface { + public: + virtual void AddRef() const = 0; + virtual RefCountReleaseStatus Release() const = 0; + + // Non-public destructor, because Release() has exclusive responsibility for + // destroying the object. + protected: + virtual ~RefCountInterface() {} +}; + +} // namespace rtc + +#endif // RTC_BASE_REF_COUNT_H_ diff --git a/webrtc/rtc_base/ref_counted_object.h b/webrtc/rtc_base/ref_counted_object.h new file mode 100644 index 0000000..ce18379 --- /dev/null +++ b/webrtc/rtc_base/ref_counted_object.h @@ -0,0 +1,64 @@ +/* + * Copyright 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. + */ +#ifndef RTC_BASE_REF_COUNTED_OBJECT_H_ +#define RTC_BASE_REF_COUNTED_OBJECT_H_ + +#include +#include + +#include "rtc_base/constructor_magic.h" +#include "rtc_base/ref_count.h" +#include "rtc_base/ref_counter.h" + +namespace rtc { + +template +class RefCountedObject : public T { + public: + RefCountedObject() {} + + template + explicit RefCountedObject(P0&& p0) : T(std::forward(p0)) {} + + template + RefCountedObject(P0&& p0, P1&& p1, Args&&... args) + : T(std::forward(p0), + std::forward(p1), + std::forward(args)...) {} + + virtual void AddRef() const { ref_count_.IncRef(); } + + virtual RefCountReleaseStatus Release() const { + const auto status = ref_count_.DecRef(); + if (status == RefCountReleaseStatus::kDroppedLastRef) { + delete this; + } + return status; + } + + // Return whether the reference count is one. If the reference count is used + // in the conventional way, a reference count of 1 implies that the current + // thread owns the reference and no other thread shares it. This call + // performs the test for a reference count of one, and performs the memory + // barrier needed for the owning thread to act on the object, knowing that it + // has exclusive access to the object. + virtual bool HasOneRef() const { return ref_count_.HasOneRef(); } + + protected: + virtual ~RefCountedObject() {} + + mutable webrtc::webrtc_impl::RefCounter ref_count_{0}; + + RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedObject); +}; + +} // namespace rtc + +#endif // RTC_BASE_REF_COUNTED_OBJECT_H_ diff --git a/webrtc/rtc_base/ref_counter.h b/webrtc/rtc_base/ref_counter.h new file mode 100644 index 0000000..6ffeda8 --- /dev/null +++ b/webrtc/rtc_base/ref_counter.h @@ -0,0 +1,75 @@ +/* + * Copyright 2017 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. + */ +#ifndef RTC_BASE_REF_COUNTER_H_ +#define RTC_BASE_REF_COUNTER_H_ + +#include + +#include "rtc_base/ref_count.h" + +namespace webrtc { +namespace webrtc_impl { + +class RefCounter { + public: + explicit RefCounter(int ref_count) : ref_count_(ref_count) {} + RefCounter() = delete; + + void IncRef() { + // Relaxed memory order: The current thread is allowed to act on the + // resource protected by the reference counter both before and after the + // atomic op, so this function doesn't prevent memory access reordering. + ref_count_.fetch_add(1, std::memory_order_relaxed); + } + + // Returns kDroppedLastRef if this call dropped the last reference; the caller + // should therefore free the resource protected by the reference counter. + // Otherwise, returns kOtherRefsRemained (note that in case of multithreading, + // some other caller may have dropped the last reference by the time this call + // returns; all we know is that we didn't do it). + rtc::RefCountReleaseStatus DecRef() { + // Use release-acquire barrier to ensure all actions on the protected + // resource are finished before the resource can be freed. + // When ref_count_after_subtract > 0, this function require + // std::memory_order_release part of the barrier. + // When ref_count_after_subtract == 0, this function require + // std::memory_order_acquire part of the barrier. + // In addition std::memory_order_release is used for synchronization with + // the HasOneRef function to make sure all actions on the protected resource + // are finished before the resource is assumed to have exclusive access. + int ref_count_after_subtract = + ref_count_.fetch_sub(1, std::memory_order_acq_rel) - 1; + return ref_count_after_subtract == 0 + ? rtc::RefCountReleaseStatus::kDroppedLastRef + : rtc::RefCountReleaseStatus::kOtherRefsRemained; + } + + // Return whether the reference count is one. If the reference count is used + // in the conventional way, a reference count of 1 implies that the current + // thread owns the reference and no other thread shares it. This call performs + // the test for a reference count of one, and performs the memory barrier + // needed for the owning thread to act on the resource protected by the + // reference counter, knowing that it has exclusive access. + bool HasOneRef() const { + // To ensure resource protected by the reference counter has exclusive + // access, all changes to the resource before it was released by other + // threads must be visible by current thread. That is provided by release + // (in DecRef) and acquire (in this function) ordering. + return ref_count_.load(std::memory_order_acquire) == 1; + } + + private: + std::atomic ref_count_; +}; + +} // namespace webrtc_impl +} // namespace webrtc + +#endif // RTC_BASE_REF_COUNTER_H_ diff --git a/webrtc/rtc_base/sanitizer.h b/webrtc/rtc_base/sanitizer.h new file mode 100644 index 0000000..8af0824 --- /dev/null +++ b/webrtc/rtc_base/sanitizer.h @@ -0,0 +1,144 @@ +/* + * Copyright 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. + */ + +#ifndef RTC_BASE_SANITIZER_H_ +#define RTC_BASE_SANITIZER_H_ + +#include // For size_t. + +#ifdef __cplusplus +#include "absl/meta/type_traits.h" +#endif + +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define RTC_HAS_ASAN 1 +#endif +#if __has_feature(memory_sanitizer) +#define RTC_HAS_MSAN 1 +#endif +#endif +#ifndef RTC_HAS_ASAN +#define RTC_HAS_ASAN 0 +#endif +#ifndef RTC_HAS_MSAN +#define RTC_HAS_MSAN 0 +#endif + +#if RTC_HAS_ASAN +#include +#endif +#if RTC_HAS_MSAN +#include +#endif + +#ifdef __has_attribute +#if __has_attribute(no_sanitize) +#define RTC_NO_SANITIZE(what) __attribute__((no_sanitize(what))) +#endif +#endif +#ifndef RTC_NO_SANITIZE +#define RTC_NO_SANITIZE(what) +#endif + +// Ask ASan to mark the memory range [ptr, ptr + element_size * num_elements) +// as being unaddressable, so that reads and writes are not allowed. ASan may +// narrow the range to the nearest alignment boundaries. +static inline void rtc_AsanPoison(const volatile void* ptr, + size_t element_size, + size_t num_elements) { +#if RTC_HAS_ASAN + ASAN_POISON_MEMORY_REGION(ptr, element_size * num_elements); +#endif +} + +// Ask ASan to mark the memory range [ptr, ptr + element_size * num_elements) +// as being addressable, so that reads and writes are allowed. ASan may widen +// the range to the nearest alignment boundaries. +static inline void rtc_AsanUnpoison(const volatile void* ptr, + size_t element_size, + size_t num_elements) { +#if RTC_HAS_ASAN + ASAN_UNPOISON_MEMORY_REGION(ptr, element_size * num_elements); +#endif +} + +// Ask MSan to mark the memory range [ptr, ptr + element_size * num_elements) +// as being uninitialized. +static inline void rtc_MsanMarkUninitialized(const volatile void* ptr, + size_t element_size, + size_t num_elements) { +#if RTC_HAS_MSAN + __msan_poison(ptr, element_size * num_elements); +#endif +} + +// Force an MSan check (if any bits in the memory range [ptr, ptr + +// element_size * num_elements) are uninitialized the call will crash with an +// MSan report). +static inline void rtc_MsanCheckInitialized(const volatile void* ptr, + size_t element_size, + size_t num_elements) { +#if RTC_HAS_MSAN + __msan_check_mem_is_initialized(ptr, element_size * num_elements); +#endif +} + +#ifdef __cplusplus + +namespace rtc { +namespace sanitizer_impl { + +template +constexpr bool IsTriviallyCopyable() { + return static_cast(absl::is_trivially_copy_constructible::value && + (absl::is_trivially_copy_assignable::value || + !std::is_copy_assignable::value) && + absl::is_trivially_destructible::value); +} + +} // namespace sanitizer_impl + +template +inline void AsanPoison(const T& mem) { + rtc_AsanPoison(mem.data(), sizeof(mem.data()[0]), mem.size()); +} + +template +inline void AsanUnpoison(const T& mem) { + rtc_AsanUnpoison(mem.data(), sizeof(mem.data()[0]), mem.size()); +} + +template +inline void MsanMarkUninitialized(const T& mem) { + rtc_MsanMarkUninitialized(mem.data(), sizeof(mem.data()[0]), mem.size()); +} + +template +inline T MsanUninitialized(T t) { +#if RTC_HAS_MSAN + // TODO(bugs.webrtc.org/8762): Switch to std::is_trivially_copyable when it + // becomes available in downstream projects. + static_assert(sanitizer_impl::IsTriviallyCopyable(), ""); +#endif + rtc_MsanMarkUninitialized(&t, sizeof(T), 1); + return t; +} + +template +inline void MsanCheckInitialized(const T& mem) { + rtc_MsanCheckInitialized(mem.data(), sizeof(mem.data()[0]), mem.size()); +} + +} // namespace rtc + +#endif // __cplusplus + +#endif // RTC_BASE_SANITIZER_H_ diff --git a/webrtc/rtc_base/string_encode.cc b/webrtc/rtc_base/string_encode.cc new file mode 100644 index 0000000..1570b93 --- /dev/null +++ b/webrtc/rtc_base/string_encode.cc @@ -0,0 +1,387 @@ +/* + * Copyright 2004 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 "rtc_base/string_encode.h" + +#include + +#include "rtc_base/arraysize.h" +#include "rtc_base/checks.h" + +namespace rtc { + +///////////////////////////////////////////////////////////////////////////// +// String Encoding Utilities +///////////////////////////////////////////////////////////////////////////// + +namespace { +const char HEX[] = "0123456789abcdef"; + +// Convert an unsigned value from 0 to 15 to the hex character equivalent... +char hex_encode(unsigned char val) { + RTC_DCHECK_LT(val, 16); + return (val < 16) ? HEX[val] : '!'; +} + +// ...and vice-versa. +bool hex_decode(char ch, unsigned char* val) { + if ((ch >= '0') && (ch <= '9')) { + *val = ch - '0'; + } else if ((ch >= 'A') && (ch <= 'F')) { + *val = (ch - 'A') + 10; + } else if ((ch >= 'a') && (ch <= 'f')) { + *val = (ch - 'a') + 10; + } else { + return false; + } + return true; +} + +size_t hex_encode_output_length(size_t srclen, char delimiter) { + return delimiter && srclen > 0 ? (srclen * 3 - 1) : (srclen * 2); +} + +// hex_encode shows the hex representation of binary data in ascii, with +// |delimiter| between bytes, or none if |delimiter| == 0. +void hex_encode_with_delimiter(char* buffer, + const char* csource, + size_t srclen, + char delimiter) { + RTC_DCHECK(buffer); + + // Init and check bounds. + const unsigned char* bsource = + reinterpret_cast(csource); + size_t srcpos = 0, bufpos = 0; + + while (srcpos < srclen) { + unsigned char ch = bsource[srcpos++]; + buffer[bufpos] = hex_encode((ch >> 4) & 0xF); + buffer[bufpos + 1] = hex_encode((ch)&0xF); + bufpos += 2; + + // Don't write a delimiter after the last byte. + if (delimiter && (srcpos < srclen)) { + buffer[bufpos] = delimiter; + ++bufpos; + } + } +} + +} // namespace + +std::string hex_encode(const std::string& str) { + return hex_encode(str.c_str(), str.size()); +} + +std::string hex_encode(const char* source, size_t srclen) { + return hex_encode_with_delimiter(source, srclen, 0); +} + +std::string hex_encode_with_delimiter(const char* source, + size_t srclen, + char delimiter) { + std::string s(hex_encode_output_length(srclen, delimiter), 0); + hex_encode_with_delimiter(&s[0], source, srclen, delimiter); + return s; +} + +size_t hex_decode(char* cbuffer, + size_t buflen, + const char* source, + size_t srclen) { + return hex_decode_with_delimiter(cbuffer, buflen, source, srclen, 0); +} + +size_t hex_decode_with_delimiter(char* cbuffer, + size_t buflen, + const char* source, + size_t srclen, + char delimiter) { + RTC_DCHECK(cbuffer); // TODO(kwiberg): estimate output size + if (buflen == 0) + return 0; + + // Init and bounds check. + unsigned char* bbuffer = reinterpret_cast(cbuffer); + size_t srcpos = 0, bufpos = 0; + size_t needed = (delimiter) ? (srclen + 1) / 3 : srclen / 2; + if (buflen < needed) + return 0; + + while (srcpos < srclen) { + if ((srclen - srcpos) < 2) { + // This means we have an odd number of bytes. + return 0; + } + + unsigned char h1, h2; + if (!hex_decode(source[srcpos], &h1) || + !hex_decode(source[srcpos + 1], &h2)) + return 0; + + bbuffer[bufpos++] = (h1 << 4) | h2; + srcpos += 2; + + // Remove the delimiter if needed. + if (delimiter && (srclen - srcpos) > 1) { + if (source[srcpos] != delimiter) + return 0; + ++srcpos; + } + } + + return bufpos; +} + +size_t hex_decode(char* buffer, size_t buflen, const std::string& source) { + return hex_decode_with_delimiter(buffer, buflen, source, 0); +} +size_t hex_decode_with_delimiter(char* buffer, + size_t buflen, + const std::string& source, + char delimiter) { + return hex_decode_with_delimiter(buffer, buflen, source.c_str(), + source.length(), delimiter); +} + +size_t tokenize(const std::string& source, + char delimiter, + std::vector* fields) { + fields->clear(); + size_t last = 0; + for (size_t i = 0; i < source.length(); ++i) { + if (source[i] == delimiter) { + if (i != last) { + fields->push_back(source.substr(last, i - last)); + } + last = i + 1; + } + } + if (last != source.length()) { + fields->push_back(source.substr(last, source.length() - last)); + } + return fields->size(); +} + +size_t tokenize_with_empty_tokens(const std::string& source, + char delimiter, + std::vector* fields) { + fields->clear(); + size_t last = 0; + for (size_t i = 0; i < source.length(); ++i) { + if (source[i] == delimiter) { + fields->push_back(source.substr(last, i - last)); + last = i + 1; + } + } + fields->push_back(source.substr(last, source.length() - last)); + return fields->size(); +} + +size_t tokenize_append(const std::string& source, + char delimiter, + std::vector* fields) { + if (!fields) + return 0; + + std::vector new_fields; + tokenize(source, delimiter, &new_fields); + fields->insert(fields->end(), new_fields.begin(), new_fields.end()); + return fields->size(); +} + +size_t tokenize(const std::string& source, + char delimiter, + char start_mark, + char end_mark, + std::vector* fields) { + if (!fields) + return 0; + fields->clear(); + + std::string remain_source = source; + while (!remain_source.empty()) { + size_t start_pos = remain_source.find(start_mark); + if (std::string::npos == start_pos) + break; + std::string pre_mark; + if (start_pos > 0) { + pre_mark = remain_source.substr(0, start_pos - 1); + } + + ++start_pos; + size_t end_pos = remain_source.find(end_mark, start_pos); + if (std::string::npos == end_pos) + break; + + // We have found the matching marks. First tokenize the pre-mask. Then add + // the marked part as a single field. Finally, loop back for the post-mark. + tokenize_append(pre_mark, delimiter, fields); + fields->push_back(remain_source.substr(start_pos, end_pos - start_pos)); + remain_source = remain_source.substr(end_pos + 1); + } + + return tokenize_append(remain_source, delimiter, fields); +} + +bool tokenize_first(const std::string& source, + const char delimiter, + std::string* token, + std::string* rest) { + // Find the first delimiter + size_t left_pos = source.find(delimiter); + if (left_pos == std::string::npos) { + return false; + } + + // Look for additional occurrances of delimiter. + size_t right_pos = left_pos + 1; + while (source[right_pos] == delimiter) { + right_pos++; + } + + *token = source.substr(0, left_pos); + *rest = source.substr(right_pos); + return true; +} + +std::string join(const std::vector& source, char delimiter) { + if (source.size() == 0) { + return std::string(); + } + // Find length of the string to be returned to pre-allocate memory. + size_t source_string_length = 0; + for (size_t i = 0; i < source.size(); ++i) { + source_string_length += source[i].length(); + } + + // Build the joined string. + std::string joined_string; + joined_string.reserve(source_string_length + source.size() - 1); + for (size_t i = 0; i < source.size(); ++i) { + if (i != 0) { + joined_string += delimiter; + } + joined_string += source[i]; + } + return joined_string; +} + +size_t split(const std::string& source, + char delimiter, + std::vector* fields) { + RTC_DCHECK(fields); + fields->clear(); + size_t last = 0; + for (size_t i = 0; i < source.length(); ++i) { + if (source[i] == delimiter) { + fields->push_back(source.substr(last, i - last)); + last = i + 1; + } + } + fields->push_back(source.substr(last, source.length() - last)); + return fields->size(); +} + +std::string ToString(const bool b) { + return b ? "true" : "false"; +} + +std::string ToString(const char* const s) { + return std::string(s); +} +std::string ToString(const std::string s) { + return s; +} + +std::string ToString(const short s) { + char buf[32]; + const int len = std::snprintf(&buf[0], arraysize(buf), "%hd", s); + RTC_DCHECK_LE(len, arraysize(buf)); + return std::string(&buf[0], len); +} +std::string ToString(const unsigned short s) { + char buf[32]; + const int len = std::snprintf(&buf[0], arraysize(buf), "%hu", s); + RTC_DCHECK_LE(len, arraysize(buf)); + return std::string(&buf[0], len); +} +std::string ToString(const int s) { + char buf[32]; + const int len = std::snprintf(&buf[0], arraysize(buf), "%d", s); + RTC_DCHECK_LE(len, arraysize(buf)); + return std::string(&buf[0], len); +} +std::string ToString(const unsigned int s) { + char buf[32]; + const int len = std::snprintf(&buf[0], arraysize(buf), "%u", s); + RTC_DCHECK_LE(len, arraysize(buf)); + return std::string(&buf[0], len); +} +std::string ToString(const long int s) { + char buf[32]; + const int len = std::snprintf(&buf[0], arraysize(buf), "%ld", s); + RTC_DCHECK_LE(len, arraysize(buf)); + return std::string(&buf[0], len); +} +std::string ToString(const unsigned long int s) { + char buf[32]; + const int len = std::snprintf(&buf[0], arraysize(buf), "%lu", s); + RTC_DCHECK_LE(len, arraysize(buf)); + return std::string(&buf[0], len); +} +std::string ToString(const long long int s) { + char buf[32]; + const int len = std::snprintf(&buf[0], arraysize(buf), "%lld", s); + RTC_DCHECK_LE(len, arraysize(buf)); + return std::string(&buf[0], len); +} +std::string ToString(const unsigned long long int s) { + char buf[32]; + const int len = std::snprintf(&buf[0], arraysize(buf), "%llu", s); + RTC_DCHECK_LE(len, arraysize(buf)); + return std::string(&buf[0], len); +} + +std::string ToString(const double d) { + char buf[32]; + const int len = std::snprintf(&buf[0], arraysize(buf), "%g", d); + RTC_DCHECK_LE(len, arraysize(buf)); + return std::string(&buf[0], len); +} + +std::string ToString(const long double d) { + char buf[32]; + const int len = std::snprintf(&buf[0], arraysize(buf), "%Lg", d); + RTC_DCHECK_LE(len, arraysize(buf)); + return std::string(&buf[0], len); +} + +std::string ToString(const void* const p) { + char buf[32]; + const int len = std::snprintf(&buf[0], arraysize(buf), "%p", p); + RTC_DCHECK_LE(len, arraysize(buf)); + return std::string(&buf[0], len); +} + +bool FromString(const std::string& s, bool* b) { + if (s == "false") { + *b = false; + return true; + } + if (s == "true") { + *b = true; + return true; + } + return false; +} + +} // namespace rtc diff --git a/webrtc/rtc_base/string_encode.h b/webrtc/rtc_base/string_encode.h new file mode 100644 index 0000000..c1401b9 --- /dev/null +++ b/webrtc/rtc_base/string_encode.h @@ -0,0 +1,154 @@ +/* + * Copyright 2004 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. + */ + +#ifndef RTC_BASE_STRING_ENCODE_H_ +#define RTC_BASE_STRING_ENCODE_H_ + +#include + +#include +#include +#include + +#include "absl/types/optional.h" +#include "rtc_base/checks.h" +#include "rtc_base/string_to_number.h" + +namespace rtc { + +////////////////////////////////////////////////////////////////////// +// String Encoding Utilities +////////////////////////////////////////////////////////////////////// + +std::string hex_encode(const std::string& str); +std::string hex_encode(const char* source, size_t srclen); +std::string hex_encode_with_delimiter(const char* source, + size_t srclen, + char delimiter); + +// hex_decode converts ascii hex to binary. +size_t hex_decode(char* buffer, + size_t buflen, + const char* source, + size_t srclen); + +// hex_decode, assuming that there is a delimiter between every byte +// pair. +// |delimiter| == 0 means no delimiter +// If the buffer is too short or the data is invalid, we return 0. +size_t hex_decode_with_delimiter(char* buffer, + size_t buflen, + const char* source, + size_t srclen, + char delimiter); + +// Helper functions for hex_decode. +size_t hex_decode(char* buffer, size_t buflen, const std::string& source); +size_t hex_decode_with_delimiter(char* buffer, + size_t buflen, + const std::string& source, + char delimiter); + +// Joins the source vector of strings into a single string, with each +// field in source being separated by delimiter. No trailing delimiter is added. +std::string join(const std::vector& source, char delimiter); + +// Splits the source string into multiple fields separated by delimiter, +// with duplicates of delimiter creating empty fields. +size_t split(const std::string& source, + char delimiter, + std::vector* fields); + +// Splits the source string into multiple fields separated by delimiter, +// with duplicates of delimiter ignored. Trailing delimiter ignored. +size_t tokenize(const std::string& source, + char delimiter, + std::vector* fields); + +// Tokenize, including the empty tokens. +size_t tokenize_with_empty_tokens(const std::string& source, + char delimiter, + std::vector* fields); + +// Tokenize and append the tokens to fields. Return the new size of fields. +size_t tokenize_append(const std::string& source, + char delimiter, + std::vector* fields); + +// Splits the source string into multiple fields separated by delimiter, with +// duplicates of delimiter ignored. Trailing delimiter ignored. A substring in +// between the start_mark and the end_mark is treated as a single field. Return +// the size of fields. For example, if source is "filename +// \"/Library/Application Support/media content.txt\"", delimiter is ' ', and +// the start_mark and end_mark are '"', this method returns two fields: +// "filename" and "/Library/Application Support/media content.txt". +size_t tokenize(const std::string& source, + char delimiter, + char start_mark, + char end_mark, + std::vector* fields); + +// Extract the first token from source as separated by delimiter, with +// duplicates of delimiter ignored. Return false if the delimiter could not be +// found, otherwise return true. +bool tokenize_first(const std::string& source, + const char delimiter, + std::string* token, + std::string* rest); + +// Convert arbitrary values to/from a string. +// TODO(jonasolsson): Remove these when absl::StrCat becomes available. +std::string ToString(bool b); + +std::string ToString(const char* s); +std::string ToString(std::string t); + +std::string ToString(short s); +std::string ToString(unsigned short s); +std::string ToString(int s); +std::string ToString(unsigned int s); +std::string ToString(long int s); +std::string ToString(unsigned long int s); +std::string ToString(long long int s); +std::string ToString(unsigned long long int s); + +std::string ToString(double t); +std::string ToString(long double t); + +std::string ToString(const void* p); + +template ::value && + !std::is_same::value, + int>::type = 0> +static bool FromString(const std::string& s, T* t) { + RTC_DCHECK(t); + absl::optional result = StringToNumber(s); + + if (result) + *t = *result; + + return result.has_value(); +} + +bool FromString(const std::string& s, bool* b); + +template +static inline T FromString(const std::string& str) { + T val; + FromString(str, &val); + return val; +} + +////////////////////////////////////////////////////////////////////// + +} // namespace rtc + +#endif // RTC_BASE_STRING_ENCODE_H__ diff --git a/webrtc/rtc_base/string_to_number.cc b/webrtc/rtc_base/string_to_number.cc new file mode 100644 index 0000000..351610f --- /dev/null +++ b/webrtc/rtc_base/string_to_number.cc @@ -0,0 +1,90 @@ +/* + * Copyright 2017 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 "rtc_base/string_to_number.h" + +#include + +#include +#include + +#include "rtc_base/checks.h" + +namespace rtc { +namespace string_to_number_internal { + +absl::optional ParseSigned(const char* str, int base) { + RTC_DCHECK(str); + if (isdigit(str[0]) || str[0] == '-') { + char* end = nullptr; + errno = 0; + const signed_type value = std::strtoll(str, &end, base); + if (end && *end == '\0' && errno == 0) { + return value; + } + } + return absl::nullopt; +} + +absl::optional ParseUnsigned(const char* str, int base) { + RTC_DCHECK(str); + if (isdigit(str[0]) || str[0] == '-') { + // Explicitly discard negative values. std::strtoull parsing causes unsigned + // wraparound. We cannot just reject values that start with -, though, since + // -0 is perfectly fine, as is -0000000000000000000000000000000. + const bool is_negative = str[0] == '-'; + char* end = nullptr; + errno = 0; + const unsigned_type value = std::strtoull(str, &end, base); + if (end && *end == '\0' && errno == 0 && (value == 0 || !is_negative)) { + return value; + } + } + return absl::nullopt; +} + +template +T StrToT(const char* str, char** str_end); + +template <> +inline float StrToT(const char* str, char** str_end) { + return std::strtof(str, str_end); +} + +template <> +inline double StrToT(const char* str, char** str_end) { + return std::strtod(str, str_end); +} + +template <> +inline long double StrToT(const char* str, char** str_end) { + return std::strtold(str, str_end); +} + +template +absl::optional ParseFloatingPoint(const char* str) { + RTC_DCHECK(str); + if (*str == '\0') + return absl::nullopt; + char* end = nullptr; + errno = 0; + const T value = StrToT(str, &end); + if (end && *end == '\0' && errno == 0) { + return value; + } + return absl::nullopt; +} + +template absl::optional ParseFloatingPoint(const char* str); +template absl::optional ParseFloatingPoint(const char* str); +template absl::optional ParseFloatingPoint(const char* str); + +} // namespace string_to_number_internal +} // namespace rtc diff --git a/webrtc/rtc_base/string_to_number.h b/webrtc/rtc_base/string_to_number.h new file mode 100644 index 0000000..4cb5215 --- /dev/null +++ b/webrtc/rtc_base/string_to_number.h @@ -0,0 +1,116 @@ +/* + * Copyright 2017 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. + */ + +#ifndef RTC_BASE_STRING_TO_NUMBER_H_ +#define RTC_BASE_STRING_TO_NUMBER_H_ + +#include +#include +#include + +#include "absl/types/optional.h" + +namespace rtc { + +// This file declares a family of functions to parse integers from strings. +// The standard C library functions either fail to indicate errors (atoi, etc.) +// or are a hassle to work with (strtol, sscanf, etc.). The standard C++ library +// functions (std::stoi, etc.) indicate errors by throwing exceptions, which +// are disabled in WebRTC. +// +// Integers are parsed using one of the following functions: +// absl::optional StringToNumber(const char* str, int base = 10); +// absl::optional StringToNumber(const std::string& str, +// int base = 10); +// +// These functions parse a value from the beginning of a string into one of the +// fundamental integer types, or returns an empty Optional if parsing +// failed. Values outside of the range supported by the type will be +// rejected. The strings must begin with a digit or a minus sign. No leading +// space nor trailing contents are allowed. +// By setting base to 0, one of octal, decimal or hexadecimal will be +// detected from the string's prefix (0, nothing or 0x, respectively). +// If non-zero, base can be set to a value between 2 and 36 inclusively. +// +// If desired, this interface could be extended with support for floating-point +// types. + +namespace string_to_number_internal { +// These must be (unsigned) long long, to match the signature of strto(u)ll. +using unsigned_type = unsigned long long; // NOLINT(runtime/int) +using signed_type = long long; // NOLINT(runtime/int) + +absl::optional ParseSigned(const char* str, int base); +absl::optional ParseUnsigned(const char* str, int base); + +template +absl::optional ParseFloatingPoint(const char* str); +} // namespace string_to_number_internal + +template +typename std::enable_if::value && std::is_signed::value, + absl::optional>::type +StringToNumber(const char* str, int base = 10) { + using string_to_number_internal::signed_type; + static_assert( + std::numeric_limits::max() <= + std::numeric_limits::max() && + std::numeric_limits::lowest() >= + std::numeric_limits::lowest(), + "StringToNumber only supports signed integers as large as long long int"); + absl::optional value = + string_to_number_internal::ParseSigned(str, base); + if (value && *value >= std::numeric_limits::lowest() && + *value <= std::numeric_limits::max()) { + return static_cast(*value); + } + return absl::nullopt; +} + +template +typename std::enable_if::value && + std::is_unsigned::value, + absl::optional>::type +StringToNumber(const char* str, int base = 10) { + using string_to_number_internal::unsigned_type; + static_assert(std::numeric_limits::max() <= + std::numeric_limits::max(), + "StringToNumber only supports unsigned integers as large as " + "unsigned long long int"); + absl::optional value = + string_to_number_internal::ParseUnsigned(str, base); + if (value && *value <= std::numeric_limits::max()) { + return static_cast(*value); + } + return absl::nullopt; +} + +template +typename std::enable_if::value, + absl::optional>::type +StringToNumber(const char* str, int base = 10) { + static_assert( + std::numeric_limits::max() <= std::numeric_limits::max(), + "StringToNumber only supports floating-point numbers as large " + "as long double"); + return string_to_number_internal::ParseFloatingPoint(str); +} + +// The std::string overloads only exists if there is a matching const char* +// version. +template +auto StringToNumber(const std::string& str, int base = 10) + -> decltype(StringToNumber(str.c_str(), base)) { + return StringToNumber(str.c_str(), base); +} + +} // namespace rtc + +#endif // RTC_BASE_STRING_TO_NUMBER_H_ diff --git a/webrtc/rtc_base/string_utils.cc b/webrtc/rtc_base/string_utils.cc new file mode 100644 index 0000000..1720c62 --- /dev/null +++ b/webrtc/rtc_base/string_utils.cc @@ -0,0 +1,53 @@ +/* + * Copyright 2004 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 "rtc_base/string_utils.h" + +namespace rtc { + +size_t strcpyn(char* buffer, + size_t buflen, + const char* source, + size_t srclen /* = SIZE_UNKNOWN */) { + if (buflen <= 0) + return 0; + + if (srclen == SIZE_UNKNOWN) { + srclen = strlen(source); + } + if (srclen >= buflen) { + srclen = buflen - 1; + } + memcpy(buffer, source, srclen); + buffer[srclen] = 0; + return srclen; +} + +static const char kWhitespace[] = " \n\r\t"; + +std::string string_trim(const std::string& s) { + std::string::size_type first = s.find_first_not_of(kWhitespace); + std::string::size_type last = s.find_last_not_of(kWhitespace); + + if (first == std::string::npos || last == std::string::npos) { + return std::string(""); + } + + return s.substr(first, last - first + 1); +} + +std::string ToHex(const int i) { + char buffer[50]; + snprintf(buffer, sizeof(buffer), "%x", i); + + return std::string(buffer); +} + +} // namespace rtc diff --git a/webrtc/rtc_base/string_utils.h b/webrtc/rtc_base/string_utils.h new file mode 100644 index 0000000..23c55cb --- /dev/null +++ b/webrtc/rtc_base/string_utils.h @@ -0,0 +1,93 @@ +/* + * Copyright 2004 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. + */ + +#ifndef RTC_BASE_STRING_UTILS_H_ +#define RTC_BASE_STRING_UTILS_H_ + +#include +#include +#include +#include + +#if defined(WEBRTC_WIN) +#include +#include +#include + +#endif // WEBRTC_WIN + +#if defined(WEBRTC_POSIX) +#include +#include +#endif // WEBRTC_POSIX + +#include + +namespace rtc { + +const size_t SIZE_UNKNOWN = static_cast(-1); + +// Safe version of strncpy that always nul-terminate. +size_t strcpyn(char* buffer, + size_t buflen, + const char* source, + size_t srclen = SIZE_UNKNOWN); + +/////////////////////////////////////////////////////////////////////////////// +// UTF helpers (Windows only) +/////////////////////////////////////////////////////////////////////////////// + +#if defined(WEBRTC_WIN) + +inline std::wstring ToUtf16(const char* utf8, size_t len) { + if (len == 0) + return std::wstring(); + int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), + nullptr, 0); + std::wstring ws(len16, 0); + ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), &*ws.begin(), + len16); + return ws; +} + +inline std::wstring ToUtf16(const std::string& str) { + return ToUtf16(str.data(), str.length()); +} + +inline std::string ToUtf8(const wchar_t* wide, size_t len) { + if (len == 0) + return std::string(); + int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), + nullptr, 0, nullptr, nullptr); + std::string ns(len8, 0); + ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), &*ns.begin(), + len8, nullptr, nullptr); + return ns; +} + +inline std::string ToUtf8(const wchar_t* wide) { + return ToUtf8(wide, wcslen(wide)); +} + +inline std::string ToUtf8(const std::wstring& wstr) { + return ToUtf8(wstr.data(), wstr.length()); +} + +#endif // WEBRTC_WIN + +// Remove leading and trailing whitespaces. +std::string string_trim(const std::string& s); + +// TODO(jonasolsson): replace with absl::Hex when that becomes available. +std::string ToHex(const int i); + +} // namespace rtc + +#endif // RTC_BASE_STRING_UTILS_H_ diff --git a/webrtc/rtc_base/strings/string_builder.cc b/webrtc/rtc_base/strings/string_builder.cc new file mode 100644 index 0000000..caa931b --- /dev/null +++ b/webrtc/rtc_base/strings/string_builder.cc @@ -0,0 +1,141 @@ +/* + * Copyright 2018 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 "rtc_base/strings/string_builder.h" + +#include + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace rtc { + +SimpleStringBuilder::SimpleStringBuilder(rtc::ArrayView buffer) + : buffer_(buffer) { + buffer_[0] = '\0'; + RTC_DCHECK(IsConsistent()); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(const char* str) { + return Append(str, strlen(str)); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(char ch) { + return Append(&ch, 1); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(const std::string& str) { + return Append(str.c_str(), str.length()); +} + +// Numeric conversion routines. +// +// We use std::[v]snprintf instead of std::to_string because: +// * std::to_string relies on the current locale for formatting purposes, +// and therefore concurrent calls to std::to_string from multiple threads +// may result in partial serialization of calls +// * snprintf allows us to print the number directly into our buffer. +// * avoid allocating a std::string (potential heap alloc). +// TODO(tommi): Switch to std::to_chars in C++17. + +SimpleStringBuilder& SimpleStringBuilder::operator<<(int i) { + return AppendFormat("%d", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(unsigned i) { + return AppendFormat("%u", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(long i) { // NOLINT + return AppendFormat("%ld", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(long long i) { // NOLINT + return AppendFormat("%lld", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<( + unsigned long i) { // NOLINT + return AppendFormat("%lu", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<( + unsigned long long i) { // NOLINT + return AppendFormat("%llu", i); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(float f) { + return AppendFormat("%g", f); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(double f) { + return AppendFormat("%g", f); +} + +SimpleStringBuilder& SimpleStringBuilder::operator<<(long double f) { + return AppendFormat("%Lg", f); +} + +SimpleStringBuilder& SimpleStringBuilder::AppendFormat(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + const int len = + std::vsnprintf(&buffer_[size_], buffer_.size() - size_, fmt, args); + if (len >= 0) { + const size_t chars_added = rtc::SafeMin(len, buffer_.size() - 1 - size_); + size_ += chars_added; + RTC_DCHECK_EQ(len, chars_added) << "Buffer size was insufficient"; + } else { + // This should never happen, but we're paranoid, so re-write the + // terminator in case vsnprintf() overwrote it. + RTC_NOTREACHED(); + buffer_[size_] = '\0'; + } + va_end(args); + RTC_DCHECK(IsConsistent()); + return *this; +} + +SimpleStringBuilder& SimpleStringBuilder::Append(const char* str, + size_t length) { + RTC_DCHECK_LT(size_ + length, buffer_.size()) + << "Buffer size was insufficient"; + const size_t chars_added = rtc::SafeMin(length, buffer_.size() - size_ - 1); + memcpy(&buffer_[size_], str, chars_added); + size_ += chars_added; + buffer_[size_] = '\0'; + RTC_DCHECK(IsConsistent()); + return *this; +} + +StringBuilder& StringBuilder::AppendFormat(const char* fmt, ...) { + va_list args, copy; + va_start(args, fmt); + va_copy(copy, args); + const int predicted_length = std::vsnprintf(nullptr, 0, fmt, copy); + va_end(copy); + + RTC_DCHECK_GE(predicted_length, 0); + if (predicted_length > 0) { + const size_t size = str_.size(); + str_.resize(size + predicted_length); + // Pass "+ 1" to vsnprintf to include space for the '\0'. + const int actual_length = + std::vsnprintf(&str_[size], predicted_length + 1, fmt, args); + RTC_DCHECK_GE(actual_length, 0); + } + va_end(args); + return *this; +} + +} // namespace rtc diff --git a/webrtc/rtc_base/strings/string_builder.h b/webrtc/rtc_base/strings/string_builder.h new file mode 100644 index 0000000..e528cf2 --- /dev/null +++ b/webrtc/rtc_base/strings/string_builder.h @@ -0,0 +1,175 @@ +/* + * Copyright 2018 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. + */ + +#ifndef RTC_BASE_STRINGS_STRING_BUILDER_H_ +#define RTC_BASE_STRINGS_STRING_BUILDER_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "rtc_base/string_encode.h" + +namespace rtc { + +// This is a minimalistic string builder class meant to cover the most cases of +// when you might otherwise be tempted to use a stringstream (discouraged for +// anything except logging). It uses a fixed-size buffer provided by the caller +// and concatenates strings and numbers into it, allowing the results to be +// read via |str()|. +class SimpleStringBuilder { + public: + explicit SimpleStringBuilder(rtc::ArrayView buffer); + SimpleStringBuilder(const SimpleStringBuilder&) = delete; + SimpleStringBuilder& operator=(const SimpleStringBuilder&) = delete; + + SimpleStringBuilder& operator<<(const char* str); + SimpleStringBuilder& operator<<(char ch); + SimpleStringBuilder& operator<<(const std::string& str); + SimpleStringBuilder& operator<<(int i); + SimpleStringBuilder& operator<<(unsigned i); + SimpleStringBuilder& operator<<(long i); // NOLINT + SimpleStringBuilder& operator<<(long long i); // NOLINT + SimpleStringBuilder& operator<<(unsigned long i); // NOLINT + SimpleStringBuilder& operator<<(unsigned long long i); // NOLINT + SimpleStringBuilder& operator<<(float f); + SimpleStringBuilder& operator<<(double f); + SimpleStringBuilder& operator<<(long double f); + + // Returns a pointer to the built string. The name |str()| is borrowed for + // compatibility reasons as we replace usage of stringstream throughout the + // code base. + const char* str() const { return buffer_.data(); } + + // Returns the length of the string. The name |size()| is picked for STL + // compatibility reasons. + size_t size() const { return size_; } + +// Allows appending a printf style formatted string. +#if defined(__GNUC__) + __attribute__((__format__(__printf__, 2, 3))) +#endif + SimpleStringBuilder& + AppendFormat(const char* fmt, ...); + + // An alternate way from operator<<() to append a string. This variant is + // slightly more efficient when the length of the string to append, is known. + SimpleStringBuilder& Append(const char* str, size_t length); + + private: + bool IsConsistent() const { + return size_ <= buffer_.size() - 1 && buffer_[size_] == '\0'; + } + + // An always-zero-terminated fixed-size buffer that we write to. The fixed + // size allows the buffer to be stack allocated, which helps performance. + // Having a fixed size is furthermore useful to avoid unnecessary resizing + // while building it. + const rtc::ArrayView buffer_; + + // Represents the number of characters written to the buffer. + // This does not include the terminating '\0'. + size_t size_ = 0; +}; + +// A string builder that supports dynamic resizing while building a string. +// The class is based around an instance of std::string and allows moving +// ownership out of the class once the string has been built. +// Note that this class uses the heap for allocations, so SimpleStringBuilder +// might be more efficient for some use cases. +class StringBuilder { + public: + StringBuilder() {} + explicit StringBuilder(absl::string_view s) : str_(s) {} + + // TODO(tommi): Support construction from StringBuilder? + StringBuilder(const StringBuilder&) = delete; + StringBuilder& operator=(const StringBuilder&) = delete; + + StringBuilder& operator<<(const absl::string_view str) { + str_.append(str.data(), str.length()); + return *this; + } + + StringBuilder& operator<<(char c) = delete; + + StringBuilder& operator<<(int i) { + str_ += rtc::ToString(i); + return *this; + } + + StringBuilder& operator<<(unsigned i) { + str_ += rtc::ToString(i); + return *this; + } + + StringBuilder& operator<<(long i) { // NOLINT + str_ += rtc::ToString(i); + return *this; + } + + StringBuilder& operator<<(long long i) { // NOLINT + str_ += rtc::ToString(i); + return *this; + } + + StringBuilder& operator<<(unsigned long i) { // NOLINT + str_ += rtc::ToString(i); + return *this; + } + + StringBuilder& operator<<(unsigned long long i) { // NOLINT + str_ += rtc::ToString(i); + return *this; + } + + StringBuilder& operator<<(float f) { + str_ += rtc::ToString(f); + return *this; + } + + StringBuilder& operator<<(double f) { + str_ += rtc::ToString(f); + return *this; + } + + StringBuilder& operator<<(long double f) { + str_ += rtc::ToString(f); + return *this; + } + + const std::string& str() const { return str_; } + + void Clear() { str_.clear(); } + + size_t size() const { return str_.size(); } + + std::string Release() { + std::string ret = std::move(str_); + str_.clear(); + return ret; + } + + // Allows appending a printf style formatted string. + StringBuilder& AppendFormat(const char* fmt, ...) +#if defined(__GNUC__) + __attribute__((__format__(__printf__, 2, 3))) +#endif + ; + + private: + std::string str_; +}; + +} // namespace rtc + +#endif // RTC_BASE_STRINGS_STRING_BUILDER_H_ diff --git a/webrtc/rtc_base/swap_queue.h b/webrtc/rtc_base/swap_queue.h new file mode 100644 index 0000000..9eac49a --- /dev/null +++ b/webrtc/rtc_base/swap_queue.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2015 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. + */ + +#ifndef RTC_BASE_SWAP_QUEUE_H_ +#define RTC_BASE_SWAP_QUEUE_H_ + +#include + +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/system/unused.h" + +namespace webrtc { + +namespace internal { + +// (Internal; please don't use outside this file.) +template +bool NoopSwapQueueItemVerifierFunction(const T&) { + return true; +} + +} // namespace internal + +// Functor to use when supplying a verifier function for the queue. +template +class SwapQueueItemVerifier { + public: + bool operator()(const T& t) const { return QueueItemVerifierFunction(t); } +}; + +// This class is a fixed-size queue. A single producer calls Insert() to insert +// an element of type T at the back of the queue, and a single consumer calls +// Remove() to remove an element from the front of the queue. It's safe for the +// producer and the consumer to access the queue concurrently, from different +// threads. +// +// To avoid the construction, copying, and destruction of Ts that a naive +// queue implementation would require, for each "full" T passed from +// producer to consumer, SwapQueue passes an "empty" T in the other +// direction (an "empty" T is one that contains nothing of value for the +// consumer). This bidirectional movement is implemented with swap(). +// +// // Create queue: +// Bottle proto(568); // Prepare an empty Bottle. Heap allocates space for +// // 568 ml. +// SwapQueue q(N, proto); // Init queue with N copies of proto. +// // Each copy allocates on the heap. +// // Producer pseudo-code: +// Bottle b(568); // Prepare an empty Bottle. Heap allocates space for 568 ml. +// loop { +// b.Fill(amount); // Where amount <= 568 ml. +// q.Insert(&b); // Swap our full Bottle for an empty one from q. +// } +// +// // Consumer pseudo-code: +// Bottle b(568); // Prepare an empty Bottle. Heap allocates space for 568 ml. +// loop { +// q.Remove(&b); // Swap our empty Bottle for the next-in-line full Bottle. +// Drink(&b); +// } +// +// For a well-behaved Bottle class, there are no allocations in the +// producer, since it just fills an empty Bottle that's already large +// enough; no deallocations in the consumer, since it returns each empty +// Bottle to the queue after having drunk it; and no copies along the +// way, since the queue uses swap() everywhere to move full Bottles in +// one direction and empty ones in the other. +template > +class SwapQueue { + public: + // Creates a queue of size size and fills it with default constructed Ts. + explicit SwapQueue(size_t size) : queue_(size) { + RTC_DCHECK(VerifyQueueSlots()); + } + + // Same as above and accepts an item verification functor. + SwapQueue(size_t size, const QueueItemVerifier& queue_item_verifier) + : queue_item_verifier_(queue_item_verifier), queue_(size) { + RTC_DCHECK(VerifyQueueSlots()); + } + + // Creates a queue of size size and fills it with copies of prototype. + SwapQueue(size_t size, const T& prototype) : queue_(size, prototype) { + RTC_DCHECK(VerifyQueueSlots()); + } + + // Same as above and accepts an item verification functor. + SwapQueue(size_t size, + const T& prototype, + const QueueItemVerifier& queue_item_verifier) + : queue_item_verifier_(queue_item_verifier), queue_(size, prototype) { + RTC_DCHECK(VerifyQueueSlots()); + } + + // Resets the queue to have zero content while maintaining the queue size. + // Just like Remove(), this can only be called (safely) from the + // consumer. + void Clear() { + // Drop all non-empty elements by resetting num_elements_ and incrementing + // next_read_index_ by the previous value of num_elements_. Relaxed memory + // ordering is sufficient since the dropped elements are not accessed. + next_read_index_ += std::atomic_exchange_explicit( + &num_elements_, size_t{0}, std::memory_order_relaxed); + if (next_read_index_ >= queue_.size()) { + next_read_index_ -= queue_.size(); + } + + RTC_DCHECK_LT(next_read_index_, queue_.size()); + } + + // Inserts a "full" T at the back of the queue by swapping *input with an + // "empty" T from the queue. + // Returns true if the item was inserted or false if not (the queue was full). + // When specified, the T given in *input must pass the ItemVerifier() test. + // The contents of *input after the call are then also guaranteed to pass the + // ItemVerifier() test. + bool Insert(T* input) RTC_WARN_UNUSED_RESULT { + RTC_DCHECK(input); + + RTC_DCHECK(queue_item_verifier_(*input)); + + // Load the value of num_elements_. Acquire memory ordering prevents reads + // and writes to queue_[next_write_index_] to be reordered to before the + // load. (That element might be accessed by a concurrent call to Remove() + // until the load finishes.) + if (std::atomic_load_explicit(&num_elements_, std::memory_order_acquire) == + queue_.size()) { + return false; + } + + using std::swap; + swap(*input, queue_[next_write_index_]); + + // Increment the value of num_elements_ to account for the inserted element. + // Release memory ordering prevents the reads and writes to + // queue_[next_write_index_] to be reordered to after the increment. (Once + // the increment has finished, Remove() might start accessing that element.) + const size_t old_num_elements = std::atomic_fetch_add_explicit( + &num_elements_, size_t{1}, std::memory_order_release); + + ++next_write_index_; + if (next_write_index_ == queue_.size()) { + next_write_index_ = 0; + } + + RTC_DCHECK_LT(next_write_index_, queue_.size()); + RTC_DCHECK_LT(old_num_elements, queue_.size()); + + return true; + } + + // Removes the frontmost "full" T from the queue by swapping it with + // the "empty" T in *output. + // Returns true if an item could be removed or false if not (the queue was + // empty). When specified, The T given in *output must pass the ItemVerifier() + // test and the contents of *output after the call are then also guaranteed to + // pass the ItemVerifier() test. + bool Remove(T* output) RTC_WARN_UNUSED_RESULT { + RTC_DCHECK(output); + + RTC_DCHECK(queue_item_verifier_(*output)); + + // Load the value of num_elements_. Acquire memory ordering prevents reads + // and writes to queue_[next_read_index_] to be reordered to before the + // load. (That element might be accessed by a concurrent call to Insert() + // until the load finishes.) + if (std::atomic_load_explicit(&num_elements_, std::memory_order_acquire) == + 0) { + return false; + } + + using std::swap; + swap(*output, queue_[next_read_index_]); + + // Decrement the value of num_elements_ to account for the removed element. + // Release memory ordering prevents the reads and writes to + // queue_[next_write_index_] to be reordered to after the decrement. (Once + // the decrement has finished, Insert() might start accessing that element.) + std::atomic_fetch_sub_explicit(&num_elements_, size_t{1}, + std::memory_order_release); + + ++next_read_index_; + if (next_read_index_ == queue_.size()) { + next_read_index_ = 0; + } + + RTC_DCHECK_LT(next_read_index_, queue_.size()); + + return true; + } + + // Returns the current number of elements in the queue. Since elements may be + // concurrently added to the queue, the caller must treat this as a lower + // bound, not an exact count. + // May only be called by the consumer. + size_t SizeAtLeast() const { + // Acquire memory ordering ensures that we wait for the producer to finish + // inserting any element in progress. + return std::atomic_load_explicit(&num_elements_, std::memory_order_acquire); + } + + private: + // Verify that the queue slots complies with the ItemVerifier test. This + // function is not thread-safe and can only be used in the constructors. + bool VerifyQueueSlots() { + for (const auto& v : queue_) { + RTC_DCHECK(queue_item_verifier_(v)); + } + return true; + } + + // TODO(peah): Change this to use std::function() once we can use C++11 std + // lib. + QueueItemVerifier queue_item_verifier_; + + // Only accessed by the single producer. + size_t next_write_index_ = 0; + + // Only accessed by the single consumer. + size_t next_read_index_ = 0; + + // Accessed by both the producer and the consumer and used for synchronization + // between them. + std::atomic num_elements_{0}; + + // The elements of the queue are acced by both the producer and the consumer, + // mediated by num_elements_. queue_.size() is constant. + std::vector queue_; + + SwapQueue(const SwapQueue&) = delete; + SwapQueue& operator=(const SwapQueue&) = delete; +}; + +} // namespace webrtc + +#endif // RTC_BASE_SWAP_QUEUE_H_ diff --git a/webrtc/rtc_base/synchronization/BUILD.gn b/webrtc/rtc_base/synchronization/BUILD.gn new file mode 100644 index 0000000..a79a048 --- /dev/null +++ b/webrtc/rtc_base/synchronization/BUILD.gn @@ -0,0 +1,138 @@ +# Copyright (c) 2018 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. + +import("../../webrtc.gni") +if (is_android) { + import("//build/config/android/config.gni") + import("//build/config/android/rules.gni") +} + +rtc_library("yield") { + sources = [ + "yield.cc", + "yield.h", + ] + deps = [] +} + +rtc_library("mutex") { + sources = [ + "mutex.cc", + "mutex.h", + "mutex_critical_section.h", + "mutex_pthread.h", + ] + if (rtc_use_absl_mutex) { + sources += [ "mutex_abseil.h" ] + } + + deps = [ + ":yield", + "..:checks", + "..:macromagic", + "..:platform_thread_types", + "../system:unused", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ] + if (rtc_use_absl_mutex) { + absl_deps += [ "//third_party/abseil-cpp/absl/synchronization" ] + } +} + +rtc_library("rw_lock_wrapper") { + public = [ "rw_lock_wrapper.h" ] + sources = [ "rw_lock_wrapper.cc" ] + deps = [ "..:macromagic" ] + if (is_win) { + sources += [ + "rw_lock_win.cc", + "rw_lock_win.h", + ] + deps += [ "..:logging" ] + } else { + sources += [ + "rw_lock_posix.cc", + "rw_lock_posix.h", + ] + } +} + +rtc_library("sequence_checker") { + sources = [ + "sequence_checker.cc", + "sequence_checker.h", + ] + deps = [ + ":mutex", + "..:checks", + "..:criticalsection", + "..:macromagic", + "..:platform_thread_types", + "..:stringutils", + "../../api/task_queue", + "../system:rtc_export", + ] +} + +rtc_library("yield_policy") { + sources = [ + "yield_policy.cc", + "yield_policy.h", + ] + deps = [ "..:checks" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/base:config", + "//third_party/abseil-cpp/absl/base:core_headers", + ] +} + +if (rtc_include_tests) { + rtc_library("synchronization_unittests") { + testonly = true + sources = [ + "mutex_unittest.cc", + "yield_policy_unittest.cc", + ] + deps = [ + ":mutex", + ":yield", + ":yield_policy", + "..:checks", + "..:macromagic", + "..:rtc_base", + "..:rtc_event", + "../../test:test_support", + "//third_party/google_benchmark", + ] + } + + rtc_library("mutex_benchmark") { + testonly = true + sources = [ "mutex_benchmark.cc" ] + deps = [ + ":mutex", + "../system:unused", + "//third_party/google_benchmark", + ] + } + + rtc_library("sequence_checker_unittests") { + testonly = true + + sources = [ "sequence_checker_unittest.cc" ] + deps = [ + ":sequence_checker", + "..:checks", + "..:rtc_base_approved", + "..:task_queue_for_test", + "../../api:function_view", + "../../test:test_main", + "../../test:test_support", + ] + } +} diff --git a/webrtc/rtc_base/synchronization/mutex.cc b/webrtc/rtc_base/synchronization/mutex.cc new file mode 100644 index 0000000..6c2d6ff --- /dev/null +++ b/webrtc/rtc_base/synchronization/mutex.cc @@ -0,0 +1,39 @@ +/* + * Copyright 2020 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 "rtc_base/synchronization/mutex.h" + +#include "rtc_base/checks.h" +#include "rtc_base/synchronization/yield.h" + +namespace webrtc { + +#if !defined(WEBRTC_ABSL_MUTEX) +void GlobalMutex::Lock() { + while (mutex_locked_.exchange(1)) { + YieldCurrentThread(); + } +} + +void GlobalMutex::Unlock() { + int old = mutex_locked_.exchange(0); + RTC_DCHECK_EQ(old, 1) << "Unlock called without calling Lock first"; +} + +GlobalMutexLock::GlobalMutexLock(GlobalMutex* mutex) : mutex_(mutex) { + mutex_->Lock(); +} + +GlobalMutexLock::~GlobalMutexLock() { + mutex_->Unlock(); +} +#endif // #if !defined(WEBRTC_ABSL_MUTEX) + +} // namespace webrtc diff --git a/webrtc/rtc_base/synchronization/mutex.h b/webrtc/rtc_base/synchronization/mutex.h new file mode 100644 index 0000000..620fe74 --- /dev/null +++ b/webrtc/rtc_base/synchronization/mutex.h @@ -0,0 +1,108 @@ +/* + * Copyright 2020 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. + */ + +#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_H_ +#define RTC_BASE_SYNCHRONIZATION_MUTEX_H_ + +#include + +#include "absl/base/const_init.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/unused.h" +#include "rtc_base/thread_annotations.h" + +#if defined(WEBRTC_ABSL_MUTEX) +#include "rtc_base/synchronization/mutex_abseil.h" // nogncheck +#elif defined(WEBRTC_WIN) +#include "rtc_base/synchronization/mutex_critical_section.h" +#elif defined(WEBRTC_POSIX) +#include "rtc_base/synchronization/mutex_pthread.h" +#else +#error Unsupported platform. +#endif + +namespace webrtc { + +// The Mutex guarantees exclusive access and aims to follow Abseil semantics +// (i.e. non-reentrant etc). +class RTC_LOCKABLE Mutex final { + public: + Mutex() = default; + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; + + void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { + impl_.Lock(); + } + RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) { + return impl_.TryLock(); + } + void Unlock() RTC_UNLOCK_FUNCTION() { + impl_.Unlock(); + } + + private: + MutexImpl impl_; +}; + +// MutexLock, for serializing execution through a scope. +class RTC_SCOPED_LOCKABLE MutexLock final { + public: + MutexLock(const MutexLock&) = delete; + MutexLock& operator=(const MutexLock&) = delete; + + explicit MutexLock(Mutex* mutex) RTC_EXCLUSIVE_LOCK_FUNCTION(mutex) + : mutex_(mutex) { + mutex->Lock(); + } + ~MutexLock() RTC_UNLOCK_FUNCTION() { mutex_->Unlock(); } + + private: + Mutex* mutex_; +}; + +// A mutex used to protect global variables. Do NOT use for other purposes. +#if defined(WEBRTC_ABSL_MUTEX) +using GlobalMutex = absl::Mutex; +using GlobalMutexLock = absl::MutexLock; +#else +class RTC_LOCKABLE GlobalMutex final { + public: + GlobalMutex(const GlobalMutex&) = delete; + GlobalMutex& operator=(const GlobalMutex&) = delete; + + constexpr explicit GlobalMutex(absl::ConstInitType /*unused*/) + : mutex_locked_(0) {} + + void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION(); + void Unlock() RTC_UNLOCK_FUNCTION(); + + private: + std::atomic mutex_locked_; // 0 means lock not taken, 1 means taken. +}; + +// GlobalMutexLock, for serializing execution through a scope. +class RTC_SCOPED_LOCKABLE GlobalMutexLock final { + public: + GlobalMutexLock(const GlobalMutexLock&) = delete; + GlobalMutexLock& operator=(const GlobalMutexLock&) = delete; + + explicit GlobalMutexLock(GlobalMutex* mutex) + RTC_EXCLUSIVE_LOCK_FUNCTION(mutex_); + ~GlobalMutexLock() RTC_UNLOCK_FUNCTION(); + + private: + GlobalMutex* mutex_; +}; +#endif // if defined(WEBRTC_ABSL_MUTEX) + +} // namespace webrtc + +#endif // RTC_BASE_SYNCHRONIZATION_MUTEX_H_ diff --git a/webrtc/rtc_base/synchronization/mutex_critical_section.h b/webrtc/rtc_base/synchronization/mutex_critical_section.h new file mode 100644 index 0000000..d206794 --- /dev/null +++ b/webrtc/rtc_base/synchronization/mutex_critical_section.h @@ -0,0 +1,54 @@ +/* + * Copyright 2020 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. + */ + +#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_CRITICAL_SECTION_H_ +#define RTC_BASE_SYNCHRONIZATION_MUTEX_CRITICAL_SECTION_H_ + +#if defined(WEBRTC_WIN) +// clang-format off +// clang formating would change include order. + +// Include winsock2.h before including to maintain consistency with +// win32.h. To include win32.h directly, it must be broken out into its own +// build target. +#include +#include +#include // must come after windows headers. +// clang-format on + +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class RTC_LOCKABLE MutexImpl final { + public: + MutexImpl() { InitializeCriticalSection(&critical_section_); } + MutexImpl(const MutexImpl&) = delete; + MutexImpl& operator=(const MutexImpl&) = delete; + ~MutexImpl() { DeleteCriticalSection(&critical_section_); } + + void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { + EnterCriticalSection(&critical_section_); + } + RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) { + return TryEnterCriticalSection(&critical_section_) != FALSE; + } + void Unlock() RTC_UNLOCK_FUNCTION() { + LeaveCriticalSection(&critical_section_); + } + + private: + CRITICAL_SECTION critical_section_; +}; + +} // namespace webrtc + +#endif // #if defined(WEBRTC_WIN) +#endif // RTC_BASE_SYNCHRONIZATION_MUTEX_CRITICAL_SECTION_H_ diff --git a/webrtc/rtc_base/synchronization/mutex_pthread.h b/webrtc/rtc_base/synchronization/mutex_pthread.h new file mode 100644 index 0000000..c9496e7 --- /dev/null +++ b/webrtc/rtc_base/synchronization/mutex_pthread.h @@ -0,0 +1,53 @@ +/* + * Copyright 2020 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. + */ + +#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_PTHREAD_H_ +#define RTC_BASE_SYNCHRONIZATION_MUTEX_PTHREAD_H_ + +#if defined(WEBRTC_POSIX) + +#include +#if defined(WEBRTC_MAC) +#include +#endif + +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class RTC_LOCKABLE MutexImpl final { + public: + MutexImpl() { + pthread_mutexattr_t mutex_attribute; + pthread_mutexattr_init(&mutex_attribute); +#if defined(WEBRTC_MAC) + pthread_mutexattr_setpolicy_np(&mutex_attribute, + _PTHREAD_MUTEX_POLICY_FIRSTFIT); +#endif + pthread_mutex_init(&mutex_, &mutex_attribute); + pthread_mutexattr_destroy(&mutex_attribute); + } + MutexImpl(const MutexImpl&) = delete; + MutexImpl& operator=(const MutexImpl&) = delete; + ~MutexImpl() { pthread_mutex_destroy(&mutex_); } + + void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { pthread_mutex_lock(&mutex_); } + RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) { + return pthread_mutex_trylock(&mutex_) == 0; + } + void Unlock() RTC_UNLOCK_FUNCTION() { pthread_mutex_unlock(&mutex_); } + + private: + pthread_mutex_t mutex_; +}; + +} // namespace webrtc +#endif // #if defined(WEBRTC_POSIX) +#endif // RTC_BASE_SYNCHRONIZATION_MUTEX_PTHREAD_H_ diff --git a/webrtc/system_wrappers/source/rw_lock_posix.cc b/webrtc/rtc_base/synchronization/rw_lock_posix.cc similarity index 90% rename from webrtc/system_wrappers/source/rw_lock_posix.cc rename to webrtc/rtc_base/synchronization/rw_lock_posix.cc index cdcb7fb..15ef3d7 100644 --- a/webrtc/system_wrappers/source/rw_lock_posix.cc +++ b/webrtc/rtc_base/synchronization/rw_lock_posix.cc @@ -8,12 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/system_wrappers/source/rw_lock_posix.h" +#include "rtc_base/synchronization/rw_lock_posix.h" + +#include namespace webrtc { -RWLockPosix::RWLockPosix() : lock_() { -} +RWLockPosix::RWLockPosix() : lock_() {} RWLockPosix::~RWLockPosix() { pthread_rwlock_destroy(&lock_); diff --git a/webrtc/system_wrappers/source/rw_lock_posix.h b/webrtc/rtc_base/synchronization/rw_lock_posix.h similarity index 76% rename from webrtc/system_wrappers/source/rw_lock_posix.h rename to webrtc/rtc_base/synchronization/rw_lock_posix.h index 0ce7305..a103fe7 100644 --- a/webrtc/system_wrappers/source/rw_lock_posix.h +++ b/webrtc/rtc_base/synchronization/rw_lock_posix.h @@ -8,14 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_RW_LOCK_POSIX_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_RW_LOCK_POSIX_H_ - -#include "webrtc/system_wrappers/include/rw_lock_wrapper.h" -#include "webrtc/typedefs.h" +#ifndef RTC_BASE_SYNCHRONIZATION_RW_LOCK_POSIX_H_ +#define RTC_BASE_SYNCHRONIZATION_RW_LOCK_POSIX_H_ #include +#include "rtc_base/synchronization/rw_lock_wrapper.h" + namespace webrtc { class RWLockPosix : public RWLockWrapper { @@ -38,4 +37,4 @@ class RWLockPosix : public RWLockWrapper { } // namespace webrtc -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_RW_LOCK_POSIX_H_ +#endif // RTC_BASE_SYNCHRONIZATION_RW_LOCK_POSIX_H_ diff --git a/webrtc/rtc_base/synchronization/rw_lock_win.cc b/webrtc/rtc_base/synchronization/rw_lock_win.cc new file mode 100644 index 0000000..3274c78 --- /dev/null +++ b/webrtc/rtc_base/synchronization/rw_lock_win.cc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2012 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 "rtc_base/synchronization/rw_lock_win.h" + +#include "rtc_base/logging.h" + +namespace webrtc { + +RWLockWin::RWLockWin() { + InitializeSRWLock(&lock_); +} + +RWLockWin* RWLockWin::Create() { + return new RWLockWin(); +} + +void RWLockWin::AcquireLockExclusive() { + AcquireSRWLockExclusive(&lock_); +} + +void RWLockWin::ReleaseLockExclusive() { + ReleaseSRWLockExclusive(&lock_); +} + +void RWLockWin::AcquireLockShared() { + AcquireSRWLockShared(&lock_); +} + +void RWLockWin::ReleaseLockShared() { + ReleaseSRWLockShared(&lock_); +} + +} // namespace webrtc diff --git a/webrtc/system_wrappers/source/rw_lock_win.h b/webrtc/rtc_base/synchronization/rw_lock_win.h similarity index 59% rename from webrtc/system_wrappers/source/rw_lock_win.h rename to webrtc/rtc_base/synchronization/rw_lock_win.h index c279eab..43bde1d 100644 --- a/webrtc/system_wrappers/source/rw_lock_win.h +++ b/webrtc/rtc_base/synchronization/rw_lock_win.h @@ -8,33 +8,31 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_RW_LOCK_WIN_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_RW_LOCK_WIN_H_ - -#include "webrtc/system_wrappers/include/rw_lock_wrapper.h" +#ifndef RTC_BASE_SYNCHRONIZATION_RW_LOCK_WIN_H_ +#define RTC_BASE_SYNCHRONIZATION_RW_LOCK_WIN_H_ #include +#include "rtc_base/synchronization/rw_lock_wrapper.h" + namespace webrtc { class RWLockWin : public RWLockWrapper { public: static RWLockWin* Create(); - ~RWLockWin() {} - virtual void AcquireLockExclusive(); - virtual void ReleaseLockExclusive(); + void AcquireLockExclusive() override; + void ReleaseLockExclusive() override; - virtual void AcquireLockShared(); - virtual void ReleaseLockShared(); + void AcquireLockShared() override; + void ReleaseLockShared() override; private: RWLockWin(); - static bool LoadModule(); SRWLOCK lock_; }; } // namespace webrtc -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_RW_LOCK_WIN_H_ +#endif // RTC_BASE_SYNCHRONIZATION_RW_LOCK_WIN_H_ diff --git a/webrtc/system_wrappers/source/rw_lock.cc b/webrtc/rtc_base/synchronization/rw_lock_wrapper.cc similarity index 58% rename from webrtc/system_wrappers/source/rw_lock.cc rename to webrtc/rtc_base/synchronization/rw_lock_wrapper.cc index ff44896..fb46419 100644 --- a/webrtc/system_wrappers/source/rw_lock.cc +++ b/webrtc/rtc_base/synchronization/rw_lock_wrapper.cc @@ -8,27 +8,19 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/system_wrappers/include/rw_lock_wrapper.h" - -#include +#include "rtc_base/synchronization/rw_lock_wrapper.h" #if defined(_WIN32) -#include "webrtc/system_wrappers/source/rw_lock_generic.h" -#include "webrtc/system_wrappers/source/rw_lock_win.h" +#include "rtc_base/synchronization/rw_lock_win.h" #else -#include "webrtc/system_wrappers/source/rw_lock_posix.h" +#include "rtc_base/synchronization/rw_lock_posix.h" #endif namespace webrtc { RWLockWrapper* RWLockWrapper::CreateRWLock() { #ifdef _WIN32 - // Native implementation is faster, so use that if available. - RWLockWrapper* lock = RWLockWin::Create(); - if (lock) { - return lock; - } - return new RWLockGeneric(); + return RWLockWin::Create(); #else return RWLockPosix::Create(); #endif diff --git a/webrtc/system_wrappers/include/rw_lock_wrapper.h b/webrtc/rtc_base/synchronization/rw_lock_wrapper.h similarity index 54% rename from webrtc/system_wrappers/include/rw_lock_wrapper.h rename to webrtc/rtc_base/synchronization/rw_lock_wrapper.h index 751b6a1..39f52fc 100644 --- a/webrtc/system_wrappers/include/rw_lock_wrapper.h +++ b/webrtc/rtc_base/synchronization/rw_lock_wrapper.h @@ -8,10 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_RW_LOCK_WRAPPER_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_RW_LOCK_WRAPPER_H_ +#ifndef RTC_BASE_SYNCHRONIZATION_RW_LOCK_WRAPPER_H_ +#define RTC_BASE_SYNCHRONIZATION_RW_LOCK_WRAPPER_H_ -#include "webrtc/base/thread_annotations.h" +#include "rtc_base/thread_annotations.h" // Note, Windows pre-Vista version of RW locks are not supported natively. For // these OSs regular critical sections have been used to approximate RW lock @@ -19,45 +19,43 @@ namespace webrtc { -class LOCKABLE RWLockWrapper { +class RTC_LOCKABLE RWLockWrapper { public: static RWLockWrapper* CreateRWLock(); virtual ~RWLockWrapper() {} - virtual void AcquireLockExclusive() EXCLUSIVE_LOCK_FUNCTION() = 0; - virtual void ReleaseLockExclusive() UNLOCK_FUNCTION() = 0; + virtual void AcquireLockExclusive() RTC_EXCLUSIVE_LOCK_FUNCTION() = 0; + virtual void ReleaseLockExclusive() RTC_UNLOCK_FUNCTION() = 0; - virtual void AcquireLockShared() SHARED_LOCK_FUNCTION() = 0; - virtual void ReleaseLockShared() UNLOCK_FUNCTION() = 0; + virtual void AcquireLockShared() RTC_SHARED_LOCK_FUNCTION() = 0; + virtual void ReleaseLockShared() RTC_UNLOCK_FUNCTION() = 0; }; // RAII extensions of the RW lock. Prevents Acquire/Release missmatches and // provides more compact locking syntax. -class SCOPED_LOCKABLE ReadLockScoped { +class RTC_SCOPED_LOCKABLE ReadLockScoped { public: - ReadLockScoped(RWLockWrapper& rw_lock) SHARED_LOCK_FUNCTION(rw_lock) + explicit ReadLockScoped(RWLockWrapper& rw_lock) + RTC_SHARED_LOCK_FUNCTION(rw_lock) : rw_lock_(rw_lock) { rw_lock_.AcquireLockShared(); } - ~ReadLockScoped() UNLOCK_FUNCTION() { - rw_lock_.ReleaseLockShared(); - } + ~ReadLockScoped() RTC_UNLOCK_FUNCTION() { rw_lock_.ReleaseLockShared(); } private: RWLockWrapper& rw_lock_; }; -class SCOPED_LOCKABLE WriteLockScoped { +class RTC_SCOPED_LOCKABLE WriteLockScoped { public: - WriteLockScoped(RWLockWrapper& rw_lock) EXCLUSIVE_LOCK_FUNCTION(rw_lock) + explicit WriteLockScoped(RWLockWrapper& rw_lock) + RTC_EXCLUSIVE_LOCK_FUNCTION(rw_lock) : rw_lock_(rw_lock) { rw_lock_.AcquireLockExclusive(); } - ~WriteLockScoped() UNLOCK_FUNCTION() { - rw_lock_.ReleaseLockExclusive(); - } + ~WriteLockScoped() RTC_UNLOCK_FUNCTION() { rw_lock_.ReleaseLockExclusive(); } private: RWLockWrapper& rw_lock_; @@ -65,4 +63,4 @@ class SCOPED_LOCKABLE WriteLockScoped { } // namespace webrtc -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_RW_LOCK_WRAPPER_H_ +#endif // RTC_BASE_SYNCHRONIZATION_RW_LOCK_WRAPPER_H_ diff --git a/webrtc/rtc_base/synchronization/sequence_checker.cc b/webrtc/rtc_base/synchronization/sequence_checker.cc new file mode 100644 index 0000000..1de26cf --- /dev/null +++ b/webrtc/rtc_base/synchronization/sequence_checker.cc @@ -0,0 +1,112 @@ +/* + * Copyright 2019 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 "rtc_base/synchronization/sequence_checker.h" + +#if defined(WEBRTC_MAC) +#include +#endif + +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { +namespace { +// On Mac, returns the label of the current dispatch queue; elsewhere, return +// null. +const void* GetSystemQueueRef() { +#if defined(WEBRTC_MAC) + return dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL); +#else + return nullptr; +#endif +} + +} // namespace + +std::string ExpectationToString(const webrtc::SequenceChecker* checker) { +#if RTC_DCHECK_IS_ON + return checker->ExpectationToString(); +#endif + return std::string(); +} + +SequenceCheckerImpl::SequenceCheckerImpl() + : attached_(true), + valid_thread_(rtc::CurrentThreadRef()), + valid_queue_(TaskQueueBase::Current()), + valid_system_queue_(GetSystemQueueRef()) {} + +SequenceCheckerImpl::~SequenceCheckerImpl() = default; + +bool SequenceCheckerImpl::IsCurrent() const { + const TaskQueueBase* const current_queue = TaskQueueBase::Current(); + const rtc::PlatformThreadRef current_thread = rtc::CurrentThreadRef(); + const void* const current_system_queue = GetSystemQueueRef(); + MutexLock scoped_lock(&lock_); + if (!attached_) { // Previously detached. + attached_ = true; + valid_thread_ = current_thread; + valid_queue_ = current_queue; + valid_system_queue_ = current_system_queue; + return true; + } + if (valid_queue_ || current_queue) { + return valid_queue_ == current_queue; + } + if (valid_system_queue_ && valid_system_queue_ == current_system_queue) { + return true; + } + return rtc::IsThreadRefEqual(valid_thread_, current_thread); +} + +void SequenceCheckerImpl::Detach() { + MutexLock scoped_lock(&lock_); + attached_ = false; + // We don't need to touch the other members here, they will be + // reset on the next call to IsCurrent(). +} + +#if RTC_DCHECK_IS_ON +std::string SequenceCheckerImpl::ExpectationToString() const { + const TaskQueueBase* const current_queue = TaskQueueBase::Current(); + const rtc::PlatformThreadRef current_thread = rtc::CurrentThreadRef(); + const void* const current_system_queue = GetSystemQueueRef(); + MutexLock scoped_lock(&lock_); + if (!attached_) + return "Checker currently not attached."; + + // The format of the string is meant to compliment the one we have inside of + // FatalLog() (checks.cc). Example: + // + // # Expected: TQ: 0x0 SysQ: 0x7fff69541330 Thread: 0x11dcf6dc0 + // # Actual: TQ: 0x7fa8f0604190 SysQ: 0x7fa8f0604a30 Thread: 0x700006f1a000 + // TaskQueue doesn't match + + rtc::StringBuilder message; + message.AppendFormat( + "# Expected: TQ: %p SysQ: %p Thread: %p\n" + "# Actual: TQ: %p SysQ: %p Thread: %p\n", + valid_queue_, valid_system_queue_, + reinterpret_cast(valid_thread_), current_queue, + current_system_queue, reinterpret_cast(current_thread)); + + if ((valid_queue_ || current_queue) && valid_queue_ != current_queue) { + message << "TaskQueue doesn't match\n"; + } else if (valid_system_queue_ && + valid_system_queue_ != current_system_queue) { + message << "System queue doesn't match\n"; + } else if (!rtc::IsThreadRefEqual(valid_thread_, current_thread)) { + message << "Threads don't match\n"; + } + + return message.Release(); +} +#endif // RTC_DCHECK_IS_ON + +} // namespace webrtc diff --git a/webrtc/rtc_base/synchronization/sequence_checker.h b/webrtc/rtc_base/synchronization/sequence_checker.h new file mode 100644 index 0000000..ecf8490 --- /dev/null +++ b/webrtc/rtc_base/synchronization/sequence_checker.h @@ -0,0 +1,187 @@ +/* + * Copyright 2019 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. + */ +#ifndef RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_H_ +#define RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_H_ + +#include + +#include "api/task_queue/task_queue_base.h" +#include "rtc_base/platform_thread_types.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { +// Real implementation of SequenceChecker, for use in debug mode, or +// for temporary use in release mode (e.g. to RTC_CHECK on a threading issue +// seen only in the wild). +// +// Note: You should almost always use the SequenceChecker class to get the +// right version for your build configuration. +class RTC_EXPORT SequenceCheckerImpl { + public: + SequenceCheckerImpl(); + ~SequenceCheckerImpl(); + + bool IsCurrent() const; + // Changes the task queue or thread that is checked for in IsCurrent. This can + // be useful when an object may be created on one task queue / thread and then + // used exclusively on another thread. + void Detach(); + + // Returns a string that is formatted to match with the error string printed + // by RTC_CHECK() when a condition is not met. + // This is used in conjunction with the RTC_DCHECK_RUN_ON() macro. + std::string ExpectationToString() const; + + private: + mutable Mutex lock_; + // These are mutable so that IsCurrent can set them. + mutable bool attached_ RTC_GUARDED_BY(lock_); + mutable rtc::PlatformThreadRef valid_thread_ RTC_GUARDED_BY(lock_); + mutable const TaskQueueBase* valid_queue_ RTC_GUARDED_BY(lock_); + mutable const void* valid_system_queue_ RTC_GUARDED_BY(lock_); +}; + +// Do nothing implementation, for use in release mode. +// +// Note: You should almost always use the SequenceChecker class to get the +// right version for your build configuration. +class SequenceCheckerDoNothing { + public: + bool IsCurrent() const { return true; } + void Detach() {} +}; + +// SequenceChecker is a helper class used to help verify that some methods +// of a class are called on the same task queue or thread. A +// SequenceChecker is bound to a a task queue if the object is +// created on a task queue, or a thread otherwise. +// +// +// Example: +// class MyClass { +// public: +// void Foo() { +// RTC_DCHECK_RUN_ON(sequence_checker_); +// ... (do stuff) ... +// } +// +// private: +// SequenceChecker sequence_checker_; +// } +// +// In Release mode, IsCurrent will always return true. +#if RTC_DCHECK_IS_ON +class RTC_LOCKABLE SequenceChecker : public SequenceCheckerImpl {}; +#else +class RTC_LOCKABLE SequenceChecker : public SequenceCheckerDoNothing {}; +#endif // RTC_ENABLE_THREAD_CHECKER + +namespace webrtc_seq_check_impl { +// Helper class used by RTC_DCHECK_RUN_ON (see example usage below). +class RTC_SCOPED_LOCKABLE SequenceCheckerScope { + public: + template + explicit SequenceCheckerScope(const ThreadLikeObject* thread_like_object) + RTC_EXCLUSIVE_LOCK_FUNCTION(thread_like_object) {} + SequenceCheckerScope(const SequenceCheckerScope&) = delete; + SequenceCheckerScope& operator=(const SequenceCheckerScope&) = delete; + ~SequenceCheckerScope() RTC_UNLOCK_FUNCTION() {} + + template + static bool IsCurrent(const ThreadLikeObject* thread_like_object) { + return thread_like_object->IsCurrent(); + } +}; +} // namespace webrtc_seq_check_impl +} // namespace webrtc + +// RTC_RUN_ON/RTC_GUARDED_BY/RTC_DCHECK_RUN_ON macros allows to annotate +// variables are accessed from same thread/task queue. +// Using tools designed to check mutexes, it checks at compile time everywhere +// variable is access, there is a run-time dcheck thread/task queue is correct. +// +// class ThreadExample { +// public: +// void NeedVar1() { +// RTC_DCHECK_RUN_ON(network_thread_); +// transport_->Send(); +// } +// +// private: +// rtc::Thread* network_thread_; +// int transport_ RTC_GUARDED_BY(network_thread_); +// }; +// +// class SequenceCheckerExample { +// public: +// int CalledFromPacer() RTC_RUN_ON(pacer_sequence_checker_) { +// return var2_; +// } +// +// void CallMeFromPacer() { +// RTC_DCHECK_RUN_ON(&pacer_sequence_checker_) +// << "Should be called from pacer"; +// CalledFromPacer(); +// } +// +// private: +// int pacer_var_ RTC_GUARDED_BY(pacer_sequence_checker_); +// SequenceChecker pacer_sequence_checker_; +// }; +// +// class TaskQueueExample { +// public: +// class Encoder { +// public: +// rtc::TaskQueue* Queue() { return encoder_queue_; } +// void Encode() { +// RTC_DCHECK_RUN_ON(encoder_queue_); +// DoSomething(var_); +// } +// +// private: +// rtc::TaskQueue* const encoder_queue_; +// Frame var_ RTC_GUARDED_BY(encoder_queue_); +// }; +// +// void Encode() { +// // Will fail at runtime when DCHECK is enabled: +// // encoder_->Encode(); +// // Will work: +// rtc::scoped_refptr encoder = encoder_; +// encoder_->Queue()->PostTask([encoder] { encoder->Encode(); }); +// } +// +// private: +// rtc::scoped_refptr encoder_; +// } + +// Document if a function expected to be called from same thread/task queue. +#define RTC_RUN_ON(x) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(x)) + +namespace webrtc { +std::string ExpectationToString(const webrtc::SequenceChecker* checker); + +// Catch-all implementation for types other than explicitly supported above. +template +std::string ExpectationToString(const ThreadLikeObject*) { + return std::string(); +} + +} // namespace webrtc + +#define RTC_DCHECK_RUN_ON(x) \ + webrtc::webrtc_seq_check_impl::SequenceCheckerScope seq_check_scope(x); \ + RTC_DCHECK((x)->IsCurrent()) << webrtc::ExpectationToString(x) + +#endif // RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_H_ diff --git a/webrtc/rtc_base/synchronization/yield.cc b/webrtc/rtc_base/synchronization/yield.cc new file mode 100644 index 0000000..cbb58d1 --- /dev/null +++ b/webrtc/rtc_base/synchronization/yield.cc @@ -0,0 +1,36 @@ +/* + * Copyright 2020 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 "rtc_base/synchronization/yield.h" + +#if defined(WEBRTC_WIN) +#include +#else +#include +#include +#endif + +namespace webrtc { + +void YieldCurrentThread() { + // TODO(bugs.webrtc.org/11634): use dedicated OS functionality instead of + // sleep for yielding. +#if defined(WEBRTC_WIN) + ::Sleep(0); +#elif defined(WEBRTC_MAC) && defined(RTC_USE_NATIVE_MUTEX_ON_MAC) && \ + !RTC_USE_NATIVE_MUTEX_ON_MAC + sched_yield(); +#else + static const struct timespec ts_null = {0}; + nanosleep(&ts_null, nullptr); +#endif +} + +} // namespace webrtc diff --git a/webrtc/rtc_base/synchronization/yield.h b/webrtc/rtc_base/synchronization/yield.h new file mode 100644 index 0000000..d4f5f99 --- /dev/null +++ b/webrtc/rtc_base/synchronization/yield.h @@ -0,0 +1,20 @@ +/* + * Copyright 2020 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. + */ +#ifndef RTC_BASE_SYNCHRONIZATION_YIELD_H_ +#define RTC_BASE_SYNCHRONIZATION_YIELD_H_ + +namespace webrtc { + +// Request rescheduling of threads. +void YieldCurrentThread(); + +} // namespace webrtc + +#endif // RTC_BASE_SYNCHRONIZATION_YIELD_H_ diff --git a/webrtc/rtc_base/synchronization/yield_policy.cc b/webrtc/rtc_base/synchronization/yield_policy.cc new file mode 100644 index 0000000..d883d42 --- /dev/null +++ b/webrtc/rtc_base/synchronization/yield_policy.cc @@ -0,0 +1,82 @@ +/* + * Copyright 2019 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 "rtc_base/synchronization/yield_policy.h" + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "rtc_base/checks.h" +#if !defined(ABSL_HAVE_THREAD_LOCAL) && defined(WEBRTC_POSIX) +#include +#endif + +namespace rtc { +namespace { + +#if defined(ABSL_HAVE_THREAD_LOCAL) + +ABSL_CONST_INIT thread_local YieldInterface* current_yield_policy = nullptr; + +YieldInterface* GetCurrentYieldPolicy() { + return current_yield_policy; +} + +void SetCurrentYieldPolicy(YieldInterface* ptr) { + current_yield_policy = ptr; +} + +#elif defined(WEBRTC_POSIX) + +// Emscripten does not support the C++11 thread_local keyword but does support +// the pthread thread-local storage API. +// https://github.com/emscripten-core/emscripten/issues/3502 + +ABSL_CONST_INIT pthread_key_t g_current_yield_policy_tls = 0; + +void InitializeTls() { + RTC_CHECK_EQ(pthread_key_create(&g_current_yield_policy_tls, nullptr), 0); +} + +pthread_key_t GetCurrentYieldPolicyTls() { + static pthread_once_t init_once = PTHREAD_ONCE_INIT; + RTC_CHECK_EQ(pthread_once(&init_once, &InitializeTls), 0); + return g_current_yield_policy_tls; +} + +YieldInterface* GetCurrentYieldPolicy() { + return static_cast( + pthread_getspecific(GetCurrentYieldPolicyTls())); +} + +void SetCurrentYieldPolicy(YieldInterface* ptr) { + pthread_setspecific(GetCurrentYieldPolicyTls(), ptr); +} + +#else +#error Unsupported platform +#endif + +} // namespace + +ScopedYieldPolicy::ScopedYieldPolicy(YieldInterface* policy) + : previous_(GetCurrentYieldPolicy()) { + SetCurrentYieldPolicy(policy); +} + +ScopedYieldPolicy::~ScopedYieldPolicy() { + SetCurrentYieldPolicy(previous_); +} + +void ScopedYieldPolicy::YieldExecution() { + YieldInterface* current = GetCurrentYieldPolicy(); + if (current) + current->YieldExecution(); +} + +} // namespace rtc diff --git a/webrtc/rtc_base/synchronization/yield_policy.h b/webrtc/rtc_base/synchronization/yield_policy.h new file mode 100644 index 0000000..5def6b7 --- /dev/null +++ b/webrtc/rtc_base/synchronization/yield_policy.h @@ -0,0 +1,38 @@ +/* + * Copyright 2019 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. + */ +#ifndef RTC_BASE_SYNCHRONIZATION_YIELD_POLICY_H_ +#define RTC_BASE_SYNCHRONIZATION_YIELD_POLICY_H_ + +namespace rtc { +class YieldInterface { + public: + virtual ~YieldInterface() = default; + virtual void YieldExecution() = 0; +}; + +// Sets the current thread-local yield policy while it's in scope and reverts +// to the previous policy when it leaves the scope. +class ScopedYieldPolicy final { + public: + explicit ScopedYieldPolicy(YieldInterface* policy); + ScopedYieldPolicy(const ScopedYieldPolicy&) = delete; + ScopedYieldPolicy& operator=(const ScopedYieldPolicy&) = delete; + ~ScopedYieldPolicy(); + // Will yield as specified by the currently active thread-local yield policy + // (which by default is a no-op). + static void YieldExecution(); + + private: + YieldInterface* const previous_; +}; + +} // namespace rtc + +#endif // RTC_BASE_SYNCHRONIZATION_YIELD_POLICY_H_ diff --git a/webrtc/rtc_base/system/arch.h b/webrtc/rtc_base/system/arch.h new file mode 100644 index 0000000..ed216e6 --- /dev/null +++ b/webrtc/rtc_base/system/arch.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018 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. + */ + +// This file contains platform-specific typedefs and defines. +// Much of it is derived from Chromium's build/build_config.h. + +#ifndef RTC_BASE_SYSTEM_ARCH_H_ +#define RTC_BASE_SYSTEM_ARCH_H_ + +// Processor architecture detection. For more info on what's defined, see: +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// http://www.agner.org/optimize/calling_conventions.pdf +// or with gcc, run: "echo | gcc -E -dM -" +#if defined(_M_X64) || defined(__x86_64__) +#define WEBRTC_ARCH_X86_FAMILY +#define WEBRTC_ARCH_X86_64 +#define WEBRTC_ARCH_64_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(_M_ARM64) || defined(__aarch64__) +#define WEBRTC_ARCH_ARM_FAMILY +#define WEBRTC_ARCH_64_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(_M_IX86) || defined(__i386__) +#define WEBRTC_ARCH_X86_FAMILY +#define WEBRTC_ARCH_X86 +#define WEBRTC_ARCH_32_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__ARMEL__) +#define WEBRTC_ARCH_ARM_FAMILY +#define WEBRTC_ARCH_32_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__MIPSEL__) +#define WEBRTC_ARCH_MIPS_FAMILY +#if defined(__LP64__) +#define WEBRTC_ARCH_64_BITS +#else +#define WEBRTC_ARCH_32_BITS +#endif +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__pnacl__) +#define WEBRTC_ARCH_32_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#elif defined(__EMSCRIPTEN__) +#define WEBRTC_ARCH_32_BITS +#define WEBRTC_ARCH_LITTLE_ENDIAN +#else +#error Please add support for your architecture in rtc_base/system/arch.h +#endif + +#if !(defined(WEBRTC_ARCH_LITTLE_ENDIAN) ^ defined(WEBRTC_ARCH_BIG_ENDIAN)) +#error Define either WEBRTC_ARCH_LITTLE_ENDIAN or WEBRTC_ARCH_BIG_ENDIAN +#endif + +#endif // RTC_BASE_SYSTEM_ARCH_H_ diff --git a/webrtc/system_wrappers/include/asm_defines.h b/webrtc/rtc_base/system/asm_defines.h similarity index 84% rename from webrtc/system_wrappers/include/asm_defines.h rename to webrtc/rtc_base/system/asm_defines.h index fe4c05e..a7f6aad 100644 --- a/webrtc/system_wrappers/include/asm_defines.h +++ b/webrtc/rtc_base/system/asm_defines.h @@ -8,8 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ASM_DEFINES_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ASM_DEFINES_H_ +#ifndef RTC_BASE_SYSTEM_ASM_DEFINES_H_ +#define RTC_BASE_SYSTEM_ASM_DEFINES_H_ + +// clang-format off +// clang formatting breaks everything here, e.g. concatenating directives, +// due to absence of context via asm keyword. #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits @@ -63,4 +67,6 @@ strheq \reg1, \reg2, \num .text -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ASM_DEFINES_H_ +// clang-format on + +#endif // RTC_BASE_SYSTEM_ASM_DEFINES_H_ diff --git a/webrtc/rtc_base/system/file_wrapper.cc b/webrtc/rtc_base/system/file_wrapper.cc new file mode 100644 index 0000000..2828790 --- /dev/null +++ b/webrtc/rtc_base/system/file_wrapper.cc @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2012 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 "rtc_base/system/file_wrapper.h" +#include "rtc_base/numerics/safe_conversions.h" + +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include + +namespace webrtc { +namespace { +FILE* FileOpen(const char* file_name_utf8, bool read_only, int* error) { +#if defined(_WIN32) + int len = MultiByteToWideChar(CP_UTF8, 0, file_name_utf8, -1, nullptr, 0); + std::wstring wstr(len, 0); + MultiByteToWideChar(CP_UTF8, 0, file_name_utf8, -1, &wstr[0], len); + FILE* file = _wfopen(wstr.c_str(), read_only ? L"rb" : L"wb"); +#else + FILE* file = fopen(file_name_utf8, read_only ? "rb" : "wb"); +#endif + if (!file && error) { + *error = errno; + } + return file; +} + +const char* GetCstrCheckNoEmbeddedNul(const std::string& s) { + const char* p = s.c_str(); + RTC_CHECK_EQ(strlen(p), s.size()) + << "Invalid filename, containing NUL character"; + return p; +} +} // namespace + +// static +FileWrapper FileWrapper::OpenReadOnly(const char* file_name_utf8) { + return FileWrapper(FileOpen(file_name_utf8, true, nullptr)); +} + +// static +FileWrapper FileWrapper::OpenReadOnly(const std::string& file_name_utf8) { + return OpenReadOnly(GetCstrCheckNoEmbeddedNul(file_name_utf8)); +} + +// static +FileWrapper FileWrapper::OpenWriteOnly(const char* file_name_utf8, + int* error /*=nullptr*/) { + return FileWrapper(FileOpen(file_name_utf8, false, error)); +} + +// static +FileWrapper FileWrapper::OpenWriteOnly(const std::string& file_name_utf8, + int* error /*=nullptr*/) { + return OpenWriteOnly(GetCstrCheckNoEmbeddedNul(file_name_utf8), error); +} + +FileWrapper::FileWrapper(FileWrapper&& other) { + operator=(std::move(other)); +} + +FileWrapper& FileWrapper::operator=(FileWrapper&& other) { + Close(); + file_ = other.file_; + other.file_ = nullptr; + return *this; +} + +bool FileWrapper::SeekRelative(int64_t offset) { + RTC_DCHECK(file_); + return fseek(file_, rtc::checked_cast(offset), SEEK_CUR) == 0; +} + +bool FileWrapper::SeekTo(int64_t position) { + RTC_DCHECK(file_); + return fseek(file_, rtc::checked_cast(position), SEEK_SET) == 0; +} + +bool FileWrapper::Flush() { + RTC_DCHECK(file_); + return fflush(file_) == 0; +} + +size_t FileWrapper::Read(void* buf, size_t length) { + RTC_DCHECK(file_); + return fread(buf, 1, length, file_); +} + +bool FileWrapper::ReadEof() const { + RTC_DCHECK(file_); + return feof(file_); +} + +bool FileWrapper::Write(const void* buf, size_t length) { + RTC_DCHECK(file_); + return fwrite(buf, 1, length, file_) == length; +} + +bool FileWrapper::Close() { + if (file_ == nullptr) + return true; + + bool success = fclose(file_) == 0; + file_ = nullptr; + return success; +} + +FILE* FileWrapper::Release() { + FILE* file = file_; + file_ = nullptr; + return file; +} + +} // namespace webrtc diff --git a/webrtc/rtc_base/system/file_wrapper.h b/webrtc/rtc_base/system/file_wrapper.h new file mode 100644 index 0000000..42c463c --- /dev/null +++ b/webrtc/rtc_base/system/file_wrapper.h @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#ifndef RTC_BASE_SYSTEM_FILE_WRAPPER_H_ +#define RTC_BASE_SYSTEM_FILE_WRAPPER_H_ + +#include +#include + +#include + +// Implementation that can read (exclusive) or write from/to a file. + +namespace webrtc { + +// This class is a thin wrapper around FILE*. It's main features are that it +// owns the FILE*, calling fclose on destruction, and that on windows, file +// names passed to the open methods are always treated as utf-8, regardless of +// system code page. + +// Most of the methods return only a success/fail indication. When needed, an +// optional argument |int* error| should be added to all methods, in the same +// way as for the OpenWriteOnly methods. +class FileWrapper final { + public: + // Opens a file, in read or write mode. Use the is_open() method on the + // returned object to check if the open operation was successful. On failure, + // and if |error| is non-null, the system errno value is stored at |*error|. + // The file is closed by the destructor. + static FileWrapper OpenReadOnly(const char* file_name_utf8); + static FileWrapper OpenReadOnly(const std::string& file_name_utf8); + static FileWrapper OpenWriteOnly(const char* file_name_utf8, + int* error = nullptr); + + static FileWrapper OpenWriteOnly(const std::string& file_name_utf8, + int* error = nullptr); + + FileWrapper() = default; + + // Takes over ownership of |file|, closing it on destruction. Calling with + // null |file| is allowed, and results in a FileWrapper with is_open() false. + explicit FileWrapper(FILE* file) : file_(file) {} + ~FileWrapper() { Close(); } + + // Copying is not supported. + FileWrapper(const FileWrapper&) = delete; + FileWrapper& operator=(const FileWrapper&) = delete; + + // Support for move semantics. + FileWrapper(FileWrapper&&); + FileWrapper& operator=(FileWrapper&&); + + // Returns true if a file has been opened. If the file is not open, no methods + // but is_open and Close may be called. + bool is_open() const { return file_ != nullptr; } + + // Closes the file, and implies Flush. Returns true on success, false if + // writing buffered data fails. On failure, the file is nevertheless closed. + // Calling Close on an already closed file does nothing and returns success. + bool Close(); + + // Releases and returns the wrapped file without closing it. This call passes + // the ownership of the file to the caller, and the wrapper is no longer + // responsible for closing it. Similarly the previously wrapped file is no + // longer available for the wrapper to use in any aspect. + FILE* Release(); + + // Write any buffered data to the underlying file. Returns true on success, + // false on write error. Note: Flushing when closing, is not required. + bool Flush(); + + // Seeks to the beginning of file. Returns true on success, false on failure, + // e.g., if the underlying file isn't seekable. + bool Rewind() { return SeekTo(0); } + // TODO(nisse): The seek functions are used only by the WavReader. If that + // code is demoted to test code, seek functions can be deleted from this + // utility. + // Seek relative to current file position. + bool SeekRelative(int64_t offset); + // Seek to given position. + bool SeekTo(int64_t position); + + // Returns number of bytes read. Short count indicates EOF or error. + size_t Read(void* buf, size_t length); + + // If the most recent Read() returned a short count, this methods returns true + // if the short count was due to EOF, and false it it was due to some i/o + // error. + bool ReadEof() const; + + // Returns true if all data was successfully written (or buffered), or false + // if there was an error. Writing buffered data can fail later, and is + // reported with return value from Flush or Close. + bool Write(const void* buf, size_t length); + + private: + FILE* file_ = nullptr; +}; + +} // namespace webrtc + +#endif // RTC_BASE_SYSTEM_FILE_WRAPPER_H_ diff --git a/webrtc/rtc_base/system/ignore_warnings.h b/webrtc/rtc_base/system/ignore_warnings.h new file mode 100644 index 0000000..e891c50 --- /dev/null +++ b/webrtc/rtc_base/system/ignore_warnings.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef RTC_BASE_SYSTEM_IGNORE_WARNINGS_H_ +#define RTC_BASE_SYSTEM_IGNORE_WARNINGS_H_ + +#ifdef __clang__ +#define RTC_PUSH_IGNORING_WFRAME_LARGER_THAN() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wframe-larger-than=\"") +#define RTC_POP_IGNORING_WFRAME_LARGER_THAN() _Pragma("clang diagnostic pop") +#elif __GNUC__ +#define RTC_PUSH_IGNORING_WFRAME_LARGER_THAN() \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wframe-larger-than=\"") +#define RTC_POP_IGNORING_WFRAME_LARGER_THAN() _Pragma("GCC diagnostic pop") +#else +#define RTC_PUSH_IGNORING_WFRAME_LARGER_THAN() +#define RTC_POP_IGNORING_WFRAME_LARGER_THAN() +#endif + +#endif // RTC_BASE_SYSTEM_IGNORE_WARNINGS_H_ diff --git a/webrtc/rtc_base/system/inline.h b/webrtc/rtc_base/system/inline.h new file mode 100644 index 0000000..f585d34 --- /dev/null +++ b/webrtc/rtc_base/system/inline.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef RTC_BASE_SYSTEM_INLINE_H_ +#define RTC_BASE_SYSTEM_INLINE_H_ + +#if defined(_MSC_VER) + +#define RTC_FORCE_INLINE __forceinline +#define RTC_NO_INLINE __declspec(noinline) + +#elif defined(__GNUC__) + +#define RTC_FORCE_INLINE __attribute__((__always_inline__)) +#define RTC_NO_INLINE __attribute__((__noinline__)) + +#else + +#define RTC_FORCE_INLINE +#define RTC_NO_INLINE + +#endif + +#endif // RTC_BASE_SYSTEM_INLINE_H_ diff --git a/webrtc/rtc_base/system/rtc_export.h b/webrtc/rtc_base/system/rtc_export.h new file mode 100644 index 0000000..d1eb60a --- /dev/null +++ b/webrtc/rtc_base/system/rtc_export.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef RTC_BASE_SYSTEM_RTC_EXPORT_H_ +#define RTC_BASE_SYSTEM_RTC_EXPORT_H_ + +// RTC_EXPORT is used to mark symbols as exported or imported when WebRTC is +// built or used as a shared library. +// When WebRTC is built as a static library the RTC_EXPORT macro expands to +// nothing. + +#ifdef WEBRTC_ENABLE_SYMBOL_EXPORT + +#ifdef WEBRTC_WIN + +#ifdef WEBRTC_LIBRARY_IMPL +#define RTC_EXPORT __declspec(dllexport) +#else +#define RTC_EXPORT __declspec(dllimport) +#endif + +#else // WEBRTC_WIN + +#if __has_attribute(visibility) && defined(WEBRTC_LIBRARY_IMPL) +#define RTC_EXPORT __attribute__((visibility("default"))) +#endif + +#endif // WEBRTC_WIN + +#endif // WEBRTC_ENABLE_SYMBOL_EXPORT + +#ifndef RTC_EXPORT +#define RTC_EXPORT +#endif + +#endif // RTC_BASE_SYSTEM_RTC_EXPORT_H_ diff --git a/webrtc/rtc_base/system/unused.h b/webrtc/rtc_base/system/unused.h new file mode 100644 index 0000000..a0add4e --- /dev/null +++ b/webrtc/rtc_base/system/unused.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef RTC_BASE_SYSTEM_UNUSED_H_ +#define RTC_BASE_SYSTEM_UNUSED_H_ + +// Annotate a function indicating the caller must examine the return value. +// Use like: +// int foo() RTC_WARN_UNUSED_RESULT; +// To explicitly ignore a result, cast to void. +// TODO(kwiberg): Remove when we can use [[nodiscard]] from C++17. +#if defined(__clang__) +#define RTC_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) +#elif defined(__GNUC__) +// gcc has a __warn_unused_result__ attribute, but you can't quiet it by +// casting to void, so we don't use it. +#define RTC_WARN_UNUSED_RESULT +#else +#define RTC_WARN_UNUSED_RESULT +#endif + +// Prevent the compiler from warning about an unused variable. For example: +// int result = DoSomething(); +// assert(result == 17); +// RTC_UNUSED(result); +// Note: In most cases it is better to remove the unused variable rather than +// suppressing the compiler warning. +#ifndef RTC_UNUSED +#define RTC_UNUSED(x) static_cast(x) +#endif // RTC_UNUSED + +#endif // RTC_BASE_SYSTEM_UNUSED_H_ diff --git a/webrtc/rtc_base/system/warn_current_thread_is_deadlocked.h b/webrtc/rtc_base/system/warn_current_thread_is_deadlocked.h new file mode 100644 index 0000000..4a0ba9d --- /dev/null +++ b/webrtc/rtc_base/system/warn_current_thread_is_deadlocked.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019 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. + */ + +#ifndef RTC_BASE_SYSTEM_WARN_CURRENT_THREAD_IS_DEADLOCKED_H_ +#define RTC_BASE_SYSTEM_WARN_CURRENT_THREAD_IS_DEADLOCKED_H_ + +namespace webrtc { + +#if defined(WEBRTC_ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) +void WarnThatTheCurrentThreadIsProbablyDeadlocked(); +#else +inline void WarnThatTheCurrentThreadIsProbablyDeadlocked() {} +#endif + +} // namespace webrtc + +#endif // RTC_BASE_SYSTEM_WARN_CURRENT_THREAD_IS_DEADLOCKED_H_ diff --git a/webrtc/rtc_base/thread_annotations.h b/webrtc/rtc_base/thread_annotations.h new file mode 100644 index 0000000..8569fab --- /dev/null +++ b/webrtc/rtc_base/thread_annotations.h @@ -0,0 +1,95 @@ +// +// Copyright (c) 2013 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. +// +// Borrowed from +// https://code.google.com/p/gperftools/source/browse/src/base/thread_annotations.h +// but adapted for clang attributes instead of the gcc. +// +// This header file contains the macro definitions for thread safety +// annotations that allow the developers to document the locking policies +// of their multi-threaded code. The annotations can also help program +// analysis tools to identify potential thread safety issues. + +#ifndef RTC_BASE_THREAD_ANNOTATIONS_H_ +#define RTC_BASE_THREAD_ANNOTATIONS_H_ + +#if defined(__clang__) && (!defined(SWIG)) +#define RTC_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define RTC_THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +// Document if a shared variable/field needs to be protected by a lock. +// GUARDED_BY allows the user to specify a particular lock that should be +// held when accessing the annotated variable. +#define RTC_GUARDED_BY(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +// Document if the memory location pointed to by a pointer should be guarded +// by a lock when dereferencing the pointer. Note that a pointer variable to a +// shared memory location could itself be a shared variable. For example, if a +// shared global pointer q, which is guarded by mu1, points to a shared memory +// location that is guarded by mu2, q should be annotated as follows: +// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2); +#define RTC_PT_GUARDED_BY(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +// Document the acquisition order between locks that can be held +// simultaneously by a thread. For any two locks that need to be annotated +// to establish an acquisition order, only one of them needs the annotation. +// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER +// and ACQUIRED_BEFORE.) +#define RTC_ACQUIRED_AFTER(x) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(x)) +#define RTC_ACQUIRED_BEFORE(x) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(x)) + +// The following three annotations document the lock requirements for +// functions/methods. + +// Document if a function expects certain locks to be held before it is called +#define RTC_EXCLUSIVE_LOCKS_REQUIRED(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) +#define RTC_SHARED_LOCKS_REQUIRED(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) + +// Document the locks acquired in the body of the function. These locks +// cannot be held when calling this function (as google3's Mutex locks are +// non-reentrant). +#define RTC_LOCKS_EXCLUDED(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +// Document the lock the annotated function returns without acquiring it. +#define RTC_LOCK_RETURNED(x) RTC_THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +// Document if a class/type is a lockable type (such as the Mutex class). +#define RTC_LOCKABLE RTC_THREAD_ANNOTATION_ATTRIBUTE__(lockable) + +// Document if a class is a scoped lockable type (such as the MutexLock class). +#define RTC_SCOPED_LOCKABLE RTC_THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +// The following annotations specify lock and unlock primitives. +#define RTC_EXCLUSIVE_LOCK_FUNCTION(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) + +#define RTC_SHARED_LOCK_FUNCTION(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) + +#define RTC_EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) + +#define RTC_SHARED_TRYLOCK_FUNCTION(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) + +#define RTC_UNLOCK_FUNCTION(...) \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) + +// An escape hatch for thread safety analysis to ignore the annotated function. +#define RTC_NO_THREAD_SAFETY_ANALYSIS \ + RTC_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +#endif // RTC_BASE_THREAD_ANNOTATIONS_H_ diff --git a/webrtc/rtc_base/thread_checker.h b/webrtc/rtc_base/thread_checker.h new file mode 100644 index 0000000..876a08e --- /dev/null +++ b/webrtc/rtc_base/thread_checker.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014 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. + */ + +// Borrowed from Chromium's src/base/threading/thread_checker.h. + +#ifndef RTC_BASE_THREAD_CHECKER_H_ +#define RTC_BASE_THREAD_CHECKER_H_ + +#include "rtc_base/deprecation.h" +#include "rtc_base/synchronization/sequence_checker.h" + +namespace rtc { +// TODO(srte): Replace usages of this with SequenceChecker. +class ThreadChecker : public webrtc::SequenceChecker { + public: + RTC_DEPRECATED bool CalledOnValidThread() const { return IsCurrent(); } + RTC_DEPRECATED void DetachFromThread() { Detach(); } +}; +} // namespace rtc +#endif // RTC_BASE_THREAD_CHECKER_H_ diff --git a/webrtc/rtc_base/time_utils.cc b/webrtc/rtc_base/time_utils.cc new file mode 100644 index 0000000..11c9d5a --- /dev/null +++ b/webrtc/rtc_base/time_utils.cc @@ -0,0 +1,327 @@ +/* + * Copyright 2004 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 + +#if defined(WEBRTC_POSIX) +#include +#if defined(WEBRTC_MAC) +#include +#endif +#endif + +#if defined(WEBRTC_WIN) +// clang-format off +// clang formatting would put last, +// which leads to compilation failure. +#include +#include +#include +// clang-format on +#endif + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/time_utils.h" + +namespace rtc { + +ClockInterface* g_clock = nullptr; + +ClockInterface* SetClockForTesting(ClockInterface* clock) { + ClockInterface* prev = g_clock; + g_clock = clock; + return prev; +} + +ClockInterface* GetClockForTesting() { + return g_clock; +} + +#if defined(WINUWP) + +namespace { + +class TimeHelper final { + public: + TimeHelper(const TimeHelper&) = delete; + + // Resets the clock based upon an NTP server. This routine must be called + // prior to the main system start-up to ensure all clocks are based upon + // an NTP server time if NTP synchronization is required. No critical + // section is used thus this method must be called prior to any clock + // routines being used. + static void SyncWithNtp(int64_t ntp_server_time_ms) { + auto& singleton = Singleton(); + TIME_ZONE_INFORMATION time_zone; + GetTimeZoneInformation(&time_zone); + int64_t time_zone_bias_ns = + rtc::dchecked_cast(time_zone.Bias) * 60 * 1000 * 1000 * 1000; + singleton.app_start_time_ns_ = + (ntp_server_time_ms - kNTPTimeToUnixTimeEpochOffset) * 1000000 - + time_zone_bias_ns; + singleton.UpdateReferenceTime(); + } + + // Returns the number of nanoseconds that have passed since unix epoch. + static int64_t TicksNs() { + auto& singleton = Singleton(); + int64_t result = 0; + LARGE_INTEGER qpcnt; + QueryPerformanceCounter(&qpcnt); + result = rtc::dchecked_cast( + (rtc::dchecked_cast(qpcnt.QuadPart) * 100000 / + rtc::dchecked_cast(singleton.os_ticks_per_second_)) * + 10000); + result = singleton.app_start_time_ns_ + result - + singleton.time_since_os_start_ns_; + return result; + } + + private: + TimeHelper() { + TIME_ZONE_INFORMATION time_zone; + GetTimeZoneInformation(&time_zone); + int64_t time_zone_bias_ns = + rtc::dchecked_cast(time_zone.Bias) * 60 * 1000 * 1000 * 1000; + FILETIME ft; + // This will give us system file in UTC format. + GetSystemTimeAsFileTime(&ft); + LARGE_INTEGER li; + li.HighPart = ft.dwHighDateTime; + li.LowPart = ft.dwLowDateTime; + + app_start_time_ns_ = (li.QuadPart - kFileTimeToUnixTimeEpochOffset) * 100 - + time_zone_bias_ns; + + UpdateReferenceTime(); + } + + static TimeHelper& Singleton() { + static TimeHelper singleton; + return singleton; + } + + void UpdateReferenceTime() { + LARGE_INTEGER qpfreq; + QueryPerformanceFrequency(&qpfreq); + os_ticks_per_second_ = rtc::dchecked_cast(qpfreq.QuadPart); + + LARGE_INTEGER qpcnt; + QueryPerformanceCounter(&qpcnt); + time_since_os_start_ns_ = rtc::dchecked_cast( + (rtc::dchecked_cast(qpcnt.QuadPart) * 100000 / + rtc::dchecked_cast(os_ticks_per_second_)) * + 10000); + } + + private: + static constexpr uint64_t kFileTimeToUnixTimeEpochOffset = + 116444736000000000ULL; + static constexpr uint64_t kNTPTimeToUnixTimeEpochOffset = 2208988800000L; + + // The number of nanoseconds since unix system epoch + int64_t app_start_time_ns_; + // The number of nanoseconds since the OS started + int64_t time_since_os_start_ns_; + // The OS calculated ticks per second + int64_t os_ticks_per_second_; +}; + +} // namespace + +void SyncWithNtp(int64_t time_from_ntp_server_ms) { + TimeHelper::SyncWithNtp(time_from_ntp_server_ms); +} + +#endif // defined(WINUWP) + +int64_t SystemTimeNanos() { + int64_t ticks; +#if defined(WEBRTC_MAC) + static mach_timebase_info_data_t timebase; + if (timebase.denom == 0) { + // Get the timebase if this is the first time we run. + // Recommended by Apple's QA1398. + if (mach_timebase_info(&timebase) != KERN_SUCCESS) { + RTC_NOTREACHED(); + } + } + // Use timebase to convert absolute time tick units into nanoseconds. + const auto mul = [](uint64_t a, uint32_t b) -> int64_t { + RTC_DCHECK_NE(b, 0); + RTC_DCHECK_LE(a, std::numeric_limits::max() / b) + << "The multiplication " << a << " * " << b << " overflows"; + return rtc::dchecked_cast(a * b); + }; + ticks = mul(mach_absolute_time(), timebase.numer) / timebase.denom; +#elif defined(WEBRTC_POSIX) + struct timespec ts; + // TODO(deadbeef): Do we need to handle the case when CLOCK_MONOTONIC is not + // supported? + clock_gettime(CLOCK_MONOTONIC, &ts); + ticks = kNumNanosecsPerSec * static_cast(ts.tv_sec) + + static_cast(ts.tv_nsec); +#elif defined(WINUWP) + ticks = TimeHelper::TicksNs(); +#elif defined(WEBRTC_WIN) + static volatile LONG last_timegettime = 0; + static volatile int64_t num_wrap_timegettime = 0; + volatile LONG* last_timegettime_ptr = &last_timegettime; + DWORD now = timeGetTime(); + // Atomically update the last gotten time + DWORD old = InterlockedExchange(last_timegettime_ptr, now); + if (now < old) { + // If now is earlier than old, there may have been a race between threads. + // 0x0fffffff ~3.1 days, the code will not take that long to execute + // so it must have been a wrap around. + if (old > 0xf0000000 && now < 0x0fffffff) { + num_wrap_timegettime++; + } + } + ticks = now + (num_wrap_timegettime << 32); + // TODO(deadbeef): Calculate with nanosecond precision. Otherwise, we're + // just wasting a multiply and divide when doing Time() on Windows. + ticks = ticks * kNumNanosecsPerMillisec; +#else +#error Unsupported platform. +#endif + return ticks; +} + +int64_t SystemTimeMillis() { + return static_cast(SystemTimeNanos() / kNumNanosecsPerMillisec); +} + +int64_t TimeNanos() { + if (g_clock) { + return g_clock->TimeNanos(); + } + return SystemTimeNanos(); +} + +uint32_t Time32() { + return static_cast(TimeNanos() / kNumNanosecsPerMillisec); +} + +int64_t TimeMillis() { + return TimeNanos() / kNumNanosecsPerMillisec; +} + +int64_t TimeMicros() { + return TimeNanos() / kNumNanosecsPerMicrosec; +} + +int64_t TimeAfter(int64_t elapsed) { + RTC_DCHECK_GE(elapsed, 0); + return TimeMillis() + elapsed; +} + +int32_t TimeDiff32(uint32_t later, uint32_t earlier) { + return later - earlier; +} + +int64_t TimeDiff(int64_t later, int64_t earlier) { + return later - earlier; +} + +TimestampWrapAroundHandler::TimestampWrapAroundHandler() + : last_ts_(0), num_wrap_(-1) {} + +int64_t TimestampWrapAroundHandler::Unwrap(uint32_t ts) { + if (num_wrap_ == -1) { + last_ts_ = ts; + num_wrap_ = 0; + return ts; + } + + if (ts < last_ts_) { + if (last_ts_ >= 0xf0000000 && ts < 0x0fffffff) + ++num_wrap_; + } else if ((ts - last_ts_) > 0xf0000000) { + // Backwards wrap. Unwrap with last wrap count and don't update last_ts_. + return ts + (num_wrap_ - 1) * (int64_t{1} << 32); + } + + last_ts_ = ts; + return ts + (num_wrap_ << 32); +} + +int64_t TmToSeconds(const tm& tm) { + static short int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + static short int cumul_mdays[12] = {0, 31, 59, 90, 120, 151, + 181, 212, 243, 273, 304, 334}; + int year = tm.tm_year + 1900; + int month = tm.tm_mon; + int day = tm.tm_mday - 1; // Make 0-based like the rest. + int hour = tm.tm_hour; + int min = tm.tm_min; + int sec = tm.tm_sec; + + bool expiry_in_leap_year = + (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); + + if (year < 1970) + return -1; + if (month < 0 || month > 11) + return -1; + if (day < 0 || day >= mdays[month] + (expiry_in_leap_year && month == 2 - 1)) + return -1; + if (hour < 0 || hour > 23) + return -1; + if (min < 0 || min > 59) + return -1; + if (sec < 0 || sec > 59) + return -1; + + day += cumul_mdays[month]; + + // Add number of leap days between 1970 and the expiration year, inclusive. + day += ((year / 4 - 1970 / 4) - (year / 100 - 1970 / 100) + + (year / 400 - 1970 / 400)); + + // We will have added one day too much above if expiration is during a leap + // year, and expiration is in January or February. + if (expiry_in_leap_year && month <= 2 - 1) // |month| is zero based. + day -= 1; + + // Combine all variables into seconds from 1970-01-01 00:00 (except |month| + // which was accumulated into |day| above). + return (((static_cast(year - 1970) * 365 + day) * 24 + hour) * 60 + + min) * + 60 + + sec; +} + +int64_t TimeUTCMicros() { + if (g_clock) { + return g_clock->TimeNanos() / kNumNanosecsPerMicrosec; + } +#if defined(WEBRTC_POSIX) + struct timeval time; + gettimeofday(&time, nullptr); + // Convert from second (1.0) and microsecond (1e-6). + return (static_cast(time.tv_sec) * rtc::kNumMicrosecsPerSec + + time.tv_usec); + +#elif defined(WEBRTC_WIN) + struct _timeb time; + _ftime(&time); + // Convert from second (1.0) and milliseconds (1e-3). + return (static_cast(time.time) * rtc::kNumMicrosecsPerSec + + static_cast(time.millitm) * rtc::kNumMicrosecsPerMillisec); +#endif +} + +int64_t TimeUTCMillis() { + return TimeUTCMicros() / kNumMicrosecsPerMillisec; +} + +} // namespace rtc diff --git a/webrtc/rtc_base/time_utils.h b/webrtc/rtc_base/time_utils.h new file mode 100644 index 0000000..147ab8d --- /dev/null +++ b/webrtc/rtc_base/time_utils.h @@ -0,0 +1,139 @@ +/* + * Copyright 2005 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. + */ + +#ifndef RTC_BASE_TIME_UTILS_H_ +#define RTC_BASE_TIME_UTILS_H_ + +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/system/rtc_export.h" + +namespace rtc { + +static const int64_t kNumMillisecsPerSec = INT64_C(1000); +static const int64_t kNumMicrosecsPerSec = INT64_C(1000000); +static const int64_t kNumNanosecsPerSec = INT64_C(1000000000); + +static const int64_t kNumMicrosecsPerMillisec = + kNumMicrosecsPerSec / kNumMillisecsPerSec; +static const int64_t kNumNanosecsPerMillisec = + kNumNanosecsPerSec / kNumMillisecsPerSec; +static const int64_t kNumNanosecsPerMicrosec = + kNumNanosecsPerSec / kNumMicrosecsPerSec; + +// TODO(honghaiz): Define a type for the time value specifically. + +class ClockInterface { + public: + virtual ~ClockInterface() {} + virtual int64_t TimeNanos() const = 0; +}; + +// Sets the global source of time. This is useful mainly for unit tests. +// +// Returns the previously set ClockInterface, or nullptr if none is set. +// +// Does not transfer ownership of the clock. SetClockForTesting(nullptr) +// should be called before the ClockInterface is deleted. +// +// This method is not thread-safe; it should only be used when no other thread +// is running (for example, at the start/end of a unit test, or start/end of +// main()). +// +// TODO(deadbeef): Instead of having functions that access this global +// ClockInterface, we may want to pass the ClockInterface into everything +// that uses it, eliminating the need for a global variable and this function. +RTC_EXPORT ClockInterface* SetClockForTesting(ClockInterface* clock); + +// Returns previously set clock, or nullptr if no custom clock is being used. +RTC_EXPORT ClockInterface* GetClockForTesting(); + +#if defined(WINUWP) +// Synchronizes the current clock based upon an NTP server's epoch in +// milliseconds. +void SyncWithNtp(int64_t time_from_ntp_server_ms); +#endif // defined(WINUWP) + +// Returns the actual system time, even if a clock is set for testing. +// Useful for timeouts while using a test clock, or for logging. +int64_t SystemTimeNanos(); +int64_t SystemTimeMillis(); + +// Returns the current time in milliseconds in 32 bits. +uint32_t Time32(); + +// Returns the current time in milliseconds in 64 bits. +RTC_EXPORT int64_t TimeMillis(); +// Deprecated. Do not use this in any new code. +inline int64_t Time() { + return TimeMillis(); +} + +// Returns the current time in microseconds. +RTC_EXPORT int64_t TimeMicros(); + +// Returns the current time in nanoseconds. +RTC_EXPORT int64_t TimeNanos(); + +// Returns a future timestamp, 'elapsed' milliseconds from now. +int64_t TimeAfter(int64_t elapsed); + +// Number of milliseconds that would elapse between 'earlier' and 'later' +// timestamps. The value is negative if 'later' occurs before 'earlier'. +int64_t TimeDiff(int64_t later, int64_t earlier); +int32_t TimeDiff32(uint32_t later, uint32_t earlier); + +// The number of milliseconds that have elapsed since 'earlier'. +inline int64_t TimeSince(int64_t earlier) { + return TimeMillis() - earlier; +} + +// The number of milliseconds that will elapse between now and 'later'. +inline int64_t TimeUntil(int64_t later) { + return later - TimeMillis(); +} + +class TimestampWrapAroundHandler { + public: + TimestampWrapAroundHandler(); + + int64_t Unwrap(uint32_t ts); + + private: + uint32_t last_ts_; + int64_t num_wrap_; +}; + +// Convert from tm, which is relative to 1900-01-01 00:00 to number of +// seconds from 1970-01-01 00:00 ("epoch"). Don't return time_t since that +// is still 32 bits on many systems. +int64_t TmToSeconds(const tm& tm); + +// Return the number of microseconds since January 1, 1970, UTC. +// Useful mainly when producing logs to be correlated with other +// devices, and when the devices in question all have properly +// synchronized clocks. +// +// Note that this function obeys the system's idea about what the time +// is. It is not guaranteed to be monotonic; it will jump in case the +// system time is changed, e.g., by some other process calling +// settimeofday. Always use rtc::TimeMicros(), not this function, for +// measuring time intervals and timeouts. +int64_t TimeUTCMicros(); + +// Return the number of milliseconds since January 1, 1970, UTC. +// See above. +int64_t TimeUTCMillis(); + +} // namespace rtc + +#endif // RTC_BASE_TIME_UTILS_H_ diff --git a/webrtc/rtc_base/trace_event.h b/webrtc/rtc_base/trace_event.h new file mode 100644 index 0000000..a0b788f --- /dev/null +++ b/webrtc/rtc_base/trace_event.h @@ -0,0 +1,1022 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file under third_party_mods/chromium or at: +// http://src.chromium.org/svn/trunk/src/LICENSE + +#ifndef RTC_BASE_TRACE_EVENT_H_ +#define RTC_BASE_TRACE_EVENT_H_ + +#include + +#include "rtc_base/event_tracer.h" + +#if defined(TRACE_EVENT0) +#error "Another copy of trace_event.h has already been included." +#endif + +#if defined(RTC_DISABLE_TRACE_EVENTS) +#define RTC_TRACE_EVENTS_ENABLED 0 +#else +#define RTC_TRACE_EVENTS_ENABLED 1 +#endif + +// Type values for identifying types in the TraceValue union. +#define TRACE_VALUE_TYPE_BOOL (static_cast(1)) +#define TRACE_VALUE_TYPE_UINT (static_cast(2)) +#define TRACE_VALUE_TYPE_INT (static_cast(3)) +#define TRACE_VALUE_TYPE_DOUBLE (static_cast(4)) +#define TRACE_VALUE_TYPE_POINTER (static_cast(5)) +#define TRACE_VALUE_TYPE_STRING (static_cast(6)) +#define TRACE_VALUE_TYPE_COPY_STRING (static_cast(7)) + +#if RTC_TRACE_EVENTS_ENABLED + +// Extracted from Chromium's src/base/debug/trace_event.h. + +// This header is designed to give you trace_event macros without specifying +// how the events actually get collected and stored. If you need to expose trace +// event to some other universe, you can copy-and-paste this file, +// implement the TRACE_EVENT_API macros, and do any other necessary fixup for +// the target platform. The end result is that multiple libraries can funnel +// events through to a shared trace event collector. + +// Trace events are for tracking application performance and resource usage. +// Macros are provided to track: +// Begin and end of function calls +// Counters +// +// Events are issued against categories. Whereas RTC_LOG's +// categories are statically defined, TRACE categories are created +// implicitly with a string. For example: +// TRACE_EVENT_INSTANT0("MY_SUBSYSTEM", "SomeImportantEvent") +// +// Events can be INSTANT, or can be pairs of BEGIN and END in the same scope: +// TRACE_EVENT_BEGIN0("MY_SUBSYSTEM", "SomethingCostly") +// doSomethingCostly() +// TRACE_EVENT_END0("MY_SUBSYSTEM", "SomethingCostly") +// Note: our tools can't always determine the correct BEGIN/END pairs unless +// these are used in the same scope. Use ASYNC_BEGIN/ASYNC_END macros if you +// need them to be in separate scopes. +// +// A common use case is to trace entire function scopes. This +// issues a trace BEGIN and END automatically: +// void doSomethingCostly() { +// TRACE_EVENT0("MY_SUBSYSTEM", "doSomethingCostly"); +// ... +// } +// +// Additional parameters can be associated with an event: +// void doSomethingCostly2(int howMuch) { +// TRACE_EVENT1("MY_SUBSYSTEM", "doSomethingCostly", +// "howMuch", howMuch); +// ... +// } +// +// The trace system will automatically add to this information the +// current process id, thread id, and a timestamp in microseconds. +// +// To trace an asynchronous procedure such as an IPC send/receive, use +// ASYNC_BEGIN and ASYNC_END: +// [single threaded sender code] +// static int send_count = 0; +// ++send_count; +// TRACE_EVENT_ASYNC_BEGIN0("ipc", "message", send_count); +// Send(new MyMessage(send_count)); +// [receive code] +// void OnMyMessage(send_count) { +// TRACE_EVENT_ASYNC_END0("ipc", "message", send_count); +// } +// The third parameter is a unique ID to match ASYNC_BEGIN/ASYNC_END pairs. +// ASYNC_BEGIN and ASYNC_END can occur on any thread of any traced process. +// Pointers can be used for the ID parameter, and they will be mangled +// internally so that the same pointer on two different processes will not +// match. For example: +// class MyTracedClass { +// public: +// MyTracedClass() { +// TRACE_EVENT_ASYNC_BEGIN0("category", "MyTracedClass", this); +// } +// ~MyTracedClass() { +// TRACE_EVENT_ASYNC_END0("category", "MyTracedClass", this); +// } +// } +// +// Trace event also supports counters, which is a way to track a quantity +// as it varies over time. Counters are created with the following macro: +// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter", g_myCounterValue); +// +// Counters are process-specific. The macro itself can be issued from any +// thread, however. +// +// Sometimes, you want to track two counters at once. You can do this with two +// counter macros: +// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter0", g_myCounterValue[0]); +// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter1", g_myCounterValue[1]); +// Or you can do it with a combined macro: +// TRACE_COUNTER2("MY_SUBSYSTEM", "myCounter", +// "bytesPinned", g_myCounterValue[0], +// "bytesAllocated", g_myCounterValue[1]); +// This indicates to the tracing UI that these counters should be displayed +// in a single graph, as a summed area chart. +// +// Since counters are in a global namespace, you may want to disembiguate with a +// unique ID, by using the TRACE_COUNTER_ID* variations. +// +// By default, trace collection is compiled in, but turned off at runtime. +// Collecting trace data is the responsibility of the embedding +// application. In Chrome's case, navigating to about:tracing will turn on +// tracing and display data collected across all active processes. +// +// +// Memory scoping note: +// Tracing copies the pointers, not the string content, of the strings passed +// in for category, name, and arg_names. Thus, the following code will +// cause problems: +// char* str = strdup("impprtantName"); +// TRACE_EVENT_INSTANT0("SUBSYSTEM", str); // BAD! +// free(str); // Trace system now has dangling pointer +// +// To avoid this issue with the |name| and |arg_name| parameters, use the +// TRACE_EVENT_COPY_XXX overloads of the macros at additional runtime overhead. +// Notes: The category must always be in a long-lived char* (i.e. static const). +// The |arg_values|, when used, are always deep copied with the _COPY +// macros. +// +// When are string argument values copied: +// const char* arg_values are only referenced by default: +// TRACE_EVENT1("category", "name", +// "arg1", "literal string is only referenced"); +// Use TRACE_STR_COPY to force copying of a const char*: +// TRACE_EVENT1("category", "name", +// "arg1", TRACE_STR_COPY("string will be copied")); +// std::string arg_values are always copied: +// TRACE_EVENT1("category", "name", +// "arg1", std::string("string will be copied")); +// +// +// Thread Safety: +// Thread safety is provided by methods defined in event_tracer.h. See the file +// for details. + +// By default, const char* argument values are assumed to have long-lived scope +// and will not be copied. Use this macro to force a const char* to be copied. +#define TRACE_STR_COPY(str) \ + webrtc::trace_event_internal::TraceStringWithCopy(str) + +// This will mark the trace event as disabled by default. The user will need +// to explicitly enable the event. +#define TRACE_DISABLED_BY_DEFAULT(name) "disabled-by-default-" name + +// By default, uint64 ID argument values are not mangled with the Process ID in +// TRACE_EVENT_ASYNC macros. Use this macro to force Process ID mangling. +#define TRACE_ID_MANGLE(id) \ + webrtc::trace_event_internal::TraceID::ForceMangle(id) + +// Records a pair of begin and end events called "name" for the current +// scope, with 0, 1 or 2 associated arguments. If the category is not +// enabled, then this does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT0(category, name) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name) +#define TRACE_EVENT1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, arg1_name, arg1_val) +#define TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) + +// Records a single event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT_INSTANT0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \ + category, name, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \ + category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \ + category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ + arg2_name, arg2_val) +#define TRACE_EVENT_COPY_INSTANT0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \ + category, name, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_INSTANT1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \ + category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_INSTANT2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \ + category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ + arg2_name, arg2_val) + +// Records a single BEGIN event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT_BEGIN0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \ + category, name, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_BEGIN1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \ + category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_BEGIN2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \ + category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ + arg2_name, arg2_val) +#define TRACE_EVENT_COPY_BEGIN0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \ + category, name, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_BEGIN1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \ + category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_BEGIN2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \ + category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ + arg2_name, arg2_val) + +// Records a single END event for "name" immediately. If the category +// is not enabled, then this does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT_END0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \ + category, name, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_END1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \ + category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_END2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \ + category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ + arg2_name, arg2_val) +#define TRACE_EVENT_COPY_END0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \ + category, name, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_END1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \ + category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_END2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \ + category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ + arg2_name, arg2_val) + +// Records the value of a counter called "name" immediately. Value +// must be representable as a 32 bit integer. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_COUNTER1(category, name, value) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \ + category, name, TRACE_EVENT_FLAG_NONE, \ + "value", static_cast(value)) +#define TRACE_COPY_COUNTER1(category, name, value) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \ + category, name, TRACE_EVENT_FLAG_COPY, \ + "value", static_cast(value)) + +// Records the values of a multi-parted counter called "name" immediately. +// The UI will treat value1 and value2 as parts of a whole, displaying their +// values as a stacked-bar chart. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_COUNTER2(category, name, value1_name, value1_val, \ + value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \ + category, name, TRACE_EVENT_FLAG_NONE, \ + value1_name, static_cast(value1_val), \ + value2_name, static_cast(value2_val)) +#define TRACE_COPY_COUNTER2(category, name, value1_name, value1_val, \ + value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \ + category, name, TRACE_EVENT_FLAG_COPY, \ + value1_name, static_cast(value1_val), \ + value2_name, static_cast(value2_val)) + +// Records the value of a counter called "name" immediately. Value +// must be representable as a 32 bit integer. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to disambiguate counters with the same name. It must either +// be a pointer or an integer value up to 64 bits. If it's a pointer, the bits +// will be xored with a hash of the process ID so that the same pointer on +// two different processes will not collide. +#define TRACE_COUNTER_ID1(category, name, id, value) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \ + category, name, id, TRACE_EVENT_FLAG_NONE, \ + "value", static_cast(value)) +#define TRACE_COPY_COUNTER_ID1(category, name, id, value) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \ + category, name, id, TRACE_EVENT_FLAG_COPY, \ + "value", static_cast(value)) + +// Records the values of a multi-parted counter called "name" immediately. +// The UI will treat value1 and value2 as parts of a whole, displaying their +// values as a stacked-bar chart. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to disambiguate counters with the same name. It must either +// be a pointer or an integer value up to 64 bits. If it's a pointer, the bits +// will be xored with a hash of the process ID so that the same pointer on +// two different processes will not collide. +#define TRACE_COUNTER_ID2(category, name, id, value1_name, value1_val, \ + value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \ + category, name, id, TRACE_EVENT_FLAG_NONE, \ + value1_name, static_cast(value1_val), \ + value2_name, static_cast(value2_val)) +#define TRACE_COPY_COUNTER_ID2(category, name, id, value1_name, value1_val, \ + value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \ + category, name, id, TRACE_EVENT_FLAG_COPY, \ + value1_name, static_cast(value1_val), \ + value2_name, static_cast(value2_val)) + + +// Records a single ASYNC_BEGIN event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to match the ASYNC_BEGIN event with the ASYNC_END event. ASYNC +// events are considered to match if their category, name and id values all +// match. |id| must either be a pointer or an integer value up to 64 bits. If +// it's a pointer, the bits will be xored with a hash of the process ID so +// that the same pointer on two different processes will not collide. +// An asynchronous operation can consist of multiple phases. The first phase is +// defined by the ASYNC_BEGIN calls. Additional phases can be defined using the +// ASYNC_STEP macros. When the operation completes, call ASYNC_END. +// An ASYNC trace typically occur on a single thread (if not, they will only be +// drawn on the thread defined in the ASYNC_BEGIN event), but all events in that +// operation must use the same |name| and |id|. Each event can have its own +// args. +#define TRACE_EVENT_ASYNC_BEGIN0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_NONE, \ + arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_ASYNC_BEGIN0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val) +#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val, arg2_name, arg2_val) + +// Records a single ASYNC_STEP event for |step| immediately. If the category +// is not enabled, then this does nothing. The |name| and |id| must match the +// ASYNC_BEGIN event above. The |step| param identifies this step within the +// async event. This should be called at the beginning of the next phase of an +// asynchronous operation. +#define TRACE_EVENT_ASYNC_STEP0(category, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \ + category, name, id, TRACE_EVENT_FLAG_NONE, "step", step) +#define TRACE_EVENT_ASYNC_STEP1(category, name, id, step, \ + arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \ + category, name, id, TRACE_EVENT_FLAG_NONE, "step", step, \ + arg1_name, arg1_val) +#define TRACE_EVENT_COPY_ASYNC_STEP0(category, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \ + category, name, id, TRACE_EVENT_FLAG_COPY, "step", step) +#define TRACE_EVENT_COPY_ASYNC_STEP1(category, name, id, step, \ + arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \ + category, name, id, TRACE_EVENT_FLAG_COPY, "step", step, \ + arg1_name, arg1_val) + +// Records a single ASYNC_END event for "name" immediately. If the category +// is not enabled, then this does nothing. +#define TRACE_EVENT_ASYNC_END0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \ + category, name, id, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_ASYNC_END1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \ + category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_ASYNC_END2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \ + category, name, id, TRACE_EVENT_FLAG_NONE, \ + arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_ASYNC_END0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \ + category, name, id, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_ASYNC_END1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \ + category, name, id, TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val) +#define TRACE_EVENT_COPY_ASYNC_END2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \ + category, name, id, TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val, arg2_name, arg2_val) + + +// Records a single FLOW_BEGIN event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to match the FLOW_BEGIN event with the FLOW_END event. FLOW +// events are considered to match if their category, name and id values all +// match. |id| must either be a pointer or an integer value up to 64 bits. If +// it's a pointer, the bits will be xored with a hash of the process ID so +// that the same pointer on two different processes will not collide. +// FLOW events are different from ASYNC events in how they are drawn by the +// tracing UI. A FLOW defines asynchronous data flow, such as posting a task +// (FLOW_BEGIN) and later executing that task (FLOW_END). Expect FLOWs to be +// drawn as lines or arrows from FLOW_BEGIN scopes to FLOW_END scopes. Similar +// to ASYNC, a FLOW can consist of multiple phases. The first phase is defined +// by the FLOW_BEGIN calls. Additional phases can be defined using the FLOW_STEP +// macros. When the operation completes, call FLOW_END. An async operation can +// span threads and processes, but all events in that operation must use the +// same |name| and |id|. Each event can have its own args. +#define TRACE_EVENT_FLOW_BEGIN0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_NONE, \ + arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_FLOW_BEGIN0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val) +#define TRACE_EVENT_COPY_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \ + category, name, id, TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val, arg2_name, arg2_val) + +// Records a single FLOW_STEP event for |step| immediately. If the category +// is not enabled, then this does nothing. The |name| and |id| must match the +// FLOW_BEGIN event above. The |step| param identifies this step within the +// async event. This should be called at the beginning of the next phase of an +// asynchronous operation. +#define TRACE_EVENT_FLOW_STEP0(category, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \ + category, name, id, TRACE_EVENT_FLAG_NONE, "step", step) +#define TRACE_EVENT_FLOW_STEP1(category, name, id, step, \ + arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \ + category, name, id, TRACE_EVENT_FLAG_NONE, "step", step, \ + arg1_name, arg1_val) +#define TRACE_EVENT_COPY_FLOW_STEP0(category, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \ + category, name, id, TRACE_EVENT_FLAG_COPY, "step", step) +#define TRACE_EVENT_COPY_FLOW_STEP1(category, name, id, step, \ + arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \ + category, name, id, TRACE_EVENT_FLAG_COPY, "step", step, \ + arg1_name, arg1_val) + +// Records a single FLOW_END event for "name" immediately. If the category +// is not enabled, then this does nothing. +#define TRACE_EVENT_FLOW_END0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \ + category, name, id, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_FLOW_END1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \ + category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_FLOW_END2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \ + category, name, id, TRACE_EVENT_FLAG_NONE, \ + arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_FLOW_END0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \ + category, name, id, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_FLOW_END1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \ + category, name, id, TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val) +#define TRACE_EVENT_COPY_FLOW_END2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \ + category, name, id, TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val, arg2_name, arg2_val) + + +//////////////////////////////////////////////////////////////////////////////// +// Implementation specific tracing API definitions. + +// Get a pointer to the enabled state of the given trace category. Only +// long-lived literal strings should be given as the category name. The returned +// pointer can be held permanently in a local static for example. If the +// unsigned char is non-zero, tracing is enabled. If tracing is enabled, +// TRACE_EVENT_API_ADD_TRACE_EVENT can be called. It's OK if tracing is disabled +// between the load of the tracing state and the call to +// TRACE_EVENT_API_ADD_TRACE_EVENT, because this flag only provides an early out +// for best performance when tracing is disabled. +// const unsigned char* +// TRACE_EVENT_API_GET_CATEGORY_ENABLED(const char* category_name) +#define TRACE_EVENT_API_GET_CATEGORY_ENABLED \ + webrtc::EventTracer::GetCategoryEnabled + +// Add a trace event to the platform tracing system. +// void TRACE_EVENT_API_ADD_TRACE_EVENT( +// char phase, +// const unsigned char* category_enabled, +// const char* name, +// unsigned long long id, +// int num_args, +// const char** arg_names, +// const unsigned char* arg_types, +// const unsigned long long* arg_values, +// unsigned char flags) +#define TRACE_EVENT_API_ADD_TRACE_EVENT webrtc::EventTracer::AddTraceEvent + +//////////////////////////////////////////////////////////////////////////////// + +// Implementation detail: trace event macros create temporary variables +// to keep instrumentation overhead low. These macros give each temporary +// variable a unique name based on the line number to prevent name collissions. +#define INTERNAL_TRACE_EVENT_UID3(a,b) \ + trace_event_unique_##a##b +#define INTERNAL_TRACE_EVENT_UID2(a,b) \ + INTERNAL_TRACE_EVENT_UID3(a,b) +#define INTERNAL_TRACE_EVENT_UID(name_prefix) \ + INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__) + +#if WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS +#define INTERNAL_TRACE_EVENT_INFO_TYPE const unsigned char* +#else +#define INTERNAL_TRACE_EVENT_INFO_TYPE static const unsigned char* +#endif // WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS + +// Implementation detail: internal macro to create static category. +#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category) \ + INTERNAL_TRACE_EVENT_INFO_TYPE INTERNAL_TRACE_EVENT_UID(catstatic) = \ + TRACE_EVENT_API_GET_CATEGORY_ENABLED(category); + +// Implementation detail: internal macro to create static category and add +// event if the category is enabled. +#define INTERNAL_TRACE_EVENT_ADD(phase, category, name, flags, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \ + if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \ + webrtc::trace_event_internal::AddTraceEvent( \ + phase, INTERNAL_TRACE_EVENT_UID(catstatic), name, \ + webrtc::trace_event_internal::kNoEventId, flags, ##__VA_ARGS__); \ + } \ + } while (0) + +// Implementation detail: internal macro to create static category and add begin +// event if the category is enabled. Also adds the end event when the scope +// ends. +#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, ...) \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \ + webrtc::trace_event_internal::TraceEndOnScopeClose \ + INTERNAL_TRACE_EVENT_UID(profileScope); \ + if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \ + webrtc::trace_event_internal::AddTraceEvent( \ + TRACE_EVENT_PHASE_BEGIN, \ + INTERNAL_TRACE_EVENT_UID(catstatic), \ + name, webrtc::trace_event_internal::kNoEventId, \ + TRACE_EVENT_FLAG_NONE, ##__VA_ARGS__); \ + INTERNAL_TRACE_EVENT_UID(profileScope).Initialize( \ + INTERNAL_TRACE_EVENT_UID(catstatic), name); \ + } + +// Implementation detail: internal macro to create static category and add +// event if the category is enabled. +#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category, name, id, flags, \ + ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \ + if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \ + unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \ + webrtc::trace_event_internal::TraceID trace_event_trace_id( \ + id, &trace_event_flags); \ + webrtc::trace_event_internal::AddTraceEvent( \ + phase, INTERNAL_TRACE_EVENT_UID(catstatic), \ + name, trace_event_trace_id.data(), trace_event_flags, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +// Notes regarding the following definitions: +// New values can be added and propagated to third party libraries, but existing +// definitions must never be changed, because third party libraries may use old +// definitions. + +// Phase indicates the nature of an event entry. E.g. part of a begin/end pair. +#define TRACE_EVENT_PHASE_BEGIN ('B') +#define TRACE_EVENT_PHASE_END ('E') +#define TRACE_EVENT_PHASE_INSTANT ('I') +#define TRACE_EVENT_PHASE_ASYNC_BEGIN ('S') +#define TRACE_EVENT_PHASE_ASYNC_STEP ('T') +#define TRACE_EVENT_PHASE_ASYNC_END ('F') +#define TRACE_EVENT_PHASE_FLOW_BEGIN ('s') +#define TRACE_EVENT_PHASE_FLOW_STEP ('t') +#define TRACE_EVENT_PHASE_FLOW_END ('f') +#define TRACE_EVENT_PHASE_METADATA ('M') +#define TRACE_EVENT_PHASE_COUNTER ('C') + +// Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT. +#define TRACE_EVENT_FLAG_NONE (static_cast(0)) +#define TRACE_EVENT_FLAG_COPY (static_cast(1 << 0)) +#define TRACE_EVENT_FLAG_HAS_ID (static_cast(1 << 1)) +#define TRACE_EVENT_FLAG_MANGLE_ID (static_cast(1 << 2)) + +namespace webrtc { +namespace trace_event_internal { + +// Specify these values when the corresponding argument of AddTraceEvent is not +// used. +const int kZeroNumArgs = 0; +const unsigned long long kNoEventId = 0; + +// TraceID encapsulates an ID that can either be an integer or pointer. Pointers +// are mangled with the Process ID so that they are unlikely to collide when the +// same pointer is used on different processes. +class TraceID { + public: + class ForceMangle { + public: + explicit ForceMangle(unsigned long long id) : data_(id) {} + explicit ForceMangle(unsigned long id) : data_(id) {} + explicit ForceMangle(unsigned int id) : data_(id) {} + explicit ForceMangle(unsigned short id) : data_(id) {} + explicit ForceMangle(unsigned char id) : data_(id) {} + explicit ForceMangle(long long id) + : data_(static_cast(id)) {} + explicit ForceMangle(long id) + : data_(static_cast(id)) {} + explicit ForceMangle(int id) + : data_(static_cast(id)) {} + explicit ForceMangle(short id) + : data_(static_cast(id)) {} + explicit ForceMangle(signed char id) + : data_(static_cast(id)) {} + + unsigned long long data() const { return data_; } + + private: + unsigned long long data_; + }; + + explicit TraceID(const void* id, unsigned char* flags) + : data_(static_cast( + reinterpret_cast(id))) { + *flags |= TRACE_EVENT_FLAG_MANGLE_ID; + } + explicit TraceID(ForceMangle id, unsigned char* flags) : data_(id.data()) { + *flags |= TRACE_EVENT_FLAG_MANGLE_ID; + } + explicit TraceID(unsigned long long id, unsigned char* flags) + : data_(id) { (void)flags; } + explicit TraceID(unsigned long id, unsigned char* flags) + : data_(id) { (void)flags; } + explicit TraceID(unsigned int id, unsigned char* flags) + : data_(id) { (void)flags; } + explicit TraceID(unsigned short id, unsigned char* flags) + : data_(id) { (void)flags; } + explicit TraceID(unsigned char id, unsigned char* flags) + : data_(id) { (void)flags; } + explicit TraceID(long long id, unsigned char* flags) + : data_(static_cast(id)) { (void)flags; } + explicit TraceID(long id, unsigned char* flags) + : data_(static_cast(id)) { (void)flags; } + explicit TraceID(int id, unsigned char* flags) + : data_(static_cast(id)) { (void)flags; } + explicit TraceID(short id, unsigned char* flags) + : data_(static_cast(id)) { (void)flags; } + explicit TraceID(signed char id, unsigned char* flags) + : data_(static_cast(id)) { (void)flags; } + + unsigned long long data() const { return data_; } + + private: + unsigned long long data_; +}; + +// Simple union to store various types as unsigned long long. +union TraceValueUnion { + bool as_bool; + unsigned long long as_uint; + long long as_int; + double as_double; + const void* as_pointer; + const char* as_string; +}; + +// Simple container for const char* that should be copied instead of retained. +class TraceStringWithCopy { + public: + explicit TraceStringWithCopy(const char* str) : str_(str) {} + operator const char* () const { return str_; } + private: + const char* str_; +}; + +// Define SetTraceValue for each allowed type. It stores the type and +// value in the return arguments. This allows this API to avoid declaring any +// structures so that it is portable to third_party libraries. +#define INTERNAL_DECLARE_SET_TRACE_VALUE(actual_type, \ + union_member, \ + value_type_id) \ + static inline void SetTraceValue(actual_type arg, \ + unsigned char* type, \ + unsigned long long* value) { \ + TraceValueUnion type_value; \ + type_value.union_member = arg; \ + *type = value_type_id; \ + *value = type_value.as_uint; \ + } +// Simpler form for int types that can be safely casted. +#define INTERNAL_DECLARE_SET_TRACE_VALUE_INT(actual_type, \ + value_type_id) \ + static inline void SetTraceValue(actual_type arg, \ + unsigned char* type, \ + unsigned long long* value) { \ + *type = value_type_id; \ + *value = static_cast(arg); \ + } + +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned long long, TRACE_VALUE_TYPE_UINT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned long, TRACE_VALUE_TYPE_UINT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned int, TRACE_VALUE_TYPE_UINT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned short, TRACE_VALUE_TYPE_UINT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned char, TRACE_VALUE_TYPE_UINT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(long long, TRACE_VALUE_TYPE_INT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(long, TRACE_VALUE_TYPE_INT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int, TRACE_VALUE_TYPE_INT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(short, TRACE_VALUE_TYPE_INT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(signed char, TRACE_VALUE_TYPE_INT) +INTERNAL_DECLARE_SET_TRACE_VALUE(bool, as_bool, TRACE_VALUE_TYPE_BOOL) +INTERNAL_DECLARE_SET_TRACE_VALUE(double, as_double, TRACE_VALUE_TYPE_DOUBLE) +INTERNAL_DECLARE_SET_TRACE_VALUE(const void*, as_pointer, + TRACE_VALUE_TYPE_POINTER) +INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, as_string, + TRACE_VALUE_TYPE_STRING) +INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, as_string, + TRACE_VALUE_TYPE_COPY_STRING) + +#undef INTERNAL_DECLARE_SET_TRACE_VALUE +#undef INTERNAL_DECLARE_SET_TRACE_VALUE_INT + +// std::string version of SetTraceValue so that trace arguments can be strings. +static inline void SetTraceValue(const std::string& arg, + unsigned char* type, + unsigned long long* value) { + TraceValueUnion type_value; + type_value.as_string = arg.c_str(); + *type = TRACE_VALUE_TYPE_COPY_STRING; + *value = type_value.as_uint; +} + +// These AddTraceEvent template functions are defined here instead of in the +// macro, because the arg_values could be temporary objects, such as +// std::string. In order to store pointers to the internal c_str and pass +// through to the tracing API, the arg_values must live throughout +// these procedures. + +static inline void AddTraceEvent(char phase, + const unsigned char* category_enabled, + const char* name, + unsigned long long id, + unsigned char flags) { + TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_enabled, name, id, + kZeroNumArgs, nullptr, nullptr, nullptr, + flags); +} + +template +static inline void AddTraceEvent(char phase, + const unsigned char* category_enabled, + const char* name, + unsigned long long id, + unsigned char flags, + const char* arg1_name, + const ARG1_TYPE& arg1_val) { + const int num_args = 1; + unsigned char arg_types[1]; + unsigned long long arg_values[1]; + SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]); + TRACE_EVENT_API_ADD_TRACE_EVENT( + phase, category_enabled, name, id, + num_args, &arg1_name, arg_types, arg_values, + flags); +} + +template +static inline void AddTraceEvent(char phase, + const unsigned char* category_enabled, + const char* name, + unsigned long long id, + unsigned char flags, + const char* arg1_name, + const ARG1_TYPE& arg1_val, + const char* arg2_name, + const ARG2_TYPE& arg2_val) { + const int num_args = 2; + const char* arg_names[2] = { arg1_name, arg2_name }; + unsigned char arg_types[2]; + unsigned long long arg_values[2]; + SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]); + SetTraceValue(arg2_val, &arg_types[1], &arg_values[1]); + TRACE_EVENT_API_ADD_TRACE_EVENT( + phase, category_enabled, name, id, + num_args, arg_names, arg_types, arg_values, + flags); +} + +// Used by TRACE_EVENTx macro. Do not use directly. +class TraceEndOnScopeClose { + public: + // Note: members of data_ intentionally left uninitialized. See Initialize. + TraceEndOnScopeClose() : p_data_(nullptr) {} + ~TraceEndOnScopeClose() { + if (p_data_) + AddEventIfEnabled(); + } + + void Initialize(const unsigned char* category_enabled, + const char* name) { + data_.category_enabled = category_enabled; + data_.name = name; + p_data_ = &data_; + } + + private: + // Add the end event if the category is still enabled. + void AddEventIfEnabled() { + // Only called when p_data_ is non-null. + if (*p_data_->category_enabled) { + TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_END, + p_data_->category_enabled, p_data_->name, + kNoEventId, kZeroNumArgs, nullptr, + nullptr, nullptr, TRACE_EVENT_FLAG_NONE); + } + } + + // This Data struct workaround is to avoid initializing all the members + // in Data during construction of this object, since this object is always + // constructed, even when tracing is disabled. If the members of Data were + // members of this class instead, compiler warnings occur about potential + // uninitialized accesses. + struct Data { + const unsigned char* category_enabled; + const char* name; + }; + Data* p_data_; + Data data_; +}; + +} // namespace trace_event_internal +} // namespace webrtc +#else + +//////////////////////////////////////////////////////////////////////////////// +// This section defines no-op alternatives to the tracing macros when +// RTC_DISABLE_TRACE_EVENTS is defined. + +#define RTC_NOOP() do {} while (0) + +#define TRACE_STR_COPY(str) RTC_NOOP() + +#define TRACE_DISABLED_BY_DEFAULT(name) "disabled-by-default-" name + +#define TRACE_ID_MANGLE(id) 0 + +#define TRACE_EVENT0(category, name) RTC_NOOP() +#define TRACE_EVENT1(category, name, arg1_name, arg1_val) RTC_NOOP() +#define TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + RTC_NOOP() + +#define TRACE_EVENT_INSTANT0(category, name) RTC_NOOP() +#define TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) RTC_NOOP() + +#define TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() + +#define TRACE_EVENT_COPY_INSTANT0(category, name) RTC_NOOP() +#define TRACE_EVENT_COPY_INSTANT1(category, name, arg1_name, arg1_val) \ + RTC_NOOP() +#define TRACE_EVENT_COPY_INSTANT2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() + +#define TRACE_EVENT_BEGIN0(category, name) RTC_NOOP() +#define TRACE_EVENT_BEGIN1(category, name, arg1_name, arg1_val) RTC_NOOP() +#define TRACE_EVENT_BEGIN2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() +#define TRACE_EVENT_COPY_BEGIN0(category, name) RTC_NOOP() +#define TRACE_EVENT_COPY_BEGIN1(category, name, arg1_name, arg1_val) RTC_NOOP() +#define TRACE_EVENT_COPY_BEGIN2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() + +#define TRACE_EVENT_END0(category, name) RTC_NOOP() +#define TRACE_EVENT_END1(category, name, arg1_name, arg1_val) RTC_NOOP() +#define TRACE_EVENT_END2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() +#define TRACE_EVENT_COPY_END0(category, name) RTC_NOOP() +#define TRACE_EVENT_COPY_END1(category, name, arg1_name, arg1_val) RTC_NOOP() +#define TRACE_EVENT_COPY_END2(category, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() + +#define TRACE_COUNTER1(category, name, value) RTC_NOOP() +#define TRACE_COPY_COUNTER1(category, name, value) RTC_NOOP() + +#define TRACE_COUNTER2(category, name, value1_name, value1_val, \ + value2_name, value2_val) RTC_NOOP() +#define TRACE_COPY_COUNTER2(category, name, value1_name, value1_val, \ + value2_name, value2_val) RTC_NOOP() + +#define TRACE_COUNTER_ID1(category, name, id, value) RTC_NOOP() +#define TRACE_COPY_COUNTER_ID1(category, name, id, value) RTC_NOOP() + +#define TRACE_COUNTER_ID2(category, name, id, value1_name, value1_val, \ + value2_name, value2_val) RTC_NOOP() +#define TRACE_COPY_COUNTER_ID2(category, name, id, value1_name, value1_val, \ + value2_name, value2_val) RTC_NOOP() + +#define TRACE_EVENT_ASYNC_BEGIN0(category, name, id) RTC_NOOP() +#define TRACE_EVENT_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \ + RTC_NOOP() +#define TRACE_EVENT_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() +#define TRACE_EVENT_COPY_ASYNC_BEGIN0(category, name, id) RTC_NOOP() +#define TRACE_EVENT_COPY_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \ + RTC_NOOP() +#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() + +#define TRACE_EVENT_ASYNC_STEP0(category, name, id, step) RTC_NOOP() +#define TRACE_EVENT_ASYNC_STEP1(category, name, id, step, \ + arg1_name, arg1_val) RTC_NOOP() +#define TRACE_EVENT_COPY_ASYNC_STEP0(category, name, id, step) RTC_NOOP() +#define TRACE_EVENT_COPY_ASYNC_STEP1(category, name, id, step, \ + arg1_name, arg1_val) RTC_NOOP() + +#define TRACE_EVENT_ASYNC_END0(category, name, id) RTC_NOOP() +#define TRACE_EVENT_ASYNC_END1(category, name, id, arg1_name, arg1_val) \ + RTC_NOOP() +#define TRACE_EVENT_ASYNC_END2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() +#define TRACE_EVENT_COPY_ASYNC_END0(category, name, id) RTC_NOOP() +#define TRACE_EVENT_COPY_ASYNC_END1(category, name, id, arg1_name, arg1_val) \ + RTC_NOOP() +#define TRACE_EVENT_COPY_ASYNC_END2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() + +#define TRACE_EVENT_FLOW_BEGIN0(category, name, id) RTC_NOOP() +#define TRACE_EVENT_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \ + RTC_NOOP() +#define TRACE_EVENT_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() +#define TRACE_EVENT_COPY_FLOW_BEGIN0(category, name, id) RTC_NOOP() +#define TRACE_EVENT_COPY_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \ + RTC_NOOP() +#define TRACE_EVENT_COPY_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() + +#define TRACE_EVENT_FLOW_STEP0(category, name, id, step) RTC_NOOP() +#define TRACE_EVENT_FLOW_STEP1(category, name, id, step, \ + arg1_name, arg1_val) RTC_NOOP() +#define TRACE_EVENT_COPY_FLOW_STEP0(category, name, id, step) RTC_NOOP() +#define TRACE_EVENT_COPY_FLOW_STEP1(category, name, id, step, \ + arg1_name, arg1_val) RTC_NOOP() + +#define TRACE_EVENT_FLOW_END0(category, name, id) RTC_NOOP() +#define TRACE_EVENT_FLOW_END1(category, name, id, arg1_name, arg1_val) \ + RTC_NOOP() +#define TRACE_EVENT_FLOW_END2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() +#define TRACE_EVENT_COPY_FLOW_END0(category, name, id) RTC_NOOP() +#define TRACE_EVENT_COPY_FLOW_END1(category, name, id, arg1_name, arg1_val) \ + RTC_NOOP() +#define TRACE_EVENT_COPY_FLOW_END2(category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) RTC_NOOP() + +#define TRACE_EVENT_API_GET_CATEGORY_ENABLED "" + +#define TRACE_EVENT_API_ADD_TRACE_EVENT RTC_NOOP() + +#endif // RTC_TRACE_EVENTS_ENABLED + +#endif // RTC_BASE_TRACE_EVENT_H_ \ No newline at end of file diff --git a/webrtc/rtc_base/type_traits.h b/webrtc/rtc_base/type_traits.h new file mode 100644 index 0000000..0cb899c --- /dev/null +++ b/webrtc/rtc_base/type_traits.h @@ -0,0 +1,140 @@ +/* + * Copyright 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. + */ + +#ifndef RTC_BASE_TYPE_TRAITS_H_ +#define RTC_BASE_TYPE_TRAITS_H_ + +#include +#include + +namespace rtc { + +// Determines if the given class has zero-argument .data() and .size() methods +// whose return values are convertible to T* and size_t, respectively. +template +class HasDataAndSize { + private: + template < + typename C, + typename std::enable_if< + std::is_convertible().data()), T*>::value && + std::is_convertible().size()), + std::size_t>::value>::type* = nullptr> + static int Test(int); + + template + static char Test(...); + + public: + static constexpr bool value = std::is_same(0)), int>::value; +}; + +namespace test_has_data_and_size { + +template +struct Test1 { + DR data(); + SR size(); +}; +static_assert(HasDataAndSize, int>::value, ""); +static_assert(HasDataAndSize, const int>::value, ""); +static_assert(HasDataAndSize, const int>::value, ""); +static_assert(!HasDataAndSize, int>::value, + "implicit cast of const int* to int*"); +static_assert(!HasDataAndSize, int>::value, + "implicit cast of char* to int*"); + +struct Test2 { + int* data; + size_t size; +}; +static_assert(!HasDataAndSize::value, + ".data and .size aren't functions"); + +struct Test3 { + int* data(); +}; +static_assert(!HasDataAndSize::value, ".size() is missing"); + +class Test4 { + int* data(); + size_t size(); +}; +static_assert(!HasDataAndSize::value, + ".data() and .size() are private"); + +} // namespace test_has_data_and_size + +namespace type_traits_impl { + +// Determines if the given type is an enum that converts implicitly to +// an integral type. +template +struct IsIntEnum { + private: + // This overload is used if the type is an enum, and unary plus + // compiles and turns it into an integral type. + template ::value && + std::is_integral())>::value>::type* = + nullptr> + static int Test(int); + + // Otherwise, this overload is used. + template + static char Test(...); + + public: + static constexpr bool value = + std::is_same::type>(0)), + int>::value; +}; + +} // namespace type_traits_impl + +// Determines if the given type is integral, or an enum that +// converts implicitly to an integral type. +template +struct IsIntlike { + private: + using X = typename std::remove_reference::type; + + public: + static constexpr bool value = + std::is_integral::value || type_traits_impl::IsIntEnum::value; +}; + +namespace test_enum_intlike { + +enum E1 { e1 }; +enum { e2 }; +enum class E3 { e3 }; +struct S {}; + +static_assert(type_traits_impl::IsIntEnum::value, ""); +static_assert(type_traits_impl::IsIntEnum::value, ""); +static_assert(!type_traits_impl::IsIntEnum::value, ""); +static_assert(!type_traits_impl::IsIntEnum::value, ""); +static_assert(!type_traits_impl::IsIntEnum::value, ""); +static_assert(!type_traits_impl::IsIntEnum::value, ""); + +static_assert(IsIntlike::value, ""); +static_assert(IsIntlike::value, ""); +static_assert(!IsIntlike::value, ""); +static_assert(IsIntlike::value, ""); +static_assert(!IsIntlike::value, ""); +static_assert(!IsIntlike::value, ""); + +} // namespace test_enum_intlike + +} // namespace rtc + +#endif // RTC_BASE_TYPE_TRAITS_H_ diff --git a/webrtc/rtc_base/units/BUILD.gn b/webrtc/rtc_base/units/BUILD.gn new file mode 100644 index 0000000..e2ab873 --- /dev/null +++ b/webrtc/rtc_base/units/BUILD.gn @@ -0,0 +1,33 @@ +# Copyright (c) 2018 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. + +import("../../webrtc.gni") + +rtc_source_set("unit_base") { + visibility = [ + "../../api/units:*", + ":*", + ] + sources = [ "unit_base.h" ] + + deps = [ + "../../rtc_base:checks", + "../../rtc_base:safe_conversions", + ] +} + +if (rtc_include_tests) { + rtc_library("units_unittests") { + testonly = true + sources = [ "unit_base_unittest.cc" ] + deps = [ + ":unit_base", + "../../test:test_support", + ] + } +} diff --git a/webrtc/rtc_base/units/unit_base.h b/webrtc/rtc_base/units/unit_base.h new file mode 100644 index 0000000..7196bae --- /dev/null +++ b/webrtc/rtc_base/units/unit_base.h @@ -0,0 +1,306 @@ +/* + * Copyright 2018 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. + */ +#ifndef RTC_BASE_UNITS_UNIT_BASE_H_ +#define RTC_BASE_UNITS_UNIT_BASE_H_ + +#include + +#include +#include +#include +#include + +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" + +namespace webrtc { +namespace rtc_units_impl { + +// UnitBase is a base class for implementing custom value types with a specific +// unit. It provides type safety and commonly useful operations. The underlying +// storage is always an int64_t, it's up to the unit implementation to choose +// what scale it represents. +// +// It's used like: +// class MyUnit: public UnitBase {...}; +// +// Unit_T is the subclass representing the specific unit. +template +class UnitBase { + public: + UnitBase() = delete; + static constexpr Unit_T Zero() { return Unit_T(0); } + static constexpr Unit_T PlusInfinity() { return Unit_T(PlusInfinityVal()); } + static constexpr Unit_T MinusInfinity() { return Unit_T(MinusInfinityVal()); } + + constexpr bool IsZero() const { return value_ == 0; } + constexpr bool IsFinite() const { return !IsInfinite(); } + constexpr bool IsInfinite() const { + return value_ == PlusInfinityVal() || value_ == MinusInfinityVal(); + } + constexpr bool IsPlusInfinity() const { return value_ == PlusInfinityVal(); } + constexpr bool IsMinusInfinity() const { + return value_ == MinusInfinityVal(); + } + + constexpr bool operator==(const Unit_T& other) const { + return value_ == other.value_; + } + constexpr bool operator!=(const Unit_T& other) const { + return value_ != other.value_; + } + constexpr bool operator<=(const Unit_T& other) const { + return value_ <= other.value_; + } + constexpr bool operator>=(const Unit_T& other) const { + return value_ >= other.value_; + } + constexpr bool operator>(const Unit_T& other) const { + return value_ > other.value_; + } + constexpr bool operator<(const Unit_T& other) const { + return value_ < other.value_; + } + constexpr Unit_T RoundTo(const Unit_T& resolution) const { + RTC_DCHECK(IsFinite()); + RTC_DCHECK(resolution.IsFinite()); + RTC_DCHECK_GT(resolution.value_, 0); + return Unit_T((value_ + resolution.value_ / 2) / resolution.value_) * + resolution.value_; + } + constexpr Unit_T RoundUpTo(const Unit_T& resolution) const { + RTC_DCHECK(IsFinite()); + RTC_DCHECK(resolution.IsFinite()); + RTC_DCHECK_GT(resolution.value_, 0); + return Unit_T((value_ + resolution.value_ - 1) / resolution.value_) * + resolution.value_; + } + constexpr Unit_T RoundDownTo(const Unit_T& resolution) const { + RTC_DCHECK(IsFinite()); + RTC_DCHECK(resolution.IsFinite()); + RTC_DCHECK_GT(resolution.value_, 0); + return Unit_T(value_ / resolution.value_) * resolution.value_; + } + + protected: + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static constexpr Unit_T FromValue(T value) { + if (Unit_T::one_sided) + RTC_DCHECK_GE(value, 0); + RTC_DCHECK_GT(value, MinusInfinityVal()); + RTC_DCHECK_LT(value, PlusInfinityVal()); + return Unit_T(rtc::dchecked_cast(value)); + } + template ::value>::type* = + nullptr> + static constexpr Unit_T FromValue(T value) { + if (value == std::numeric_limits::infinity()) { + return PlusInfinity(); + } else if (value == -std::numeric_limits::infinity()) { + return MinusInfinity(); + } else { + RTC_DCHECK(!std::isnan(value)); + return FromValue(rtc::dchecked_cast(value)); + } + } + + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static constexpr Unit_T FromFraction(int64_t denominator, T value) { + if (Unit_T::one_sided) + RTC_DCHECK_GE(value, 0); + RTC_DCHECK_GT(value, MinusInfinityVal() / denominator); + RTC_DCHECK_LT(value, PlusInfinityVal() / denominator); + return Unit_T(rtc::dchecked_cast(value * denominator)); + } + template ::value>::type* = + nullptr> + static constexpr Unit_T FromFraction(int64_t denominator, T value) { + return FromValue(value * denominator); + } + + template + constexpr typename std::enable_if::value, T>::type + ToValue() const { + RTC_DCHECK(IsFinite()); + return rtc::dchecked_cast(value_); + } + template + constexpr typename std::enable_if::value, T>::type + ToValue() const { + return IsPlusInfinity() + ? std::numeric_limits::infinity() + : IsMinusInfinity() ? -std::numeric_limits::infinity() + : value_; + } + template + constexpr T ToValueOr(T fallback_value) const { + return IsFinite() ? value_ : fallback_value; + } + + template + constexpr typename std::enable_if::value, T>::type + ToFraction() const { + RTC_DCHECK(IsFinite()); + if (Unit_T::one_sided) { + return rtc::dchecked_cast( + DivRoundPositiveToNearest(value_, Denominator)); + } else { + return rtc::dchecked_cast(DivRoundToNearest(value_, Denominator)); + } + } + template + constexpr typename std::enable_if::value, T>::type + ToFraction() const { + return ToValue() * (1 / static_cast(Denominator)); + } + + template + constexpr int64_t ToFractionOr(int64_t fallback_value) const { + return IsFinite() ? Unit_T::one_sided + ? DivRoundPositiveToNearest(value_, Denominator) + : DivRoundToNearest(value_, Denominator) + : fallback_value; + } + + template + constexpr typename std::enable_if::value, T>::type + ToMultiple() const { + RTC_DCHECK_GE(ToValue(), std::numeric_limits::min() / Factor); + RTC_DCHECK_LE(ToValue(), std::numeric_limits::max() / Factor); + return rtc::dchecked_cast(ToValue() * Factor); + } + template + constexpr typename std::enable_if::value, T>::type + ToMultiple() const { + return ToValue() * Factor; + } + + explicit constexpr UnitBase(int64_t value) : value_(value) {} + + private: + template + friend class RelativeUnit; + + static inline constexpr int64_t PlusInfinityVal() { + return std::numeric_limits::max(); + } + static inline constexpr int64_t MinusInfinityVal() { + return std::numeric_limits::min(); + } + + constexpr Unit_T& AsSubClassRef() { return static_cast(*this); } + constexpr const Unit_T& AsSubClassRef() const { + return static_cast(*this); + } + // Assumes that n >= 0 and d > 0. + static constexpr int64_t DivRoundPositiveToNearest(int64_t n, int64_t d) { + return (n + d / 2) / d; + } + // Assumes that d > 0. + static constexpr int64_t DivRoundToNearest(int64_t n, int64_t d) { + return (n + (n >= 0 ? d / 2 : -d / 2)) / d; + } + + int64_t value_; +}; + +// Extends UnitBase to provide operations for relative units, that is, units +// that have a meaningful relation between values such that a += b is a +// sensible thing to do. For a,b <- same unit. +template +class RelativeUnit : public UnitBase { + public: + constexpr Unit_T Clamped(Unit_T min_value, Unit_T max_value) const { + return std::max(min_value, + std::min(UnitBase::AsSubClassRef(), max_value)); + } + constexpr void Clamp(Unit_T min_value, Unit_T max_value) { + *this = Clamped(min_value, max_value); + } + constexpr Unit_T operator+(const Unit_T other) const { + if (this->IsPlusInfinity() || other.IsPlusInfinity()) { + RTC_DCHECK(!this->IsMinusInfinity()); + RTC_DCHECK(!other.IsMinusInfinity()); + return this->PlusInfinity(); + } else if (this->IsMinusInfinity() || other.IsMinusInfinity()) { + RTC_DCHECK(!this->IsPlusInfinity()); + RTC_DCHECK(!other.IsPlusInfinity()); + return this->MinusInfinity(); + } + return UnitBase::FromValue(this->ToValue() + other.ToValue()); + } + constexpr Unit_T operator-(const Unit_T other) const { + if (this->IsPlusInfinity() || other.IsMinusInfinity()) { + RTC_DCHECK(!this->IsMinusInfinity()); + RTC_DCHECK(!other.IsPlusInfinity()); + return this->PlusInfinity(); + } else if (this->IsMinusInfinity() || other.IsPlusInfinity()) { + RTC_DCHECK(!this->IsPlusInfinity()); + RTC_DCHECK(!other.IsMinusInfinity()); + return this->MinusInfinity(); + } + return UnitBase::FromValue(this->ToValue() - other.ToValue()); + } + constexpr Unit_T& operator+=(const Unit_T other) { + *this = *this + other; + return this->AsSubClassRef(); + } + constexpr Unit_T& operator-=(const Unit_T other) { + *this = *this - other; + return this->AsSubClassRef(); + } + constexpr double operator/(const Unit_T other) const { + return UnitBase::template ToValue() / + other.template ToValue(); + } + template + constexpr typename std::enable_if::value, Unit_T>::type + operator/(const T& scalar) const { + return UnitBase::FromValue( + std::round(UnitBase::template ToValue() / scalar)); + } + constexpr Unit_T operator*(double scalar) const { + return UnitBase::FromValue(std::round(this->ToValue() * scalar)); + } + constexpr Unit_T operator*(int64_t scalar) const { + return UnitBase::FromValue(this->ToValue() * scalar); + } + constexpr Unit_T operator*(int32_t scalar) const { + return UnitBase::FromValue(this->ToValue() * scalar); + } + + protected: + using UnitBase::UnitBase; +}; + +template +inline constexpr Unit_T operator*(double scalar, RelativeUnit other) { + return other * scalar; +} +template +inline constexpr Unit_T operator*(int64_t scalar, RelativeUnit other) { + return other * scalar; +} +template +inline constexpr Unit_T operator*(int32_t scalar, RelativeUnit other) { + return other * scalar; +} + +} // namespace rtc_units_impl + +} // namespace webrtc + +#endif // RTC_BASE_UNITS_UNIT_BASE_H_ diff --git a/webrtc/rtc_base/win32.h b/webrtc/rtc_base/win32.h new file mode 100644 index 0000000..c90296e --- /dev/null +++ b/webrtc/rtc_base/win32.h @@ -0,0 +1,104 @@ +/* + * Copyright 2004 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. + */ + +#ifndef RTC_BASE_WIN32_H_ +#define RTC_BASE_WIN32_H_ + +#ifndef WEBRTC_WIN +#error "Only #include this header in Windows builds" +#endif + +// Make sure we don't get min/max macros +#ifndef NOMINMAX +#define NOMINMAX +#endif + +// clang-format off +// clang formating would change include order. +#include // must come first +#include +// clang-format on + +typedef int socklen_t; + +#ifndef SECURITY_MANDATORY_LABEL_AUTHORITY +// Add defines that we use if we are compiling against older sdks +#define SECURITY_MANDATORY_MEDIUM_RID (0x00002000L) +#define TokenIntegrityLevel static_cast(0x19) +typedef struct _TOKEN_MANDATORY_LABEL { + SID_AND_ATTRIBUTES Label; +} TOKEN_MANDATORY_LABEL, *PTOKEN_MANDATORY_LABEL; +#endif // SECURITY_MANDATORY_LABEL_AUTHORITY + +#undef SetPort + +#include + +namespace rtc { + +const char* win32_inet_ntop(int af, const void* src, char* dst, socklen_t size); +int win32_inet_pton(int af, const char* src, void* dst); + +enum WindowsMajorVersions { + kWindows2000 = 5, + kWindowsVista = 6, + kWindows10 = 10, +}; + +#if !defined(WINUWP) +bool GetOsVersion(int* major, int* minor, int* build); + +inline bool IsWindowsVistaOrLater() { + int major; + return (GetOsVersion(&major, nullptr, nullptr) && major >= kWindowsVista); +} + +inline bool IsWindowsXpOrLater() { + int major, minor; + return (GetOsVersion(&major, &minor, nullptr) && + (major >= kWindowsVista || (major == kWindows2000 && minor >= 1))); +} + +inline bool IsWindows8OrLater() { + int major, minor; + return (GetOsVersion(&major, &minor, nullptr) && + (major > kWindowsVista || (major == kWindowsVista && minor >= 2))); +} + +inline bool IsWindows10OrLater() { + int major; + return (GetOsVersion(&major, nullptr, nullptr) && (major >= kWindows10)); +} + +#else + +// When targetting WinUWP the OS must be Windows 10 (or greater) as lesser +// Windows OS targets are not supported. +inline bool IsWindowsVistaOrLater() { + return true; +} + +inline bool IsWindowsXpOrLater() { + return true; +} + +inline bool IsWindows8OrLater() { + return true; +} + +inline bool IsWindows10OrLater() { + return true; +} + +#endif // !defined(WINUWP) + +} // namespace rtc + +#endif // RTC_BASE_WIN32_H_ diff --git a/webrtc/rtc_base/zero_memory.cc b/webrtc/rtc_base/zero_memory.cc new file mode 100644 index 0000000..b9c5b38 --- /dev/null +++ b/webrtc/rtc_base/zero_memory.cc @@ -0,0 +1,38 @@ +/* + * Copyright 2017 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. + */ + +#if defined(WEBRTC_WIN) +#include +#else +#include +#endif + +#include "rtc_base/checks.h" +#include "rtc_base/zero_memory.h" + +namespace rtc { + +// Code and comment taken from "OPENSSL_cleanse" of BoringSSL. +void ExplicitZeroMemory(void* ptr, size_t len) { + RTC_DCHECK(ptr || !len); +#if defined(WEBRTC_WIN) + SecureZeroMemory(ptr, len); +#else + memset(ptr, 0, len); +#if !defined(__pnacl__) + /* As best as we can tell, this is sufficient to break any optimisations that + might try to eliminate "superfluous" memsets. If there's an easy way to + detect memset_s, it would be better to use that. */ + __asm__ __volatile__("" : : "r"(ptr) : "memory"); // NOLINT +#endif +#endif // !WEBRTC_WIN +} + +} // namespace rtc diff --git a/webrtc/rtc_base/zero_memory.h b/webrtc/rtc_base/zero_memory.h new file mode 100644 index 0000000..b92f52f --- /dev/null +++ b/webrtc/rtc_base/zero_memory.h @@ -0,0 +1,35 @@ +/* + * Copyright 2017 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. + */ + +#ifndef RTC_BASE_ZERO_MEMORY_H_ +#define RTC_BASE_ZERO_MEMORY_H_ + +#include + +#include + +#include "api/array_view.h" + +namespace rtc { + +// Fill memory with zeros in a way that the compiler doesn't optimize it away +// even if the pointer is not used afterwards. +void ExplicitZeroMemory(void* ptr, size_t len); + +template ::value && + std::is_trivial::value>::type* = nullptr> +void ExplicitZeroMemory(rtc::ArrayView a) { + ExplicitZeroMemory(a.data(), a.size()); +} + +} // namespace rtc + +#endif // RTC_BASE_ZERO_MEMORY_H_ diff --git a/webrtc/system_wrappers/BUILD.gn b/webrtc/system_wrappers/BUILD.gn index 2f68eae..b446648 100644 --- a/webrtc/system_wrappers/BUILD.gn +++ b/webrtc/system_wrappers/BUILD.gn @@ -6,218 +6,138 @@ # in the file PATENTS. All contributing project authors may # be found in the AUTHORS file in the root of the source tree. -import("//build/config/android/config.gni") -import("../build/webrtc.gni") +if (is_android) { + import("//build/config/android/config.gni") + import("//build/config/android/rules.gni") +} +import("../webrtc.gni") -static_library("system_wrappers") { +rtc_library("system_wrappers") { + visibility = [ "*" ] sources = [ - "include/aligned_array.h", - "include/aligned_malloc.h", - "include/atomic32.h", "include/clock.h", - "include/condition_variable_wrapper.h", "include/cpu_features_wrapper.h", "include/cpu_info.h", - "include/critical_section_wrapper.h", - "include/data_log.h", - "include/data_log_c.h", - "include/data_log_impl.h", - "include/event_tracer.h", - "include/event_wrapper.h", - "include/field_trial.h", - "include/file_wrapper.h", - "include/fix_interlocked_exchange_pointer_win.h", - "include/logging.h", - "include/metrics.h", - "include/ref_count.h", - "include/rtp_to_ntp.h", - "include/rw_lock_wrapper.h", - "include/scoped_vector.h", + "include/ntp_time.h", + "include/rtp_to_ntp_estimator.h", "include/sleep.h", - "include/sort.h", - "include/static_instance.h", - "include/stl_util.h", - "include/stringize_macros.h", - "include/thread_wrapper.h", - "include/tick_util.h", - "include/timestamp_extrapolator.h", - "include/trace.h", - "include/utf_util_win.h", - "source/aligned_malloc.cc", - "source/atomic32_mac.cc", - "source/atomic32_win.cc", "source/clock.cc", - "source/condition_variable.cc", - "source/condition_variable_event_win.cc", - "source/condition_variable_event_win.h", - "source/condition_variable_native_win.cc", - "source/condition_variable_native_win.h", - "source/condition_variable_posix.cc", - "source/condition_variable_posix.h", "source/cpu_features.cc", "source/cpu_info.cc", - "source/critical_section.cc", - "source/critical_section_posix.cc", - "source/critical_section_posix.h", - "source/critical_section_win.cc", - "source/critical_section_win.h", - "source/data_log_c.cc", - "source/event.cc", - "source/event_timer_posix.cc", - "source/event_timer_posix.h", - "source/event_timer_win.cc", - "source/event_timer_win.h", - "source/event_tracer.cc", - "source/file_impl.cc", - "source/file_impl.h", - "source/logging.cc", - "source/rtp_to_ntp.cc", - "source/rw_lock.cc", - "source/rw_lock_generic.cc", - "source/rw_lock_generic.h", - "source/rw_lock_posix.cc", - "source/rw_lock_posix.h", - "source/rw_lock_win.cc", - "source/rw_lock_win.h", + "source/rtp_to_ntp_estimator.cc", "source/sleep.cc", - "source/sort.cc", - "source/thread.cc", - "source/thread_posix.cc", - "source/thread_posix.h", - "source/thread_win.cc", - "source/thread_win.h", - "source/tick_util.cc", - "source/timestamp_extrapolator.cc", - "source/trace_impl.cc", - "source/trace_impl.h", - "source/trace_posix.cc", - "source/trace_posix.h", - "source/trace_win.cc", - "source/trace_win.h", ] - configs += [ "..:common_config" ] - - public_configs = [ "..:common_inherited_config" ] - - if (rtc_enable_data_logging) { - sources += [ "source/data_log.cc" ] - } else { - sources += [ "source/data_log_no_op.cc" ] - } - defines = [] libs = [] deps = [ - "..:webrtc_common", + ":field_trial", + "../api:array_view", + "../api/units:timestamp", + "../modules:module_api_public", + "../rtc_base:checks", + "../rtc_base/synchronization:mutex", + "../rtc_base/synchronization:rw_lock_wrapper", + "../rtc_base/system:arch", + "../rtc_base/system:rtc_export", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] if (is_android) { - sources += [ - "include/logcat_trace_context.h", - "source/logcat_trace_context.cc", - ] - - defines += [ - "WEBRTC_THREAD_RR", - - # TODO(leozwang): Investigate CLOCK_REALTIME and CLOCK_MONOTONIC - # support on Android. Keep WEBRTC_CLOCK_TYPE_REALTIME for now, - # remove it after I verify that CLOCK_MONOTONIC is fully functional - # with condition and event functions in system_wrappers. - "WEBRTC_CLOCK_TYPE_REALTIME", - ] - - deps += [ ":cpu_features_android" ] + if (build_with_mozilla) { + include_dirs = [ + "/config/external/nspr", + "/nsprpub/lib/ds", + "/nsprpub/pr/include", + ] + } else { + sources += [ "source/cpu_features_android.cc" ] + deps += [ "//third_party/android_sdk:cpu_features" ] + } libs += [ "log" ] } - if (is_linux) { - defines += [ - "WEBRTC_THREAD_RR", - # TODO(andrew): can we select this automatically? - # Define this if the Linux system does not support CLOCK_MONOTONIC. - #"WEBRTC_CLOCK_TYPE_REALTIME", - ] + if (is_linux || is_chromeos) { + if (!build_with_chromium) { + sources += [ "source/cpu_features_linux.cc" ] + } libs += [ "rt" ] } - if (!is_mac && !is_ios) { - sources += [ "source/atomic32_posix.cc" ] - } - - if (is_ios || is_mac) { - defines += [ - "WEBRTC_THREAD_RR", - "WEBRTC_CLOCK_TYPE_REALTIME", - ] - } - - if (is_ios) { - sources += [ "source/atomic32_mac.cc" ] - } - if (is_win) { libs += [ "winmm.lib" ] - cflags = [ - "/wd4267", # size_t to int truncation. - "/wd4334", # Ignore warning on shift operator promotion. - ] + # Windows needs ../rtc_base due to include of + # webrtc/rtc_base/win32.h in source/clock.cc. + deps += [ "../rtc_base:win32" ] } - deps += [ "../base:rtc_base_approved" ] + deps += [ + "../rtc_base:rtc_base_approved", + "../rtc_base:rtc_numerics", + ] } -source_set("field_trial_default") { - sources = [ - "include/field_trial_default.h", - "source/field_trial_default.cc", - ] - - configs += [ "..:common_config" ] - public_configs = [ "..:common_inherited_config" ] - +rtc_library("field_trial") { + visibility = [ "*" ] + public = [ "include/field_trial.h" ] + sources = [ "source/field_trial.cc" ] + if (rtc_exclude_field_trial_default) { + defines = [ "WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT" ] + } deps = [ - ":system_wrappers", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:stringutils", ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } -source_set("metrics_default") { - sources = [ - "source/metrics_default.cc", - ] - - configs += [ "..:common_config" ] - public_configs = [ "..:common_inherited_config" ] - +rtc_library("metrics") { + visibility = [ "*" ] + public = [ "include/metrics.h" ] + sources = [ "source/metrics.cc" ] + if (rtc_exclude_metrics_default) { + defines = [ "WEBRTC_EXCLUDE_METRICS_DEFAULT" ] + } deps = [ - ":system_wrappers", + "../rtc_base:checks", + "../rtc_base:rtc_base_approved", + "../rtc_base/synchronization:mutex", ] } -source_set("system_wrappers_default") { - configs += [ "..:common_config" ] - public_configs = [ "..:common_inherited_config" ] - - deps = [ - ":field_trial_default", - ":metrics_default", - ] -} - -if (is_android) { - source_set("cpu_features_android") { +if (rtc_include_tests) { + rtc_test("system_wrappers_unittests") { + testonly = true sources = [ - "source/cpu_features_android.c", + "source/clock_unittest.cc", + "source/field_trial_unittest.cc", + "source/metrics_default_unittest.cc", + "source/metrics_unittest.cc", + "source/ntp_time_unittest.cc", + "source/rtp_to_ntp_estimator_unittest.cc", ] - configs += [ "..:common_config" ] - public_configs = [ "..:common_inherited_config" ] deps = [ - "//third_party/android_tools:cpu_features", + ":field_trial", + ":metrics", + ":system_wrappers", + "../rtc_base:checks", + "../rtc_base:rtc_base_approved", + "../test:rtc_expect_death", + "../test:test_main", + "../test:test_support", + "//testing/gtest", + "//third_party/abseil-cpp/absl/strings", ] + + if (is_android) { + deps += [ "//testing/android/native_test:native_test_support" ] + + shard_timeout = 900 + } } } diff --git a/webrtc/system_wrappers/include/aligned_array.h b/webrtc/system_wrappers/include/aligned_array.h deleted file mode 100644 index 7cd182c..0000000 --- a/webrtc/system_wrappers/include/aligned_array.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ALIGNED_ARRAY_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ALIGNED_ARRAY_ - -#include "webrtc/base/checks.h" -#include "webrtc/system_wrappers/include/aligned_malloc.h" - -namespace webrtc { - -// Wrapper class for aligned arrays. Every row (and the first dimension) are -// aligned to the given byte alignment. -template class AlignedArray { - public: - AlignedArray(int rows, size_t cols, int alignment) - : rows_(rows), - cols_(cols), - alignment_(alignment) { - RTC_CHECK_GT(alignment_, 0); - head_row_ = static_cast(AlignedMalloc(rows_ * sizeof(*head_row_), - alignment_)); - for (int i = 0; i < rows_; ++i) { - head_row_[i] = static_cast(AlignedMalloc(cols_ * sizeof(**head_row_), - alignment_)); - } - } - - ~AlignedArray() { - for (int i = 0; i < rows_; ++i) { - AlignedFree(head_row_[i]); - } - AlignedFree(head_row_); - } - - T* const* Array() { - return head_row_; - } - - const T* const* Array() const { - return head_row_; - } - - T* Row(int row) { - RTC_CHECK_LE(row, rows_); - return head_row_[row]; - } - - const T* Row(int row) const { - RTC_CHECK_LE(row, rows_); - return head_row_[row]; - } - - T& At(int row, size_t col) { - RTC_CHECK_LE(col, cols_); - return Row(row)[col]; - } - - const T& At(int row, size_t col) const { - RTC_CHECK_LE(col, cols_); - return Row(row)[col]; - } - - int rows() const { - return rows_; - } - - size_t cols() const { - return cols_; - } - - private: - int rows_; - size_t cols_; - int alignment_; - T** head_row_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ALIGNED_ARRAY_ diff --git a/webrtc/system_wrappers/include/condition_variable_wrapper.h b/webrtc/system_wrappers/include/condition_variable_wrapper.h deleted file mode 100644 index 37ca30f..0000000 --- a/webrtc/system_wrappers/include/condition_variable_wrapper.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_CONDITION_VARIABLE_WRAPPER_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_CONDITION_VARIABLE_WRAPPER_H_ - -namespace webrtc { - -class CriticalSectionWrapper; - -class ConditionVariableWrapper { - public: - // Factory method, constructor disabled. - static ConditionVariableWrapper* CreateConditionVariable(); - - virtual ~ConditionVariableWrapper() {} - - // Calling thread will atomically release crit_sect and wait until next - // some other thread calls Wake() or WakeAll(). - virtual void SleepCS(CriticalSectionWrapper& crit_sect) = 0; - - // Same as above but with a timeout. - virtual bool SleepCS(CriticalSectionWrapper& crit_sect, - unsigned long max_time_in_ms) = 0; - - // Wakes one thread calling SleepCS(). - virtual void Wake() = 0; - - // Wakes all threads calling SleepCS(). - virtual void WakeAll() = 0; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_CONDITION_VARIABLE_WRAPPER_H_ diff --git a/webrtc/system_wrappers/include/cpu_features_wrapper.h b/webrtc/system_wrappers/include/cpu_features_wrapper.h index 9838d94..612b4a5 100644 --- a/webrtc/system_wrappers/include/cpu_features_wrapper.h +++ b/webrtc/system_wrappers/include/cpu_features_wrapper.h @@ -8,44 +8,35 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_CPU_FEATURES_WRAPPER_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_CPU_FEATURES_WRAPPER_H_ +#ifndef SYSTEM_WRAPPERS_INCLUDE_CPU_FEATURES_WRAPPER_H_ +#define SYSTEM_WRAPPERS_INCLUDE_CPU_FEATURES_WRAPPER_H_ -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif +#include -#include "webrtc/typedefs.h" +namespace webrtc { // List of features in x86. -typedef enum { - kSSE2, - kSSE3 -} CPUFeature; +typedef enum { kSSE2, kSSE3, kAVX2 } CPUFeature; // List of features in ARM. enum { - kCPUFeatureARMv7 = (1 << 0), - kCPUFeatureVFPv3 = (1 << 1), - kCPUFeatureNEON = (1 << 2), - kCPUFeatureLDREXSTREX = (1 << 3) + kCPUFeatureARMv7 = (1 << 0), + kCPUFeatureVFPv3 = (1 << 1), + kCPUFeatureNEON = (1 << 2), + kCPUFeatureLDREXSTREX = (1 << 3) }; -typedef int (*WebRtc_CPUInfo)(CPUFeature feature); - // Returns true if the CPU supports the feature. -extern WebRtc_CPUInfo WebRtc_GetCPUInfo; +int GetCPUInfo(CPUFeature feature); // No CPU feature is available => straight C path. -extern WebRtc_CPUInfo WebRtc_GetCPUInfoNoASM; +int GetCPUInfoNoASM(CPUFeature feature); // Return the features in an ARM device. // It detects the features in the hardware platform, and returns supported // values in the above enum definition as a bitmask. -extern uint64_t WebRtc_GetCPUFeaturesARM(void); +uint64_t GetCPUFeaturesARM(void); -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif +} // namespace webrtc -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_CPU_FEATURES_WRAPPER_H_ +#endif // SYSTEM_WRAPPERS_INCLUDE_CPU_FEATURES_WRAPPER_H_ diff --git a/webrtc/system_wrappers/include/critical_section_wrapper.h b/webrtc/system_wrappers/include/critical_section_wrapper.h deleted file mode 100644 index 7dd217e..0000000 --- a/webrtc/system_wrappers/include/critical_section_wrapper.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_CRITICAL_SECTION_WRAPPER_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_CRITICAL_SECTION_WRAPPER_H_ - -// If the critical section is heavily contended it may be beneficial to use -// read/write locks instead. - -#include "webrtc/base/thread_annotations.h" -#include "webrtc/common_types.h" - -namespace webrtc { -class LOCKABLE CriticalSectionWrapper { - public: - // Factory method, constructor disabled - static CriticalSectionWrapper* CreateCriticalSection(); - - virtual ~CriticalSectionWrapper() {} - - // Tries to grab lock, beginning of a critical section. Will wait for the - // lock to become available if the grab failed. - virtual void Enter() EXCLUSIVE_LOCK_FUNCTION() = 0; - - // Returns a grabbed lock, end of critical section. - virtual void Leave() UNLOCK_FUNCTION() = 0; -}; - -// RAII extension of the critical section. Prevents Enter/Leave mismatches and -// provides more compact critical section syntax. -class SCOPED_LOCKABLE CriticalSectionScoped { - public: - explicit CriticalSectionScoped(CriticalSectionWrapper* critsec) - EXCLUSIVE_LOCK_FUNCTION(critsec) - : ptr_crit_sec_(critsec) { - ptr_crit_sec_->Enter(); - } - - ~CriticalSectionScoped() UNLOCK_FUNCTION() { ptr_crit_sec_->Leave(); } - - private: - CriticalSectionWrapper* ptr_crit_sec_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_CRITICAL_SECTION_WRAPPER_H_ diff --git a/webrtc/system_wrappers/include/event_wrapper.h b/webrtc/system_wrappers/include/event_wrapper.h deleted file mode 100644 index cc3722b..0000000 --- a/webrtc/system_wrappers/include/event_wrapper.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_EVENT_WRAPPER_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_EVENT_WRAPPER_H_ - -namespace webrtc { -enum EventTypeWrapper { - kEventSignaled = 1, - kEventError = 2, - kEventTimeout = 3 -}; - -#define WEBRTC_EVENT_INFINITE 0xffffffff - -class EventTimerWrapper; - -class EventWrapper { - public: - // Factory method. Constructor disabled. - static EventWrapper* Create(); - - virtual ~EventWrapper() {} - - // Releases threads who are calling Wait() and has started waiting. Please - // note that a thread calling Wait() will not start waiting immediately. - // assumptions to the contrary is a very common source of issues in - // multithreaded programming. - // Set is sticky in the sense that it will release at least one thread - // either immediately or some time in the future. - virtual bool Set() = 0; - - // Puts the calling thread into a wait state. The thread may be released - // by a Set() call depending on if other threads are waiting and if so on - // timing. The thread that was released will reset the event before leaving - // preventing more threads from being released. If multiple threads - // are waiting for the same Set(), only one (random) thread is guaranteed to - // be released. It is possible that multiple (random) threads are released - // Depending on timing. - // - // |max_time| is the maximum time to wait in milliseconds or - // WEBRTC_EVENT_INFINITE to wait infinitely. - virtual EventTypeWrapper Wait(unsigned long max_time) = 0; -}; - -class EventTimerWrapper : public EventWrapper { - public: - static EventTimerWrapper* Create(); - - // Starts a timer that will call a non-sticky version of Set() either once - // or periodically. If the timer is periodic it ensures that there is no - // drift over time relative to the system clock. - // - // |time| is in milliseconds. - virtual bool StartTimer(bool periodic, unsigned long time) = 0; - - virtual bool StopTimer() = 0; - -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_EVENT_WRAPPER_H_ diff --git a/webrtc/system_wrappers/include/field_trial.h b/webrtc/system_wrappers/include/field_trial.h new file mode 100644 index 0000000..52db33b --- /dev/null +++ b/webrtc/system_wrappers/include/field_trial.h @@ -0,0 +1,102 @@ +// +// Copyright (c) 2014 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. +// + +#ifndef SYSTEM_WRAPPERS_INCLUDE_FIELD_TRIAL_H_ +#define SYSTEM_WRAPPERS_INCLUDE_FIELD_TRIAL_H_ + +#include + +// Field trials allow webrtc clients (such as Chrome) to turn on feature code +// in binaries out in the field and gather information with that. +// +// By default WebRTC provides an implementation of field trials that can be +// found in system_wrappers/source/field_trial.cc. If clients want to provide +// a custom version, they will have to: +// +// 1. Compile WebRTC defining the preprocessor macro +// WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT (if GN is used this can be achieved +// by setting the GN arg rtc_exclude_field_trial_default to true). +// 2. Provide an implementation of: +// std::string webrtc::field_trial::FindFullName(const std::string& trial). +// +// They are designed to wire up directly to chrome field trials and to speed up +// developers by reducing the need to wire APIs to control whether a feature is +// on/off. E.g. to experiment with a new method that could lead to a different +// trade-off between CPU/bandwidth: +// +// 1 - Develop the feature with default behaviour off: +// +// if (FieldTrial::FindFullName("WebRTCExperimentMethod2") == "Enabled") +// method2(); +// else +// method1(); +// +// 2 - Once the changes are rolled to chrome, the new code path can be +// controlled as normal chrome field trials. +// +// 3 - Evaluate the new feature and clean the code paths. +// +// Notes: +// - NOT every feature is a candidate to be controlled by this mechanism as +// it may require negotiation between involved parties (e.g. SDP). +// +// TODO(andresp): since chrome --force-fieldtrials does not marks the trial +// as active it does not get propagated to the renderer process. For now one +// needs to push a config with start_active:true or run a local finch +// server. +// +// TODO(andresp): find out how to get bots to run tests with trials enabled. + +namespace webrtc { +namespace field_trial { + +// Returns the group name chosen for the named trial, or the empty string +// if the trial does not exists. +// +// Note: To keep things tidy append all the trial names with WebRTC. +std::string FindFullName(const std::string& name); + +// Convenience method, returns true iff FindFullName(name) return a string that +// starts with "Enabled". +// TODO(tommi): Make sure all implementations support this. +inline bool IsEnabled(const char* name) { + return FindFullName(name).find("Enabled") == 0; +} + +// Convenience method, returns true iff FindFullName(name) return a string that +// starts with "Disabled". +inline bool IsDisabled(const char* name) { + return FindFullName(name).find("Disabled") == 0; +} + +// Optionally initialize field trial from a string. +// This method can be called at most once before any other call into webrtc. +// E.g. before the peer connection factory is constructed. +// Note: trials_string must never be destroyed. +void InitFieldTrialsFromString(const char* trials_string); + +const char* GetFieldTrialString(); + +#ifndef WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT +// Validates the given field trial string. +bool FieldTrialsStringIsValid(const char* trials_string); + +// Merges two field trial strings. +// +// If a key (trial) exists twice with conflicting values (groups), the value +// in 'second' takes precedence. +// Shall only be called with valid FieldTrial strings. +std::string MergeFieldTrialsStrings(const char* first, const char* second); +#endif // WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT + +} // namespace field_trial +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_INCLUDE_FIELD_TRIAL_H_ diff --git a/webrtc/system_wrappers/include/file_wrapper.h b/webrtc/system_wrappers/include/file_wrapper.h deleted file mode 100644 index b32a62f..0000000 --- a/webrtc/system_wrappers/include/file_wrapper.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_FILE_WRAPPER_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_FILE_WRAPPER_H_ - -#include -#include - -#include "webrtc/common_types.h" -#include "webrtc/typedefs.h" - -// Implementation of an InStream and OutStream that can read (exclusive) or -// write from/to a file. - -namespace webrtc { - -class FileWrapper : public InStream, public OutStream { - public: - static const size_t kMaxFileNameSize = 1024; - - // Factory method. Constructor disabled. - static FileWrapper* Create(); - - // Returns true if a file has been opened. - virtual bool Open() const = 0; - - // Opens a file in read or write mode, decided by the read_only parameter. - virtual int OpenFile(const char* file_name_utf8, - bool read_only, - bool loop = false, - bool text = false) = 0; - - // Initializes the wrapper from an existing handle. |read_only| must match in - // the mode the file was opened in. If |manage_file| is true, the wrapper - // takes ownership of |handle| and closes it in CloseFile(). - virtual int OpenFromFileHandle(FILE* handle, - bool manage_file, - bool read_only, - bool loop = false) = 0; - - virtual int CloseFile() = 0; - - // Limits the file size to |bytes|. Writing will fail after the cap - // is hit. Pass zero to use an unlimited size. - virtual int SetMaxFileSize(size_t bytes) = 0; - - // Flush any pending writes. - virtual int Flush() = 0; - - // Returns the opened file's name in |file_name_utf8|. Provide the size of - // the buffer in bytes in |size|. The name will be truncated if |size| is - // too small. - virtual int FileName(char* file_name_utf8, - size_t size) const = 0; - - // Write |format| to the opened file. Arguments are taken in the same manner - // as printf. That is, supply a format string containing text and - // specifiers. Returns the number of characters written or -1 on error. - virtual int WriteText(const char* format, ...) = 0; - - // Inherited from both Instream and OutStream. - // Rewinds the file to the start. Only available when OpenFile() has been - // called with |loop| == true or |readOnly| == true. - // virtual int Rewind() = 0; - int Rewind() override; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_FILE_WRAPPER_H_ diff --git a/webrtc/system_wrappers/include/fix_interlocked_exchange_pointer_win.h b/webrtc/system_wrappers/include/fix_interlocked_exchange_pointer_win.h deleted file mode 100644 index 8fb32ef..0000000 --- a/webrtc/system_wrappers/include/fix_interlocked_exchange_pointer_win.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2013 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. - */ - -// Various inline functions and macros to fix compilation of 32 bit target -// on MSVC with /Wp64 flag enabled. - -// The original code can be found here: -// http://src.chromium.org/svn/trunk/src/base/fix_wp64.h - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_FIX_INTERLOCKED_EXCHANGE_POINTER_WINDOWS_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_FIX_INTERLOCKED_EXCHANGE_POINTER_WINDOWS_H_ - -#include - -// Platform SDK fixes when building with /Wp64 for a 32 bits target. -#if !defined(_WIN64) && defined(_Wp64) - -#ifdef InterlockedExchangePointer -#undef InterlockedExchangePointer -// The problem is that the macro provided for InterlockedExchangePointer() is -// doing a (LONG) C-style cast that triggers invariably the warning C4312 when -// building on 32 bits. -inline void* InterlockedExchangePointer(void* volatile* target, void* value) { - return reinterpret_cast(static_cast(InterlockedExchange( - reinterpret_cast(target), - static_cast(reinterpret_cast(value))))); -} -#endif // #ifdef InterlockedExchangePointer - -#endif // #if !defined(_WIN64) && defined(_Wp64) - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_FIX_INTERLOCKED_EXCHANGE_POINTER_WINDOWS_H_ diff --git a/webrtc/system_wrappers/include/logging.h b/webrtc/system_wrappers/include/logging.h deleted file mode 100644 index 2b53f4f..0000000 --- a/webrtc/system_wrappers/include/logging.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -// This is a highly stripped-down version of libjingle's talk/base/logging.h. -// It is a thin wrapper around WEBRTC_TRACE, maintaining the libjingle log -// semantics to ease a transition to that format. - -// NOTE: LS_INFO maps to a new trace level which should be reserved for -// infrequent, non-verbose logs. The other levels below kTraceWarning have been -// rendered essentially useless due to their verbosity. Carefully consider the -// impact of adding a new LS_INFO log. If it will be logged at anything -// approaching a frame or packet frequency, use LS_VERBOSE if necessary, or -// preferably, do not log at all. - -// LOG(...) an ostream target that can be used to send formatted -// output to a variety of logging targets, such as debugger console, stderr, -// file, or any StreamInterface. -// The severity level passed as the first argument to the LOGging -// functions is used as a filter, to limit the verbosity of the logging. -// Static members of LogMessage documented below are used to control the -// verbosity and target of the output. -// There are several variations on the LOG macro which facilitate logging -// of common error conditions, detailed below. - -// LOG(sev) logs the given stream at severity "sev", which must be a -// compile-time constant of the LoggingSeverity type, without the namespace -// prefix. -// LOG_V(sev) Like LOG(), but sev is a run-time variable of the LoggingSeverity -// type (basically, it just doesn't prepend the namespace). -// LOG_F(sev) Like LOG(), but includes the name of the current function. - -// Additional helper macros added by WebRTC: -// LOG_API is a shortcut for API call logging. Pass in the input parameters of -// the method. For example: -// Foo(int bar, int baz) { -// LOG_API2(bar, baz); -// } -// -// LOG_FERR is a shortcut for logging a failed function call. For example: -// if (!Foo(bar)) { -// LOG_FERR1(LS_WARNING, Foo, bar); -// } - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_LOGGING_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_LOGGING_H_ - -#include - -namespace webrtc { - -////////////////////////////////////////////////////////////////////// - -// Note that the non-standard LoggingSeverity aliases exist because they are -// still in broad use. The meanings of the levels are: -// LS_SENSITIVE: Information which should only be logged with the consent -// of the user, due to privacy concerns. -// LS_VERBOSE: This level is for data which we do not want to appear in the -// normal debug log, but should appear in diagnostic logs. -// LS_INFO: Chatty level used in debugging for all sorts of things, the default -// in debug builds. -// LS_WARNING: Something that may warrant investigation. -// LS_ERROR: Something that should not have occurred. -enum LoggingSeverity { - LS_SENSITIVE, LS_VERBOSE, LS_INFO, LS_WARNING, LS_ERROR -}; - -class LogMessage { - public: - LogMessage(const char* file, int line, LoggingSeverity sev); - ~LogMessage(); - - static bool Loggable(LoggingSeverity sev); - std::ostream& stream() { return print_stream_; } - - private: - // The ostream that buffers the formatted message before output - std::ostringstream print_stream_; - - // The severity level of this message - LoggingSeverity severity_; -}; - -////////////////////////////////////////////////////////////////////// -// Macros which automatically disable logging when WEBRTC_LOGGING == 0 -////////////////////////////////////////////////////////////////////// - -#ifndef LOG -// The following non-obvious technique for implementation of a -// conditional log stream was stolen from google3/base/logging.h. - -// This class is used to explicitly ignore values in the conditional -// logging macros. This avoids compiler warnings like "value computed -// is not used" and "statement has no effect". - -class LogMessageVoidify { - public: - LogMessageVoidify() { } - // This has to be an operator with a precedence lower than << but - // higher than ?: - void operator&(std::ostream&) { } -}; - -#if defined(WEBRTC_RESTRICT_LOGGING) -// This should compile away logs matching the following condition. -#define RESTRICT_LOGGING_PRECONDITION(sev) \ - sev < webrtc::LS_INFO ? (void) 0 : -#else -#define RESTRICT_LOGGING_PRECONDITION(sev) -#endif - -#define LOG_SEVERITY_PRECONDITION(sev) \ - RESTRICT_LOGGING_PRECONDITION(sev) !(webrtc::LogMessage::Loggable(sev)) \ - ? (void) 0 \ - : webrtc::LogMessageVoidify() & - -#define LOG(sev) \ - LOG_SEVERITY_PRECONDITION(webrtc::sev) \ - webrtc::LogMessage(__FILE__, __LINE__, webrtc::sev).stream() - -// The _V version is for when a variable is passed in. It doesn't do the -// namespace concatination. -#define LOG_V(sev) \ - LOG_SEVERITY_PRECONDITION(sev) \ - webrtc::LogMessage(__FILE__, __LINE__, sev).stream() - -// The _F version prefixes the message with the current function name. -#if (defined(__GNUC__) && !defined(NDEBUG)) || defined(WANT_PRETTY_LOG_F) -#define LOG_F(sev) LOG(sev) << __PRETTY_FUNCTION__ << ": " -#else -#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": " -#endif - -#define LOG_API0() LOG_F(LS_VERBOSE) -#define LOG_API1(v1) LOG_API0() << #v1 << "=" << v1 -#define LOG_API2(v1, v2) LOG_API1(v1) \ - << ", " << #v2 << "=" << v2 -#define LOG_API3(v1, v2, v3) LOG_API2(v1, v2) \ - << ", " << #v3 << "=" << v3 - -#define LOG_FERR0(sev, func) LOG(sev) << #func << " failed" -#define LOG_FERR1(sev, func, v1) LOG_FERR0(sev, func) \ - << ": " << #v1 << "=" << v1 -#define LOG_FERR2(sev, func, v1, v2) LOG_FERR1(sev, func, v1) \ - << ", " << #v2 << "=" << v2 -#define LOG_FERR3(sev, func, v1, v2, v3) LOG_FERR2(sev, func, v1, v2) \ - << ", " << #v3 << "=" << v3 -#define LOG_FERR4(sev, func, v1, v2, v3, v4) LOG_FERR3(sev, func, v1, v2, v3) \ - << ", " << #v4 << "=" << v4 - -#endif // LOG - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_LOGGING_H_ diff --git a/webrtc/system_wrappers/include/metrics.h b/webrtc/system_wrappers/include/metrics.h index 2e6e7b7..8e0f084 100644 --- a/webrtc/system_wrappers/include/metrics.h +++ b/webrtc/system_wrappers/include/metrics.h @@ -8,13 +8,51 @@ // be found in the AUTHORS file in the root of the source tree. // -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_METRICS_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_METRICS_H_ +#ifndef SYSTEM_WRAPPERS_INCLUDE_METRICS_H_ +#define SYSTEM_WRAPPERS_INCLUDE_METRICS_H_ +#include + +#include +#include #include -#include "webrtc/common_types.h" +#include "rtc_base/atomic_ops.h" +#include "rtc_base/checks.h" +#if defined(RTC_DISABLE_METRICS) +#define RTC_METRICS_ENABLED 0 +#else +#define RTC_METRICS_ENABLED 1 +#endif + +namespace webrtc { +namespace metrics_impl { +template +void NoOp(const Ts&...) {} +} +} + +#if RTC_METRICS_ENABLED +#define EXPECT_METRIC_EQ(val1, val2) EXPECT_EQ(val1, val2) +#define EXPECT_METRIC_EQ_WAIT(val1, val2, timeout) \ + EXPECT_EQ_WAIT(val1, val2, timeout) +#define EXPECT_METRIC_GT(val1, val2) EXPECT_GT(val1, val2) +#define EXPECT_METRIC_LE(val1, val2) EXPECT_LE(val1, val2) +#define EXPECT_METRIC_TRUE(conditon) EXPECT_TRUE(conditon) +#define EXPECT_METRIC_FALSE(conditon) EXPECT_FALSE(conditon) +#define EXPECT_METRIC_THAT(value, matcher) EXPECT_THAT(value, matcher) +#else +#define EXPECT_METRIC_EQ(val1, val2) webrtc::metrics_impl::NoOp(val1, val2) +#define EXPECT_METRIC_EQ_WAIT(val1, val2, timeout) webrtc::metrics_impl::NoOp(val1, val2, timeout) +#define EXPECT_METRIC_GT(val1, val2) webrtc::metrics_impl::NoOp(val1, val2) +#define EXPECT_METRIC_LE(val1, val2) webrtc::metrics_impl::NoOp(val1, val2) +#define EXPECT_METRIC_TRUE(condition) webrtc::metrics_impl::NoOp(condition || true) +#define EXPECT_METRIC_FALSE(condition) webrtc::metrics_impl::NoOp(condition && false) +#define EXPECT_METRIC_THAT(value, matcher) webrtc::metrics_impl::NoOp(value, testing::_) +#endif + +#if RTC_METRICS_ENABLED // Macros for allowing WebRTC clients (e.g. Chrome) to gather and aggregate // statistics. // @@ -29,20 +67,21 @@ // The macros use the methods HistogramFactoryGetCounts, // HistogramFactoryGetEnumeration and HistogramAdd. // -// Therefore, WebRTC clients must either: -// -// - provide implementations of -// Histogram* webrtc::metrics::HistogramFactoryGetCounts( -// const std::string& name, int sample, int min, int max, -// int bucket_count); -// Histogram* webrtc::metrics::HistogramFactoryGetEnumeration( -// const std::string& name, int sample, int boundary); -// void webrtc::metrics::HistogramAdd( -// Histogram* histogram_pointer, const std::string& name, int sample); -// -// - or link with the default implementations (i.e. -// system_wrappers/system_wrappers.gyp:metrics_default). +// By default WebRTC provides implementations of the aforementioned methods +// that can be found in system_wrappers/source/metrics.cc. If clients want to +// provide a custom version, they will have to: // +// 1. Compile WebRTC defining the preprocessor macro +// WEBRTC_EXCLUDE_METRICS_DEFAULT (if GN is used this can be achieved +// by setting the GN arg rtc_exclude_metrics_default to true). +// 2. Provide implementations of: +// Histogram* webrtc::metrics::HistogramFactoryGetCounts( +// const std::string& name, int sample, int min, int max, +// int bucket_count); +// Histogram* webrtc::metrics::HistogramFactoryGetEnumeration( +// const std::string& name, int sample, int boundary); +// void webrtc::metrics::HistogramAdd( +// Histogram* histogram_pointer, const std::string& name, int sample); // // Example usage: // @@ -55,57 +94,270 @@ // }; // // RTC_HISTOGRAM_ENUMERATION("WebRTC.Types", kTypeX, kBoundary); - +// +// NOTE: It is recommended to do the Chromium review for modifications to +// histograms.xml before new metrics are committed to WebRTC. // Macros for adding samples to a named histogram. + +// Histogram for counters (exponentially spaced buckets). +#define RTC_HISTOGRAM_COUNTS_100(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 100, 50) + +#define RTC_HISTOGRAM_COUNTS_200(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 200, 50) + +#define RTC_HISTOGRAM_COUNTS_500(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 500, 50) + +#define RTC_HISTOGRAM_COUNTS_1000(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 1000, 50) + +#define RTC_HISTOGRAM_COUNTS_10000(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 10000, 50) + +#define RTC_HISTOGRAM_COUNTS_100000(name, sample) \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 100000, 50) + +#define RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count) \ + RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \ + webrtc::metrics::HistogramFactoryGetCounts( \ + name, min, max, bucket_count)) + +#define RTC_HISTOGRAM_COUNTS_LINEAR(name, sample, min, max, bucket_count) \ + RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \ + webrtc::metrics::HistogramFactoryGetCountsLinear( \ + name, min, max, bucket_count)) + +// Slow metrics: pointer to metric is acquired at each call and is not cached. // -// NOTE: this is a temporary solution. -// The aim is to mimic the behaviour in Chromium's src/base/metrics/histograms.h -// However as atomics are not supported in webrtc, this is for now a modified -// and temporary solution. Note that the histogram is constructed/found for -// each call. Therefore, for now only use this implementation for metrics -// that do not need to be updated frequently. -// TODO(asapersson): Change implementation when atomics are supported. -// Also consider changing string to const char* when switching to atomics. +#define RTC_HISTOGRAM_COUNTS_SPARSE_100(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 100, 50) -// Histogram for counters. -#define RTC_HISTOGRAM_COUNTS_100(name, sample) RTC_HISTOGRAM_COUNTS( \ - name, sample, 1, 100, 50) +#define RTC_HISTOGRAM_COUNTS_SPARSE_200(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 200, 50) -#define RTC_HISTOGRAM_COUNTS_200(name, sample) RTC_HISTOGRAM_COUNTS( \ - name, sample, 1, 200, 50) +#define RTC_HISTOGRAM_COUNTS_SPARSE_500(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 500, 50) -#define RTC_HISTOGRAM_COUNTS_1000(name, sample) RTC_HISTOGRAM_COUNTS( \ - name, sample, 1, 1000, 50) +#define RTC_HISTOGRAM_COUNTS_SPARSE_1000(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 1000, 50) -#define RTC_HISTOGRAM_COUNTS_10000(name, sample) RTC_HISTOGRAM_COUNTS( \ - name, sample, 1, 10000, 50) +#define RTC_HISTOGRAM_COUNTS_SPARSE_10000(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 10000, 50) -#define RTC_HISTOGRAM_COUNTS_100000(name, sample) RTC_HISTOGRAM_COUNTS( \ - name, sample, 1, 100000, 50) +#define RTC_HISTOGRAM_COUNTS_SPARSE_100000(name, sample) \ + RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, 1, 100000, 50) -#define RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count) \ - RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \ - webrtc::metrics::HistogramFactoryGetCounts( \ - name, min, max, bucket_count)) +#define RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, min, max, bucket_count) \ + RTC_HISTOGRAM_COMMON_BLOCK_SLOW(name, sample, \ + webrtc::metrics::HistogramFactoryGetCounts( \ + name, min, max, bucket_count)) -// Histogram for percentage. +// Histogram for percentage (evenly spaced buckets). +#define RTC_HISTOGRAM_PERCENTAGE_SPARSE(name, sample) \ + RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, 101) + +// Histogram for booleans. +#define RTC_HISTOGRAM_BOOLEAN_SPARSE(name, sample) \ + RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, 2) + +// Histogram for enumerators (evenly spaced buckets). +// |boundary| should be above the max enumerator sample. +// +// TODO(qingsi): Refactor the default implementation given by RtcHistogram, +// which is already sparse, and remove the boundary argument from the macro. +#define RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, boundary) \ + RTC_HISTOGRAM_COMMON_BLOCK_SLOW( \ + name, sample, \ + webrtc::metrics::SparseHistogramFactoryGetEnumeration(name, boundary)) + +// Histogram for percentage (evenly spaced buckets). #define RTC_HISTOGRAM_PERCENTAGE(name, sample) \ - RTC_HISTOGRAM_ENUMERATION(name, sample, 101) + RTC_HISTOGRAM_ENUMERATION(name, sample, 101) -// Histogram for enumerators. +// Histogram for booleans. +#define RTC_HISTOGRAM_BOOLEAN(name, sample) \ + RTC_HISTOGRAM_ENUMERATION(name, sample, 2) + +// Histogram for enumerators (evenly spaced buckets). // |boundary| should be above the max enumerator sample. #define RTC_HISTOGRAM_ENUMERATION(name, sample, boundary) \ - RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \ - webrtc::metrics::HistogramFactoryGetEnumeration(name, boundary)) + RTC_HISTOGRAM_COMMON_BLOCK_SLOW( \ + name, sample, \ + webrtc::metrics::HistogramFactoryGetEnumeration(name, boundary)) -#define RTC_HISTOGRAM_COMMON_BLOCK(constant_name, sample, \ - factory_get_invocation) \ - do { \ - webrtc::metrics::Histogram* histogram_pointer = factory_get_invocation; \ - webrtc::metrics::HistogramAdd(histogram_pointer, constant_name, sample); \ +// The name of the histogram should not vary. +// TODO(asapersson): Consider changing string to const char*. +#define RTC_HISTOGRAM_COMMON_BLOCK(constant_name, sample, \ + factory_get_invocation) \ + do { \ + static webrtc::metrics::Histogram* atomic_histogram_pointer = nullptr; \ + webrtc::metrics::Histogram* histogram_pointer = \ + rtc::AtomicOps::AcquireLoadPtr(&atomic_histogram_pointer); \ + if (!histogram_pointer) { \ + histogram_pointer = factory_get_invocation; \ + webrtc::metrics::Histogram* prev_pointer = \ + rtc::AtomicOps::CompareAndSwapPtr( \ + &atomic_histogram_pointer, \ + static_cast(nullptr), \ + histogram_pointer); \ + RTC_DCHECK(prev_pointer == nullptr || \ + prev_pointer == histogram_pointer); \ + } \ + if (histogram_pointer) { \ + webrtc::metrics::HistogramAdd(histogram_pointer, sample); \ + } \ } while (0) +// The histogram is constructed/found for each call. +// May be used for histograms with infrequent updates.` +#define RTC_HISTOGRAM_COMMON_BLOCK_SLOW(name, sample, factory_get_invocation) \ + do { \ + webrtc::metrics::Histogram* histogram_pointer = factory_get_invocation; \ + if (histogram_pointer) { \ + webrtc::metrics::HistogramAdd(histogram_pointer, sample); \ + } \ + } while (0) + +// Helper macros. +// Macros for calling a histogram with varying name (e.g. when using a metric +// in different modes such as real-time vs screenshare). Fast, because pointer +// is cached. |index| should be different for different names. Allowed |index| +// values are 0, 1, and 2. +#define RTC_HISTOGRAMS_COUNTS_100(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 100, 50)) + +#define RTC_HISTOGRAMS_COUNTS_200(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 200, 50)) + +#define RTC_HISTOGRAMS_COUNTS_500(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 500, 50)) + +#define RTC_HISTOGRAMS_COUNTS_1000(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 1000, 50)) + +#define RTC_HISTOGRAMS_COUNTS_10000(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 10000, 50)) + +#define RTC_HISTOGRAMS_COUNTS_100000(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_COUNTS(name, sample, 1, 100000, 50)) + +#define RTC_HISTOGRAMS_ENUMERATION(index, name, sample, boundary) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_ENUMERATION(name, sample, boundary)) + +#define RTC_HISTOGRAMS_PERCENTAGE(index, name, sample) \ + RTC_HISTOGRAMS_COMMON(index, name, sample, \ + RTC_HISTOGRAM_PERCENTAGE(name, sample)) + +#define RTC_HISTOGRAMS_COMMON(index, name, sample, macro_invocation) \ + do { \ + switch (index) { \ + case 0: \ + macro_invocation; \ + break; \ + case 1: \ + macro_invocation; \ + break; \ + case 2: \ + macro_invocation; \ + break; \ + default: \ + RTC_NOTREACHED(); \ + } \ + } while (0) + +#else + +//////////////////////////////////////////////////////////////////////////////// +// This section defines no-op alternatives to the metrics macros when +// RTC_METRICS_ENABLED is defined. + +#define RTC_HISTOGRAM_COUNTS_100(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_200(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_500(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_1000(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_10000(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_100000(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count) \ + webrtc::metrics_impl::NoOp(name, sample, min, max, bucket_count) + +#define RTC_HISTOGRAM_COUNTS_LINEAR(name, sample, min, max, bucket_count) \ + webrtc::metrics_impl::NoOp(name, sample, min, max, bucket_count) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_100(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_200(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_500(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_1000(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_10000(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE_100000(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_COUNTS_SPARSE(name, sample, min, max, bucket_count) \ + webrtc::metrics_impl::NoOp(name, sample, min, max, bucket_count) + +#define RTC_HISTOGRAM_PERCENTAGE_SPARSE(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_BOOLEAN_SPARSE(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_ENUMERATION_SPARSE(name, sample, boundary) \ + webrtc::metrics_impl::NoOp(name, sample, boundary) + +#define RTC_HISTOGRAM_PERCENTAGE(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_BOOLEAN(name, sample) webrtc::metrics_impl::NoOp(name, sample) + +#define RTC_HISTOGRAM_ENUMERATION(name, sample, boundary) \ + webrtc::metrics_impl::NoOp(name, sample, boundary) + +#define RTC_HISTOGRAM_COMMON_BLOCK(constant_name, sample, \ + factory_get_invocation) \ + webrtc::metrics_impl::NoOp(constant_name, sample, factory_get_invocation) + +#define RTC_HISTOGRAM_COMMON_BLOCK_SLOW(name, sample, factory_get_invocation) \ + webrtc::metrics_impl::NoOp(name, sample, factory_get_invocation) + +#define RTC_HISTOGRAMS_COUNTS_100(index, name, sample) webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COUNTS_200(index, name, sample) webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COUNTS_500(index, name, sample) webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COUNTS_1000(index, name, sample) \ + webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COUNTS_10000(index, name, sample) \ + webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COUNTS_100000(index, name, sample) \ + webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_ENUMERATION(index, name, sample, boundary) \ + webrtc::metrics_impl::NoOp(index, name, sample, boundary) + +#define RTC_HISTOGRAMS_PERCENTAGE(index, name, sample) webrtc::metrics_impl::NoOp(index, name, sample) + +#define RTC_HISTOGRAMS_COMMON(index, name, sample, macro_invocation) \ + webrtc::metrics_impl::NoOp(index, name, sample, macro_invocation) + +#endif // RTC_METRICS_ENABLED namespace webrtc { namespace metrics { @@ -119,21 +371,68 @@ class Histogram; // histogram). // Get histogram for counters. -Histogram* HistogramFactoryGetCounts( - const std::string& name, int min, int max, int bucket_count); +Histogram* HistogramFactoryGetCounts(const std::string& name, + int min, + int max, + int bucket_count); + +// Get histogram for counters with linear bucket spacing. +Histogram* HistogramFactoryGetCountsLinear(const std::string& name, + int min, + int max, + int bucket_count); // Get histogram for enumerators. // |boundary| should be above the max enumerator sample. -Histogram* HistogramFactoryGetEnumeration( - const std::string& name, int boundary); +Histogram* HistogramFactoryGetEnumeration(const std::string& name, + int boundary); + +// Get sparse histogram for enumerators. +// |boundary| should be above the max enumerator sample. +Histogram* SparseHistogramFactoryGetEnumeration(const std::string& name, + int boundary); // Function for adding a |sample| to a histogram. -// |name| can be used to verify that it matches the histogram name. -void HistogramAdd( - Histogram* histogram_pointer, const std::string& name, int sample); +void HistogramAdd(Histogram* histogram_pointer, int sample); + +struct SampleInfo { + SampleInfo(const std::string& name, int min, int max, size_t bucket_count); + ~SampleInfo(); + + const std::string name; + const int min; + const int max; + const size_t bucket_count; + std::map samples; // +}; + +// Enables collection of samples. +// This method should be called before any other call into webrtc. +void Enable(); + +// Gets histograms and clears all samples. +void GetAndReset( + std::map>* histograms); + +// Functions below are mainly for testing. + +// Clears all samples. +void Reset(); + +// Returns the number of times the |sample| has been added to the histogram. +int NumEvents(const std::string& name, int sample); + +// Returns the total number of added samples to the histogram. +int NumSamples(const std::string& name); + +// Returns the minimum sample value (or -1 if the histogram has no samples). +int MinSample(const std::string& name); + +// Returns a map with keys the samples with at least one event and values the +// number of events for that sample. +std::map Samples(const std::string& name); } // namespace metrics } // namespace webrtc -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_METRICS_H_ - +#endif // SYSTEM_WRAPPERS_INCLUDE_METRICS_H_ diff --git a/webrtc/system_wrappers/include/scoped_vector.h b/webrtc/system_wrappers/include/scoped_vector.h deleted file mode 100644 index 7336d98..0000000 --- a/webrtc/system_wrappers/include/scoped_vector.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -// Borrowed from Chromium's src/base/memory/scoped_vector.h. - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_SCOPED_VECTOR_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_SCOPED_VECTOR_H_ - -#include - -#include "webrtc/base/checks.h" -#include "webrtc/system_wrappers/include/stl_util.h" - -namespace webrtc { - -// ScopedVector wraps a vector deleting the elements from its -// destructor. -template -class ScopedVector { - public: - typedef typename std::vector::allocator_type allocator_type; - typedef typename std::vector::size_type size_type; - typedef typename std::vector::difference_type difference_type; - typedef typename std::vector::pointer pointer; - typedef typename std::vector::const_pointer const_pointer; - typedef typename std::vector::reference reference; - typedef typename std::vector::const_reference const_reference; - typedef typename std::vector::value_type value_type; - typedef typename std::vector::iterator iterator; - typedef typename std::vector::const_iterator const_iterator; - typedef typename std::vector::reverse_iterator reverse_iterator; - typedef typename std::vector::const_reverse_iterator - const_reverse_iterator; - - ScopedVector() {} - ~ScopedVector() { clear(); } - - // Move construction and assignment. - ScopedVector(ScopedVector&& other) { - *this = static_cast(other); - } - ScopedVector& operator=(ScopedVector&& other) { - std::swap(v_, other.v_); // The arguments are std::vectors, so std::swap - // is the one that we want. - other.clear(); - return *this; - } - - // Deleted copy constructor and copy assignment, to make the type move-only. - ScopedVector(const ScopedVector& other) = delete; - ScopedVector& operator=(const ScopedVector& other) = delete; - - // Get an rvalue reference. (sv.Pass() does the same thing as std::move(sv).) - ScopedVector&& Pass() { return static_cast(*this); } - - reference operator[](size_t index) { return v_[index]; } - const_reference operator[](size_t index) const { return v_[index]; } - - bool empty() const { return v_.empty(); } - size_t size() const { return v_.size(); } - - reverse_iterator rbegin() { return v_.rbegin(); } - const_reverse_iterator rbegin() const { return v_.rbegin(); } - reverse_iterator rend() { return v_.rend(); } - const_reverse_iterator rend() const { return v_.rend(); } - - iterator begin() { return v_.begin(); } - const_iterator begin() const { return v_.begin(); } - iterator end() { return v_.end(); } - const_iterator end() const { return v_.end(); } - - const_reference front() const { return v_.front(); } - reference front() { return v_.front(); } - const_reference back() const { return v_.back(); } - reference back() { return v_.back(); } - - void push_back(T* elem) { v_.push_back(elem); } - - void pop_back() { - RTC_DCHECK(!empty()); - delete v_.back(); - v_.pop_back(); - } - - std::vector& get() { return v_; } - const std::vector& get() const { return v_; } - void swap(std::vector& other) { v_.swap(other); } - void swap(ScopedVector& other) { v_.swap(other.v_); } - void release(std::vector* out) { - out->swap(v_); - v_.clear(); - } - - void reserve(size_t capacity) { v_.reserve(capacity); } - - // Resize, deleting elements in the disappearing range if we are shrinking. - void resize(size_t new_size) { - if (v_.size() > new_size) - STLDeleteContainerPointers(v_.begin() + new_size, v_.end()); - v_.resize(new_size); - } - - template - void assign(InputIterator begin, InputIterator end) { - v_.assign(begin, end); - } - - void clear() { STLDeleteElements(&v_); } - - // Like |clear()|, but doesn't delete any elements. - void weak_clear() { v_.clear(); } - - // Lets the ScopedVector take ownership of |x|. - iterator insert(iterator position, T* x) { - return v_.insert(position, x); - } - - // Lets the ScopedVector take ownership of elements in [first,last). - template - void insert(iterator position, InputIterator first, InputIterator last) { - v_.insert(position, first, last); - } - - iterator erase(iterator position) { - delete *position; - return v_.erase(position); - } - - iterator erase(iterator first, iterator last) { - STLDeleteContainerPointers(first, last); - return v_.erase(first, last); - } - - // Like |erase()|, but doesn't delete the element at |position|. - iterator weak_erase(iterator position) { - return v_.erase(position); - } - - // Like |erase()|, but doesn't delete the elements in [first, last). - iterator weak_erase(iterator first, iterator last) { - return v_.erase(first, last); - } - - private: - std::vector v_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_SCOPED_VECTOR_H_ diff --git a/webrtc/system_wrappers/include/sleep.h b/webrtc/system_wrappers/include/sleep.h index e7ed8b3..3bf8df2 100644 --- a/webrtc/system_wrappers/include/sleep.h +++ b/webrtc/system_wrappers/include/sleep.h @@ -9,8 +9,8 @@ */ // An OS-independent sleep function. -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_SLEEP_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_SLEEP_H_ +#ifndef SYSTEM_WRAPPERS_INCLUDE_SLEEP_H_ +#define SYSTEM_WRAPPERS_INCLUDE_SLEEP_H_ namespace webrtc { @@ -21,4 +21,4 @@ void SleepMs(int msecs); } // namespace webrtc -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_SLEEP_H_ +#endif // SYSTEM_WRAPPERS_INCLUDE_SLEEP_H_ diff --git a/webrtc/system_wrappers/include/static_instance.h b/webrtc/system_wrappers/include/static_instance.h deleted file mode 100644 index 41946d9..0000000 --- a/webrtc/system_wrappers/include/static_instance.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STATIC_INSTANCE_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STATIC_INSTANCE_H_ - -#include - -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" -#ifdef _WIN32 -#include "webrtc/system_wrappers/include/fix_interlocked_exchange_pointer_win.h" -#endif - -namespace webrtc { - -enum CountOperation { - kRelease, - kAddRef, - kAddRefNoCreate -}; -enum CreateOperation { - kInstanceExists, - kCreate, - kDestroy -}; - -template -// Construct On First Use idiom. Avoids -// "static initialization order fiasco". -static T* GetStaticInstance(CountOperation count_operation) { - // TODO (hellner): use atomic wrapper instead. - static volatile long instance_count = 0; - static T* volatile instance = NULL; - CreateOperation state = kInstanceExists; -#ifndef _WIN32 - // This memory is staticly allocated once. The application does not try to - // free this memory. This approach is taken to avoid issues with - // destruction order for statically allocated memory. The memory will be - // reclaimed by the OS and memory leak tools will not recognize memory - // reachable from statics leaked so no noise is added by doing this. - static CriticalSectionWrapper* crit_sect( - CriticalSectionWrapper::CreateCriticalSection()); - CriticalSectionScoped lock(crit_sect); - - if (count_operation == - kAddRefNoCreate && instance_count == 0) { - return NULL; - } - if (count_operation == - kAddRef || - count_operation == kAddRefNoCreate) { - instance_count++; - if (instance_count == 1) { - state = kCreate; - } - } else { - instance_count--; - if (instance_count == 0) { - state = kDestroy; - } - } - if (state == kCreate) { - instance = T::CreateInstance(); - } else if (state == kDestroy) { - T* old_instance = instance; - instance = NULL; - // The state will not change past this point. Release the critical - // section while deleting the object in case it would be blocking on - // access back to this object. (This is the case for the tracing class - // since the thread owned by the tracing class also traces). - // TODO(hellner): this is a bit out of place but here goes, de-couple - // thread implementation with trace implementation. - crit_sect->Leave(); - if (old_instance) { - delete old_instance; - } - // Re-acquire the lock since the scoped critical section will release - // it. - crit_sect->Enter(); - return NULL; - } -#else // _WIN32 - if (count_operation == - kAddRefNoCreate && instance_count == 0) { - return NULL; - } - if (count_operation == kAddRefNoCreate) { - if (1 == InterlockedIncrement(&instance_count)) { - // The instance has been destroyed by some other thread. Rollback. - InterlockedDecrement(&instance_count); - assert(false); - return NULL; - } - // Sanity to catch corrupt state. - if (instance == NULL) { - assert(false); - InterlockedDecrement(&instance_count); - return NULL; - } - } else if (count_operation == kAddRef) { - if (instance_count == 0) { - state = kCreate; - } else { - if (1 == InterlockedIncrement(&instance_count)) { - // InterlockedDecrement because reference count should not be - // updated just yet (that's done when the instance is created). - InterlockedDecrement(&instance_count); - state = kCreate; - } - } - } else { - int new_value = InterlockedDecrement(&instance_count); - if (new_value == 0) { - state = kDestroy; - } - } - - if (state == kCreate) { - // Create instance and let whichever thread finishes first assign its - // local copy to the global instance. All other threads reclaim their - // local copy. - T* new_instance = T::CreateInstance(); - if (1 == InterlockedIncrement(&instance_count)) { - InterlockedExchangePointer(reinterpret_cast(&instance), - new_instance); - } else { - InterlockedDecrement(&instance_count); - if (new_instance) { - delete static_cast(new_instance); - } - } - } else if (state == kDestroy) { - T* old_value = static_cast(InterlockedExchangePointer( - reinterpret_cast(&instance), NULL)); - if (old_value) { - delete static_cast(old_value); - } - return NULL; - } -#endif // #ifndef _WIN32 - return instance; -} - -} // namspace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STATIC_INSTANCE_H_ diff --git a/webrtc/system_wrappers/include/stl_util.h b/webrtc/system_wrappers/include/stl_util.h deleted file mode 100644 index b7a7021..0000000 --- a/webrtc/system_wrappers/include/stl_util.h +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -// Borrowed from Chromium's src/base/stl_util.h. - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STL_UTIL_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STL_UTIL_H_ - -#include -#include -#include -#include -#include -#include - -namespace webrtc { - -// Clears internal memory of an STL object. -// STL clear()/reserve(0) does not always free internal memory allocated -// This function uses swap/destructor to ensure the internal memory is freed. -template -void STLClearObject(T* obj) { - T tmp; - tmp.swap(*obj); - // Sometimes "T tmp" allocates objects with memory (arena implementation?). - // Hence using additional reserve(0) even if it doesn't always work. - obj->reserve(0); -} - -// For a range within a container of pointers, calls delete (non-array version) -// on these pointers. -// NOTE: for these three functions, we could just implement a DeleteObject -// functor and then call for_each() on the range and functor, but this -// requires us to pull in all of algorithm.h, which seems expensive. -// For hash_[multi]set, it is important that this deletes behind the iterator -// because the hash_set may call the hash function on the iterator when it is -// advanced, which could result in the hash function trying to deference a -// stale pointer. -template -void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end) { - while (begin != end) { - ForwardIterator temp = begin; - ++begin; - delete *temp; - } -} - -// For a range within a container of pairs, calls delete (non-array version) on -// BOTH items in the pairs. -// NOTE: Like STLDeleteContainerPointers, it is important that this deletes -// behind the iterator because if both the key and value are deleted, the -// container may call the hash function on the iterator when it is advanced, -// which could result in the hash function trying to dereference a stale -// pointer. -template -void STLDeleteContainerPairPointers(ForwardIterator begin, - ForwardIterator end) { - while (begin != end) { - ForwardIterator temp = begin; - ++begin; - delete temp->first; - delete temp->second; - } -} - -// For a range within a container of pairs, calls delete (non-array version) on -// the FIRST item in the pairs. -// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator. -template -void STLDeleteContainerPairFirstPointers(ForwardIterator begin, - ForwardIterator end) { - while (begin != end) { - ForwardIterator temp = begin; - ++begin; - delete temp->first; - } -} - -// For a range within a container of pairs, calls delete. -// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator. -// Deleting the value does not always invalidate the iterator, but it may -// do so if the key is a pointer into the value object. -template -void STLDeleteContainerPairSecondPointers(ForwardIterator begin, - ForwardIterator end) { - while (begin != end) { - ForwardIterator temp = begin; - ++begin; - delete temp->second; - } -} - -// To treat a possibly-empty vector as an array, use these functions. -// If you know the array will never be empty, you can use &*v.begin() -// directly, but that is undefined behaviour if |v| is empty. -template -inline T* vector_as_array(std::vector* v) { - return v->empty() ? NULL : &*v->begin(); -} - -template -inline const T* vector_as_array(const std::vector* v) { - return v->empty() ? NULL : &*v->begin(); -} - -// Return a mutable char* pointing to a string's internal buffer, -// which may not be null-terminated. Writing through this pointer will -// modify the string. -// -// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the -// next call to a string method that invalidates iterators. -// -// As of 2006-04, there is no standard-blessed way of getting a -// mutable reference to a string's internal buffer. However, issue 530 -// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530) -// proposes this as the method. According to Matt Austern, this should -// already work on all current implementations. -inline char* string_as_array(std::string* str) { - // DO NOT USE const_cast(str->data()) - return str->empty() ? NULL : &*str->begin(); -} - -// The following functions are useful for cleaning up STL containers whose -// elements point to allocated memory. - -// STLDeleteElements() deletes all the elements in an STL container and clears -// the container. This function is suitable for use with a vector, set, -// hash_set, or any other STL container which defines sensible begin(), end(), -// and clear() methods. -// -// If container is NULL, this function is a no-op. -// -// As an alternative to calling STLDeleteElements() directly, consider -// STLElementDeleter (defined below), which ensures that your container's -// elements are deleted when the STLElementDeleter goes out of scope. -template -void STLDeleteElements(T* container) { - if (!container) - return; - STLDeleteContainerPointers(container->begin(), container->end()); - container->clear(); -} - -// Given an STL container consisting of (key, value) pairs, STLDeleteValues -// deletes all the "value" components and clears the container. Does nothing -// in the case it's given a NULL pointer. -template -void STLDeleteValues(T* container) { - if (!container) - return; - for (typename T::iterator i(container->begin()); i != container->end(); ++i) - delete i->second; - container->clear(); -} - - -// The following classes provide a convenient way to delete all elements or -// values from STL containers when they goes out of scope. This greatly -// simplifies code that creates temporary objects and has multiple return -// statements. Example: -// -// vector tmp_proto; -// STLElementDeleter > d(&tmp_proto); -// if (...) return false; -// ... -// return success; - -// Given a pointer to an STL container this class will delete all the element -// pointers when it goes out of scope. -template -class STLElementDeleter { - public: - STLElementDeleter(T* container) : container_(container) {} - ~STLElementDeleter() { STLDeleteElements(container_); } - - private: - T* container_; -}; - -// Given a pointer to an STL container this class will delete all the value -// pointers when it goes out of scope. -template -class STLValueDeleter { - public: - STLValueDeleter(T* container) : container_(container) {} - ~STLValueDeleter() { STLDeleteValues(container_); } - - private: - T* container_; -}; - -// Test to see if a set, map, hash_set or hash_map contains a particular key. -// Returns true if the key is in the collection. -template -bool ContainsKey(const Collection& collection, const Key& key) { - return collection.find(key) != collection.end(); -} - -// Returns true if the container is sorted. -template -bool STLIsSorted(const Container& cont) { - // Note: Use reverse iterator on container to ensure we only require - // value_type to implement operator<. - return std::adjacent_find(cont.rbegin(), cont.rend(), - std::less()) - == cont.rend(); -} - -// Returns a new ResultType containing the difference of two sorted containers. -template -ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) { - assert(STLIsSorted(a1)); - assert(STLIsSorted(a2)); - ResultType difference; - std::set_difference(a1.begin(), a1.end(), - a2.begin(), a2.end(), - std::inserter(difference, difference.end())); - return difference; -} - -// Returns a new ResultType containing the union of two sorted containers. -template -ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) { - assert(STLIsSorted(a1)); - assert(STLIsSorted(a2)); - ResultType result; - std::set_union(a1.begin(), a1.end(), - a2.begin(), a2.end(), - std::inserter(result, result.end())); - return result; -} - -// Returns a new ResultType containing the intersection of two sorted -// containers. -template -ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) { - assert(STLIsSorted(a1)); - assert(STLIsSorted(a2)); - ResultType result; - std::set_intersection(a1.begin(), a1.end(), - a2.begin(), a2.end(), - std::inserter(result, result.end())); - return result; -} - -// Returns true if the sorted container |a1| contains all elements of the sorted -// container |a2|. -template -bool STLIncludes(const Arg1& a1, const Arg2& a2) { - assert(STLIsSorted(a1)); - assert(STLIsSorted(a2)); - return std::includes(a1.begin(), a1.end(), - a2.begin(), a2.end()); -} - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_STL_UTIL_H_ diff --git a/webrtc/system_wrappers/include/thread_wrapper.h b/webrtc/system_wrappers/include/thread_wrapper.h deleted file mode 100644 index d475302..0000000 --- a/webrtc/system_wrappers/include/thread_wrapper.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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. - */ - -// System independant wrapper for spawning threads -// Note: the spawned thread will loop over the callback function until stopped. -// Note: The callback function is expected to return every 2 seconds or more -// often. - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_THREAD_WRAPPER_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_THREAD_WRAPPER_H_ - -#if defined(WEBRTC_WIN) -#include -#endif - -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/common_types.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -// Callback function that the spawned thread will enter once spawned. -// A return value of false is interpreted as that the function has no -// more work to do and that the thread can be released. -typedef bool(*ThreadRunFunction)(void*); - -enum ThreadPriority { -#ifdef WEBRTC_WIN - kLowPriority = THREAD_PRIORITY_BELOW_NORMAL, - kNormalPriority = THREAD_PRIORITY_NORMAL, - kHighPriority = THREAD_PRIORITY_ABOVE_NORMAL, - kHighestPriority = THREAD_PRIORITY_HIGHEST, - kRealtimePriority = THREAD_PRIORITY_TIME_CRITICAL -#else - kLowPriority = 1, - kNormalPriority = 2, - kHighPriority = 3, - kHighestPriority = 4, - kRealtimePriority = 5 -#endif -}; - -// Represents a simple worker thread. The implementation must be assumed -// to be single threaded, meaning that all methods of the class, must be -// called from the same thread, including instantiation. -// TODO(tommi): There's no need for this to be a virtual interface since there's -// only ever a single implementation of it. -class ThreadWrapper { - public: - virtual ~ThreadWrapper() {} - - // Factory method. Constructor disabled. - // - // func Pointer to a, by user, specified callback function. - // obj Object associated with the thread. Passed in the callback - // function. - // prio Thread priority. May require root/admin rights. - // thread_name NULL terminated thread name, will be visable in the Windows - // debugger. - static rtc::scoped_ptr CreateThread(ThreadRunFunction func, - void* obj, const char* thread_name); - - // Get the current thread's thread ID. - // NOTE: This is a static method. It returns the id of the calling thread, - // *not* the id of the worker thread that a ThreadWrapper instance represents. - // TODO(tommi): Move outside of the ThreadWrapper class to avoid confusion. - static uint32_t GetThreadId(); - - // Tries to spawns a thread and returns true if that was successful. - // Additionally, it tries to set thread priority according to the priority - // from when CreateThread was called. However, failure to set priority will - // not result in a false return value. - virtual bool Start() = 0; - - // Stops the spawned thread and waits for it to be reclaimed with a timeout - // of two seconds. Will return false if the thread was not reclaimed. - // Multiple tries to Stop are allowed (e.g. to wait longer than 2 seconds). - // It's ok to call Stop() even if the spawned thread has been reclaimed. - virtual bool Stop() = 0; - - // Set the priority of the worker thread. Must be called when thread - // is running. - virtual bool SetPriority(ThreadPriority priority) = 0; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_THREAD_WRAPPER_H_ diff --git a/webrtc/system_wrappers/include/trace.h b/webrtc/system_wrappers/include/trace.h deleted file mode 100644 index 25a3d74..0000000 --- a/webrtc/system_wrappers/include/trace.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2012 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. - * - * System independent wrapper for logging runtime information to file. - * Note: All log messages will be written to the same trace file. - * Note: If too many messages are written to file there will be a build up of - * messages. Apply filtering to avoid that. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_TRACE_H_ -#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_TRACE_H_ - -#include "webrtc/common_types.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -#if defined(WEBRTC_RESTRICT_LOGGING) -// Disable all TRACE macros. The LOG macro is still functional. -#define WEBRTC_TRACE true ? (void) 0 : Trace::Add -#else -#define WEBRTC_TRACE Trace::Add -#endif - -class Trace { - public: - // The length of the trace text preceeding the log message. - static const int kBoilerplateLength; - // The position of the timestamp text within a trace. - static const int kTimestampPosition; - // The length of the timestamp (without "delta" field). - static const int kTimestampLength; - - // Increments the reference count to the trace. - static void CreateTrace(); - // Decrements the reference count to the trace. - static void ReturnTrace(); - // Note: any instance that writes to the trace file should increment and - // decrement the reference count on construction and destruction, - // respectively. - - // Specifies what type of messages should be written to the trace file. The - // filter parameter is a bitmask where each message type is enumerated by the - // TraceLevel enumerator. TODO(hellner): why is the TraceLevel enumerator not - // defined in this file? - static void set_level_filter(int filter); - - // Returns what type of messages are written to the trace file. - static int level_filter(); - - // Sets the file name. If add_file_counter is false the same file will be - // reused when it fills up. If it's true a new file with incremented name - // will be used. - static int32_t SetTraceFile(const char* file_name, - const bool add_file_counter = false); - - // Returns the name of the file that the trace is currently writing to. - static int32_t TraceFile(char file_name[1024]); - - // Registers callback to receive trace messages. - // TODO(hellner): Why not use OutStream instead? Why is TraceCallback not - // defined in this file? - static int32_t SetTraceCallback(TraceCallback* callback); - - // Adds a trace message for writing to file. The message is put in a queue - // for writing to file whenever possible for performance reasons. I.e. there - // is a crash it is possible that the last, vital logs are not logged yet. - // level is the type of message to log. If that type of messages is - // filtered it will not be written to file. module is an identifier for what - // part of the code the message is coming. - // id is an identifier that should be unique for that set of classes that - // are associated (e.g. all instances owned by an engine). - // msg and the ellipsis are the same as e.g. sprintf. - // TODO(hellner) Why is TraceModule not defined in this file? - static void Add(const TraceLevel level, - const TraceModule module, - const int32_t id, - const char* msg, ...); - - private: - static volatile int level_filter_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_TRACE_H_ diff --git a/webrtc/system_wrappers/meson.build b/webrtc/system_wrappers/meson.build index c8cb9ed..22a9a1b 100644 --- a/webrtc/system_wrappers/meson.build +++ b/webrtc/system_wrappers/meson.build @@ -1,47 +1,18 @@ system_wrappers_sources = [ - 'source/aligned_malloc.cc', 'source/cpu_features.cc', - 'source/event.cc', - 'source/file_impl.cc', - 'source/critical_section.cc', - 'source/logging.cc', - 'source/metrics_default.cc', - 'source/rw_lock.cc', + 'source/field_trial.cc', + 'source/metrics.cc', 'source/sleep.cc', - 'source/thread.cc', - 'source/trace_impl.cc', ] -if have_posix - system_wrappers_sources += [ - 'source/critical_section_posix.cc', - 'source/event_timer_posix.cc', - 'source/rw_lock_posix.cc', - 'source/thread_posix.cc', - 'source/trace_posix.cc', - ] -endif - -if have_win - system_wrappers_sources += [ - 'source/critical_section_win.cc', - 'source/condition_variable.cc', - 'source/condition_variable_event_win.cc', - 'source/condition_variable_native_win.cc', - 'source/event_timer_win.cc', - 'source/rw_lock_win.cc', - 'source/rw_lock_generic.cc', - 'source/thread_win.cc', - 'source/trace_win.cc', - ] -endif - system_headers = [ - 'include/trace.h', + 'include/cpu_features_wrapper.h', + 'include/metrics.h', + 'include/sleep.h', ] install_headers(system_headers, - subdir: 'webrtc_audio_processing/webrtc/system_wrappers/include' + subdir: 'webrtc_audio_processing/system_wrappers/include' ) libsystem_wrappers = static_library('system_wrappers', diff --git a/webrtc/system_wrappers/source/condition_variable.cc b/webrtc/system_wrappers/source/condition_variable.cc deleted file mode 100644 index f5ae93a..0000000 --- a/webrtc/system_wrappers/source/condition_variable.cc +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 "webrtc/system_wrappers/include/condition_variable_wrapper.h" - -#if defined(_WIN32) -#include -#include "webrtc/system_wrappers/source/condition_variable_event_win.h" -#include "webrtc/system_wrappers/source/condition_variable_native_win.h" -#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) -#include -#include "webrtc/system_wrappers/source/condition_variable_posix.h" -#endif - -namespace webrtc { - -ConditionVariableWrapper* ConditionVariableWrapper::CreateConditionVariable() { -#if defined(_WIN32) - // Try to create native condition variable implementation. - ConditionVariableWrapper* ret_val = ConditionVariableNativeWin::Create(); - if (!ret_val) { - // Native condition variable implementation does not exist. Create generic - // condition variable based on events. - ret_val = new ConditionVariableEventWin(); - } - return ret_val; -#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) - return ConditionVariablePosix::Create(); -#else - return NULL; -#endif -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/condition_variable_event_win.cc b/webrtc/system_wrappers/source/condition_variable_event_win.cc deleted file mode 100644 index 41b019d..0000000 --- a/webrtc/system_wrappers/source/condition_variable_event_win.cc +++ /dev/null @@ -1,195 +0,0 @@ -/* -Source: -http://www1.cse.wustl.edu/~schmidt/ACE-copying.html - -License: -Copyright and Licensing Information for ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), -and CoSMIC(TM) - -ACE(TM), TAO(TM), CIAO(TM), DAnCE>(TM), and CoSMIC(TM) (henceforth referred to -as "DOC software") are copyrighted by Douglas C. Schmidt and his research -group at Washington University, University of California, Irvine, and -Vanderbilt University, Copyright (c) 1993-2009, all rights reserved. Since DOC -software is open-source, freely available software, you are free to use, -modify, copy, and distribute--perpetually and irrevocably--the DOC software -source code and object code produced from the source, as well as copy and -distribute modified versions of this software. You must, however, include this -copyright statement along with any code built using DOC software that you -release. No copyright statement needs to be provided if you just ship binary -executables of your software products. -You can use DOC software in commercial and/or binary software releases and are -under no obligation to redistribute any of your source code that is built -using DOC software. Note, however, that you may not misappropriate the DOC -software code, such as copyrighting it yourself or claiming authorship of the -DOC software code, in a way that will prevent DOC software from being -distributed freely using an open-source development model. You needn't inform -anyone that you're using DOC software in your software, though we encourage -you to let us know so we can promote your project in the DOC software success -stories. - -The ACE, TAO, CIAO, DAnCE, and CoSMIC web sites are maintained by the DOC -Group at the Institute for Software Integrated Systems (ISIS) and the Center -for Distributed Object Computing of Washington University, St. Louis for the -development of open-source software as part of the open-source software -community. Submissions are provided by the submitter ``as is'' with no -warranties whatsoever, including any warranty of merchantability, -noninfringement of third party intellectual property, or fitness for any -particular purpose. In no event shall the submitter be liable for any direct, -indirect, special, exemplary, punitive, or consequential damages, including -without limitation, lost profits, even if advised of the possibility of such -damages. Likewise, DOC software is provided as is with no warranties of any -kind, including the warranties of design, merchantability, and fitness for a -particular purpose, noninfringement, or arising from a course of dealing, -usage or trade practice. Washington University, UC Irvine, Vanderbilt -University, their employees, and students shall have no liability with respect -to the infringement of copyrights, trade secrets or any patents by DOC -software or any part thereof. Moreover, in no event will Washington -University, UC Irvine, or Vanderbilt University, their employees, or students -be liable for any lost revenue or profits or other special, indirect and -consequential damages. - -DOC software is provided with no support and without any obligation on the -part of Washington University, UC Irvine, Vanderbilt University, their -employees, or students to assist in its use, correction, modification, or -enhancement. A number of companies around the world provide commercial support -for DOC software, however. DOC software is Y2K-compliant, as long as the -underlying OS platform is Y2K-compliant. Likewise, DOC software is compliant -with the new US daylight savings rule passed by Congress as "The Energy Policy -Act of 2005," which established new daylight savings times (DST) rules for the -United States that expand DST as of March 2007. Since DOC software obtains -time/date and calendaring information from operating systems users will not be -affected by the new DST rules as long as they upgrade their operating systems -accordingly. - -The names ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), CoSMIC(TM), Washington -University, UC Irvine, and Vanderbilt University, may not be used to endorse -or promote products or services derived from this source without express -written permission from Washington University, UC Irvine, or Vanderbilt -University. This license grants no permission to call products or services -derived from this source ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), or CoSMIC(TM), -nor does it grant permission for the name Washington University, UC Irvine, or -Vanderbilt University to appear in their names. -*/ - -/* - * This source code contain modifications to the original source code - * which can be found here: - * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html (section 3.2). - * Modifications: - * 1) Dynamic detection of native support for condition variables. - * 2) Use of WebRTC defined types and classes. Renaming of some functions. - * 3) Introduction of a second event for wake all functionality. This prevents - * a thread from spinning on the same condition variable, preventing other - * threads from waking up. - */ - -#include "webrtc/system_wrappers/source/condition_variable_event_win.h" -#include "webrtc/system_wrappers/source/critical_section_win.h" - -namespace webrtc { - -ConditionVariableEventWin::ConditionVariableEventWin() : eventID_(WAKEALL_0) { - memset(&num_waiters_[0], 0, sizeof(num_waiters_)); - - InitializeCriticalSection(&num_waiters_crit_sect_); - - events_[WAKEALL_0] = CreateEvent(NULL, // no security attributes - TRUE, // manual-reset, sticky event - FALSE, // initial state non-signaled - NULL); // no name for event - - events_[WAKEALL_1] = CreateEvent(NULL, // no security attributes - TRUE, // manual-reset, sticky event - FALSE, // initial state non-signaled - NULL); // no name for event - - events_[WAKE] = CreateEvent(NULL, // no security attributes - FALSE, // auto-reset, sticky event - FALSE, // initial state non-signaled - NULL); // no name for event -} - -ConditionVariableEventWin::~ConditionVariableEventWin() { - CloseHandle(events_[WAKE]); - CloseHandle(events_[WAKEALL_1]); - CloseHandle(events_[WAKEALL_0]); - - DeleteCriticalSection(&num_waiters_crit_sect_); -} - -void ConditionVariableEventWin::SleepCS(CriticalSectionWrapper& crit_sect) { - SleepCS(crit_sect, INFINITE); -} - -bool ConditionVariableEventWin::SleepCS(CriticalSectionWrapper& crit_sect, - unsigned long max_time_in_ms) { - EnterCriticalSection(&num_waiters_crit_sect_); - - // Get the eventID for the event that will be triggered by next - // WakeAll() call and start waiting for it. - const EventWakeUpType eventID = - (WAKEALL_0 == eventID_) ? WAKEALL_1 : WAKEALL_0; - - ++(num_waiters_[eventID]); - LeaveCriticalSection(&num_waiters_crit_sect_); - - CriticalSectionWindows* cs = - static_cast(&crit_sect); - LeaveCriticalSection(&cs->crit); - HANDLE events[2]; - events[0] = events_[WAKE]; - events[1] = events_[eventID]; - const DWORD result = WaitForMultipleObjects(2, // Wait on 2 events. - events, - FALSE, // Wait for either. - max_time_in_ms); - - const bool ret_val = (result != WAIT_TIMEOUT); - - EnterCriticalSection(&num_waiters_crit_sect_); - --(num_waiters_[eventID]); - - // Last waiter should only be true for WakeAll(). WakeAll() correspond - // to position 1 in events[] -> (result == WAIT_OBJECT_0 + 1) - const bool last_waiter = (result == WAIT_OBJECT_0 + 1) && - (num_waiters_[eventID] == 0); - LeaveCriticalSection(&num_waiters_crit_sect_); - - if (last_waiter) { - // Reset/unset the WakeAll() event since all threads have been - // released. - ResetEvent(events_[eventID]); - } - - EnterCriticalSection(&cs->crit); - return ret_val; -} - -void ConditionVariableEventWin::Wake() { - EnterCriticalSection(&num_waiters_crit_sect_); - const bool have_waiters = (num_waiters_[WAKEALL_0] > 0) || - (num_waiters_[WAKEALL_1] > 0); - LeaveCriticalSection(&num_waiters_crit_sect_); - - if (have_waiters) { - SetEvent(events_[WAKE]); - } -} - -void ConditionVariableEventWin::WakeAll() { - EnterCriticalSection(&num_waiters_crit_sect_); - - // Update current WakeAll() event - eventID_ = (WAKEALL_0 == eventID_) ? WAKEALL_1 : WAKEALL_0; - - // Trigger current event - const EventWakeUpType eventID = eventID_; - const bool have_waiters = num_waiters_[eventID] > 0; - LeaveCriticalSection(&num_waiters_crit_sect_); - - if (have_waiters) { - SetEvent(events_[eventID]); - } -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/condition_variable_event_win.h b/webrtc/system_wrappers/source/condition_variable_event_win.h deleted file mode 100644 index cdcef7d..0000000 --- a/webrtc/system_wrappers/source/condition_variable_event_win.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2013 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_CONDITION_VARIABLE_EVENT_WIN_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_CONDITION_VARIABLE_EVENT_WIN_H_ - -#include - -#include "webrtc/system_wrappers/include/condition_variable_wrapper.h" - -namespace webrtc { - -class ConditionVariableEventWin : public ConditionVariableWrapper { - public: - ConditionVariableEventWin(); - virtual ~ConditionVariableEventWin(); - - void SleepCS(CriticalSectionWrapper& crit_sect); - bool SleepCS(CriticalSectionWrapper& crit_sect, unsigned long max_time_inMS); - void Wake(); - void WakeAll(); - - private: - enum EventWakeUpType { - WAKEALL_0 = 0, - WAKEALL_1 = 1, - WAKE = 2, - EVENT_COUNT = 3 - }; - - unsigned int num_waiters_[2]; - EventWakeUpType eventID_; - CRITICAL_SECTION num_waiters_crit_sect_; - HANDLE events_[EVENT_COUNT]; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_CONDITION_VARIABLE_EVENT_WIN_H_ diff --git a/webrtc/system_wrappers/source/condition_variable_native_win.cc b/webrtc/system_wrappers/source/condition_variable_native_win.cc deleted file mode 100644 index 45225f2..0000000 --- a/webrtc/system_wrappers/source/condition_variable_native_win.cc +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2013 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 "webrtc/system_wrappers/include/trace.h" -#include "webrtc/system_wrappers/source/condition_variable_native_win.h" -#include "webrtc/system_wrappers/source/critical_section_win.h" - -namespace webrtc { - -static HMODULE library = NULL; -static bool win_support_condition_variables_primitive = false; - -PInitializeConditionVariable PInitializeConditionVariable_; -PSleepConditionVariableCS PSleepConditionVariableCS_; -PWakeConditionVariable PWakeConditionVariable_; -PWakeAllConditionVariable PWakeAllConditionVariable_; - -typedef void (WINAPI *PInitializeConditionVariable)(PCONDITION_VARIABLE); -typedef BOOL (WINAPI *PSleepConditionVariableCS)(PCONDITION_VARIABLE, - PCRITICAL_SECTION, DWORD); -typedef void (WINAPI *PWakeConditionVariable)(PCONDITION_VARIABLE); -typedef void (WINAPI *PWakeAllConditionVariable)(PCONDITION_VARIABLE); - -ConditionVariableNativeWin::ConditionVariableNativeWin() { -} - -ConditionVariableNativeWin::~ConditionVariableNativeWin() { -} - -ConditionVariableWrapper* ConditionVariableNativeWin::Create() { - ConditionVariableNativeWin* ret_val = new ConditionVariableNativeWin(); - if (!ret_val->Init()) { - delete ret_val; - return NULL; - } - return ret_val; -} - -bool ConditionVariableNativeWin::Init() { - if (!library) { - // Native implementation is supported on Vista+. - library = LoadLibrary(TEXT("Kernel32.dll")); - // TODO(henrike): this code results in an attempt to load the above dll - // every time the previous attempt failed. Only try to load once. - if (library) { - // TODO(henrike): not thread safe as reading and writing to library is not - // serialized. Fix. - WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, "Loaded Kernel.dll"); - - PInitializeConditionVariable_ = - (PInitializeConditionVariable) GetProcAddress( - library, "InitializeConditionVariable"); - PSleepConditionVariableCS_ = (PSleepConditionVariableCS) GetProcAddress( - library, "SleepConditionVariableCS"); - PWakeConditionVariable_ = (PWakeConditionVariable) GetProcAddress( - library, "WakeConditionVariable"); - PWakeAllConditionVariable_ = (PWakeAllConditionVariable) GetProcAddress( - library, "WakeAllConditionVariable"); - - if (PInitializeConditionVariable_ && PSleepConditionVariableCS_ - && PWakeConditionVariable_ && PWakeAllConditionVariable_) { - WEBRTC_TRACE( - kTraceStateInfo, kTraceUtility, -1, - "Loaded native condition variables"); - win_support_condition_variables_primitive = true; - } - } - } - if (!win_support_condition_variables_primitive) { - return false; - } - PInitializeConditionVariable_(&condition_variable_); - return true; -} - -void ConditionVariableNativeWin::SleepCS(CriticalSectionWrapper& crit_sect) { - SleepCS(crit_sect, INFINITE); -} - -bool ConditionVariableNativeWin::SleepCS(CriticalSectionWrapper& crit_sect, - unsigned long max_time_in_ms) { - CriticalSectionWindows* cs = - static_cast(&crit_sect); - BOOL ret_val = PSleepConditionVariableCS_(&condition_variable_, - &(cs->crit), max_time_in_ms); - return ret_val != 0; -} - -void ConditionVariableNativeWin::Wake() { - PWakeConditionVariable_(&condition_variable_); -} - -void ConditionVariableNativeWin::WakeAll() { - PWakeAllConditionVariable_(&condition_variable_); -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/condition_variable_native_win.h b/webrtc/system_wrappers/source/condition_variable_native_win.h deleted file mode 100644 index c22787f..0000000 --- a/webrtc/system_wrappers/source/condition_variable_native_win.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2013 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_CONDITION_VARIABLE_NATIVE_WIN_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_CONDITION_VARIABLE_NATIVE_WIN_H_ - -#include - -#include "webrtc/system_wrappers/include/condition_variable_wrapper.h" - -namespace webrtc { - -#if !defined CONDITION_VARIABLE_INIT -typedef struct RTL_CONDITION_VARIABLE_ { - void* Ptr; -} RTL_CONDITION_VARIABLE, *PRTL_CONDITION_VARIABLE; - -typedef RTL_CONDITION_VARIABLE CONDITION_VARIABLE, *PCONDITION_VARIABLE; -#endif - -typedef void (WINAPI* PInitializeConditionVariable)(PCONDITION_VARIABLE); -typedef BOOL (WINAPI* PSleepConditionVariableCS)(PCONDITION_VARIABLE, - PCRITICAL_SECTION, DWORD); -typedef void (WINAPI* PWakeConditionVariable)(PCONDITION_VARIABLE); -typedef void (WINAPI* PWakeAllConditionVariable)(PCONDITION_VARIABLE); - -class ConditionVariableNativeWin : public ConditionVariableWrapper { - public: - static ConditionVariableWrapper* Create(); - virtual ~ConditionVariableNativeWin(); - - void SleepCS(CriticalSectionWrapper& crit_sect); - bool SleepCS(CriticalSectionWrapper& crit_sect, unsigned long max_time_inMS); - void Wake(); - void WakeAll(); - - private: - ConditionVariableNativeWin(); - - bool Init(); - - CONDITION_VARIABLE condition_variable_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_CONDITION_VARIABLE_NATIVE_WIN_H_ diff --git a/webrtc/system_wrappers/source/cpu_features.cc b/webrtc/system_wrappers/source/cpu_features.cc index 49840eb..0f81212 100644 --- a/webrtc/system_wrappers/source/cpu_features.cc +++ b/webrtc/system_wrappers/source/cpu_features.cc @@ -10,13 +10,15 @@ // Parts of this file derived from Chromium's base/cpu.cc. -#include "webrtc/system_wrappers/include/cpu_features_wrapper.h" +#include "rtc_base/system/arch.h" +#include "system_wrappers/include/cpu_features_wrapper.h" +#include "system_wrappers/include/field_trial.h" #if defined(WEBRTC_ARCH_X86_FAMILY) && defined(_MSC_VER) #include #endif -#include "webrtc/typedefs.h" +namespace webrtc { // No CPU feature is available => straight C path. int GetCPUInfoNoASM(CPUFeature feature) { @@ -25,23 +27,40 @@ int GetCPUInfoNoASM(CPUFeature feature) { } #if defined(WEBRTC_ARCH_X86_FAMILY) + +#if defined(WEBRTC_ENABLE_AVX2) +// xgetbv returns the value of an Intel Extended Control Register (XCR). +// Currently only XCR0 is defined by Intel so |xcr| should always be zero. +static uint64_t xgetbv(uint32_t xcr) { +#if defined(_MSC_VER) + return _xgetbv(xcr); +#else + uint32_t eax, edx; + + __asm__ volatile("xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr)); + return (static_cast(edx) << 32) | eax; +#endif // _MSC_VER +} +#endif // WEBRTC_ENABLE_AVX2 + #ifndef _MSC_VER // Intrinsic for "cpuid". #if defined(__pic__) && defined(__i386__) static inline void __cpuid(int cpu_info[4], int info_type) { __asm__ volatile( - "mov %%ebx, %%edi\n" - "cpuid\n" - "xchg %%edi, %%ebx\n" - : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) - : "a"(info_type)); + "mov %%ebx, %%edi\n" + "cpuid\n" + "xchg %%edi, %%ebx\n" + : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), + "=d"(cpu_info[3]) + : "a"(info_type)); } #else static inline void __cpuid(int cpu_info[4], int info_type) { - __asm__ volatile( - "cpuid\n" - : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) - : "a"(info_type)); + __asm__ volatile("cpuid\n" + : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), + "=d"(cpu_info[3]) + : "a"(info_type), "c"(0)); } #endif #endif // _MSC_VER @@ -49,7 +68,7 @@ static inline void __cpuid(int cpu_info[4], int info_type) { #if defined(WEBRTC_ARCH_X86_FAMILY) // Actual feature detection for x86. -static int GetCPUInfo(CPUFeature feature) { +int GetCPUInfo(CPUFeature feature) { int cpu_info[4]; __cpuid(cpu_info, 1); if (feature == kSSE2) { @@ -58,15 +77,39 @@ static int GetCPUInfo(CPUFeature feature) { if (feature == kSSE3) { return 0 != (cpu_info[2] & 0x00000001); } +#if defined(WEBRTC_ENABLE_AVX2) + if (feature == kAVX2 && + !webrtc::field_trial::IsEnabled("WebRTC-Avx2SupportKillSwitch")) { + int cpu_info7[4]; + __cpuid(cpu_info7, 0); + int num_ids = cpu_info7[0]; + if (num_ids < 7) { + return 0; + } + // Interpret CPU feature information. + __cpuid(cpu_info7, 7); + + // AVX instructions can be used when + // a) AVX are supported by the CPU, + // b) XSAVE is supported by the CPU, + // c) XSAVE is enabled by the kernel. + // See http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled + // AVX2 support needs (avx_support && (cpu_info7[1] & 0x00000020) != 0;). + return (cpu_info[2] & 0x10000000) != 0 && + (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ && + (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ && + (xgetbv(0) & 0x00000006) == 6 /* XSAVE enabled by kernel */ && + (cpu_info7[1] & 0x00000020) != 0; + } +#endif // WEBRTC_ENABLE_AVX2 return 0; } #else // Default to straight C for other platforms. -static int GetCPUInfo(CPUFeature feature) { +int GetCPUInfo(CPUFeature feature) { (void)feature; return 0; } #endif -WebRtc_CPUInfo WebRtc_GetCPUInfo = GetCPUInfo; -WebRtc_CPUInfo WebRtc_GetCPUInfoNoASM = GetCPUInfoNoASM; +} // namespace webrtc diff --git a/webrtc/system_wrappers/source/critical_section.cc b/webrtc/system_wrappers/source/critical_section.cc deleted file mode 100644 index c586588..0000000 --- a/webrtc/system_wrappers/source/critical_section.cc +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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. - */ - -#if defined(_WIN32) -#include -#include "webrtc/system_wrappers/source/critical_section_win.h" -#else -#include "webrtc/system_wrappers/source/critical_section_posix.h" -#endif - -namespace webrtc { - -CriticalSectionWrapper* CriticalSectionWrapper::CreateCriticalSection() { -#ifdef _WIN32 - return new CriticalSectionWindows(); -#else - return new CriticalSectionPosix(); -#endif -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/critical_section_posix.cc b/webrtc/system_wrappers/source/critical_section_posix.cc deleted file mode 100644 index 41b7732..0000000 --- a/webrtc/system_wrappers/source/critical_section_posix.cc +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -// General note: return values for the various pthread synchronization APIs -// are explicitly ignored here. In Chromium, the same thing is done for release. -// However, in debugging, failure in these APIs are logged. -// TODO(henrike): add logging when pthread synchronization APIs are failing. - -#include "webrtc/system_wrappers/source/critical_section_posix.h" - -namespace webrtc { - -CriticalSectionPosix::CriticalSectionPosix() { - pthread_mutexattr_t attr; - (void) pthread_mutexattr_init(&attr); - (void) pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - (void) pthread_mutex_init(&mutex_, &attr); -} - -CriticalSectionPosix::~CriticalSectionPosix() { - (void) pthread_mutex_destroy(&mutex_); -} - -void -CriticalSectionPosix::Enter() { - (void) pthread_mutex_lock(&mutex_); -} - -void -CriticalSectionPosix::Leave() { - (void) pthread_mutex_unlock(&mutex_); -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/critical_section_posix.h b/webrtc/system_wrappers/source/critical_section_posix.h deleted file mode 100644 index 099f74c..0000000 --- a/webrtc/system_wrappers/source/critical_section_posix.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_POSIX_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_POSIX_H_ - -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" - -#include - -namespace webrtc { - -class CriticalSectionPosix : public CriticalSectionWrapper { - public: - CriticalSectionPosix(); - - ~CriticalSectionPosix() override; - - void Enter() override; - void Leave() override; - - private: - pthread_mutex_t mutex_; - friend class ConditionVariablePosix; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_POSIX_H_ diff --git a/webrtc/system_wrappers/source/critical_section_win.cc b/webrtc/system_wrappers/source/critical_section_win.cc deleted file mode 100644 index b5149d1..0000000 --- a/webrtc/system_wrappers/source/critical_section_win.cc +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 "webrtc/system_wrappers/source/critical_section_win.h" - -namespace webrtc { - -CriticalSectionWindows::CriticalSectionWindows() { - InitializeCriticalSection(&crit); -} - -CriticalSectionWindows::~CriticalSectionWindows() { - DeleteCriticalSection(&crit); -} - -void -CriticalSectionWindows::Enter() { - EnterCriticalSection(&crit); -} - -void -CriticalSectionWindows::Leave() { - LeaveCriticalSection(&crit); -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/critical_section_win.h b/webrtc/system_wrappers/source/critical_section_win.h deleted file mode 100644 index 8268bc3..0000000 --- a/webrtc/system_wrappers/source/critical_section_win.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_WIN_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_WIN_H_ - -#include -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -class CriticalSectionWindows : public CriticalSectionWrapper { - public: - CriticalSectionWindows(); - - virtual ~CriticalSectionWindows(); - - virtual void Enter(); - virtual void Leave(); - - private: - CRITICAL_SECTION crit; - - friend class ConditionVariableEventWin; - friend class ConditionVariableNativeWin; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_CRITICAL_SECTION_WIN_H_ diff --git a/webrtc/system_wrappers/source/event.cc b/webrtc/system_wrappers/source/event.cc deleted file mode 100644 index 05f918f..0000000 --- a/webrtc/system_wrappers/source/event.cc +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 "webrtc/system_wrappers/include/event_wrapper.h" - -#if defined(_WIN32) -#include -#include "webrtc/system_wrappers/source/event_timer_win.h" -#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) -#include -#include -#include "webrtc/system_wrappers/source/event_timer_posix.h" -#else -#include -#include "webrtc/system_wrappers/source/event_timer_posix.h" -#endif - -#include "webrtc/base/event.h" - -namespace webrtc { - -class EventWrapperImpl : public EventWrapper { - public: - EventWrapperImpl() : event_(false, false) {} - ~EventWrapperImpl() override {} - - bool Set() override { - event_.Set(); - return true; - } - - EventTypeWrapper Wait(unsigned long max_time) override { - int to_wait = max_time == WEBRTC_EVENT_INFINITE ? - rtc::Event::kForever : static_cast(max_time); - return event_.Wait(to_wait) ? kEventSignaled : kEventTimeout; - } - - private: - rtc::Event event_; -}; - -// static -EventWrapper* EventWrapper::Create() { - return new EventWrapperImpl(); -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/event_timer_posix.cc b/webrtc/system_wrappers/source/event_timer_posix.cc deleted file mode 100644 index 99eebcb..0000000 --- a/webrtc/system_wrappers/source/event_timer_posix.cc +++ /dev/null @@ -1,231 +0,0 @@ -/* - * 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 "webrtc/system_wrappers/source/event_timer_posix.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "webrtc/base/checks.h" - -namespace webrtc { - -// static -EventTimerWrapper* EventTimerWrapper::Create() { - return new EventTimerPosix(); -} - -const long int E6 = 1000000; -const long int E9 = 1000 * E6; - -EventTimerPosix::EventTimerPosix() - : event_set_(false), - timer_thread_(nullptr), - created_at_(), - periodic_(false), - time_(0), - count_(0) { - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&mutex_, &attr); -#ifdef WEBRTC_CLOCK_TYPE_REALTIME - pthread_cond_init(&cond_, 0); -#else - pthread_condattr_t cond_attr; - pthread_condattr_init(&cond_attr); - pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); - pthread_cond_init(&cond_, &cond_attr); - pthread_condattr_destroy(&cond_attr); -#endif -} - -EventTimerPosix::~EventTimerPosix() { - StopTimer(); - pthread_cond_destroy(&cond_); - pthread_mutex_destroy(&mutex_); -} - -// TODO(pbos): Make this void. -bool EventTimerPosix::Set() { - RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_)); - event_set_ = true; - pthread_cond_signal(&cond_); - pthread_mutex_unlock(&mutex_); - return true; -} - -EventTypeWrapper EventTimerPosix::Wait(unsigned long timeout) { - int ret_val = 0; - RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_)); - - if (!event_set_) { - if (WEBRTC_EVENT_INFINITE != timeout) { - timespec end_at; -#ifndef WEBRTC_MAC -#ifdef WEBRTC_CLOCK_TYPE_REALTIME - clock_gettime(CLOCK_REALTIME, &end_at); -#else - clock_gettime(CLOCK_MONOTONIC, &end_at); -#endif -#else - timeval value; - struct timezone time_zone; - time_zone.tz_minuteswest = 0; - time_zone.tz_dsttime = 0; - gettimeofday(&value, &time_zone); - TIMEVAL_TO_TIMESPEC(&value, &end_at); -#endif - end_at.tv_sec += timeout / 1000; - end_at.tv_nsec += (timeout - (timeout / 1000) * 1000) * E6; - - if (end_at.tv_nsec >= E9) { - end_at.tv_sec++; - end_at.tv_nsec -= E9; - } - while (ret_val == 0 && !event_set_) - ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at); - } else { - while (ret_val == 0 && !event_set_) - ret_val = pthread_cond_wait(&cond_, &mutex_); - } - } - - RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT); - - // Reset and signal if set, regardless of why the thread woke up. - if (event_set_) { - ret_val = 0; - event_set_ = false; - } - pthread_mutex_unlock(&mutex_); - - return ret_val == 0 ? kEventSignaled : kEventTimeout; -} - -EventTypeWrapper EventTimerPosix::Wait(timespec* end_at) { - int ret_val = 0; - RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_)); - - while (ret_val == 0 && !event_set_) - ret_val = pthread_cond_timedwait(&cond_, &mutex_, end_at); - - RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT); - - // Reset and signal if set, regardless of why the thread woke up. - if (event_set_) { - ret_val = 0; - event_set_ = false; - } - pthread_mutex_unlock(&mutex_); - - return ret_val == 0 ? kEventSignaled : kEventTimeout; -} - -bool EventTimerPosix::StartTimer(bool periodic, unsigned long time) { - pthread_mutex_lock(&mutex_); - if (timer_thread_) { - if (periodic_) { - // Timer already started. - pthread_mutex_unlock(&mutex_); - return false; - } else { - // New one shot timer - time_ = time; - created_at_.tv_sec = 0; - timer_event_->Set(); - pthread_mutex_unlock(&mutex_); - return true; - } - } - - // Start the timer thread - timer_event_.reset(new EventTimerPosix()); - const char* thread_name = "WebRtc_event_timer_thread"; - timer_thread_ = ThreadWrapper::CreateThread(Run, this, thread_name); - periodic_ = periodic; - time_ = time; - bool started = timer_thread_->Start(); - timer_thread_->SetPriority(kRealtimePriority); - pthread_mutex_unlock(&mutex_); - - return started; -} - -bool EventTimerPosix::Run(void* obj) { - return static_cast(obj)->Process(); -} - -bool EventTimerPosix::Process() { - pthread_mutex_lock(&mutex_); - if (created_at_.tv_sec == 0) { -#ifndef WEBRTC_MAC -#ifdef WEBRTC_CLOCK_TYPE_REALTIME - clock_gettime(CLOCK_REALTIME, &created_at_); -#else - clock_gettime(CLOCK_MONOTONIC, &created_at_); -#endif -#else - timeval value; - struct timezone time_zone; - time_zone.tz_minuteswest = 0; - time_zone.tz_dsttime = 0; - gettimeofday(&value, &time_zone); - TIMEVAL_TO_TIMESPEC(&value, &created_at_); -#endif - count_ = 0; - } - - timespec end_at; - unsigned long long time = time_ * ++count_; - end_at.tv_sec = created_at_.tv_sec + time / 1000; - end_at.tv_nsec = created_at_.tv_nsec + (time - (time / 1000) * 1000) * E6; - - if (end_at.tv_nsec >= E9) { - end_at.tv_sec++; - end_at.tv_nsec -= E9; - } - - pthread_mutex_unlock(&mutex_); - if (timer_event_->Wait(&end_at) == kEventSignaled) - return true; - - pthread_mutex_lock(&mutex_); - if (periodic_ || count_ == 1) - Set(); - pthread_mutex_unlock(&mutex_); - - return true; -} - -bool EventTimerPosix::StopTimer() { - if (timer_event_) { - timer_event_->Set(); - } - if (timer_thread_) { - if (!timer_thread_->Stop()) { - return false; - } - timer_thread_.reset(); - } - timer_event_.reset(); - - // Set time to zero to force new reference time for the timer. - memset(&created_at_, 0, sizeof(created_at_)); - count_ = 0; - return true; -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/event_timer_posix.h b/webrtc/system_wrappers/source/event_timer_posix.h deleted file mode 100644 index 21c4ac7..0000000 --- a/webrtc/system_wrappers/source/event_timer_posix.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_EVENT_POSIX_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_EVENT_POSIX_H_ - -#include "webrtc/system_wrappers/include/event_wrapper.h" - -#include -#include - -#include "webrtc/system_wrappers/include/thread_wrapper.h" - -namespace webrtc { - -enum State { - kUp = 1, - kDown = 2 -}; - -class EventTimerPosix : public EventTimerWrapper { - public: - EventTimerPosix(); - ~EventTimerPosix() override; - - EventTypeWrapper Wait(unsigned long max_time) override; - bool Set() override; - - bool StartTimer(bool periodic, unsigned long time) override; - bool StopTimer() override; - - private: - static bool Run(void* obj); - bool Process(); - EventTypeWrapper Wait(timespec* end_at); - - private: - pthread_cond_t cond_; - pthread_mutex_t mutex_; - bool event_set_; - - rtc::scoped_ptr timer_thread_; - rtc::scoped_ptr timer_event_; - timespec created_at_; - - bool periodic_; - unsigned long time_; // In ms - unsigned long count_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_EVENT_POSIX_H_ diff --git a/webrtc/system_wrappers/source/event_timer_win.cc b/webrtc/system_wrappers/source/event_timer_win.cc deleted file mode 100644 index 4c58698..0000000 --- a/webrtc/system_wrappers/source/event_timer_win.cc +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/system_wrappers/source/event_timer_win.h" - -#include "Mmsystem.h" - -namespace webrtc { - -// static -EventTimerWrapper* EventTimerWrapper::Create() { - return new EventTimerWin(); -} - -EventTimerWin::EventTimerWin() - : event_(::CreateEvent(NULL, // security attributes - FALSE, // manual reset - FALSE, // initial state - NULL)), // name of event - timerID_(NULL) { -} - -EventTimerWin::~EventTimerWin() { - StopTimer(); - CloseHandle(event_); -} - -bool EventTimerWin::Set() { - // Note: setting an event that is already set has no effect. - return SetEvent(event_) == 1; -} - -EventTypeWrapper EventTimerWin::Wait(unsigned long max_time) { - unsigned long res = WaitForSingleObject(event_, max_time); - switch (res) { - case WAIT_OBJECT_0: - return kEventSignaled; - case WAIT_TIMEOUT: - return kEventTimeout; - default: - return kEventError; - } -} - -bool EventTimerWin::StartTimer(bool periodic, unsigned long time) { - if (timerID_ != NULL) { - timeKillEvent(timerID_); - timerID_ = NULL; - } - - if (periodic) { - timerID_ = timeSetEvent(time, 0, (LPTIMECALLBACK)HANDLE(event_), 0, - TIME_PERIODIC | TIME_CALLBACK_EVENT_PULSE); - } else { - timerID_ = timeSetEvent(time, 0, (LPTIMECALLBACK)HANDLE(event_), 0, - TIME_ONESHOT | TIME_CALLBACK_EVENT_SET); - } - - return timerID_ != NULL; -} - -bool EventTimerWin::StopTimer() { - if (timerID_ != NULL) { - timeKillEvent(timerID_); - timerID_ = NULL; - } - - return true; -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/event_timer_win.h b/webrtc/system_wrappers/source/event_timer_win.h deleted file mode 100644 index 163cdde..0000000 --- a/webrtc/system_wrappers/source/event_timer_win.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_EVENT_WIN_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_EVENT_WIN_H_ - -#include - -#include "webrtc/system_wrappers/include/event_wrapper.h" - -#include "webrtc/typedefs.h" - -namespace webrtc { - -class EventTimerWin : public EventTimerWrapper { - public: - EventTimerWin(); - virtual ~EventTimerWin(); - - virtual EventTypeWrapper Wait(unsigned long max_time); - virtual bool Set(); - - virtual bool StartTimer(bool periodic, unsigned long time); - virtual bool StopTimer(); - - private: - HANDLE event_; - uint32_t timerID_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_EVENT_WIN_H_ diff --git a/webrtc/system_wrappers/source/field_trial.cc b/webrtc/system_wrappers/source/field_trial.cc new file mode 100644 index 0000000..f1dccc9 --- /dev/null +++ b/webrtc/system_wrappers/source/field_trial.cc @@ -0,0 +1,155 @@ +// Copyright (c) 2014 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 "system_wrappers/include/field_trial.h" + +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/string_encode.h" + +// Simple field trial implementation, which allows client to +// specify desired flags in InitFieldTrialsFromString. +namespace webrtc { +namespace field_trial { + +static const char* trials_init_string = NULL; + +#ifndef WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT +namespace { +constexpr char kPersistentStringSeparator = '/'; +// Validates the given field trial string. +// E.g.: +// "WebRTC-experimentFoo/Enabled/WebRTC-experimentBar/Enabled100kbps/" +// Assigns the process to group "Enabled" on WebRTCExperimentFoo trial +// and to group "Enabled100kbps" on WebRTCExperimentBar. +// +// E.g. invalid config: +// "WebRTC-experiment1/Enabled" (note missing / separator at the end). +bool FieldTrialsStringIsValidInternal(const absl::string_view trials) { + if (trials.empty()) + return true; + + size_t next_item = 0; + std::map field_trials; + while (next_item < trials.length()) { + size_t name_end = trials.find(kPersistentStringSeparator, next_item); + if (name_end == trials.npos || next_item == name_end) + return false; + size_t group_name_end = + trials.find(kPersistentStringSeparator, name_end + 1); + if (group_name_end == trials.npos || name_end + 1 == group_name_end) + return false; + absl::string_view name = trials.substr(next_item, name_end - next_item); + absl::string_view group_name = + trials.substr(name_end + 1, group_name_end - name_end - 1); + + next_item = group_name_end + 1; + + // Fail if duplicate with different group name. + if (field_trials.find(name) != field_trials.end() && + field_trials.find(name)->second != group_name) { + return false; + } + + field_trials[name] = group_name; + } + + return true; +} +} // namespace + +bool FieldTrialsStringIsValid(const char* trials_string) { + return FieldTrialsStringIsValidInternal(trials_string); +} + +void InsertOrReplaceFieldTrialStringsInMap( + std::map* fieldtrial_map, + const absl::string_view trials_string) { + if (FieldTrialsStringIsValidInternal(trials_string)) { + std::vector tokens; + rtc::split(std::string(trials_string), '/', &tokens); + // Skip last token which is empty due to trailing '/'. + for (size_t idx = 0; idx < tokens.size() - 1; idx += 2) { + (*fieldtrial_map)[tokens[idx]] = tokens[idx + 1]; + } + } else { + RTC_DCHECK(false) << "Invalid field trials string:" << trials_string; + } +} + +std::string MergeFieldTrialsStrings(const char* first, const char* second) { + std::map fieldtrial_map; + InsertOrReplaceFieldTrialStringsInMap(&fieldtrial_map, first); + InsertOrReplaceFieldTrialStringsInMap(&fieldtrial_map, second); + + // Merge into fieldtrial string. + std::string merged = ""; + for (auto const& fieldtrial : fieldtrial_map) { + merged += fieldtrial.first + '/' + fieldtrial.second + '/'; + } + return merged; +} + +std::string FindFullName(const std::string& name) { + if (trials_init_string == NULL) + return std::string(); + + std::string trials_string(trials_init_string); + if (trials_string.empty()) + return std::string(); + + size_t next_item = 0; + while (next_item < trials_string.length()) { + // Find next name/value pair in field trial configuration string. + size_t field_name_end = + trials_string.find(kPersistentStringSeparator, next_item); + if (field_name_end == trials_string.npos || field_name_end == next_item) + break; + size_t field_value_end = + trials_string.find(kPersistentStringSeparator, field_name_end + 1); + if (field_value_end == trials_string.npos || + field_value_end == field_name_end + 1) + break; + std::string field_name(trials_string, next_item, + field_name_end - next_item); + std::string field_value(trials_string, field_name_end + 1, + field_value_end - field_name_end - 1); + next_item = field_value_end + 1; + + if (name == field_name) + return field_value; + } + return std::string(); +} +#endif // WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT + +// Optionally initialize field trial from a string. +void InitFieldTrialsFromString(const char* trials_string) { + RTC_LOG(LS_INFO) << "Setting field trial string:" << trials_string; +#ifndef WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT + if (trials_string) { + RTC_DCHECK(FieldTrialsStringIsValidInternal(trials_string)) + << "Invalid field trials string:" << trials_string; + }; +#endif // WEBRTC_EXCLUDE_FIELD_TRIAL_DEFAULT + trials_init_string = trials_string; +} + +const char* GetFieldTrialString() { + return trials_init_string; +} + +} // namespace field_trial +} // namespace webrtc diff --git a/webrtc/system_wrappers/source/file_impl.cc b/webrtc/system_wrappers/source/file_impl.cc deleted file mode 100644 index 0ee0dea..0000000 --- a/webrtc/system_wrappers/source/file_impl.cc +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/system_wrappers/source/file_impl.h" - -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#endif - -#include "webrtc/base/checks.h" -#include "webrtc/system_wrappers/include/rw_lock_wrapper.h" - -namespace webrtc { - -FileWrapper* FileWrapper::Create() { - return new FileWrapperImpl(); -} - -FileWrapperImpl::FileWrapperImpl() - : rw_lock_(RWLockWrapper::CreateRWLock()), - id_(NULL), - managed_file_handle_(true), - open_(false), - looping_(false), - read_only_(false), - max_size_in_bytes_(0), - size_in_bytes_(0) { - memset(file_name_utf8_, 0, kMaxFileNameSize); -} - -FileWrapperImpl::~FileWrapperImpl() { - if (id_ != NULL && managed_file_handle_) { - fclose(id_); - } -} - -int FileWrapperImpl::CloseFile() { - WriteLockScoped write(*rw_lock_); - return CloseFileImpl(); -} - -int FileWrapperImpl::Rewind() { - WriteLockScoped write(*rw_lock_); - if (looping_ || !read_only_) { - if (id_ != NULL) { - size_in_bytes_ = 0; - return fseek(id_, 0, SEEK_SET); - } - } - return -1; -} - -int FileWrapperImpl::SetMaxFileSize(size_t bytes) { - WriteLockScoped write(*rw_lock_); - max_size_in_bytes_ = bytes; - return 0; -} - -int FileWrapperImpl::Flush() { - WriteLockScoped write(*rw_lock_); - return FlushImpl(); -} - -int FileWrapperImpl::FileName(char* file_name_utf8, size_t size) const { - ReadLockScoped read(*rw_lock_); - size_t length = strlen(file_name_utf8_); - if (length > kMaxFileNameSize) { - assert(false); - return -1; - } - if (length < 1) { - return -1; - } - - // Make sure to NULL terminate - if (size < length) { - length = size - 1; - } - memcpy(file_name_utf8, file_name_utf8_, length); - file_name_utf8[length] = 0; - return 0; -} - -bool FileWrapperImpl::Open() const { - ReadLockScoped read(*rw_lock_); - return open_; -} - -int FileWrapperImpl::OpenFile(const char* file_name_utf8, bool read_only, - bool loop, bool text) { - WriteLockScoped write(*rw_lock_); - if (id_ != NULL && !managed_file_handle_) - return -1; - size_t length = strlen(file_name_utf8); - if (length > kMaxFileNameSize - 1) { - return -1; - } - - read_only_ = read_only; - - FILE* tmp_id = NULL; -#if defined _WIN32 - wchar_t wide_file_name[kMaxFileNameSize]; - wide_file_name[0] = 0; - - MultiByteToWideChar(CP_UTF8, - 0, // UTF8 flag - file_name_utf8, - -1, // Null terminated string - wide_file_name, - kMaxFileNameSize); - if (text) { - if (read_only) { - tmp_id = _wfopen(wide_file_name, L"rt"); - } else { - tmp_id = _wfopen(wide_file_name, L"wt"); - } - } else { - if (read_only) { - tmp_id = _wfopen(wide_file_name, L"rb"); - } else { - tmp_id = _wfopen(wide_file_name, L"wb"); - } - } -#else - if (text) { - if (read_only) { - tmp_id = fopen(file_name_utf8, "rt"); - } else { - tmp_id = fopen(file_name_utf8, "wt"); - } - } else { - if (read_only) { - tmp_id = fopen(file_name_utf8, "rb"); - } else { - tmp_id = fopen(file_name_utf8, "wb"); - } - } -#endif - - if (tmp_id != NULL) { - // +1 comes from copying the NULL termination character. - memcpy(file_name_utf8_, file_name_utf8, length + 1); - if (id_ != NULL) { - fclose(id_); - } - id_ = tmp_id; - managed_file_handle_ = true; - looping_ = loop; - open_ = true; - return 0; - } - return -1; -} - -int FileWrapperImpl::OpenFromFileHandle(FILE* handle, - bool manage_file, - bool read_only, - bool loop) { - WriteLockScoped write(*rw_lock_); - if (!handle) - return -1; - - if (id_ != NULL) { - if (managed_file_handle_) - fclose(id_); - else - return -1; - } - - id_ = handle; - managed_file_handle_ = manage_file; - read_only_ = read_only; - looping_ = loop; - open_ = true; - return 0; -} - -int FileWrapperImpl::Read(void* buf, size_t length) { - WriteLockScoped write(*rw_lock_); - if (id_ == NULL) - return -1; - - size_t bytes_read = fread(buf, 1, length, id_); - if (bytes_read != length && !looping_) { - CloseFileImpl(); - } - return static_cast(bytes_read); -} - -int FileWrapperImpl::WriteText(const char* format, ...) { - WriteLockScoped write(*rw_lock_); - if (format == NULL) - return -1; - - if (read_only_) - return -1; - - if (id_ == NULL) - return -1; - - va_list args; - va_start(args, format); - int num_chars = vfprintf(id_, format, args); - va_end(args); - - if (num_chars >= 0) { - return num_chars; - } else { - CloseFileImpl(); - return -1; - } -} - -bool FileWrapperImpl::Write(const void* buf, size_t length) { - WriteLockScoped write(*rw_lock_); - if (buf == NULL) - return false; - - if (read_only_) - return false; - - if (id_ == NULL) - return false; - - // Check if it's time to stop writing. - if (max_size_in_bytes_ > 0 && - (size_in_bytes_ + length) > max_size_in_bytes_) { - FlushImpl(); - return false; - } - - size_t num_bytes = fwrite(buf, 1, length, id_); - if (num_bytes > 0) { - size_in_bytes_ += num_bytes; - return true; - } - - CloseFileImpl(); - return false; -} - -int FileWrapperImpl::CloseFileImpl() { - if (id_ != NULL) { - if (managed_file_handle_) - fclose(id_); - id_ = NULL; - } - memset(file_name_utf8_, 0, kMaxFileNameSize); - open_ = false; - return 0; -} - -int FileWrapperImpl::FlushImpl() { - if (id_ != NULL) { - return fflush(id_); - } - return -1; -} - -int FileWrapper::Rewind() { - RTC_DCHECK(false); - return -1; -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/file_impl.h b/webrtc/system_wrappers/source/file_impl.h deleted file mode 100644 index 06ba582..0000000 --- a/webrtc/system_wrappers/source/file_impl.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_FILE_IMPL_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_FILE_IMPL_H_ - -#include - -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/system_wrappers/include/file_wrapper.h" - -namespace webrtc { - -class RWLockWrapper; - -class FileWrapperImpl : public FileWrapper { - public: - FileWrapperImpl(); - ~FileWrapperImpl() override; - - int FileName(char* file_name_utf8, size_t size) const override; - - bool Open() const override; - - int OpenFile(const char* file_name_utf8, - bool read_only, - bool loop = false, - bool text = false) override; - - int OpenFromFileHandle(FILE* handle, - bool manage_file, - bool read_only, - bool loop = false) override; - - int CloseFile() override; - int SetMaxFileSize(size_t bytes) override; - int Flush() override; - - int Read(void* buf, size_t length) override; - bool Write(const void* buf, size_t length) override; - int WriteText(const char* format, ...) override; - int Rewind() override; - - private: - int CloseFileImpl(); - int FlushImpl(); - - rtc::scoped_ptr rw_lock_; - - FILE* id_; - bool managed_file_handle_; - bool open_; - bool looping_; - bool read_only_; - size_t max_size_in_bytes_; // -1 indicates file size limitation is off - size_t size_in_bytes_; - char file_name_utf8_[kMaxFileNameSize]; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_FILE_IMPL_H_ diff --git a/webrtc/system_wrappers/source/logging.cc b/webrtc/system_wrappers/source/logging.cc deleted file mode 100644 index 6b50d6a..0000000 --- a/webrtc/system_wrappers/source/logging.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/system_wrappers/include/logging.h" - -#include - -#include - -#include "webrtc/common_types.h" -#include "webrtc/system_wrappers/include/trace.h" - -namespace webrtc { -namespace { - -TraceLevel WebRtcSeverity(LoggingSeverity sev) { - switch (sev) { - // TODO(ajm): SENSITIVE doesn't have a corresponding webrtc level. - case LS_SENSITIVE: return kTraceInfo; - case LS_VERBOSE: return kTraceInfo; - case LS_INFO: return kTraceTerseInfo; - case LS_WARNING: return kTraceWarning; - case LS_ERROR: return kTraceError; - default: return kTraceNone; - } -} - -// Return the filename portion of the string (that following the last slash). -const char* FilenameFromPath(const char* file) { - const char* end1 = ::strrchr(file, '/'); - const char* end2 = ::strrchr(file, '\\'); - if (!end1 && !end2) - return file; - else - return (end1 > end2) ? end1 + 1 : end2 + 1; -} - -} // namespace - -LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev) - : severity_(sev) { - print_stream_ << "(" << FilenameFromPath(file) << ":" << line << "): "; -} - -bool LogMessage::Loggable(LoggingSeverity sev) { - // |level_filter| is a bitmask, unlike libjingle's minimum severity value. - return WebRtcSeverity(sev) & Trace::level_filter() ? true : false; -} - -LogMessage::~LogMessage() { - const std::string& str = print_stream_.str(); - Trace::Add(WebRtcSeverity(severity_), kTraceUndefined, 0, "%s", str.c_str()); -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/metrics.cc b/webrtc/system_wrappers/source/metrics.cc new file mode 100644 index 0000000..d428336 --- /dev/null +++ b/webrtc/system_wrappers/source/metrics.cc @@ -0,0 +1,328 @@ +// Copyright (c) 2014 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 "system_wrappers/include/metrics.h" + +#include + +#include "rtc_base/constructor_magic.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" + +// Default implementation of histogram methods for WebRTC clients that do not +// want to provide their own implementation. + +namespace webrtc { +namespace metrics { +class Histogram; + +namespace { +// Limit for the maximum number of sample values that can be stored. +// TODO(asapersson): Consider using bucket count (and set up +// linearly/exponentially spaced buckets) if samples are logged more frequently. +const int kMaxSampleMapSize = 300; + +class RtcHistogram { + public: + RtcHistogram(const std::string& name, int min, int max, int bucket_count) + : min_(min), max_(max), info_(name, min, max, bucket_count) { + RTC_DCHECK_GT(bucket_count, 0); + } + + void Add(int sample) { + sample = std::min(sample, max_); + sample = std::max(sample, min_ - 1); // Underflow bucket. + + MutexLock lock(&mutex_); + if (info_.samples.size() == kMaxSampleMapSize && + info_.samples.find(sample) == info_.samples.end()) { + return; + } + ++info_.samples[sample]; + } + + // Returns a copy (or nullptr if there are no samples) and clears samples. + std::unique_ptr GetAndReset() { + MutexLock lock(&mutex_); + if (info_.samples.empty()) + return nullptr; + + SampleInfo* copy = + new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count); + + std::swap(info_.samples, copy->samples); + + return std::unique_ptr(copy); + } + + const std::string& name() const { return info_.name; } + + // Functions only for testing. + void Reset() { + MutexLock lock(&mutex_); + info_.samples.clear(); + } + + int NumEvents(int sample) const { + MutexLock lock(&mutex_); + const auto it = info_.samples.find(sample); + return (it == info_.samples.end()) ? 0 : it->second; + } + + int NumSamples() const { + int num_samples = 0; + MutexLock lock(&mutex_); + for (const auto& sample : info_.samples) { + num_samples += sample.second; + } + return num_samples; + } + + int MinSample() const { + MutexLock lock(&mutex_); + return (info_.samples.empty()) ? -1 : info_.samples.begin()->first; + } + + std::map Samples() const { + MutexLock lock(&mutex_); + return info_.samples; + } + + private: + mutable Mutex mutex_; + const int min_; + const int max_; + SampleInfo info_ RTC_GUARDED_BY(mutex_); + + RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogram); +}; + +class RtcHistogramMap { + public: + RtcHistogramMap() {} + ~RtcHistogramMap() {} + + Histogram* GetCountsHistogram(const std::string& name, + int min, + int max, + int bucket_count) { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + if (it != map_.end()) + return reinterpret_cast(it->second.get()); + + RtcHistogram* hist = new RtcHistogram(name, min, max, bucket_count); + map_[name].reset(hist); + return reinterpret_cast(hist); + } + + Histogram* GetEnumerationHistogram(const std::string& name, int boundary) { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + if (it != map_.end()) + return reinterpret_cast(it->second.get()); + + RtcHistogram* hist = new RtcHistogram(name, 1, boundary, boundary + 1); + map_[name].reset(hist); + return reinterpret_cast(hist); + } + + void GetAndReset( + std::map>* histograms) { + MutexLock lock(&mutex_); + for (const auto& kv : map_) { + std::unique_ptr info = kv.second->GetAndReset(); + if (info) + histograms->insert(std::make_pair(kv.first, std::move(info))); + } + } + + // Functions only for testing. + void Reset() { + MutexLock lock(&mutex_); + for (const auto& kv : map_) + kv.second->Reset(); + } + + int NumEvents(const std::string& name, int sample) const { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + return (it == map_.end()) ? 0 : it->second->NumEvents(sample); + } + + int NumSamples(const std::string& name) const { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + return (it == map_.end()) ? 0 : it->second->NumSamples(); + } + + int MinSample(const std::string& name) const { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + return (it == map_.end()) ? -1 : it->second->MinSample(); + } + + std::map Samples(const std::string& name) const { + MutexLock lock(&mutex_); + const auto& it = map_.find(name); + return (it == map_.end()) ? std::map() : it->second->Samples(); + } + + private: + mutable Mutex mutex_; + std::map> map_ + RTC_GUARDED_BY(mutex_); + + RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogramMap); +}; + +// RtcHistogramMap is allocated upon call to Enable(). +// The histogram getter functions, which return pointer values to the histograms +// in the map, are cached in WebRTC. Therefore, this memory is not freed by the +// application (the memory will be reclaimed by the OS). +static RtcHistogramMap* volatile g_rtc_histogram_map = nullptr; + +void CreateMap() { + RtcHistogramMap* map = rtc::AtomicOps::AcquireLoadPtr(&g_rtc_histogram_map); + if (map == nullptr) { + RtcHistogramMap* new_map = new RtcHistogramMap(); + RtcHistogramMap* old_map = rtc::AtomicOps::CompareAndSwapPtr( + &g_rtc_histogram_map, static_cast(nullptr), new_map); + if (old_map != nullptr) + delete new_map; + } +} + +// Set the first time we start using histograms. Used to make sure Enable() is +// not called thereafter. +#if RTC_DCHECK_IS_ON +static volatile int g_rtc_histogram_called = 0; +#endif + +// Gets the map (or nullptr). +RtcHistogramMap* GetMap() { +#if RTC_DCHECK_IS_ON + rtc::AtomicOps::ReleaseStore(&g_rtc_histogram_called, 1); +#endif + return g_rtc_histogram_map; +} +} // namespace + +#ifndef WEBRTC_EXCLUDE_METRICS_DEFAULT +// Implementation of histogram methods in +// webrtc/system_wrappers/interface/metrics.h. + +// Histogram with exponentially spaced buckets. +// Creates (or finds) histogram. +// The returned histogram pointer is cached (and used for adding samples in +// subsequent calls). +Histogram* HistogramFactoryGetCounts(const std::string& name, + int min, + int max, + int bucket_count) { + // TODO(asapersson): Alternative implementation will be needed if this + // histogram type should be truly exponential. + return HistogramFactoryGetCountsLinear(name, min, max, bucket_count); +} + +// Histogram with linearly spaced buckets. +// Creates (or finds) histogram. +// The returned histogram pointer is cached (and used for adding samples in +// subsequent calls). +Histogram* HistogramFactoryGetCountsLinear(const std::string& name, + int min, + int max, + int bucket_count) { + RtcHistogramMap* map = GetMap(); + if (!map) + return nullptr; + + return map->GetCountsHistogram(name, min, max, bucket_count); +} + +// Histogram with linearly spaced buckets. +// Creates (or finds) histogram. +// The returned histogram pointer is cached (and used for adding samples in +// subsequent calls). +Histogram* HistogramFactoryGetEnumeration(const std::string& name, + int boundary) { + RtcHistogramMap* map = GetMap(); + if (!map) + return nullptr; + + return map->GetEnumerationHistogram(name, boundary); +} + +// Our default implementation reuses the non-sparse histogram. +Histogram* SparseHistogramFactoryGetEnumeration(const std::string& name, + int boundary) { + return HistogramFactoryGetEnumeration(name, boundary); +} + +// Fast path. Adds |sample| to cached |histogram_pointer|. +void HistogramAdd(Histogram* histogram_pointer, int sample) { + RtcHistogram* ptr = reinterpret_cast(histogram_pointer); + ptr->Add(sample); +} + +#endif // WEBRTC_EXCLUDE_METRICS_DEFAULT + +SampleInfo::SampleInfo(const std::string& name, + int min, + int max, + size_t bucket_count) + : name(name), min(min), max(max), bucket_count(bucket_count) {} + +SampleInfo::~SampleInfo() {} + +// Implementation of global functions in metrics.h. +void Enable() { + RTC_DCHECK(g_rtc_histogram_map == nullptr); +#if RTC_DCHECK_IS_ON + RTC_DCHECK_EQ(0, rtc::AtomicOps::AcquireLoad(&g_rtc_histogram_called)); +#endif + CreateMap(); +} + +void GetAndReset( + std::map>* histograms) { + histograms->clear(); + RtcHistogramMap* map = GetMap(); + if (map) + map->GetAndReset(histograms); +} + +void Reset() { + RtcHistogramMap* map = GetMap(); + if (map) + map->Reset(); +} + +int NumEvents(const std::string& name, int sample) { + RtcHistogramMap* map = GetMap(); + return map ? map->NumEvents(name, sample) : 0; +} + +int NumSamples(const std::string& name) { + RtcHistogramMap* map = GetMap(); + return map ? map->NumSamples(name) : 0; +} + +int MinSample(const std::string& name) { + RtcHistogramMap* map = GetMap(); + return map ? map->MinSample(name) : -1; +} + +std::map Samples(const std::string& name) { + RtcHistogramMap* map = GetMap(); + return map ? map->Samples(name) : std::map(); +} + +} // namespace metrics +} // namespace webrtc diff --git a/webrtc/system_wrappers/source/metrics_default.cc b/webrtc/system_wrappers/source/metrics_default.cc deleted file mode 100644 index 48c9111..0000000 --- a/webrtc/system_wrappers/source/metrics_default.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2014 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 "webrtc/system_wrappers/include/metrics.h" - -// Default implementation of histogram methods for WebRTC clients that do not -// want to provide their own implementation. - -namespace webrtc { -namespace metrics { - -Histogram* HistogramFactoryGetCounts(const std::string& name, int min, int max, - int bucket_count) { return NULL; } - -Histogram* HistogramFactoryGetEnumeration(const std::string& name, - int boundary) { return NULL; } - -void HistogramAdd( - Histogram* histogram_pointer, const std::string& name, int sample) {} - -} // namespace metrics -} // namespace webrtc - diff --git a/webrtc/system_wrappers/source/rw_lock_generic.cc b/webrtc/system_wrappers/source/rw_lock_generic.cc deleted file mode 100644 index 9786155..0000000 --- a/webrtc/system_wrappers/source/rw_lock_generic.cc +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 "webrtc/system_wrappers/source/rw_lock_generic.h" - -#include "webrtc/system_wrappers/include/condition_variable_wrapper.h" -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" - -namespace webrtc { - -RWLockGeneric::RWLockGeneric() - : readers_active_(0), - writer_active_(false), - readers_waiting_(0), - writers_waiting_(0) { - critical_section_ = CriticalSectionWrapper::CreateCriticalSection(); - read_condition_ = ConditionVariableWrapper::CreateConditionVariable(); - write_condition_ = ConditionVariableWrapper::CreateConditionVariable(); -} - -RWLockGeneric::~RWLockGeneric() { - delete write_condition_; - delete read_condition_; - delete critical_section_; -} - -void RWLockGeneric::AcquireLockExclusive() { - CriticalSectionScoped cs(critical_section_); - if (writer_active_ || readers_active_ > 0) { - ++writers_waiting_; - while (writer_active_ || readers_active_ > 0) { - write_condition_->SleepCS(*critical_section_); - } - --writers_waiting_; - } - writer_active_ = true; -} - -void RWLockGeneric::ReleaseLockExclusive() { - CriticalSectionScoped cs(critical_section_); - writer_active_ = false; - if (writers_waiting_ > 0) { - write_condition_->Wake(); - } else if (readers_waiting_ > 0) { - read_condition_->WakeAll(); - } -} - -void RWLockGeneric::AcquireLockShared() { - CriticalSectionScoped cs(critical_section_); - if (writer_active_ || writers_waiting_ > 0) { - ++readers_waiting_; - - while (writer_active_ || writers_waiting_ > 0) { - read_condition_->SleepCS(*critical_section_); - } - --readers_waiting_; - } - ++readers_active_; -} - -void RWLockGeneric::ReleaseLockShared() { - CriticalSectionScoped cs(critical_section_); - --readers_active_; - if (readers_active_ == 0 && writers_waiting_ > 0) { - write_condition_->Wake(); - } -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/rw_lock_generic.h b/webrtc/system_wrappers/source/rw_lock_generic.h deleted file mode 100644 index f0d4456..0000000 --- a/webrtc/system_wrappers/source/rw_lock_generic.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_RW_LOCK_GENERIC_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_RW_LOCK_GENERIC_H_ - -#include "webrtc/system_wrappers/include/rw_lock_wrapper.h" -#include "webrtc/typedefs.h" - -namespace webrtc { - -class CriticalSectionWrapper; -class ConditionVariableWrapper; - -class RWLockGeneric : public RWLockWrapper { - public: - RWLockGeneric(); - ~RWLockGeneric() override; - - void AcquireLockExclusive() override; - void ReleaseLockExclusive() override; - - void AcquireLockShared() override; - void ReleaseLockShared() override; - - private: - CriticalSectionWrapper* critical_section_; - ConditionVariableWrapper* read_condition_; - ConditionVariableWrapper* write_condition_; - - int readers_active_; - bool writer_active_; - int readers_waiting_; - int writers_waiting_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_RW_LOCK_GENERIC_H_ diff --git a/webrtc/system_wrappers/source/rw_lock_win.cc b/webrtc/system_wrappers/source/rw_lock_win.cc deleted file mode 100644 index 2372b9b..0000000 --- a/webrtc/system_wrappers/source/rw_lock_win.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/system_wrappers/source/rw_lock_win.h" - -#include "webrtc/system_wrappers/include/trace.h" - -namespace webrtc { - -static bool native_rw_locks_supported = false; -static bool module_load_attempted = false; -static HMODULE library = NULL; - -typedef void (WINAPI* InitializeSRWLock)(PSRWLOCK); - -typedef void (WINAPI* AcquireSRWLockExclusive)(PSRWLOCK); -typedef void (WINAPI* ReleaseSRWLockExclusive)(PSRWLOCK); - -typedef void (WINAPI* AcquireSRWLockShared)(PSRWLOCK); -typedef void (WINAPI* ReleaseSRWLockShared)(PSRWLOCK); - -InitializeSRWLock initialize_srw_lock; -AcquireSRWLockExclusive acquire_srw_lock_exclusive; -AcquireSRWLockShared acquire_srw_lock_shared; -ReleaseSRWLockShared release_srw_lock_shared; -ReleaseSRWLockExclusive release_srw_lock_exclusive; - -RWLockWin::RWLockWin() { - initialize_srw_lock(&lock_); -} - -RWLockWin* RWLockWin::Create() { - if (!LoadModule()) { - return NULL; - } - return new RWLockWin(); -} - -void RWLockWin::AcquireLockExclusive() { - acquire_srw_lock_exclusive(&lock_); -} - -void RWLockWin::ReleaseLockExclusive() { - release_srw_lock_exclusive(&lock_); -} - -void RWLockWin::AcquireLockShared() { - acquire_srw_lock_shared(&lock_); -} - -void RWLockWin::ReleaseLockShared() { - release_srw_lock_shared(&lock_); -} - -bool RWLockWin::LoadModule() { - if (module_load_attempted) { - return native_rw_locks_supported; - } - module_load_attempted = true; - // Use native implementation if supported (i.e Vista+) - library = LoadLibrary(TEXT("Kernel32.dll")); - if (!library) { - return false; - } - WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, "Loaded Kernel.dll"); - - initialize_srw_lock = - (InitializeSRWLock)GetProcAddress(library, "InitializeSRWLock"); - - acquire_srw_lock_exclusive = - (AcquireSRWLockExclusive)GetProcAddress(library, - "AcquireSRWLockExclusive"); - release_srw_lock_exclusive = - (ReleaseSRWLockExclusive)GetProcAddress(library, - "ReleaseSRWLockExclusive"); - acquire_srw_lock_shared = - (AcquireSRWLockShared)GetProcAddress(library, "AcquireSRWLockShared"); - release_srw_lock_shared = - (ReleaseSRWLockShared)GetProcAddress(library, "ReleaseSRWLockShared"); - - if (initialize_srw_lock && acquire_srw_lock_exclusive && - release_srw_lock_exclusive && acquire_srw_lock_shared && - release_srw_lock_shared) { - WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, "Loaded Native RW Lock"); - native_rw_locks_supported = true; - } - return native_rw_locks_supported; -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/sleep.cc b/webrtc/system_wrappers/source/sleep.cc index 181381f..e2fa486 100644 --- a/webrtc/system_wrappers/source/sleep.cc +++ b/webrtc/system_wrappers/source/sleep.cc @@ -9,7 +9,7 @@ */ // An OS-independent sleep function. -#include "webrtc/system_wrappers/include/sleep.h" +#include "system_wrappers/include/sleep.h" #ifdef _WIN32 // For Sleep() diff --git a/webrtc/system_wrappers/source/thread.cc b/webrtc/system_wrappers/source/thread.cc deleted file mode 100644 index 7da1e3d..0000000 --- a/webrtc/system_wrappers/source/thread.cc +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 "webrtc/system_wrappers/include/thread_wrapper.h" - -#if defined(_WIN32) -#include "webrtc/system_wrappers/source/thread_win.h" -#else -#include "webrtc/system_wrappers/source/thread_posix.h" -#endif - -namespace webrtc { - -#if defined(_WIN32) -typedef ThreadWindows ThreadType; -#else -typedef ThreadPosix ThreadType; -#endif - -rtc::scoped_ptr ThreadWrapper::CreateThread( - ThreadRunFunction func, void* obj, const char* thread_name) { - return rtc::scoped_ptr( - new ThreadType(func, obj, thread_name)).Pass(); -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/thread_posix.cc b/webrtc/system_wrappers/source/thread_posix.cc deleted file mode 100644 index 32ab13c..0000000 --- a/webrtc/system_wrappers/source/thread_posix.cc +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/system_wrappers/source/thread_posix.h" - -#include - -#include -#include -#ifdef WEBRTC_LINUX -#include -#include -#include -#endif - -#include "webrtc/base/checks.h" -#include "webrtc/base/platform_thread.h" -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" -#include "webrtc/system_wrappers/include/event_wrapper.h" -#include "webrtc/system_wrappers/include/sleep.h" -#include "webrtc/system_wrappers/include/trace.h" - -namespace webrtc { -namespace { -struct ThreadAttributes { - ThreadAttributes() { pthread_attr_init(&attr); } - ~ThreadAttributes() { pthread_attr_destroy(&attr); } - pthread_attr_t* operator&() { return &attr; } - pthread_attr_t attr; -}; -} // namespace - -int ConvertToSystemPriority(ThreadPriority priority, int min_prio, - int max_prio) { - RTC_DCHECK(max_prio - min_prio > 2); - const int top_prio = max_prio - 1; - const int low_prio = min_prio + 1; - - switch (priority) { - case kLowPriority: - return low_prio; - case kNormalPriority: - // The -1 ensures that the kHighPriority is always greater or equal to - // kNormalPriority. - return (low_prio + top_prio - 1) / 2; - case kHighPriority: - return std::max(top_prio - 2, low_prio); - case kHighestPriority: - return std::max(top_prio - 1, low_prio); - case kRealtimePriority: - return top_prio; - } - RTC_DCHECK(false); - return low_prio; -} - -// static -void* ThreadPosix::StartThread(void* param) { - static_cast(param)->Run(); - return 0; -} - -ThreadPosix::ThreadPosix(ThreadRunFunction func, void* obj, - const char* thread_name) - : run_function_(func), - obj_(obj), - stop_event_(false, false), - name_(thread_name ? thread_name : "webrtc"), - thread_(0) { - RTC_DCHECK(name_.length() < 64); -} - -uint32_t ThreadWrapper::GetThreadId() { - return rtc::CurrentThreadId(); -} - -ThreadPosix::~ThreadPosix() { - RTC_DCHECK(thread_checker_.CalledOnValidThread()); -} - -// TODO(pbos): Make Start void, calling code really doesn't support failures -// here. -bool ThreadPosix::Start() { - RTC_DCHECK(thread_checker_.CalledOnValidThread()); - RTC_DCHECK(!thread_) << "Thread already started?"; - - ThreadAttributes attr; - // Set the stack stack size to 1M. - pthread_attr_setstacksize(&attr, 1024 * 1024); - RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this)); - return true; -} - -bool ThreadPosix::Stop() { - RTC_DCHECK(thread_checker_.CalledOnValidThread()); - if (!thread_) - return true; - - stop_event_.Set(); - RTC_CHECK_EQ(0, pthread_join(thread_, nullptr)); - thread_ = 0; - - return true; -} - -bool ThreadPosix::SetPriority(ThreadPriority priority) { - RTC_DCHECK(thread_checker_.CalledOnValidThread()); - if (!thread_) - return false; -#if defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX) - // TODO(tommi): Switch to the same mechanism as Chromium uses for - // changing thread priorities. - return true; -#else -#ifdef WEBRTC_THREAD_RR - const int policy = SCHED_RR; -#else - const int policy = SCHED_FIFO; -#endif - const int min_prio = sched_get_priority_min(policy); - const int max_prio = sched_get_priority_max(policy); - if (min_prio == -1 || max_prio == -1) { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - "unable to retreive min or max priority for threads"); - return false; - } - - if (max_prio - min_prio <= 2) - return false; - - sched_param param; - param.sched_priority = ConvertToSystemPriority(priority, min_prio, max_prio); - if (pthread_setschedparam(thread_, policy, ¶m) != 0) { - WEBRTC_TRACE( - kTraceError, kTraceUtility, -1, "unable to set thread priority"); - return false; - } - - return true; -#endif // defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX) -} - -void ThreadPosix::Run() { - if (!name_.empty()) { - // Setting the thread name may fail (harmlessly) if running inside a - // sandbox. Ignore failures if they happen. - rtc::SetCurrentThreadName(name_.substr(0, 63).c_str()); - } - - // It's a requirement that for successful thread creation that the run - // function be called at least once (see RunFunctionIsCalled unit test), - // so to fullfill that requirement, we use a |do| loop and not |while|. - do { - if (!run_function_(obj_)) - break; - } while (!stop_event_.Wait(0)); -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/thread_posix.h b/webrtc/system_wrappers/source/thread_posix.h deleted file mode 100644 index bcdd732..0000000 --- a/webrtc/system_wrappers/source/thread_posix.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_THREAD_POSIX_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_THREAD_POSIX_H_ - -#include "webrtc/base/event.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/base/thread_checker.h" -#include "webrtc/system_wrappers/include/thread_wrapper.h" - -#include - -namespace webrtc { - -int ConvertToSystemPriority(ThreadPriority priority, int min_prio, - int max_prio); - -class ThreadPosix : public ThreadWrapper { - public: - ThreadPosix(ThreadRunFunction func, void* obj, const char* thread_name); - ~ThreadPosix() override; - - // From ThreadWrapper. - bool Start() override; - bool Stop() override; - - bool SetPriority(ThreadPriority priority) override; - - private: - static void* StartThread(void* param); - - void Run(); - - rtc::ThreadChecker thread_checker_; - ThreadRunFunction const run_function_; - void* const obj_; - rtc::Event stop_event_; - const std::string name_; - - pthread_t thread_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_THREAD_POSIX_H_ diff --git a/webrtc/system_wrappers/source/thread_win.cc b/webrtc/system_wrappers/source/thread_win.cc deleted file mode 100644 index c421967..0000000 --- a/webrtc/system_wrappers/source/thread_win.cc +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 "webrtc/system_wrappers/source/thread_win.h" - -#include -#include -#include - -#include "webrtc/base/checks.h" -#include "webrtc/base/platform_thread.h" -#include "webrtc/system_wrappers/include/trace.h" - -namespace webrtc { -namespace { -void CALLBACK RaiseFlag(ULONG_PTR param) { - *reinterpret_cast(param) = true; -} -} - -ThreadWindows::ThreadWindows(ThreadRunFunction func, void* obj, - const char* thread_name) - : run_function_(func), - obj_(obj), - stop_(false), - thread_(NULL), - name_(thread_name ? thread_name : "webrtc") { - RTC_DCHECK(func); -} - -ThreadWindows::~ThreadWindows() { - RTC_DCHECK(main_thread_.CalledOnValidThread()); - RTC_DCHECK(!thread_); -} - -// static -uint32_t ThreadWrapper::GetThreadId() { - return GetCurrentThreadId(); -} - -// static -DWORD WINAPI ThreadWindows::StartThread(void* param) { - static_cast(param)->Run(); - return 0; -} - -bool ThreadWindows::Start() { - RTC_DCHECK(main_thread_.CalledOnValidThread()); - RTC_DCHECK(!thread_); - - stop_ = false; - - // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION. - // Set the reserved stack stack size to 1M, which is the default on Windows - // and Linux. - DWORD thread_id; - thread_ = ::CreateThread(NULL, 1024 * 1024, &StartThread, this, - STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id); - if (!thread_ ) { - RTC_DCHECK(false) << "CreateThread failed"; - return false; - } - - return true; -} - -bool ThreadWindows::Stop() { - RTC_DCHECK(main_thread_.CalledOnValidThread()); - if (thread_) { - // Set stop_ to |true| on the worker thread. - QueueUserAPC(&RaiseFlag, thread_, reinterpret_cast(&stop_)); - WaitForSingleObject(thread_, INFINITE); - CloseHandle(thread_); - thread_ = nullptr; - } - - return true; -} - -bool ThreadWindows::SetPriority(ThreadPriority priority) { - RTC_DCHECK(main_thread_.CalledOnValidThread()); - return thread_ && SetThreadPriority(thread_, priority); -} - -void ThreadWindows::Run() { - if (!name_.empty()) - rtc::SetCurrentThreadName(name_.c_str()); - - do { - // The interface contract of Start/Stop is that for a successfull call to - // Start, there should be at least one call to the run function. So we - // call the function before checking |stop_|. - if (!run_function_(obj_)) - break; - // Alertable sleep to permit RaiseFlag to run and update |stop_|. - SleepEx(0, true); - } while (!stop_); -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/thread_win.h b/webrtc/system_wrappers/source/thread_win.h deleted file mode 100644 index 34edd6d..0000000 --- a/webrtc/system_wrappers/source/thread_win.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_THREAD_WIN_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_THREAD_WIN_H_ - -#include "webrtc/system_wrappers/include/thread_wrapper.h" - -#include - -#include "webrtc/base/thread_checker.h" - -namespace webrtc { - -class ThreadWindows : public ThreadWrapper { - public: - ThreadWindows(ThreadRunFunction func, void* obj, const char* thread_name); - ~ThreadWindows() override; - - bool Start() override; - bool Stop() override; - - bool SetPriority(ThreadPriority priority) override; - - protected: - void Run(); - - private: - static DWORD WINAPI StartThread(void* param); - - ThreadRunFunction const run_function_; - void* const obj_; - bool stop_; - HANDLE thread_; - const std::string name_; - rtc::ThreadChecker main_thread_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_THREAD_WIN_H_ diff --git a/webrtc/system_wrappers/source/trace_impl.cc b/webrtc/system_wrappers/source/trace_impl.cc deleted file mode 100644 index ffe79b9..0000000 --- a/webrtc/system_wrappers/source/trace_impl.cc +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/system_wrappers/source/trace_impl.h" - -#include -#include -#include -#include - -#include "webrtc/base/atomicops.h" -#ifdef _WIN32 -#include "webrtc/system_wrappers/source/trace_win.h" -#else -#include "webrtc/system_wrappers/source/trace_posix.h" -#endif // _WIN32 - -#define KEY_LEN_CHARS 31 - -#ifdef _WIN32 -#pragma warning(disable:4355) -#endif // _WIN32 - -namespace webrtc { - -const int Trace::kBoilerplateLength = 71; -const int Trace::kTimestampPosition = 13; -const int Trace::kTimestampLength = 12; -volatile int Trace::level_filter_ = kTraceDefault; - -// Construct On First Use idiom. Avoids "static initialization order fiasco". -TraceImpl* TraceImpl::StaticInstance(CountOperation count_operation, - const TraceLevel level) { - // Sanities to avoid taking lock unless absolutely necessary (for - // performance reasons). count_operation == kAddRefNoCreate implies that a - // message will be written to file. - if ((level != kTraceAll) && (count_operation == kAddRefNoCreate)) { - if (!(level & level_filter())) { - return NULL; - } - } - TraceImpl* impl = - GetStaticInstance(count_operation); - return impl; -} - -TraceImpl* TraceImpl::GetTrace(const TraceLevel level) { - return StaticInstance(kAddRefNoCreate, level); -} - -TraceImpl* TraceImpl::CreateInstance() { -#if defined(_WIN32) - return new TraceWindows(); -#else - return new TracePosix(); -#endif -} - -TraceImpl::TraceImpl() - : callback_(NULL), - row_count_text_(0), - file_count_text_(0), - trace_file_(FileWrapper::Create()) { -} - -TraceImpl::~TraceImpl() { - trace_file_->Flush(); - trace_file_->CloseFile(); -} - -int32_t TraceImpl::AddThreadId(char* trace_message) const { - uint32_t thread_id = ThreadWrapper::GetThreadId(); - // Messages is 12 characters. - return sprintf(trace_message, "%10u; ", thread_id); -} - -int32_t TraceImpl::AddLevel(char* sz_message, const TraceLevel level) const { - const int kMessageLength = 12; - switch (level) { - case kTraceTerseInfo: - // Add the appropriate amount of whitespace. - memset(sz_message, ' ', kMessageLength); - sz_message[kMessageLength] = '\0'; - break; - case kTraceStateInfo: - sprintf(sz_message, "STATEINFO ; "); - break; - case kTraceWarning: - sprintf(sz_message, "WARNING ; "); - break; - case kTraceError: - sprintf(sz_message, "ERROR ; "); - break; - case kTraceCritical: - sprintf(sz_message, "CRITICAL ; "); - break; - case kTraceInfo: - sprintf(sz_message, "DEBUGINFO ; "); - break; - case kTraceModuleCall: - sprintf(sz_message, "MODULECALL; "); - break; - case kTraceMemory: - sprintf(sz_message, "MEMORY ; "); - break; - case kTraceTimer: - sprintf(sz_message, "TIMER ; "); - break; - case kTraceStream: - sprintf(sz_message, "STREAM ; "); - break; - case kTraceApiCall: - sprintf(sz_message, "APICALL ; "); - break; - case kTraceDebug: - sprintf(sz_message, "DEBUG ; "); - break; - default: - assert(false); - return 0; - } - // All messages are 12 characters. - return kMessageLength; -} - -int32_t TraceImpl::AddModuleAndId(char* trace_message, - const TraceModule module, - const int32_t id) const { - // Use long int to prevent problems with different definitions of - // int32_t. - // TODO(hellner): is this actually a problem? If so, it should be better to - // clean up int32_t - const long int idl = id; - const int kMessageLength = 25; - if (idl != -1) { - const unsigned long int id_engine = id >> 16; - const unsigned long int id_channel = id & 0xffff; - - switch (module) { - case kTraceUndefined: - // Add the appropriate amount of whitespace. - memset(trace_message, ' ', kMessageLength); - trace_message[kMessageLength] = '\0'; - break; - case kTraceVoice: - sprintf(trace_message, " VOICE:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceVideo: - sprintf(trace_message, " VIDEO:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceUtility: - sprintf(trace_message, " UTILITY:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceRtpRtcp: - sprintf(trace_message, " RTP/RTCP:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceTransport: - sprintf(trace_message, " TRANSPORT:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceAudioCoding: - sprintf(trace_message, "AUDIO CODING:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceSrtp: - sprintf(trace_message, " SRTP:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceAudioMixerServer: - sprintf(trace_message, " AUDIO MIX/S:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceAudioMixerClient: - sprintf(trace_message, " AUDIO MIX/C:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceVideoCoding: - sprintf(trace_message, "VIDEO CODING:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceVideoMixer: - // Print sleep time and API call - sprintf(trace_message, " VIDEO MIX:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceFile: - sprintf(trace_message, " FILE:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceAudioProcessing: - sprintf(trace_message, " AUDIO PROC:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceAudioDevice: - sprintf(trace_message, "AUDIO DEVICE:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceVideoRenderer: - sprintf(trace_message, "VIDEO RENDER:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceVideoCapture: - sprintf(trace_message, "VIDEO CAPTUR:%5ld %5ld;", id_engine, - id_channel); - break; - case kTraceRemoteBitrateEstimator: - sprintf(trace_message, " BWE RBE:%5ld %5ld;", id_engine, - id_channel); - break; - } - } else { - switch (module) { - case kTraceUndefined: - // Add the appropriate amount of whitespace. - memset(trace_message, ' ', kMessageLength); - trace_message[kMessageLength] = '\0'; - break; - case kTraceVoice: - sprintf(trace_message, " VOICE:%11ld;", idl); - break; - case kTraceVideo: - sprintf(trace_message, " VIDEO:%11ld;", idl); - break; - case kTraceUtility: - sprintf(trace_message, " UTILITY:%11ld;", idl); - break; - case kTraceRtpRtcp: - sprintf(trace_message, " RTP/RTCP:%11ld;", idl); - break; - case kTraceTransport: - sprintf(trace_message, " TRANSPORT:%11ld;", idl); - break; - case kTraceAudioCoding: - sprintf(trace_message, "AUDIO CODING:%11ld;", idl); - break; - case kTraceSrtp: - sprintf(trace_message, " SRTP:%11ld;", idl); - break; - case kTraceAudioMixerServer: - sprintf(trace_message, " AUDIO MIX/S:%11ld;", idl); - break; - case kTraceAudioMixerClient: - sprintf(trace_message, " AUDIO MIX/C:%11ld;", idl); - break; - case kTraceVideoCoding: - sprintf(trace_message, "VIDEO CODING:%11ld;", idl); - break; - case kTraceVideoMixer: - sprintf(trace_message, " VIDEO MIX:%11ld;", idl); - break; - case kTraceFile: - sprintf(trace_message, " FILE:%11ld;", idl); - break; - case kTraceAudioProcessing: - sprintf(trace_message, " AUDIO PROC:%11ld;", idl); - break; - case kTraceAudioDevice: - sprintf(trace_message, "AUDIO DEVICE:%11ld;", idl); - break; - case kTraceVideoRenderer: - sprintf(trace_message, "VIDEO RENDER:%11ld;", idl); - break; - case kTraceVideoCapture: - sprintf(trace_message, "VIDEO CAPTUR:%11ld;", idl); - break; - case kTraceRemoteBitrateEstimator: - sprintf(trace_message, " BWE RBE:%11ld;", idl); - break; - } - } - return kMessageLength; -} - -int32_t TraceImpl::SetTraceFileImpl(const char* file_name_utf8, - const bool add_file_counter) { - rtc::CritScope lock(&crit_); - - trace_file_->Flush(); - trace_file_->CloseFile(); - - if (file_name_utf8) { - if (add_file_counter) { - file_count_text_ = 1; - - char file_name_with_counter_utf8[FileWrapper::kMaxFileNameSize]; - CreateFileName(file_name_utf8, file_name_with_counter_utf8, - file_count_text_); - if (trace_file_->OpenFile(file_name_with_counter_utf8, false, false, - true) == -1) { - return -1; - } - } else { - file_count_text_ = 0; - if (trace_file_->OpenFile(file_name_utf8, false, false, true) == -1) { - return -1; - } - } - } - row_count_text_ = 0; - return 0; -} - -int32_t TraceImpl::TraceFileImpl( - char file_name_utf8[FileWrapper::kMaxFileNameSize]) { - rtc::CritScope lock(&crit_); - return trace_file_->FileName(file_name_utf8, FileWrapper::kMaxFileNameSize); -} - -int32_t TraceImpl::SetTraceCallbackImpl(TraceCallback* callback) { - rtc::CritScope lock(&crit_); - callback_ = callback; - return 0; -} - -int32_t TraceImpl::AddMessage( - char* trace_message, - const char msg[WEBRTC_TRACE_MAX_MESSAGE_SIZE], - const uint16_t written_so_far) const { - int length = 0; - if (written_so_far >= WEBRTC_TRACE_MAX_MESSAGE_SIZE) { - return -1; - } - // - 2 to leave room for newline and NULL termination. -#ifdef _WIN32 - length = _snprintf(trace_message, - WEBRTC_TRACE_MAX_MESSAGE_SIZE - written_so_far - 2, - "%s", msg); - if (length < 0) { - length = WEBRTC_TRACE_MAX_MESSAGE_SIZE - written_so_far - 2; - trace_message[length] = 0; - } -#else - length = snprintf(trace_message, - WEBRTC_TRACE_MAX_MESSAGE_SIZE - written_so_far - 2, - "%s", msg); - if (length < 0 || - length > WEBRTC_TRACE_MAX_MESSAGE_SIZE - written_so_far - 2) { - length = WEBRTC_TRACE_MAX_MESSAGE_SIZE - written_so_far - 2; - trace_message[length] = 0; - } -#endif - // Length with NULL termination. - return length + 1; -} - -void TraceImpl::AddMessageToList( - const char trace_message[WEBRTC_TRACE_MAX_MESSAGE_SIZE], - const uint16_t length, - const TraceLevel level) { - rtc::CritScope lock(&crit_); - if (callback_) - callback_->Print(level, trace_message, length); - WriteToFile(trace_message, length); -} - -void TraceImpl::WriteToFile(const char* msg, uint16_t length) { - if (!trace_file_->Open()) - return; - - if (row_count_text_ > WEBRTC_TRACE_MAX_FILE_SIZE) { - // wrap file - row_count_text_ = 0; - trace_file_->Flush(); - - if (file_count_text_ == 0) { - trace_file_->Rewind(); - } else { - char old_file_name[FileWrapper::kMaxFileNameSize]; - char new_file_name[FileWrapper::kMaxFileNameSize]; - - // get current name - trace_file_->FileName(old_file_name, FileWrapper::kMaxFileNameSize); - trace_file_->CloseFile(); - - file_count_text_++; - - UpdateFileName(old_file_name, new_file_name, file_count_text_); - - if (trace_file_->OpenFile(new_file_name, false, false, true) == -1) { - return; - } - } - } - if (row_count_text_ == 0) { - char message[WEBRTC_TRACE_MAX_MESSAGE_SIZE + 1]; - int32_t length = AddDateTimeInfo(message); - if (length != -1) { - message[length] = 0; - message[length - 1] = '\n'; - trace_file_->Write(message, length); - row_count_text_++; - } - } - - char trace_message[WEBRTC_TRACE_MAX_MESSAGE_SIZE]; - memcpy(trace_message, msg, length); - trace_message[length] = 0; - trace_message[length - 1] = '\n'; - trace_file_->Write(trace_message, length); - row_count_text_++; -} - -void TraceImpl::AddImpl(const TraceLevel level, - const TraceModule module, - const int32_t id, - const char msg[WEBRTC_TRACE_MAX_MESSAGE_SIZE]) { - if (!TraceCheck(level)) - return; - - char trace_message[WEBRTC_TRACE_MAX_MESSAGE_SIZE]; - char* message_ptr = &trace_message[0]; - int32_t len = AddLevel(message_ptr, level); - if (len == -1) - return; - - message_ptr += len; - int32_t ack_len = len; - - len = AddTime(message_ptr, level); - if (len == -1) - return; - - message_ptr += len; - ack_len += len; - - len = AddModuleAndId(message_ptr, module, id); - if (len == -1) - return; - - message_ptr += len; - ack_len += len; - - len = AddThreadId(message_ptr); - if (len < 0) - return; - - message_ptr += len; - ack_len += len; - - len = AddMessage(message_ptr, msg, static_cast(ack_len)); - if (len == -1) - return; - - ack_len += len; - AddMessageToList(trace_message, static_cast(ack_len), level); -} - -bool TraceImpl::TraceCheck(const TraceLevel level) const { - return (level & level_filter()) ? true : false; -} - -bool TraceImpl::UpdateFileName( - const char file_name_utf8[FileWrapper::kMaxFileNameSize], - char file_name_with_counter_utf8[FileWrapper::kMaxFileNameSize], - const uint32_t new_count) const { - int32_t length = (int32_t)strlen(file_name_utf8); - if (length < 0) { - return false; - } - - int32_t length_without_file_ending = length - 1; - while (length_without_file_ending > 0) { - if (file_name_utf8[length_without_file_ending] == '.') { - break; - } else { - length_without_file_ending--; - } - } - if (length_without_file_ending == 0) { - length_without_file_ending = length; - } - int32_t length_to_ = length_without_file_ending - 1; - while (length_to_ > 0) { - if (file_name_utf8[length_to_] == '_') { - break; - } else { - length_to_--; - } - } - - memcpy(file_name_with_counter_utf8, file_name_utf8, length_to_); - sprintf(file_name_with_counter_utf8 + length_to_, "_%lu%s", - static_cast(new_count), - file_name_utf8 + length_without_file_ending); - return true; -} - -bool TraceImpl::CreateFileName( - const char file_name_utf8[FileWrapper::kMaxFileNameSize], - char file_name_with_counter_utf8[FileWrapper::kMaxFileNameSize], - const uint32_t new_count) const { - int32_t length = (int32_t)strlen(file_name_utf8); - if (length < 0) { - return false; - } - - int32_t length_without_file_ending = length - 1; - while (length_without_file_ending > 0) { - if (file_name_utf8[length_without_file_ending] == '.') { - break; - } else { - length_without_file_ending--; - } - } - if (length_without_file_ending == 0) { - length_without_file_ending = length; - } - memcpy(file_name_with_counter_utf8, file_name_utf8, - length_without_file_ending); - sprintf(file_name_with_counter_utf8 + length_without_file_ending, "_%lu%s", - static_cast(new_count), - file_name_utf8 + length_without_file_ending); - return true; -} - -// static -void Trace::CreateTrace() { - TraceImpl::StaticInstance(kAddRef); -} - -// static -void Trace::ReturnTrace() { - TraceImpl::StaticInstance(kRelease); -} - -// static -int32_t Trace::TraceFile(char file_name[FileWrapper::kMaxFileNameSize]) { - TraceImpl* trace = TraceImpl::GetTrace(); - if (trace) { - int ret_val = trace->TraceFileImpl(file_name); - ReturnTrace(); - return ret_val; - } - return -1; -} - -// static -void Trace::set_level_filter(int filter) { - rtc::AtomicOps::ReleaseStore(&level_filter_, filter); -} - -// static -int Trace::level_filter() { - return rtc::AtomicOps::AcquireLoad(&level_filter_); -} - -// static -int32_t Trace::SetTraceFile(const char* file_name, - const bool add_file_counter) { - TraceImpl* trace = TraceImpl::GetTrace(); - if (trace) { - int ret_val = trace->SetTraceFileImpl(file_name, add_file_counter); - ReturnTrace(); - return ret_val; - } - return -1; -} - -int32_t Trace::SetTraceCallback(TraceCallback* callback) { - TraceImpl* trace = TraceImpl::GetTrace(); - if (trace) { - int ret_val = trace->SetTraceCallbackImpl(callback); - ReturnTrace(); - return ret_val; - } - return -1; -} - -void Trace::Add(const TraceLevel level, const TraceModule module, - const int32_t id, const char* msg, ...) { - TraceImpl* trace = TraceImpl::GetTrace(level); - if (trace) { - if (trace->TraceCheck(level)) { - char temp_buff[WEBRTC_TRACE_MAX_MESSAGE_SIZE]; - char* buff = 0; - if (msg) { - va_list args; - va_start(args, msg); -#ifdef _WIN32 - _vsnprintf(temp_buff, WEBRTC_TRACE_MAX_MESSAGE_SIZE - 1, msg, args); -#else - vsnprintf(temp_buff, WEBRTC_TRACE_MAX_MESSAGE_SIZE - 1, msg, args); -#endif - va_end(args); - buff = temp_buff; - } - trace->AddImpl(level, module, id, buff); - } - ReturnTrace(); - } -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/trace_impl.h b/webrtc/system_wrappers/source/trace_impl.h deleted file mode 100644 index ed49d9d..0000000 --- a/webrtc/system_wrappers/source/trace_impl.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_TRACE_IMPL_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_TRACE_IMPL_H_ - -#include "webrtc/base/criticalsection.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/system_wrappers/include/event_wrapper.h" -#include "webrtc/system_wrappers/include/file_wrapper.h" -#include "webrtc/system_wrappers/include/static_instance.h" -#include "webrtc/system_wrappers/include/thread_wrapper.h" -#include "webrtc/system_wrappers/include/trace.h" - -namespace webrtc { - -#define WEBRTC_TRACE_MAX_MESSAGE_SIZE 1024 -// Total buffer size is WEBRTC_TRACE_NUM_ARRAY (number of buffer partitions) * -// WEBRTC_TRACE_MAX_QUEUE (number of lines per buffer partition) * -// WEBRTC_TRACE_MAX_MESSAGE_SIZE (number of 1 byte charachters per line) = -// 1 or 4 Mbyte. - -#define WEBRTC_TRACE_MAX_FILE_SIZE 100*1000 -// Number of rows that may be written to file. On average 110 bytes per row (max -// 256 bytes per row). So on average 110*100*1000 = 11 Mbyte, max 256*100*1000 = -// 25.6 Mbyte - -class TraceImpl : public Trace { - public: - virtual ~TraceImpl(); - - static TraceImpl* CreateInstance(); - static TraceImpl* GetTrace(const TraceLevel level = kTraceAll); - - int32_t SetTraceFileImpl(const char* file_name, const bool add_file_counter); - int32_t TraceFileImpl(char file_name[FileWrapper::kMaxFileNameSize]); - - int32_t SetTraceCallbackImpl(TraceCallback* callback); - - void AddImpl(const TraceLevel level, const TraceModule module, - const int32_t id, const char* msg); - - bool TraceCheck(const TraceLevel level) const; - - protected: - TraceImpl(); - - static TraceImpl* StaticInstance(CountOperation count_operation, - const TraceLevel level = kTraceAll); - - int32_t AddThreadId(char* trace_message) const; - - // OS specific implementations. - virtual int32_t AddTime(char* trace_message, - const TraceLevel level) const = 0; - - virtual int32_t AddDateTimeInfo(char* trace_message) const = 0; - - private: - friend class Trace; - - int32_t AddLevel(char* sz_message, const TraceLevel level) const; - - int32_t AddModuleAndId(char* trace_message, const TraceModule module, - const int32_t id) const; - - int32_t AddMessage(char* trace_message, - const char msg[WEBRTC_TRACE_MAX_MESSAGE_SIZE], - const uint16_t written_so_far) const; - - void AddMessageToList( - const char trace_message[WEBRTC_TRACE_MAX_MESSAGE_SIZE], - const uint16_t length, - const TraceLevel level); - - bool UpdateFileName( - const char file_name_utf8[FileWrapper::kMaxFileNameSize], - char file_name_with_counter_utf8[FileWrapper::kMaxFileNameSize], - const uint32_t new_count) const; - - bool CreateFileName( - const char file_name_utf8[FileWrapper::kMaxFileNameSize], - char file_name_with_counter_utf8[FileWrapper::kMaxFileNameSize], - const uint32_t new_count) const; - - void WriteToFile(const char* msg, uint16_t length) - EXCLUSIVE_LOCKS_REQUIRED(crit_); - - TraceCallback* callback_ GUARDED_BY(crit_); - uint32_t row_count_text_ GUARDED_BY(crit_); - uint32_t file_count_text_ GUARDED_BY(crit_); - - const rtc::scoped_ptr trace_file_ GUARDED_BY(crit_); - rtc::CriticalSection crit_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_TRACE_IMPL_H_ diff --git a/webrtc/system_wrappers/source/trace_posix.cc b/webrtc/system_wrappers/source/trace_posix.cc deleted file mode 100644 index cb702d8..0000000 --- a/webrtc/system_wrappers/source/trace_posix.cc +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/system_wrappers/source/trace_posix.h" - -#include -#include -#include -#include -#include -#include - -namespace webrtc { - -TracePosix::TracePosix() - : crit_sect_(*CriticalSectionWrapper::CreateCriticalSection()) { - struct timeval system_time_high_res; - gettimeofday(&system_time_high_res, 0); - prev_api_tick_count_ = prev_tick_count_ = system_time_high_res.tv_sec; -} - -TracePosix::~TracePosix() { - delete &crit_sect_; -} - -int32_t TracePosix::AddTime(char* trace_message, const TraceLevel level) const { - struct timeval system_time_high_res; - if (gettimeofday(&system_time_high_res, 0) == -1) { - return -1; - } - struct tm buffer; - const struct tm* system_time = - localtime_r(&system_time_high_res.tv_sec, &buffer); - - const uint32_t ms_time = system_time_high_res.tv_usec / 1000; - uint32_t prev_tickCount = 0; - { - CriticalSectionScoped lock(&crit_sect_); - if (level == kTraceApiCall) { - prev_tickCount = prev_tick_count_; - prev_tick_count_ = ms_time; - } else { - prev_tickCount = prev_api_tick_count_; - prev_api_tick_count_ = ms_time; - } - } - - uint32_t dw_delta_time = ms_time - prev_tickCount; - if (prev_tickCount == 0) { - dw_delta_time = 0; - } - if (dw_delta_time > 0x0fffffff) { - // Either wraparound or data race. - dw_delta_time = 0; - } - if (dw_delta_time > 99999) { - dw_delta_time = 99999; - } - - sprintf(trace_message, "(%2u:%2u:%2u:%3u |%5lu) ", system_time->tm_hour, - system_time->tm_min, system_time->tm_sec, ms_time, - static_cast(dw_delta_time)); - // Messages are 22 characters. - return 22; -} - -int32_t TracePosix::AddDateTimeInfo(char* trace_message) const { - time_t t; - time(&t); - char buffer[26]; // man ctime says buffer should have room for >=26 bytes. - sprintf(trace_message, "Local Date: %s", ctime_r(&t, buffer)); - int32_t len = static_cast(strlen(trace_message)); - - if ('\n' == trace_message[len - 1]) { - trace_message[len - 1] = '\0'; - --len; - } - - // Messages is 12 characters. - return len + 1; -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/trace_posix.h b/webrtc/system_wrappers/source/trace_posix.h deleted file mode 100644 index 25dfeec..0000000 --- a/webrtc/system_wrappers/source/trace_posix.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_TRACE_POSIX_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_TRACE_POSIX_H_ - -#include "webrtc/system_wrappers/include/critical_section_wrapper.h" -#include "webrtc/system_wrappers/source/trace_impl.h" - -namespace webrtc { - -class TracePosix : public TraceImpl { - public: - TracePosix(); - ~TracePosix() override; - - // This method can be called on several different threads different from - // the creating thread. - int32_t AddTime(char* trace_message, const TraceLevel level) const override; - - int32_t AddDateTimeInfo(char* trace_message) const override; - - private: - volatile mutable uint32_t prev_api_tick_count_; - volatile mutable uint32_t prev_tick_count_; - - CriticalSectionWrapper& crit_sect_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_TRACE_POSIX_H_ diff --git a/webrtc/system_wrappers/source/trace_win.cc b/webrtc/system_wrappers/source/trace_win.cc deleted file mode 100644 index 4caedfc..0000000 --- a/webrtc/system_wrappers/source/trace_win.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2012 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 "webrtc/system_wrappers/source/trace_win.h" - -#include -#include - -#include "Mmsystem.h" - -namespace webrtc { -TraceWindows::TraceWindows() - : prev_api_tick_count_(0), - prev_tick_count_(0) { -} - -TraceWindows::~TraceWindows() { -} - -int32_t TraceWindows::AddTime(char* trace_message, - const TraceLevel level) const { - uint32_t dw_current_time = timeGetTime(); - SYSTEMTIME system_time; - GetSystemTime(&system_time); - - if (level == kTraceApiCall) { - uint32_t dw_delta_time = dw_current_time - prev_tick_count_; - prev_tick_count_ = dw_current_time; - - if (prev_tick_count_ == 0) { - dw_delta_time = 0; - } - if (dw_delta_time > 0x0fffffff) { - // Either wrap-around or data race. - dw_delta_time = 0; - } - if (dw_delta_time > 99999) { - dw_delta_time = 99999; - } - - sprintf(trace_message, "(%2u:%2u:%2u:%3u |%5u) ", system_time.wHour, - system_time.wMinute, system_time.wSecond, - system_time.wMilliseconds, dw_delta_time); - } else { - uint32_t dw_delta_time = dw_current_time - prev_api_tick_count_; - prev_api_tick_count_ = dw_current_time; - - if (prev_api_tick_count_ == 0) { - dw_delta_time = 0; - } - if (dw_delta_time > 0x0fffffff) { - // Either wraparound or data race. - dw_delta_time = 0; - } - if (dw_delta_time > 99999) { - dw_delta_time = 99999; - } - sprintf(trace_message, "(%2u:%2u:%2u:%3u |%5u) ", system_time.wHour, - system_time.wMinute, system_time.wSecond, - system_time.wMilliseconds, dw_delta_time); - } - return 22; -} - -int32_t TraceWindows::AddDateTimeInfo(char* trace_message) const { - prev_api_tick_count_ = timeGetTime(); - prev_tick_count_ = prev_api_tick_count_; - - SYSTEMTIME sys_time; - GetLocalTime(&sys_time); - - TCHAR sz_date_str[20]; - TCHAR sz_time_str[20]; - - // Create date string (e.g. Apr 04 2002) - GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &sys_time, TEXT("MMM dd yyyy"), - sz_date_str, 20); - - // Create time string (e.g. 15:32:08) - GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &sys_time, TEXT("HH':'mm':'ss"), - sz_time_str, 20); - - sprintf(trace_message, "Local Date: %ls Local Time: %ls", sz_date_str, - sz_time_str); - - // Include NULL termination (hence + 1). - return static_cast(strlen(trace_message) + 1); -} - -} // namespace webrtc diff --git a/webrtc/system_wrappers/source/trace_win.h b/webrtc/system_wrappers/source/trace_win.h deleted file mode 100644 index 1311b23..0000000 --- a/webrtc/system_wrappers/source/trace_win.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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. - */ - -#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_TRACE_WIN_H_ -#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_TRACE_WIN_H_ - -#include -#include - -#include "webrtc/system_wrappers/source/trace_impl.h" - -namespace webrtc { - -class TraceWindows : public TraceImpl { - public: - TraceWindows(); - virtual ~TraceWindows(); - - virtual int32_t AddTime(char* trace_message, const TraceLevel level) const; - - virtual int32_t AddDateTimeInfo(char* trace_message) const; - private: - volatile mutable uint32_t prev_api_tick_count_; - volatile mutable uint32_t prev_tick_count_; -}; - -} // namespace webrtc - -#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_TRACE_WIN_H_ diff --git a/webrtc/third_party/pffft/BUILD.gn b/webrtc/third_party/pffft/BUILD.gn new file mode 100644 index 0000000..7d6c537 --- /dev/null +++ b/webrtc/third_party/pffft/BUILD.gn @@ -0,0 +1,110 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/arm.gni") +import("//testing/libfuzzer/fuzzer_test.gni") +import("//testing/test.gni") + +config("common_config") { + if (is_win) { + defines = [ + # Required to use math constants from math.h. + "_USE_MATH_DEFINES", + ] + } + + # PFFFT doesn't support SIMD on some cpus, so build a scalar version. + if ((current_cpu == "arm" && !arm_use_neon) || current_cpu == "mipsel" || + current_cpu == "mips64el" || current_cpu == "ppc64" || + current_cpu == "s390x") { + defines = [ "PFFFT_SIMD_DISABLE" ] + } +} + +static_library("pffft") { + configs += [ ":common_config" ] + sources = [ + "src/pffft.c", + "src/pffft.h", + ] +} + +# Fuzzing. + +group("fuzzers") { + testonly = true + deps = [ + ":pffft_complex_fuzzer", + ":pffft_real_fuzzer", + ] +} + +fuzzer_testdata_dir = "$target_gen_dir/testdata" + +action("generate_pffft_fuzzer_corpus") { + script = "generate_seed_corpus.py" + sources = [ "generate_seed_corpus.py" ] + args = [ rebase_path(fuzzer_testdata_dir, root_build_dir) ] + outputs = [ fuzzer_testdata_dir ] +} + +fuzzer_test("pffft_complex_fuzzer") { + sources = [ "pffft_fuzzer.cc" ] + cflags = [ "-DTRANSFORM_COMPLEX" ] + deps = [ ":pffft" ] + seed_corpus = fuzzer_testdata_dir + seed_corpus_deps = [ ":generate_pffft_fuzzer_corpus" ] +} + +fuzzer_test("pffft_real_fuzzer") { + sources = [ "pffft_fuzzer.cc" ] + cflags = [ "-DTRANSFORM_REAL" ] + deps = [ ":pffft" ] + seed_corpus = fuzzer_testdata_dir + seed_corpus_deps = [ ":generate_pffft_fuzzer_corpus" ] +} + +# Unit tests and benchmark. + +# This target must be used only for testing and benchmark purposes. +static_library("fftpack") { + testonly = true + configs += [ ":common_config" ] + sources = [ + "src/fftpack.c", + "src/fftpack.h", + ] + visibility = [ ":*" ] +} + +config("pffft_benchmark_internal_config") { + cflags = [ + # test_pffft.c contains an `exit(1)` following a `break` statement. + "-Wno-unreachable-code", + ] +} + +executable("pffft_benchmark") { + testonly = true + configs += [ + ":common_config", + ":pffft_benchmark_internal_config", + ] + sources = [ "src/test_pffft.c" ] + deps = [ + ":fftpack", + ":pffft", + ] +} + +test("pffft_unittest") { + testonly = true + sources = [ "pffft_unittest.cc" ] + deps = [ + ":fftpack", + ":pffft", + "//testing/gtest", + "//testing/gtest:gtest_main", + ] +} diff --git a/webrtc/third_party/pffft/LICENSE b/webrtc/third_party/pffft/LICENSE new file mode 100644 index 0000000..b37aae8 --- /dev/null +++ b/webrtc/third_party/pffft/LICENSE @@ -0,0 +1 @@ +Q29weXJpZ2h0IChjKSAyMDEzICBKdWxpZW4gUG9tbWllciAoIHBvbW1pZXJAbW9kYXJ0dC5jb20gKQoKQmFzZWQgb24gb3JpZ2luYWwgZm9ydHJhbiA3NyBjb2RlIGZyb20gRkZUUEFDS3Y0IGZyb20gTkVUTElCLAphdXRob3JlZCBieSBEciBQYXVsIFN3YXJ6dHJhdWJlciBvZiBOQ0FSLCBpbiAxOTg1LgoKQXMgY29uZmlybWVkIGJ5IHRoZSBOQ0FSIGZmdHBhY2sgc29mdHdhcmUgY3VyYXRvcnMsIHRoZSBmb2xsb3dpbmcKRkZUUEFDS3Y1IGxpY2Vuc2UgYXBwbGllcyB0byBGRlRQQUNLdjQgc291cmNlcy4gTXkgY2hhbmdlcyBhcmUKcmVsZWFzZWQgdW5kZXIgdGhlIHNhbWUgdGVybXMuCgpGRlRQQUNLIGxpY2Vuc2U6CgpodHRwOi8vd3d3LmNpc2wudWNhci5lZHUvY3NzL3NvZnR3YXJlL2ZmdHBhY2s1L2Z0cGsuaHRtbAoKQ29weXJpZ2h0IChjKSAyMDA0IHRoZSBVbml2ZXJzaXR5IENvcnBvcmF0aW9uIGZvciBBdG1vc3BoZXJpYwpSZXNlYXJjaCAoIlVDQVIiKS4gQWxsIHJpZ2h0cyByZXNlcnZlZC4gRGV2ZWxvcGVkIGJ5IE5DQVIncwpDb21wdXRhdGlvbmFsIGFuZCBJbmZvcm1hdGlvbiBTeXN0ZW1zIExhYm9yYXRvcnksIFVDQVIsCnd3dy5jaXNsLnVjYXIuZWR1LgoKUmVkaXN0cmlidXRpb24gYW5kIHVzZSBvZiB0aGUgU29mdHdhcmUgaW4gc291cmNlIGFuZCBiaW5hcnkgZm9ybXMsCndpdGggb3Igd2l0aG91dCBtb2RpZmljYXRpb24sIGlzIHBlcm1pdHRlZCBwcm92aWRlZCB0aGF0IHRoZQpmb2xsb3dpbmcgY29uZGl0aW9ucyBhcmUgbWV0OgoKLSBOZWl0aGVyIHRoZSBuYW1lcyBvZiBOQ0FSJ3MgQ29tcHV0YXRpb25hbCBhbmQgSW5mb3JtYXRpb24gU3lzdGVtcwpMYWJvcmF0b3J5LCB0aGUgVW5pdmVyc2l0eSBDb3Jwb3JhdGlvbiBmb3IgQXRtb3NwaGVyaWMgUmVzZWFyY2gsCm5vciB0aGUgbmFtZXMgb2YgaXRzIHNwb25zb3JzIG9yIGNvbnRyaWJ1dG9ycyBtYXkgYmUgdXNlZCB0bwplbmRvcnNlIG9yIHByb21vdGUgcHJvZHVjdHMgZGVyaXZlZCBmcm9tIHRoaXMgU29mdHdhcmUgd2l0aG91dApzcGVjaWZpYyBwcmlvciB3cml0dGVuIHBlcm1pc3Npb24uCgotIFJlZGlzdHJpYnV0aW9ucyBvZiBzb3VyY2UgY29kZSBtdXN0IHJldGFpbiB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZXMsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zLCBhbmQgdGhlIGRpc2NsYWltZXIgYmVsb3cuCgotIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMsIGFuZCB0aGUgZGlzY2xhaW1lciBiZWxvdyBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlCmRpc3RyaWJ1dGlvbi4KClRISVMgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwKRVhQUkVTUyBPUiBJTVBMSUVELCBJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRgpNRVJDSEFOVEFCSUxJVFksIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORApOT05JTkZSSU5HRU1FTlQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRSBDT05UUklCVVRPUlMgT1IgQ09QWVJJR0hUCkhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIElORElSRUNULCBJTkNJREVOVEFMLCBTUEVDSUFMLApFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyBPUiBPVEhFUiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4KQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLCBPVVQgT0YgT1IgSU4KQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIFdJVEggVEhFClNPRlRXQVJFLgo= \ No newline at end of file diff --git a/webrtc/third_party/pffft/meson.build b/webrtc/third_party/pffft/meson.build new file mode 100644 index 0000000..c1eb5c6 --- /dev/null +++ b/webrtc/third_party/pffft/meson.build @@ -0,0 +1,21 @@ +pffft_sources = [ + 'src/pffft.c', +] + +pffft_cflags = [ '-D_GNU_SOURCE' ] + +if (have_arm and not have_neon) or (have_mips and host_machine.endian() == 'little') or have_mips64 + pffft_cflags += [ '-DPFFFT_SIMD_DISABLE' ] +endif + +libpffft = static_library('libpffft', + pffft_sources, + dependencies: common_deps, + include_directories: webrtc_inc, + c_args : common_cflags + pffft_cflags +) + +pffft_dep = declare_dependency( + link_with: libpffft +) + diff --git a/webrtc/third_party/pffft/src/pffft.c b/webrtc/third_party/pffft/src/pffft.c new file mode 100644 index 0000000..bdac4d7 --- /dev/null +++ b/webrtc/third_party/pffft/src/pffft.c @@ -0,0 +1,1881 @@ +/* Copyright (c) 2013 Julien Pommier ( pommier@modartt.com ) + + Based on original fortran 77 code from FFTPACKv4 from NETLIB + (http://www.netlib.org/fftpack), authored by Dr Paul Swarztrauber + of NCAR, in 1985. + + As confirmed by the NCAR fftpack software curators, the following + FFTPACKv5 license applies to FFTPACKv4 sources. My changes are + released under the same terms. + + FFTPACK license: + + http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html + + Copyright (c) 2004 the University Corporation for Atmospheric + Research ("UCAR"). All rights reserved. Developed by NCAR's + Computational and Information Systems Laboratory, UCAR, + www.cisl.ucar.edu. + + Redistribution and use of the Software in source and binary forms, + with or without modification, is permitted provided that the + following conditions are met: + + - Neither the names of NCAR's Computational and Information Systems + Laboratory, the University Corporation for Atmospheric Research, + nor the names of its sponsors or contributors may be used to + endorse or promote products derived from this Software without + specific prior written permission. + + - Redistributions of source code must retain the above copyright + notices, this list of conditions, and the disclaimer below. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the disclaimer below in the + documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE + SOFTWARE. + + + PFFFT : a Pretty Fast FFT. + + This file is largerly based on the original FFTPACK implementation, modified in + order to take advantage of SIMD instructions of modern CPUs. +*/ + +/* + ChangeLog: + - 2011/10/02, version 1: This is the very first release of this file. +*/ + +#include "pffft.h" +#include +#include +#include +#include + +/* detect compiler flavour */ +#if defined(_MSC_VER) +# define COMPILER_MSVC +#elif defined(__GNUC__) +# define COMPILER_GCC +#endif + +#if defined(COMPILER_GCC) +# define ALWAYS_INLINE(return_type) inline return_type __attribute__ ((always_inline)) +# define NEVER_INLINE(return_type) return_type __attribute__ ((noinline)) +# define RESTRICT __restrict +# define VLA_ARRAY_ON_STACK(type__, varname__, size__) type__ varname__[size__]; +# define VLA_ARRAY_ON_STACK_FREE(varname__) +#elif defined(COMPILER_MSVC) +#include +# define ALWAYS_INLINE(return_type) __forceinline return_type +# define NEVER_INLINE(return_type) __declspec(noinline) return_type +# define RESTRICT __restrict +# define VLA_ARRAY_ON_STACK(type__, varname__, size__) type__ *varname__ = (type__*)_malloca(size__ * sizeof(type__)) +# define VLA_ARRAY_ON_STACK_FREE(varname__) _freea(varname__) +#endif + + +/* + vector support macros: the rest of the code is independant of + SSE/Altivec/NEON -- adding support for other platforms with 4-element + vectors should be limited to these macros +*/ + + +// define PFFFT_SIMD_DISABLE if you want to use scalar code instead of simd code +//#define PFFFT_SIMD_DISABLE + +/* + Altivec support macros +*/ +#if !defined(PFFFT_SIMD_DISABLE) && (defined(__ppc__) || defined(__ppc64__)) +typedef vector float v4sf; +# define SIMD_SZ 4 +# define VZERO() ((vector float) vec_splat_u8(0)) +# define VMUL(a,b) vec_madd(a,b, VZERO()) +# define VADD(a,b) vec_add(a,b) +# define VMADD(a,b,c) vec_madd(a,b,c) +# define VSUB(a,b) vec_sub(a,b) +inline v4sf ld_ps1(const float *p) { v4sf v=vec_lde(0,p); return vec_splat(vec_perm(v, v, vec_lvsl(0, p)), 0); } +# define LD_PS1(p) ld_ps1(&p) +# define INTERLEAVE2(in1, in2, out1, out2) { v4sf tmp__ = vec_mergeh(in1, in2); out2 = vec_mergel(in1, in2); out1 = tmp__; } +# define UNINTERLEAVE2(in1, in2, out1, out2) { \ + vector unsigned char vperm1 = (vector unsigned char)(0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27); \ + vector unsigned char vperm2 = (vector unsigned char)(4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31); \ + v4sf tmp__ = vec_perm(in1, in2, vperm1); out2 = vec_perm(in1, in2, vperm2); out1 = tmp__; \ + } +# define VTRANSPOSE4(x0,x1,x2,x3) { \ + v4sf y0 = vec_mergeh(x0, x2); \ + v4sf y1 = vec_mergel(x0, x2); \ + v4sf y2 = vec_mergeh(x1, x3); \ + v4sf y3 = vec_mergel(x1, x3); \ + x0 = vec_mergeh(y0, y2); \ + x1 = vec_mergel(y0, y2); \ + x2 = vec_mergeh(y1, y3); \ + x3 = vec_mergel(y1, y3); \ + } +# define VSWAPHL(a,b) vec_perm(a,b, (vector unsigned char)(16,17,18,19,20,21,22,23,8,9,10,11,12,13,14,15)) +# define VALIGNED(ptr) ((((uintptr_t)(ptr)) & 0xF) == 0) + +/* + SSE1 support macros +*/ +#elif !defined(PFFFT_SIMD_DISABLE) && (defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || defined(_M_IX86)) + +#include +typedef __m128 v4sf; +# define SIMD_SZ 4 // 4 floats by simd vector -- this is pretty much hardcoded in the preprocess/finalize functions anyway so you will have to work if you want to enable AVX with its 256-bit vectors. +# define VZERO() _mm_setzero_ps() +# define VMUL(a,b) _mm_mul_ps(a,b) +# define VADD(a,b) _mm_add_ps(a,b) +# define VMADD(a,b,c) _mm_add_ps(_mm_mul_ps(a,b), c) +# define VSUB(a,b) _mm_sub_ps(a,b) +# define LD_PS1(p) _mm_set1_ps(p) +# define INTERLEAVE2(in1, in2, out1, out2) { v4sf tmp__ = _mm_unpacklo_ps(in1, in2); out2 = _mm_unpackhi_ps(in1, in2); out1 = tmp__; } +# define UNINTERLEAVE2(in1, in2, out1, out2) { v4sf tmp__ = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(2,0,2,0)); out2 = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(3,1,3,1)); out1 = tmp__; } +# define VTRANSPOSE4(x0,x1,x2,x3) _MM_TRANSPOSE4_PS(x0,x1,x2,x3) +# define VSWAPHL(a,b) _mm_shuffle_ps(b, a, _MM_SHUFFLE(3,2,1,0)) +# define VALIGNED(ptr) ((((uintptr_t)(ptr)) & 0xF) == 0) + +/* + ARM NEON support macros +*/ +#elif !defined(PFFFT_SIMD_DISABLE) && (defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(_M_ARM64)) +# include +typedef float32x4_t v4sf; +# define SIMD_SZ 4 +# define VZERO() vdupq_n_f32(0) +# define VMUL(a,b) vmulq_f32(a,b) +# define VADD(a,b) vaddq_f32(a,b) +# define VMADD(a,b,c) vmlaq_f32(c,a,b) +# define VSUB(a,b) vsubq_f32(a,b) +# define LD_PS1(p) vld1q_dup_f32(&(p)) +# define INTERLEAVE2(in1, in2, out1, out2) { float32x4x2_t tmp__ = vzipq_f32(in1,in2); out1=tmp__.val[0]; out2=tmp__.val[1]; } +# define UNINTERLEAVE2(in1, in2, out1, out2) { float32x4x2_t tmp__ = vuzpq_f32(in1,in2); out1=tmp__.val[0]; out2=tmp__.val[1]; } +# define VTRANSPOSE4(x0,x1,x2,x3) { \ + float32x4x2_t t0_ = vzipq_f32(x0, x2); \ + float32x4x2_t t1_ = vzipq_f32(x1, x3); \ + float32x4x2_t u0_ = vzipq_f32(t0_.val[0], t1_.val[0]); \ + float32x4x2_t u1_ = vzipq_f32(t0_.val[1], t1_.val[1]); \ + x0 = u0_.val[0]; x1 = u0_.val[1]; x2 = u1_.val[0]; x3 = u1_.val[1]; \ + } +// marginally faster version +//# define VTRANSPOSE4(x0,x1,x2,x3) { asm("vtrn.32 %q0, %q1;\n vtrn.32 %q2,%q3\n vswp %f0,%e2\n vswp %f1,%e3" : "+w"(x0), "+w"(x1), "+w"(x2), "+w"(x3)::); } +# define VSWAPHL(a,b) vcombine_f32(vget_low_f32(b), vget_high_f32(a)) +# define VALIGNED(ptr) ((((uintptr_t)(ptr)) & 0x3) == 0) +#else +# if !defined(PFFFT_SIMD_DISABLE) +# warning "building with simd disabled !\n"; +# define PFFFT_SIMD_DISABLE // fallback to scalar code +# endif +#endif + +// fallback mode for situations where SSE/Altivec are not available, use scalar mode instead +#ifdef PFFFT_SIMD_DISABLE +typedef float v4sf; +# define SIMD_SZ 1 +# define VZERO() 0.f +# define VMUL(a,b) ((a)*(b)) +# define VADD(a,b) ((a)+(b)) +# define VMADD(a,b,c) ((a)*(b)+(c)) +# define VSUB(a,b) ((a)-(b)) +# define LD_PS1(p) (p) +# define VALIGNED(ptr) ((((uintptr_t)(ptr)) & 0x3) == 0) +#endif + +// shortcuts for complex multiplcations +#define VCPLXMUL(ar,ai,br,bi) { v4sf tmp; tmp=VMUL(ar,bi); ar=VMUL(ar,br); ar=VSUB(ar,VMUL(ai,bi)); ai=VMUL(ai,br); ai=VADD(ai,tmp); } +#define VCPLXMULCONJ(ar,ai,br,bi) { v4sf tmp; tmp=VMUL(ar,bi); ar=VMUL(ar,br); ar=VADD(ar,VMUL(ai,bi)); ai=VMUL(ai,br); ai=VSUB(ai,tmp); } +#ifndef SVMUL +// multiply a scalar with a vector +#define SVMUL(f,v) VMUL(LD_PS1(f),v) +#endif + +#if !defined(PFFFT_SIMD_DISABLE) +typedef union v4sf_union { + v4sf v; + float f[4]; +} v4sf_union; + +#include + +#define assertv4(v,f0,f1,f2,f3) assert(v.f[0] == (f0) && v.f[1] == (f1) && v.f[2] == (f2) && v.f[3] == (f3)) + +/* detect bugs with the vector support macros */ +void validate_pffft_simd() { + float f[16] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 }; + v4sf_union a0, a1, a2, a3, t, u; + memcpy(a0.f, f, 4*sizeof(float)); + memcpy(a1.f, f+4, 4*sizeof(float)); + memcpy(a2.f, f+8, 4*sizeof(float)); + memcpy(a3.f, f+12, 4*sizeof(float)); + + t = a0; u = a1; t.v = VZERO(); + assertv4(t, 0, 0, 0, 0); + t.v = VADD(a1.v, a2.v); + assertv4(t, 12, 14, 16, 18); + t.v = VMUL(a1.v, a2.v); + assertv4(t, 32, 45, 60, 77); + t.v = VMADD(a1.v, a2.v,a0.v); + assertv4(t, 32, 46, 62, 80); + + INTERLEAVE2(a1.v,a2.v,t.v,u.v); + assertv4(t, 4, 8, 5, 9); assertv4(u, 6, 10, 7, 11); + UNINTERLEAVE2(a1.v,a2.v,t.v,u.v); + assertv4(t, 4, 6, 8, 10); assertv4(u, 5, 7, 9, 11); + + t.v=LD_PS1(f[15]); + assertv4(t, 15, 15, 15, 15); + t.v = VSWAPHL(a1.v, a2.v); + assertv4(t, 8, 9, 6, 7); + VTRANSPOSE4(a0.v, a1.v, a2.v, a3.v); + assertv4(a0, 0, 4, 8, 12); assertv4(a1, 1, 5, 9, 13); assertv4(a2, 2, 6, 10, 14); assertv4(a3, 3, 7, 11, 15); +} +#endif //!PFFFT_SIMD_DISABLE + +/* SSE and co like 16-bytes aligned pointers */ +#define MALLOC_V4SF_ALIGNMENT 64 // with a 64-byte alignment, we are even aligned on L2 cache lines... +void *pffft_aligned_malloc(size_t nb_bytes) { + void *p, *p0 = malloc(nb_bytes + MALLOC_V4SF_ALIGNMENT); + if (!p0) return (void *) 0; + p = (void *) (((size_t) p0 + MALLOC_V4SF_ALIGNMENT) & (~((size_t) (MALLOC_V4SF_ALIGNMENT-1)))); + *((void **) p - 1) = p0; + return p; +} + +void pffft_aligned_free(void *p) { + if (p) free(*((void **) p - 1)); +} + +int pffft_simd_size() { return SIMD_SZ; } + +/* + passf2 and passb2 has been merged here, fsign = -1 for passf2, +1 for passb2 +*/ +static NEVER_INLINE(void) passf2_ps(int ido, int l1, const v4sf *cc, v4sf *ch, const float *wa1, float fsign) { + int k, i; + int l1ido = l1*ido; + if (ido <= 2) { + for (k=0; k < l1ido; k += ido, ch += ido, cc+= 2*ido) { + ch[0] = VADD(cc[0], cc[ido+0]); + ch[l1ido] = VSUB(cc[0], cc[ido+0]); + ch[1] = VADD(cc[1], cc[ido+1]); + ch[l1ido + 1] = VSUB(cc[1], cc[ido+1]); + } + } else { + for (k=0; k < l1ido; k += ido, ch += ido, cc += 2*ido) { + for (i=0; i 2); + for (k=0; k< l1ido; k += ido, cc+= 3*ido, ch +=ido) { + for (i=0; i 2); + for (k = 0; k < l1; ++k, cc += 5*ido, ch += ido) { + for (i = 0; i < ido-1; i += 2) { + ti5 = VSUB(cc_ref(i , 2), cc_ref(i , 5)); + ti2 = VADD(cc_ref(i , 2), cc_ref(i , 5)); + ti4 = VSUB(cc_ref(i , 3), cc_ref(i , 4)); + ti3 = VADD(cc_ref(i , 3), cc_ref(i , 4)); + tr5 = VSUB(cc_ref(i-1, 2), cc_ref(i-1, 5)); + tr2 = VADD(cc_ref(i-1, 2), cc_ref(i-1, 5)); + tr4 = VSUB(cc_ref(i-1, 3), cc_ref(i-1, 4)); + tr3 = VADD(cc_ref(i-1, 3), cc_ref(i-1, 4)); + ch_ref(i-1, 1) = VADD(cc_ref(i-1, 1), VADD(tr2, tr3)); + ch_ref(i , 1) = VADD(cc_ref(i , 1), VADD(ti2, ti3)); + cr2 = VADD(cc_ref(i-1, 1), VADD(SVMUL(tr11, tr2),SVMUL(tr12, tr3))); + ci2 = VADD(cc_ref(i , 1), VADD(SVMUL(tr11, ti2),SVMUL(tr12, ti3))); + cr3 = VADD(cc_ref(i-1, 1), VADD(SVMUL(tr12, tr2),SVMUL(tr11, tr3))); + ci3 = VADD(cc_ref(i , 1), VADD(SVMUL(tr12, ti2),SVMUL(tr11, ti3))); + cr5 = VADD(SVMUL(ti11, tr5), SVMUL(ti12, tr4)); + ci5 = VADD(SVMUL(ti11, ti5), SVMUL(ti12, ti4)); + cr4 = VSUB(SVMUL(ti12, tr5), SVMUL(ti11, tr4)); + ci4 = VSUB(SVMUL(ti12, ti5), SVMUL(ti11, ti4)); + dr3 = VSUB(cr3, ci4); + dr4 = VADD(cr3, ci4); + di3 = VADD(ci3, cr4); + di4 = VSUB(ci3, cr4); + dr5 = VADD(cr2, ci5); + dr2 = VSUB(cr2, ci5); + di5 = VSUB(ci2, cr5); + di2 = VADD(ci2, cr5); + wr1=wa1[i], wi1=fsign*wa1[i+1], wr2=wa2[i], wi2=fsign*wa2[i+1]; + wr3=wa3[i], wi3=fsign*wa3[i+1], wr4=wa4[i], wi4=fsign*wa4[i+1]; + VCPLXMUL(dr2, di2, LD_PS1(wr1), LD_PS1(wi1)); + ch_ref(i - 1, 2) = dr2; + ch_ref(i, 2) = di2; + VCPLXMUL(dr3, di3, LD_PS1(wr2), LD_PS1(wi2)); + ch_ref(i - 1, 3) = dr3; + ch_ref(i, 3) = di3; + VCPLXMUL(dr4, di4, LD_PS1(wr3), LD_PS1(wi3)); + ch_ref(i - 1, 4) = dr4; + ch_ref(i, 4) = di4; + VCPLXMUL(dr5, di5, LD_PS1(wr4), LD_PS1(wi4)); + ch_ref(i - 1, 5) = dr5; + ch_ref(i, 5) = di5; + } + } +#undef ch_ref +#undef cc_ref +} + +static NEVER_INLINE(void) radf2_ps(int ido, int l1, const v4sf * RESTRICT cc, v4sf * RESTRICT ch, const float *wa1) { + static const float minus_one = -1.f; + int i, k, l1ido = l1*ido; + for (k=0; k < l1ido; k += ido) { + v4sf a = cc[k], b = cc[k + l1ido]; + ch[2*k] = VADD(a, b); + ch[2*(k+ido)-1] = VSUB(a, b); + } + if (ido < 2) return; + if (ido != 2) { + for (k=0; k < l1ido; k += ido) { + for (i=2; i 5) { + wa[i1-1] = wa[i-1]; + wa[i1] = wa[i]; + } + } + l1 = l2; + } +} /* cffti1 */ + + +v4sf *cfftf1_ps(int n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, const float *wa, const int *ifac, int isign) { + v4sf *in = (v4sf*)input_readonly; + v4sf *out = (in == work2 ? work1 : work2); + int nf = ifac[1], k1; + int l1 = 1; + int iw = 0; + assert(in != out && work1 != work2); + for (k1=2; k1<=nf+1; k1++) { + int ip = ifac[k1]; + int l2 = ip*l1; + int ido = n / l2; + int idot = ido + ido; + switch (ip) { + case 5: { + int ix2 = iw + idot; + int ix3 = ix2 + idot; + int ix4 = ix3 + idot; + passf5_ps(idot, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], isign); + } break; + case 4: { + int ix2 = iw + idot; + int ix3 = ix2 + idot; + passf4_ps(idot, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], isign); + } break; + case 2: { + passf2_ps(idot, l1, in, out, &wa[iw], isign); + } break; + case 3: { + int ix2 = iw + idot; + passf3_ps(idot, l1, in, out, &wa[iw], &wa[ix2], isign); + } break; + default: + assert(0); + } + l1 = l2; + iw += (ip - 1)*idot; + if (out == work2) { + out = work1; in = work2; + } else { + out = work2; in = work1; + } + } + + return in; /* this is in fact the output .. */ +} + + +struct PFFFT_Setup { + int N; + int Ncvec; // nb of complex simd vectors (N/4 if PFFFT_COMPLEX, N/8 if PFFFT_REAL) + int ifac[15]; + pffft_transform_t transform; + v4sf *data; // allocated room for twiddle coefs + float *e; // points into 'data' , N/4*3 elements + float *twiddle; // points into 'data', N/4 elements +}; + +PFFFT_Setup *pffft_new_setup(int N, pffft_transform_t transform) { + PFFFT_Setup *s = (PFFFT_Setup*)malloc(sizeof(PFFFT_Setup)); + int k, m; + /* unfortunately, the fft size must be a multiple of 16 for complex FFTs + and 32 for real FFTs -- a lot of stuff would need to be rewritten to + handle other cases (or maybe just switch to a scalar fft, I don't know..) */ + if (transform == PFFFT_REAL) { assert((N%(2*SIMD_SZ*SIMD_SZ))==0 && N>0); } + if (transform == PFFFT_COMPLEX) { assert((N%(SIMD_SZ*SIMD_SZ))==0 && N>0); } + //assert((N % 32) == 0); + s->N = N; + s->transform = transform; + /* nb of complex simd vectors */ + s->Ncvec = (transform == PFFFT_REAL ? N/2 : N)/SIMD_SZ; + s->data = (v4sf*)pffft_aligned_malloc(2*s->Ncvec * sizeof(v4sf)); + s->e = (float*)s->data; + s->twiddle = (float*)(s->data + (2*s->Ncvec*(SIMD_SZ-1))/SIMD_SZ); + + if (transform == PFFFT_REAL) { + for (k=0; k < s->Ncvec; ++k) { + int i = k/SIMD_SZ; + int j = k%SIMD_SZ; + for (m=0; m < SIMD_SZ-1; ++m) { + float A = -2*M_PI*(m+1)*k / N; + s->e[(2*(i*3 + m) + 0) * SIMD_SZ + j] = cos(A); + s->e[(2*(i*3 + m) + 1) * SIMD_SZ + j] = sin(A); + } + } + rffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac); + } else { + for (k=0; k < s->Ncvec; ++k) { + int i = k/SIMD_SZ; + int j = k%SIMD_SZ; + for (m=0; m < SIMD_SZ-1; ++m) { + float A = -2*M_PI*(m+1)*k / N; + s->e[(2*(i*3 + m) + 0)*SIMD_SZ + j] = cos(A); + s->e[(2*(i*3 + m) + 1)*SIMD_SZ + j] = sin(A); + } + } + cffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac); + } + + /* check that N is decomposable with allowed prime factors */ + for (k=0, m=1; k < s->ifac[1]; ++k) { m *= s->ifac[2+k]; } + if (m != N/SIMD_SZ) { + pffft_destroy_setup(s); s = 0; + } + + return s; +} + + +void pffft_destroy_setup(PFFFT_Setup *s) { + pffft_aligned_free(s->data); + free(s); +} + +#if !defined(PFFFT_SIMD_DISABLE) + +/* [0 0 1 2 3 4 5 6 7 8] -> [0 8 7 6 5 4 3 2 1] */ +static void reversed_copy(int N, const v4sf *in, int in_stride, v4sf *out) { + v4sf g0, g1; + int k; + INTERLEAVE2(in[0], in[1], g0, g1); in += in_stride; + + *--out = VSWAPHL(g0, g1); // [g0l, g0h], [g1l g1h] -> [g1l, g0h] + for (k=1; k < N; ++k) { + v4sf h0, h1; + INTERLEAVE2(in[0], in[1], h0, h1); in += in_stride; + *--out = VSWAPHL(g1, h0); + *--out = VSWAPHL(h0, h1); + g1 = h1; + } + *--out = VSWAPHL(g1, g0); +} + +static void unreversed_copy(int N, const v4sf *in, v4sf *out, int out_stride) { + v4sf g0, g1, h0, h1; + int k; + g0 = g1 = in[0]; ++in; + for (k=1; k < N; ++k) { + h0 = *in++; h1 = *in++; + g1 = VSWAPHL(g1, h0); + h0 = VSWAPHL(h0, h1); + UNINTERLEAVE2(h0, g1, out[0], out[1]); out += out_stride; + g1 = h1; + } + h0 = *in++; h1 = g0; + g1 = VSWAPHL(g1, h0); + h0 = VSWAPHL(h0, h1); + UNINTERLEAVE2(h0, g1, out[0], out[1]); +} + +void pffft_zreorder(PFFFT_Setup *setup, const float *in, float *out, pffft_direction_t direction) { + int k, N = setup->N, Ncvec = setup->Ncvec; + const v4sf *vin = (const v4sf*)in; + v4sf *vout = (v4sf*)out; + assert(in != out); + if (setup->transform == PFFFT_REAL) { + int k, dk = N/32; + if (direction == PFFFT_FORWARD) { + for (k=0; k < dk; ++k) { + INTERLEAVE2(vin[k*8 + 0], vin[k*8 + 1], vout[2*(0*dk + k) + 0], vout[2*(0*dk + k) + 1]); + INTERLEAVE2(vin[k*8 + 4], vin[k*8 + 5], vout[2*(2*dk + k) + 0], vout[2*(2*dk + k) + 1]); + } + reversed_copy(dk, vin+2, 8, (v4sf*)(out + N/2)); + reversed_copy(dk, vin+6, 8, (v4sf*)(out + N)); + } else { + for (k=0; k < dk; ++k) { + UNINTERLEAVE2(vin[2*(0*dk + k) + 0], vin[2*(0*dk + k) + 1], vout[k*8 + 0], vout[k*8 + 1]); + UNINTERLEAVE2(vin[2*(2*dk + k) + 0], vin[2*(2*dk + k) + 1], vout[k*8 + 4], vout[k*8 + 5]); + } + unreversed_copy(dk, (v4sf*)(in + N/4), (v4sf*)(out + N - 6*SIMD_SZ), -8); + unreversed_copy(dk, (v4sf*)(in + 3*N/4), (v4sf*)(out + N - 2*SIMD_SZ), -8); + } + } else { + if (direction == PFFFT_FORWARD) { + for (k=0; k < Ncvec; ++k) { + int kk = (k/4) + (k%4)*(Ncvec/4); + INTERLEAVE2(vin[k*2], vin[k*2+1], vout[kk*2], vout[kk*2+1]); + } + } else { + for (k=0; k < Ncvec; ++k) { + int kk = (k/4) + (k%4)*(Ncvec/4); + UNINTERLEAVE2(vin[kk*2], vin[kk*2+1], vout[k*2], vout[k*2+1]); + } + } + } +} + +void pffft_cplx_finalize(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) { + int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks + v4sf r0, i0, r1, i1, r2, i2, r3, i3; + v4sf sr0, dr0, sr1, dr1, si0, di0, si1, di1; + assert(in != out); + for (k=0; k < dk; ++k) { + r0 = in[8*k+0]; i0 = in[8*k+1]; + r1 = in[8*k+2]; i1 = in[8*k+3]; + r2 = in[8*k+4]; i2 = in[8*k+5]; + r3 = in[8*k+6]; i3 = in[8*k+7]; + VTRANSPOSE4(r0,r1,r2,r3); + VTRANSPOSE4(i0,i1,i2,i3); + VCPLXMUL(r1,i1,e[k*6+0],e[k*6+1]); + VCPLXMUL(r2,i2,e[k*6+2],e[k*6+3]); + VCPLXMUL(r3,i3,e[k*6+4],e[k*6+5]); + + sr0 = VADD(r0,r2); dr0 = VSUB(r0, r2); + sr1 = VADD(r1,r3); dr1 = VSUB(r1, r3); + si0 = VADD(i0,i2); di0 = VSUB(i0, i2); + si1 = VADD(i1,i3); di1 = VSUB(i1, i3); + + /* + transformation for each column is: + + [1 1 1 1 0 0 0 0] [r0] + [1 0 -1 0 0 -1 0 1] [r1] + [1 -1 1 -1 0 0 0 0] [r2] + [1 0 -1 0 0 1 0 -1] [r3] + [0 0 0 0 1 1 1 1] * [i0] + [0 1 0 -1 1 0 -1 0] [i1] + [0 0 0 0 1 -1 1 -1] [i2] + [0 -1 0 1 1 0 -1 0] [i3] + */ + + r0 = VADD(sr0, sr1); i0 = VADD(si0, si1); + r1 = VADD(dr0, di1); i1 = VSUB(di0, dr1); + r2 = VSUB(sr0, sr1); i2 = VSUB(si0, si1); + r3 = VSUB(dr0, di1); i3 = VADD(di0, dr1); + + *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1; + *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3; + } +} + +void pffft_cplx_preprocess(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) { + int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks + v4sf r0, i0, r1, i1, r2, i2, r3, i3; + v4sf sr0, dr0, sr1, dr1, si0, di0, si1, di1; + assert(in != out); + for (k=0; k < dk; ++k) { + r0 = in[8*k+0]; i0 = in[8*k+1]; + r1 = in[8*k+2]; i1 = in[8*k+3]; + r2 = in[8*k+4]; i2 = in[8*k+5]; + r3 = in[8*k+6]; i3 = in[8*k+7]; + + sr0 = VADD(r0,r2); dr0 = VSUB(r0, r2); + sr1 = VADD(r1,r3); dr1 = VSUB(r1, r3); + si0 = VADD(i0,i2); di0 = VSUB(i0, i2); + si1 = VADD(i1,i3); di1 = VSUB(i1, i3); + + r0 = VADD(sr0, sr1); i0 = VADD(si0, si1); + r1 = VSUB(dr0, di1); i1 = VADD(di0, dr1); + r2 = VSUB(sr0, sr1); i2 = VSUB(si0, si1); + r3 = VADD(dr0, di1); i3 = VSUB(di0, dr1); + + VCPLXMULCONJ(r1,i1,e[k*6+0],e[k*6+1]); + VCPLXMULCONJ(r2,i2,e[k*6+2],e[k*6+3]); + VCPLXMULCONJ(r3,i3,e[k*6+4],e[k*6+5]); + + VTRANSPOSE4(r0,r1,r2,r3); + VTRANSPOSE4(i0,i1,i2,i3); + + *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1; + *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3; + } +} + + +static ALWAYS_INLINE(void) pffft_real_finalize_4x4(const v4sf *in0, const v4sf *in1, const v4sf *in, + const v4sf *e, v4sf *out) { + v4sf r0, i0, r1, i1, r2, i2, r3, i3; + v4sf sr0, dr0, sr1, dr1, si0, di0, si1, di1; + r0 = *in0; i0 = *in1; + r1 = *in++; i1 = *in++; r2 = *in++; i2 = *in++; r3 = *in++; i3 = *in++; + VTRANSPOSE4(r0,r1,r2,r3); + VTRANSPOSE4(i0,i1,i2,i3); + + /* + transformation for each column is: + + [1 1 1 1 0 0 0 0] [r0] + [1 0 -1 0 0 -1 0 1] [r1] + [1 0 -1 0 0 1 0 -1] [r2] + [1 -1 1 -1 0 0 0 0] [r3] + [0 0 0 0 1 1 1 1] * [i0] + [0 -1 0 1 -1 0 1 0] [i1] + [0 -1 0 1 1 0 -1 0] [i2] + [0 0 0 0 -1 1 -1 1] [i3] + */ + + //cerr << "matrix initial, before e , REAL:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n"; + //cerr << "matrix initial, before e, IMAG :\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n"; + + VCPLXMUL(r1,i1,e[0],e[1]); + VCPLXMUL(r2,i2,e[2],e[3]); + VCPLXMUL(r3,i3,e[4],e[5]); + + //cerr << "matrix initial, real part:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n"; + //cerr << "matrix initial, imag part:\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n"; + + sr0 = VADD(r0,r2); dr0 = VSUB(r0,r2); + sr1 = VADD(r1,r3); dr1 = VSUB(r3,r1); + si0 = VADD(i0,i2); di0 = VSUB(i0,i2); + si1 = VADD(i1,i3); di1 = VSUB(i3,i1); + + r0 = VADD(sr0, sr1); + r3 = VSUB(sr0, sr1); + i0 = VADD(si0, si1); + i3 = VSUB(si1, si0); + r1 = VADD(dr0, di1); + r2 = VSUB(dr0, di1); + i1 = VSUB(dr1, di0); + i2 = VADD(dr1, di0); + + *out++ = r0; + *out++ = i0; + *out++ = r1; + *out++ = i1; + *out++ = r2; + *out++ = i2; + *out++ = r3; + *out++ = i3; + +} + +static NEVER_INLINE(void) pffft_real_finalize(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) { + int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks + /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */ + + v4sf_union cr, ci, *uout = (v4sf_union*)out; + v4sf save = in[7], zero=VZERO(); + float xr0, xi0, xr1, xi1, xr2, xi2, xr3, xi3; + static const float s = M_SQRT2/2; + + cr.v = in[0]; ci.v = in[Ncvec*2-1]; + assert(in != out); + pffft_real_finalize_4x4(&zero, &zero, in+1, e, out); + + /* + [cr0 cr1 cr2 cr3 ci0 ci1 ci2 ci3] + + [Xr(1)] ] [1 1 1 1 0 0 0 0] + [Xr(N/4) ] [0 0 0 0 1 s 0 -s] + [Xr(N/2) ] [1 0 -1 0 0 0 0 0] + [Xr(3N/4)] [0 0 0 0 1 -s 0 s] + [Xi(1) ] [1 -1 1 -1 0 0 0 0] + [Xi(N/4) ] [0 0 0 0 0 -s -1 -s] + [Xi(N/2) ] [0 -1 0 1 0 0 0 0] + [Xi(3N/4)] [0 0 0 0 0 -s 1 -s] + */ + + xr0=(cr.f[0]+cr.f[2]) + (cr.f[1]+cr.f[3]); uout[0].f[0] = xr0; + xi0=(cr.f[0]+cr.f[2]) - (cr.f[1]+cr.f[3]); uout[1].f[0] = xi0; + xr2=(cr.f[0]-cr.f[2]); uout[4].f[0] = xr2; + xi2=(cr.f[3]-cr.f[1]); uout[5].f[0] = xi2; + xr1= ci.f[0] + s*(ci.f[1]-ci.f[3]); uout[2].f[0] = xr1; + xi1=-ci.f[2] - s*(ci.f[1]+ci.f[3]); uout[3].f[0] = xi1; + xr3= ci.f[0] - s*(ci.f[1]-ci.f[3]); uout[6].f[0] = xr3; + xi3= ci.f[2] - s*(ci.f[1]+ci.f[3]); uout[7].f[0] = xi3; + + for (k=1; k < dk; ++k) { + v4sf save_next = in[8*k+7]; + pffft_real_finalize_4x4(&save, &in[8*k+0], in + 8*k+1, + e + k*6, out + k*8); + save = save_next; + } + +} + +static ALWAYS_INLINE(void) pffft_real_preprocess_4x4(const v4sf *in, + const v4sf *e, v4sf *out, int first) { + v4sf r0=in[0], i0=in[1], r1=in[2], i1=in[3], r2=in[4], i2=in[5], r3=in[6], i3=in[7]; + /* + transformation for each column is: + + [1 1 1 1 0 0 0 0] [r0] + [1 0 0 -1 0 -1 -1 0] [r1] + [1 -1 -1 1 0 0 0 0] [r2] + [1 0 0 -1 0 1 1 0] [r3] + [0 0 0 0 1 -1 1 -1] * [i0] + [0 -1 1 0 1 0 0 1] [i1] + [0 0 0 0 1 1 -1 -1] [i2] + [0 1 -1 0 1 0 0 1] [i3] + */ + + v4sf sr0 = VADD(r0,r3), dr0 = VSUB(r0,r3); + v4sf sr1 = VADD(r1,r2), dr1 = VSUB(r1,r2); + v4sf si0 = VADD(i0,i3), di0 = VSUB(i0,i3); + v4sf si1 = VADD(i1,i2), di1 = VSUB(i1,i2); + + r0 = VADD(sr0, sr1); + r2 = VSUB(sr0, sr1); + r1 = VSUB(dr0, si1); + r3 = VADD(dr0, si1); + i0 = VSUB(di0, di1); + i2 = VADD(di0, di1); + i1 = VSUB(si0, dr1); + i3 = VADD(si0, dr1); + + VCPLXMULCONJ(r1,i1,e[0],e[1]); + VCPLXMULCONJ(r2,i2,e[2],e[3]); + VCPLXMULCONJ(r3,i3,e[4],e[5]); + + VTRANSPOSE4(r0,r1,r2,r3); + VTRANSPOSE4(i0,i1,i2,i3); + + if (!first) { + *out++ = r0; + *out++ = i0; + } + *out++ = r1; + *out++ = i1; + *out++ = r2; + *out++ = i2; + *out++ = r3; + *out++ = i3; +} + +static NEVER_INLINE(void) pffft_real_preprocess(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) { + int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks + /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */ + + v4sf_union Xr, Xi, *uout = (v4sf_union*)out; + float cr0, ci0, cr1, ci1, cr2, ci2, cr3, ci3; + static const float s = M_SQRT2; + assert(in != out); + for (k=0; k < 4; ++k) { + Xr.f[k] = ((float*)in)[8*k]; + Xi.f[k] = ((float*)in)[8*k+4]; + } + + pffft_real_preprocess_4x4(in, e, out+1, 1); // will write only 6 values + + /* + [Xr0 Xr1 Xr2 Xr3 Xi0 Xi1 Xi2 Xi3] + + [cr0] [1 0 2 0 1 0 0 0] + [cr1] [1 0 0 0 -1 0 -2 0] + [cr2] [1 0 -2 0 1 0 0 0] + [cr3] [1 0 0 0 -1 0 2 0] + [ci0] [0 2 0 2 0 0 0 0] + [ci1] [0 s 0 -s 0 -s 0 -s] + [ci2] [0 0 0 0 0 -2 0 2] + [ci3] [0 -s 0 s 0 -s 0 -s] + */ + for (k=1; k < dk; ++k) { + pffft_real_preprocess_4x4(in+8*k, e + k*6, out-1+k*8, 0); + } + + cr0=(Xr.f[0]+Xi.f[0]) + 2*Xr.f[2]; uout[0].f[0] = cr0; + cr1=(Xr.f[0]-Xi.f[0]) - 2*Xi.f[2]; uout[0].f[1] = cr1; + cr2=(Xr.f[0]+Xi.f[0]) - 2*Xr.f[2]; uout[0].f[2] = cr2; + cr3=(Xr.f[0]-Xi.f[0]) + 2*Xi.f[2]; uout[0].f[3] = cr3; + ci0= 2*(Xr.f[1]+Xr.f[3]); uout[2*Ncvec-1].f[0] = ci0; + ci1= s*(Xr.f[1]-Xr.f[3]) - s*(Xi.f[1]+Xi.f[3]); uout[2*Ncvec-1].f[1] = ci1; + ci2= 2*(Xi.f[3]-Xi.f[1]); uout[2*Ncvec-1].f[2] = ci2; + ci3=-s*(Xr.f[1]-Xr.f[3]) - s*(Xi.f[1]+Xi.f[3]); uout[2*Ncvec-1].f[3] = ci3; +} + + +void pffft_transform_internal(PFFFT_Setup *setup, const float *finput, float *foutput, v4sf *scratch, + pffft_direction_t direction, int ordered) { + int k, Ncvec = setup->Ncvec; + int nf_odd = (setup->ifac[1] & 1); + + // temporary buffer is allocated on the stack if the scratch pointer is NULL + int stack_allocate = (scratch == 0 ? Ncvec*2 : 1); + VLA_ARRAY_ON_STACK(v4sf, scratch_on_stack, stack_allocate); + + const v4sf *vinput = (const v4sf*)finput; + v4sf *voutput = (v4sf*)foutput; + v4sf *buff[2] = { voutput, scratch ? scratch : scratch_on_stack }; + int ib = (nf_odd ^ ordered ? 1 : 0); + + assert(VALIGNED(finput) && VALIGNED(foutput)); + + //assert(finput != foutput); + if (direction == PFFFT_FORWARD) { + ib = !ib; + if (setup->transform == PFFFT_REAL) { + ib = (rfftf1_ps(Ncvec*2, vinput, buff[ib], buff[!ib], + setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1); + pffft_real_finalize(Ncvec, buff[ib], buff[!ib], (v4sf*)setup->e); + } else { + v4sf *tmp = buff[ib]; + for (k=0; k < Ncvec; ++k) { + UNINTERLEAVE2(vinput[k*2], vinput[k*2+1], tmp[k*2], tmp[k*2+1]); + } + ib = (cfftf1_ps(Ncvec, buff[ib], buff[!ib], buff[ib], + setup->twiddle, &setup->ifac[0], -1) == buff[0] ? 0 : 1); + pffft_cplx_finalize(Ncvec, buff[ib], buff[!ib], (v4sf*)setup->e); + } + if (ordered) { + pffft_zreorder(setup, (float*)buff[!ib], (float*)buff[ib], PFFFT_FORWARD); + } else ib = !ib; + } else { + if (vinput == buff[ib]) { + ib = !ib; // may happen when finput == foutput + } + if (ordered) { + pffft_zreorder(setup, (float*)vinput, (float*)buff[ib], PFFFT_BACKWARD); + vinput = buff[ib]; ib = !ib; + } + if (setup->transform == PFFFT_REAL) { + pffft_real_preprocess(Ncvec, vinput, buff[ib], (v4sf*)setup->e); + ib = (rfftb1_ps(Ncvec*2, buff[ib], buff[0], buff[1], + setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1); + } else { + pffft_cplx_preprocess(Ncvec, vinput, buff[ib], (v4sf*)setup->e); + ib = (cfftf1_ps(Ncvec, buff[ib], buff[0], buff[1], + setup->twiddle, &setup->ifac[0], +1) == buff[0] ? 0 : 1); + for (k=0; k < Ncvec; ++k) { + INTERLEAVE2(buff[ib][k*2], buff[ib][k*2+1], buff[ib][k*2], buff[ib][k*2+1]); + } + } + } + + if (buff[ib] != voutput) { + /* extra copy required -- this situation should only happen when finput == foutput */ + assert(finput==foutput); + for (k=0; k < Ncvec; ++k) { + v4sf a = buff[ib][2*k], b = buff[ib][2*k+1]; + voutput[2*k] = a; voutput[2*k+1] = b; + } + ib = !ib; + } + assert(buff[ib] == voutput); + + VLA_ARRAY_ON_STACK_FREE(scratch_on_stack); +} + +void pffft_zconvolve_accumulate(PFFFT_Setup *s, const float *a, const float *b, float *ab, float scaling) { + int Ncvec = s->Ncvec; + const v4sf * RESTRICT va = (const v4sf*)a; + const v4sf * RESTRICT vb = (const v4sf*)b; + v4sf * RESTRICT vab = (v4sf*)ab; + +#ifdef __arm__ + __builtin_prefetch(va); + __builtin_prefetch(vb); + __builtin_prefetch(vab); + __builtin_prefetch(va+2); + __builtin_prefetch(vb+2); + __builtin_prefetch(vab+2); + __builtin_prefetch(va+4); + __builtin_prefetch(vb+4); + __builtin_prefetch(vab+4); + __builtin_prefetch(va+6); + __builtin_prefetch(vb+6); + __builtin_prefetch(vab+6); +# ifndef __clang__ +# define ZCONVOLVE_USING_INLINE_NEON_ASM +# endif +#endif + + float ar, ai, br, bi, abr, abi; +#ifndef ZCONVOLVE_USING_INLINE_ASM + v4sf vscal = LD_PS1(scaling); + int i; +#endif + + assert(VALIGNED(a) && VALIGNED(b) && VALIGNED(ab)); + ar = ((v4sf_union*)va)[0].f[0]; + ai = ((v4sf_union*)va)[1].f[0]; + br = ((v4sf_union*)vb)[0].f[0]; + bi = ((v4sf_union*)vb)[1].f[0]; + abr = ((v4sf_union*)vab)[0].f[0]; + abi = ((v4sf_union*)vab)[1].f[0]; + +#ifdef ZCONVOLVE_USING_INLINE_ASM // inline asm version, unfortunately miscompiled by clang 3.2, at least on ubuntu.. so this will be restricted to gcc + const float *a_ = a, *b_ = b; float *ab_ = ab; + int N = Ncvec; + asm volatile("mov r8, %2 \n" + "vdup.f32 q15, %4 \n" + "1: \n" + "pld [%0,#64] \n" + "pld [%1,#64] \n" + "pld [%2,#64] \n" + "pld [%0,#96] \n" + "pld [%1,#96] \n" + "pld [%2,#96] \n" + "vld1.f32 {q0,q1}, [%0,:128]! \n" + "vld1.f32 {q4,q5}, [%1,:128]! \n" + "vld1.f32 {q2,q3}, [%0,:128]! \n" + "vld1.f32 {q6,q7}, [%1,:128]! \n" + "vld1.f32 {q8,q9}, [r8,:128]! \n" + + "vmul.f32 q10, q0, q4 \n" + "vmul.f32 q11, q0, q5 \n" + "vmul.f32 q12, q2, q6 \n" + "vmul.f32 q13, q2, q7 \n" + "vmls.f32 q10, q1, q5 \n" + "vmla.f32 q11, q1, q4 \n" + "vld1.f32 {q0,q1}, [r8,:128]! \n" + "vmls.f32 q12, q3, q7 \n" + "vmla.f32 q13, q3, q6 \n" + "vmla.f32 q8, q10, q15 \n" + "vmla.f32 q9, q11, q15 \n" + "vmla.f32 q0, q12, q15 \n" + "vmla.f32 q1, q13, q15 \n" + "vst1.f32 {q8,q9},[%2,:128]! \n" + "vst1.f32 {q0,q1},[%2,:128]! \n" + "subs %3, #2 \n" + "bne 1b \n" + : "+r"(a_), "+r"(b_), "+r"(ab_), "+r"(N) : "r"(scaling) : "r8", "q0","q1","q2","q3","q4","q5","q6","q7","q8","q9", "q10","q11","q12","q13","q15","memory"); +#else // default routine, works fine for non-arm cpus with current compilers + for (i=0; i < Ncvec; i += 2) { + v4sf ar, ai, br, bi; + ar = va[2*i+0]; ai = va[2*i+1]; + br = vb[2*i+0]; bi = vb[2*i+1]; + VCPLXMUL(ar, ai, br, bi); + vab[2*i+0] = VMADD(ar, vscal, vab[2*i+0]); + vab[2*i+1] = VMADD(ai, vscal, vab[2*i+1]); + ar = va[2*i+2]; ai = va[2*i+3]; + br = vb[2*i+2]; bi = vb[2*i+3]; + VCPLXMUL(ar, ai, br, bi); + vab[2*i+2] = VMADD(ar, vscal, vab[2*i+2]); + vab[2*i+3] = VMADD(ai, vscal, vab[2*i+3]); + } +#endif + if (s->transform == PFFFT_REAL) { + ((v4sf_union*)vab)[0].f[0] = abr + ar*br*scaling; + ((v4sf_union*)vab)[1].f[0] = abi + ai*bi*scaling; + } +} + + +#else // defined(PFFFT_SIMD_DISABLE) + +// standard routine using scalar floats, without SIMD stuff. + +#define pffft_zreorder_nosimd pffft_zreorder +void pffft_zreorder_nosimd(PFFFT_Setup *setup, const float *in, float *out, pffft_direction_t direction) { + int k, N = setup->N; + if (setup->transform == PFFFT_COMPLEX) { + for (k=0; k < 2*N; ++k) out[k] = in[k]; + return; + } + else if (direction == PFFFT_FORWARD) { + float x_N = in[N-1]; + for (k=N-1; k > 1; --k) out[k] = in[k-1]; + out[0] = in[0]; + out[1] = x_N; + } else { + float x_N = in[1]; + for (k=1; k < N-1; ++k) out[k] = in[k+1]; + out[0] = in[0]; + out[N-1] = x_N; + } +} + +#define pffft_transform_internal_nosimd pffft_transform_internal +void pffft_transform_internal_nosimd(PFFFT_Setup *setup, const float *input, float *output, float *scratch, + pffft_direction_t direction, int ordered) { + int Ncvec = setup->Ncvec; + int nf_odd = (setup->ifac[1] & 1); + + // temporary buffer is allocated on the stack if the scratch pointer is NULL + int stack_allocate = (scratch == 0 ? Ncvec*2 : 1); + VLA_ARRAY_ON_STACK(v4sf, scratch_on_stack, stack_allocate); + float *buff[2]; + int ib; + if (scratch == 0) scratch = scratch_on_stack; + buff[0] = output; buff[1] = scratch; + + if (setup->transform == PFFFT_COMPLEX) ordered = 0; // it is always ordered. + ib = (nf_odd ^ ordered ? 1 : 0); + + if (direction == PFFFT_FORWARD) { + if (setup->transform == PFFFT_REAL) { + ib = (rfftf1_ps(Ncvec*2, input, buff[ib], buff[!ib], + setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1); + } else { + ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], + setup->twiddle, &setup->ifac[0], -1) == buff[0] ? 0 : 1); + } + if (ordered) { + pffft_zreorder(setup, buff[ib], buff[!ib], PFFFT_FORWARD); ib = !ib; + } + } else { + if (input == buff[ib]) { + ib = !ib; // may happen when finput == foutput + } + if (ordered) { + pffft_zreorder(setup, input, buff[!ib], PFFFT_BACKWARD); + input = buff[!ib]; + } + if (setup->transform == PFFFT_REAL) { + ib = (rfftb1_ps(Ncvec*2, input, buff[ib], buff[!ib], + setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1); + } else { + ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], + setup->twiddle, &setup->ifac[0], +1) == buff[0] ? 0 : 1); + } + } + if (buff[ib] != output) { + int k; + // extra copy required -- this situation should happens only when finput == foutput + assert(input==output); + for (k=0; k < Ncvec; ++k) { + float a = buff[ib][2*k], b = buff[ib][2*k+1]; + output[2*k] = a; output[2*k+1] = b; + } + ib = !ib; + } + assert(buff[ib] == output); + + VLA_ARRAY_ON_STACK_FREE(scratch_on_stack); +} + +#define pffft_zconvolve_accumulate_nosimd pffft_zconvolve_accumulate +void pffft_zconvolve_accumulate_nosimd(PFFFT_Setup *s, const float *a, const float *b, + float *ab, float scaling) { + int i, Ncvec = s->Ncvec; + + if (s->transform == PFFFT_REAL) { + // take care of the fftpack ordering + ab[0] += a[0]*b[0]*scaling; + ab[2*Ncvec-1] += a[2*Ncvec-1]*b[2*Ncvec-1]*scaling; + ++ab; ++a; ++b; --Ncvec; + } + for (i=0; i < Ncvec; ++i) { + float ar, ai, br, bi; + ar = a[2*i+0]; ai = a[2*i+1]; + br = b[2*i+0]; bi = b[2*i+1]; + VCPLXMUL(ar, ai, br, bi); + ab[2*i+0] += ar*scaling; + ab[2*i+1] += ai*scaling; + } +} + +#endif // defined(PFFFT_SIMD_DISABLE) + +void pffft_transform(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction) { + pffft_transform_internal(setup, input, output, (v4sf*)work, direction, 0); +} + +void pffft_transform_ordered(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction) { + pffft_transform_internal(setup, input, output, (v4sf*)work, direction, 1); +} diff --git a/webrtc/third_party/pffft/src/pffft.h b/webrtc/third_party/pffft/src/pffft.h new file mode 100644 index 0000000..e235f12 --- /dev/null +++ b/webrtc/third_party/pffft/src/pffft.h @@ -0,0 +1,198 @@ +/* Copyright (c) 2013 Julien Pommier ( pommier@modartt.com ) + + Based on original fortran 77 code from FFTPACKv4 from NETLIB, + authored by Dr Paul Swarztrauber of NCAR, in 1985. + + As confirmed by the NCAR fftpack software curators, the following + FFTPACKv5 license applies to FFTPACKv4 sources. My changes are + released under the same terms. + + FFTPACK license: + + http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html + + Copyright (c) 2004 the University Corporation for Atmospheric + Research ("UCAR"). All rights reserved. Developed by NCAR's + Computational and Information Systems Laboratory, UCAR, + www.cisl.ucar.edu. + + Redistribution and use of the Software in source and binary forms, + with or without modification, is permitted provided that the + following conditions are met: + + - Neither the names of NCAR's Computational and Information Systems + Laboratory, the University Corporation for Atmospheric Research, + nor the names of its sponsors or contributors may be used to + endorse or promote products derived from this Software without + specific prior written permission. + + - Redistributions of source code must retain the above copyright + notices, this list of conditions, and the disclaimer below. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the disclaimer below in the + documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE + SOFTWARE. +*/ + +/* + PFFFT : a Pretty Fast FFT. + + This is basically an adaptation of the single precision fftpack + (v4) as found on netlib taking advantage of SIMD instruction found + on cpus such as intel x86 (SSE1), powerpc (Altivec), and arm (NEON). + + For architectures where no SIMD instruction is available, the code + falls back to a scalar version. + + Restrictions: + + - 1D transforms only, with 32-bit single precision. + + - supports only transforms for inputs of length N of the form + N=(2^a)*(3^b)*(5^c), a >= 5, b >=0, c >= 0 (32, 48, 64, 96, 128, + 144, 160, etc are all acceptable lengths). Performance is best for + 128<=N<=8192. + + - all (float*) pointers in the functions below are expected to + have an "simd-compatible" alignment, that is 16 bytes on x86 and + powerpc CPUs. + + You can allocate such buffers with the functions + pffft_aligned_malloc / pffft_aligned_free (or with stuff like + posix_memalign..) + +*/ + +#ifndef PFFFT_H +#define PFFFT_H + +#include // for size_t + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PFFFT_SIMD_DISABLE +// Detects compiler bugs with respect to simd instruction. +void validate_pffft_simd(); +#endif + +/* opaque struct holding internal stuff (precomputed twiddle factors) + this struct can be shared by many threads as it contains only + read-only data. +*/ +typedef struct PFFFT_Setup PFFFT_Setup; + +/* direction of the transform */ +typedef enum { PFFFT_FORWARD, PFFFT_BACKWARD } pffft_direction_t; + +/* type of transform */ +typedef enum { PFFFT_REAL, PFFFT_COMPLEX } pffft_transform_t; + +/* + prepare for performing transforms of size N -- the returned + PFFFT_Setup structure is read-only so it can safely be shared by + multiple concurrent threads. +*/ +PFFFT_Setup* pffft_new_setup(int N, pffft_transform_t transform); +void pffft_destroy_setup(PFFFT_Setup*); +/* + Perform a Fourier transform , The z-domain data is stored in the + most efficient order for transforming it back, or using it for + convolution. If you need to have its content sorted in the + "usual" way, that is as an array of interleaved complex numbers, + either use pffft_transform_ordered , or call pffft_zreorder after + the forward fft, and before the backward fft. + + Transforms are not scaled: PFFFT_BACKWARD(PFFFT_FORWARD(x)) = N*x. + Typically you will want to scale the backward transform by 1/N. + + The 'work' pointer should point to an area of N (2*N for complex + fft) floats, properly aligned. If 'work' is NULL, then stack will + be used instead (this is probably the best strategy for small + FFTs, say for N < 16384). + + input and output may alias. +*/ +void pffft_transform(PFFFT_Setup* setup, + const float* input, + float* output, + float* work, + pffft_direction_t direction); + +/* + Similar to pffft_transform, but makes sure that the output is + ordered as expected (interleaved complex numbers). This is + similar to calling pffft_transform and then pffft_zreorder. + + input and output may alias. +*/ +void pffft_transform_ordered(PFFFT_Setup* setup, + const float* input, + float* output, + float* work, + pffft_direction_t direction); + +/* + call pffft_zreorder(.., PFFFT_FORWARD) after pffft_transform(..., + PFFFT_FORWARD) if you want to have the frequency components in + the correct "canonical" order, as interleaved complex numbers. + + (for real transforms, both 0-frequency and half frequency + components, which are real, are assembled in the first entry as + F(0)+i*F(n/2+1). Note that the original fftpack did place + F(n/2+1) at the end of the arrays). + + input and output should not alias. +*/ +void pffft_zreorder(PFFFT_Setup* setup, + const float* input, + float* output, + pffft_direction_t direction); + +/* + Perform a multiplication of the frequency components of dft_a and + dft_b and accumulate them into dft_ab. The arrays should have + been obtained with pffft_transform(.., PFFFT_FORWARD) and should + *not* have been reordered with pffft_zreorder (otherwise just + perform the operation yourself as the dft coefs are stored as + interleaved complex numbers). + + the operation performed is: dft_ab += (dft_a * fdt_b)*scaling + + The dft_a, dft_b and dft_ab pointers may alias. +*/ +void pffft_zconvolve_accumulate(PFFFT_Setup* setup, + const float* dft_a, + const float* dft_b, + float* dft_ab, + float scaling); + +/* + the float buffers must have the correct alignment (16-byte boundary + on intel and powerpc). This function may be used to obtain such + correctly aligned buffers. +*/ +void* pffft_aligned_malloc(size_t nb_bytes); +void pffft_aligned_free(void*); + +/* return 4 or 1 wether support SSE/Altivec instructions was enable when + * building pffft.c */ +int pffft_simd_size(); + +#ifdef __cplusplus +} +#endif + +#endif // PFFFT_H diff --git a/webrtc/third_party/rnnoise/BUILD.gn b/webrtc/third_party/rnnoise/BUILD.gn new file mode 100644 index 0000000..7bfa784 --- /dev/null +++ b/webrtc/third_party/rnnoise/BUILD.gn @@ -0,0 +1 @@ +IyBDb3B5cmlnaHQgMjAxOCBUaGUgQ2hyb21pdW0gQXV0aG9ycy4gQWxsIHJpZ2h0cyByZXNlcnZlZC4KIyBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhIEJTRC1zdHlsZSBsaWNlbnNlIHRoYXQgY2FuIGJlCiMgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZS4KCmltcG9ydCgiLy90ZXN0aW5nL3Rlc3QuZ25pIikKCmdyb3VwKCJybm5vaXNlIikgewogIGRlcHMgPSBbICI6cm5uX3ZhZCIgXQp9Cgpzb3VyY2Vfc2V0KCJybm5fdmFkIikgewogIHNvdXJjZXMgPSBbCiAgICAic3JjL3Jubl9hY3RpdmF0aW9ucy5oIiwKICAgICJzcmMvcm5uX3ZhZF93ZWlnaHRzLmNjIiwKICAgICJzcmMvcm5uX3ZhZF93ZWlnaHRzLmgiLAogIF0KfQo= \ No newline at end of file diff --git a/webrtc/third_party/rnnoise/COPYING b/webrtc/third_party/rnnoise/COPYING new file mode 100644 index 0000000..6e286fd --- /dev/null +++ b/webrtc/third_party/rnnoise/COPYING @@ -0,0 +1 @@ +Q29weXJpZ2h0IChjKSAyMDE3LCBNb3ppbGxhCkNvcHlyaWdodCAoYykgMjAwNy0yMDE3LCBKZWFuLU1hcmMgVmFsaW4KQ29weXJpZ2h0IChjKSAyMDA1LTIwMTcsIFhpcGguT3JnIEZvdW5kYXRpb24KQ29weXJpZ2h0IChjKSAyMDAzLTIwMDQsIE1hcmsgQm9yZ2VyZGluZwoKUmVkaXN0cmlidXRpb24gYW5kIHVzZSBpbiBzb3VyY2UgYW5kIGJpbmFyeSBmb3Jtcywgd2l0aCBvciB3aXRob3V0Cm1vZGlmaWNhdGlvbiwgYXJlIHBlcm1pdHRlZCBwcm92aWRlZCB0aGF0IHRoZSBmb2xsb3dpbmcgY29uZGl0aW9ucwphcmUgbWV0OgoKLSBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCgotIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KCi0gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgWGlwaC5PcmcgRm91bmRhdGlvbiBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQgZnJvbQp0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwpgYEFTIElTJycgQU5EIEFOWSBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFOVElFUywgSU5DTFVESU5HLCBCVVQgTk9UCkxJTUlURUQgVE8sIFRIRSBJTVBMSUVEIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZIEFORCBGSVRORVNTIEZPUgpBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBUkUgRElTQ0xBSU1FRC4gIElOIE5PIEVWRU5UIFNIQUxMIFRIRSBGT1VOREFUSU9OCk9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULCBJTkNJREVOVEFMLApTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLCBCVVQgTk9UCkxJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7IExPU1MgT0YgVVNFLApEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIgQ0FVU0VEIEFORCBPTiBBTlkKVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUIExJQUJJTElUWSwgT1IgVE9SVAooSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOIEFOWSBXQVkgT1VUIE9GIFRIRSBVU0UKT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRSBQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4K \ No newline at end of file diff --git a/webrtc/third_party/rnnoise/meson.build b/webrtc/third_party/rnnoise/meson.build new file mode 100644 index 0000000..158353f --- /dev/null +++ b/webrtc/third_party/rnnoise/meson.build @@ -0,0 +1,15 @@ +rnnoise_sources = [ + 'src/rnn_vad_weights.cc', +] + +librnnoise = static_library('librnnoise', + rnnoise_sources, + dependencies: common_deps, + include_directories: webrtc_inc, + cpp_args : common_cxxflags +) + +rnnoise_dep = declare_dependency( + link_with: librnnoise +) + diff --git a/webrtc/third_party/rnnoise/src/rnn_activations.h b/webrtc/third_party/rnnoise/src/rnn_activations.h new file mode 100644 index 0000000..af81031 --- /dev/null +++ b/webrtc/third_party/rnnoise/src/rnn_activations.h @@ -0,0 +1,102 @@ +/* Copyright (c) 2008-2011 Octasic Inc. + 2012-2017 Jean-Marc Valin */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef THIRD_PARTY_RNNOISE_SRC_RNN_ACTIVATIONS_H_ +#define THIRD_PARTY_RNNOISE_SRC_RNN_ACTIVATIONS_H_ + +#include + +namespace rnnoise { + +inline float TansigApproximated(float x) { + static constexpr float kTansigTable[201] = { + 0.000000f, 0.039979f, 0.079830f, 0.119427f, 0.158649f, 0.197375f, + 0.235496f, 0.272905f, 0.309507f, 0.345214f, 0.379949f, 0.413644f, + 0.446244f, 0.477700f, 0.507977f, 0.537050f, 0.564900f, 0.591519f, + 0.616909f, 0.641077f, 0.664037f, 0.685809f, 0.706419f, 0.725897f, + 0.744277f, 0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.821040f, + 0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f, 0.885352f, + 0.893698f, 0.901468f, 0.908698f, 0.915420f, 0.921669f, 0.927473f, + 0.932862f, 0.937863f, 0.942503f, 0.946806f, 0.950795f, 0.954492f, + 0.957917f, 0.961090f, 0.964028f, 0.966747f, 0.969265f, 0.971594f, + 0.973749f, 0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f, + 0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f, 0.989027f, + 0.989867f, 0.990642f, 0.991359f, 0.992020f, 0.992631f, 0.993196f, + 0.993718f, 0.994199f, 0.994644f, 0.995055f, 0.995434f, 0.995784f, + 0.996108f, 0.996407f, 0.996682f, 0.996937f, 0.997172f, 0.997389f, + 0.997590f, 0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f, + 0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f, 0.999000f, + 0.999076f, 0.999147f, 0.999213f, 0.999273f, 0.999329f, 0.999381f, + 0.999428f, 0.999472f, 0.999513f, 0.999550f, 0.999585f, 0.999617f, + 0.999646f, 0.999673f, 0.999699f, 0.999722f, 0.999743f, 0.999763f, + 0.999781f, 0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f, + 0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f, 0.999909f, + 0.999916f, 0.999923f, 0.999929f, 0.999934f, 0.999939f, 0.999944f, + 0.999948f, 0.999952f, 0.999956f, 0.999959f, 0.999962f, 0.999965f, + 0.999968f, 0.999970f, 0.999973f, 0.999975f, 0.999977f, 0.999978f, + 0.999980f, 0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f, + 0.999988f, 0.999989f, 0.999990f, 0.999990f, 0.999991f, 0.999992f, + 0.999992f, 0.999993f, 0.999994f, 0.999994f, 0.999994f, 0.999995f, + 0.999995f, 0.999996f, 0.999996f, 0.999996f, 0.999997f, 0.999997f, + 0.999997f, 0.999997f, 0.999997f, 0.999998f, 0.999998f, 0.999998f, + 0.999998f, 0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f, + 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, + 0.999999f, 0.999999f, 0.999999f, 0.999999f, 1.000000f, 1.000000f, + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, + 1.000000f, 1.000000f, 1.000000f, + }; + + // Tests are reversed to catch NaNs. + if (!(x < 8.f)) + return 1.f; + if (!(x > -8.f)) + return -1.f; + float sign = 1.f; + if (x < 0.f) { + x = -x; + sign = -1.f; + } + // Look-up. + int i = static_cast(std::floor(0.5f + 25 * x)); + float y = kTansigTable[i]; + // Map i back to x's scale (undo 25 factor). + x -= 0.04f * i; + y = y + x * (1.f - y * y) * (1.f - y * x); + return sign * y; +} + +inline float SigmoidApproximated(const float x) { + return 0.5f + 0.5f * TansigApproximated(0.5f * x); +} + +inline float RectifiedLinearUnit(const float x) { + return x < 0.f ? 0.f : x; +} + +} // namespace rnnoise + +#endif // THIRD_PARTY_RNNOISE_SRC_RNN_ACTIVATIONS_H_ diff --git a/webrtc/third_party/rnnoise/src/rnn_vad_weights.cc b/webrtc/third_party/rnnoise/src/rnn_vad_weights.cc new file mode 100644 index 0000000..40c184b --- /dev/null +++ b/webrtc/third_party/rnnoise/src/rnn_vad_weights.cc @@ -0,0 +1,401 @@ +#include "third_party/rnnoise/src/rnn_vad_weights.h" + +namespace rnnoise { + +const int8_t kInputDenseWeights[kInputLayerWeights] = { + -10, 0, -3, 1, -8, -6, 3, -13, 1, 0, -3, -7, + -5, -3, 6, -1, -6, 0, -6, -4, -1, -2, 1, 1, + -7, 2, 21, 10, -5, -20, 24, 23, 37, 8, -2, 33, + -6, 22, 13, -2, 50, 8, 13, 1, -15, 30, -10, 30, + 0, 3, 5, 27, 1, 4, -3, 41, 56, 35, -2, 49, + -13, 11, 13, -2, -47, 5, -16, -60, -15, 77, -17, 26, + -3, 14, -21, 19, -5, -19, -13, 0, 10, 14, 9, 31, + -13, -41, -10, 4, 22, 18, -48, -6, -10, 62, -3, -18, + -14, 12, 26, -28, 3, 14, 25, -13, -19, 6, 5, 36, + -3, -65, -12, 0, 31, -7, -9, 101, -4, 26, 16, 17, + -12, -12, 14, -36, -3, 5, -15, 21, 2, 30, -3, 38, + -4, 1, -6, 7, -7, 14, 38, -22, -30, -3, -7, 3, + -39, -70, -126, 25, 34, 94, -67, -22, -33, 83, -47, -118, + 4, 70, 33, 25, 62, -128, -76, -118, -113, 49, -12, -100, + -18, -114, -33, 43, 32, 61, 40, -9, -106, 2, 36, -100, + -40, -5, 20, -75, 61, -51, -9, 126, -27, -52, 5, -24, + -21, -126, -114, -12, 15, 106, -2, 73, -125, 50, 13, -120, + 35, 35, 4, -61, 29, -124, 6, -53, -69, -125, 64, -89, + 36, -107, -103, -7, 27, 121, 69, 77, -35, 35, 95, -125, + -49, 97, -45, -43, -23, 23, -28, -65, -118, 2, 8, -126, + 27, -97, 92, 5, 55, 82, 17, -57, -115, 37, 8, -106, + -46, 41, -2, 21, -44, 8, -73, -58, -39, 34, 89, -95, + 95, -117, 120, -58, 31, 123, 1, -32, -109, -110, 60, -120, + -43, -74, 5, 91, 26, 21, 114, 82, -83, -126, 123, 22, + -16, -67, 25, -83, 46, 48, -34, -121, -124, -63, -35, -9, + 31, 82, 123, 6, -3, 117, 93, -2, -13, -36, 124, -112, + -6, -102, -5, -33, -15, 44, -69, -127, -23, -40, -34, -85, + 68, 83, -1, 40, 8, 84, 118, -58, -55, -102, 123, -55, + -14, -123, 44, -63, -14, 21, 35, 16, 24, -126, -13, -114, + 35, 20, -36, 61, -9, 97, 34, 19, -32, -109, 76, -104, + 99, -119, 45, -125, -51, -28, -8, -69, -8, 125, -45, -93, + 113, 103, -41, -82, 52, 7, 126, 0, -40, 104, 55, -58, + 17, -124, -93, -58, 8, -45, 1, 56, -123, 108, -47, -23, + 115, 127, 17, -68, -13, 116, -82, -44, 45, 67, -120, -101, + -15, -125, 120, -113, 17, -48, -73, 126, -64, -86, -118, -19, + 112, -1, -66, -27, -62, 121, -86, -58, 50, 89, -38, -75, + 95, -111, 12, -113, 2, -68, 2, -94, -121, 91, -5, 0, + 79, 43, -7, -18, 79, 35, -38, 47, 1, -45, 83, -50, + 102, 32, 55, -96, 15, -122, -69, 45, -27, 91, -62, -30, + 46, -95, 22, -72, -97, -1, 14, -122, 28, 127, 61, -126, + 121, 9, 68, -120, 49, -60, 90, 3, 43, 68, 54, 34, + -10, 28, 21, -24, -54, 22, -113, -12, 82, -2, -17, -9, + 127, 8, 116, -92, 0, -70, -33, 123, 66, 116, -74, -4, + 74, -72, -22, -47, 1, -83, -60, -124, 1, 122, -57, -43, + 49, 40, -126, -128, -8, -29, 28, -24, -123, -121, -70, -93, + -37, -126, 11, -125, -37, 11, -31, -51, -124, 116, -128, 8, + -25, 109, 75, -12, 7, 8, 10, 117, 124, -128, -128, 29, + -26, 101, 21, -128, 87, 8, -39, 23, -128, 127, -127, 74, + -55, 74, 112, 127, 4, 55, 44, -92, 123, 34, -93, 47, + -21, -92, 17, 49, -121, 92, 7, -126, -125, 124, -74, 3, + -59, 18, -91, 3, -9, 9, 56, 116, 7, -29, 33, 87, + -21, -128, -13, 57, 74, 9, -29, -61, -97, -21, -95, -12, + -114, 16, 82, 125, -7, 10, -24, 9, 77, -128, -102, -25, + 3, -126, 10, 13, -18, 51, 26, 127, -79, 35, 51, 12, + -50, -24, 1, -7, 22, 81, 65, 120, -30, -38, 85, 122, + -4, -106, -11, 27, 53, 41, 8, -104, -66, -38, -124, 10, + 12, 76, 117, -109, 9, 11, 2, -18, 3, 113, -16, -79, + -39, -123, -20, -128, 2, 13, -33, -58, 10, 84, -104, 13, + 64, 109, 1, 54, -12, 28, 24, 63, -126, 118, -82, 46, + -12, -15, 14, -43, 60, 22, -32, -19, -46, 91, -107, 24, + -94, 26, -47, 125, 6, 58, -15, -75, -26, -38, -35, 103, + -16, -17, -13, 63, -2, 45, -45, -73, -23, 70, -87, 51, + -17, 53, 76, 14, -18, -31, -14, 103, 8, 21, -28, -33, + -20, -47, 6, 39, 40, -30, 7, -76, 55, 31, -20, -21, + -59, 1, 25, -11, 17, 5, -13, -39, 0, -76, 50, -33, + -29, -50, -16, -11, -12, -1, -46, 40, -10, 65, -19, 21, + -41, -32, -83, -19, -4, 49, -60, 118, -24, -46, 9, 102, + -20, 8, -19, 25, 31, -3, -37, 0, 25, 7, 29, 2, + -39, 127, -64, -20, 64, 115, -30, 36, 100, 35, 122, 127, + 127, -127, 127, -127, 19, 127, -89, -79, -32, 39, -127, 125, + -80, 126, -127, 26, 8, 98, -8, -57, -90, -50, 126, 61, + 127, -126, 40, -106, -68, 104, -125, -119, 11, 10, -127, 66, + -56, -12, -126, -104, 27, 75, 38, -124, -126, -125, 84, -123, + -45, -114, -128, 127, 103, -101, -124, 127, -11, -23, -123, 92, + -123, 24, 126, 41, -2, -39, -27, -94, 40, -112, -48, 127, + 58, 14, 38, -75, -64, 73, 117, 100, -119, -11, 6, 32, + -126, -14, 35, 121, -10, 54, -60, 89, -3, 69, -25, -20, + 43, -86, -34, 24, 27, 7, -81, -99, -23, -16, -26, 13, + 35, -97, 80, -29, -13, -121, -12, -65, -94, 70, -89, -126, + -95, 88, 33, 96, 29, -90, 69, 114, -78, 65, 90, -47, + -47, 89, 1, -12, 3, 8, 30, 5, 2, -30, -1, 6, + -7, 10, -4, 46, -27, -40, 22, -6, -17, 45, 24, -9, + 23, -14, -63, -26, -12, -57, 27, 25, 55, -76, -47, 21, + 34, 33, 26, 17, 14, 6, 9, 26, 25, -25, -25, -18}; + +const int8_t kInputDenseBias[kInputLayerOutputSize] = { + 38, -6, 127, 127, 127, -43, -127, 78, 127, 5, 127, 123, + 127, 127, -128, -76, -126, 28, 127, 125, -30, 127, -89, -20}; + +const int8_t kHiddenGruWeights[kHiddenLayerWeights] = { + -124, 23, -123, -33, -95, -4, 8, -84, 4, 101, -119, 116, + -4, 123, 103, -51, 29, -124, -114, -49, 31, 9, 75, -128, + 0, -49, 37, -50, 46, -21, -63, -104, 54, 82, 33, 21, + 70, 127, -9, -79, -39, -23, -127, 107, 122, -96, -46, -18, + -39, 13, -28, -48, 14, 56, -52, 49, -1, -121, 25, -18, + -36, -52, -57, -30, 54, -124, -26, -47, 10, 39, 12, 2, + 9, -127, -128, 102, 21, 11, -64, -71, 89, -113, -111, 54, + 31, 94, 121, -40, 30, 40, -109, 73, -9, 108, -92, 2, + -127, 116, 127, 127, -122, 95, 127, -37, -127, 28, 89, 10, + 24, -104, -62, -67, -14, 38, 14, -71, 22, -41, 20, -50, + 39, 63, 86, 127, -18, 79, 4, -51, 2, 33, 117, -113, + -78, 56, -91, 37, 34, -45, -44, -22, 21, -16, 56, 30, + -84, -79, 38, -74, 127, 9, -25, 2, 82, 61, 25, -26, + 26, 11, 117, -65, 12, -58, 42, -62, -93, 11, 11, 124, + -123, 80, -125, 11, -90, 42, 94, 4, -109, -1, 85, -52, + 45, -26, -27, 77, -5, 30, 90, 0, 95, -7, 53, 29, + -82, 22, -9, 74, 2, -12, -73, 114, 97, -64, 122, -77, + 43, 91, 86, 126, 106, 72, 90, -43, 46, 96, -51, 21, + 22, 68, 22, 41, 79, 75, -46, -105, 23, -116, 127, -123, + 102, 57, 85, 10, -29, 34, 125, 126, 124, 81, -15, 54, + 96, -128, 39, -124, 103, 74, 126, 127, -50, -71, -122, -64, + 93, -75, 71, 105, 122, 123, 126, 122, -127, 33, -63, -74, + 124, -71, 33, 41, -56, 19, 6, 65, 41, 90, -116, -3, + -46, 75, -13, 98, -74, -42, 74, -95, -96, 81, 24, 32, + -19, -123, 74, 55, 109, 115, 0, 32, 33, 12, -20, 9, + 127, 127, -61, 79, -48, -54, -49, 101, -9, 27, -106, 74, + 119, 77, 87, -126, -24, 127, 124, 31, 34, 127, 40, 3, + -90, 127, 23, 57, -53, 127, -69, -88, -33, 127, 19, -46, + -9, -125, 13, -126, -113, 127, -41, 46, 106, -62, 3, -10, + 111, 49, -34, -24, -20, -112, 11, 101, -50, -34, 50, 65, + -64, -106, 70, -48, 60, 9, -122, -45, 15, -112, -26, -4, + 1, 39, 23, 58, -45, -80, 127, 82, 58, 30, -94, -119, + 51, -89, 95, -107, 30, 127, 125, 58, -52, -42, -38, -20, + -122, 115, 39, -26, 5, 73, 13, -39, 43, -23, -20, -125, + 23, 35, 53, -61, -66, 72, -20, 33, 8, 35, 4, 7, + 18, 19, 16, -45, -50, -71, 31, -29, -41, -27, 10, 14, + 27, 9, -23, 98, 6, -94, 92, 127, -114, 59, -26, -100, + -62, -127, -17, -85, -60, 126, -42, -6, 33, -120, -26, -126, + -127, -35, -114, -31, 25, -126, -100, -126, -64, -46, -31, 30, + 25, -74, -111, -97, -81, -104, -114, -19, -9, -116, -69, 22, + 30, 59, 8, -51, 16, -97, 18, -4, -89, 80, -50, 3, + 36, -67, 56, 69, -26, 107, -10, 58, -28, -4, -57, -72, + -111, 0, -75, -119, 14, -75, -49, -66, -49, 8, -121, 22, + -54, 121, 30, 54, -26, -126, -123, 56, 5, 48, 21, -127, + -11, 23, 25, -82, 6, -25, 119, 78, 4, -104, 27, 61, + -48, 37, -13, -52, 50, -50, 44, -1, -22, -43, -59, -78, + -67, -32, -26, 9, -3, 40, 16, 19, 3, -9, 20, -6, + -37, 28, 39, 17, -19, -10, 1, 6, -59, 74, 47, 3, + -119, 0, -128, -107, -25, -22, -69, -23, -111, -42, -93, -120, + 90, -85, -54, -118, 76, -79, 124, 101, -77, -75, -17, -71, + -114, 68, 55, 79, -1, -123, -20, 127, -65, -123, -128, -87, + 123, 9, -115, -14, 7, -4, 127, -79, -115, 125, -28, 89, + -83, 49, 89, 119, -69, -5, 12, -49, 60, 57, -24, -99, + -110, 76, -83, 125, 73, 81, 11, 8, -45, 1, 83, 13, + -70, -2, 97, 112, -97, 53, -9, -94, 124, 44, -49, -24, + 52, 76, -110, -70, -114, -12, 72, -4, -114, 43, -43, 81, + 102, -84, -27, 62, -40, 52, 58, 124, -35, -51, -123, -43, + 56, -75, -34, -35, -106, 93, -43, 14, -16, 46, 62, -97, + 21, 30, -53, 21, -11, -33, -20, -95, 4, -126, 12, 45, + 20, 108, 85, 11, 20, -40, 99, 4, -25, -18, -23, -12, + -126, -55, -20, -44, -51, 91, -127, 127, -44, 7, 127, 78, + 38, 125, -6, -94, -103, 73, 126, -126, 18, 59, -46, 106, + 76, 116, -31, 75, -4, 92, 102, 32, -31, 73, 42, -21, + -28, 57, 127, -8, -107, 115, 124, -94, -4, -128, 29, -57, + 70, -82, 50, -13, -44, 38, 67, -93, 6, -39, -46, 56, + 68, 27, 61, 26, 18, -72, 127, 22, 18, -31, 127, 61, + -65, -38, 1, -67, -1, 8, -73, 46, -116, -94, 58, -49, + 71, -40, -63, -82, -20, -60, 93, 76, 69, -106, 34, -31, + 4, -25, 107, -18, 45, 4, -61, 126, 54, -126, -125, 41, + 19, 44, 32, -98, 125, -24, 125, -96, -125, 15, 87, -4, + -90, 18, -40, 28, -69, 67, 22, 41, 39, 7, -48, -44, + 12, 69, -13, 2, 44, -38, 111, -7, -126, -22, -9, 74, + -128, -36, -7, -123, -15, -79, -91, -37, -127, -122, 104, 30, + 7, 98, -37, 111, -116, -47, 127, -45, 118, -111, -123, -120, + -77, -64, -125, 124, 77, 111, 77, 18, -113, 117, -9, 67, + -77, 126, 49, -20, -124, 39, 41, -124, -34, 114, -87, -126, + 98, -20, 59, -17, -24, 125, 107, 54, 35, 33, -44, 12, + -29, 125, -71, -28, -63, -114, 28, -17, 121, -36, 127, 89, + -122, -49, -18, -48, 17, 24, 19, -64, -128, 13, 86, 45, + 13, -49, 55, 84, 48, 80, -39, 99, -127, 70, -33, 30, + 50, 126, -65, -117, -13, -20, -24, 127, 115, -72, -104, 63, + 126, -42, 57, 17, 46, 21, 119, 110, -100, -60, -112, 62, + -33, 28, 26, -22, -60, -33, -54, 78, 25, 32, -114, 86, + 44, 26, 43, 76, 121, 19, 97, -2, -3, -73, -68, 6, + -116, 6, -43, -97, 46, -128, -120, -31, -119, -29, 16, 16, + -126, -128, -126, -46, -9, -3, 92, -31, -76, -126, -3, -107, + -12, -23, -69, 5, 51, 27, -42, 23, -70, -128, -29, 22, + 29, -126, -55, 50, -71, -3, 127, 44, -27, -70, -63, -66, + -70, 104, 86, 115, 29, -92, 41, -90, 44, -11, -28, 20, + -11, -63, -16, 43, 31, 17, -73, -31, -1, -17, -11, -39, + 56, 18, 124, 72, -14, 28, 69, -121, -125, 34, 127, 63, + 86, -80, -126, -125, -124, -47, 124, 77, 124, -19, 23, -7, + -50, 96, -128, -93, 102, -53, -36, -87, 119, -125, 92, -126, + 118, 102, 72, -2, 125, 10, 97, 124, -125, 125, 71, -20, + -47, -116, -121, -4, -9, -32, 79, -124, -36, 33, -128, -74, + 125, 23, 127, -29, -115, -32, 124, -89, 32, -107, 43, -17, + 24, 24, 18, 29, -13, -15, -36, 62, -91, 4, -41, 95, + 28, -23, 6, 46, 84, 66, 77, 68, -70, -1, -23, -6, + 65, 70, -21, 9, 77, -12, 2, -118, 4, 9, -108, 84, + 52, 2, 52, 13, -10, 58, -110, 18, 66, -95, -23, 70, + 31, -3, 56, 56, -3, -7, 1, -27, -48, -61, 41, -4, + 10, -62, 32, -7, -24, 9, -48, -60, -4, 79, -20, -38, + -76, 68, -49, -97, 0, -15, 5, -100, -49, -95, -99, -115, + -9, -40, 10, 104, 13, 56, 127, -27, -109, -94, -118, -102, + -44, -85, 52, 127, -4, 14, 62, 121, -122, -26, -79, -42, + -34, 1, 25, -38, -79, -58, -31, -31, -90, -30, -123, 32, + -56, 125, 66, 124, -1, 3, 91, -103, -7, 23, 78, -18, + 9, 69, -69, 76, -38, -33, -2, -98, 18, 106, 84, 55, + 87, -47, 35, -124, 64, 41, -14, 46, 25, -2, 120, -21, + 82, 19, -79, -37, -3, -8, -16, 21, 19, -5, -28, -112, + 39, -6, -30, 53, -69, 53, 46, 127, 123, 78, 20, 28, + -7, 73, 72, 17, -40, 41, 111, 57, 32, -95, 29, 28, + -39, -65, 54, -20, -63, 29, -67, 3, 44, -57, -47, 11, + 61, -22, -44, 61, 48, -100, 20, 125, 96, -24, -16, 3, + -69, -126, 74, -125, 9, 45, -67, -123, -59, -72, 118, 69, + 45, 50, -57, 67, 13, -66, -106, 47, 62, 22, -1, -22, + -25, -40, -125, 3, 125, 32, 102, -56, -25, -75, -30, 122, + 60, -13, 36, -73, 7, -84, 124, 40, -118, 17, -87, -118, + -8, 3, -27, 111, -40, 40, -51, 127, 125, -45, -30, -54, + 46, 80, -1, -30, 101, -17, 18, 26, 54, 7, -12, 1, + -127, 123, -122, -27, -75, 64, 10, 25, -15, -44, 127, -127, + 5, -84, -81, -7, 19, -26, 126, 15, 116, -126, 14, -76, + 44, 62, -110, -124, 125, -29, -87, -3, -69, 82, 90, 57, + -123, 123, 100, -19, -51, -32, 69, 37, -57, -128, -124, -72, + -13, 51, -7, -45, -73, 5, 99, -26, -117, -96, -109, 4, + -31, -12, 0, 31, -42, -27, 12, -81, 118, 39, 83, 14, + 41, -126, 107, -82, 94, -116, -122, -47, -109, -84, -128, -35, + -56, 66, 8, -65, 19, 42, -46, -72, -109, 41, 43, -127, + -113, 58, 127, 42, -75, -1, 65, 117, -55, -113, -123, 124, + 43, -96, -115, -19, 68, 15, 94, 3, 75, 0, 34, 9, + 42, 110, -48, 92, -76, 99, -17, 27, 32, 13, 125, 50, + -17, 56, 4, 53, 34, -8, 99, 80, -126, -21, -65, -11, + -46, 44, -81, -3, -121, 123, 66, -81, -84, 119, 127, 84, + 105, 45, -66, -42, -23, 32, -25, 12, 111, 127, 88, 125, + 30, 24, -127, -9, -54, 127, -116, -119, 88, 70, 94, -120, + 35, -93, 15, 22, -21, 25, -110, -123, -45, 8, -109, 125, + -122, -86, -126, 8, -14, -120, -45, -45, 69, -125, -122, 6, + 81, 86, 125, 95, 54, 77, 54, -123, 126, -85, -117, 56, + 11, 0, -61, -91, -12, -2, -113, -3, -15, -122, -63, -91, + 10, 84, -111, 125, 93, 21, 62, -78, -116, 13, -57, 28, + -124, 126, 110, 12, 15, 95, 15, -19, -125, -97, 52, -7, + 101, 9, 20, -125, -26, -56, 72, 77, 12, -126, 22, -29, + 47, 62, 95, 112, 69, 32, 97, -83, -8, -5, 67, -63, + -123, 79, 59, 0, -6, -17, 4, -111, -52, 27, 65, 0}; + +const int8_t kHiddenGruRecurrentWeights[kHiddenLayerWeights] = { + 65, 83, 35, 56, 24, -34, -28, -2, 125, 19, 42, -9, + 124, -53, 24, -87, 11, 35, -81, -35, -125, -31, 123, -21, + 33, -91, 113, -93, 45, -6, 53, 38, -92, 8, -27, 87, + 4, 43, 43, 10, -128, -128, -46, 127, -38, -45, 25, -87, + 19, 5, 52, -96, -23, -29, 121, -126, -24, -20, -2, 69, + -50, 6, 71, -81, -125, 90, -94, 1, -38, 36, 89, 17, + -60, 71, -48, 18, -15, 44, -18, 59, 11, 114, -51, 32, + 110, 1, 4, 109, -24, 127, 27, 60, 88, 24, 45, -59, + 75, -36, 8, 57, -32, -25, 13, 126, -89, -61, -76, 127, + 18, -62, -68, 23, -113, 5, 126, 43, -88, 26, -78, 18, + 75, 21, 9, -74, 20, 41, 126, -118, -15, 9, 116, 126, + -127, 34, -6, 126, -128, -53, -54, -55, -121, 70, 127, -12, + -68, 82, -25, 104, -126, 126, -21, -26, 124, -75, -127, -120, + 13, 61, -64, -108, -63, -65, -44, -35, -61, -39, 109, -74, + 113, -3, 108, -30, 125, 120, 39, 125, -128, -95, -99, 111, + 9, 25, 114, -75, -92, -54, -12, -32, -38, 10, 31, 10, + 63, 51, 40, -99, 74, 4, 50, -128, -36, -35, -11, -28, + -126, -7, 66, -58, -126, -22, -83, -61, -127, 49, 126, -8, + 7, 62, 36, -11, -32, -44, 63, 116, 41, 65, -127, 126, + 63, -30, -96, 74, -92, 127, 38, -18, -128, 68, -5, 101, + -4, 85, 58, 79, 0, -58, 8, 119, -70, -1, -79, -68, + 114, -28, -90, -6, -112, 2, 127, -8, 10, 55, -59, -126, + 127, 125, 80, 72, 35, -54, 95, -124, -124, 79, 23, -46, + -61, -127, -100, 99, -77, 8, -87, 5, -2, 49, 85, 7, + -71, 82, 53, -41, 22, -22, -93, -103, 6, 52, -56, 14, + -8, -111, 85, 16, 54, 32, -118, -24, 61, -53, 96, -70, + -5, -17, -67, -84, -7, -82, -107, -96, 21, -83, -58, 50, + 12, -126, -1, -28, 34, -126, 115, 17, 91, 1, -127, 72, + 11, 126, -81, 6, 96, -8, 77, 15, -6, 63, -27, 20, + -123, -109, 85, -79, -17, 126, -92, 2, -61, 20, 14, 17, + 121, 123, 30, 57, 120, 127, 57, 42, 117, 98, 67, 39, + -20, -70, 100, 7, 125, 122, 40, 16, -79, 125, 83, 41, + -106, -57, 24, 55, 27, -66, -111, -44, -7, -43, -66, 121, + 42, -128, -45, 35, 15, -127, 34, -35, -34, -40, -18, -6, + 63, 111, 31, 116, 127, 19, 24, -71, -39, 34, 11, 19, + -40, 27, 12, 106, -10, 56, -82, -106, -2, -50, -52, 114, + -126, -34, -43, -68, 10, 76, 57, -118, -128, 37, -104, 76, + 125, 3, -76, 127, -29, 84, -94, -15, 55, 125, 79, 127, + -57, -125, 104, -68, 126, 126, -77, 51, 45, 33, -109, 115, + -11, 1, 95, -121, -5, -9, -126, -114, 39, 68, -126, -107, + -51, -42, 24, -8, 51, -27, -43, 66, -45, 62, -98, -109, + 69, 67, 0, -125, -128, 49, 31, 126, -122, 2, -55, -67, + -126, -70, -128, -125, -77, 25, 16, -8, -102, 11, -75, 82, + 38, -5, 5, 19, 34, 47, -127, -93, 21, 24, -97, -18, + 31, 39, 34, -20, 22, 123, 7, -77, -81, -46, -9, 1, + 23, 39, -127, -43, -8, -50, 10, -21, 59, -9, -4, -13, + -27, 44, 127, 52, -47, 70, -43, 52, 101, -49, 27, 45, + 49, 33, -125, 55, 114, 20, -1, 76, -24, -96, 105, 24, + 126, 75, -21, -105, 13, -42, 40, 126, -30, -39, -95, 125, + -63, 11, 6, 125, 125, -14, 5, 42, -61, -4, 49, 88, + 6, -107, -28, 19, -29, 47, 126, 6, -46, -89, -18, 91, + -20, -6, 118, -21, -22, 39, 115, 11, -42, 54, 73, -55, + -77, 62, -27, -59, -99, -12, -127, -40, 56, -3, -124, -91, + 71, -111, 6, -19, 82, -24, -35, 102, -42, 7, -126, -126, + -125, 18, 98, -52, 127, 105, -52, 40, -83, 126, -122, 109, + 5, 127, 48, 6, 5, -125, 100, -16, 29, 85, -89, 8, + 4, 41, 62, -127, 62, 122, 85, 122, -107, 8, -125, 93, + -127, 127, 102, 19, 19, -66, 41, -42, 114, 127, -48, -117, + -29, -6, -73, -102, -3, -19, 0, 88, 42, 87, -117, -20, + 2, 122, 28, 63, 71, 66, 120, 93, 124, -43, 49, 103, + 31, 90, -91, -22, -126, 26, -24, -21, 51, -126, 87, -103, + -69, -10, -66, -23, 20, 97, 36, 25, -127, 30, -20, -63, + 30, 51, -116, 23, 40, -39, 36, -83, -77, -25, -50, 110, + 14, 13, -109, 125, -65, -55, -87, 124, -126, -32, -72, -108, + 127, 127, -125, -124, 61, 121, 102, -128, -127, 16, 100, 127, + -124, -68, 72, -93, -128, 43, -93, -19, -125, -97, -113, -33, + 83, 127, -44, 127, -75, 127, 16, 44, 50, -122, 23, 118, + 46, 19, 26, -128, 10, 4, 99, -14, -82, -13, 30, 125, + 57, 65, 60, -71, 35, 98, 28, 7, 1, 43, 89, 70, + 75, 121, -59, 82, -126, -53, -16, -116, -65, 52, -52, 0, + 80, 35, 45, -61, 46, 8, 107, 27, -26, -118, 90, 57, + -10, 7, -15, 0, -39, -4, 12, 29, -1, 116, 84, 79, + 119, 125, -59, 28, -6, -25, -43, 2, 90, 79, 67, 103, + -82, 2, -6, 125, 19, 73, 0, -105, 112, -17, 104, 107, + 124, 106, 19, 56, -44, 55, -112, 6, -39, -83, 126, -93, + -98, 57, -120, -23, -38, 2, -31, -48, 106, 127, 127, 69, + 16, 110, 71, 104, 62, -12, -22, 42, -37, -94, 34, -1, + -32, -12, -124, -47, -13, 60, -75, -66, 58, -127, -2, 64, + 76, -106, 73, -49, -31, 127, 126, 31, 16, 127, -110, 107, + -16, -53, 20, 69, -14, -125, 59, -44, 15, 120, 125, 125, + 43, 6, 19, -58, 127, 127, 43, 16, 82, 97, -127, 127, + -93, -41, 88, 0, 77, -15, 116, 16, -124, -31, -3, 95, + -40, -126, -54, -126, -83, -8, -59, 6, 67, -29, 4, 124, + -10, 112, -28, -8, 85, -21, 45, 84, 6, -8, 11, 72, + 32, 84, -62, 77, 2, -36, 75, 31, -50, 116, 126, 119, + -88, -55, -14, -37, 126, 40, -108, -6, -6, 57, 64, -28, + -76, 30, -117, -93, 31, -92, -44, -64, 94, 58, 65, 114, + 41, 47, 71, 42, -26, 99, -126, 57, -5, 74, -19, -113, + -1, 67, -21, 126, 1, -3, 33, 60, -82, 37, -48, 89, + 114, -38, 127, -114, 35, 58, -5, 21, -46, 121, -123, -43, + 127, 115, 123, 122, -101, 126, 127, 81, 52, 89, -127, 102, + 42, 117, -9, -2, 125, 127, 110, 96, 120, 66, 70, 124, + 55, 84, -38, -58, 119, -127, -16, -79, 123, 18, -127, -50, + -38, 120, -85, 1, 7, -56, 108, -77, -2, 21, 37, 1, + 13, -105, -69, 28, -87, 33, -104, -51, 126, 41, 3, -121, + 28, 71, 58, 86, -8, 127, 94, -55, 125, 40, -19, 127, + -33, -87, -23, 7, -111, -68, 9, 84, -119, 55, -82, 78, + -37, -20, -9, -23, 53, -13, 15, -46, 116, 126, -127, 56, + -126, 125, -7, -1, 45, 26, 125, 121, 29, 47, -86, 30, + 10, 76, -125, -7, 23, 92, -12, -39, -18, 92, -97, -8, + -85, -41, 49, -50, 123, -37, -126, -30, 14, 79, -49, -65, + 9, -36, -38, -96, 85, -24, -13, 37, -25, -5, -64, -127, + 55, -60, -18, -61, -63, 127, 56, 67, 15, 124, 72, 120, + 127, 40, -10, 114, 24, -23, 46, 78, -53, 125, 86, 124, + 86, 0, 38, 93, 21, 127, 123, 75, -72, 13, 48, 33, + 83, -51, 15, -32, -49, -33, 120, 64, 7, 9, 65, 60, + 21, -21, -61, -53, -113, 84, -97, 101, 37, -114, -27, 41, + 73, 126, -10, 59, 61, -15, 70, -13, 82, -4, 69, 56, + 94, -91, -50, 92, -74, -48, 53, -7, -107, 127, 28, 30, + -26, -21, -61, 77, 82, 64, -91, -125, 122, -104, 127, 123, + 122, 123, 76, -126, 127, -6, -80, 7, 40, -66, -65, 54, + -2, 23, 96, -64, 74, 2, -53, -12, -123, 39, 60, -20, + 16, -17, -97, 23, -4, -53, -122, 32, -16, -54, -95, 43, + 71, -1, -67, -33, 41, 18, 72, 28, -83, 31, -100, -91, + -27, 10, -128, -106, 2, 76, -13, 42, 34, 112, -19, 44, + 40, -9, -11, 65, 92, -43, -125, 2, 47, -32, 25, 122, + -29, 12, 101, -8, -126, -23, 43, 7, 125, -20, -124, 82, + -2, 13, -73, -106, 115, 31, 116, -23, -44, -71, 84, 3, + 47, 91, 127, 127, -15, 95, 7, 93, 5, 113, -50, 54, + 11, 13, -127, 17, 72, 43, -23, 5, -70, 20, 15, -27, + 99, 69, -109, -122, -94, 16, 127, 0, 116, 104, 45, 108, + -34, 87, 72, -14, 118, 46, 42, 109, -26, 95, 93, 127, + 60, 127, -93, -54, -122, 34, -105, 56, 55, 103, 125, -71, + -50, 95, -72, 127, 107, 21, 73, 126, 61, 127, 127, 24, + -62, 90, 73, 90, -46, -78, -124, 72, 123, -42, 50, -107, + 17, -32, -62, -89, 124, 1, 80, -2, 117, 119, -65, -127, + -95, -121, -52, 103, 66, 75, -3, -62, -127, 127, -74, 124, + 79, 49, 40, 105, -67, -71, -70, 43, 127, 119, -4, 66, + 43, 23, 91, -126, 15, 63, -119, 112, 103, 15, -99, 31, + -127, 69, 116, -46, -67, 2, -126, -29, 30, 30, -69, -98, + -47, -87, -70, -127, 23, -73, 30, -7, 94, -52, -65, 98, + -45, 97, 53, 23, -9, -22, -52, -47, 6, -1, -85, -15, + -61, -14, 68, 110, -10, -121, -25, -35, -15, -94, -123, 27, + 75, 48, -66, -56, -44, 93, 109, 67, -36, 24, 70, -126, + 8, -127, 126, 52, 11, -32, 120, -13, -26, -28, -125, 127, + 106, -50, 124, 36, -126, -12, 0, -23, 76, -71, -126, -12, + -17, -82, 12, 124, 57, 33, 4, 77, -46, 71, -34, 72, + 125, -128, 124, -24, -128, 75, -120, 69, -45, 55, 33, 127, + -33, 4, -105, -41, -59, -91, 123, 44, -127, 127, -67, 52, + 25, -125, -65, 100, -25, 123, 6, 11, -123, -92, -33, 126, + -17, -4, 29, 33, 127, 96, 3, 87, -48, -18, -70, 123, + 58, -127, -3, -52, -1, -36, -41, 127, 51, -52, -27, 46, + -83, 57, 9, 126, 127, 94, 79, -37, -127, -40, 67, 52, + 82, -66, 122, -13, -73, 127, -8, -80, 46, -48, 4, -54}; + +const int8_t kHiddenGruBias[kHiddenLayerBiases] = { + 124, 125, -57, -126, 53, 123, 127, -75, 68, 102, -2, 116, + 124, 127, 124, 125, 126, 123, -16, 48, 125, 126, 78, 85, + 11, 126, -30, -30, -64, -3, -105, -29, -17, 69, 63, 2, + -32, -10, -62, 113, -52, 112, -109, 112, 7, -40, 73, 53, + 62, 6, -2, 0, 0, 100, -16, 26, -24, 56, 26, -10, + -33, 41, 70, 109, -29, 127, 34, -66, 49, 53, 27, 62}; + +const int8_t kOutputDenseWeights[kOutputLayerWeights] = { + 127, 127, 127, 127, 127, 20, 127, -126, -126, -54, 14, 125, + -126, -126, 127, -125, -126, 127, -127, -127, -57, -30, 127, 80}; + +const int8_t kOutputDenseBias[kOutputLayerOutputSize] = {-50}; + +} // namespace rnnoise diff --git a/webrtc/third_party/rnnoise/src/rnn_vad_weights.h b/webrtc/third_party/rnnoise/src/rnn_vad_weights.h new file mode 100644 index 0000000..d55e33d --- /dev/null +++ b/webrtc/third_party/rnnoise/src/rnn_vad_weights.h @@ -0,0 +1,37 @@ +#ifndef THIRD_PARTY_RNNOISE_SRC_RNN_VAD_WEIGHTS_H_ +#define THIRD_PARTY_RNNOISE_SRC_RNN_VAD_WEIGHTS_H_ + +#include +#include + +namespace rnnoise { + +// Weights scaling factor. +const float kWeightsScale = 1.f / 256.f; + +// Input layer (dense). +const size_t kInputLayerInputSize = 42; +const size_t kInputLayerOutputSize = 24; +const size_t kInputLayerWeights = kInputLayerInputSize * kInputLayerOutputSize; +extern const int8_t kInputDenseWeights[kInputLayerWeights]; +extern const int8_t kInputDenseBias[kInputLayerOutputSize]; + +// Hidden layer (GRU). +const size_t kHiddenLayerOutputSize = 24; +const size_t kHiddenLayerWeights = + 3 * kInputLayerOutputSize * kHiddenLayerOutputSize; +const size_t kHiddenLayerBiases = 3 * kHiddenLayerOutputSize; +extern const int8_t kHiddenGruWeights[kHiddenLayerWeights]; +extern const int8_t kHiddenGruRecurrentWeights[kHiddenLayerWeights]; +extern const int8_t kHiddenGruBias[kHiddenLayerBiases]; + +// Output layer (dense). +const size_t kOutputLayerOutputSize = 1; +const size_t kOutputLayerWeights = + kHiddenLayerOutputSize * kOutputLayerOutputSize; +extern const int8_t kOutputDenseWeights[kOutputLayerWeights]; +extern const int8_t kOutputDenseBias[kOutputLayerOutputSize]; + +} // namespace rnnoise + +#endif // THIRD_PARTY_RNNOISE_SRC_RNN_VAD_WEIGHTS_H_ diff --git a/webrtc/typedefs.h b/webrtc/typedefs.h deleted file mode 100644 index d875490..0000000 --- a/webrtc/typedefs.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -// This file contains platform-specific typedefs and defines. -// Much of it is derived from Chromium's build/build_config.h. - -#ifndef WEBRTC_TYPEDEFS_H_ -#define WEBRTC_TYPEDEFS_H_ - -// Processor architecture detection. For more info on what's defined, see: -// http://msdn.microsoft.com/en-us/library/b0084kay.aspx -// http://www.agner.org/optimize/calling_conventions.pdf -// or with gcc, run: "echo | gcc -E -dM -" -#if defined(_M_X64) || defined(__x86_64__) -#define WEBRTC_ARCH_X86_FAMILY -#define WEBRTC_ARCH_X86_64 -#define WEBRTC_ARCH_64_BITS -#define WEBRTC_ARCH_LITTLE_ENDIAN -#elif defined(__aarch64__) -#define WEBRTC_ARCH_64_BITS -#define WEBRTC_ARCH_LITTLE_ENDIAN -#elif defined(_M_IX86) || defined(__i386__) -#define WEBRTC_ARCH_X86_FAMILY -#define WEBRTC_ARCH_X86 -#define WEBRTC_ARCH_32_BITS -#define WEBRTC_ARCH_LITTLE_ENDIAN -#elif defined(__ARMEL__) -// TODO(ajm): We'd prefer to control platform defines here, but this is -// currently provided by the Android makefiles. Commented to avoid duplicate -// definition warnings. -//#define WEBRTC_ARCH_ARM -// TODO(ajm): Chromium uses the following two defines. Should we switch? -//#define WEBRTC_ARCH_ARM_FAMILY -//#define WEBRTC_ARCH_ARMEL -#define WEBRTC_ARCH_32_BITS -#define WEBRTC_ARCH_LITTLE_ENDIAN -#elif defined(__MIPSEL__) -#define WEBRTC_ARCH_32_BITS -#define WEBRTC_ARCH_LITTLE_ENDIAN -#elif defined(__pnacl__) -#define WEBRTC_ARCH_32_BITS -#define WEBRTC_ARCH_LITTLE_ENDIAN -#else -#error Please add support for your architecture in typedefs.h -#endif - -#if !(defined(WEBRTC_ARCH_LITTLE_ENDIAN) ^ defined(WEBRTC_ARCH_BIG_ENDIAN)) -#error Define either WEBRTC_ARCH_LITTLE_ENDIAN or WEBRTC_ARCH_BIG_ENDIAN -#endif - -// TODO(zhongwei.yao): WEBRTC_CPU_DETECTION is only used in one place; we should -// probably just remove it. -#if (defined(WEBRTC_ARCH_X86_FAMILY) && !defined(__SSE2__)) || \ - defined(WEBRTC_DETECT_NEON) -#define WEBRTC_CPU_DETECTION -#endif - -// TODO(pbos): Use webrtc/base/basictypes.h instead to include fixed-size ints. -#include - -// Annotate a function indicating the caller must examine the return value. -// Use like: -// int foo() WARN_UNUSED_RESULT; -// TODO(ajm): Hack to avoid multiple definitions until the base/ of webrtc and -// libjingle are merged. -#if !defined(WARN_UNUSED_RESULT) -#if defined(__GNUC__) -#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#else -#define WARN_UNUSED_RESULT -#endif -#endif // WARN_UNUSED_RESULT - -// Put after a variable that might not be used, to prevent compiler warnings: -// int result ATTRIBUTE_UNUSED = DoSomething(); -// assert(result == 17); -#ifndef ATTRIBUTE_UNUSED -#if defined(__GNUC__) || defined(__clang__) -#define ATTRIBUTE_UNUSED __attribute__((unused)) -#else -#define ATTRIBUTE_UNUSED -#endif -#endif - -// Macro to be used for switch-case fallthrough (required for enabling -// -Wimplicit-fallthrough warning on Clang). -#ifndef FALLTHROUGH -#if defined(__clang__) -#define FALLTHROUGH() [[clang::fallthrough]] -#else -#define FALLTHROUGH() do { } while (0) -#endif -#endif - -// Annotate a function that will not return control flow to the caller. -#if defined(_MSC_VER) -#define NO_RETURN __declspec(noreturn) -#elif defined(__GNUC__) -#define NO_RETURN __attribute__((noreturn)) -#else -#define NO_RETURN -#endif - -#endif // WEBRTC_TYPEDEFS_H_